テーブル分割
This commit is contained in:
		
							parent
							
								
									9603f3fa4f
								
							
						
					
					
						commit
						626cfb61ac
					
				
					 33 changed files with 267 additions and 232 deletions
				
			
		| 
						 | 
					@ -36,10 +36,10 @@ import { Emoji } from '../models/entities/emoji';
 | 
				
			||||||
import { ReversiGame } from '../models/entities/games/reversi/game';
 | 
					import { ReversiGame } from '../models/entities/games/reversi/game';
 | 
				
			||||||
import { ReversiMatching } from '../models/entities/games/reversi/matching';
 | 
					import { ReversiMatching } from '../models/entities/games/reversi/matching';
 | 
				
			||||||
import { UserNotePining } from '../models/entities/user-note-pinings';
 | 
					import { UserNotePining } from '../models/entities/user-note-pinings';
 | 
				
			||||||
import { UserServiceLinking } from '../models/entities/user-service-linking';
 | 
					 | 
				
			||||||
import { Poll } from '../models/entities/poll';
 | 
					import { Poll } from '../models/entities/poll';
 | 
				
			||||||
import { UserKeypair } from '../models/entities/user-keypair';
 | 
					import { UserKeypair } from '../models/entities/user-keypair';
 | 
				
			||||||
import { UserPublickey } from '../models/entities/user-publickey';
 | 
					import { UserPublickey } from '../models/entities/user-publickey';
 | 
				
			||||||
 | 
					import { UserProfile } from '../models/entities/user-profile';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
 | 
					const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,12 +101,12 @@ export function initDb(justBorrow = false, sync = false, log = false) {
 | 
				
			||||||
			AuthSession,
 | 
								AuthSession,
 | 
				
			||||||
			AccessToken,
 | 
								AccessToken,
 | 
				
			||||||
			User,
 | 
								User,
 | 
				
			||||||
 | 
								UserProfile,
 | 
				
			||||||
			UserKeypair,
 | 
								UserKeypair,
 | 
				
			||||||
			UserPublickey,
 | 
								UserPublickey,
 | 
				
			||||||
			UserList,
 | 
								UserList,
 | 
				
			||||||
			UserListJoining,
 | 
								UserListJoining,
 | 
				
			||||||
			UserNotePining,
 | 
								UserNotePining,
 | 
				
			||||||
			UserServiceLinking,
 | 
					 | 
				
			||||||
			Following,
 | 
								Following,
 | 
				
			||||||
			FollowRequest,
 | 
								FollowRequest,
 | 
				
			||||||
			Muting,
 | 
								Muting,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@ import { UserKeypair } from './models/entities/user-keypair';
 | 
				
			||||||
import { extractPublic } from './crypto_key';
 | 
					import { extractPublic } from './crypto_key';
 | 
				
			||||||
import { Emoji } from './models/entities/emoji';
 | 
					import { Emoji } from './models/entities/emoji';
 | 
				
			||||||
import { toPuny } from './misc/convert-host';
 | 
					import { toPuny } from './misc/convert-host';
 | 
				
			||||||
 | 
					import { UserProfile } from './models/entities/user-profile';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const u = (config as any).mongodb.user ? encodeURIComponent((config as any).mongodb.user) : null;
 | 
					const u = (config as any).mongodb.user ? encodeURIComponent((config as any).mongodb.user) : null;
 | 
				
			||||||
const p = (config as any).mongodb.pass ? encodeURIComponent((config as any).mongodb.pass) : null;
 | 
					const p = (config as any).mongodb.pass ? encodeURIComponent((config as any).mongodb.pass) : null;
 | 
				
			||||||
| 
						 | 
					@ -70,6 +71,7 @@ const getDriveFileBucket = async (): Promise<mongo.GridFSBucket> => {
 | 
				
			||||||
async function main() {
 | 
					async function main() {
 | 
				
			||||||
	await initDb();
 | 
						await initDb();
 | 
				
			||||||
	const Users = getRepository(User);
 | 
						const Users = getRepository(User);
 | 
				
			||||||
 | 
						const UserProfiles = getRepository(UserProfile);
 | 
				
			||||||
	const DriveFiles = getRepository(DriveFile);
 | 
						const DriveFiles = getRepository(DriveFile);
 | 
				
			||||||
	const DriveFolders = getRepository(DriveFolder);
 | 
						const DriveFolders = getRepository(DriveFolder);
 | 
				
			||||||
	const Notes = getRepository(Note);
 | 
						const Notes = getRepository(Note);
 | 
				
			||||||
| 
						 | 
					@ -90,17 +92,11 @@ async function main() {
 | 
				
			||||||
			usernameLower: user.username.toLowerCase(),
 | 
								usernameLower: user.username.toLowerCase(),
 | 
				
			||||||
			host: toPuny(user.host),
 | 
								host: toPuny(user.host),
 | 
				
			||||||
			token: generateUserToken(),
 | 
								token: generateUserToken(),
 | 
				
			||||||
			password: user.password,
 | 
					 | 
				
			||||||
			isAdmin: user.isAdmin,
 | 
								isAdmin: user.isAdmin,
 | 
				
			||||||
			autoAcceptFollowed: true,
 | 
					 | 
				
			||||||
			autoWatch: false,
 | 
					 | 
				
			||||||
			name: user.name,
 | 
								name: user.name,
 | 
				
			||||||
			location: user.profile ? user.profile.location : null,
 | 
					 | 
				
			||||||
			birthday: user.profile ? user.profile.birthday : null,
 | 
					 | 
				
			||||||
			followersCount: user.followersCount,
 | 
								followersCount: user.followersCount,
 | 
				
			||||||
			followingCount: user.followingCount,
 | 
								followingCount: user.followingCount,
 | 
				
			||||||
			notesCount: user.notesCount,
 | 
								notesCount: user.notesCount,
 | 
				
			||||||
			description: user.description,
 | 
					 | 
				
			||||||
			isBot: user.isBot,
 | 
								isBot: user.isBot,
 | 
				
			||||||
			isCat: user.isCat,
 | 
								isCat: user.isCat,
 | 
				
			||||||
			isVerified: user.isVerified,
 | 
								isVerified: user.isVerified,
 | 
				
			||||||
| 
						 | 
					@ -108,9 +104,18 @@ async function main() {
 | 
				
			||||||
			sharedInbox: user.sharedInbox,
 | 
								sharedInbox: user.sharedInbox,
 | 
				
			||||||
			uri: user.uri,
 | 
								uri: user.uri,
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
							await UserProfiles.save({
 | 
				
			||||||
 | 
								userId: user._id.toHexString(),
 | 
				
			||||||
 | 
								description: user.description,
 | 
				
			||||||
 | 
								userHost: toPuny(user.host),
 | 
				
			||||||
 | 
								autoAcceptFollowed: true,
 | 
				
			||||||
 | 
								autoWatch: false,
 | 
				
			||||||
 | 
								password: user.password,
 | 
				
			||||||
 | 
								location: user.profile ? user.profile.location : null,
 | 
				
			||||||
 | 
								birthday: user.profile ? user.profile.birthday : null,
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
		if (user.publicKey) {
 | 
							if (user.publicKey) {
 | 
				
			||||||
			await UserPublickeys.save({
 | 
								await UserPublickeys.save({
 | 
				
			||||||
				id: genId(),
 | 
					 | 
				
			||||||
				userId: user._id.toHexString(),
 | 
									userId: user._id.toHexString(),
 | 
				
			||||||
				keyId: user.publicKey.id,
 | 
									keyId: user.publicKey.id,
 | 
				
			||||||
				keyPem: user.publicKey.publicKeyPem
 | 
									keyPem: user.publicKey.publicKeyPem
 | 
				
			||||||
| 
						 | 
					@ -118,7 +123,6 @@ async function main() {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (user.keypair) {
 | 
							if (user.keypair) {
 | 
				
			||||||
			await UserKeypairs.save({
 | 
								await UserKeypairs.save({
 | 
				
			||||||
				id: genId(),
 | 
					 | 
				
			||||||
				userId: user._id.toHexString(),
 | 
									userId: user._id.toHexString(),
 | 
				
			||||||
				publicKey: extractPublic(user.keypair),
 | 
									publicKey: extractPublic(user.keypair),
 | 
				
			||||||
				privateKey: user.keypair,
 | 
									privateKey: user.keypair,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,11 +4,8 @@ import { id } from '../id';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Entity()
 | 
					@Entity()
 | 
				
			||||||
export class UserKeypair {
 | 
					export class UserKeypair {
 | 
				
			||||||
	@PrimaryColumn(id())
 | 
					 | 
				
			||||||
	public id: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Index({ unique: true })
 | 
						@Index({ unique: true })
 | 
				
			||||||
	@Column(id())
 | 
						@PrimaryColumn(id())
 | 
				
			||||||
	public userId: User['id'];
 | 
						public userId: User['id'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@OneToOne(type => User, {
 | 
						@OneToOne(type => User, {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,11 @@
 | 
				
			||||||
import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm';
 | 
					import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
 | 
				
			||||||
import { User } from './user';
 | 
					 | 
				
			||||||
import { id } from '../id';
 | 
					import { id } from '../id';
 | 
				
			||||||
 | 
					import { User } from './user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Entity()
 | 
					@Entity()
 | 
				
			||||||
export class UserServiceLinking {
 | 
					export class UserProfile {
 | 
				
			||||||
	@PrimaryColumn(id())
 | 
					 | 
				
			||||||
	public id: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Index({ unique: true })
 | 
						@Index({ unique: true })
 | 
				
			||||||
	@Column(id())
 | 
						@PrimaryColumn(id())
 | 
				
			||||||
	public userId: User['id'];
 | 
						public userId: User['id'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@OneToOne(type => User, {
 | 
						@OneToOne(type => User, {
 | 
				
			||||||
| 
						 | 
					@ -17,6 +14,96 @@ export class UserServiceLinking {
 | 
				
			||||||
	@JoinColumn()
 | 
						@JoinColumn()
 | 
				
			||||||
	public user: User | null;
 | 
						public user: User | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('varchar', {
 | 
				
			||||||
 | 
							length: 128, nullable: true,
 | 
				
			||||||
 | 
							comment: 'The location of the User.'
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public location: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('char', {
 | 
				
			||||||
 | 
							length: 10, nullable: true,
 | 
				
			||||||
 | 
							comment: 'The birthday (YYYY-MM-DD) of the User.'
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public birthday: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('varchar', {
 | 
				
			||||||
 | 
							length: 1024, nullable: true,
 | 
				
			||||||
 | 
							comment: 'The description (bio) of the User.'
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public description: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('jsonb', {
 | 
				
			||||||
 | 
							default: [],
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public fields: {
 | 
				
			||||||
 | 
							name: string;
 | 
				
			||||||
 | 
							value: string;
 | 
				
			||||||
 | 
						}[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('varchar', {
 | 
				
			||||||
 | 
							length: 128, nullable: true,
 | 
				
			||||||
 | 
							comment: 'The email address of the User.'
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public email: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('varchar', {
 | 
				
			||||||
 | 
							length: 128, nullable: true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public emailVerifyCode: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('boolean', {
 | 
				
			||||||
 | 
							default: false,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public emailVerified: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('varchar', {
 | 
				
			||||||
 | 
							length: 128, nullable: true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public twoFactorTempSecret: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('varchar', {
 | 
				
			||||||
 | 
							length: 128, nullable: true,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public twoFactorSecret: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('boolean', {
 | 
				
			||||||
 | 
							default: false,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public twoFactorEnabled: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('varchar', {
 | 
				
			||||||
 | 
							length: 128, nullable: true,
 | 
				
			||||||
 | 
							comment: 'The password hash of the User. It will be null if the origin of the user is local.'
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public password: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('jsonb', {
 | 
				
			||||||
 | 
							default: {},
 | 
				
			||||||
 | 
							comment: 'The client-specific data of the User.'
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public clientData: Record<string, any>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('boolean', {
 | 
				
			||||||
 | 
							default: false,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public autoWatch: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('boolean', {
 | 
				
			||||||
 | 
							default: false,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public autoAcceptFollowed: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('boolean', {
 | 
				
			||||||
 | 
							default: false,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public alwaysMarkNsfw: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Column('boolean', {
 | 
				
			||||||
 | 
							default: false,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						public carefulBot: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//#region Linking
 | 
				
			||||||
	@Column('boolean', {
 | 
						@Column('boolean', {
 | 
				
			||||||
		default: false,
 | 
							default: false,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
| 
						 | 
					@ -96,6 +183,7 @@ export class UserServiceLinking {
 | 
				
			||||||
		length: 64, nullable: true, default: null,
 | 
							length: 64, nullable: true, default: null,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	public discordDiscriminator: string | null;
 | 
						public discordDiscriminator: string | null;
 | 
				
			||||||
 | 
						//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//#region Denormalized fields
 | 
						//#region Denormalized fields
 | 
				
			||||||
	@Index()
 | 
						@Index()
 | 
				
			||||||
| 
						 | 
					@ -4,11 +4,8 @@ import { id } from '../id';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Entity()
 | 
					@Entity()
 | 
				
			||||||
export class UserPublickey {
 | 
					export class UserPublickey {
 | 
				
			||||||
	@PrimaryColumn(id())
 | 
					 | 
				
			||||||
	public id: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Index({ unique: true })
 | 
						@Index({ unique: true })
 | 
				
			||||||
	@Column(id())
 | 
						@PrimaryColumn(id())
 | 
				
			||||||
	public userId: User['id'];
 | 
						public userId: User['id'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@OneToOne(type => User, {
 | 
						@OneToOne(type => User, {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,18 +45,6 @@ export class User {
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	public name: string | null;
 | 
						public name: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Column('varchar', {
 | 
					 | 
				
			||||||
		length: 128, nullable: true,
 | 
					 | 
				
			||||||
		comment: 'The location of the User.'
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public location: string | null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('char', {
 | 
					 | 
				
			||||||
		length: 10, nullable: true,
 | 
					 | 
				
			||||||
		comment: 'The birthday (YYYY-MM-DD) of the User.'
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public birthday: string | null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('integer', {
 | 
						@Column('integer', {
 | 
				
			||||||
		default: 0,
 | 
							default: 0,
 | 
				
			||||||
		comment: 'The count of followers.'
 | 
							comment: 'The count of followers.'
 | 
				
			||||||
| 
						 | 
					@ -101,44 +89,12 @@ export class User {
 | 
				
			||||||
	@JoinColumn()
 | 
						@JoinColumn()
 | 
				
			||||||
	public banner: DriveFile | null;
 | 
						public banner: DriveFile | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Column('varchar', {
 | 
					 | 
				
			||||||
		length: 1024, nullable: true,
 | 
					 | 
				
			||||||
		comment: 'The description (bio) of the User.'
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public description: string | null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Index()
 | 
						@Index()
 | 
				
			||||||
	@Column('varchar', {
 | 
						@Column('varchar', {
 | 
				
			||||||
		length: 128, array: true, default: '{}'
 | 
							length: 128, array: true, default: '{}'
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	public tags: string[];
 | 
						public tags: string[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Column('varchar', {
 | 
					 | 
				
			||||||
		length: 128, nullable: true,
 | 
					 | 
				
			||||||
		comment: 'The email address of the User.'
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public email: string | null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('varchar', {
 | 
					 | 
				
			||||||
		length: 128, nullable: true,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public emailVerifyCode: string | null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('boolean', {
 | 
					 | 
				
			||||||
		default: false,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public emailVerified: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('varchar', {
 | 
					 | 
				
			||||||
		length: 128, nullable: true,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public twoFactorTempSecret: string | null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('varchar', {
 | 
					 | 
				
			||||||
		length: 128, nullable: true,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public twoFactorSecret: string | null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('varchar', {
 | 
						@Column('varchar', {
 | 
				
			||||||
		length: 256, nullable: true,
 | 
							length: 256, nullable: true,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
| 
						 | 
					@ -206,11 +162,6 @@ export class User {
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	public isVerified: boolean;
 | 
						public isVerified: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Column('boolean', {
 | 
					 | 
				
			||||||
		default: false,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public twoFactorEnabled: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('varchar', {
 | 
						@Column('varchar', {
 | 
				
			||||||
		length: 128, array: true, default: '{}'
 | 
							length: 128, array: true, default: '{}'
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
| 
						 | 
					@ -248,44 +199,12 @@ export class User {
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	public uri: string | null;
 | 
						public uri: string | null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Column('varchar', {
 | 
					 | 
				
			||||||
		length: 128, nullable: true,
 | 
					 | 
				
			||||||
		comment: 'The password hash of the User. It will be null if the origin of the user is local.'
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public password: string | null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Index({ unique: true })
 | 
						@Index({ unique: true })
 | 
				
			||||||
	@Column('char', {
 | 
						@Column('char', {
 | 
				
			||||||
		length: 16, nullable: true, unique: true,
 | 
							length: 16, nullable: true, unique: true,
 | 
				
			||||||
		comment: 'The native access token of the User. It will be null if the origin of the user is local.'
 | 
							comment: 'The native access token of the User. It will be null if the origin of the user is local.'
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	public token: string | null;
 | 
						public token: string | null;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('jsonb', {
 | 
					 | 
				
			||||||
		default: {},
 | 
					 | 
				
			||||||
		comment: 'The client-specific data of the User.'
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public clientData: Record<string, any>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('boolean', {
 | 
					 | 
				
			||||||
		default: false,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public autoWatch: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('boolean', {
 | 
					 | 
				
			||||||
		default: false,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public autoAcceptFollowed: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('boolean', {
 | 
					 | 
				
			||||||
		default: false,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public alwaysMarkNsfw: boolean;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@Column('boolean', {
 | 
					 | 
				
			||||||
		default: false,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	public carefulBot: boolean;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ILocalUser extends User {
 | 
					export interface ILocalUser extends User {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,6 @@ import { FollowRequestRepository } from './repositories/follow-request';
 | 
				
			||||||
import { MutingRepository } from './repositories/muting';
 | 
					import { MutingRepository } from './repositories/muting';
 | 
				
			||||||
import { BlockingRepository } from './repositories/blocking';
 | 
					import { BlockingRepository } from './repositories/blocking';
 | 
				
			||||||
import { NoteReactionRepository } from './repositories/note-reaction';
 | 
					import { NoteReactionRepository } from './repositories/note-reaction';
 | 
				
			||||||
import { UserServiceLinking } from './entities/user-service-linking';
 | 
					 | 
				
			||||||
import { NotificationRepository } from './repositories/notification';
 | 
					import { NotificationRepository } from './repositories/notification';
 | 
				
			||||||
import { NoteFavoriteRepository } from './repositories/note-favorite';
 | 
					import { NoteFavoriteRepository } from './repositories/note-favorite';
 | 
				
			||||||
import { ReversiMatchingRepository } from './repositories/games/reversi/matching';
 | 
					import { ReversiMatchingRepository } from './repositories/games/reversi/matching';
 | 
				
			||||||
| 
						 | 
					@ -35,6 +34,7 @@ import { AppRepository } from './repositories/app';
 | 
				
			||||||
import { FollowingRepository } from './repositories/following';
 | 
					import { FollowingRepository } from './repositories/following';
 | 
				
			||||||
import { AbuseUserReportRepository } from './repositories/abuse-user-report';
 | 
					import { AbuseUserReportRepository } from './repositories/abuse-user-report';
 | 
				
			||||||
import { AuthSessionRepository } from './repositories/auth-session';
 | 
					import { AuthSessionRepository } from './repositories/auth-session';
 | 
				
			||||||
 | 
					import { UserProfile } from './entities/user-profile';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const Apps = getCustomRepository(AppRepository);
 | 
					export const Apps = getCustomRepository(AppRepository);
 | 
				
			||||||
export const Notes = getCustomRepository(NoteRepository);
 | 
					export const Notes = getCustomRepository(NoteRepository);
 | 
				
			||||||
| 
						 | 
					@ -45,12 +45,12 @@ export const NoteUnreads = getRepository(NoteUnread);
 | 
				
			||||||
export const Polls = getRepository(Poll);
 | 
					export const Polls = getRepository(Poll);
 | 
				
			||||||
export const PollVotes = getRepository(PollVote);
 | 
					export const PollVotes = getRepository(PollVote);
 | 
				
			||||||
export const Users = getCustomRepository(UserRepository);
 | 
					export const Users = getCustomRepository(UserRepository);
 | 
				
			||||||
 | 
					export const UserProfiles = getRepository(UserProfile);
 | 
				
			||||||
export const UserKeypairs = getRepository(UserKeypair);
 | 
					export const UserKeypairs = getRepository(UserKeypair);
 | 
				
			||||||
export const UserPublickeys = getRepository(UserPublickey);
 | 
					export const UserPublickeys = getRepository(UserPublickey);
 | 
				
			||||||
export const UserLists = getCustomRepository(UserListRepository);
 | 
					export const UserLists = getCustomRepository(UserListRepository);
 | 
				
			||||||
export const UserListJoinings = getRepository(UserListJoining);
 | 
					export const UserListJoinings = getRepository(UserListJoining);
 | 
				
			||||||
export const UserNotePinings = getRepository(UserNotePining);
 | 
					export const UserNotePinings = getRepository(UserNotePining);
 | 
				
			||||||
export const UserServiceLinkings = getRepository(UserServiceLinking);
 | 
					 | 
				
			||||||
export const Followings = getCustomRepository(FollowingRepository);
 | 
					export const Followings = getCustomRepository(FollowingRepository);
 | 
				
			||||||
export const FollowRequests = getCustomRepository(FollowRequestRepository);
 | 
					export const FollowRequests = getCustomRepository(FollowRequestRepository);
 | 
				
			||||||
export const Instances = getRepository(Instance);
 | 
					export const Instances = getRepository(Instance);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
import { EntityRepository, Repository, In } from 'typeorm';
 | 
					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 } from '..';
 | 
					import { Emojis, Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles } from '..';
 | 
				
			||||||
import rap from '@prezzemolo/rap';
 | 
					import rap from '@prezzemolo/rap';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@EntityRepository(User)
 | 
					@EntityRepository(User)
 | 
				
			||||||
| 
						 | 
					@ -80,6 +80,7 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return await rap({
 | 
							return await rap({
 | 
				
			||||||
			id: user.id,
 | 
								id: user.id,
 | 
				
			||||||
| 
						 | 
					@ -116,9 +117,9 @@ export class UserRepository extends Repository<User> {
 | 
				
			||||||
			} : {}),
 | 
								} : {}),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			...(opts.detail ? {
 | 
								...(opts.detail ? {
 | 
				
			||||||
				description: user.description,
 | 
									description: profile.description,
 | 
				
			||||||
				location: user.location,
 | 
									location: profile.location,
 | 
				
			||||||
				birthday: user.birthday,
 | 
									birthday: profile.birthday,
 | 
				
			||||||
				followersCount: user.followersCount,
 | 
									followersCount: user.followersCount,
 | 
				
			||||||
				followingCount: user.followingCount,
 | 
									followingCount: user.followingCount,
 | 
				
			||||||
				notesCount: user.notesCount,
 | 
									notesCount: user.notesCount,
 | 
				
			||||||
| 
						 | 
					@ -131,9 +132,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: user.autoWatch,
 | 
									autoWatch: profile.autoWatch,
 | 
				
			||||||
				alwaysMarkNsfw: user.alwaysMarkNsfw,
 | 
									alwaysMarkNsfw: profile.alwaysMarkNsfw,
 | 
				
			||||||
				carefulBot: user.carefulBot,
 | 
									carefulBot: profile.carefulBot,
 | 
				
			||||||
				hasUnreadMessagingMessage: MessagingMessages.count({
 | 
									hasUnreadMessagingMessage: MessagingMessages.count({
 | 
				
			||||||
					where: {
 | 
										where: {
 | 
				
			||||||
						recipientId: user.id,
 | 
											recipientId: user.id,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,16 +14,16 @@ import { IIdentifier } from './identifier';
 | 
				
			||||||
import { apLogger } from '../logger';
 | 
					import { apLogger } from '../logger';
 | 
				
			||||||
import { Note } from '../../../models/entities/note';
 | 
					import { Note } from '../../../models/entities/note';
 | 
				
			||||||
import { updateHashtag } from '../../../services/update-hashtag';
 | 
					import { updateHashtag } from '../../../services/update-hashtag';
 | 
				
			||||||
import { Users, UserNotePinings, Instances, DriveFiles, Followings, UserServiceLinkings, UserPublickeys } from '../../../models';
 | 
					import { Users, UserNotePinings, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '../../../models';
 | 
				
			||||||
import { User, IRemoteUser } from '../../../models/entities/user';
 | 
					import { User, IRemoteUser } from '../../../models/entities/user';
 | 
				
			||||||
import { Emoji } from '../../../models/entities/emoji';
 | 
					import { Emoji } from '../../../models/entities/emoji';
 | 
				
			||||||
import { UserNotePining } from '../../../models/entities/user-note-pinings';
 | 
					import { UserNotePining } from '../../../models/entities/user-note-pinings';
 | 
				
			||||||
import { genId } from '../../../misc/gen-id';
 | 
					import { genId } from '../../../misc/gen-id';
 | 
				
			||||||
import { UserServiceLinking } from '../../../models/entities/user-service-linking';
 | 
					 | 
				
			||||||
import { instanceChart, usersChart } from '../../../services/chart';
 | 
					import { instanceChart, usersChart } from '../../../services/chart';
 | 
				
			||||||
import { UserPublickey } from '../../../models/entities/user-publickey';
 | 
					import { UserPublickey } from '../../../models/entities/user-publickey';
 | 
				
			||||||
import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-error';
 | 
					import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-error';
 | 
				
			||||||
import { toPuny } from '../../../misc/convert-host';
 | 
					import { toPuny } from '../../../misc/convert-host';
 | 
				
			||||||
 | 
					import { UserProfile } from '../../../models/entities/user-profile';
 | 
				
			||||||
const logger = apLogger;
 | 
					const logger = apLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -126,7 +126,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, services } = 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());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -141,7 +141,6 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
 | 
				
			||||||
			bannerId: null,
 | 
								bannerId: null,
 | 
				
			||||||
			createdAt: Date.parse(person.published) || new Date(),
 | 
								createdAt: Date.parse(person.published) || new Date(),
 | 
				
			||||||
			lastFetchedAt: new Date(),
 | 
								lastFetchedAt: new Date(),
 | 
				
			||||||
			description: fromHtml(person.summary),
 | 
					 | 
				
			||||||
			name: person.name,
 | 
								name: person.name,
 | 
				
			||||||
			isLocked: person.manuallyApprovesFollowers,
 | 
								isLocked: person.manuallyApprovesFollowers,
 | 
				
			||||||
			username: person.preferredUsername,
 | 
								username: person.preferredUsername,
 | 
				
			||||||
| 
						 | 
					@ -153,8 +152,6 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
 | 
				
			||||||
			endpoints: person.endpoints,
 | 
								endpoints: person.endpoints,
 | 
				
			||||||
			uri: person.id,
 | 
								uri: person.id,
 | 
				
			||||||
			url: person.url,
 | 
								url: person.url,
 | 
				
			||||||
			fields,
 | 
					 | 
				
			||||||
			...services,
 | 
					 | 
				
			||||||
			tags,
 | 
								tags,
 | 
				
			||||||
			isBot,
 | 
								isBot,
 | 
				
			||||||
			isCat: (person as any).isCat === true
 | 
								isCat: (person as any).isCat === true
 | 
				
			||||||
| 
						 | 
					@ -169,18 +166,18 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
 | 
				
			||||||
		throw e;
 | 
							throw e;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						await UserProfiles.save({
 | 
				
			||||||
 | 
							userId: user.id,
 | 
				
			||||||
 | 
							description: fromHtml(person.summary),
 | 
				
			||||||
 | 
							fields,
 | 
				
			||||||
 | 
						} as Partial<UserProfile>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await UserPublickeys.save({
 | 
						await UserPublickeys.save({
 | 
				
			||||||
		id: genId(),
 | 
					 | 
				
			||||||
		userId: user.id,
 | 
							userId: user.id,
 | 
				
			||||||
		keyId: person.publicKey.id,
 | 
							keyId: person.publicKey.id,
 | 
				
			||||||
		keyPem: person.publicKey.publicKeyPem
 | 
							keyPem: person.publicKey.publicKeyPem
 | 
				
			||||||
	} as UserPublickey);
 | 
						} as UserPublickey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await UserServiceLinkings.save({
 | 
					 | 
				
			||||||
		id: genId(),
 | 
					 | 
				
			||||||
		userId: user.id
 | 
					 | 
				
			||||||
	} as UserServiceLinking);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Register host
 | 
						// Register host
 | 
				
			||||||
	registerOrFetchInstanceDoc(host).then(i => {
 | 
						registerOrFetchInstanceDoc(host).then(i => {
 | 
				
			||||||
		Instances.increment({ id: i.id }, 'usersCount', 1);
 | 
							Instances.increment({ id: i.id }, 'usersCount', 1);
 | 
				
			||||||
| 
						 | 
					@ -347,7 +344,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje
 | 
				
			||||||
		keyPem: person.publicKey.publicKeyPem
 | 
							keyPem: person.publicKey.publicKeyPem
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await UserServiceLinkings.update({ userId: exist.id }, {
 | 
						await UserProfiles.update({ userId: exist.id }, {
 | 
				
			||||||
		twitterUserId: services.twitter.userId,
 | 
							twitterUserId: services.twitter.userId,
 | 
				
			||||||
		twitterScreenName: services.twitter.screenName,
 | 
							twitterScreenName: services.twitter.screenName,
 | 
				
			||||||
		githubId: services.github.id,
 | 
							githubId: services.github.id,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,15 +8,15 @@ import { getEmojis } from './note';
 | 
				
			||||||
import renderEmoji from './emoji';
 | 
					import renderEmoji from './emoji';
 | 
				
			||||||
import { IIdentifier } from '../models/identifier';
 | 
					import { IIdentifier } from '../models/identifier';
 | 
				
			||||||
import renderHashtag from './hashtag';
 | 
					import renderHashtag from './hashtag';
 | 
				
			||||||
import { DriveFiles, UserServiceLinkings, UserKeypairs } from '../../../models';
 | 
					import { DriveFiles, UserProfiles, UserKeypairs } from '../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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, links] = await Promise.all([
 | 
						const [avatar, banner, profile] = await Promise.all([
 | 
				
			||||||
		DriveFiles.findOne(user.avatarId),
 | 
							DriveFiles.findOne(user.avatarId),
 | 
				
			||||||
		DriveFiles.findOne(user.bannerId),
 | 
							DriveFiles.findOne(user.bannerId),
 | 
				
			||||||
		UserServiceLinkings.findOne({ userId: user.id })
 | 
							UserProfiles.findOne({ userId: user.id })
 | 
				
			||||||
	]);
 | 
						]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const attachment: {
 | 
						const attachment: {
 | 
				
			||||||
| 
						 | 
					@ -27,41 +27,41 @@ export async function renderPerson(user: ILocalUser) {
 | 
				
			||||||
		identifier?: IIdentifier
 | 
							identifier?: IIdentifier
 | 
				
			||||||
	}[] = [];
 | 
						}[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (links.twitter) {
 | 
						if (profile.twitter) {
 | 
				
			||||||
		attachment.push({
 | 
							attachment.push({
 | 
				
			||||||
			type: 'PropertyValue',
 | 
								type: 'PropertyValue',
 | 
				
			||||||
			name: 'Twitter',
 | 
								name: 'Twitter',
 | 
				
			||||||
			value: `<a href="https://twitter.com/intent/user?user_id=${links.twitterUserId}" rel="me nofollow noopener" target="_blank"><span>@${links.twitterScreenName}</span></a>`,
 | 
								value: `<a href="https://twitter.com/intent/user?user_id=${profile.twitterUserId}" rel="me nofollow noopener" target="_blank"><span>@${profile.twitterScreenName}</span></a>`,
 | 
				
			||||||
			identifier: {
 | 
								identifier: {
 | 
				
			||||||
				type: 'PropertyValue',
 | 
									type: 'PropertyValue',
 | 
				
			||||||
				name: 'misskey:authentication:twitter',
 | 
									name: 'misskey:authentication:twitter',
 | 
				
			||||||
				value: `${links.twitterUserId}@${links.twitterScreenName}`
 | 
									value: `${profile.twitterUserId}@${profile.twitterScreenName}`
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (links.github) {
 | 
						if (profile.github) {
 | 
				
			||||||
		attachment.push({
 | 
							attachment.push({
 | 
				
			||||||
			type: 'PropertyValue',
 | 
								type: 'PropertyValue',
 | 
				
			||||||
			name: 'GitHub',
 | 
								name: 'GitHub',
 | 
				
			||||||
			value: `<a href="https://github.com/${links.githubLogin}" rel="me nofollow noopener" target="_blank"><span>@${links.githubLogin}</span></a>`,
 | 
								value: `<a href="https://github.com/${profile.githubLogin}" rel="me nofollow noopener" target="_blank"><span>@${profile.githubLogin}</span></a>`,
 | 
				
			||||||
			identifier: {
 | 
								identifier: {
 | 
				
			||||||
				type: 'PropertyValue',
 | 
									type: 'PropertyValue',
 | 
				
			||||||
				name: 'misskey:authentication:github',
 | 
									name: 'misskey:authentication:github',
 | 
				
			||||||
				value: `${links.githubId}@${links.githubLogin}`
 | 
									value: `${profile.githubId}@${profile.githubLogin}`
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (links.discord) {
 | 
						if (profile.discord) {
 | 
				
			||||||
		attachment.push({
 | 
							attachment.push({
 | 
				
			||||||
			type: 'PropertyValue',
 | 
								type: 'PropertyValue',
 | 
				
			||||||
			name: 'Discord',
 | 
								name: 'Discord',
 | 
				
			||||||
			value: `<a href="https://discordapp.com/users/${links.discordId}" rel="me nofollow noopener" target="_blank"><span>${links.discordUsername}#${links.discordDiscriminator}</span></a>`,
 | 
								value: `<a href="https://discordapp.com/users/${profile.discordId}" rel="me nofollow noopener" target="_blank"><span>${profile.discordUsername}#${profile.discordDiscriminator}</span></a>`,
 | 
				
			||||||
			identifier: {
 | 
								identifier: {
 | 
				
			||||||
				type: 'PropertyValue',
 | 
									type: 'PropertyValue',
 | 
				
			||||||
				name: 'misskey:authentication:discord',
 | 
									name: 'misskey:authentication:discord',
 | 
				
			||||||
				value: `${links.discordId}@${links.discordUsername}#${links.discordDiscriminator}`
 | 
									value: `${profile.discordId}@${profile.discordUsername}#${profile.discordDiscriminator}`
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -93,7 +93,7 @@ export async function renderPerson(user: ILocalUser) {
 | 
				
			||||||
		url: `${config.url}/@${user.username}`,
 | 
							url: `${config.url}/@${user.username}`,
 | 
				
			||||||
		preferredUsername: user.username,
 | 
							preferredUsername: user.username,
 | 
				
			||||||
		name: user.name,
 | 
							name: user.name,
 | 
				
			||||||
		summary: toHtml(parse(user.description)),
 | 
							summary: toHtml(parse(profile.description)),
 | 
				
			||||||
		icon: user.avatarId && renderImage(avatar),
 | 
							icon: user.avatarId && renderImage(avatar),
 | 
				
			||||||
		image: user.bannerId && renderImage(banner),
 | 
							image: user.bannerId && renderImage(banner),
 | 
				
			||||||
		tag,
 | 
							tag,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ import { ID } from '../../../../misc/cafy-id';
 | 
				
			||||||
import define from '../../define';
 | 
					import define from '../../define';
 | 
				
			||||||
import * as bcrypt from 'bcryptjs';
 | 
					import * as bcrypt from 'bcryptjs';
 | 
				
			||||||
import rndstr from 'rndstr';
 | 
					import rndstr from 'rndstr';
 | 
				
			||||||
import { Users } from '../../../../models';
 | 
					import { Users, UserProfiles } from '../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	desc: {
 | 
						desc: {
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,9 @@ export default define(meta, async (ps) => {
 | 
				
			||||||
	// Generate hash of password
 | 
						// Generate hash of password
 | 
				
			||||||
	const hash = bcrypt.hashSync(passwd);
 | 
						const hash = bcrypt.hashSync(passwd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await Users.update(user.id, {
 | 
						await UserProfiles.update({
 | 
				
			||||||
 | 
							userId: user.id
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
		password: hash
 | 
							password: hash
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import * as speakeasy from 'speakeasy';
 | 
					import * as speakeasy from 'speakeasy';
 | 
				
			||||||
import define from '../../../define';
 | 
					import define from '../../../define';
 | 
				
			||||||
import { Users } from '../../../../../models';
 | 
					import { UserProfiles } from '../../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	requireCredential: true,
 | 
						requireCredential: true,
 | 
				
			||||||
| 
						 | 
					@ -16,24 +16,26 @@ export const meta = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default define(meta, async (ps, user) => {
 | 
					export default define(meta, async (ps, user) => {
 | 
				
			||||||
	const _token = ps.token.replace(/\s/g, '');
 | 
						const token = ps.token.replace(/\s/g, '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (user.twoFactorTempSecret == null) {
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (profile.twoFactorTempSecret == null) {
 | 
				
			||||||
		throw new Error('二段階認証の設定が開始されていません');
 | 
							throw new Error('二段階認証の設定が開始されていません');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const verified = (speakeasy as any).totp.verify({
 | 
						const verified = (speakeasy as any).totp.verify({
 | 
				
			||||||
		secret: user.twoFactorTempSecret,
 | 
							secret: profile.twoFactorTempSecret,
 | 
				
			||||||
		encoding: 'base32',
 | 
							encoding: 'base32',
 | 
				
			||||||
		token: _token
 | 
							token: token
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!verified) {
 | 
						if (!verified) {
 | 
				
			||||||
		throw new Error('not verified');
 | 
							throw new Error('not verified');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await Users.update(user.id, {
 | 
						await UserProfiles.update({ userId: user.id }, {
 | 
				
			||||||
		twoFactorSecret: user.twoFactorTempSecret,
 | 
							twoFactorSecret: profile.twoFactorTempSecret,
 | 
				
			||||||
		twoFactorEnabled: true
 | 
							twoFactorEnabled: true
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import * as speakeasy from 'speakeasy';
 | 
				
			||||||
import * as QRCode from 'qrcode';
 | 
					import * as QRCode from 'qrcode';
 | 
				
			||||||
import config from '../../../../../config';
 | 
					import config from '../../../../../config';
 | 
				
			||||||
import define from '../../../define';
 | 
					import define from '../../../define';
 | 
				
			||||||
import { Users } from '../../../../../models';
 | 
					import { UserProfiles } from '../../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	requireCredential: true,
 | 
						requireCredential: true,
 | 
				
			||||||
| 
						 | 
					@ -19,8 +19,10 @@ export const meta = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default define(meta, async (ps, user) => {
 | 
					export default define(meta, async (ps, user) => {
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Compare password
 | 
						// Compare password
 | 
				
			||||||
	const same = await bcrypt.compare(ps.password, user.password);
 | 
						const same = await bcrypt.compare(ps.password, profile.password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!same) {
 | 
						if (!same) {
 | 
				
			||||||
		throw new Error('incorrect password');
 | 
							throw new Error('incorrect password');
 | 
				
			||||||
| 
						 | 
					@ -31,7 +33,7 @@ export default define(meta, async (ps, user) => {
 | 
				
			||||||
		length: 32
 | 
							length: 32
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await Users.update(user.id, {
 | 
						await UserProfiles.update({ userId: user.id }, {
 | 
				
			||||||
		twoFactorTempSecret: secret.base32
 | 
							twoFactorTempSecret: secret.base32
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import * as bcrypt from 'bcryptjs';
 | 
					import * as bcrypt from 'bcryptjs';
 | 
				
			||||||
import define from '../../../define';
 | 
					import define from '../../../define';
 | 
				
			||||||
import { Users } from '../../../../../models';
 | 
					import { UserProfiles } from '../../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	requireCredential: true,
 | 
						requireCredential: true,
 | 
				
			||||||
| 
						 | 
					@ -16,17 +16,17 @@ export const meta = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default define(meta, async (ps, user) => {
 | 
					export default define(meta, async (ps, user) => {
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Compare password
 | 
						// Compare password
 | 
				
			||||||
	const same = await bcrypt.compare(ps.password, user.password);
 | 
						const same = await bcrypt.compare(ps.password, profile.password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!same) {
 | 
						if (!same) {
 | 
				
			||||||
		throw new Error('incorrect password');
 | 
							throw new Error('incorrect password');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await Users.update(user.id, {
 | 
						await UserProfiles.update({ userId: user.id }, {
 | 
				
			||||||
		twoFactorSecret: null,
 | 
							twoFactorSecret: null,
 | 
				
			||||||
		twoFactorEnabled: false
 | 
							twoFactorEnabled: false
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return;
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import * as bcrypt from 'bcryptjs';
 | 
					import * as bcrypt from 'bcryptjs';
 | 
				
			||||||
import define from '../../define';
 | 
					import define from '../../define';
 | 
				
			||||||
import { Users } from '../../../../models';
 | 
					import { UserProfiles } from '../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	requireCredential: true,
 | 
						requireCredential: true,
 | 
				
			||||||
| 
						 | 
					@ -20,8 +20,10 @@ export const meta = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default define(meta, async (ps, user) => {
 | 
					export default define(meta, async (ps, user) => {
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Compare password
 | 
						// Compare password
 | 
				
			||||||
	const same = await bcrypt.compare(ps.currentPassword, user.password);
 | 
						const same = await bcrypt.compare(ps.currentPassword, profile.password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!same) {
 | 
						if (!same) {
 | 
				
			||||||
		throw new Error('incorrect password');
 | 
							throw new Error('incorrect password');
 | 
				
			||||||
| 
						 | 
					@ -31,7 +33,7 @@ export default define(meta, async (ps, user) => {
 | 
				
			||||||
	const salt = await bcrypt.genSalt(8);
 | 
						const salt = await bcrypt.genSalt(8);
 | 
				
			||||||
	const hash = await bcrypt.hash(ps.newPassword, salt);
 | 
						const hash = await bcrypt.hash(ps.newPassword, salt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await Users.update(user.id, {
 | 
						await UserProfiles.update({ userId: user.id }, {
 | 
				
			||||||
		password: hash
 | 
							password: hash
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import * as bcrypt from 'bcryptjs';
 | 
					import * as bcrypt from 'bcryptjs';
 | 
				
			||||||
import define from '../../define';
 | 
					import define from '../../define';
 | 
				
			||||||
import { Users } from '../../../../models';
 | 
					import { Users, UserProfiles } from '../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	requireCredential: true,
 | 
						requireCredential: true,
 | 
				
			||||||
| 
						 | 
					@ -16,8 +16,10 @@ export const meta = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default define(meta, async (ps, user) => {
 | 
					export default define(meta, async (ps, user) => {
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Compare password
 | 
						// Compare password
 | 
				
			||||||
	const same = await bcrypt.compare(ps.password, user.password);
 | 
						const same = await bcrypt.compare(ps.password, profile.password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!same) {
 | 
						if (!same) {
 | 
				
			||||||
		throw new Error('incorrect password');
 | 
							throw new Error('incorrect password');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ import * as bcrypt from 'bcryptjs';
 | 
				
			||||||
import { publishMainStream } from '../../../../services/stream';
 | 
					import { publishMainStream } from '../../../../services/stream';
 | 
				
			||||||
import generateUserToken from '../../common/generate-native-user-token';
 | 
					import generateUserToken from '../../common/generate-native-user-token';
 | 
				
			||||||
import define from '../../define';
 | 
					import define from '../../define';
 | 
				
			||||||
import { Users } from '../../../../models';
 | 
					import { Users, UserProfiles } from '../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	requireCredential: true,
 | 
						requireCredential: true,
 | 
				
			||||||
| 
						 | 
					@ -18,8 +18,10 @@ export const meta = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default define(meta, async (ps, user) => {
 | 
					export default define(meta, async (ps, user) => {
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Compare password
 | 
						// Compare password
 | 
				
			||||||
	const same = await bcrypt.compare(ps.password, user.password);
 | 
						const same = await bcrypt.compare(ps.password, profile.password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!same) {
 | 
						if (!same) {
 | 
				
			||||||
		throw new Error('incorrect password');
 | 
							throw new Error('incorrect password');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import $ from 'cafy';
 | 
					import $ from 'cafy';
 | 
				
			||||||
import { publishMainStream } from '../../../../services/stream';
 | 
					import { publishMainStream } from '../../../../services/stream';
 | 
				
			||||||
import define from '../../define';
 | 
					import define from '../../define';
 | 
				
			||||||
import { Users } from '../../../../models';
 | 
					import { UserProfiles } from '../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	requireCredential: true,
 | 
						requireCredential: true,
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ export const meta = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default define(meta, async (ps, user) => {
 | 
					export default define(meta, async (ps, user) => {
 | 
				
			||||||
	await Users.createQueryBuilder().update()
 | 
						await UserProfiles.createQueryBuilder().update()
 | 
				
			||||||
		.set({
 | 
							.set({
 | 
				
			||||||
			clientData: {
 | 
								clientData: {
 | 
				
			||||||
				[ps.name]: ps.value
 | 
									[ps.name]: ps.value
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import config from '../../../../config';
 | 
				
			||||||
import * as ms from 'ms';
 | 
					import * as ms from 'ms';
 | 
				
			||||||
import * as bcrypt from 'bcryptjs';
 | 
					import * as bcrypt from 'bcryptjs';
 | 
				
			||||||
import { apiLogger } from '../../logger';
 | 
					import { apiLogger } from '../../logger';
 | 
				
			||||||
import { Users } from '../../../../models';
 | 
					import { Users, UserProfiles } from '../../../../models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	requireCredential: true,
 | 
						requireCredential: true,
 | 
				
			||||||
| 
						 | 
					@ -32,14 +32,16 @@ export const meta = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default define(meta, async (ps, user) => {
 | 
					export default define(meta, async (ps, user) => {
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Compare password
 | 
						// Compare password
 | 
				
			||||||
	const same = await bcrypt.compare(ps.password, user.password);
 | 
						const same = await bcrypt.compare(ps.password, profile.password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!same) {
 | 
						if (!same) {
 | 
				
			||||||
		throw new Error('incorrect password');
 | 
							throw new Error('incorrect password');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await Users.update(user.id, {
 | 
						await UserProfiles.update({ userId: user.id }, {
 | 
				
			||||||
		email: ps.email,
 | 
							email: ps.email,
 | 
				
			||||||
		emailVerified: false,
 | 
							emailVerified: false,
 | 
				
			||||||
		emailVerifyCode: null
 | 
							emailVerifyCode: null
 | 
				
			||||||
| 
						 | 
					@ -56,7 +58,7 @@ export default define(meta, async (ps, user) => {
 | 
				
			||||||
	if (ps.email != null) {
 | 
						if (ps.email != null) {
 | 
				
			||||||
		const code = rndstr('a-z0-9', 16);
 | 
							const code = rndstr('a-z0-9', 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		await Users.update(user.id, {
 | 
							await UserProfiles.update({ userId: user.id }, {
 | 
				
			||||||
			emailVerifyCode: code
 | 
								emailVerifyCode: code
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,9 @@ import extractHashtags from '../../../../misc/extract-hashtags';
 | 
				
			||||||
import * as langmap from 'langmap';
 | 
					import * as langmap from 'langmap';
 | 
				
			||||||
import { updateHashtag } from '../../../../services/update-hashtag';
 | 
					import { updateHashtag } from '../../../../services/update-hashtag';
 | 
				
			||||||
import { ApiError } from '../../error';
 | 
					import { ApiError } from '../../error';
 | 
				
			||||||
import { Users, DriveFiles } from '../../../../models';
 | 
					import { Users, DriveFiles, UserProfiles } from '../../../../models';
 | 
				
			||||||
 | 
					import { User } from '../../../../models/entities/user';
 | 
				
			||||||
 | 
					import { UserProfile } from '../../../../models/entities/user-profile';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	desc: {
 | 
						desc: {
 | 
				
			||||||
| 
						 | 
					@ -154,22 +156,23 @@ export const meta = {
 | 
				
			||||||
export default define(meta, async (ps, user, app) => {
 | 
					export default define(meta, async (ps, user, app) => {
 | 
				
			||||||
	const isSecure = user != null && app == null;
 | 
						const isSecure = user != null && app == null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const updates = {} as any;
 | 
						const updates = {} as Partial<User>;
 | 
				
			||||||
 | 
						const profile = {} as Partial<UserProfile>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ps.name !== undefined) updates.name = ps.name;
 | 
						if (ps.name !== undefined) updates.name = ps.name;
 | 
				
			||||||
	if (ps.description !== undefined) updates.description = ps.description;
 | 
						if (ps.description !== undefined) profile.description = ps.description;
 | 
				
			||||||
	if (ps.lang !== undefined) updates.lang = ps.lang;
 | 
						//if (ps.lang !== undefined) updates.lang = ps.lang;
 | 
				
			||||||
	if (ps.location !== undefined) updates.location = ps.location;
 | 
						if (ps.location !== undefined) profile.location = ps.location;
 | 
				
			||||||
	if (ps.birthday !== undefined) updates.birthday = ps.birthday;
 | 
						if (ps.birthday !== undefined) profile.birthday = ps.birthday;
 | 
				
			||||||
	if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId;
 | 
						if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId;
 | 
				
			||||||
	if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId;
 | 
						if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId;
 | 
				
			||||||
	if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked;
 | 
						if (typeof ps.isLocked == 'boolean') updates.isLocked = ps.isLocked;
 | 
				
			||||||
	if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot;
 | 
						if (typeof ps.isBot == 'boolean') updates.isBot = ps.isBot;
 | 
				
			||||||
	if (typeof ps.carefulBot == 'boolean') updates.carefulBot = ps.carefulBot;
 | 
						if (typeof ps.carefulBot == 'boolean') profile.carefulBot = ps.carefulBot;
 | 
				
			||||||
	if (typeof ps.autoAcceptFollowed == 'boolean') updates.autoAcceptFollowed = ps.autoAcceptFollowed;
 | 
						if (typeof ps.autoAcceptFollowed == 'boolean') profile.autoAcceptFollowed = ps.autoAcceptFollowed;
 | 
				
			||||||
	if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat;
 | 
						if (typeof ps.isCat == 'boolean') updates.isCat = ps.isCat;
 | 
				
			||||||
	if (typeof ps.autoWatch == 'boolean') updates.autoWatch = ps.autoWatch;
 | 
						if (typeof ps.autoWatch == 'boolean') profile.autoWatch = ps.autoWatch;
 | 
				
			||||||
	if (typeof ps.alwaysMarkNsfw == 'boolean') updates.alwaysMarkNsfw = ps.alwaysMarkNsfw;
 | 
						if (typeof ps.alwaysMarkNsfw == 'boolean') profile.alwaysMarkNsfw = ps.alwaysMarkNsfw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ps.avatarId) {
 | 
						if (ps.avatarId) {
 | 
				
			||||||
		const avatar = await DriveFiles.findOne(ps.avatarId);
 | 
							const avatar = await DriveFiles.findOne(ps.avatarId);
 | 
				
			||||||
| 
						 | 
					@ -206,8 +209,8 @@ export default define(meta, async (ps, user, app) => {
 | 
				
			||||||
		emojis = emojis.concat(extractEmojis(tokens));
 | 
							emojis = emojis.concat(extractEmojis(tokens));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (updates.description != null) {
 | 
						if (profile.description != null) {
 | 
				
			||||||
		const tokens = parse(updates.description);
 | 
							const tokens = parse(profile.description);
 | 
				
			||||||
		emojis = emojis.concat(extractEmojis(tokens));
 | 
							emojis = emojis.concat(extractEmojis(tokens));
 | 
				
			||||||
		tags = extractHashtags(tokens).map(tag => tag.toLowerCase());
 | 
							tags = extractHashtags(tokens).map(tag => tag.toLowerCase());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -221,6 +224,7 @@ export default define(meta, async (ps, user, app) => {
 | 
				
			||||||
	//#endregion
 | 
						//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await Users.update(user.id, updates);
 | 
						await Users.update(user.id, updates);
 | 
				
			||||||
 | 
						await UserProfiles.update({ userId: user.id }, profile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const iObj = await Users.pack(user.id, user, {
 | 
						const iObj = await Users.pack(user.id, user, {
 | 
				
			||||||
		detail: true,
 | 
							detail: true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ import { deliver } from '../../../../../queue';
 | 
				
			||||||
import { renderActivity } from '../../../../../remote/activitypub/renderer';
 | 
					import { renderActivity } from '../../../../../remote/activitypub/renderer';
 | 
				
			||||||
import renderVote from '../../../../../remote/activitypub/renderer/vote';
 | 
					import renderVote from '../../../../../remote/activitypub/renderer/vote';
 | 
				
			||||||
import { deliverQuestionUpdate } from '../../../../../services/note/polls/update';
 | 
					import { deliverQuestionUpdate } from '../../../../../services/note/polls/update';
 | 
				
			||||||
import { PollVotes, NoteWatchings, Users, Polls } from '../../../../../models';
 | 
					import { PollVotes, NoteWatchings, Users, Polls, UserProfiles } from '../../../../../models';
 | 
				
			||||||
import { Not } from 'typeorm';
 | 
					import { Not } from 'typeorm';
 | 
				
			||||||
import { IRemoteUser } from '../../../../../models/entities/user';
 | 
					import { IRemoteUser } from '../../../../../models/entities/user';
 | 
				
			||||||
import { genId } from '../../../../../misc/gen-id';
 | 
					import { genId } from '../../../../../misc/gen-id';
 | 
				
			||||||
| 
						 | 
					@ -149,8 +149,10 @@ export default define(meta, async (ps, user) => {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// この投稿をWatchする
 | 
						// この投稿をWatchする
 | 
				
			||||||
	if (user.autoWatch !== false) {
 | 
						if (profile.autoWatch !== false) {
 | 
				
			||||||
		watch(user.id, note);
 | 
							watch(user.id, note);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import * as speakeasy from 'speakeasy';
 | 
				
			||||||
import { publishMainStream } from '../../../services/stream';
 | 
					import { publishMainStream } from '../../../services/stream';
 | 
				
			||||||
import signin from '../common/signin';
 | 
					import signin from '../common/signin';
 | 
				
			||||||
import config from '../../../config';
 | 
					import config from '../../../config';
 | 
				
			||||||
import { Users, Signins } from '../../../models';
 | 
					import { Users, Signins, UserProfiles } from '../../../models';
 | 
				
			||||||
import { ILocalUser } from '../../../models/entities/user';
 | 
					import { ILocalUser } from '../../../models/entities/user';
 | 
				
			||||||
import { genId } from '../../../misc/gen-id';
 | 
					import { genId } from '../../../misc/gen-id';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,13 +45,15 @@ export default async (ctx: Koa.BaseContext) => {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Compare password
 | 
						// Compare password
 | 
				
			||||||
	const same = await bcrypt.compare(password, user.password);
 | 
						const same = await bcrypt.compare(password, profile.password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (same) {
 | 
						if (same) {
 | 
				
			||||||
		if (user.twoFactorEnabled) {
 | 
							if (profile.twoFactorEnabled) {
 | 
				
			||||||
			const verified = (speakeasy as any).totp.verify({
 | 
								const verified = (speakeasy as any).totp.verify({
 | 
				
			||||||
				secret: user.twoFactorSecret,
 | 
									secret: profile.twoFactorSecret,
 | 
				
			||||||
				encoding: 'base32',
 | 
									encoding: 'base32',
 | 
				
			||||||
				token: token
 | 
									token: token
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,13 +5,13 @@ import generateUserToken from '../common/generate-native-user-token';
 | 
				
			||||||
import config from '../../../config';
 | 
					import config from '../../../config';
 | 
				
			||||||
import fetchMeta from '../../../misc/fetch-meta';
 | 
					import fetchMeta from '../../../misc/fetch-meta';
 | 
				
			||||||
import * as recaptcha from 'recaptcha-promise';
 | 
					import * as recaptcha from 'recaptcha-promise';
 | 
				
			||||||
import { Users, RegistrationTickets, UserServiceLinkings, UserKeypairs } from '../../../models';
 | 
					import { Users, RegistrationTickets, UserProfiles, UserKeypairs } from '../../../models';
 | 
				
			||||||
import { genId } from '../../../misc/gen-id';
 | 
					import { genId } from '../../../misc/gen-id';
 | 
				
			||||||
import { usersChart } from '../../../services/chart';
 | 
					import { usersChart } from '../../../services/chart';
 | 
				
			||||||
import { UserServiceLinking } from '../../../models/entities/user-service-linking';
 | 
					 | 
				
			||||||
import { User } from '../../../models/entities/user';
 | 
					import { User } from '../../../models/entities/user';
 | 
				
			||||||
import { UserKeypair } from '../../../models/entities/user-keypair';
 | 
					import { UserKeypair } from '../../../models/entities/user-keypair';
 | 
				
			||||||
import { toPuny } from '../../../misc/convert-host';
 | 
					import { toPuny } from '../../../misc/convert-host';
 | 
				
			||||||
 | 
					import { UserProfile } from '../../../models/entities/user-profile';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async (ctx: Koa.BaseContext) => {
 | 
					export default async (ctx: Koa.BaseContext) => {
 | 
				
			||||||
	const body = ctx.request.body as any;
 | 
						const body = ctx.request.body as any;
 | 
				
			||||||
| 
						 | 
					@ -106,23 +106,21 @@ export default async (ctx: Koa.BaseContext) => {
 | 
				
			||||||
		usernameLower: username.toLowerCase(),
 | 
							usernameLower: username.toLowerCase(),
 | 
				
			||||||
		host: toPuny(host),
 | 
							host: toPuny(host),
 | 
				
			||||||
		token: secret,
 | 
							token: secret,
 | 
				
			||||||
		password: hash,
 | 
					 | 
				
			||||||
		isAdmin: config.autoAdmin && usersCount === 0,
 | 
							isAdmin: config.autoAdmin && usersCount === 0,
 | 
				
			||||||
		autoAcceptFollowed: true,
 | 
					 | 
				
			||||||
		autoWatch: false
 | 
					 | 
				
			||||||
	} as User);
 | 
						} as User);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await UserKeypairs.save({
 | 
						await UserKeypairs.save({
 | 
				
			||||||
		id: genId(),
 | 
					 | 
				
			||||||
		publicKey: keyPair[0],
 | 
							publicKey: keyPair[0],
 | 
				
			||||||
		privateKey: keyPair[1],
 | 
							privateKey: keyPair[1],
 | 
				
			||||||
		userId: account.id
 | 
							userId: account.id
 | 
				
			||||||
	} as UserKeypair);
 | 
						} as UserKeypair);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await UserServiceLinkings.save({
 | 
						await UserProfiles.save({
 | 
				
			||||||
		id: genId(),
 | 
							userId: account.id,
 | 
				
			||||||
		userId: account.id
 | 
							autoAcceptFollowed: true,
 | 
				
			||||||
	} as UserServiceLinking);
 | 
							autoWatch: false,
 | 
				
			||||||
 | 
							password: hash,
 | 
				
			||||||
 | 
						} as Partial<UserProfile>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	usersChart.update(account, true);
 | 
						usersChart.update(account, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import redis from '../../../db/redis';
 | 
				
			||||||
import * as uuid from 'uuid';
 | 
					import * as uuid from 'uuid';
 | 
				
			||||||
import signin from '../common/signin';
 | 
					import signin from '../common/signin';
 | 
				
			||||||
import fetchMeta from '../../../misc/fetch-meta';
 | 
					import fetchMeta from '../../../misc/fetch-meta';
 | 
				
			||||||
import { Users, UserServiceLinkings } from '../../../models';
 | 
					import { Users, UserProfiles } from '../../../models';
 | 
				
			||||||
import { ILocalUser } from '../../../models/entities/user';
 | 
					import { ILocalUser } from '../../../models/entities/user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getUserToken(ctx: Koa.BaseContext) {
 | 
					function getUserToken(ctx: Koa.BaseContext) {
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,7 @@ router.get('/disconnect/discord', async ctx => {
 | 
				
			||||||
		token: userToken
 | 
							token: userToken
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await UserServiceLinkings.update({
 | 
						await UserProfiles.update({
 | 
				
			||||||
		userId: user.id
 | 
							userId: user.id
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		discord: false,
 | 
							discord: false,
 | 
				
			||||||
| 
						 | 
					@ -202,7 +202,7 @@ router.get('/dc/cb', async ctx => {
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const link = await UserServiceLinkings.createQueryBuilder()
 | 
							const profile = await UserProfiles.createQueryBuilder()
 | 
				
			||||||
			.where('discord @> :discord', {
 | 
								.where('discord @> :discord', {
 | 
				
			||||||
				discord: {
 | 
									discord: {
 | 
				
			||||||
					id: id,
 | 
										id: id,
 | 
				
			||||||
| 
						 | 
					@ -211,12 +211,12 @@ router.get('/dc/cb', async ctx => {
 | 
				
			||||||
			.andWhere('userHost IS NULL')
 | 
								.andWhere('userHost IS NULL')
 | 
				
			||||||
			.getOne();
 | 
								.getOne();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (link == null) {
 | 
							if (profile == null) {
 | 
				
			||||||
			ctx.throw(404, `@${username}#${discriminator}と連携しているMisskeyアカウントはありませんでした...`);
 | 
								ctx.throw(404, `@${username}#${discriminator}と連携しているMisskeyアカウントはありませんでした...`);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		await UserServiceLinkings.update(link.id, {
 | 
							await UserProfiles.update({ userId: profile.userId }, {
 | 
				
			||||||
			discord: true,
 | 
								discord: true,
 | 
				
			||||||
			discordAccessToken: accessToken,
 | 
								discordAccessToken: accessToken,
 | 
				
			||||||
			discordRefreshToken: refreshToken,
 | 
								discordRefreshToken: refreshToken,
 | 
				
			||||||
| 
						 | 
					@ -225,7 +225,7 @@ router.get('/dc/cb', async ctx => {
 | 
				
			||||||
			discordDiscriminator: discriminator
 | 
								discordDiscriminator: discriminator
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		signin(ctx, await Users.findOne(link.userId) as ILocalUser, true);
 | 
							signin(ctx, await Users.findOne(profile.userId) as ILocalUser, true);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		const code = ctx.query.code;
 | 
							const code = ctx.query.code;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -289,7 +289,7 @@ router.get('/dc/cb', async ctx => {
 | 
				
			||||||
			token: userToken
 | 
								token: userToken
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		await UserServiceLinkings.update({ userId: user.id }, {
 | 
							await UserProfiles.update({ userId: user.id }, {
 | 
				
			||||||
			discord: true,
 | 
								discord: true,
 | 
				
			||||||
			discordAccessToken: accessToken,
 | 
								discordAccessToken: accessToken,
 | 
				
			||||||
			discordRefreshToken: refreshToken,
 | 
								discordRefreshToken: refreshToken,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import redis from '../../../db/redis';
 | 
				
			||||||
import * as uuid from 'uuid';
 | 
					import * as uuid from 'uuid';
 | 
				
			||||||
import signin from '../common/signin';
 | 
					import signin from '../common/signin';
 | 
				
			||||||
import fetchMeta from '../../../misc/fetch-meta';
 | 
					import fetchMeta from '../../../misc/fetch-meta';
 | 
				
			||||||
import { Users, UserServiceLinkings } from '../../../models';
 | 
					import { Users, UserProfiles } from '../../../models';
 | 
				
			||||||
import { ILocalUser } from '../../../models/entities/user';
 | 
					import { ILocalUser } from '../../../models/entities/user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getUserToken(ctx: Koa.BaseContext) {
 | 
					function getUserToken(ctx: Koa.BaseContext) {
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,7 @@ router.get('/disconnect/github', async ctx => {
 | 
				
			||||||
		token: userToken
 | 
							token: userToken
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await UserServiceLinkings.update({
 | 
						await UserProfiles.update({
 | 
				
			||||||
		userId: user.id
 | 
							userId: user.id
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		github: false,
 | 
							github: false,
 | 
				
			||||||
| 
						 | 
					@ -191,7 +191,7 @@ router.get('/gh/cb', async ctx => {
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const link = await UserServiceLinkings.createQueryBuilder()
 | 
							const link = await UserProfiles.createQueryBuilder()
 | 
				
			||||||
			.where('github @> :github', {
 | 
								.where('github @> :github', {
 | 
				
			||||||
				github: {
 | 
									github: {
 | 
				
			||||||
					id: id,
 | 
										id: id,
 | 
				
			||||||
| 
						 | 
					@ -263,7 +263,7 @@ router.get('/gh/cb', async ctx => {
 | 
				
			||||||
			token: userToken
 | 
								token: userToken
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		await UserServiceLinkings.update({ userId: user.id }, {
 | 
							await UserProfiles.update({ userId: user.id }, {
 | 
				
			||||||
			github: true,
 | 
								github: true,
 | 
				
			||||||
			githubAccessToken: accessToken,
 | 
								githubAccessToken: accessToken,
 | 
				
			||||||
			githubId: id,
 | 
								githubId: id,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ import { publishMainStream } from '../../../services/stream';
 | 
				
			||||||
import config from '../../../config';
 | 
					import config from '../../../config';
 | 
				
			||||||
import signin from '../common/signin';
 | 
					import signin from '../common/signin';
 | 
				
			||||||
import fetchMeta from '../../../misc/fetch-meta';
 | 
					import fetchMeta from '../../../misc/fetch-meta';
 | 
				
			||||||
import { Users, UserServiceLinkings } from '../../../models';
 | 
					import { Users, UserProfiles } from '../../../models';
 | 
				
			||||||
import { ILocalUser } from '../../../models/entities/user';
 | 
					import { ILocalUser } from '../../../models/entities/user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getUserToken(ctx: Koa.BaseContext) {
 | 
					function getUserToken(ctx: Koa.BaseContext) {
 | 
				
			||||||
| 
						 | 
					@ -44,7 +44,7 @@ router.get('/disconnect/twitter', async ctx => {
 | 
				
			||||||
		token: userToken
 | 
							token: userToken
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	await UserServiceLinkings.update({
 | 
						await UserProfiles.update({
 | 
				
			||||||
		userId: user.id
 | 
							userId: user.id
 | 
				
			||||||
	}, {
 | 
						}, {
 | 
				
			||||||
		twitter: false,
 | 
							twitter: false,
 | 
				
			||||||
| 
						 | 
					@ -139,7 +139,7 @@ router.get('/tw/cb', async ctx => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const result = await twAuth.done(JSON.parse(twCtx), ctx.query.oauth_verifier);
 | 
							const result = await twAuth.done(JSON.parse(twCtx), ctx.query.oauth_verifier);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const link = await UserServiceLinkings.createQueryBuilder()
 | 
							const link = await UserProfiles.createQueryBuilder()
 | 
				
			||||||
			.where('twitter @> :twitter', {
 | 
								.where('twitter @> :twitter', {
 | 
				
			||||||
				twitter: {
 | 
									twitter: {
 | 
				
			||||||
					userId: result.userId,
 | 
										userId: result.userId,
 | 
				
			||||||
| 
						 | 
					@ -177,7 +177,7 @@ router.get('/tw/cb', async ctx => {
 | 
				
			||||||
			token: userToken
 | 
								token: userToken
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		await UserServiceLinkings.update({ userId: user.id }, {
 | 
							await UserProfiles.update({ userId: user.id }, {
 | 
				
			||||||
			twitter: true,
 | 
								twitter: true,
 | 
				
			||||||
			twitterAccessToken: result.accessToken,
 | 
								twitterAccessToken: result.accessToken,
 | 
				
			||||||
			twitterAccessTokenSecret: result.accessTokenSecret,
 | 
								twitterAccessTokenSecret: result.accessTokenSecret,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ import apiServer from './api';
 | 
				
			||||||
import { sum } from '../prelude/array';
 | 
					import { sum } from '../prelude/array';
 | 
				
			||||||
import Logger from '../services/logger';
 | 
					import Logger from '../services/logger';
 | 
				
			||||||
import { program } from '../argv';
 | 
					import { program } from '../argv';
 | 
				
			||||||
import { Users } from '../models';
 | 
					import { UserProfiles } from '../models';
 | 
				
			||||||
import { networkChart } from '../services/chart';
 | 
					import { networkChart } from '../services/chart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const serverLogger = new Logger('server', 'gray', false);
 | 
					export const serverLogger = new Logger('server', 'gray', false);
 | 
				
			||||||
| 
						 | 
					@ -73,15 +73,15 @@ router.use(nodeinfo.routes());
 | 
				
			||||||
router.use(wellKnown.routes());
 | 
					router.use(wellKnown.routes());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router.get('/verify-email/:code', async ctx => {
 | 
					router.get('/verify-email/:code', async ctx => {
 | 
				
			||||||
	const user = await Users.findOne({
 | 
						const profile = await UserProfiles.findOne({
 | 
				
			||||||
		emailVerifyCode: ctx.params.code
 | 
							emailVerifyCode: ctx.params.code
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (user != null) {
 | 
						if (profile != null) {
 | 
				
			||||||
		ctx.body = 'Verify succeeded!';
 | 
							ctx.body = 'Verify succeeded!';
 | 
				
			||||||
		ctx.status = 200;
 | 
							ctx.status = 200;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Users.update(user.id, {
 | 
							UserProfiles.update({ userId: profile.userId }, {
 | 
				
			||||||
			emailVerified: true,
 | 
								emailVerified: true,
 | 
				
			||||||
			emailVerifyCode: null
 | 
								emailVerifyCode: null
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
import { Feed } from 'feed';
 | 
					import { Feed } from 'feed';
 | 
				
			||||||
import config from '../../config';
 | 
					import config from '../../config';
 | 
				
			||||||
import { User } from '../../models/entities/user';
 | 
					import { User } from '../../models/entities/user';
 | 
				
			||||||
import { Notes, DriveFiles } from '../../models';
 | 
					import { Notes, DriveFiles, UserProfiles } from '../../models';
 | 
				
			||||||
import { In } from 'typeorm';
 | 
					import { In } from 'typeorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default async function(user: User) {
 | 
					export default async function(user: User) {
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,8 @@ export default async function(user: User) {
 | 
				
			||||||
		name: user.name || user.username
 | 
							name: user.name || user.username
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const notes = await Notes.find({
 | 
						const notes = await Notes.find({
 | 
				
			||||||
		where: {
 | 
							where: {
 | 
				
			||||||
			userId: user.id,
 | 
								userId: user.id,
 | 
				
			||||||
| 
						 | 
					@ -25,7 +27,7 @@ export default async function(user: User) {
 | 
				
			||||||
		title: `${author.name} (@${user.username}@${config.host})`,
 | 
							title: `${author.name} (@${user.username}@${config.host})`,
 | 
				
			||||||
		updated: notes[0].createdAt,
 | 
							updated: notes[0].createdAt,
 | 
				
			||||||
		generator: 'Misskey',
 | 
							generator: 'Misskey',
 | 
				
			||||||
		description: `${user.notesCount} Notes, ${user.followingCount} Following, ${user.followersCount} Followers${user.description ? ` · ${user.description}` : ''}`,
 | 
							description: `${user.notesCount} Notes, ${user.followingCount} Following, ${user.followersCount} Followers${profile.description ? ` · ${profile.description}` : ''}`,
 | 
				
			||||||
		link: author.link,
 | 
							link: author.link,
 | 
				
			||||||
		image: user.avatarUrl,
 | 
							image: user.avatarUrl,
 | 
				
			||||||
		feedLinks: {
 | 
							feedLinks: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ import { driveLogger } from './logger';
 | 
				
			||||||
import { IImage, ConvertToJpeg, ConvertToWebp, ConvertToPng } from './image-processor';
 | 
					import { IImage, ConvertToJpeg, ConvertToWebp, ConvertToPng } from './image-processor';
 | 
				
			||||||
import { contentDisposition } from '../../misc/content-disposition';
 | 
					import { contentDisposition } from '../../misc/content-disposition';
 | 
				
			||||||
import { detectMine } from '../../misc/detect-mine';
 | 
					import { detectMine } from '../../misc/detect-mine';
 | 
				
			||||||
import { DriveFiles, DriveFolders, Users, Instances } from '../../models';
 | 
					import { DriveFiles, DriveFolders, Users, Instances, UserProfiles } from '../../models';
 | 
				
			||||||
import { InternalStorage } from './internal-storage';
 | 
					import { InternalStorage } from './internal-storage';
 | 
				
			||||||
import { DriveFile } from '../../models/entities/drive-file';
 | 
					import { DriveFile } from '../../models/entities/drive-file';
 | 
				
			||||||
import { IRemoteUser, User } from '../../models/entities/user';
 | 
					import { IRemoteUser, User } from '../../models/entities/user';
 | 
				
			||||||
| 
						 | 
					@ -365,6 +365,8 @@ export default async function(
 | 
				
			||||||
		propPromises = [calcWh(), calcAvg()];
 | 
							propPromises = [calcWh(), calcAvg()];
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const [folder] = await Promise.all([fetchFolder(), Promise.all(propPromises)]);
 | 
						const [folder] = await Promise.all([fetchFolder(), Promise.all(propPromises)]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let file = new DriveFile();
 | 
						let file = new DriveFile();
 | 
				
			||||||
| 
						 | 
					@ -376,7 +378,7 @@ export default async function(
 | 
				
			||||||
	file.comment = comment;
 | 
						file.comment = comment;
 | 
				
			||||||
	file.properties = properties;
 | 
						file.properties = properties;
 | 
				
			||||||
	file.isLink = isLink;
 | 
						file.isLink = isLink;
 | 
				
			||||||
	file.isSensitive = Users.isLocalUser(user) && user.alwaysMarkNsfw ? true :
 | 
						file.isSensitive = Users.isLocalUser(user) && profile.alwaysMarkNsfw ? true :
 | 
				
			||||||
		(sensitive !== null && sensitive !== undefined)
 | 
							(sensitive !== null && sensitive !== undefined)
 | 
				
			||||||
			? sensitive
 | 
								? sensitive
 | 
				
			||||||
			: false;
 | 
								: false;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc';
 | 
				
			||||||
import Logger from '../logger';
 | 
					import Logger from '../logger';
 | 
				
			||||||
import { IdentifiableError } from '../../misc/identifiable-error';
 | 
					import { IdentifiableError } from '../../misc/identifiable-error';
 | 
				
			||||||
import { User } from '../../models/entities/user';
 | 
					import { User } from '../../models/entities/user';
 | 
				
			||||||
import { Followings, Users, FollowRequests, Blockings, Instances } from '../../models';
 | 
					import { Followings, Users, FollowRequests, Blockings, Instances, UserProfiles } from '../../models';
 | 
				
			||||||
import { instanceChart, perUserFollowingChart } from '../chart';
 | 
					import { instanceChart, perUserFollowingChart } from '../chart';
 | 
				
			||||||
import { genId } from '../../misc/gen-id';
 | 
					import { genId } from '../../misc/gen-id';
 | 
				
			||||||
import { createNotification } from '../create-notification';
 | 
					import { createNotification } from '../create-notification';
 | 
				
			||||||
| 
						 | 
					@ -115,11 +115,13 @@ export default async function(follower: User, followee: User, requestId?: string
 | 
				
			||||||
		if (blocked != null) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked');
 | 
							if (blocked != null) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked');
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const followeeProfile = await UserProfiles.findOne({ userId: followee.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// フォロー対象が鍵アカウントである or
 | 
						// フォロー対象が鍵アカウントである or
 | 
				
			||||||
	// フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or
 | 
						// フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or
 | 
				
			||||||
	// フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである
 | 
						// フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである
 | 
				
			||||||
	// 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく
 | 
						// 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく
 | 
				
			||||||
	if (followee.isLocked || (followee.carefulBot && follower.isBot) || (Users.isLocalUser(follower) && Users.isRemoteUser(followee))) {
 | 
						if (followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || (Users.isLocalUser(follower) && Users.isRemoteUser(followee))) {
 | 
				
			||||||
		let autoAccept = false;
 | 
							let autoAccept = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 鍵アカウントであっても、既にフォローされていた場合はスルー
 | 
							// 鍵アカウントであっても、既にフォローされていた場合はスルー
 | 
				
			||||||
| 
						 | 
					@ -132,7 +134,7 @@ export default async function(follower: User, followee: User, requestId?: string
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// フォローしているユーザーは自動承認オプション
 | 
							// フォローしているユーザーは自動承認オプション
 | 
				
			||||||
		if (!autoAccept && (Users.isLocalUser(followee) && followee.autoAcceptFollowed)) {
 | 
							if (!autoAccept && (Users.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) {
 | 
				
			||||||
			const followed = await Followings.findOne({
 | 
								const followed = await Followings.findOne({
 | 
				
			||||||
				followerId: followee.id,
 | 
									followerId: followee.id,
 | 
				
			||||||
				followeeId: follower.id
 | 
									followeeId: follower.id
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ import extractMentions from '../../misc/extract-mentions';
 | 
				
			||||||
import extractEmojis from '../../misc/extract-emojis';
 | 
					import extractEmojis from '../../misc/extract-emojis';
 | 
				
			||||||
import extractHashtags from '../../misc/extract-hashtags';
 | 
					import extractHashtags from '../../misc/extract-hashtags';
 | 
				
			||||||
import { Note } from '../../models/entities/note';
 | 
					import { Note } from '../../models/entities/note';
 | 
				
			||||||
import { Mutings, Users, NoteWatchings, Followings, Notes, Instances, Polls } from '../../models';
 | 
					import { Mutings, Users, NoteWatchings, Followings, Notes, Instances, Polls, UserProfiles } from '../../models';
 | 
				
			||||||
import { DriveFile } from '../../models/entities/drive-file';
 | 
					import { DriveFile } from '../../models/entities/drive-file';
 | 
				
			||||||
import { App } from '../../models/entities/app';
 | 
					import { App } from '../../models/entities/app';
 | 
				
			||||||
import { Not } from 'typeorm';
 | 
					import { Not } from 'typeorm';
 | 
				
			||||||
| 
						 | 
					@ -256,13 +256,15 @@ export default async (user: User, data: Option, silent = false) => new Promise<N
 | 
				
			||||||
		deliverNoteToMentionedRemoteUsers(mentionedUsers, user, noteActivity);
 | 
							deliverNoteToMentionedRemoteUsers(mentionedUsers, user, noteActivity);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// If has in reply to note
 | 
						// If has in reply to note
 | 
				
			||||||
	if (data.reply) {
 | 
						if (data.reply) {
 | 
				
			||||||
		// Fetch watchers
 | 
							// Fetch watchers
 | 
				
			||||||
		nmRelatedPromises.push(notifyToWatchersOfReplyee(data.reply, user, nm));
 | 
							nmRelatedPromises.push(notifyToWatchersOfReplyee(data.reply, user, nm));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// この投稿をWatchする
 | 
							// この投稿をWatchする
 | 
				
			||||||
		if (Users.isLocalUser(user) && user.autoWatch !== false) {
 | 
							if (Users.isLocalUser(user) && profile.autoWatch) {
 | 
				
			||||||
			watch(user.id, data.reply);
 | 
								watch(user.id, data.reply);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -286,7 +288,7 @@ export default async (user: User, data: Option, silent = false) => new Promise<N
 | 
				
			||||||
		nmRelatedPromises.push(notifyToWatchersOfRenotee(data.renote, user, nm, type));
 | 
							nmRelatedPromises.push(notifyToWatchersOfRenotee(data.renote, user, nm, type));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// この投稿をWatchする
 | 
							// この投稿をWatchする
 | 
				
			||||||
		if (Users.isLocalUser(user) && user.autoWatch !== false) {
 | 
							if (Users.isLocalUser(user) && profile.autoWatch) {
 | 
				
			||||||
			watch(user.id, data.renote);
 | 
								watch(user.id, data.renote);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ import watch from '../../../services/note/watch';
 | 
				
			||||||
import { publishNoteStream } from '../../stream';
 | 
					import { publishNoteStream } from '../../stream';
 | 
				
			||||||
import { User } from '../../../models/entities/user';
 | 
					import { User } from '../../../models/entities/user';
 | 
				
			||||||
import { Note } from '../../../models/entities/note';
 | 
					import { Note } from '../../../models/entities/note';
 | 
				
			||||||
import { PollVotes, Users, NoteWatchings, Polls } from '../../../models';
 | 
					import { PollVotes, Users, NoteWatchings, Polls, UserProfiles } from '../../../models';
 | 
				
			||||||
import { Not } from 'typeorm';
 | 
					import { Not } from 'typeorm';
 | 
				
			||||||
import { genId } from '../../../misc/gen-id';
 | 
					import { genId } from '../../../misc/gen-id';
 | 
				
			||||||
import { createNotification } from '../../create-notification';
 | 
					import { createNotification } from '../../create-notification';
 | 
				
			||||||
| 
						 | 
					@ -67,8 +67,10 @@ export default (user: User, note: Note, choice: number) => new Promise(async (re
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// ローカルユーザーが投票した場合この投稿をWatchする
 | 
						// ローカルユーザーが投票した場合この投稿をWatchする
 | 
				
			||||||
	if (Users.isLocalUser(user) && user.autoWatch) {
 | 
						if (Users.isLocalUser(user) && profile.autoWatch) {
 | 
				
			||||||
		watch(user.id, note);
 | 
							watch(user.id, note);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ import { toDbReaction } from '../../../misc/reaction-lib';
 | 
				
			||||||
import fetchMeta from '../../../misc/fetch-meta';
 | 
					import fetchMeta from '../../../misc/fetch-meta';
 | 
				
			||||||
import { User } from '../../../models/entities/user';
 | 
					import { User } from '../../../models/entities/user';
 | 
				
			||||||
import { Note } from '../../../models/entities/note';
 | 
					import { Note } from '../../../models/entities/note';
 | 
				
			||||||
import { NoteReactions, Users, NoteWatchings, Notes } from '../../../models';
 | 
					import { NoteReactions, Users, NoteWatchings, Notes, UserProfiles } from '../../../models';
 | 
				
			||||||
import { Not } from 'typeorm';
 | 
					import { Not } from 'typeorm';
 | 
				
			||||||
import { perUserReactionsChart } from '../../chart';
 | 
					import { perUserReactionsChart } from '../../chart';
 | 
				
			||||||
import { genId } from '../../../misc/gen-id';
 | 
					import { genId } from '../../../misc/gen-id';
 | 
				
			||||||
| 
						 | 
					@ -79,8 +79,10 @@ export default async (user: User, note: Note, reaction: string) => {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	});
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const profile = await UserProfiles.findOne({ userId: user.id });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// ユーザーがローカルユーザーかつ自動ウォッチ設定がオンならばこの投稿をWatchする
 | 
						// ユーザーがローカルユーザーかつ自動ウォッチ設定がオンならばこの投稿をWatchする
 | 
				
			||||||
	if (Users.isLocalUser(user) && user.autoWatch !== false) {
 | 
						if (Users.isLocalUser(user) && profile.autoWatch) {
 | 
				
			||||||
		watch(user.id, note);
 | 
							watch(user.id, note);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue