0
Fork 0
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:
wangsijie 2024-11-15 14:04:26 +08:00 committed by GitHub
parent 859495b13b
commit f8d21e49fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 127 additions and 105 deletions

View 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();
}
);
}

View file

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