test
This commit is contained in:
		| @@ -23,9 +23,9 @@ const accessDenied = { | ||||
|  | ||||
| @Injectable() | ||||
| export class ApiCallService implements OnApplicationShutdown { | ||||
| 	#logger: Logger; | ||||
| 	#userIpHistories: Map<User['id'], Set<string>>; | ||||
| 	#userIpHistoriesClearIntervalId: NodeJS.Timer; | ||||
| 	private logger: Logger; | ||||
| 	private userIpHistories: Map<User['id'], Set<string>>; | ||||
| 	private userIpHistoriesClearIntervalId: NodeJS.Timer; | ||||
|  | ||||
| 	constructor( | ||||
| 		@Inject(DI.userIpsRepository) | ||||
| @@ -36,11 +36,11 @@ export class ApiCallService implements OnApplicationShutdown { | ||||
| 		private rateLimiterService: RateLimiterService, | ||||
| 		private apiLoggerService: ApiLoggerService, | ||||
| 	) { | ||||
| 		this.#logger = this.apiLoggerService.logger; | ||||
| 		this.#userIpHistories = new Map<User['id'], Set<string>>(); | ||||
| 		this.logger = this.apiLoggerService.logger; | ||||
| 		this.userIpHistories = new Map<User['id'], Set<string>>(); | ||||
|  | ||||
| 		this.#userIpHistoriesClearIntervalId = setInterval(() => { | ||||
| 			this.#userIpHistories.clear(); | ||||
| 		this.userIpHistoriesClearIntervalId = setInterval(() => { | ||||
| 			this.userIpHistories.clear(); | ||||
| 		}, 1000 * 60 * 60); | ||||
| 	} | ||||
|  | ||||
| @@ -76,7 +76,7 @@ export class ApiCallService implements OnApplicationShutdown { | ||||
| 			// Authentication | ||||
| 			this.authenticateService.authenticate(body['i']).then(([user, app]) => { | ||||
| 				// API invoking | ||||
| 				this.#call(endpoint, exec, user, app, body, ctx).then((res: any) => { | ||||
| 				this.call(endpoint, exec, user, app, body, ctx).then((res: any) => { | ||||
| 					if (ctx.method === 'GET' && endpoint.meta.cacheSec && !body['i'] && !user) { | ||||
| 						ctx.set('Cache-Control', `public, max-age=${endpoint.meta.cacheSec}`); | ||||
| 					} | ||||
| @@ -90,10 +90,10 @@ export class ApiCallService implements OnApplicationShutdown { | ||||
| 					this.metaService.fetch().then(meta => { | ||||
| 						if (!meta.enableIpLogging) return; | ||||
| 						const ip = ctx.ip; | ||||
| 						const ips = this.#userIpHistories.get(user.id); | ||||
| 						const ips = this.userIpHistories.get(user.id); | ||||
| 						if (ips == null || !ips.has(ip)) { | ||||
| 							if (ips == null) { | ||||
| 								this.#userIpHistories.set(user.id, new Set([ip])); | ||||
| 								this.userIpHistories.set(user.id, new Set([ip])); | ||||
| 							} else { | ||||
| 								ips.add(ip); | ||||
| 							} | ||||
| @@ -123,7 +123,7 @@ export class ApiCallService implements OnApplicationShutdown { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	async #call( | ||||
| 	private async call( | ||||
| 		ep: IEndpoint, | ||||
| 		exec: any, | ||||
| 		user: CacheableLocalUser | null | undefined, | ||||
| @@ -225,7 +225,7 @@ export class ApiCallService implements OnApplicationShutdown { | ||||
| 			if (err instanceof ApiError) { | ||||
| 				throw err; | ||||
| 			} else { | ||||
| 				this.#logger.error(`Internal error occurred in ${ep.name}: ${err.message}`, { | ||||
| 				this.logger.error(`Internal error occurred in ${ep.name}: ${err.message}`, { | ||||
| 					ep: ep.name, | ||||
| 					ps: data, | ||||
| 					e: { | ||||
| @@ -247,12 +247,12 @@ export class ApiCallService implements OnApplicationShutdown { | ||||
| 			const after = performance.now(); | ||||
| 			const time = after - before; | ||||
| 			if (time > 1000) { | ||||
| 				this.#logger.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`); | ||||
| 				this.logger.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	public onApplicationShutdown(signal?: string | undefined) { | ||||
| 		clearInterval(this.#userIpHistoriesClearIntervalId); | ||||
| 		clearInterval(this.userIpHistoriesClearIntervalId); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -17,7 +17,7 @@ export class AuthenticationError extends Error { | ||||
|  | ||||
| @Injectable() | ||||
| export class AuthenticateService { | ||||
| 	#appCache: Cache<App>; | ||||
| 	private appCache: Cache<App>; | ||||
|  | ||||
| 	constructor( | ||||
| 		@Inject(DI.usersRepository) | ||||
| @@ -31,7 +31,7 @@ export class AuthenticateService { | ||||
|  | ||||
| 		private userCacheService: UserCacheService, | ||||
| 	) { | ||||
| 		this.#appCache = new Cache<App>(Infinity); | ||||
| 		this.appCache = new Cache<App>(Infinity); | ||||
| 	} | ||||
|  | ||||
| 	public async authenticate(token: string | null): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> { | ||||
| @@ -71,7 +71,7 @@ export class AuthenticateService { | ||||
| 				}) as Promise<ILocalUser>); | ||||
| 	 | ||||
| 			if (accessToken.appId) { | ||||
| 				const app = await this.#appCache.fetch(accessToken.appId, | ||||
| 				const app = await this.appCache.fetch(accessToken.appId, | ||||
| 					() => this.appsRepository.findOneByOrFail({ id: accessToken.appId! })); | ||||
| 	 | ||||
| 				return [user, { | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import type { IEndpointMeta } from './endpoints.js'; | ||||
|  | ||||
| @Injectable() | ||||
| export class RateLimiterService { | ||||
| 	#logger: Logger; | ||||
| 	private logger: Logger; | ||||
|  | ||||
| 	constructor( | ||||
| 		@Inject(DI.redis) | ||||
| @@ -16,7 +16,7 @@ export class RateLimiterService { | ||||
|  | ||||
| 		private loggerService: LoggerService, | ||||
| 	) { | ||||
| 		this.#logger = this.loggerService.getLogger('limiter'); | ||||
| 		this.logger = this.loggerService.getLogger('limiter'); | ||||
| 	} | ||||
|  | ||||
| 	public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable<string> }, actor: string) { | ||||
| @@ -37,7 +37,7 @@ export class RateLimiterService { | ||||
| 						return reject('ERR'); | ||||
| 					} | ||||
| 		 | ||||
| 					this.#logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`); | ||||
| 					this.logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`); | ||||
| 		 | ||||
| 					if (info.remaining === 0) { | ||||
| 						reject('BRIEF_REQUEST_INTERVAL'); | ||||
| @@ -65,7 +65,7 @@ export class RateLimiterService { | ||||
| 						return reject('ERR'); | ||||
| 					} | ||||
| 		 | ||||
| 					this.#logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`); | ||||
| 					this.logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`); | ||||
| 		 | ||||
| 					if (info.remaining === 0) { | ||||
| 						reject('RATE_LIMIT_EXCEEDED'); | ||||
|   | ||||
| @@ -71,13 +71,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
|  | ||||
| 			(async () => { | ||||
| 				await this.userSuspendService.doPostSuspend(user).catch(e => {}); | ||||
| 				await this.#unFollowAll(user).catch(e => {}); | ||||
| 				await this.#readAllNotify(user).catch(e => {}); | ||||
| 				await this.unFollowAll(user).catch(e => {}); | ||||
| 				await this.readAllNotify(user).catch(e => {}); | ||||
| 			})(); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	async #unFollowAll(follower: User) { | ||||
| 	private async unFollowAll(follower: User) { | ||||
| 		const followings = await this.followingsRepository.findBy({ | ||||
| 			followerId: follower.id, | ||||
| 		}); | ||||
| @@ -95,7 +95,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	async #readAllNotify(notifier: User) { | ||||
| 	private async readAllNotify(notifier: User) { | ||||
| 		await this.notificationsRepository.update({ | ||||
| 			notifierId: notifier.id, | ||||
| 			isRead: false, | ||||
|   | ||||
| @@ -100,7 +100,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 		private apNoteService: ApNoteService, | ||||
| 	) { | ||||
| 		super(meta, paramDef, async (ps, me) => { | ||||
| 			const object = await this.#fetchAny(ps.uri, me); | ||||
| 			const object = await this.fetchAny(ps.uri, me); | ||||
| 			if (object) { | ||||
| 				return object; | ||||
| 			} else { | ||||
| @@ -112,12 +112,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 	/*** | ||||
| 	 * URIからUserかNoteを解決する | ||||
| 	 */ | ||||
| 	async #fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> { | ||||
| 	private async fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> { | ||||
| 	// ブロックしてたら中断 | ||||
| 		const fetchedMeta = await this.metaService.fetch(); | ||||
| 		if (fetchedMeta.blockedHosts.includes(this.utilityService.extractDbHost(uri))) return null; | ||||
|  | ||||
| 		let local = await this.#mergePack(me, ...await Promise.all([ | ||||
| 		let local = await this.mergePack(me, ...await Promise.all([ | ||||
| 			this.apDbResolverService.getUserFromApId(uri), | ||||
| 			this.apDbResolverService.getNoteFromApId(uri), | ||||
| 		])); | ||||
| @@ -130,21 +130,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { | ||||
| 		// /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する | ||||
| 		// これはDBに存在する可能性があるため再度DB検索 | ||||
| 		if (uri !== object.id) { | ||||
| 			local = await this.#mergePack(me, ...await Promise.all([ | ||||
| 			local = await this.mergePack(me, ...await Promise.all([ | ||||
| 				this.apDbResolverService.getUserFromApId(object.id), | ||||
| 				this.apDbResolverService.getNoteFromApId(object.id), | ||||
| 			])); | ||||
| 			if (local != null) return local; | ||||
| 		} | ||||
|  | ||||
| 		return await this.#mergePack( | ||||
| 		return await this.mergePack( | ||||
| 			me, | ||||
| 			isActor(object) ? await this.apPersonService.createPerson(getApId(object)) : null, | ||||
| 			isPost(object) ? await this.apNoteService.createNote(getApId(object), undefined, true) : null, | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	async #mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise<SchemaType<typeof meta.res> | null> { | ||||
| 	private async mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise<SchemaType<typeof meta.res> | null> { | ||||
| 		if (user != null) { | ||||
| 			return { | ||||
| 				type: 'User', | ||||
|   | ||||
| @@ -42,12 +42,12 @@ export class DiscordServerService { | ||||
| 		const router = new Router(); | ||||
|  | ||||
| 		router.get('/disconnect/discord', async ctx => { | ||||
| 			if (!this.#compareOrigin(ctx)) { | ||||
| 			if (!this.compareOrigin(ctx)) { | ||||
| 				ctx.throw(400, 'invalid origin'); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			const userToken = this.#getUserToken(ctx); | ||||
| 			const userToken = this.getUserToken(ctx); | ||||
| 			if (!userToken) { | ||||
| 				ctx.throw(400, 'signin required'); | ||||
| 				return; | ||||
| @@ -91,12 +91,12 @@ export class DiscordServerService { | ||||
| 		}; | ||||
|  | ||||
| 		router.get('/connect/discord', async ctx => { | ||||
| 			if (!this.#compareOrigin(ctx)) { | ||||
| 			if (!this.compareOrigin(ctx)) { | ||||
| 				ctx.throw(400, 'invalid origin'); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			const userToken = this.#getUserToken(ctx); | ||||
| 			const userToken = this.getUserToken(ctx); | ||||
| 			if (!userToken) { | ||||
| 				ctx.throw(400, 'signin required'); | ||||
| 				return; | ||||
| @@ -138,7 +138,7 @@ export class DiscordServerService { | ||||
| 		}); | ||||
|  | ||||
| 		router.get('/dc/cb', async ctx => { | ||||
| 			const userToken = this.#getUserToken(ctx); | ||||
| 			const userToken = this.getUserToken(ctx); | ||||
|  | ||||
| 			const oauth2 = await getOAuth2(); | ||||
|  | ||||
| @@ -299,11 +299,11 @@ export class DiscordServerService { | ||||
| 		return router; | ||||
| 	} | ||||
|  | ||||
| 	#getUserToken(ctx: Koa.BaseContext): string | null { | ||||
| 	private getUserToken(ctx: Koa.BaseContext): string | null { | ||||
| 		return ((ctx.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; | ||||
| 	} | ||||
| 	 | ||||
| 	#compareOrigin(ctx: Koa.BaseContext): boolean { | ||||
| 	private compareOrigin(ctx: Koa.BaseContext): boolean { | ||||
| 		function normalizeUrl(url?: string): string { | ||||
| 			return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; | ||||
| 		} | ||||
|   | ||||
| @@ -42,12 +42,12 @@ export class GithubServerService { | ||||
| 		const router = new Router(); | ||||
|  | ||||
| 		router.get('/disconnect/github', async ctx => { | ||||
| 			if (!this.#compareOrigin(ctx)) { | ||||
| 			if (!this.compareOrigin(ctx)) { | ||||
| 				ctx.throw(400, 'invalid origin'); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			const userToken = this.#getUserToken(ctx); | ||||
| 			const userToken = this.getUserToken(ctx); | ||||
| 			if (!userToken) { | ||||
| 				ctx.throw(400, 'signin required'); | ||||
| 				return; | ||||
| @@ -91,12 +91,12 @@ export class GithubServerService { | ||||
| 		}; | ||||
|  | ||||
| 		router.get('/connect/github', async ctx => { | ||||
| 			if (!this.#compareOrigin(ctx)) { | ||||
| 			if (!this.compareOrigin(ctx)) { | ||||
| 				ctx.throw(400, 'invalid origin'); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			const userToken = this.#getUserToken(ctx); | ||||
| 			const userToken = this.getUserToken(ctx); | ||||
| 			if (!userToken) { | ||||
| 				ctx.throw(400, 'signin required'); | ||||
| 				return; | ||||
| @@ -136,7 +136,7 @@ export class GithubServerService { | ||||
| 		}); | ||||
|  | ||||
| 		router.get('/gh/cb', async ctx => { | ||||
| 			const userToken = this.#getUserToken(ctx); | ||||
| 			const userToken = this.getUserToken(ctx); | ||||
|  | ||||
| 			const oauth2 = await getOath2(); | ||||
|  | ||||
| @@ -271,11 +271,11 @@ export class GithubServerService { | ||||
| 		return router; | ||||
| 	} | ||||
|  | ||||
| 	#getUserToken(ctx: Koa.BaseContext): string | null { | ||||
| 	private getUserToken(ctx: Koa.BaseContext): string | null { | ||||
| 		return ((ctx.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; | ||||
| 	} | ||||
| 	 | ||||
| 	#compareOrigin(ctx: Koa.BaseContext): boolean { | ||||
| 	private compareOrigin(ctx: Koa.BaseContext): boolean { | ||||
| 		function normalizeUrl(url?: string): string { | ||||
| 			return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; | ||||
| 		} | ||||
|   | ||||
| @@ -42,12 +42,12 @@ export class TwitterServerService { | ||||
| 		const router = new Router(); | ||||
|  | ||||
| 		router.get('/disconnect/twitter', async ctx => { | ||||
| 			if (!this.#compareOrigin(ctx)) { | ||||
| 			if (!this.compareOrigin(ctx)) { | ||||
| 				ctx.throw(400, 'invalid origin'); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			const userToken = this.#getUserToken(ctx); | ||||
| 			const userToken = this.getUserToken(ctx); | ||||
| 			if (userToken == null) { | ||||
| 				ctx.throw(400, 'signin required'); | ||||
| 				return; | ||||
| @@ -90,12 +90,12 @@ export class TwitterServerService { | ||||
| 		}; | ||||
|  | ||||
| 		router.get('/connect/twitter', async ctx => { | ||||
| 			if (!this.#compareOrigin(ctx)) { | ||||
| 			if (!this.compareOrigin(ctx)) { | ||||
| 				ctx.throw(400, 'invalid origin'); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			const userToken = this.#getUserToken(ctx); | ||||
| 			const userToken = this.getUserToken(ctx); | ||||
| 			if (userToken == null) { | ||||
| 				ctx.throw(400, 'signin required'); | ||||
| 				return; | ||||
| @@ -125,7 +125,7 @@ export class TwitterServerService { | ||||
| 		}); | ||||
|  | ||||
| 		router.get('/tw/cb', async ctx => { | ||||
| 			const userToken = this.#getUserToken(ctx); | ||||
| 			const userToken = this.getUserToken(ctx); | ||||
|  | ||||
| 			const twAuth = await getTwAuth(); | ||||
|  | ||||
| @@ -214,11 +214,11 @@ export class TwitterServerService { | ||||
| 		return router; | ||||
| 	} | ||||
|  | ||||
| 	#getUserToken(ctx: Koa.BaseContext): string | null { | ||||
| 	private getUserToken(ctx: Koa.BaseContext): string | null { | ||||
| 		return ((ctx.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; | ||||
| 	} | ||||
| 	 | ||||
| 	#compareOrigin(ctx: Koa.BaseContext): boolean { | ||||
| 	private compareOrigin(ctx: Koa.BaseContext): boolean { | ||||
| 		function normalizeUrl(url?: string): string { | ||||
| 			return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; | ||||
| 		} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo