diff --git a/src/client/app/common/views/deck/deck.user-column.vue b/src/client/app/common/views/deck/deck.user-column.vue index e18a11114..fb50d880e 100644 --- a/src/client/app/common/views/deck/deck.user-column.vue +++ b/src/client/app/common/views/deck/deck.user-column.vue @@ -8,7 +8,7 @@
{{ $t('@.is-remote-user') }} - {{ $t('@.view-on-remote') }} + {{ $t('@.view-on-remote') }}
diff --git a/src/client/app/desktop/views/home/user/index.vue b/src/client/app/desktop/views/home/user/index.vue index 17a34a30c..338cd1c59 100644 --- a/src/client/app/desktop/views/home/user/index.vue +++ b/src/client/app/desktop/views/home/user/index.vue @@ -4,7 +4,7 @@ {{ $t('@.user-suspended') }}
- {{ $t('@.is-remote-user') }}{{ $t('@.view-on-remote') }} + {{ $t('@.is-remote-user') }}{{ $t('@.view-on-remote') }}
diff --git a/src/client/app/mobile/views/pages/user/index.vue b/src/client/app/mobile/views/pages/user/index.vue index 1376f3965..cc53ae0cf 100644 --- a/src/client/app/mobile/views/pages/user/index.vue +++ b/src/client/app/mobile/views/pages/user/index.vue @@ -5,7 +5,7 @@

{{ $t('@.user-suspended') }}

-

{{ $t('@.is-remote-user') }}{{ $t('@.view-on-remote') }}

+

{{ $t('@.is-remote-user') }}{{ $t('@.view-on-remote') }}

diff --git a/src/models/entities/user-keypair.ts b/src/models/entities/user-keypair.ts index 808985f47..603321d75 100644 --- a/src/models/entities/user-keypair.ts +++ b/src/models/entities/user-keypair.ts @@ -22,4 +22,12 @@ export class UserKeypair { length: 4096, }) public privateKey: string; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } } diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts index 421d17c59..a2d7b8d2c 100644 --- a/src/models/entities/user-profile.ts +++ b/src/models/entities/user-profile.ts @@ -39,6 +39,12 @@ export class UserProfile { value: string; }[]; + @Column('varchar', { + length: 512, nullable: true, + comment: 'Remote URL of the user.' + }) + public url: string | null; + @Column('varchar', { length: 128, nullable: true, comment: 'The email address of the User.' @@ -192,4 +198,12 @@ export class UserProfile { }) public userHost: string | null; //#endregion + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } } diff --git a/src/models/entities/user-publickey.ts b/src/models/entities/user-publickey.ts index 26b694407..21edc3e9e 100644 --- a/src/models/entities/user-publickey.ts +++ b/src/models/entities/user-publickey.ts @@ -23,4 +23,12 @@ export class UserPublickey { length: 4096, }) public keyPem: string; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } } diff --git a/src/models/entities/user.ts b/src/models/entities/user.ts index ebf07ff3f..e40c32a76 100644 --- a/src/models/entities/user.ts +++ b/src/models/entities/user.ts @@ -205,6 +205,14 @@ export class User { comment: 'The native access token of the User. It will be null if the origin of the user is local.' }) public token: string | null; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } } export interface ILocalUser extends User { diff --git a/src/models/repositories/user.ts b/src/models/repositories/user.ts index ac18b1899..d7f2c3d04 100644 --- a/src/models/repositories/user.ts +++ b/src/models/repositories/user.ts @@ -87,7 +87,6 @@ export class UserRepository extends Repository { name: user.name, username: user.username, host: user.host, - uri: user.uri, avatarUrl: user.avatarUrl, bannerUrl: user.bannerUrl, avatarColor: user.avatarColor, @@ -118,6 +117,7 @@ export class UserRepository extends Repository { } : {}), ...(opts.detail ? { + url: profile.url, createdAt: user.createdAt, updatedAt: user.updatedAt, description: profile.description, diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index b2edf3973..58aca48eb 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -25,6 +25,7 @@ import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-e import { toPuny } from '../../../misc/convert-host'; import { UserProfile } from '../../../models/entities/user-profile'; import { validActor } from '../../../remote/activitypub/type'; +import { getConnection } from 'typeorm'; const logger = apLogger; /** @@ -136,27 +137,42 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise) as IRemoteUser; + // Start transaction + await getConnection().transaction(async transactionalEntityManager => { + user = await transactionalEntityManager.save(new User({ + id: genId(), + avatarId: null, + bannerId: null, + createdAt: new Date(person.published) || new Date(), + lastFetchedAt: new Date(), + name: person.name, + isLocked: person.manuallyApprovesFollowers, + username: person.preferredUsername, + usernameLower: person.preferredUsername.toLowerCase(), + host, + inbox: person.inbox, + sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), + featured: person.featured, + uri: person.id, + tags, + isBot, + isCat: (person as any).isCat === true + })) as IRemoteUser; + + await transactionalEntityManager.save(new UserProfile({ + userId: user.id, + description: fromHtml(person.summary), + url: person.url, + fields, + userHost: host + })); + + await transactionalEntityManager.save(new UserPublickey({ + userId: user.id, + keyId: person.publicKey.id, + keyPem: person.publicKey.publicKeyPem + })); + }); } catch (e) { // duplicate key error if (isDuplicateKeyValueError(e)) { @@ -167,19 +183,6 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise); - - await UserPublickeys.save({ - userId: user.id, - keyId: person.publicKey.id, - keyPem: person.publicKey.publicKeyPem - } as UserPublickey); - // Register host registerOrFetchInstanceDoc(host).then(i => { Instances.increment({ id: i.id }, 'usersCount', 1); diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts index 5ed25fa41..ed4a533de 100644 --- a/src/server/api/private/signup.ts +++ b/src/server/api/private/signup.ts @@ -12,6 +12,7 @@ import { User } from '../../../models/entities/user'; import { UserKeypair } from '../../../models/entities/user-keypair'; import { toPuny } from '../../../misc/convert-host'; import { UserProfile } from '../../../models/entities/user-profile'; +import { getConnection } from 'typeorm'; export default async (ctx: Koa.BaseContext) => { const body = ctx.request.body as any; @@ -99,28 +100,33 @@ export default async (ctx: Koa.BaseContext) => { e ? j(e) : s([publicKey, privateKey]) )); - const account = await Users.save({ - id: genId(), - createdAt: new Date(), - username: username, - usernameLower: username.toLowerCase(), - host: toPuny(host), - token: secret, - isAdmin: config.autoAdmin && usersCount === 0, - } as User); + let account: User; - await UserKeypairs.save({ - publicKey: keyPair[0], - privateKey: keyPair[1], - userId: account.id - } as UserKeypair); + // Start transaction + await getConnection().transaction(async transactionalEntityManager => { + account = await transactionalEntityManager.save(new User({ + id: genId(), + createdAt: new Date(), + username: username, + usernameLower: username.toLowerCase(), + host: toPuny(host), + token: secret, + isAdmin: config.autoAdmin && usersCount === 0, + })); - await UserProfiles.save({ - userId: account.id, - autoAcceptFollowed: true, - autoWatch: false, - password: hash, - } as Partial); + await transactionalEntityManager.save(new UserKeypair({ + publicKey: keyPair[0], + privateKey: keyPair[1], + userId: account.id + })); + + await transactionalEntityManager.save(new UserProfile({ + userId: account.id, + autoAcceptFollowed: true, + autoWatch: false, + password: hash, + })); + }); usersChart.update(account, true); diff --git a/src/server/web/index.ts b/src/server/web/index.ts index de0d65cf3..d1a15e392 100644 --- a/src/server/web/index.ts +++ b/src/server/web/index.ts @@ -16,7 +16,7 @@ import fetchMeta from '../../misc/fetch-meta'; import * as pkg from '../../../package.json'; import { genOpenapiSpec } from '../api/openapi/gen-spec'; import config from '../../config'; -import { Users, Notes, Emojis } from '../../models'; +import { Users, Notes, Emojis, UserProfiles } from '../../models'; import parseAcct from '../../misc/acct/parse'; import getNoteSummary from '../../misc/get-note-summary'; @@ -149,11 +149,14 @@ router.get('/@:user', async (ctx, next) => { usernameLower: username.toLowerCase(), host }); + const profile = await UserProfiles.findOne({ + userId: user.id + }); if (user != null) { const meta = await fetchMeta(); await ctx.render('user', { - user, + user, profile, instanceName: meta.name || 'Misskey' }); ctx.set('Cache-Control', 'public, max-age=180'); diff --git a/src/server/web/views/user.pug b/src/server/web/views/user.pug index 3f32933f5..bff98ba80 100644 --- a/src/server/web/views/user.pug +++ b/src/server/web/views/user.pug @@ -9,12 +9,12 @@ block title = `${title} | ${instanceName}` block desc - meta(name='description' content= user.description) + meta(name='description' content= profile.description) block og meta(property='og:type' content='blog') meta(property='og:title' content= title) - meta(property='og:description' content= user.description) + meta(property='og:description' content= profile.description) meta(property='og:url' content= url) meta(property='og:image' content= img) @@ -24,12 +24,12 @@ block meta meta(name='twitter:card' content='summary') - if user.twitter - meta(name='twitter:creator' content=`@${user.twitter.screenName}`) + if profile.twitter + meta(name='twitter:creator' content=`@${profile.twitter.screenName}`) if !user.host - link(rel='alternate' href=`${config.url}/users/${user._id}` type='application/activity+json') + link(rel='alternate' href=`${config.url}/users/${user.id}` type='application/activity+json') if user.uri link(rel='alternate' href=user.uri type='application/activity+json') - if user.url - link(rel='alternate' href=user.url type='text/html') + if profile.url + link(rel='alternate' href=profile.url type='text/html')