テーブル分割

This commit is contained in:
syuilo 2019-04-10 15:04:27 +09:00
parent 9603f3fa4f
commit 626cfb61ac
No known key found for this signature in database
GPG key ID: BDC4C49D06AB9D69
33 changed files with 267 additions and 232 deletions

View file

@ -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,

View file

@ -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,

View file

@ -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, {

View file

@ -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()

View file

@ -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, {

View file

@ -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 {

View file

@ -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);

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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
}); });

View file

@ -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
}); });
}); });

View file

@ -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
}); });

View file

@ -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;
}); });

View file

@ -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
}); });
}); });

View file

@ -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');

View file

@ -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');

View file

@ -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

View file

@ -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
}); });

View file

@ -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,

View file

@ -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);
} }

View file

@ -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
}); });

View file

@ -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);

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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
}); });

View file

@ -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: {

View file

@ -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;

View file

@ -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

View file

@ -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);
} }

View file

@ -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);
} }
}); });

View file

@ -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);
} }