strictNullChecks (#4666)
* wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip
This commit is contained in:
		
							parent
							
								
									4ee40c3345
								
							
						
					
					
						commit
						987168b863
					
				
					 214 changed files with 939 additions and 785 deletions
				
			
		|  | @ -120,7 +120,7 @@ gulp.task('copy:client', () => | ||||||
| 		]) | 		]) | ||||||
| 			.pipe(isProduction ? (imagemin as any)() : gutil.noop()) | 			.pipe(isProduction ? (imagemin as any)() : gutil.noop()) | ||||||
| 			.pipe(rename(path => { | 			.pipe(rename(path => { | ||||||
| 				path.dirname = path.dirname.replace('assets', '.'); | 				path.dirname = path.dirname!.replace('assets', '.'); | ||||||
| 			})) | 			})) | ||||||
| 			.pipe(gulp.dest('./built/client/assets/')) | 			.pipe(gulp.dest('./built/client/assets/')) | ||||||
| ); | ); | ||||||
|  |  | ||||||
|  | @ -106,7 +106,7 @@ | ||||||
| 		"bcryptjs": "2.4.3", | 		"bcryptjs": "2.4.3", | ||||||
| 		"bootstrap-vue": "2.0.0-rc.13", | 		"bootstrap-vue": "2.0.0-rc.13", | ||||||
| 		"bull": "3.7.0", | 		"bull": "3.7.0", | ||||||
| 		"cafy": "15.1.0", | 		"cafy": "15.1.1", | ||||||
| 		"chai": "4.2.0", | 		"chai": "4.2.0", | ||||||
| 		"chai-http": "4.2.1", | 		"chai-http": "4.2.1", | ||||||
| 		"chalk": "2.4.2", | 		"chalk": "2.4.2", | ||||||
|  |  | ||||||
|  | @ -44,7 +44,7 @@ function greet() { | ||||||
| export async function masterMain() { | export async function masterMain() { | ||||||
| 	greet(); | 	greet(); | ||||||
| 
 | 
 | ||||||
| 	let config: Config; | 	let config!: Config; | ||||||
| 
 | 
 | ||||||
| 	try { | 	try { | ||||||
| 		// initialize app
 | 		// initialize app
 | ||||||
|  |  | ||||||
|  | @ -15,6 +15,6 @@ export async function workerMain() { | ||||||
| 
 | 
 | ||||||
| 	if (cluster.isWorker) { | 	if (cluster.isWorker) { | ||||||
| 		// Send a 'ready' message to parent process
 | 		// Send a 'ready' message to parent process
 | ||||||
| 		process.send('ready'); | 		process.send!('ready'); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ export default function load() { | ||||||
| 
 | 
 | ||||||
| 	config.url = url.origin; | 	config.url = url.origin; | ||||||
| 
 | 
 | ||||||
| 	config.port = config.port || parseInt(process.env.PORT, 10); | 	config.port = config.port || parseInt(process.env.PORT || '', 10); | ||||||
| 
 | 
 | ||||||
| 	mixin.host = url.host; | 	mixin.host = url.host; | ||||||
| 	mixin.hostname = url.hostname; | 	mixin.hostname = url.hostname; | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ initDb().then(() => { | ||||||
| 			all, local | 			all, local | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		process.send(stats); | 		process.send!(stats); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tick(); | 	tick(); | ||||||
|  |  | ||||||
|  | @ -76,7 +76,7 @@ class MyCustomLogger implements Logger { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function initDb(justBorrow = false, sync = false, log = false) { | export function initDb(justBorrow = false, sync = false, log = false) { | ||||||
| 	const enableLogging = log || !['production', 'test'].includes(process.env.NODE_ENV); | 	const enableLogging = log || !['production', 'test'].includes(process.env.NODE_ENV || ''); | ||||||
| 
 | 
 | ||||||
| 	try { | 	try { | ||||||
| 		const conn = getConnection(); | 		const conn = getConnection(); | ||||||
|  | @ -93,7 +93,7 @@ export function initDb(justBorrow = false, sync = false, log = false) { | ||||||
| 		synchronize: process.env.NODE_ENV === 'test' || sync, | 		synchronize: process.env.NODE_ENV === 'test' || sync, | ||||||
| 		dropSchema: process.env.NODE_ENV === 'test' && !justBorrow, | 		dropSchema: process.env.NODE_ENV === 'test' && !justBorrow, | ||||||
| 		logging: enableLogging, | 		logging: enableLogging, | ||||||
| 		logger: enableLogging ? new MyCustomLogger() : null, | 		logger: enableLogging ? new MyCustomLogger() : undefined, | ||||||
| 		entities: [ | 		entities: [ | ||||||
| 			Meta, | 			Meta, | ||||||
| 			Instance, | 			Instance, | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ export type Undo = { | ||||||
| 	/** | 	/** | ||||||
| 	 * ターン | 	 * ターン | ||||||
| 	 */ | 	 */ | ||||||
| 	turn: Color; | 	turn: Color | null; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -47,12 +47,12 @@ export default class Reversi { | ||||||
| 	public map: MapPixel[]; | 	public map: MapPixel[]; | ||||||
| 	public mapWidth: number; | 	public mapWidth: number; | ||||||
| 	public mapHeight: number; | 	public mapHeight: number; | ||||||
| 	public board: Color[]; | 	public board: (Color | null | undefined)[]; | ||||||
| 	public turn: Color = BLACK; | 	public turn: Color | null = BLACK; | ||||||
| 	public opts: Options; | 	public opts: Options; | ||||||
| 
 | 
 | ||||||
| 	public prevPos = -1; | 	public prevPos = -1; | ||||||
| 	public prevColor: Color = null; | 	public prevColor: Color | null = null; | ||||||
| 
 | 
 | ||||||
| 	private logs: Undo[] = []; | 	private logs: Undo[] = []; | ||||||
| 
 | 
 | ||||||
|  | @ -145,12 +145,12 @@ export default class Reversi { | ||||||
| 		// ターン計算
 | 		// ターン計算
 | ||||||
| 		this.turn = | 		this.turn = | ||||||
| 			this.canPutSomewhere(!this.prevColor) ? !this.prevColor : | 			this.canPutSomewhere(!this.prevColor) ? !this.prevColor : | ||||||
| 			this.canPutSomewhere(this.prevColor) ? this.prevColor : | 			this.canPutSomewhere(this.prevColor!) ? this.prevColor : | ||||||
| 			null; | 			null; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public undo() { | 	public undo() { | ||||||
| 		const undo = this.logs.pop(); | 		const undo = this.logs.pop()!; | ||||||
| 		this.prevColor = undo.color; | 		this.prevColor = undo.color; | ||||||
| 		this.prevPos = undo.pos; | 		this.prevPos = undo.pos; | ||||||
| 		this.board[undo.pos] = null; | 		this.board[undo.pos] = null; | ||||||
|  | @ -254,10 +254,10 @@ export default class Reversi { | ||||||
| 	/** | 	/** | ||||||
| 	 * ゲームの勝者 (null = 引き分け) | 	 * ゲームの勝者 (null = 引き分け) | ||||||
| 	 */ | 	 */ | ||||||
| 	public get winner(): Color { | 	public get winner(): Color | null { | ||||||
| 		return this.isEnded ? | 		return this.isEnded ? | ||||||
| 			this.blackCount == this.whiteCount ? null : | 			this.blackCount == this.whiteCount ? null : | ||||||
| 			this.opts.isLlotheo === this.blackCount > this.whiteCount ? WHITE : BLACK : | 			this.opts.isLlotheo === this.blackCount > this.whiteCount ? WHITE : BLACK : | ||||||
| 			undefined; | 			undefined as never; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,8 +3,6 @@ import { URL } from 'url'; | ||||||
| import { urlRegex } from './prelude'; | import { urlRegex } from './prelude'; | ||||||
| 
 | 
 | ||||||
| export function fromHtml(html: string): string { | export function fromHtml(html: string): string { | ||||||
| 	if (html == null) return null; |  | ||||||
| 
 |  | ||||||
| 	const dom = parseFragment(html) as DefaultTreeDocumentFragment; | 	const dom = parseFragment(html) as DefaultTreeDocumentFragment; | ||||||
| 
 | 
 | ||||||
| 	let text = ''; | 	let text = ''; | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ import { mfmLanguage } from './language'; | ||||||
| import { MfmForest } from './prelude'; | import { MfmForest } from './prelude'; | ||||||
| import { normalize } from './normalize'; | import { normalize } from './normalize'; | ||||||
| 
 | 
 | ||||||
| export function parse(source: string): MfmForest { | export function parse(source: string | null): MfmForest | null { | ||||||
| 	if (source == null || source == '') { | 	if (source == null || source == '') { | ||||||
| 		return null; | 		return null; | ||||||
| 	} | 	} | ||||||
|  | @ -10,7 +10,7 @@ export function parse(source: string): MfmForest { | ||||||
| 	return normalize(mfmLanguage.root.tryParse(source)); | 	return normalize(mfmLanguage.root.tryParse(source)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function parsePlain(source: string): MfmForest { | export function parsePlain(source: string | null): MfmForest | null { | ||||||
| 	if (source == null || source == '') { | 	if (source == null || source == '') { | ||||||
| 		return null; | 		return null; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ import { intersperse } from '../prelude/array'; | ||||||
| import { MfmForest, MfmTree } from './prelude'; | import { MfmForest, MfmTree } from './prelude'; | ||||||
| import { IMentionedRemoteUsers } from '../models/entities/note'; | import { IMentionedRemoteUsers } from '../models/entities/note'; | ||||||
| 
 | 
 | ||||||
| export function toHtml(tokens: MfmForest, mentionedRemoteUsers: IMentionedRemoteUsers = []) { | export function toHtml(tokens: MfmForest | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) { | ||||||
| 	if (tokens == null) { | 	if (tokens == null) { | ||||||
| 		return null; | 		return null; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| type Acct = { | type Acct = { | ||||||
| 	username: string; | 	username: string; | ||||||
| 	host: string; | 	host: string | null; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default Acct; | export default Acct; | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ import config from '../config'; | ||||||
| import { toASCII } from 'punycode'; | import { toASCII } from 'punycode'; | ||||||
| import { URL } from 'url'; | import { URL } from 'url'; | ||||||
| 
 | 
 | ||||||
| export function getFullApAccount(username: string, host: string) { | export function getFullApAccount(username: string, host: string | null) { | ||||||
| 	return host ? `${username}@${toPuny(host)}` : `${username}@${toPuny(config.host)}`; | 	return host ? `${username}@${toPuny(host)}` : `${username}@${toPuny(config.host)}`; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -17,6 +17,5 @@ export function extractDbHost(uri: string) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function toPuny(host: string) { | export function toPuny(host: string) { | ||||||
| 	if (host == null) return null; |  | ||||||
| 	return toASCII(host.toLowerCase()); | 	return toASCII(host.toLowerCase()); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ import fileType from 'file-type'; | ||||||
| import checkSvg from '../misc/check-svg'; | import checkSvg from '../misc/check-svg'; | ||||||
| 
 | 
 | ||||||
| export async function detectMine(path: string) { | export async function detectMine(path: string) { | ||||||
| 	return new Promise<[string, string]>((res, rej) => { | 	return new Promise<[string, string | null]>((res, rej) => { | ||||||
| 		const readable = fs.createReadStream(path); | 		const readable = fs.createReadStream(path); | ||||||
| 		readable | 		readable | ||||||
| 			.on('error', rej) | 			.on('error', rej) | ||||||
|  |  | ||||||
|  | @ -1,5 +1,4 @@ | ||||||
| import * as fs from 'fs'; | import * as fs from 'fs'; | ||||||
| import * as URL from 'url'; |  | ||||||
| import * as request from 'request'; | import * as request from 'request'; | ||||||
| import config from '../config'; | import config from '../config'; | ||||||
| import chalk from 'chalk'; | import chalk from 'chalk'; | ||||||
|  | @ -26,7 +25,7 @@ export async function downloadUrl(url: string, path: string) { | ||||||
| 			rej(error); | 			rej(error); | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		const requestUrl = URL.parse(url).pathname.match(/[^\u0021-\u00ff]/) ? encodeURI(url) : url; | 		const requestUrl = new URL(url).pathname.match(/[^\u0021-\u00ff]/) ? encodeURI(url) : url; | ||||||
| 
 | 
 | ||||||
| 		const req = request({ | 		const req = request({ | ||||||
| 			url: requestUrl, | 			url: requestUrl, | ||||||
|  |  | ||||||
|  | @ -9,7 +9,6 @@ export default async function(): Promise<Meta> { | ||||||
| 	} else { | 	} else { | ||||||
| 		return Metas.save({ | 		return Metas.save({ | ||||||
| 			id: genId(), | 			id: genId(), | ||||||
| 			hiddenTags: [] |  | ||||||
| 		} as Meta); | 		} as Meta); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,8 +1,9 @@ | ||||||
| import fetchMeta from './fetch-meta'; | import fetchMeta from './fetch-meta'; | ||||||
| import { ILocalUser } from '../models/entities/user'; | import { ILocalUser } from '../models/entities/user'; | ||||||
| import { Users } from '../models'; | import { Users } from '../models'; | ||||||
|  | import { ensure } from '../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| export async function fetchProxyAccount(): Promise<ILocalUser> { | export async function fetchProxyAccount(): Promise<ILocalUser> { | ||||||
| 	const meta = await fetchMeta(); | 	const meta = await fetchMeta(); | ||||||
| 	return await Users.findOne({ username: meta.proxyAccount, host: null }) as ILocalUser; | 	return await Users.findOne({ username: meta.proxyAccount!, host: null }).then(ensure) as ILocalUser; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ export class IdentifiableError extends Error { | ||||||
| 
 | 
 | ||||||
| 	constructor(id: string, message?: string) { | 	constructor(id: string, message?: string) { | ||||||
| 		super(message); | 		super(message); | ||||||
| 		this.message = message; | 		this.message = message || ''; | ||||||
| 		this.id = id; | 		this.id = id; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ export function nyaize(text: string): string { | ||||||
| 		// ja-JP
 | 		// ja-JP
 | ||||||
| 		.replace(/な/g, 'にゃ').replace(/ナ/g, 'ニャ').replace(/ナ/g, 'ニャ') | 		.replace(/な/g, 'にゃ').replace(/ナ/g, 'ニャ').replace(/ナ/g, 'ニャ') | ||||||
| 		// ko-KR
 | 		// ko-KR
 | ||||||
| 		.replace(/[나-낳]/g, (match: string) => String.fromCharCode( | 		.replace(/[나-낳]/g, match => String.fromCharCode( | ||||||
| 			match.codePointAt(0)  + '냐'.charCodeAt(0) - '나'.charCodeAt(0) | 			match.codePointAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0) | ||||||
| 		)); | 		)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -19,8 +19,8 @@ type MyType<T extends Schema> = { | ||||||
| export type SchemaType<p extends Schema> = | export type SchemaType<p extends Schema> = | ||||||
| 	p['type'] extends 'number' ? number : | 	p['type'] extends 'number' ? number : | ||||||
| 	p['type'] extends 'string' ? string : | 	p['type'] extends 'string' ? string : | ||||||
| 	p['type'] extends 'array' ? MyType<p['items']>[] : | 	p['type'] extends 'array' ? MyType<NonNullable<p['items']>>[] : | ||||||
| 	p['type'] extends 'object' ? ObjType<p['properties']> : | 	p['type'] extends 'object' ? ObjType<NonNullable<p['properties']>> : | ||||||
| 	any; | 	any; | ||||||
| 
 | 
 | ||||||
| export function convertOpenApiSchema(schema: Schema) { | export function convertOpenApiSchema(schema: Schema) { | ||||||
|  |  | ||||||
|  | @ -67,5 +67,5 @@ export type IPoll = { | ||||||
| 	choices: string[]; | 	choices: string[]; | ||||||
| 	votes?: number[]; | 	votes?: number[]; | ||||||
| 	multiple: boolean; | 	multiple: boolean; | ||||||
| 	expiresAt: Date; | 	expiresAt: Date | null; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { Users } from '..'; | import { Users } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
| import { AbuseUserReport } from '../entities/abuse-user-report'; | import { AbuseUserReport } from '../entities/abuse-user-report'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(AbuseUserReport) | @EntityRepository(AbuseUserReport) | ||||||
| export class AbuseUserReportRepository extends Repository<AbuseUserReport> { | export class AbuseUserReportRepository extends Repository<AbuseUserReport> { | ||||||
|  | @ -14,7 +15,7 @@ export class AbuseUserReportRepository extends Repository<AbuseUserReport> { | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: AbuseUserReport['id'] | AbuseUserReport, | 		src: AbuseUserReport['id'] | AbuseUserReport, | ||||||
| 	) { | 	) { | ||||||
| 		const report = typeof src === 'object' ? src : await this.findOne(src); | 		const report = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return await rap({ | 		return await rap({ | ||||||
| 			id: report.id, | 			id: report.id, | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { EntityRepository, Repository } from 'typeorm'; | import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { App } from '../entities/app'; | import { App } from '../entities/app'; | ||||||
| import { AccessTokens } from '..'; | import { AccessTokens } from '..'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(App) | @EntityRepository(App) | ||||||
| export class AppRepository extends Repository<App> { | export class AppRepository extends Repository<App> { | ||||||
|  | @ -19,7 +20,7 @@ export class AppRepository extends Repository<App> { | ||||||
| 			includeProfileImageIds: false | 			includeProfileImageIds: false | ||||||
| 		}, options); | 		}, options); | ||||||
| 
 | 
 | ||||||
| 		const app = typeof src === 'object' ? src : await this.findOne(src); | 		const app = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
| 			id: app.id, | 			id: app.id, | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { Apps } from '..'; | import { Apps } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
| import { AuthSession } from '../entities/auth-session'; | import { AuthSession } from '../entities/auth-session'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(AuthSession) | @EntityRepository(AuthSession) | ||||||
| export class AuthSessionRepository extends Repository<AuthSession> { | export class AuthSessionRepository extends Repository<AuthSession> { | ||||||
|  | @ -9,7 +10,7 @@ export class AuthSessionRepository extends Repository<AuthSession> { | ||||||
| 		src: AuthSession['id'] | AuthSession, | 		src: AuthSession['id'] | AuthSession, | ||||||
| 		me?: any | 		me?: any | ||||||
| 	) { | 	) { | ||||||
| 		const session = typeof src === 'object' ? src : await this.findOne(src); | 		const session = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return await rap({ | 		return await rap({ | ||||||
| 			id: session.id, | 			id: session.id, | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { Users } from '..'; | import { Users } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
| import { Blocking } from '../entities/blocking'; | import { Blocking } from '../entities/blocking'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(Blocking) | @EntityRepository(Blocking) | ||||||
| export class BlockingRepository extends Repository<Blocking> { | export class BlockingRepository extends Repository<Blocking> { | ||||||
|  | @ -16,7 +17,7 @@ export class BlockingRepository extends Repository<Blocking> { | ||||||
| 		src: Blocking['id'] | Blocking, | 		src: Blocking['id'] | Blocking, | ||||||
| 		me?: any | 		me?: any | ||||||
| 	) { | 	) { | ||||||
| 		const blocking = typeof src === 'object' ? src : await this.findOne(src); | 		const blocking = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return await rap({ | 		return await rap({ | ||||||
| 			id: blocking.id, | 			id: blocking.id, | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import { Users, DriveFolders } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
| import { User } from '../entities/user'; | import { User } from '../entities/user'; | ||||||
| import { toPuny } from '../../misc/convert-host'; | import { toPuny } from '../../misc/convert-host'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(DriveFile) | @EntityRepository(DriveFile) | ||||||
| export class DriveFileRepository extends Repository<DriveFile> { | export class DriveFileRepository extends Repository<DriveFile> { | ||||||
|  | @ -91,7 +92,7 @@ export class DriveFileRepository extends Repository<DriveFile> { | ||||||
| 			self: false | 			self: false | ||||||
| 		}, options); | 		}, options); | ||||||
| 
 | 
 | ||||||
| 		const file = typeof src === 'object' ? src : await this.findOne(src); | 		const file = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return await rap({ | 		return await rap({ | ||||||
| 			id: file.id, | 			id: file.id, | ||||||
|  | @ -108,7 +109,7 @@ export class DriveFileRepository extends Repository<DriveFile> { | ||||||
| 			folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, { | 			folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, { | ||||||
| 				detail: true | 				detail: true | ||||||
| 			}) : null, | 			}) : null, | ||||||
| 			user: opts.withUser ? Users.pack(file.userId) : null | 			user: opts.withUser ? Users.pack(file.userId!) : null | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { DriveFolders, DriveFiles } from '..'; | import { DriveFolders, DriveFiles } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
| import { DriveFolder } from '../entities/drive-folder'; | import { DriveFolder } from '../entities/drive-folder'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(DriveFolder) | @EntityRepository(DriveFolder) | ||||||
| export class DriveFolderRepository extends Repository<DriveFolder> { | export class DriveFolderRepository extends Repository<DriveFolder> { | ||||||
|  | @ -22,7 +23,7 @@ export class DriveFolderRepository extends Repository<DriveFolder> { | ||||||
| 			detail: false | 			detail: false | ||||||
| 		}, options); | 		}, options); | ||||||
| 
 | 
 | ||||||
| 		const folder = typeof src === 'object' ? src : await this.findOne(src); | 		const folder = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return await rap({ | 		return await rap({ | ||||||
| 			id: folder.id, | 			id: folder.id, | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { EntityRepository, Repository } from 'typeorm'; | import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { FollowRequest } from '../entities/follow-request'; | import { FollowRequest } from '../entities/follow-request'; | ||||||
| import { Users } from '..'; | import { Users } from '..'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(FollowRequest) | @EntityRepository(FollowRequest) | ||||||
| export class FollowRequestRepository extends Repository<FollowRequest> { | export class FollowRequestRepository extends Repository<FollowRequest> { | ||||||
|  | @ -8,7 +9,7 @@ export class FollowRequestRepository extends Repository<FollowRequest> { | ||||||
| 		src: FollowRequest['id'] | FollowRequest, | 		src: FollowRequest['id'] | FollowRequest, | ||||||
| 		me?: any | 		me?: any | ||||||
| 	) { | 	) { | ||||||
| 		const request = typeof src === 'object' ? src : await this.findOne(src); | 		const request = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
| 			id: request.id, | 			id: request.id, | ||||||
|  |  | ||||||
|  | @ -2,9 +2,50 @@ import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { Users } from '..'; | import { Users } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
| import { Following } from '../entities/following'; | import { Following } from '../entities/following'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
|  | 
 | ||||||
|  | type LocalFollowerFollowing = Following & { | ||||||
|  | 	followerHost: null; | ||||||
|  | 	followerInbox: null; | ||||||
|  | 	followerSharedInbox: null; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type RemoteFollowerFollowing = Following & { | ||||||
|  | 	followerHost: string; | ||||||
|  | 	followerInbox: string; | ||||||
|  | 	followerSharedInbox: string; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type LocalFolloweeFollowing = Following & { | ||||||
|  | 	followeeHost: null; | ||||||
|  | 	followeeInbox: null; | ||||||
|  | 	followeeSharedInbox: null; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type RemoteFolloweeFollowing = Following & { | ||||||
|  | 	followeeHost: string; | ||||||
|  | 	followeeInbox: string; | ||||||
|  | 	followeeSharedInbox: string; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(Following) | @EntityRepository(Following) | ||||||
| export class FollowingRepository extends Repository<Following> { | export class FollowingRepository extends Repository<Following> { | ||||||
|  | 	public isLocalFollower(following: Following): following is LocalFollowerFollowing { | ||||||
|  | 		return following.followerHost == null; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public isRemoteFollower(following: Following): following is RemoteFollowerFollowing { | ||||||
|  | 		return following.followerHost != null; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public isLocalFollowee(following: Following): following is LocalFolloweeFollowing { | ||||||
|  | 		return following.followeeHost == null; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing { | ||||||
|  | 		return following.followeeHost != null; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	public packMany( | 	public packMany( | ||||||
| 		followings: any[], | 		followings: any[], | ||||||
| 		me?: any, | 		me?: any, | ||||||
|  | @ -24,7 +65,7 @@ export class FollowingRepository extends Repository<Following> { | ||||||
| 			populateFollower?: boolean; | 			populateFollower?: boolean; | ||||||
| 		} | 		} | ||||||
| 	) { | 	) { | ||||||
| 		const following = typeof src === 'object' ? src : await this.findOne(src); | 		const following = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		if (opts == null) opts = {}; | 		if (opts == null) opts = {}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { EntityRepository, Repository } from 'typeorm'; | import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { Users } from '../../..'; | import { Users } from '../../..'; | ||||||
| import { ReversiGame } from '../../../entities/games/reversi/game'; | import { ReversiGame } from '../../../entities/games/reversi/game'; | ||||||
|  | import { ensure } from '../../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(ReversiGame) | @EntityRepository(ReversiGame) | ||||||
| export class ReversiGameRepository extends Repository<ReversiGame> { | export class ReversiGameRepository extends Repository<ReversiGame> { | ||||||
|  | @ -15,7 +16,7 @@ export class ReversiGameRepository extends Repository<ReversiGame> { | ||||||
| 			detail: true | 			detail: true | ||||||
| 		}, options); | 		}, options); | ||||||
| 
 | 
 | ||||||
| 		const game = typeof src === 'object' ? src : await this.findOne(src); | 		const game = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 		const meId = me ? typeof me === 'string' ? me : me.id : null; | 		const meId = me ? typeof me === 'string' ? me : me.id : null; | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
| import { ReversiMatching } from '../../../entities/games/reversi/matching'; | import { ReversiMatching } from '../../../entities/games/reversi/matching'; | ||||||
| import { Users } from '../../..'; | import { Users } from '../../..'; | ||||||
|  | import { ensure } from '../../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(ReversiMatching) | @EntityRepository(ReversiMatching) | ||||||
| export class ReversiMatchingRepository extends Repository<ReversiMatching> { | export class ReversiMatchingRepository extends Repository<ReversiMatching> { | ||||||
|  | @ -9,7 +10,7 @@ export class ReversiMatchingRepository extends Repository<ReversiMatching> { | ||||||
| 		src: ReversiMatching['id'] | ReversiMatching, | 		src: ReversiMatching['id'] | ReversiMatching, | ||||||
| 		me: any | 		me: any | ||||||
| 	) { | 	) { | ||||||
| 		const matching = typeof src === 'object' ? src : await this.findOne(src); | 		const matching = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return await rap({ | 		return await rap({ | ||||||
| 			id: matching.id, | 			id: matching.id, | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { EntityRepository, Repository } from 'typeorm'; | import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { MessagingMessage } from '../entities/messaging-message'; | import { MessagingMessage } from '../entities/messaging-message'; | ||||||
| import { Users, DriveFiles } from '..'; | import { Users, DriveFiles } from '..'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(MessagingMessage) | @EntityRepository(MessagingMessage) | ||||||
| export class MessagingMessageRepository extends Repository<MessagingMessage> { | export class MessagingMessageRepository extends Repository<MessagingMessage> { | ||||||
|  | @ -19,7 +20,7 @@ export class MessagingMessageRepository extends Repository<MessagingMessage> { | ||||||
| 			populateRecipient: true | 			populateRecipient: true | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		const message = typeof src === 'object' ? src : await this.findOne(src); | 		const message = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
| 			id: message.id, | 			id: message.id, | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { Users } from '..'; | import { Users } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
| import { Muting } from '../entities/muting'; | import { Muting } from '../entities/muting'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(Muting) | @EntityRepository(Muting) | ||||||
| export class MutingRepository extends Repository<Muting> { | export class MutingRepository extends Repository<Muting> { | ||||||
|  | @ -16,7 +17,7 @@ export class MutingRepository extends Repository<Muting> { | ||||||
| 		src: Muting['id'] | Muting, | 		src: Muting['id'] | Muting, | ||||||
| 		me?: any | 		me?: any | ||||||
| 	) { | 	) { | ||||||
| 		const muting = typeof src === 'object' ? src : await this.findOne(src); | 		const muting = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return await rap({ | 		return await rap({ | ||||||
| 			id: muting.id, | 			id: muting.id, | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { EntityRepository, Repository } from 'typeorm'; | import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { NoteFavorite } from '../entities/note-favorite'; | import { NoteFavorite } from '../entities/note-favorite'; | ||||||
| import { Notes } from '..'; | import { Notes } from '..'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(NoteFavorite) | @EntityRepository(NoteFavorite) | ||||||
| export class NoteFavoriteRepository extends Repository<NoteFavorite> { | export class NoteFavoriteRepository extends Repository<NoteFavorite> { | ||||||
|  | @ -15,7 +16,7 @@ export class NoteFavoriteRepository extends Repository<NoteFavorite> { | ||||||
| 		src: NoteFavorite['id'] | NoteFavorite, | 		src: NoteFavorite['id'] | NoteFavorite, | ||||||
| 		me?: any | 		me?: any | ||||||
| 	) { | 	) { | ||||||
| 		const favorite = typeof src === 'object' ? src : await this.findOne(src); | 		const favorite = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
| 			id: favorite.id, | 			id: favorite.id, | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { EntityRepository, Repository } from 'typeorm'; | import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { NoteReaction } from '../entities/note-reaction'; | import { NoteReaction } from '../entities/note-reaction'; | ||||||
| import { Users } from '..'; | import { Users } from '..'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(NoteReaction) | @EntityRepository(NoteReaction) | ||||||
| export class NoteReactionRepository extends Repository<NoteReaction> { | export class NoteReactionRepository extends Repository<NoteReaction> { | ||||||
|  | @ -8,7 +9,7 @@ export class NoteReactionRepository extends Repository<NoteReaction> { | ||||||
| 		src: NoteReaction['id'] | NoteReaction, | 		src: NoteReaction['id'] | NoteReaction, | ||||||
| 		me?: any | 		me?: any | ||||||
| 	) { | 	) { | ||||||
| 		const reaction = typeof src === 'object' ? src : await this.findOne(src); | 		const reaction = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
| 			id: reaction.id, | 			id: reaction.id, | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import { unique, concat } from '../../prelude/array'; | ||||||
| import { nyaize } from '../../misc/nyaize'; | import { nyaize } from '../../misc/nyaize'; | ||||||
| import { Emojis, Users, Apps, PollVotes, DriveFiles, NoteReactions, Followings, Polls } from '..'; | import { Emojis, Users, Apps, PollVotes, DriveFiles, NoteReactions, Followings, Polls } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(Note) | @EntityRepository(Note) | ||||||
| export class NoteRepository extends Repository<Note> { | export class NoteRepository extends Repository<Note> { | ||||||
|  | @ -12,7 +13,7 @@ export class NoteRepository extends Repository<Note> { | ||||||
| 		return x.trim().length <= 100; | 		return x.trim().length <= 100; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private async hideNote(packedNote: any, meId: User['id']) { | 	private async hideNote(packedNote: any, meId: User['id'] | null) { | ||||||
| 		let hide = false; | 		let hide = false; | ||||||
| 
 | 
 | ||||||
| 		// visibility が specified かつ自分が指定されていなかったら非表示
 | 		// visibility が specified かつ自分が指定されていなかったら非表示
 | ||||||
|  | @ -75,7 +76,7 @@ export class NoteRepository extends Repository<Note> { | ||||||
| 
 | 
 | ||||||
| 	public packMany( | 	public packMany( | ||||||
| 		notes: (Note['id'] | Note)[], | 		notes: (Note['id'] | Note)[], | ||||||
| 		me?: User['id'] | User, | 		me?: User['id'] | User | null | undefined, | ||||||
| 		options?: { | 		options?: { | ||||||
| 			detail?: boolean; | 			detail?: boolean; | ||||||
| 			skipHide?: boolean; | 			skipHide?: boolean; | ||||||
|  | @ -86,7 +87,7 @@ export class NoteRepository extends Repository<Note> { | ||||||
| 
 | 
 | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: Note['id'] | Note, | 		src: Note['id'] | Note, | ||||||
| 		me?: User['id'] | User, | 		me?: User['id'] | User | null | undefined, | ||||||
| 		options?: { | 		options?: { | ||||||
| 			detail?: boolean; | 			detail?: boolean; | ||||||
| 			skipHide?: boolean; | 			skipHide?: boolean; | ||||||
|  | @ -98,11 +99,11 @@ export class NoteRepository extends Repository<Note> { | ||||||
| 		}, options); | 		}, options); | ||||||
| 
 | 
 | ||||||
| 		const meId = me ? typeof me === 'string' ? me : me.id : null; | 		const meId = me ? typeof me === 'string' ? me : me.id : null; | ||||||
| 		const note = typeof src === 'object' ? src : await this.findOne(src); | 		const note = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 		const host = note.userHost; | 		const host = note.userHost; | ||||||
| 
 | 
 | ||||||
| 		async function populatePoll() { | 		async function populatePoll() { | ||||||
| 			const poll = await Polls.findOne({ noteId: note.id }); | 			const poll = await Polls.findOne({ noteId: note.id }).then(ensure); | ||||||
| 			const choices = poll.choices.map(c => ({ | 			const choices = poll.choices.map(c => ({ | ||||||
| 				text: c, | 				text: c, | ||||||
| 				votes: poll.votes[poll.choices.indexOf(c)], | 				votes: poll.votes[poll.choices.indexOf(c)], | ||||||
|  | @ -111,7 +112,7 @@ export class NoteRepository extends Repository<Note> { | ||||||
| 
 | 
 | ||||||
| 			if (poll.multiple) { | 			if (poll.multiple) { | ||||||
| 				const votes = await PollVotes.find({ | 				const votes = await PollVotes.find({ | ||||||
| 					userId: meId, | 					userId: meId!, | ||||||
| 					noteId: note.id | 					noteId: note.id | ||||||
| 				}); | 				}); | ||||||
| 
 | 
 | ||||||
|  | @ -121,7 +122,7 @@ export class NoteRepository extends Repository<Note> { | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				const vote = await PollVotes.findOne({ | 				const vote = await PollVotes.findOne({ | ||||||
| 					userId: meId, | 					userId: meId!, | ||||||
| 					noteId: note.id | 					noteId: note.id | ||||||
| 				}); | 				}); | ||||||
| 
 | 
 | ||||||
|  | @ -139,7 +140,7 @@ export class NoteRepository extends Repository<Note> { | ||||||
| 
 | 
 | ||||||
| 		async function populateMyReaction() { | 		async function populateMyReaction() { | ||||||
| 			const reaction = await NoteReactions.findOne({ | 			const reaction = await NoteReactions.findOne({ | ||||||
| 				userId: meId, | 				userId: meId!, | ||||||
| 				noteId: note.id, | 				noteId: note.id, | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { Users, Notes } from '..'; | import { Users, Notes } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
| import { Notification } from '../entities/notification'; | import { Notification } from '../entities/notification'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(Notification) | @EntityRepository(Notification) | ||||||
| export class NotificationRepository extends Repository<Notification> { | export class NotificationRepository extends Repository<Notification> { | ||||||
|  | @ -14,7 +15,7 @@ export class NotificationRepository extends Repository<Notification> { | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: Notification['id'] | Notification, | 		src: Notification['id'] | Notification, | ||||||
| 	) { | 	) { | ||||||
| 		const notification = typeof src === 'object' ? src : await this.findOne(src); | 		const notification = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return await rap({ | 		return await rap({ | ||||||
| 			id: notification.id, | 			id: notification.id, | ||||||
|  | @ -23,23 +24,23 @@ export class NotificationRepository extends Repository<Notification> { | ||||||
| 			userId: notification.notifierId, | 			userId: notification.notifierId, | ||||||
| 			user: Users.pack(notification.notifier || notification.notifierId), | 			user: Users.pack(notification.notifier || notification.notifierId), | ||||||
| 			...(notification.type === 'mention' ? { | 			...(notification.type === 'mention' ? { | ||||||
| 				note: Notes.pack(notification.note || notification.noteId), | 				note: Notes.pack(notification.note || notification.noteId!), | ||||||
| 			} : {}), | 			} : {}), | ||||||
| 			...(notification.type === 'reply' ? { | 			...(notification.type === 'reply' ? { | ||||||
| 				note: Notes.pack(notification.note || notification.noteId), | 				note: Notes.pack(notification.note || notification.noteId!), | ||||||
| 			} : {}), | 			} : {}), | ||||||
| 			...(notification.type === 'renote' ? { | 			...(notification.type === 'renote' ? { | ||||||
| 				note: Notes.pack(notification.note || notification.noteId), | 				note: Notes.pack(notification.note || notification.noteId!), | ||||||
| 			} : {}), | 			} : {}), | ||||||
| 			...(notification.type === 'quote' ? { | 			...(notification.type === 'quote' ? { | ||||||
| 				note: Notes.pack(notification.note || notification.noteId), | 				note: Notes.pack(notification.note || notification.noteId!), | ||||||
| 			} : {}), | 			} : {}), | ||||||
| 			...(notification.type === 'reaction' ? { | 			...(notification.type === 'reaction' ? { | ||||||
| 				note: Notes.pack(notification.note || notification.noteId), | 				note: Notes.pack(notification.note || notification.noteId!), | ||||||
| 				reaction: notification.reaction | 				reaction: notification.reaction | ||||||
| 			} : {}), | 			} : {}), | ||||||
| 			...(notification.type === 'pollVote' ? { | 			...(notification.type === 'pollVote' ? { | ||||||
| 				note: Notes.pack(notification.note || notification.noteId), | 				note: Notes.pack(notification.note || notification.noteId!), | ||||||
| 				choice: notification.choice | 				choice: notification.choice | ||||||
| 			} : {}) | 			} : {}) | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
|  | @ -1,12 +1,13 @@ | ||||||
| import { EntityRepository, Repository } from 'typeorm'; | import { EntityRepository, Repository } from 'typeorm'; | ||||||
| import { UserList } from '../entities/user-list'; | import { UserList } from '../entities/user-list'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(UserList) | @EntityRepository(UserList) | ||||||
| export class UserListRepository extends Repository<UserList> { | export class UserListRepository extends Repository<UserList> { | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: any, | 		src: UserList['id'] | UserList, | ||||||
| 	) { | 	) { | ||||||
| 		const userList = typeof src === 'object' ? src : await this.findOne(src); | 		const userList = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		return { | 		return { | ||||||
| 			id: userList.id, | 			id: userList.id, | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { EntityRepository, Repository, In } from 'typeorm'; | ||||||
| import { User, ILocalUser, IRemoteUser } from '../entities/user'; | import { User, ILocalUser, IRemoteUser } from '../entities/user'; | ||||||
| import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..'; | import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..'; | ||||||
| import rap from '@prezzemolo/rap'; | import rap from '@prezzemolo/rap'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| @EntityRepository(User) | @EntityRepository(User) | ||||||
| export class UserRepository extends Repository<User> { | export class UserRepository extends Repository<User> { | ||||||
|  | @ -51,7 +52,7 @@ export class UserRepository extends Repository<User> { | ||||||
| 
 | 
 | ||||||
| 	public packMany( | 	public packMany( | ||||||
| 		users: (User['id'] | User)[], | 		users: (User['id'] | User)[], | ||||||
| 		me?: User['id'] | User, | 		me?: User['id'] | User | null | undefined, | ||||||
| 		options?: { | 		options?: { | ||||||
| 			detail?: boolean, | 			detail?: boolean, | ||||||
| 			includeSecrets?: boolean, | 			includeSecrets?: boolean, | ||||||
|  | @ -63,7 +64,7 @@ export class UserRepository extends Repository<User> { | ||||||
| 
 | 
 | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: User['id'] | User, | 		src: User['id'] | User, | ||||||
| 		me?: User['id'] | User, | 		me?: User['id'] | User | null | undefined, | ||||||
| 		options?: { | 		options?: { | ||||||
| 			detail?: boolean, | 			detail?: boolean, | ||||||
| 			includeSecrets?: boolean, | 			includeSecrets?: boolean, | ||||||
|  | @ -75,12 +76,12 @@ export class UserRepository extends Repository<User> { | ||||||
| 			includeSecrets: false | 			includeSecrets: false | ||||||
| 		}, options); | 		}, options); | ||||||
| 
 | 
 | ||||||
| 		const user = typeof src === 'object' ? src : await this.findOne(src); | 		const user = typeof src === 'object' ? src : await this.findOne(src).then(ensure); | ||||||
| 		const meId = me ? typeof me === 'string' ? me : me.id : null; | 		const meId = me ? typeof me === 'string' ? me : me.id : null; | ||||||
| 
 | 
 | ||||||
| 		const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null; | 		const relation = meId && (meId !== user.id) && opts.detail ? await this.getRelation(meId, user.id) : null; | ||||||
| 		const pins = opts.detail ? await UserNotePinings.find({ userId: user.id }) : []; | 		const pins = opts.detail ? await UserNotePinings.find({ userId: user.id }) : []; | ||||||
| 		const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }) : null; | 		const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }).then(ensure) : null; | ||||||
| 
 | 
 | ||||||
| 		return await rap({ | 		return await rap({ | ||||||
| 			id: user.id, | 			id: user.id, | ||||||
|  | @ -117,12 +118,12 @@ export class UserRepository extends Repository<User> { | ||||||
| 			} : {}), | 			} : {}), | ||||||
| 
 | 
 | ||||||
| 			...(opts.detail ? { | 			...(opts.detail ? { | ||||||
| 				url: profile.url, | 				url: profile!.url, | ||||||
| 				createdAt: user.createdAt, | 				createdAt: user.createdAt, | ||||||
| 				updatedAt: user.updatedAt, | 				updatedAt: user.updatedAt, | ||||||
| 				description: profile.description, | 				description: profile!.description, | ||||||
| 				location: profile.location, | 				location: profile!.location, | ||||||
| 				birthday: profile.birthday, | 				birthday: profile!.birthday, | ||||||
| 				followersCount: user.followersCount, | 				followersCount: user.followersCount, | ||||||
| 				followingCount: user.followingCount, | 				followingCount: user.followingCount, | ||||||
| 				notesCount: user.notesCount, | 				notesCount: user.notesCount, | ||||||
|  | @ -135,9 +136,9 @@ export class UserRepository extends Repository<User> { | ||||||
| 			...(opts.detail && meId === user.id ? { | 			...(opts.detail && meId === user.id ? { | ||||||
| 				avatarId: user.avatarId, | 				avatarId: user.avatarId, | ||||||
| 				bannerId: user.bannerId, | 				bannerId: user.bannerId, | ||||||
| 				autoWatch: profile.autoWatch, | 				autoWatch: profile!.autoWatch, | ||||||
| 				alwaysMarkNsfw: profile.alwaysMarkNsfw, | 				alwaysMarkNsfw: profile!.alwaysMarkNsfw, | ||||||
| 				carefulBot: profile.carefulBot, | 				carefulBot: profile!.carefulBot, | ||||||
| 				hasUnreadMessagingMessage: MessagingMessages.count({ | 				hasUnreadMessagingMessage: MessagingMessages.count({ | ||||||
| 					where: { | 					where: { | ||||||
| 						recipientId: user.id, | 						recipientId: user.id, | ||||||
|  | @ -158,9 +159,9 @@ export class UserRepository extends Repository<User> { | ||||||
| 			} : {}), | 			} : {}), | ||||||
| 
 | 
 | ||||||
| 			...(opts.includeSecrets ? { | 			...(opts.includeSecrets ? { | ||||||
| 				clientData: profile.clientData, | 				clientData: profile!.clientData, | ||||||
| 				email: profile.email, | 				email: profile!.email, | ||||||
| 				emailVerified: profile.emailVerified, | 				emailVerified: profile!.emailVerified, | ||||||
| 			} : {}), | 			} : {}), | ||||||
| 
 | 
 | ||||||
| 			...(relation ? { | 			...(relation ? { | ||||||
|  |  | ||||||
							
								
								
									
										7
									
								
								src/prelude/ensure.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/prelude/ensure.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | export function ensure<T>(x: T): NonNullable<T> { | ||||||
|  | 	if (x == null) { | ||||||
|  | 		throw 'ぬるぽ'; | ||||||
|  | 	} else { | ||||||
|  | 		return x!; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -12,7 +12,7 @@ import { queueLogger } from './logger'; | ||||||
| import { DriveFile } from '../models/entities/drive-file'; | import { DriveFile } from '../models/entities/drive-file'; | ||||||
| 
 | 
 | ||||||
| function initializeQueue(name: string) { | function initializeQueue(name: string) { | ||||||
| 	return new Queue(name, config.redis != null ? { | 	return new Queue(name, { | ||||||
| 		redis: { | 		redis: { | ||||||
| 			port: config.redis.port, | 			port: config.redis.port, | ||||||
| 			host: config.redis.host, | 			host: config.redis.host, | ||||||
|  | @ -20,7 +20,7 @@ function initializeQueue(name: string) { | ||||||
| 			db: config.redis.db || 0, | 			db: config.redis.db || 0, | ||||||
| 		}, | 		}, | ||||||
| 		prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue' | 		prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue' | ||||||
| 	} : null); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const deliverQueue = initializeQueue('deliver'); | export const deliverQueue = initializeQueue('deliver'); | ||||||
|  |  | ||||||
|  | @ -10,9 +10,11 @@ const logger = queueLogger.createSubLogger('delete-drive-files'); | ||||||
| export async function deleteDriveFiles(job: Bull.Job, done: any): Promise<void> { | export async function deleteDriveFiles(job: Bull.Job, done: any): Promise<void> { | ||||||
| 	logger.info(`Deleting drive files of ${job.data.user.id} ...`); | 	logger.info(`Deleting drive files of ${job.data.user.id} ...`); | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne(job.data.user.id); | ||||||
| 		id: job.data.user.id | 	if (user == null) { | ||||||
| 	}); | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	let deletedCount = 0; | 	let deletedCount = 0; | ||||||
| 	let ended = false; | 	let ended = false; | ||||||
|  |  | ||||||
|  | @ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-blocking'); | ||||||
| export async function exportBlocking(job: Bull.Job, done: any): Promise<void> { | export async function exportBlocking(job: Bull.Job, done: any): Promise<void> { | ||||||
| 	logger.info(`Exporting blocking of ${job.data.user.id} ...`); | 	logger.info(`Exporting blocking of ${job.data.user.id} ...`); | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne(job.data.user.id); | ||||||
| 		id: job.data.user.id | 	if (user == null) { | ||||||
| 	}); | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// Create temp file
 | 	// Create temp file
 | ||||||
| 	const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { | 	const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { | ||||||
|  | @ -56,6 +58,10 @@ export async function exportBlocking(job: Bull.Job, done: any): Promise<void> { | ||||||
| 
 | 
 | ||||||
| 		for (const block of blockings) { | 		for (const block of blockings) { | ||||||
| 			const u = await Users.findOne({ id: block.blockeeId }); | 			const u = await Users.findOne({ id: block.blockeeId }); | ||||||
|  | 			if (u == null) { | ||||||
|  | 				exportedCount++; continue; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			const content = getFullApAccount(u.username, u.host); | 			const content = getFullApAccount(u.username, u.host); | ||||||
| 			await new Promise((res, rej) => { | 			await new Promise((res, rej) => { | ||||||
| 				stream.write(content + '\n', err => { | 				stream.write(content + '\n', err => { | ||||||
|  |  | ||||||
|  | @ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-following'); | ||||||
| export async function exportFollowing(job: Bull.Job, done: any): Promise<void> { | export async function exportFollowing(job: Bull.Job, done: any): Promise<void> { | ||||||
| 	logger.info(`Exporting following of ${job.data.user.id} ...`); | 	logger.info(`Exporting following of ${job.data.user.id} ...`); | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne(job.data.user.id); | ||||||
| 		id: job.data.user.id | 	if (user == null) { | ||||||
| 	}); | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// Create temp file
 | 	// Create temp file
 | ||||||
| 	const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { | 	const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { | ||||||
|  | @ -56,6 +58,10 @@ export async function exportFollowing(job: Bull.Job, done: any): Promise<void> { | ||||||
| 
 | 
 | ||||||
| 		for (const following of followings) { | 		for (const following of followings) { | ||||||
| 			const u = await Users.findOne({ id: following.followeeId }); | 			const u = await Users.findOne({ id: following.followeeId }); | ||||||
|  | 			if (u == null) { | ||||||
|  | 				exportedCount++; continue; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			const content = getFullApAccount(u.username, u.host); | 			const content = getFullApAccount(u.username, u.host); | ||||||
| 			await new Promise((res, rej) => { | 			await new Promise((res, rej) => { | ||||||
| 				stream.write(content + '\n', err => { | 				stream.write(content + '\n', err => { | ||||||
|  |  | ||||||
|  | @ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-mute'); | ||||||
| export async function exportMute(job: Bull.Job, done: any): Promise<void> { | export async function exportMute(job: Bull.Job, done: any): Promise<void> { | ||||||
| 	logger.info(`Exporting mute of ${job.data.user.id} ...`); | 	logger.info(`Exporting mute of ${job.data.user.id} ...`); | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne(job.data.user.id); | ||||||
| 		id: job.data.user.id | 	if (user == null) { | ||||||
| 	}); | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// Create temp file
 | 	// Create temp file
 | ||||||
| 	const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { | 	const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { | ||||||
|  | @ -56,6 +58,10 @@ export async function exportMute(job: Bull.Job, done: any): Promise<void> { | ||||||
| 
 | 
 | ||||||
| 		for (const mute of mutes) { | 		for (const mute of mutes) { | ||||||
| 			const u = await Users.findOne({ id: mute.muteeId }); | 			const u = await Users.findOne({ id: mute.muteeId }); | ||||||
|  | 			if (u == null) { | ||||||
|  | 				exportedCount++; continue; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
| 			const content = getFullApAccount(u.username, u.host); | 			const content = getFullApAccount(u.username, u.host); | ||||||
| 			await new Promise((res, rej) => { | 			await new Promise((res, rej) => { | ||||||
| 				stream.write(content + '\n', err => { | 				stream.write(content + '\n', err => { | ||||||
|  |  | ||||||
|  | @ -9,15 +9,18 @@ import { Users, Notes, Polls } from '../../../models'; | ||||||
| import { MoreThan } from 'typeorm'; | import { MoreThan } from 'typeorm'; | ||||||
| import { Note } from '../../../models/entities/note'; | import { Note } from '../../../models/entities/note'; | ||||||
| import { Poll } from '../../../models/entities/poll'; | import { Poll } from '../../../models/entities/poll'; | ||||||
|  | import { ensure } from '../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| const logger = queueLogger.createSubLogger('export-notes'); | const logger = queueLogger.createSubLogger('export-notes'); | ||||||
| 
 | 
 | ||||||
| export async function exportNotes(job: Bull.Job, done: any): Promise<void> { | export async function exportNotes(job: Bull.Job, done: any): Promise<void> { | ||||||
| 	logger.info(`Exporting notes of ${job.data.user.id} ...`); | 	logger.info(`Exporting notes of ${job.data.user.id} ...`); | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne(job.data.user.id); | ||||||
| 		id: job.data.user.id | 	if (user == null) { | ||||||
| 	}); | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// Create temp file
 | 	// Create temp file
 | ||||||
| 	const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { | 	const [path, cleanup] = await new Promise<[string, any]>((res, rej) => { | ||||||
|  | @ -67,9 +70,9 @@ export async function exportNotes(job: Bull.Job, done: any): Promise<void> { | ||||||
| 		cursor = notes[notes.length - 1].id; | 		cursor = notes[notes.length - 1].id; | ||||||
| 
 | 
 | ||||||
| 		for (const note of notes) { | 		for (const note of notes) { | ||||||
| 			let poll: Poll; | 			let poll: Poll | undefined; | ||||||
| 			if (note.hasPoll) { | 			if (note.hasPoll) { | ||||||
| 				poll = await Polls.findOne({ noteId: note.id }); | 				poll = await Polls.findOne({ noteId: note.id }).then(ensure); | ||||||
| 			} | 			} | ||||||
| 			const content = JSON.stringify(serialize(note, poll)); | 			const content = JSON.stringify(serialize(note, poll)); | ||||||
| 			await new Promise((res, rej) => { | 			await new Promise((res, rej) => { | ||||||
|  | @ -114,7 +117,7 @@ export async function exportNotes(job: Bull.Job, done: any): Promise<void> { | ||||||
| 	done(); | 	done(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function serialize(note: Note, poll: Poll): any { | function serialize(note: Note, poll: Poll | null = null): any { | ||||||
| 	return { | 	return { | ||||||
| 		id: note.id, | 		id: note.id, | ||||||
| 		text: note.text, | 		text: note.text, | ||||||
|  |  | ||||||
|  | @ -14,9 +14,11 @@ const logger = queueLogger.createSubLogger('export-user-lists'); | ||||||
| export async function exportUserLists(job: Bull.Job, done: any): Promise<void> { | export async function exportUserLists(job: Bull.Job, done: any): Promise<void> { | ||||||
| 	logger.info(`Exporting user lists of ${job.data.user.id} ...`); | 	logger.info(`Exporting user lists of ${job.data.user.id} ...`); | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne(job.data.user.id); | ||||||
| 		id: job.data.user.id | 	if (user == null) { | ||||||
| 	}); | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	const lists = await UserLists.find({ | 	const lists = await UserLists.find({ | ||||||
| 		userId: user.id | 		userId: user.id | ||||||
|  |  | ||||||
|  | @ -13,13 +13,19 @@ const logger = queueLogger.createSubLogger('import-following'); | ||||||
| export async function importFollowing(job: Bull.Job, done: any): Promise<void> { | export async function importFollowing(job: Bull.Job, done: any): Promise<void> { | ||||||
| 	logger.info(`Importing following of ${job.data.user.id} ...`); | 	logger.info(`Importing following of ${job.data.user.id} ...`); | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne(job.data.user.id); | ||||||
| 		id: job.data.user.id | 	if (user == null) { | ||||||
| 	}); | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	const file = await DriveFiles.findOne({ | 	const file = await DriveFiles.findOne({ | ||||||
| 		id: job.data.fileId | 		id: job.data.fileId | ||||||
| 	}); | 	}); | ||||||
|  | 	if (file == null) { | ||||||
|  | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	const csv = await downloadTextFile(file.url); | 	const csv = await downloadTextFile(file.url); | ||||||
| 
 | 
 | ||||||
|  | @ -31,11 +37,11 @@ export async function importFollowing(job: Bull.Job, done: any): Promise<void> { | ||||||
| 		try { | 		try { | ||||||
| 			const { username, host } = parseAcct(line.trim()); | 			const { username, host } = parseAcct(line.trim()); | ||||||
| 
 | 
 | ||||||
| 			let target = isSelfHost(host) ? await Users.findOne({ | 			let target = isSelfHost(host!) ? await Users.findOne({ | ||||||
| 				host: null, | 				host: null, | ||||||
| 				usernameLower: username.toLowerCase() | 				usernameLower: username.toLowerCase() | ||||||
| 			}) : await Users.findOne({ | 			}) : await Users.findOne({ | ||||||
| 				host: toPuny(host), | 				host: toPuny(host!), | ||||||
| 				usernameLower: username.toLowerCase() | 				usernameLower: username.toLowerCase() | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,13 +14,19 @@ const logger = queueLogger.createSubLogger('import-user-lists'); | ||||||
| export async function importUserLists(job: Bull.Job, done: any): Promise<void> { | export async function importUserLists(job: Bull.Job, done: any): Promise<void> { | ||||||
| 	logger.info(`Importing user lists of ${job.data.user.id} ...`); | 	logger.info(`Importing user lists of ${job.data.user.id} ...`); | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne(job.data.user.id); | ||||||
| 		id: job.data.user.id | 	if (user == null) { | ||||||
| 	}); | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	const file = await DriveFiles.findOne({ | 	const file = await DriveFiles.findOne({ | ||||||
| 		id: job.data.fileId | 		id: job.data.fileId | ||||||
| 	}); | 	}); | ||||||
|  | 	if (file == null) { | ||||||
|  | 		done(); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	const csv = await downloadTextFile(file.url); | 	const csv = await downloadTextFile(file.url); | ||||||
| 
 | 
 | ||||||
|  | @ -43,22 +49,20 @@ export async function importUserLists(job: Bull.Job, done: any): Promise<void> { | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		let target = isSelfHost(host) ? await Users.findOne({ | 		let target = isSelfHost(host!) ? await Users.findOne({ | ||||||
| 			host: null, | 			host: null, | ||||||
| 			usernameLower: username.toLowerCase() | 			usernameLower: username.toLowerCase() | ||||||
| 		}) : await Users.findOne({ | 		}) : await Users.findOne({ | ||||||
| 			host: toPuny(host), | 			host: toPuny(host!), | ||||||
| 			usernameLower: username.toLowerCase() | 			usernameLower: username.toLowerCase() | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		if (host == null && target == null) continue; |  | ||||||
| 
 |  | ||||||
| 		if (await UserListJoinings.findOne({ userListId: list.id, userId: target.id }) != null) continue; |  | ||||||
| 
 |  | ||||||
| 		if (target == null) { | 		if (target == null) { | ||||||
| 			target = await resolveUser(username, host); | 			target = await resolveUser(username, host); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		if (await UserListJoinings.findOne({ userListId: list.id, userId: target.id }) != null) continue; | ||||||
|  | 
 | ||||||
| 		pushUserToUserList(target, list); | 		pushUserToUserList(target, list); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ import { instanceChart } from '../../services/chart'; | ||||||
| 
 | 
 | ||||||
| const logger = new Logger('deliver'); | const logger = new Logger('deliver'); | ||||||
| 
 | 
 | ||||||
| let latest: string = null; | let latest: string | null = null; | ||||||
| 
 | 
 | ||||||
| export default async (job: Bull.Job) => { | export default async (job: Bull.Job) => { | ||||||
| 	const { host } = new URL(job.data.to); | 	const { host } = new URL(job.data.to); | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ import { UserPublickey } from '../../models/entities/user-publickey'; | ||||||
| import fetchMeta from '../../misc/fetch-meta'; | import fetchMeta from '../../misc/fetch-meta'; | ||||||
| import { toPuny } from '../../misc/convert-host'; | import { toPuny } from '../../misc/convert-host'; | ||||||
| import { validActor } from '../../remote/activitypub/type'; | import { validActor } from '../../remote/activitypub/type'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| const logger = new Logger('inbox'); | const logger = new Logger('inbox'); | ||||||
| 
 | 
 | ||||||
|  | @ -35,7 +36,7 @@ export default async (job: Bull.Job): Promise<void> => { | ||||||
| 
 | 
 | ||||||
| 	if (keyIdLower.startsWith('acct:')) { | 	if (keyIdLower.startsWith('acct:')) { | ||||||
| 		const acct = parseAcct(keyIdLower.slice('acct:'.length)); | 		const acct = parseAcct(keyIdLower.slice('acct:'.length)); | ||||||
| 		const host = toPuny(acct.host); | 		const host = acct.host ? toPuny(acct.host) : null; | ||||||
| 		const username = toPuny(acct.username); | 		const username = toPuny(acct.username); | ||||||
| 
 | 
 | ||||||
| 		if (host === null) { | 		if (host === null) { | ||||||
|  | @ -64,9 +65,7 @@ export default async (job: Bull.Job): Promise<void> => { | ||||||
| 			host: host | 			host: host | ||||||
| 		}) as IRemoteUser; | 		}) as IRemoteUser; | ||||||
| 
 | 
 | ||||||
| 		key = await UserPublickeys.findOne({ | 		key = await UserPublickeys.findOne(user.id).then(ensure); | ||||||
| 			userId: user.id |  | ||||||
| 		}); |  | ||||||
| 	} else { | 	} else { | ||||||
| 		// アクティビティ内のホストの検証
 | 		// アクティビティ内のホストの検証
 | ||||||
| 		const host = toPuny(new URL(signature.keyId).hostname); | 		const host = toPuny(new URL(signature.keyId).hostname); | ||||||
|  | @ -87,7 +86,7 @@ export default async (job: Bull.Job): Promise<void> => { | ||||||
| 
 | 
 | ||||||
| 		key = await UserPublickeys.findOne({ | 		key = await UserPublickeys.findOne({ | ||||||
| 			keyId: signature.keyId | 			keyId: signature.keyId | ||||||
| 		}); | 		}).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		user = await Users.findOne(key.userId) as IRemoteUser; | 		user = await Users.findOne(key.userId) as IRemoteUser; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -6,9 +6,10 @@ import { Users } from '../../../../models'; | ||||||
| 
 | 
 | ||||||
| export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => { | export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => { | ||||||
| 	const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id; | 	const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id; | ||||||
|  | 	if (id == null) throw 'missing id'; | ||||||
| 
 | 
 | ||||||
| 	if (!id.startsWith(config.url + '/')) { | 	if (!id.startsWith(config.url + '/')) { | ||||||
| 		return null; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const follower = await Users.findOne({ | 	const follower = await Users.findOne({ | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ export default async (actor: IRemoteUser, activity: IAdd): Promise<void> => { | ||||||
| 
 | 
 | ||||||
| 	if (activity.target === actor.featured) { | 	if (activity.target === actor.featured) { | ||||||
| 		const note = await resolveNote(activity.object); | 		const note = await resolveNote(activity.object); | ||||||
|  | 		if (note == null) throw new Error('note not found'); | ||||||
| 		await addPinned(actor, note.id); | 		await addPinned(actor, note.id); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -53,16 +53,16 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity: | ||||||
| 	logger.info(`Creating the (Re)Note: ${uri}`); | 	logger.info(`Creating the (Re)Note: ${uri}`); | ||||||
| 
 | 
 | ||||||
| 	//#region Visibility
 | 	//#region Visibility
 | ||||||
| 	const visibility = getVisibility(activity.to, activity.cc, actor); | 	const visibility = getVisibility(activity.to || [], activity.cc || [], actor); | ||||||
| 
 | 
 | ||||||
| 	let visibleUsers: User[] = []; | 	let visibleUsers: User[] = []; | ||||||
| 	if (visibility == 'specified') { | 	if (visibility == 'specified') { | ||||||
| 		visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri))); | 		visibleUsers = await Promise.all((note.to || []).map(uri => resolvePerson(uri))); | ||||||
| 	} | 	} | ||||||
| 	//#endergion
 | 	//#endergion
 | ||||||
| 
 | 
 | ||||||
| 	await post(actor, { | 	await post(actor, { | ||||||
| 		createdAt: new Date(activity.published), | 		createdAt: activity.published ? new Date(activity.published) : null, | ||||||
| 		renote, | 		renote, | ||||||
| 		visibility, | 		visibility, | ||||||
| 		visibleUsers, | 		visibleUsers, | ||||||
|  | @ -75,9 +75,6 @@ type visibility = 'public' | 'home' | 'followers' | 'specified'; | ||||||
| function getVisibility(to: string[], cc: string[], actor: IRemoteUser): visibility { | function getVisibility(to: string[], cc: string[], actor: IRemoteUser): visibility { | ||||||
| 	const PUBLIC = 'https://www.w3.org/ns/activitystreams#Public'; | 	const PUBLIC = 'https://www.w3.org/ns/activitystreams#Public'; | ||||||
| 
 | 
 | ||||||
| 	to = to || []; |  | ||||||
| 	cc = cc || []; |  | ||||||
| 
 |  | ||||||
| 	if (to.includes(PUBLIC)) { | 	if (to.includes(PUBLIC)) { | ||||||
| 		return 'public'; | 		return 'public'; | ||||||
| 	} else if (cc.includes(PUBLIC)) { | 	} else if (cc.includes(PUBLIC)) { | ||||||
|  |  | ||||||
|  | @ -9,13 +9,14 @@ const logger = apLogger; | ||||||
| 
 | 
 | ||||||
| export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => { | export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => { | ||||||
| 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | ||||||
|  | 	if (id == null) throw 'missing id'; | ||||||
| 
 | 
 | ||||||
| 	const uri = activity.id || activity; | 	const uri = activity.id || activity; | ||||||
| 
 | 
 | ||||||
| 	logger.info(`Block: ${uri}`); | 	logger.info(`Block: ${uri}`); | ||||||
| 
 | 
 | ||||||
| 	if (!id.startsWith(config.url + '/')) { | 	if (!id.startsWith(config.url + '/')) { | ||||||
| 		return null; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const blockee = await Users.findOne(id.split('/').pop()); | 	const blockee = await Users.findOne(id.split('/').pop()); | ||||||
|  |  | ||||||
|  | @ -6,9 +6,10 @@ import { Users } from '../../../models'; | ||||||
| 
 | 
 | ||||||
| export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => { | export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => { | ||||||
| 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | ||||||
|  | 	if (id == null) throw 'missing id'; | ||||||
| 
 | 
 | ||||||
| 	if (!id.startsWith(config.url + '/')) { | 	if (!id.startsWith(config.url + '/')) { | ||||||
| 		return null; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const followee = await Users.findOne(id.split('/').pop()); | 	const followee = await Users.findOne(id.split('/').pop()); | ||||||
|  |  | ||||||
|  | @ -71,7 +71,7 @@ const self = async (actor: IRemoteUser, activity: Object): Promise<void> => { | ||||||
| 
 | 
 | ||||||
| 	default: | 	default: | ||||||
| 		apLogger.warn(`unknown activity type: ${(activity as any).type}`); | 		apLogger.warn(`unknown activity type: ${(activity as any).type}`); | ||||||
| 		return null; | 		return; | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import { Notes } from '../../../models'; | ||||||
| 
 | 
 | ||||||
| export default async (actor: IRemoteUser, activity: ILike) => { | export default async (actor: IRemoteUser, activity: ILike) => { | ||||||
| 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | ||||||
|  | 	if (id == null) throw 'missing id'; | ||||||
| 
 | 
 | ||||||
| 	// Transform:
 | 	// Transform:
 | ||||||
| 	// https://misskey.ex/notes/xxxx to
 | 	// https://misskey.ex/notes/xxxx to
 | ||||||
|  |  | ||||||
|  | @ -6,9 +6,10 @@ import { Users } from '../../../../models'; | ||||||
| 
 | 
 | ||||||
| export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => { | export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => { | ||||||
| 	const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id; | 	const id = typeof activity.actor == 'string' ? activity.actor : activity.actor.id; | ||||||
|  | 	if (id == null) throw 'missing id'; | ||||||
| 
 | 
 | ||||||
| 	if (!id.startsWith(config.url + '/')) { | 	if (!id.startsWith(config.url + '/')) { | ||||||
| 		return null; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const follower = await Users.findOne(id.split('/').pop()); | 	const follower = await Users.findOne(id.split('/').pop()); | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ export default async (actor: IRemoteUser, activity: IRemove): Promise<void> => { | ||||||
| 
 | 
 | ||||||
| 	if (activity.target === actor.featured) { | 	if (activity.target === actor.featured) { | ||||||
| 		const note = await resolveNote(activity.object); | 		const note = await resolveNote(activity.object); | ||||||
|  | 		if (note == null) throw new Error('note not found'); | ||||||
| 		await removePinned(actor, note.id); | 		await removePinned(actor, note.id); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -9,13 +9,14 @@ const logger = apLogger; | ||||||
| 
 | 
 | ||||||
| export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => { | export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => { | ||||||
| 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | ||||||
|  | 	if (id == null) throw 'missing id'; | ||||||
| 
 | 
 | ||||||
| 	const uri = activity.id || activity; | 	const uri = activity.id || activity; | ||||||
| 
 | 
 | ||||||
| 	logger.info(`UnBlock: ${uri}`); | 	logger.info(`UnBlock: ${uri}`); | ||||||
| 
 | 
 | ||||||
| 	if (!id.startsWith(config.url + '/')) { | 	if (!id.startsWith(config.url + '/')) { | ||||||
| 		return null; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const blockee = await Users.findOne(id.split('/').pop()); | 	const blockee = await Users.findOne(id.split('/').pop()); | ||||||
|  |  | ||||||
|  | @ -7,9 +7,10 @@ import { Users, FollowRequests, Followings } from '../../../../models'; | ||||||
| 
 | 
 | ||||||
| export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => { | export default async (actor: IRemoteUser, activity: IFollow): Promise<void> => { | ||||||
| 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | ||||||
|  | 	if (id == null) throw 'missing id'; | ||||||
| 
 | 
 | ||||||
| 	if (!id.startsWith(config.url + '/')) { | 	if (!id.startsWith(config.url + '/')) { | ||||||
| 		return null; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const followee = await Users.findOne(id.split('/').pop()); | 	const followee = await Users.findOne(id.split('/').pop()); | ||||||
|  |  | ||||||
|  | @ -39,6 +39,4 @@ export default async (actor: IRemoteUser, activity: IUndo): Promise<void> => { | ||||||
| 			undoLike(actor, object as ILike); | 			undoLike(actor, object as ILike); | ||||||
| 			break; | 			break; | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	return null; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import { Notes } from '../../../../models'; | ||||||
|  */ |  */ | ||||||
| export default async (actor: IRemoteUser, activity: ILike): Promise<void> => { | export default async (actor: IRemoteUser, activity: ILike): Promise<void> => { | ||||||
| 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | 	const id = typeof activity.object == 'string' ? activity.object : activity.object.id; | ||||||
|  | 	if (id == null) throw 'missing id'; | ||||||
| 
 | 
 | ||||||
| 	const noteId = id.split('/').pop(); | 	const noteId = id.split('/').pop(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import fetchMeta from '../../../misc/fetch-meta'; | ||||||
| import { apLogger } from '../logger'; | import { apLogger } from '../logger'; | ||||||
| import { DriveFile } from '../../../models/entities/drive-file'; | import { DriveFile } from '../../../models/entities/drive-file'; | ||||||
| import { DriveFiles } from '../../../models'; | import { DriveFiles } from '../../../models'; | ||||||
|  | import { ensure } from '../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| const logger = apLogger; | const logger = apLogger; | ||||||
| 
 | 
 | ||||||
|  | @ -14,7 +15,7 @@ const logger = apLogger; | ||||||
| export async function createImage(actor: IRemoteUser, value: any): Promise<DriveFile> { | export async function createImage(actor: IRemoteUser, value: any): Promise<DriveFile> { | ||||||
| 	// 投稿者が凍結されていたらスキップ
 | 	// 投稿者が凍結されていたらスキップ
 | ||||||
| 	if (actor.isSuspended) { | 	if (actor.isSuspended) { | ||||||
| 		return null; | 		throw new Error('actor has been suspended'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const image = await new Resolver().resolve(value) as any; | 	const image = await new Resolver().resolve(value) as any; | ||||||
|  | @ -28,17 +29,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise<Drive | ||||||
| 	const instance = await fetchMeta(); | 	const instance = await fetchMeta(); | ||||||
| 	const cache = instance.cacheRemoteFiles; | 	const cache = instance.cacheRemoteFiles; | ||||||
| 
 | 
 | ||||||
| 	let file; | 	let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache); | ||||||
| 	try { |  | ||||||
| 		file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache); |  | ||||||
| 	} catch (e) { |  | ||||||
| 		// 4xxの場合は添付されてなかったことにする
 |  | ||||||
| 		if (e >= 400 && e < 500) { |  | ||||||
| 			logger.warn(`Ignored image: ${image.url} - ${e}`); |  | ||||||
| 			return null; |  | ||||||
| 		} |  | ||||||
| 		throw e; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	if (file.isLink) { | 	if (file.isLink) { | ||||||
| 		// URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、
 | 		// URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、
 | ||||||
|  | @ -49,7 +40,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise<Drive | ||||||
| 				uri: image.url | 				uri: image.url | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
| 			file = DriveFiles.findOne(file.id); | 			file = await DriveFiles.findOne(file.id).then(ensure); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ import { IObject, INote } from '../type'; | ||||||
| import { Emoji } from '../../../models/entities/emoji'; | import { Emoji } from '../../../models/entities/emoji'; | ||||||
| import { genId } from '../../../misc/gen-id'; | import { genId } from '../../../misc/gen-id'; | ||||||
| import fetchMeta from '../../../misc/fetch-meta'; | import fetchMeta from '../../../misc/fetch-meta'; | ||||||
|  | import { ensure } from '../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| const logger = apLogger; | const logger = apLogger; | ||||||
| 
 | 
 | ||||||
|  | @ -29,13 +30,14 @@ const logger = apLogger; | ||||||
|  * |  * | ||||||
|  * Misskeyに対象のNoteが登録されていればそれを返します。 |  * Misskeyに対象のNoteが登録されていればそれを返します。 | ||||||
|  */ |  */ | ||||||
| export async function fetchNote(value: string | IObject, resolver?: Resolver): Promise<Note> { | export async function fetchNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> { | ||||||
| 	const uri = typeof value == 'string' ? value : value.id; | 	const uri = typeof value == 'string' ? value : value.id; | ||||||
|  | 	if (uri == null) throw 'missing uri'; | ||||||
| 
 | 
 | ||||||
| 	// URIがこのサーバーを指しているならデータベースからフェッチ
 | 	// URIがこのサーバーを指しているならデータベースからフェッチ
 | ||||||
| 	if (uri.startsWith(config.url + '/')) { | 	if (uri.startsWith(config.url + '/')) { | ||||||
| 		const id = uri.split('/').pop(); | 		const id = uri.split('/').pop(); | ||||||
| 		return await Notes.findOne(id); | 		return await Notes.findOne(id).then(x => x || null); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//#region このサーバーに既に登録されていたらそれを返す
 | 	//#region このサーバーに既に登録されていたらそれを返す
 | ||||||
|  | @ -52,7 +54,7 @@ export async function fetchNote(value: string | IObject, resolver?: Resolver): P | ||||||
| /** | /** | ||||||
|  * Noteを作成します。 |  * Noteを作成します。 | ||||||
|  */ |  */ | ||||||
| export async function createNote(value: any, resolver?: Resolver, silent = false): Promise<Note> { | export async function createNote(value: any, resolver?: Resolver, silent = false): Promise<Note | null> { | ||||||
| 	if (resolver == null) resolver = new Resolver(); | 	if (resolver == null) resolver = new Resolver(); | ||||||
| 
 | 
 | ||||||
| 	const object: any = await resolver.resolve(value); | 	const object: any = await resolver.resolve(value); | ||||||
|  | @ -65,7 +67,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false | ||||||
| 			value: value, | 			value: value, | ||||||
| 			object: object | 			object: object | ||||||
| 		}); | 		}); | ||||||
| 		return null; | 		throw 'invalid note'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const note: INote = object; | 	const note: INote = object; | ||||||
|  | @ -75,11 +77,11 @@ export async function createNote(value: any, resolver?: Resolver, silent = false | ||||||
| 	logger.info(`Creating the Note: ${note.id}`); | 	logger.info(`Creating the Note: ${note.id}`); | ||||||
| 
 | 
 | ||||||
| 	// 投稿者をフェッチ
 | 	// 投稿者をフェッチ
 | ||||||
| 	const actor = await resolvePerson(note.attributedTo, null, resolver) as IRemoteUser; | 	const actor = await resolvePerson(note.attributedTo, resolver) as IRemoteUser; | ||||||
| 
 | 
 | ||||||
| 	// 投稿者が凍結されていたらスキップ
 | 	// 投稿者が凍結されていたらスキップ
 | ||||||
| 	if (actor.isSuspended) { | 	if (actor.isSuspended) { | ||||||
| 		return null; | 		throw 'actor has been suspended'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//#region Visibility
 | 	//#region Visibility
 | ||||||
|  | @ -95,9 +97,9 @@ export async function createNote(value: any, resolver?: Resolver, silent = false | ||||||
| 			visibility = 'followers'; | 			visibility = 'followers'; | ||||||
| 		} else { | 		} else { | ||||||
| 			visibility = 'specified'; | 			visibility = 'specified'; | ||||||
| 			visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri, null, resolver))); | 			visibleUsers = await Promise.all(note.to.map(uri => resolvePerson(uri, resolver))); | ||||||
| 		} | 		} | ||||||
| } | 	} | ||||||
| 	//#endergion
 | 	//#endergion
 | ||||||
| 
 | 
 | ||||||
| 	const apMentions = await extractMentionedUsers(actor, note.to, note.cc, resolver); | 	const apMentions = await extractMentionedUsers(actor, note.to, note.cc, resolver); | ||||||
|  | @ -118,7 +120,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false | ||||||
| 		: []; | 		: []; | ||||||
| 
 | 
 | ||||||
| 	// リプライ
 | 	// リプライ
 | ||||||
| 	const reply: Note = note.inReplyTo | 	const reply: Note | undefined | null = note.inReplyTo | ||||||
| 		? await resolveNote(note.inReplyTo, resolver).catch(e => { | 		? await resolveNote(note.inReplyTo, resolver).catch(e => { | ||||||
| 			// 4xxの場合はリプライしてないことにする
 | 			// 4xxの場合はリプライしてないことにする
 | ||||||
| 			if (e.statusCode >= 400 && e.statusCode < 500) { | 			if (e.statusCode >= 400 && e.statusCode < 500) { | ||||||
|  | @ -131,7 +133,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false | ||||||
| 		: null; | 		: null; | ||||||
| 
 | 
 | ||||||
| 	// 引用
 | 	// 引用
 | ||||||
| 	let quote: Note; | 	let quote: Note | undefined | null; | ||||||
| 
 | 
 | ||||||
| 	if (note._misskey_quote && typeof note._misskey_quote == 'string') { | 	if (note._misskey_quote && typeof note._misskey_quote == 'string') { | ||||||
| 		quote = await resolveNote(note._misskey_quote).catch(e => { | 		quote = await resolveNote(note._misskey_quote).catch(e => { | ||||||
|  | @ -152,7 +154,8 @@ export async function createNote(value: any, resolver?: Resolver, silent = false | ||||||
| 
 | 
 | ||||||
| 	// vote
 | 	// vote
 | ||||||
| 	if (reply && reply.hasPoll) { | 	if (reply && reply.hasPoll) { | ||||||
| 		const poll = await Polls.findOne({ noteId: reply.id }); | 		const poll = await Polls.findOne({ noteId: reply.id }).then(ensure); | ||||||
|  | 
 | ||||||
| 		const tryCreateVote = async (name: string, index: number): Promise<null> => { | 		const tryCreateVote = async (name: string, index: number): Promise<null> => { | ||||||
| 			if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { | 			if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { | ||||||
| 				logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); | 				logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); | ||||||
|  | @ -180,7 +183,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const emojis = await extractEmojis(note.tag, actor.host).catch(e => { | 	const emojis = await extractEmojis(note.tag || [], actor.host).catch(e => { | ||||||
| 		logger.info(`extractEmojis: ${e}`); | 		logger.info(`extractEmojis: ${e}`); | ||||||
| 		return [] as Emoji[]; | 		return [] as Emoji[]; | ||||||
| 	}); | 	}); | ||||||
|  | @ -196,7 +199,7 @@ export async function createNote(value: any, resolver?: Resolver, silent = false | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return await post(actor, { | 	return await post(actor, { | ||||||
| 		createdAt: new Date(note.published), | 		createdAt: note.published ? new Date(note.published) : null, | ||||||
| 		files, | 		files, | ||||||
| 		reply, | 		reply, | ||||||
| 		renote: quote, | 		renote: quote, | ||||||
|  | @ -223,8 +226,9 @@ export async function createNote(value: any, resolver?: Resolver, silent = false | ||||||
|  * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ |  * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ | ||||||
|  * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 |  * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 | ||||||
|  */ |  */ | ||||||
| export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note> { | export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> { | ||||||
| 	const uri = typeof value == 'string' ? value : value.id; | 	const uri = typeof value == 'string' ? value : value.id; | ||||||
|  | 	if (uri == null) throw 'missing uri'; | ||||||
| 
 | 
 | ||||||
| 	// ブロックしてたら中断
 | 	// ブロックしてたら中断
 | ||||||
| 	// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
 | 	// TODO: いちいちデータベースにアクセスするのはコスト高そうなのでどっかにキャッシュしておく
 | ||||||
|  | @ -244,75 +248,79 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver): | ||||||
| 	// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
 | 	// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
 | ||||||
| 	return await createNote(uri, resolver).catch(e => { | 	return await createNote(uri, resolver).catch(e => { | ||||||
| 		if (e.name === 'duplicated') { | 		if (e.name === 'duplicated') { | ||||||
| 			return fetchNote(uri); | 			return fetchNote(uri).then(note => { | ||||||
|  | 				if (note == null) { | ||||||
|  | 					throw 'something happened'; | ||||||
|  | 				} else { | ||||||
|  | 					return note; | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
| 		} else { | 		} else { | ||||||
| 			throw e; | 			throw e; | ||||||
| 		} | 		} | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function extractEmojis(tags: ITag[], host: string) { | export async function extractEmojis(tags: ITag[], host: string): Promise<Emoji[]> { | ||||||
| 	host = toPuny(host); | 	host = toPuny(host); | ||||||
| 
 | 
 | ||||||
| 	if (!tags) return []; | 	if (!tags) return []; | ||||||
| 
 | 
 | ||||||
| 	const eomjiTags = tags.filter(tag => tag.type === 'Emoji' && tag.icon && tag.icon.url); | 	const eomjiTags = tags.filter(tag => tag.type === 'Emoji' && tag.icon && tag.icon.url && tag.name); | ||||||
| 
 | 
 | ||||||
| 	return await Promise.all( | 	return await Promise.all(eomjiTags.map(async tag => { | ||||||
| 		eomjiTags.map(async tag => { | 		const name = tag.name!.replace(/^:/, '').replace(/:$/, ''); | ||||||
| 			const name = tag.name.replace(/^:/, '').replace(/:$/, ''); |  | ||||||
| 
 | 
 | ||||||
| 			const exists = await Emojis.findOne({ | 		const exists = await Emojis.findOne({ | ||||||
| 				host, | 			host, | ||||||
| 				name | 			name | ||||||
| 			}); | 		}); | ||||||
| 
 | 
 | ||||||
| 			if (exists) { | 		if (exists) { | ||||||
| 				if ((tag.updated != null && exists.updatedAt == null) | 			if ((tag.updated != null && exists.updatedAt == null) | ||||||
| 					|| (tag.id != null && exists.uri == null) | 				|| (tag.id != null && exists.uri == null) | ||||||
| 					|| (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt) | 				|| (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt) | ||||||
| 				) { | 			) { | ||||||
| 					await Emojis.update({ | 				await Emojis.update({ | ||||||
| 						host, | 					host, | ||||||
| 						name, | 					name, | ||||||
| 					}, { | 				}, { | ||||||
| 						uri: tag.id, | 					uri: tag.id, | ||||||
| 						url: tag.icon.url, | 					url: tag.icon!.url, | ||||||
| 						updatedAt: new Date(tag.updated), | 					updatedAt: new Date(tag.updated!), | ||||||
| 					}); | 				}); | ||||||
| 
 | 
 | ||||||
| 					return await Emojis.findOne({ | 				return await Emojis.findOne({ | ||||||
| 						host, | 					host, | ||||||
| 						name | 					name | ||||||
| 					}); | 				}) as Emoji; | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				return exists; |  | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			logger.info(`register emoji host=${host}, name=${name}`); | 			return exists; | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 			return await Emojis.save({ | 		logger.info(`register emoji host=${host}, name=${name}`); | ||||||
| 				id: genId(), | 
 | ||||||
| 				host, | 		return await Emojis.save({ | ||||||
| 				name, | 			id: genId(), | ||||||
| 				uri: tag.id, | 			host, | ||||||
| 				url: tag.icon.url, | 			name, | ||||||
| 				updatedAt: tag.updated ? new Date(tag.updated) : undefined, | 			uri: tag.id, | ||||||
| 				aliases: [] | 			url: tag.icon!.url, | ||||||
| 			} as Emoji); | 			updatedAt: tag.updated ? new Date(tag.updated) : undefined, | ||||||
| 		}) | 			aliases: [] | ||||||
| 	); | 		} as Partial<Emoji>); | ||||||
|  | 	})); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function extractMentionedUsers(actor: IRemoteUser, to: string[], cc: string[], resolver: Resolver) { | async function extractMentionedUsers(actor: IRemoteUser, to: string[], cc: string[], resolver: Resolver) { | ||||||
| 	const ignoreUris = ['https://www.w3.org/ns/activitystreams#Public', `${actor.uri}/followers`]; | 	const ignoreUris = ['https://www.w3.org/ns/activitystreams#Public', `${actor.uri}/followers`]; | ||||||
| 	const uris = difference(unique(concat([to || [], cc || []])), ignoreUris); | 	const uris = difference(unique(concat([to || [], cc || []])), ignoreUris); | ||||||
| 
 | 
 | ||||||
| 	const limit = promiseLimit(2); | 	const limit = promiseLimit<User | null>(2); | ||||||
| 	const users = await Promise.all( | 	const users = await Promise.all( | ||||||
| 		uris.map(uri => limit(() => resolvePerson(uri, null, resolver).catch(() => null)) as Promise<User>) | 		uris.map(uri => limit(() => resolvePerson(uri, resolver).catch(() => null)) as Promise<User | null>) | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	return users.filter(x => x != null); | 	return users.filter(x => x != null) as User[]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ import { toPuny } from '../../../misc/convert-host'; | ||||||
| import { UserProfile } from '../../../models/entities/user-profile'; | import { UserProfile } from '../../../models/entities/user-profile'; | ||||||
| import { validActor } from '../../../remote/activitypub/type'; | import { validActor } from '../../../remote/activitypub/type'; | ||||||
| import { getConnection } from 'typeorm'; | import { getConnection } from 'typeorm'; | ||||||
|  | import { ensure } from '../../../prelude/ensure'; | ||||||
| const logger = apLogger; | const logger = apLogger; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -86,13 +87,13 @@ function validatePerson(x: any, uri: string) { | ||||||
|  * |  * | ||||||
|  * Misskeyに対象のPersonが登録されていればそれを返します。 |  * Misskeyに対象のPersonが登録されていればそれを返します。 | ||||||
|  */ |  */ | ||||||
| export async function fetchPerson(uri: string, resolver?: Resolver): Promise<User> { | export async function fetchPerson(uri: string, resolver?: Resolver): Promise<User | null> { | ||||||
| 	if (typeof uri !== 'string') throw 'uri is not string'; | 	if (typeof uri !== 'string') throw 'uri is not string'; | ||||||
| 
 | 
 | ||||||
| 	// URIがこのサーバーを指しているならデータベースからフェッチ
 | 	// URIがこのサーバーを指しているならデータベースからフェッチ
 | ||||||
| 	if (uri.startsWith(config.url + '/')) { | 	if (uri.startsWith(config.url + '/')) { | ||||||
| 		const id = uri.split('/').pop(); | 		const id = uri.split('/').pop(); | ||||||
| 		return await Users.findOne(id); | 		return await Users.findOne(id).then(x => x || null); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//#region このサーバーに既に登録されていたらそれを返す
 | 	//#region このサーバーに既に登録されていたらそれを返す
 | ||||||
|  | @ -128,7 +129,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us | ||||||
| 
 | 
 | ||||||
| 	const host = toPuny(new URL(object.id).hostname); | 	const host = toPuny(new URL(object.id).hostname); | ||||||
| 
 | 
 | ||||||
| 	const { fields } = analyzeAttachments(person.attachment); | 	const { fields } = analyzeAttachments(person.attachment || []); | ||||||
| 
 | 
 | ||||||
| 	const tags = extractHashtags(person.tag).map(tag => tag.toLowerCase()); | 	const tags = extractHashtags(person.tag).map(tag => tag.toLowerCase()); | ||||||
| 
 | 
 | ||||||
|  | @ -161,7 +162,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us | ||||||
| 
 | 
 | ||||||
| 			await transactionalEntityManager.save(new UserProfile({ | 			await transactionalEntityManager.save(new UserProfile({ | ||||||
| 				userId: user.id, | 				userId: user.id, | ||||||
| 				description: fromHtml(person.summary), | 				description: person.summary ? fromHtml(person.summary) : null, | ||||||
| 				url: person.url, | 				url: person.url, | ||||||
| 				fields, | 				fields, | ||||||
| 				userHost: host | 				userHost: host | ||||||
|  | @ -189,20 +190,20 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us | ||||||
| 		instanceChart.newUser(i.host); | 		instanceChart.newUser(i.host); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	usersChart.update(user, true); | 	usersChart.update(user!, true); | ||||||
| 
 | 
 | ||||||
| 	// ハッシュタグ更新
 | 	// ハッシュタグ更新
 | ||||||
| 	for (const tag of tags) updateHashtag(user, tag, true, true); | 	for (const tag of tags) updateHashtag(user!, tag, true, true); | ||||||
| 	for (const tag of (user.tags || []).filter(x => !tags.includes(x))) updateHashtag(user, tag, true, false); | 	for (const tag of (user!.tags || []).filter(x => !tags.includes(x))) updateHashtag(user!, tag, true, false); | ||||||
| 
 | 
 | ||||||
| 	//#region アイコンとヘッダー画像をフェッチ
 | 	//#region アイコンとヘッダー画像をフェッチ
 | ||||||
| 	const [avatar, banner] = (await Promise.all<DriveFile>([ | 	const [avatar, banner] = (await Promise.all<DriveFile | null>([ | ||||||
| 		person.icon, | 		person.icon, | ||||||
| 		person.image | 		person.image | ||||||
| 	].map(img => | 	].map(img => | ||||||
| 		img == null | 		img == null | ||||||
| 			? Promise.resolve(null) | 			? Promise.resolve(null) | ||||||
| 			: resolveImage(user, img).catch(() => null) | 			: resolveImage(user!, img).catch(() => null) | ||||||
| 	))); | 	))); | ||||||
| 
 | 
 | ||||||
| 	const avatarId = avatar ? avatar.id : null; | 	const avatarId = avatar ? avatar.id : null; | ||||||
|  | @ -210,9 +211,9 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us | ||||||
| 	const avatarUrl = avatar ? DriveFiles.getPublicUrl(avatar) : null; | 	const avatarUrl = avatar ? DriveFiles.getPublicUrl(avatar) : null; | ||||||
| 	const bannerUrl = banner ? DriveFiles.getPublicUrl(banner) : null; | 	const bannerUrl = banner ? DriveFiles.getPublicUrl(banner) : null; | ||||||
| 	const avatarColor = avatar && avatar.properties.avgColor ? avatar.properties.avgColor : null; | 	const avatarColor = avatar && avatar.properties.avgColor ? avatar.properties.avgColor : null; | ||||||
| 	const bannerColor = banner && avatar.properties.avgColor ? banner.properties.avgColor : null; | 	const bannerColor = banner && banner.properties.avgColor ? banner.properties.avgColor : null; | ||||||
| 
 | 
 | ||||||
| 	await Users.update(user.id, { | 	await Users.update(user!.id, { | ||||||
| 		avatarId, | 		avatarId, | ||||||
| 		bannerId, | 		bannerId, | ||||||
| 		avatarUrl, | 		avatarUrl, | ||||||
|  | @ -221,30 +222,30 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us | ||||||
| 		bannerColor | 		bannerColor | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	user.avatarId = avatarId; | 	user!.avatarId = avatarId; | ||||||
| 	user.bannerId = bannerId; | 	user!.bannerId = bannerId; | ||||||
| 	user.avatarUrl = avatarUrl; | 	user!.avatarUrl = avatarUrl; | ||||||
| 	user.bannerUrl = bannerUrl; | 	user!.bannerUrl = bannerUrl; | ||||||
| 	user.avatarColor = avatarColor; | 	user!.avatarColor = avatarColor; | ||||||
| 	user.bannerColor = bannerColor; | 	user!.bannerColor = bannerColor; | ||||||
| 	//#endregion
 | 	//#endregion
 | ||||||
| 
 | 
 | ||||||
| 	//#region カスタム絵文字取得
 | 	//#region カスタム絵文字取得
 | ||||||
| 	const emojis = await extractEmojis(person.tag, host).catch(e => { | 	const emojis = await extractEmojis(person.tag || [], host).catch(e => { | ||||||
| 		logger.info(`extractEmojis: ${e}`); | 		logger.info(`extractEmojis: ${e}`); | ||||||
| 		return [] as Emoji[]; | 		return [] as Emoji[]; | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	const emojiNames = emojis.map(emoji => emoji.name); | 	const emojiNames = emojis.map(emoji => emoji.name); | ||||||
| 
 | 
 | ||||||
| 	await Users.update(user.id, { | 	await Users.update(user!.id, { | ||||||
| 		emojis: emojiNames | 		emojis: emojiNames | ||||||
| 	}); | 	}); | ||||||
| 	//#endregion
 | 	//#endregion
 | ||||||
| 
 | 
 | ||||||
| 	await updateFeatured(user.id).catch(err => logger.error(err)); | 	await updateFeatured(user!.id).catch(err => logger.error(err)); | ||||||
| 
 | 
 | ||||||
| 	return user; | 	return user!; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -254,7 +255,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us | ||||||
|  * @param resolver Resolver |  * @param resolver Resolver | ||||||
|  * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します) |  * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します) | ||||||
|  */ |  */ | ||||||
| export async function updatePerson(uri: string, resolver?: Resolver, hint?: object): Promise<void> { | export async function updatePerson(uri: string, resolver?: Resolver | null, hint?: object): Promise<void> { | ||||||
| 	if (typeof uri !== 'string') throw 'uri is not string'; | 	if (typeof uri !== 'string') throw 'uri is not string'; | ||||||
| 
 | 
 | ||||||
| 	// URIがこのサーバーを指しているならスキップ
 | 	// URIがこのサーバーを指しているならスキップ
 | ||||||
|  | @ -290,7 +291,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje | ||||||
| 	logger.info(`Updating the Person: ${person.id}`); | 	logger.info(`Updating the Person: ${person.id}`); | ||||||
| 
 | 
 | ||||||
| 	// アイコンとヘッダー画像をフェッチ
 | 	// アイコンとヘッダー画像をフェッチ
 | ||||||
| 	const [avatar, banner] = (await Promise.all<DriveFile>([ | 	const [avatar, banner] = (await Promise.all<DriveFile | null>([ | ||||||
| 		person.icon, | 		person.icon, | ||||||
| 		person.image | 		person.image | ||||||
| 	].map(img => | 	].map(img => | ||||||
|  | @ -300,14 +301,14 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje | ||||||
| 	))); | 	))); | ||||||
| 
 | 
 | ||||||
| 	// カスタム絵文字取得
 | 	// カスタム絵文字取得
 | ||||||
| 	const emojis = await extractEmojis(person.tag, exist.host).catch(e => { | 	const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => { | ||||||
| 		logger.info(`extractEmojis: ${e}`); | 		logger.info(`extractEmojis: ${e}`); | ||||||
| 		return [] as Emoji[]; | 		return [] as Emoji[]; | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	const emojiNames = emojis.map(emoji => emoji.name); | 	const emojiNames = emojis.map(emoji => emoji.name); | ||||||
| 
 | 
 | ||||||
| 	const { fields, services } = analyzeAttachments(person.attachment); | 	const { fields, services } = analyzeAttachments(person.attachment || []); | ||||||
| 
 | 
 | ||||||
| 	const tags = extractHashtags(person.tag).map(tag => tag.toLowerCase()); | 	const tags = extractHashtags(person.tag).map(tag => tag.toLowerCase()); | ||||||
| 
 | 
 | ||||||
|  | @ -317,7 +318,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje | ||||||
| 		sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), | 		sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), | ||||||
| 		featured: person.featured, | 		featured: person.featured, | ||||||
| 		emojis: emojiNames, | 		emojis: emojiNames, | ||||||
| 		description: fromHtml(person.summary), | 		description: person.summary ? fromHtml(person.summary) : null, | ||||||
| 		name: person.name, | 		name: person.name, | ||||||
| 		url: person.url, | 		url: person.url, | ||||||
| 		endpoints: person.endpoints, | 		endpoints: person.endpoints, | ||||||
|  | @ -326,7 +327,6 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje | ||||||
| 		isBot: object.type == 'Service', | 		isBot: object.type == 'Service', | ||||||
| 		isCat: (person as any).isCat === true, | 		isCat: (person as any).isCat === true, | ||||||
| 		isLocked: person.manuallyApprovesFollowers, | 		isLocked: person.manuallyApprovesFollowers, | ||||||
| 		createdAt: new Date(Date.parse(person.published)) || null, |  | ||||||
| 	} as Partial<User>; | 	} as Partial<User>; | ||||||
| 
 | 
 | ||||||
| 	if (avatar) { | 	if (avatar) { | ||||||
|  | @ -379,7 +379,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje | ||||||
|  * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ |  * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ | ||||||
|  * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 |  * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 | ||||||
|  */ |  */ | ||||||
| export async function resolvePerson(uri: string, verifier?: string, resolver?: Resolver): Promise<User> { | export async function resolvePerson(uri: string, resolver?: Resolver): Promise<User> { | ||||||
| 	if (typeof uri !== 'string') throw 'uri is not string'; | 	if (typeof uri !== 'string') throw 'uri is not string'; | ||||||
| 
 | 
 | ||||||
| 	//#region このサーバーに既に登録されていたらそれを返す
 | 	//#region このサーバーに既に登録されていたらそれを返す
 | ||||||
|  | @ -439,21 +439,24 @@ export function analyzeAttachments(attachments: ITag[]) { | ||||||
| 	}[] = []; | 	}[] = []; | ||||||
| 	const services: { [x: string]: any } = {}; | 	const services: { [x: string]: any } = {}; | ||||||
| 
 | 
 | ||||||
| 	if (Array.isArray(attachments)) | 	if (Array.isArray(attachments)) { | ||||||
| 		for (const attachment of attachments.filter(isPropertyValue)) | 		for (const attachment of attachments.filter(isPropertyValue)) { | ||||||
| 			if (isPropertyValue(attachment.identifier)) | 			if (isPropertyValue(attachment.identifier!)) { | ||||||
| 				addService(services, attachment.identifier); | 				addService(services, attachment.identifier!); | ||||||
| 			else | 			} else { | ||||||
| 				fields.push({ | 				fields.push({ | ||||||
| 					name: attachment.name, | 					name: attachment.name!, | ||||||
| 					value: fromHtml(attachment.value) | 					value: fromHtml(attachment.value!) | ||||||
| 				}); | 				}); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	return { fields, services }; | 	return { fields, services }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function updateFeatured(userId: User['id']) { | export async function updateFeatured(userId: User['id']) { | ||||||
| 	const user = await Users.findOne(userId); | 	const user = await Users.findOne(userId).then(ensure); | ||||||
| 	if (!Users.isRemoteUser(user)) return; | 	if (!Users.isRemoteUser(user)) return; | ||||||
| 	if (!user.featured) return; | 	if (!user.featured) return; | ||||||
| 
 | 
 | ||||||
|  | @ -471,18 +474,18 @@ export async function updateFeatured(userId: User['id']) { | ||||||
| 	if (!Array.isArray(items)) throw new Error(`Collection items is not an array`); | 	if (!Array.isArray(items)) throw new Error(`Collection items is not an array`); | ||||||
| 
 | 
 | ||||||
| 	// Resolve and regist Notes
 | 	// Resolve and regist Notes
 | ||||||
| 	const limit = promiseLimit(2); | 	const limit = promiseLimit<Note | null>(2); | ||||||
| 	const featuredNotes = await Promise.all(items | 	const featuredNotes = await Promise.all(items | ||||||
| 		.filter(item => item.type === 'Note') | 		.filter(item => item.type === 'Note') | ||||||
| 		.slice(0, 5) | 		.slice(0, 5) | ||||||
| 		.map(item => limit(() => resolveNote(item, resolver)) as Promise<Note>)); | 		.map(item => limit(() => resolveNote(item, resolver)))); | ||||||
| 
 | 
 | ||||||
| 	for (const note of featuredNotes.filter(note => note != null)) { | 	for (const note of featuredNotes.filter(note => note != null)) { | ||||||
| 		UserNotePinings.save({ | 		UserNotePinings.save({ | ||||||
| 			id: genId(), | 			id: genId(), | ||||||
| 			createdAt: new Date(), | 			createdAt: new Date(), | ||||||
| 			userId: user.id, | 			userId: user.id, | ||||||
| 			noteId: note.id | 			noteId: note!.id | ||||||
| 		} as UserNotePining); | 		} as UserNotePining); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -14,10 +14,10 @@ export async function extractPollFromQuestion(source: string | IQuestion): Promi | ||||||
| 		throw 'invalid question'; | 		throw 'invalid question'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const choices = question[multiple ? 'anyOf' : 'oneOf'] | 	const choices = question[multiple ? 'anyOf' : 'oneOf']! | ||||||
| 		.map((x, i) => x.name); | 		.map((x, i) => x.name!); | ||||||
| 
 | 
 | ||||||
| 	const votes = question[multiple ? 'anyOf' : 'oneOf'] | 	const votes = question[multiple ? 'anyOf' : 'oneOf']! | ||||||
| 		.map((x, i) => x.replies && x.replies.totalItems || x._misskey_votes || 0); | 		.map((x, i) => x.replies && x.replies.totalItems || x._misskey_votes || 0); | ||||||
| 
 | 
 | ||||||
| 	return { | 	return { | ||||||
|  | @ -60,7 +60,7 @@ export async function updateQuestion(value: any) { | ||||||
| 
 | 
 | ||||||
| 	for (const choice of poll.choices) { | 	for (const choice of poll.choices) { | ||||||
| 		const oldCount = poll.votes[poll.choices.indexOf(choice)]; | 		const oldCount = poll.votes[poll.choices.indexOf(choice)]; | ||||||
| 		const newCount = apChoices.filter(ap => ap.name === choice)[0].replies.totalItems; | 		const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems; | ||||||
| 
 | 
 | ||||||
| 		if (oldCount != newCount) { | 		if (oldCount != newCount) { | ||||||
| 			changed = true; | 			changed = true; | ||||||
|  |  | ||||||
|  | @ -14,13 +14,13 @@ export type ITag = { | ||||||
| 	identifier?: IIdentifier; | 	identifier?: IIdentifier; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export function extractHashtags(tags: ITag[]) { | export function extractHashtags(tags: ITag[] | null | undefined): string[] { | ||||||
| 	if (!tags) return []; | 	if (tags == null) return []; | ||||||
| 
 | 
 | ||||||
| 	const hashtags = tags.filter(tag => tag.type === 'Hashtag' && typeof tag.name == 'string'); | 	const hashtags = tags.filter(tag => tag.type === 'Hashtag' && typeof tag.name == 'string'); | ||||||
| 
 | 
 | ||||||
| 	return hashtags.map(tag => { | 	return hashtags.map(tag => { | ||||||
| 		const m = tag.name.match(/^#(.+)/); | 		const m = tag.name ? tag.name.match(/^#(.+)/) : null; | ||||||
| 		return m ? m[1] : null; | 		return m ? m[1] : null; | ||||||
| 	}).filter(x => x != null); | 	}).filter(x => x != null) as string[]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import config from '../../../config'; | import config from '../../../config'; | ||||||
| import { ILocalUser, IRemoteUser } from '../../../models/entities/user'; | import { ILocalUser, IRemoteUser } from '../../../models/entities/user'; | ||||||
| 
 | 
 | ||||||
| export default (blocker?: ILocalUser, blockee?: IRemoteUser) => ({ | export default (blocker: ILocalUser, blockee: IRemoteUser) => ({ | ||||||
| 	type: 'Block', | 	type: 'Block', | ||||||
| 	actor: `${config.url}/users/${blocker.id}`, | 	actor: `${config.url}/users/${blocker.id}`, | ||||||
| 	object: blockee.uri | 	object: blockee.uri | ||||||
|  |  | ||||||
|  | @ -1,12 +1,13 @@ | ||||||
| import config from '../../../config'; | import config from '../../../config'; | ||||||
| import { Users } from '../../../models'; | import { Users } from '../../../models'; | ||||||
| import { User } from '../../../models/entities/user'; | import { User } from '../../../models/entities/user'; | ||||||
|  | import { ensure } from '../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Convert (local|remote)(Follower|Followee)ID to URL |  * Convert (local|remote)(Follower|Followee)ID to URL | ||||||
|  * @param id Follower|Followee ID |  * @param id Follower|Followee ID | ||||||
|  */ |  */ | ||||||
| export default async function renderFollowUser(id: User['id']): Promise<any> { | export default async function renderFollowUser(id: User['id']): Promise<any> { | ||||||
| 	const user = await Users.findOne(id); | 	const user = await Users.findOne(id).then(ensure); | ||||||
| 	return Users.isLocalUser(user) ? `${config.url}/users/${user.id}` : user.uri; | 	return Users.isLocalUser(user) ? `${config.url}/users/${user.id}` : user.uri; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ import { DriveFiles, Notes, Users, Emojis, Polls } from '../../../models'; | ||||||
| import { In } from 'typeorm'; | import { In } from 'typeorm'; | ||||||
| import { Emoji } from '../../../models/entities/emoji'; | import { Emoji } from '../../../models/entities/emoji'; | ||||||
| import { Poll } from '../../../models/entities/poll'; | import { Poll } from '../../../models/entities/poll'; | ||||||
|  | import { ensure } from '../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| export default async function renderNote(note: Note, dive = true): Promise<any> { | export default async function renderNote(note: Note, dive = true): Promise<any> { | ||||||
| 	const promisedFiles: Promise<DriveFile[]> = note.fileIds.length > 0 | 	const promisedFiles: Promise<DriveFile[]> = note.fileIds.length > 0 | ||||||
|  | @ -17,15 +18,15 @@ export default async function renderNote(note: Note, dive = true): Promise<any> | ||||||
| 		: Promise.resolve([]); | 		: Promise.resolve([]); | ||||||
| 
 | 
 | ||||||
| 	let inReplyTo; | 	let inReplyTo; | ||||||
| 	let inReplyToNote: Note; | 	let inReplyToNote: Note | undefined; | ||||||
| 
 | 
 | ||||||
| 	if (note.replyId) { | 	if (note.replyId) { | ||||||
| 		inReplyToNote = await Notes.findOne(note.replyId); | 		inReplyToNote = await Notes.findOne(note.replyId); | ||||||
| 
 | 
 | ||||||
| 		if (inReplyToNote !== null) { | 		if (inReplyToNote != null) { | ||||||
| 			const inReplyToUser = await Users.findOne(inReplyToNote.userId); | 			const inReplyToUser = await Users.findOne(inReplyToNote.userId); | ||||||
| 
 | 
 | ||||||
| 			if (inReplyToUser !== null) { | 			if (inReplyToUser != null) { | ||||||
| 				if (inReplyToNote.uri) { | 				if (inReplyToNote.uri) { | ||||||
| 					inReplyTo = inReplyToNote.uri; | 					inReplyTo = inReplyToNote.uri; | ||||||
| 				} else { | 				} else { | ||||||
|  | @ -51,9 +52,7 @@ export default async function renderNote(note: Note, dive = true): Promise<any> | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne(note.userId).then(ensure); | ||||||
| 		id: note.userId |  | ||||||
| 	}); |  | ||||||
| 
 | 
 | ||||||
| 	const attributedTo = `${config.url}/users/${user.id}`; | 	const attributedTo = `${config.url}/users/${user.id}`; | ||||||
| 
 | 
 | ||||||
|  | @ -85,13 +84,13 @@ export default async function renderNote(note: Note, dive = true): Promise<any> | ||||||
| 	const files = await promisedFiles; | 	const files = await promisedFiles; | ||||||
| 
 | 
 | ||||||
| 	let text = note.text; | 	let text = note.text; | ||||||
| 	let poll: Poll; | 	let poll: Poll | undefined; | ||||||
| 
 | 
 | ||||||
| 	if (note.hasPoll) { | 	if (note.hasPoll) { | ||||||
| 		poll = await Polls.findOne({ noteId: note.id }); | 		poll = await Polls.findOne({ noteId: note.id }); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	let question: string; | 	let question: string | undefined; | ||||||
| 	if (poll) { | 	if (poll) { | ||||||
| 		if (text == null) text = ''; | 		if (text == null) text = ''; | ||||||
| 		const url = `${config.url}/notes/${note.id}`; | 		const url = `${config.url}/notes/${note.id}`; | ||||||
|  | @ -144,7 +143,7 @@ export default async function renderNote(note: Note, dive = true): Promise<any> | ||||||
| 			name: text, | 			name: text, | ||||||
| 			replies: { | 			replies: { | ||||||
| 				type: 'Collection', | 				type: 'Collection', | ||||||
| 				totalItems: poll.votes[i] | 				totalItems: poll!.votes[i] | ||||||
| 			} | 			} | ||||||
| 		})) | 		})) | ||||||
| 	} : {}; | 	} : {}; | ||||||
|  | @ -179,5 +178,5 @@ export async function getEmojis(names: string[]): Promise<Emoji[]> { | ||||||
| 		})) | 		})) | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	return emojis.filter(emoji => emoji != null); | 	return emojis.filter(emoji => emoji != null) as Emoji[]; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
|  * @param prev URL of prev page (optional) |  * @param prev URL of prev page (optional) | ||||||
|  * @param next URL of next page (optional) |  * @param next URL of next page (optional) | ||||||
|  */ |  */ | ||||||
| export default function(id: string, totalItems: any, orderedItems: any, partOf: string, prev: string, next: string) { | export default function(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) { | ||||||
| 	const page = { | 	const page = { | ||||||
| 		id, | 		id, | ||||||
| 		partOf, | 		partOf, | ||||||
|  |  | ||||||
|  | @ -9,14 +9,15 @@ import renderEmoji from './emoji'; | ||||||
| import { IIdentifier } from '../models/identifier'; | import { IIdentifier } from '../models/identifier'; | ||||||
| import renderHashtag from './hashtag'; | import renderHashtag from './hashtag'; | ||||||
| import { DriveFiles, UserProfiles, UserKeypairs } from '../../../models'; | import { DriveFiles, UserProfiles, UserKeypairs } from '../../../models'; | ||||||
|  | import { ensure } from '../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| export async function renderPerson(user: ILocalUser) { | export async function renderPerson(user: ILocalUser) { | ||||||
| 	const id = `${config.url}/users/${user.id}`; | 	const id = `${config.url}/users/${user.id}`; | ||||||
| 
 | 
 | ||||||
| 	const [avatar, banner, profile] = await Promise.all([ | 	const [avatar, banner, profile] = await Promise.all([ | ||||||
| 		DriveFiles.findOne(user.avatarId), | 		user.avatarId ? DriveFiles.findOne(user.avatarId) : Promise.resolve(undefined), | ||||||
| 		DriveFiles.findOne(user.bannerId), | 		user.bannerId ? DriveFiles.findOne(user.bannerId) : Promise.resolve(undefined), | ||||||
| 		UserProfiles.findOne({ userId: user.id }) | 		UserProfiles.findOne({ userId: user.id }).then(ensure) | ||||||
| 	]); | 	]); | ||||||
| 
 | 
 | ||||||
| 	const attachment: { | 	const attachment: { | ||||||
|  | @ -76,9 +77,7 @@ export async function renderPerson(user: ILocalUser) { | ||||||
| 		...hashtagTags, | 		...hashtagTags, | ||||||
| 	]; | 	]; | ||||||
| 
 | 
 | ||||||
| 	const keypair = await UserKeypairs.findOne({ | 	const keypair = await UserKeypairs.findOne(user.id).then(ensure); | ||||||
| 		userId: user.id |  | ||||||
| 	}); |  | ||||||
| 
 | 
 | ||||||
| 	return { | 	return { | ||||||
| 		type: user.isBot ? 'Service' : 'Person', | 		type: user.isBot ? 'Service' : 'Person', | ||||||
|  | @ -94,8 +93,8 @@ export async function renderPerson(user: ILocalUser) { | ||||||
| 		preferredUsername: user.username, | 		preferredUsername: user.username, | ||||||
| 		name: user.name, | 		name: user.name, | ||||||
| 		summary: toHtml(parse(profile.description)), | 		summary: toHtml(parse(profile.description)), | ||||||
| 		icon: user.avatarId && renderImage(avatar), | 		icon: avatar ? renderImage(avatar) : null, | ||||||
| 		image: user.bannerId && renderImage(banner), | 		image: banner ? renderImage(banner) : null, | ||||||
| 		tag, | 		tag, | ||||||
| 		manuallyApprovesFollowers: user.isLocked, | 		manuallyApprovesFollowers: user.isLocked, | ||||||
| 		publicKey: renderKey(user, keypair), | 		publicKey: renderKey(user, keypair), | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ import { apLogger } from './logger'; | ||||||
| import { UserKeypairs } from '../../models'; | import { UserKeypairs } from '../../models'; | ||||||
| import fetchMeta from '../../misc/fetch-meta'; | import fetchMeta from '../../misc/fetch-meta'; | ||||||
| import { toPuny } from '../../misc/convert-host'; | import { toPuny } from '../../misc/convert-host'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| export const logger = apLogger.createSubLogger('deliver'); | export const logger = apLogger.createSubLogger('deliver'); | ||||||
| 
 | 
 | ||||||
|  | @ -38,7 +39,7 @@ export default async (user: ILocalUser, url: string, object: any) => { | ||||||
| 
 | 
 | ||||||
| 	const keypair = await UserKeypairs.findOne({ | 	const keypair = await UserKeypairs.findOne({ | ||||||
| 		userId: user.id | 		userId: user.id | ||||||
| 	}); | 	}).then(ensure); | ||||||
| 
 | 
 | ||||||
| 	const _ = new Promise((resolve, reject) => { | 	const _ = new Promise((resolve, reject) => { | ||||||
| 		const req = request({ | 		const req = request({ | ||||||
|  | @ -56,7 +57,7 @@ export default async (user: ILocalUser, url: string, object: any) => { | ||||||
| 				'Digest': `SHA-256=${hash}` | 				'Digest': `SHA-256=${hash}` | ||||||
| 			} | 			} | ||||||
| 		}, res => { | 		}, res => { | ||||||
| 			if (res.statusCode >= 400) { | 			if (res.statusCode! >= 400) { | ||||||
| 				logger.warn(`${url} --> ${res.statusCode}`); | 				logger.warn(`${url} --> ${res.statusCode}`); | ||||||
| 				reject(res); | 				reject(res); | ||||||
| 			} else { | 			} else { | ||||||
|  | @ -73,7 +74,7 @@ export default async (user: ILocalUser, url: string, object: any) => { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		// Signature: Signature ... => Signature: ...
 | 		// Signature: Signature ... => Signature: ...
 | ||||||
| 		let sig = req.getHeader('Signature').toString(); | 		let sig = req.getHeader('Signature')!.toString(); | ||||||
| 		sig = sig.replace(/^Signature /, ''); | 		sig = sig.replace(/^Signature /, ''); | ||||||
| 		req.setHeader('Signature', sig); | 		req.setHeader('Signature', sig); | ||||||
| 
 | 
 | ||||||
|  | @ -112,7 +113,7 @@ async function resolveAddr(domain: string) { | ||||||
| 
 | 
 | ||||||
| function resolveAddrInner(domain: string, options: IRunOptions = {}): Promise<string> { | function resolveAddrInner(domain: string, options: IRunOptions = {}): Promise<string> { | ||||||
| 	return new Promise((res, rej) => { | 	return new Promise((res, rej) => { | ||||||
| 		lookup(domain, options, (error: any, address: string | string[]) => { | 		lookup(domain, options, (error, address) => { | ||||||
| 			if (error) return rej(error); | 			if (error) return rej(error); | ||||||
| 			return res(Array.isArray(address) ? address[0] : address); | 			return res(Array.isArray(address) ? address[0] : address); | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
|  | @ -10,18 +10,31 @@ import { toPuny } from '../misc/convert-host'; | ||||||
| 
 | 
 | ||||||
| const logger = remoteLogger.createSubLogger('resolve-user'); | const logger = remoteLogger.createSubLogger('resolve-user'); | ||||||
| 
 | 
 | ||||||
| export async function resolveUser(username: string, host: string, option?: any, resync = false): Promise<User> { | export async function resolveUser(username: string, host: string | null, option?: any, resync = false): Promise<User> { | ||||||
| 	const usernameLower = username.toLowerCase(); | 	const usernameLower = username.toLowerCase(); | ||||||
| 	host = toPuny(host); |  | ||||||
| 
 | 
 | ||||||
| 	if (host == null) { | 	if (host == null) { | ||||||
| 		logger.info(`return local user: ${usernameLower}`); | 		logger.info(`return local user: ${usernameLower}`); | ||||||
| 		return await Users.findOne({ usernameLower, host: null }); | 		return await Users.findOne({ usernameLower, host: null }).then(u => { | ||||||
|  | 			if (u == null) { | ||||||
|  | 				throw 'user not found'; | ||||||
|  | 			} else { | ||||||
|  | 				return u; | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	host = toPuny(host); | ||||||
|  | 
 | ||||||
| 	if (config.host == host) { | 	if (config.host == host) { | ||||||
| 		logger.info(`return local user: ${usernameLower}`); | 		logger.info(`return local user: ${usernameLower}`); | ||||||
| 		return await Users.findOne({ usernameLower, host: null }); | 		return await Users.findOne({ usernameLower, host: null }).then(u => { | ||||||
|  | 			if (u == null) { | ||||||
|  | 				throw 'user not found'; | ||||||
|  | 			} else { | ||||||
|  | 				return u; | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne({ usernameLower, host }, option); | 	const user = await Users.findOne({ usernameLower, host }, option); | ||||||
|  | @ -63,7 +76,13 @@ export async function resolveUser(username: string, host: string, option?: any, | ||||||
| 		await updatePerson(self.href); | 		await updatePerson(self.href); | ||||||
| 
 | 
 | ||||||
| 		logger.info(`return resynced remote user: ${acctLower}`); | 		logger.info(`return resynced remote user: ${acctLower}`); | ||||||
| 		return await Users.findOne({ uri: self.href }); | 		return await Users.findOne({ uri: self.href }).then(u => { | ||||||
|  | 			if (u == null) { | ||||||
|  | 				throw 'user not found'; | ||||||
|  | 			} else { | ||||||
|  | 				return u; | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	logger.info(`return existing remote user: ${acctLower}`); | 	logger.info(`return existing remote user: ${acctLower}`); | ||||||
|  | @ -76,7 +95,7 @@ async function resolveSelf(acctLower: string) { | ||||||
| 		logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); | 		logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); | ||||||
| 		throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`); | 		throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`); | ||||||
| 	}); | 	}); | ||||||
| 	const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self'); | 	const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self'); | ||||||
| 	if (!self) { | 	if (!self) { | ||||||
| 		logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); | 		logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); | ||||||
| 		throw new Error('self link not found'); | 		throw new Error('self link not found'); | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import { query as urlQuery } from '../prelude/url'; | ||||||
| 
 | 
 | ||||||
| type ILink = { | type ILink = { | ||||||
| 	href: string; | 	href: string; | ||||||
| 	rel: string; | 	rel?: string; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type IWebFinger = { | type IWebFinger = { | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ import { isSelfHost } from '../misc/convert-host'; | ||||||
| import { Notes, Users, Emojis, UserKeypairs, Polls } from '../models'; | import { Notes, Users, Emojis, UserKeypairs, Polls } from '../models'; | ||||||
| import { ILocalUser, User } from '../models/entities/user'; | import { ILocalUser, User } from '../models/entities/user'; | ||||||
| import { In } from 'typeorm'; | import { In } from 'typeorm'; | ||||||
|  | import { ensure } from '../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| // Init router
 | // Init router
 | ||||||
| const router = new Router(); | const router = new Router(); | ||||||
|  | @ -123,8 +124,8 @@ router.get('/questions/:question', async (ctx, next) => { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const user = await Users.findOne(pollNote.userId); | 	const user = await Users.findOne(pollNote.userId).then(ensure); | ||||||
| 	const poll = await Polls.findOne({ noteId: pollNote.id }); | 	const poll = await Polls.findOne({ noteId: pollNote.id }).then(ensure); | ||||||
| 
 | 
 | ||||||
| 	ctx.body = renderActivity(await renderQuestion(user as ILocalUser, pollNote, poll)); | 	ctx.body = renderActivity(await renderQuestion(user as ILocalUser, pollNote, poll)); | ||||||
| 	setResponseType(ctx); | 	setResponseType(ctx); | ||||||
|  | @ -156,9 +157,7 @@ router.get('/users/:user/publickey', async ctx => { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const keypair = await UserKeypairs.findOne({ | 	const keypair = await UserKeypairs.findOne(user.id).then(ensure); | ||||||
| 		userId: user.id |  | ||||||
| 	}); |  | ||||||
| 
 | 
 | ||||||
| 	if (Users.isLocalUser(user)) { | 	if (Users.isLocalUser(user)) { | ||||||
| 		ctx.body = renderActivity(renderKey(user, keypair)); | 		ctx.body = renderActivity(renderKey(user, keypair)); | ||||||
|  | @ -189,7 +188,7 @@ router.get('/users/:user', async (ctx, next) => { | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne({ | ||||||
| 		id: userId, | 		id: userId, | ||||||
| 		host: null | 		host: null | ||||||
| 	}); | 	}).then(ensure); | ||||||
| 
 | 
 | ||||||
| 	await userInfo(ctx, user); | 	await userInfo(ctx, user); | ||||||
| }); | }); | ||||||
|  | @ -200,7 +199,7 @@ router.get('/@:user', async (ctx, next) => { | ||||||
| 	const user = await Users.findOne({ | 	const user = await Users.findOne({ | ||||||
| 		usernameLower: ctx.params.user.toLowerCase(), | 		usernameLower: ctx.params.user.toLowerCase(), | ||||||
| 		host: null | 		host: null | ||||||
| 	}); | 	}).then(ensure); | ||||||
| 
 | 
 | ||||||
| 	await userInfo(ctx, user); | 	await userInfo(ctx, user); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-c | ||||||
| import { setResponseType } from '../activitypub'; | import { setResponseType } from '../activitypub'; | ||||||
| import renderNote from '../../remote/activitypub/renderer/note'; | import renderNote from '../../remote/activitypub/renderer/note'; | ||||||
| import { Users, Notes, UserNotePinings } from '../../models'; | import { Users, Notes, UserNotePinings } from '../../models'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| export default async (ctx: Router.IRouterContext) => { | export default async (ctx: Router.IRouterContext) => { | ||||||
| 	const userId = ctx.params.user; | 	const userId = ctx.params.user; | ||||||
|  | @ -22,13 +23,14 @@ export default async (ctx: Router.IRouterContext) => { | ||||||
| 
 | 
 | ||||||
| 	const pinings = await UserNotePinings.find({ userId: user.id }); | 	const pinings = await UserNotePinings.find({ userId: user.id }); | ||||||
| 
 | 
 | ||||||
| 	const pinnedNotes = await Promise.all(pinings.map(pining => Notes.findOne(pining.noteId))); | 	const pinnedNotes = await Promise.all(pinings.map(pining => | ||||||
|  | 		Notes.findOne(pining.noteId).then(ensure))); | ||||||
| 
 | 
 | ||||||
| 	const renderedNotes = await Promise.all(pinnedNotes.map(note => renderNote(note))); | 	const renderedNotes = await Promise.all(pinnedNotes.map(note => renderNote(note))); | ||||||
| 
 | 
 | ||||||
| 	const rendered = renderOrderedCollection( | 	const rendered = renderOrderedCollection( | ||||||
| 		`${config.url}/users/${userId}/collections/featured`, | 		`${config.url}/users/${userId}/collections/featured`, | ||||||
| 		renderedNotes.length, null, null, renderedNotes | 		renderedNotes.length, undefined, undefined, renderedNotes | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	ctx.body = renderActivity(rendered); | 	ctx.body = renderActivity(rendered); | ||||||
|  |  | ||||||
|  | @ -69,18 +69,18 @@ export default async (ctx: Router.IRouterContext) => { | ||||||
| 				cursor | 				cursor | ||||||
| 			})}`,
 | 			})}`,
 | ||||||
| 			user.followersCount, renderedFollowers, partOf, | 			user.followersCount, renderedFollowers, partOf, | ||||||
| 			null, | 			undefined, | ||||||
| 			inStock ? `${partOf}?${url.query({ | 			inStock ? `${partOf}?${url.query({ | ||||||
| 				page: 'true', | 				page: 'true', | ||||||
| 				cursor: followings[followings.length - 1].id | 				cursor: followings[followings.length - 1].id | ||||||
| 			})}` : null
 | 			})}` : undefined
 | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		ctx.body = renderActivity(rendered); | 		ctx.body = renderActivity(rendered); | ||||||
| 		setResponseType(ctx); | 		setResponseType(ctx); | ||||||
| 	} else { | 	} else { | ||||||
| 		// index page
 | 		// index page
 | ||||||
| 		const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`, null); | 		const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`); | ||||||
| 		ctx.body = renderActivity(rendered); | 		ctx.body = renderActivity(rendered); | ||||||
| 		ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); | 		ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); | ||||||
| 		setResponseType(ctx); | 		setResponseType(ctx); | ||||||
|  |  | ||||||
|  | @ -70,18 +70,18 @@ export default async (ctx: Router.IRouterContext) => { | ||||||
| 				cursor | 				cursor | ||||||
| 			})}`,
 | 			})}`,
 | ||||||
| 			user.followingCount, renderedFollowees, partOf, | 			user.followingCount, renderedFollowees, partOf, | ||||||
| 			null, | 			undefined, | ||||||
| 			inStock ? `${partOf}?${url.query({ | 			inStock ? `${partOf}?${url.query({ | ||||||
| 				page: 'true', | 				page: 'true', | ||||||
| 				cursor: followings[followings.length - 1].id | 				cursor: followings[followings.length - 1].id | ||||||
| 			})}` : null
 | 			})}` : undefined
 | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		ctx.body = renderActivity(rendered); | 		ctx.body = renderActivity(rendered); | ||||||
| 		setResponseType(ctx); | 		setResponseType(ctx); | ||||||
| 	} else { | 	} else { | ||||||
| 		// index page
 | 		// index page
 | ||||||
| 		const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`, null); | 		const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`); | ||||||
| 		ctx.body = renderActivity(rendered); | 		ctx.body = renderActivity(rendered); | ||||||
| 		ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); | 		ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); | ||||||
| 		setResponseType(ctx); | 		setResponseType(ctx); | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ import { Users, Notes } from '../../models'; | ||||||
| import { makePaginationQuery } from '../api/common/make-pagination-query'; | import { makePaginationQuery } from '../api/common/make-pagination-query'; | ||||||
| import { Brackets } from 'typeorm'; | import { Brackets } from 'typeorm'; | ||||||
| import { Note } from '../../models/entities/note'; | import { Note } from '../../models/entities/note'; | ||||||
|  | import { ensure } from '../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| export default async (ctx: Router.IRouterContext) => { | export default async (ctx: Router.IRouterContext) => { | ||||||
| 	const userId = ctx.params.user; | 	const userId = ctx.params.user; | ||||||
|  | @ -73,11 +74,11 @@ export default async (ctx: Router.IRouterContext) => { | ||||||
| 			notes.length ? `${partOf}?${url.query({ | 			notes.length ? `${partOf}?${url.query({ | ||||||
| 				page: 'true', | 				page: 'true', | ||||||
| 				since_id: notes[0].id | 				since_id: notes[0].id | ||||||
| 			})}` : null,
 | 			})}` : undefined,
 | ||||||
| 			notes.length ? `${partOf}?${url.query({ | 			notes.length ? `${partOf}?${url.query({ | ||||||
| 				page: 'true', | 				page: 'true', | ||||||
| 				until_id: notes[notes.length - 1].id | 				until_id: notes[notes.length - 1].id | ||||||
| 			})}` : null
 | 			})}` : undefined
 | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		ctx.body = renderActivity(rendered); | 		ctx.body = renderActivity(rendered); | ||||||
|  | @ -99,9 +100,9 @@ export default async (ctx: Router.IRouterContext) => { | ||||||
|  * Pack Create<Note> or Announce Activity |  * Pack Create<Note> or Announce Activity | ||||||
|  * @param note Note |  * @param note Note | ||||||
|  */ |  */ | ||||||
| export async function packActivity(note: Note): Promise<object> { | export async function packActivity(note: Note): Promise<any> { | ||||||
| 	if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length == 0)) { | 	if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length == 0)) { | ||||||
| 		const renote = await Notes.findOne(note.renoteId); | 		const renote = await Notes.findOne(note.renoteId).then(ensure); | ||||||
| 		return renderAnnounce(renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`, note); | 		return renderAnnounce(renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`, note); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,11 +15,11 @@ export default (endpoint: IEndpoint, ctx: Koa.BaseContext) => new Promise((res) | ||||||
| 			ctx.status = x; | 			ctx.status = x; | ||||||
| 			ctx.body = { | 			ctx.body = { | ||||||
| 				error: { | 				error: { | ||||||
| 					message: y.message, | 					message: y!.message, | ||||||
| 					code: y.code, | 					code: y!.code, | ||||||
| 					id: y.id, | 					id: y!.id, | ||||||
| 					kind: y.kind, | 					kind: y!.kind, | ||||||
| 					...(y.info ? { info: y.info } : {}) | 					...(y!.info ? { info: y!.info } : {}) | ||||||
| 				} | 				} | ||||||
| 			}; | 			}; | ||||||
| 		} else { | 		} else { | ||||||
|  | @ -31,9 +31,9 @@ export default (endpoint: IEndpoint, ctx: Koa.BaseContext) => new Promise((res) | ||||||
| 	// Authentication
 | 	// Authentication
 | ||||||
| 	authenticate(body['i']).then(([user, app]) => { | 	authenticate(body['i']).then(([user, app]) => { | ||||||
| 		// API invoking
 | 		// API invoking
 | ||||||
| 		call(endpoint.name, user, app, body, (ctx.req as any).file).then(res => { | 		call(endpoint.name, user, app, body, (ctx.req as any).file).then((res: any) => { | ||||||
| 			reply(res); | 			reply(res); | ||||||
| 		}).catch(e => { | 		}).catch((e: ApiError) => { | ||||||
| 			reply(e.httpStatusCode ? e.httpStatusCode : e.kind == 'client' ? 400 : 500, e); | 			reply(e.httpStatusCode ? e.httpStatusCode : e.kind == 'client' ? 400 : 500, e); | ||||||
| 		}); | 		}); | ||||||
| 	}).catch(() => { | 	}).catch(() => { | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ import { User } from '../../models/entities/user'; | ||||||
| import { App } from '../../models/entities/app'; | import { App } from '../../models/entities/app'; | ||||||
| import { Users, AccessTokens, Apps } from '../../models'; | import { Users, AccessTokens, Apps } from '../../models'; | ||||||
| 
 | 
 | ||||||
| export default async (token: string): Promise<[User, App]> => { | export default async (token: string): Promise<[User | null | undefined, App | null | undefined]> => { | ||||||
| 	if (token == null) { | 	if (token == null) { | ||||||
| 		return [null, null]; | 		return [null, null]; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ const accessDenied = { | ||||||
| 	id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e' | 	id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e' | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default async (endpoint: string, user: User, app: App, data: any, file?: any) => { | export default async (endpoint: string, user: User | null | undefined, app: App | null | undefined, data: any, file?: any) => { | ||||||
| 	const isSecure = user != null && app == null; | 	const isSecure = user != null && app == null; | ||||||
| 
 | 
 | ||||||
| 	const ep = endpoints.find(e => e.name === endpoint); | 	const ep = endpoints.find(e => e.name === endpoint); | ||||||
|  | @ -39,15 +39,15 @@ export default async (endpoint: string, user: User, app: App, data: any, file?: | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (ep.meta.requireCredential && user.isSuspended) { | 	if (ep.meta.requireCredential && user!.isSuspended) { | ||||||
| 		throw new ApiError(accessDenied, { reason: 'Your account has been suspended.' }); | 		throw new ApiError(accessDenied, { reason: 'Your account has been suspended.' }); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (ep.meta.requireAdmin && !user.isAdmin) { | 	if (ep.meta.requireAdmin && !user!.isAdmin) { | ||||||
| 		throw new ApiError(accessDenied, { reason: 'You are not the admin.' }); | 		throw new ApiError(accessDenied, { reason: 'You are not the admin.' }); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (ep.meta.requireModerator && !user.isAdmin && !user.isModerator) { | 	if (ep.meta.requireModerator && !user!.isAdmin && !user!.isModerator) { | ||||||
| 		throw new ApiError(accessDenied, { reason: 'You are not a moderator.' }); | 		throw new ApiError(accessDenied, { reason: 'You are not a moderator.' }); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -61,7 +61,7 @@ export default async (endpoint: string, user: User, app: App, data: any, file?: | ||||||
| 
 | 
 | ||||||
| 	if (ep.meta.requireCredential && ep.meta.limit) { | 	if (ep.meta.requireCredential && ep.meta.limit) { | ||||||
| 		// Rate limit
 | 		// Rate limit
 | ||||||
| 		await limiter(ep, user).catch(e => { | 		await limiter(ep, user!).catch(e => { | ||||||
| 			throw new ApiError({ | 			throw new ApiError({ | ||||||
| 				message: 'Rate limit exceeded. Please try again later.', | 				message: 'Rate limit exceeded. Please try again later.', | ||||||
| 				code: 'RATE_LIMIT_EXCEEDED', | 				code: 'RATE_LIMIT_EXCEEDED', | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import { SelectQueryBuilder } from 'typeorm'; | import { SelectQueryBuilder } from 'typeorm'; | ||||||
| 
 | 
 | ||||||
| export function makePaginationQuery<T>(q: SelectQueryBuilder<T>, sinceId: string, untilId: string, sinceDate?: number, untilDate?: number) { | export function makePaginationQuery<T>(q: SelectQueryBuilder<T>, sinceId?: string, untilId?: string, sinceDate?: number, untilDate?: number) { | ||||||
| 	if (sinceId && untilId) { | 	if (sinceId && untilId) { | ||||||
| 		q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: sinceId }); | 		q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: sinceId }); | ||||||
| 		q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); | 		q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); | ||||||
|  |  | ||||||
|  | @ -5,9 +5,9 @@ import { ApiError } from './error'; | ||||||
| import { App } from '../../models/entities/app'; | import { App } from '../../models/entities/app'; | ||||||
| 
 | 
 | ||||||
| type Params<T extends IEndpointMeta> = { | type Params<T extends IEndpointMeta> = { | ||||||
| 	[P in keyof T['params']]: T['params'][P]['transform'] extends Function | 	[P in keyof T['params']]: NonNullable<T['params']>[P]['transform'] extends Function | ||||||
| 		? ReturnType<T['params'][P]['transform']> | 		? ReturnType<NonNullable<T['params']>[P]['transform']> | ||||||
| 		: ReturnType<T['params'][P]['validator']['get']>[0]; | 		: ReturnType<NonNullable<T['params']>[P]['validator']['get']>[0]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type Response = Record<string, any> | void; | export type Response = Record<string, any> | void; | ||||||
|  | @ -34,11 +34,11 @@ export default function <T extends IEndpointMeta>(meta: T, cb: (params: Params<T | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getParams<T extends IEndpointMeta>(defs: T, params: any): [Params<T>, ApiError] { | function getParams<T extends IEndpointMeta>(defs: T, params: any): [Params<T>, ApiError | null] { | ||||||
| 	if (defs.params == null) return [params, null]; | 	if (defs.params == null) return [params, null]; | ||||||
| 
 | 
 | ||||||
| 	const x: any = {}; | 	const x: any = {}; | ||||||
| 	let err: ApiError = null; | 	let err: ApiError | null = null; | ||||||
| 	Object.entries(defs.params).some(([k, def]) => { | 	Object.entries(defs.params).some(([k, def]) => { | ||||||
| 		const [v, e] = def.validator.get(params[k]); | 		const [v, e] = def.validator.get(params[k]); | ||||||
| 		if (e) { | 		if (e) { | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ export const meta = { | ||||||
| export default define(meta, async (ps) => { | export default define(meta, async (ps) => { | ||||||
| 	const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); | 	const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); | ||||||
| 
 | 
 | ||||||
| 	const reports = await query.take(ps.limit).getMany(); | 	const reports = await query.take(ps.limit!).getMany(); | ||||||
| 
 | 
 | ||||||
| 	return await AbuseUserReports.packMany(reports); | 	return await AbuseUserReports.packMany(reports); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -56,8 +56,8 @@ export default define(meta, async (ps, me) => { | ||||||
| 
 | 
 | ||||||
| 	const files = await DriveFiles.find({ | 	const files = await DriveFiles.find({ | ||||||
| 		where: q, | 		where: q, | ||||||
| 		take: ps.limit, | 		take: ps.limit!, | ||||||
| 		order: sort[ps.sort] || sort[fallback], | 		order: sort[ps.sort!] || sort[fallback], | ||||||
| 		skip: ps.offset | 		skip: ps.offset | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ export const meta = { | ||||||
| 
 | 
 | ||||||
| export default define(meta, async (ps) => { | export default define(meta, async (ps) => { | ||||||
| 	const emojis = await Emojis.find({ | 	const emojis = await Emojis.find({ | ||||||
| 		host: toPuny(ps.host) | 		host: ps.host ? toPuny(ps.host) : null | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	return emojis.map(e => ({ | 	return emojis.map(e => ({ | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import $ from 'cafy'; | ||||||
| import define from '../../../define'; | import define from '../../../define'; | ||||||
| import deleteFollowing from '../../../../../services/following/delete'; | import deleteFollowing from '../../../../../services/following/delete'; | ||||||
| import { Followings, Users } from '../../../../../models'; | import { Followings, Users } from '../../../../../models'; | ||||||
|  | import { ensure } from '../../../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| export const meta = { | export const meta = { | ||||||
| 	tags: ['admin'], | 	tags: ['admin'], | ||||||
|  | @ -22,13 +23,11 @@ export default define(meta, async (ps, me) => { | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	const pairs = await Promise.all(followings.map(f => Promise.all([ | 	const pairs = await Promise.all(followings.map(f => Promise.all([ | ||||||
| 		Users.findOne(f.followerId), | 		Users.findOne(f.followerId).then(ensure), | ||||||
| 		Users.findOne(f.followeeId) | 		Users.findOne(f.followeeId).then(ensure) | ||||||
| 	]))); | 	]))); | ||||||
| 
 | 
 | ||||||
| 	for (const pair of pairs) { | 	for (const pair of pairs) { | ||||||
| 		deleteFollowing(pair[0], pair[1]); | 		deleteFollowing(pair[0], pair[1]); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	return; |  | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -65,7 +65,7 @@ export default define(meta, async (ps) => { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	const logs = await query.orderBy('log.createdAt', 'DESC').take(ps.limit).getMany(); | 	const logs = await query.orderBy('log.createdAt', 'DESC').take(ps.limit!).getMany(); | ||||||
| 
 | 
 | ||||||
| 	return logs; | 	return logs; | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -28,9 +28,9 @@ export default define(meta, async (ps) => { | ||||||
| 	const queue = | 	const queue = | ||||||
| 		ps.domain === 'deliver' ? deliverQueue : | 		ps.domain === 'deliver' ? deliverQueue : | ||||||
| 		ps.domain === 'inbox' ? inboxQueue : | 		ps.domain === 'inbox' ? inboxQueue : | ||||||
| 		null; | 		null as never; | ||||||
| 
 | 
 | ||||||
| 	const jobs = await queue.getJobs([ps.state], 0, ps.limit); | 	const jobs = await queue.getJobs([ps.state], 0, ps.limit!); | ||||||
| 
 | 
 | ||||||
| 	return jobs.map(job => ({ | 	return jobs.map(job => ({ | ||||||
| 		id: job.id, | 		id: job.id, | ||||||
|  |  | ||||||
|  | @ -82,7 +82,7 @@ export default define(meta, async (ps, me) => { | ||||||
| 		default: query.orderBy('user.id', 'ASC'); break; | 		default: query.orderBy('user.id', 'ASC'); break; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	query.take(ps.limit); | 	query.take(ps.limit!); | ||||||
| 	query.skip(ps.offset); | 	query.skip(ps.offset); | ||||||
| 
 | 
 | ||||||
| 	const users = await query.getMany(); | 	const users = await query.getMany(); | ||||||
|  |  | ||||||
|  | @ -28,5 +28,5 @@ export const meta = { | ||||||
| 
 | 
 | ||||||
| export default define(meta, async (ps) => { | export default define(meta, async (ps) => { | ||||||
| 	const user = await getRemoteUser(ps.userId); | 	const user = await getRemoteUser(ps.userId); | ||||||
| 	await updatePerson(user.uri); | 	await updatePerson(user.uri!); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -123,14 +123,14 @@ async function fetchAny(uri: string) { | ||||||
| 		const note = await createNote(object.id); | 		const note = await createNote(object.id); | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Note', | 			type: 'Note', | ||||||
| 			object: await Notes.pack(note, null, { detail: true }) | 			object: await Notes.pack(note!, null, { detail: true }) | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return null; | 	return null; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function mergePack(user: User, note: Note) { | async function mergePack(user: User | null | undefined, note: Note | null | undefined) { | ||||||
| 	if (user != null) { | 	if (user != null) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'User', | 			type: 'User', | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import define from '../../define'; | ||||||
| import { ApiError } from '../../error'; | import { ApiError } from '../../error'; | ||||||
| import { AuthSessions, AccessTokens, Apps } from '../../../../models'; | import { AuthSessions, AccessTokens, Apps } from '../../../../models'; | ||||||
| import { genId } from '../../../../misc/gen-id'; | import { genId } from '../../../../misc/gen-id'; | ||||||
|  | import { ensure } from '../../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| export const meta = { | export const meta = { | ||||||
| 	tags: ['auth'], | 	tags: ['auth'], | ||||||
|  | @ -48,7 +49,7 @@ export default define(meta, async (ps, user) => { | ||||||
| 
 | 
 | ||||||
| 	if (exist == null) { | 	if (exist == null) { | ||||||
| 		// Lookup app
 | 		// Lookup app
 | ||||||
| 		const app = await Apps.findOne(session.appId); | 		const app = await Apps.findOne(session.appId).then(ensure); | ||||||
| 
 | 
 | ||||||
| 		// Generate Hash
 | 		// Generate Hash
 | ||||||
| 		const sha256 = crypto.createHash('sha256'); | 		const sha256 = crypto.createHash('sha256'); | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import $ from 'cafy'; | ||||||
| import define from '../../../define'; | import define from '../../../define'; | ||||||
| import { ApiError } from '../../../error'; | import { ApiError } from '../../../error'; | ||||||
| import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models'; | import { Apps, AuthSessions, AccessTokens, Users } from '../../../../../models'; | ||||||
|  | import { ensure } from '../../../../../prelude/ensure'; | ||||||
| 
 | 
 | ||||||
| export const meta = { | export const meta = { | ||||||
| 	tags: ['auth'], | 	tags: ['auth'], | ||||||
|  | @ -90,7 +91,7 @@ export default define(meta, async (ps) => { | ||||||
| 	const accessToken = await AccessTokens.findOne({ | 	const accessToken = await AccessTokens.findOne({ | ||||||
| 		appId: app.id, | 		appId: app.id, | ||||||
| 		userId: session.userId | 		userId: session.userId | ||||||
| 	}); | 	}).then(ensure); | ||||||
| 
 | 
 | ||||||
| 	// Delete session
 | 	// Delete session
 | ||||||
| 	AuthSessions.delete(session.id); | 	AuthSessions.delete(session.id); | ||||||
|  |  | ||||||
|  | @ -44,7 +44,7 @@ export default define(meta, async (ps, me) => { | ||||||
| 		.andWhere(`blocking.blockerId = :meId`, { meId: me.id }); | 		.andWhere(`blocking.blockerId = :meId`, { meId: me.id }); | ||||||
| 
 | 
 | ||||||
| 	const blockings = await query | 	const blockings = await query | ||||||
| 		.take(ps.limit) | 		.take(ps.limit!) | ||||||
| 		.getMany(); | 		.getMany(); | ||||||
| 
 | 
 | ||||||
| 	return await Blockings.packMany(blockings, me); | 	return await Blockings.packMany(blockings, me); | ||||||
|  |  | ||||||
|  | @ -33,5 +33,5 @@ export const meta = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default define(meta, async (ps) => { | export default define(meta, async (ps) => { | ||||||
| 	return await activeUsersChart.getChart(ps.span as any, ps.limit); | 	return await activeUsersChart.getChart(ps.span as any, ps.limit!); | ||||||
| }); | }); | ||||||
|  |  | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue