enhance: 招待機能の改善 (#11195)
* refactor(backend): 招待機能を改修 * feat(backend): 招待コードのcreate/delete/listエンドポイントを追加 * add(misskey-js): エンドポイントと型を追加 * change(backend): metaでinvite関連の情報も返すように * add(misskey-js): エンドポイントと型を追加 * add(backend): `/endpoints/invite/limit`を追加 * fix: createdByがnullableではなかったのを修正 * fix: relationが取得できていなかった問題を修正 * fix: パラメータを間違えていたのを修正 * feat(client): 招待ページを実装 * change(client): インスタンスメニューの「招待」押した場合に招待ページに飛ぶように変更 * feat: 招待コードをコピーできるように * change(backend): metaに招待コード発行に関する情報を持たせるのをやめる * feat: ロールごとに招待コードの発行上限数などを設定できるように * change(client): 招待コードをコピーしたときにダイアログを出すように * add: 招待に関する管理者用のエンドポイントを追加 * change(backend): モデレーターであれば作成者以外でも招待コードを削除できるように * change(backend): admin/invite/listはオフセットでページネーションするように * feat(client): 招待コードの管理ページを追加 * feat(client): 招待コードのリストをソートできるように * change: `admin/invite/create`のレスポンスを修正 * fix(client): 有効期限を指定できていなかった問題を修正 * refactor: 必要のない箇所を削除 * perf(backend): use limit() instead of take() * change(client): 作成ボタンを見た目を変更 * refactor: 招待コードの生成部分を共通化し、コード内に"01OI"のいずれかの文字を含まないように * fix(client): paginationの仕様が変わっていたので修正 * change(backend): expiresAtパラメータのnullを許容 * change(client): 有効期限を設けないときは日付の入力欄を非表示に * fix: 自身のポリシーよりもインスタンス側のポリシーが優先表示される問題を修正 * fix: n時間のときに「n時間間」となってしまうのを修正 * fix(backend): ポリシーが途中で変更されたときに作成可能数がマイナス表記になってしまうのを修正 * change(client): 招待コードのユーザー名が不明な理由を表示するように * update: CHANGELOG.md * lint * refactor * refactor * tweak ui * 🎨 * 🎨 * add(backend): indexを追加 * change(backend): indexの追加に伴う変更 * change(client): インスタンスメニューの「招待」の場所を変更 * add(frontend): MkInviteCode用のstorybookを追加 * Update misskey-js.api.md * fix(misskey-js): InviteのcreatedByの型が間違っていたのを修正 --------- Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> Co-authored-by: tamaina <tamaina@hotmail.co.jp>
This commit is contained in:
parent
1c82e97350
commit
02957a1b5d
36 changed files with 1341 additions and 56 deletions
|
@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||
import bcrypt from 'bcryptjs';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js';
|
||||
import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository, RegistrationTicket } from '@/models/index.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { CaptchaService } from '@/core/CaptchaService.js';
|
||||
|
@ -109,13 +109,15 @@ export class SignupApiService {
|
|||
}
|
||||
}
|
||||
|
||||
let ticket: RegistrationTicket | null = null;
|
||||
|
||||
if (instance.disableRegistration) {
|
||||
if (invitationCode == null || typeof invitationCode !== 'string') {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const ticket = await this.registrationTicketsRepository.findOneBy({
|
||||
ticket = await this.registrationTicketsRepository.findOneBy({
|
||||
code: invitationCode,
|
||||
});
|
||||
|
||||
|
@ -124,7 +126,15 @@ export class SignupApiService {
|
|||
return;
|
||||
}
|
||||
|
||||
this.registrationTicketsRepository.delete(ticket.id);
|
||||
if (ticket.expiresAt && ticket.expiresAt < new Date()) {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ticket.usedAt) {
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.emailRequiredForSignup) {
|
||||
|
@ -148,14 +158,14 @@ export class SignupApiService {
|
|||
const salt = await bcrypt.genSalt(8);
|
||||
const hash = await bcrypt.hash(password, salt);
|
||||
|
||||
await this.userPendingsRepository.insert({
|
||||
const pendingUser = await this.userPendingsRepository.insert({
|
||||
id: this.idService.genId(),
|
||||
createdAt: new Date(),
|
||||
code,
|
||||
email: emailAddress!,
|
||||
username: username,
|
||||
password: hash,
|
||||
});
|
||||
}).then(x => this.userPendingsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
const link = `${this.config.url}/signup-complete/${code}`;
|
||||
|
||||
|
@ -163,6 +173,13 @@ export class SignupApiService {
|
|||
`To complete signup, please click this link:<br><a href="${link}">${link}</a>`,
|
||||
`To complete signup, please click this link: ${link}`);
|
||||
|
||||
if (ticket) {
|
||||
await this.registrationTicketsRepository.update(ticket.id, {
|
||||
usedAt: new Date(),
|
||||
pendingUserId: pendingUser.id,
|
||||
});
|
||||
}
|
||||
|
||||
reply.code(204);
|
||||
return;
|
||||
} else {
|
||||
|
@ -176,6 +193,14 @@ export class SignupApiService {
|
|||
includeSecrets: true,
|
||||
});
|
||||
|
||||
if (ticket) {
|
||||
await this.registrationTicketsRepository.update(ticket.id, {
|
||||
usedAt: new Date(),
|
||||
usedBy: account,
|
||||
usedById: account.id,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...res,
|
||||
token: secret,
|
||||
|
@ -212,6 +237,15 @@ export class SignupApiService {
|
|||
emailVerifyCode: null,
|
||||
});
|
||||
|
||||
const ticket = await this.registrationTicketsRepository.findOneBy({ pendingUserId: pendingUser.id });
|
||||
if (ticket) {
|
||||
await this.registrationTicketsRepository.update(ticket.id, {
|
||||
usedBy: account,
|
||||
usedById: account.id,
|
||||
pendingUserId: null,
|
||||
});
|
||||
}
|
||||
|
||||
return this.signinService.signin(request, reply, account as LocalUser);
|
||||
} catch (err) {
|
||||
throw new FastifyReplyError(400, typeof err === 'string' ? err : (err as Error).toString());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue