トランザクションを使うようにしたり
This commit is contained in:
parent
4198246351
commit
2ff3069d23
12 changed files with 117 additions and 67 deletions
|
@ -8,7 +8,7 @@
|
||||||
<div class="is-remote" v-if="user.host != null">
|
<div class="is-remote" v-if="user.host != null">
|
||||||
<details>
|
<details>
|
||||||
<summary><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}</summary>
|
<summary><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}</summary>
|
||||||
<a :href="user.url || user.uri" target="_blank">{{ $t('@.view-on-remote') }}</a>
|
<a :href="user.url" target="_blank">{{ $t('@.view-on-remote') }}</a>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
<header :style="bannerStyle">
|
<header :style="bannerStyle">
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<fa icon="exclamation-triangle"/> {{ $t('@.user-suspended') }}
|
<fa icon="exclamation-triangle"/> {{ $t('@.user-suspended') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="is-remote" v-if="user.host != null" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
|
<div class="is-remote" v-if="user.host != null" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
|
||||||
<fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}<a :href="user.url || user.uri" target="_blank">{{ $t('@.view-on-remote') }}</a>
|
<fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}<a :href="user.url" target="_blank">{{ $t('@.view-on-remote') }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<x-header class="header" :user="user"/>
|
<x-header class="header" :user="user"/>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</template>
|
</template>
|
||||||
<div class="wwtwuxyh" v-if="!fetching">
|
<div class="wwtwuxyh" v-if="!fetching">
|
||||||
<div class="is-suspended" v-if="user.isSuspended"><p><fa icon="exclamation-triangle"/> {{ $t('@.user-suspended') }}</p></div>
|
<div class="is-suspended" v-if="user.isSuspended"><p><fa icon="exclamation-triangle"/> {{ $t('@.user-suspended') }}</p></div>
|
||||||
<div class="is-remote" v-if="user.host != null"><p><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}<a :href="user.url || user.uri" target="_blank">{{ $t('@.view-on-remote') }}</a></p></div>
|
<div class="is-remote" v-if="user.host != null"><p><fa icon="exclamation-triangle"/> {{ $t('@.is-remote-user') }}<a :href="user.url" target="_blank">{{ $t('@.view-on-remote') }}</a></p></div>
|
||||||
<header>
|
<header>
|
||||||
<div class="banner" :style="style"></div>
|
<div class="banner" :style="style"></div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
|
|
|
@ -22,4 +22,12 @@ export class UserKeypair {
|
||||||
length: 4096,
|
length: 4096,
|
||||||
})
|
})
|
||||||
public privateKey: string;
|
public privateKey: string;
|
||||||
|
|
||||||
|
constructor(data: Partial<UserKeypair>) {
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(data)) {
|
||||||
|
(this as any)[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,12 @@ export class UserProfile {
|
||||||
value: string;
|
value: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 512, nullable: true,
|
||||||
|
comment: 'Remote URL of the user.'
|
||||||
|
})
|
||||||
|
public url: string | null;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 128, nullable: true,
|
length: 128, nullable: true,
|
||||||
comment: 'The email address of the User.'
|
comment: 'The email address of the User.'
|
||||||
|
@ -192,4 +198,12 @@ export class UserProfile {
|
||||||
})
|
})
|
||||||
public userHost: string | null;
|
public userHost: string | null;
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
constructor(data: Partial<UserProfile>) {
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(data)) {
|
||||||
|
(this as any)[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,4 +23,12 @@ export class UserPublickey {
|
||||||
length: 4096,
|
length: 4096,
|
||||||
})
|
})
|
||||||
public keyPem: string;
|
public keyPem: string;
|
||||||
|
|
||||||
|
constructor(data: Partial<UserPublickey>) {
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(data)) {
|
||||||
|
(this as any)[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.'
|
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;
|
||||||
|
|
||||||
|
constructor(data: Partial<User>) {
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(data)) {
|
||||||
|
(this as any)[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ILocalUser extends User {
|
export interface ILocalUser extends User {
|
||||||
|
|
|
@ -87,7 +87,6 @@ export class UserRepository extends Repository<User> {
|
||||||
name: user.name,
|
name: user.name,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
host: user.host,
|
host: user.host,
|
||||||
uri: user.uri,
|
|
||||||
avatarUrl: user.avatarUrl,
|
avatarUrl: user.avatarUrl,
|
||||||
bannerUrl: user.bannerUrl,
|
bannerUrl: user.bannerUrl,
|
||||||
avatarColor: user.avatarColor,
|
avatarColor: user.avatarColor,
|
||||||
|
@ -118,6 +117,7 @@ export class UserRepository extends Repository<User> {
|
||||||
} : {}),
|
} : {}),
|
||||||
|
|
||||||
...(opts.detail ? {
|
...(opts.detail ? {
|
||||||
|
url: profile.url,
|
||||||
createdAt: user.createdAt,
|
createdAt: user.createdAt,
|
||||||
updatedAt: user.updatedAt,
|
updatedAt: user.updatedAt,
|
||||||
description: profile.description,
|
description: profile.description,
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-e
|
||||||
import { toPuny } from '../../../misc/convert-host';
|
import { toPuny } from '../../../misc/convert-host';
|
||||||
import { UserProfile } from '../../../models/entities/user-profile';
|
import { UserProfile } from '../../../models/entities/user-profile';
|
||||||
import { validActor } from '../../../remote/activitypub/type';
|
import { validActor } from '../../../remote/activitypub/type';
|
||||||
|
import { getConnection } from 'typeorm';
|
||||||
const logger = apLogger;
|
const logger = apLogger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -136,27 +137,42 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
||||||
// Create user
|
// Create user
|
||||||
let user: IRemoteUser;
|
let user: IRemoteUser;
|
||||||
try {
|
try {
|
||||||
user = await Users.save({
|
// Start transaction
|
||||||
id: genId(),
|
await getConnection().transaction(async transactionalEntityManager => {
|
||||||
avatarId: null,
|
user = await transactionalEntityManager.save(new User({
|
||||||
bannerId: null,
|
id: genId(),
|
||||||
createdAt: Date.parse(person.published) || new Date(),
|
avatarId: null,
|
||||||
lastFetchedAt: new Date(),
|
bannerId: null,
|
||||||
name: person.name,
|
createdAt: new Date(person.published) || new Date(),
|
||||||
isLocked: person.manuallyApprovesFollowers,
|
lastFetchedAt: new Date(),
|
||||||
username: person.preferredUsername,
|
name: person.name,
|
||||||
usernameLower: person.preferredUsername.toLowerCase(),
|
isLocked: person.manuallyApprovesFollowers,
|
||||||
host,
|
username: person.preferredUsername,
|
||||||
inbox: person.inbox,
|
usernameLower: person.preferredUsername.toLowerCase(),
|
||||||
sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
host,
|
||||||
featured: person.featured,
|
inbox: person.inbox,
|
||||||
endpoints: person.endpoints,
|
sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||||
uri: person.id,
|
featured: person.featured,
|
||||||
url: person.url,
|
uri: person.id,
|
||||||
tags,
|
tags,
|
||||||
isBot,
|
isBot,
|
||||||
isCat: (person as any).isCat === true
|
isCat: (person as any).isCat === true
|
||||||
} as Partial<User>) as IRemoteUser;
|
})) 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) {
|
} catch (e) {
|
||||||
// duplicate key error
|
// duplicate key error
|
||||||
if (isDuplicateKeyValueError(e)) {
|
if (isDuplicateKeyValueError(e)) {
|
||||||
|
@ -167,19 +183,6 @@ 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,
|
|
||||||
userHost: host
|
|
||||||
} as Partial<UserProfile>);
|
|
||||||
|
|
||||||
await UserPublickeys.save({
|
|
||||||
userId: user.id,
|
|
||||||
keyId: person.publicKey.id,
|
|
||||||
keyPem: person.publicKey.publicKeyPem
|
|
||||||
} as UserPublickey);
|
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
|
@ -12,6 +12,7 @@ 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';
|
import { UserProfile } from '../../../models/entities/user-profile';
|
||||||
|
import { getConnection } from 'typeorm';
|
||||||
|
|
||||||
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;
|
||||||
|
@ -99,28 +100,33 @@ export default async (ctx: Koa.BaseContext) => {
|
||||||
e ? j(e) : s([publicKey, privateKey])
|
e ? j(e) : s([publicKey, privateKey])
|
||||||
));
|
));
|
||||||
|
|
||||||
const account = await Users.save({
|
let account: User;
|
||||||
id: genId(),
|
|
||||||
createdAt: new Date(),
|
|
||||||
username: username,
|
|
||||||
usernameLower: username.toLowerCase(),
|
|
||||||
host: toPuny(host),
|
|
||||||
token: secret,
|
|
||||||
isAdmin: config.autoAdmin && usersCount === 0,
|
|
||||||
} as User);
|
|
||||||
|
|
||||||
await UserKeypairs.save({
|
// Start transaction
|
||||||
publicKey: keyPair[0],
|
await getConnection().transaction(async transactionalEntityManager => {
|
||||||
privateKey: keyPair[1],
|
account = await transactionalEntityManager.save(new User({
|
||||||
userId: account.id
|
id: genId(),
|
||||||
} as UserKeypair);
|
createdAt: new Date(),
|
||||||
|
username: username,
|
||||||
|
usernameLower: username.toLowerCase(),
|
||||||
|
host: toPuny(host),
|
||||||
|
token: secret,
|
||||||
|
isAdmin: config.autoAdmin && usersCount === 0,
|
||||||
|
}));
|
||||||
|
|
||||||
await UserProfiles.save({
|
await transactionalEntityManager.save(new UserKeypair({
|
||||||
userId: account.id,
|
publicKey: keyPair[0],
|
||||||
autoAcceptFollowed: true,
|
privateKey: keyPair[1],
|
||||||
autoWatch: false,
|
userId: account.id
|
||||||
password: hash,
|
}));
|
||||||
} as Partial<UserProfile>);
|
|
||||||
|
await transactionalEntityManager.save(new UserProfile({
|
||||||
|
userId: account.id,
|
||||||
|
autoAcceptFollowed: true,
|
||||||
|
autoWatch: false,
|
||||||
|
password: hash,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
usersChart.update(account, true);
|
usersChart.update(account, true);
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import fetchMeta from '../../misc/fetch-meta';
|
||||||
import * as pkg from '../../../package.json';
|
import * as pkg from '../../../package.json';
|
||||||
import { genOpenapiSpec } from '../api/openapi/gen-spec';
|
import { genOpenapiSpec } from '../api/openapi/gen-spec';
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import { Users, Notes, Emojis } from '../../models';
|
import { Users, Notes, Emojis, UserProfiles } from '../../models';
|
||||||
import parseAcct from '../../misc/acct/parse';
|
import parseAcct from '../../misc/acct/parse';
|
||||||
import getNoteSummary from '../../misc/get-note-summary';
|
import getNoteSummary from '../../misc/get-note-summary';
|
||||||
|
|
||||||
|
@ -149,11 +149,14 @@ router.get('/@:user', async (ctx, next) => {
|
||||||
usernameLower: username.toLowerCase(),
|
usernameLower: username.toLowerCase(),
|
||||||
host
|
host
|
||||||
});
|
});
|
||||||
|
const profile = await UserProfiles.findOne({
|
||||||
|
userId: user.id
|
||||||
|
});
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
const meta = await fetchMeta();
|
const meta = await fetchMeta();
|
||||||
await ctx.render('user', {
|
await ctx.render('user', {
|
||||||
user,
|
user, profile,
|
||||||
instanceName: meta.name || 'Misskey'
|
instanceName: meta.name || 'Misskey'
|
||||||
});
|
});
|
||||||
ctx.set('Cache-Control', 'public, max-age=180');
|
ctx.set('Cache-Control', 'public, max-age=180');
|
||||||
|
|
|
@ -9,12 +9,12 @@ block title
|
||||||
= `${title} | ${instanceName}`
|
= `${title} | ${instanceName}`
|
||||||
|
|
||||||
block desc
|
block desc
|
||||||
meta(name='description' content= user.description)
|
meta(name='description' content= profile.description)
|
||||||
|
|
||||||
block og
|
block og
|
||||||
meta(property='og:type' content='blog')
|
meta(property='og:type' content='blog')
|
||||||
meta(property='og:title' content= title)
|
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:url' content= url)
|
||||||
meta(property='og:image' content= img)
|
meta(property='og:image' content= img)
|
||||||
|
|
||||||
|
@ -24,12 +24,12 @@ block meta
|
||||||
|
|
||||||
meta(name='twitter:card' content='summary')
|
meta(name='twitter:card' content='summary')
|
||||||
|
|
||||||
if user.twitter
|
if profile.twitter
|
||||||
meta(name='twitter:creator' content=`@${user.twitter.screenName}`)
|
meta(name='twitter:creator' content=`@${profile.twitter.screenName}`)
|
||||||
|
|
||||||
if !user.host
|
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
|
if user.uri
|
||||||
link(rel='alternate' href=user.uri type='application/activity+json')
|
link(rel='alternate' href=user.uri type='application/activity+json')
|
||||||
if user.url
|
if profile.url
|
||||||
link(rel='alternate' href=user.url type='text/html')
|
link(rel='alternate' href=profile.url type='text/html')
|
||||||
|
|
Loading…
Reference in a new issue