mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(core): split profile route (#6802)
This commit is contained in:
parent
859495b13b
commit
f8d21e49fa
2 changed files with 127 additions and 105 deletions
123
packages/core/src/routes/profile/identities.ts
Normal file
123
packages/core/src/routes/profile/identities.ts
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import { UserScope } from '@logto/core-kit';
|
||||||
|
import { VerificationType, AccountCenterControlValue } from '@logto/schemas';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import koaGuard from '#src/middleware/koa-guard.js';
|
||||||
|
|
||||||
|
import RequestError from '../../errors/RequestError/index.js';
|
||||||
|
import { buildVerificationRecordByIdAndType } from '../../libraries/verification.js';
|
||||||
|
import assertThat from '../../utils/assert-that.js';
|
||||||
|
import type { UserRouter, RouterInitArgs } from '../types.js';
|
||||||
|
|
||||||
|
export default function identitiesRoutes<T extends UserRouter>(
|
||||||
|
...[router, { queries, libraries }]: RouterInitArgs<T>
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
users: { updateUserById, findUserById, deleteUserIdentity },
|
||||||
|
} = queries;
|
||||||
|
|
||||||
|
const {
|
||||||
|
users: { checkIdentifierCollision },
|
||||||
|
} = libraries;
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
'/profile/identities',
|
||||||
|
koaGuard({
|
||||||
|
body: z.object({
|
||||||
|
newIdentifierVerificationRecordId: z.string(),
|
||||||
|
}),
|
||||||
|
status: [204, 400, 401],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const { id: userId, scopes, identityVerified } = ctx.auth;
|
||||||
|
assertThat(
|
||||||
|
identityVerified,
|
||||||
|
new RequestError({ code: 'verification_record.permission_denied', status: 401 })
|
||||||
|
);
|
||||||
|
const { newIdentifierVerificationRecordId } = ctx.guard.body;
|
||||||
|
const { fields } = ctx.accountCenter;
|
||||||
|
assertThat(
|
||||||
|
fields.social === AccountCenterControlValue.Edit,
|
||||||
|
'account_center.filed_not_editable'
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(scopes.has(UserScope.Identities), 'auth.unauthorized');
|
||||||
|
|
||||||
|
// Check new identifier
|
||||||
|
const newVerificationRecord = await buildVerificationRecordByIdAndType({
|
||||||
|
type: VerificationType.Social,
|
||||||
|
id: newIdentifierVerificationRecordId,
|
||||||
|
queries,
|
||||||
|
libraries,
|
||||||
|
});
|
||||||
|
assertThat(newVerificationRecord.isVerified, 'verification_record.not_found');
|
||||||
|
|
||||||
|
const {
|
||||||
|
socialIdentity: { target, userInfo },
|
||||||
|
} = await newVerificationRecord.toUserProfile();
|
||||||
|
|
||||||
|
await checkIdentifierCollision({ identity: { target, id: userInfo.id } }, userId);
|
||||||
|
|
||||||
|
const user = await findUserById(userId);
|
||||||
|
|
||||||
|
assertThat(!user.identities[target], 'user.identity_already_in_use');
|
||||||
|
|
||||||
|
const updatedUser = await updateUserById(userId, {
|
||||||
|
identities: {
|
||||||
|
...user.identities,
|
||||||
|
[target]: {
|
||||||
|
userId: userInfo.id,
|
||||||
|
details: userInfo,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.appendDataHookContext('User.Data.Updated', { user: updatedUser });
|
||||||
|
|
||||||
|
ctx.status = 204;
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
'/profile/identities/:target',
|
||||||
|
koaGuard({
|
||||||
|
params: z.object({ target: z.string() }),
|
||||||
|
status: [204, 400, 401, 404],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const { id: userId, scopes, identityVerified } = ctx.auth;
|
||||||
|
assertThat(
|
||||||
|
identityVerified,
|
||||||
|
new RequestError({ code: 'verification_record.permission_denied', status: 401 })
|
||||||
|
);
|
||||||
|
const { target } = ctx.guard.params;
|
||||||
|
const { fields } = ctx.accountCenter;
|
||||||
|
assertThat(
|
||||||
|
fields.social === AccountCenterControlValue.Edit,
|
||||||
|
'account_center.filed_not_editable'
|
||||||
|
);
|
||||||
|
|
||||||
|
assertThat(scopes.has(UserScope.Identities), 'auth.unauthorized');
|
||||||
|
|
||||||
|
const user = await findUserById(userId);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
user.identities[target],
|
||||||
|
new RequestError({
|
||||||
|
code: 'user.identity_not_exist',
|
||||||
|
status: 404,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedUser = await deleteUserIdentity(userId, target);
|
||||||
|
|
||||||
|
ctx.appendDataHookContext('User.Data.Updated', { user: updatedUser });
|
||||||
|
|
||||||
|
ctx.status = 204;
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
/* eslint-disable max-lines */
|
|
||||||
import { emailRegEx, phoneRegEx, usernameRegEx, UserScope } from '@logto/core-kit';
|
import { emailRegEx, phoneRegEx, usernameRegEx, UserScope } from '@logto/core-kit';
|
||||||
import {
|
import {
|
||||||
VerificationType,
|
VerificationType,
|
||||||
|
@ -18,12 +17,12 @@ import assertThat from '../../utils/assert-that.js';
|
||||||
import { PasswordValidator } from '../experience/classes/libraries/password-validator.js';
|
import { PasswordValidator } from '../experience/classes/libraries/password-validator.js';
|
||||||
import type { UserRouter, RouterInitArgs } from '../types.js';
|
import type { UserRouter, RouterInitArgs } from '../types.js';
|
||||||
|
|
||||||
|
import identitiesRoutes from './identities.js';
|
||||||
import koaAccountCenter from './middlewares/koa-account-center.js';
|
import koaAccountCenter from './middlewares/koa-account-center.js';
|
||||||
import { getAccountCenterFilteredProfile, getScopedProfile } from './utils/get-scoped-profile.js';
|
import { getAccountCenterFilteredProfile, getScopedProfile } from './utils/get-scoped-profile.js';
|
||||||
|
|
||||||
export default function profileRoutes<T extends UserRouter>(
|
export default function profileRoutes<T extends UserRouter>(...args: RouterInitArgs<T>) {
|
||||||
...[router, { queries, libraries }]: RouterInitArgs<T>
|
const [router, { queries, libraries }] = args;
|
||||||
) {
|
|
||||||
const {
|
const {
|
||||||
users: { updateUserById, findUserById, deleteUserIdentity },
|
users: { updateUserById, findUserById, deleteUserIdentity },
|
||||||
signInExperiences: { findDefaultSignInExperience },
|
signInExperiences: { findDefaultSignInExperience },
|
||||||
|
@ -268,105 +267,5 @@ export default function profileRoutes<T extends UserRouter>(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
identitiesRoutes(...args);
|
||||||
'/profile/identities',
|
|
||||||
koaGuard({
|
|
||||||
body: z.object({
|
|
||||||
newIdentifierVerificationRecordId: z.string(),
|
|
||||||
}),
|
|
||||||
status: [204, 400, 401],
|
|
||||||
}),
|
|
||||||
async (ctx, next) => {
|
|
||||||
const { id: userId, scopes, identityVerified } = ctx.auth;
|
|
||||||
assertThat(
|
|
||||||
identityVerified,
|
|
||||||
new RequestError({ code: 'verification_record.permission_denied', status: 401 })
|
|
||||||
);
|
|
||||||
const { newIdentifierVerificationRecordId } = ctx.guard.body;
|
|
||||||
const { fields } = ctx.accountCenter;
|
|
||||||
assertThat(
|
|
||||||
fields.social === AccountCenterControlValue.Edit,
|
|
||||||
'account_center.filed_not_editable'
|
|
||||||
);
|
|
||||||
|
|
||||||
assertThat(scopes.has(UserScope.Identities), 'auth.unauthorized');
|
|
||||||
|
|
||||||
// Check new identifier
|
|
||||||
const newVerificationRecord = await buildVerificationRecordByIdAndType({
|
|
||||||
type: VerificationType.Social,
|
|
||||||
id: newIdentifierVerificationRecordId,
|
|
||||||
queries,
|
|
||||||
libraries,
|
|
||||||
});
|
|
||||||
assertThat(newVerificationRecord.isVerified, 'verification_record.not_found');
|
|
||||||
|
|
||||||
const {
|
|
||||||
socialIdentity: { target, userInfo },
|
|
||||||
} = await newVerificationRecord.toUserProfile();
|
|
||||||
|
|
||||||
await checkIdentifierCollision({ identity: { target, id: userInfo.id } }, userId);
|
|
||||||
|
|
||||||
const user = await findUserById(userId);
|
|
||||||
|
|
||||||
assertThat(!user.identities[target], 'user.identity_already_in_use');
|
|
||||||
|
|
||||||
const updatedUser = await updateUserById(userId, {
|
|
||||||
identities: {
|
|
||||||
...user.identities,
|
|
||||||
[target]: {
|
|
||||||
userId: userInfo.id,
|
|
||||||
details: userInfo,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.appendDataHookContext('User.Data.Updated', { user: updatedUser });
|
|
||||||
|
|
||||||
ctx.status = 204;
|
|
||||||
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
router.delete(
|
|
||||||
'/profile/identities/:target',
|
|
||||||
koaGuard({
|
|
||||||
params: z.object({ target: z.string() }),
|
|
||||||
status: [204, 400, 401, 404],
|
|
||||||
}),
|
|
||||||
async (ctx, next) => {
|
|
||||||
const { id: userId, scopes, identityVerified } = ctx.auth;
|
|
||||||
assertThat(
|
|
||||||
identityVerified,
|
|
||||||
new RequestError({ code: 'verification_record.permission_denied', status: 401 })
|
|
||||||
);
|
|
||||||
const { target } = ctx.guard.params;
|
|
||||||
const { fields } = ctx.accountCenter;
|
|
||||||
assertThat(
|
|
||||||
fields.social === AccountCenterControlValue.Edit,
|
|
||||||
'account_center.filed_not_editable'
|
|
||||||
);
|
|
||||||
|
|
||||||
assertThat(scopes.has(UserScope.Identities), 'auth.unauthorized');
|
|
||||||
|
|
||||||
const user = await findUserById(userId);
|
|
||||||
|
|
||||||
assertThat(
|
|
||||||
user.identities[target],
|
|
||||||
new RequestError({
|
|
||||||
code: 'user.identity_not_exist',
|
|
||||||
status: 404,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const updatedUser = await deleteUserIdentity(userId, target);
|
|
||||||
|
|
||||||
ctx.appendDataHookContext('User.Data.Updated', { user: updatedUser });
|
|
||||||
|
|
||||||
ctx.status = 204;
|
|
||||||
|
|
||||||
return next();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
/* eslint-enable max-lines */
|
|
||||||
|
|
Loading…
Reference in a new issue