mirror of
https://github.com/logto-io/logto.git
synced 2025-02-17 22:04:19 -05:00
refactor(core,console)!: remove /me
apis (#1781)
* refactor(core,console)!: remove `/me` apis * fix(phrases): add missing fr phrase
This commit is contained in:
parent
a6bb2f7ec2
commit
2c6171c2f9
12 changed files with 70 additions and 145 deletions
|
@ -30,7 +30,7 @@ const useUserPreferences = () => {
|
|||
const { isAuthenticated, error: authError } = useLogto();
|
||||
const shouldFetch = isAuthenticated && !authError;
|
||||
const { data, mutate, error } = useSWR<unknown, RequestError>(
|
||||
shouldFetch && '/api/me/custom-data'
|
||||
shouldFetch && '/api/users/me/custom-data'
|
||||
);
|
||||
const api = useApi();
|
||||
|
||||
|
@ -50,7 +50,7 @@ const useUserPreferences = () => {
|
|||
|
||||
const update = async (data: Partial<UserPreferences>) => {
|
||||
const updated = await api
|
||||
.patch('/api/me/custom-data', {
|
||||
.patch('/api/users/me/custom-data', {
|
||||
json: {
|
||||
customData: {
|
||||
[key]: {
|
||||
|
|
|
@ -30,7 +30,7 @@ const ChangePassword = () => {
|
|||
|
||||
const onSubmit = async () => {
|
||||
setIsLoading(true);
|
||||
await api.patch(`/api/me/password`, { json: { password } }).json();
|
||||
await api.patch(`/api/users/me/password`, { json: { password } }).json();
|
||||
setIsLoading(false);
|
||||
setIsOpen(false);
|
||||
toast.success(t('settings.password_changed'));
|
||||
|
|
|
@ -23,6 +23,8 @@ import assertThat from '@/utils/assert-that';
|
|||
|
||||
import { AuthedRouter } from './types';
|
||||
|
||||
const getComputedUserId = (userId: string, auth: string) => (userId === 'me' ? auth : userId);
|
||||
|
||||
export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
||||
router.get(
|
||||
'/users',
|
||||
|
@ -59,7 +61,7 @@ export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
|||
params: { userId },
|
||||
} = ctx.guard;
|
||||
|
||||
const user = await findUserById(userId);
|
||||
const user = await findUserById(getComputedUserId(userId, ctx.auth));
|
||||
|
||||
ctx.body = pick(user, ...userInfoSelectFields);
|
||||
|
||||
|
@ -67,6 +69,50 @@ export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
|||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/users/:userId/custom-data',
|
||||
koaGuard({
|
||||
params: object({ userId: string() }),
|
||||
response: arbitraryObjectGuard,
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { userId },
|
||||
} = ctx.guard;
|
||||
|
||||
const { customData } = await findUserById(getComputedUserId(userId, ctx.auth));
|
||||
ctx.body = customData;
|
||||
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.patch(
|
||||
'/users/:userId/custom-data',
|
||||
koaGuard({
|
||||
params: object({ userId: string() }),
|
||||
body: object({ customData: arbitraryObjectGuard }),
|
||||
response: arbitraryObjectGuard,
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { userId: userIdParameter },
|
||||
body: { customData },
|
||||
} = ctx.guard;
|
||||
const userId = getComputedUserId(userIdParameter, ctx.auth);
|
||||
|
||||
await findUserById(userId);
|
||||
|
||||
const user = await updateUserById(userId, {
|
||||
customData,
|
||||
});
|
||||
|
||||
ctx.body = user.customData;
|
||||
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/users',
|
||||
koaGuard({
|
||||
|
@ -117,9 +163,10 @@ export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
|||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { userId },
|
||||
params: { userId: userIdParameter },
|
||||
body,
|
||||
} = ctx.guard;
|
||||
const userId = getComputedUserId(userIdParameter, ctx.auth);
|
||||
|
||||
await findUserById(userId);
|
||||
|
||||
|
@ -164,9 +211,10 @@ export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
|||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { userId },
|
||||
params: { userId: userIdParameter },
|
||||
body: { password },
|
||||
} = ctx.guard;
|
||||
const userId = getComputedUserId(userIdParameter, ctx.auth);
|
||||
|
||||
await findUserById(userId);
|
||||
|
||||
|
@ -193,6 +241,10 @@ export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
|||
params: { userId },
|
||||
} = ctx.guard;
|
||||
|
||||
if (userId === ctx.auth) {
|
||||
throw new RequestError('user.cannot_delete_self');
|
||||
}
|
||||
|
||||
await findUserById(userId);
|
||||
|
||||
await deleteUserById(userId);
|
||||
|
@ -208,8 +260,9 @@ export default function adminUserRoutes<T extends AuthedRouter>(router: T) {
|
|||
koaGuard({ params: object({ userId: string(), target: string() }) }),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
params: { userId, target },
|
||||
params: { userId: userIdParameter, target },
|
||||
} = ctx.guard;
|
||||
const userId = getComputedUserId(userIdParameter, ctx.auth);
|
||||
|
||||
const { identities } = await findUserById(userId);
|
||||
|
||||
|
|
|
@ -6,10 +6,13 @@ import { Provider } from 'oidc-provider';
|
|||
|
||||
import koaAuth from '@/middleware/koa-auth';
|
||||
import koaLogSession from '@/middleware/koa-log-session';
|
||||
import adminUserRoutes from '@/routes/admin-user';
|
||||
import applicationRoutes from '@/routes/application';
|
||||
import connectorRoutes from '@/routes/connector';
|
||||
import dashboardRoutes from '@/routes/dashboard';
|
||||
import logRoutes from '@/routes/log';
|
||||
import resourceRoutes from '@/routes/resource';
|
||||
import roleRoutes from '@/routes/role';
|
||||
import sessionPasswordlessRoutes from '@/routes/session/passwordless';
|
||||
import sessionRoutes from '@/routes/session/session';
|
||||
import sessionSocialRoutes from '@/routes/session/social';
|
||||
|
@ -19,10 +22,6 @@ import statusRoutes from '@/routes/status';
|
|||
import swaggerRoutes from '@/routes/swagger';
|
||||
import wellKnownRoutes from '@/routes/well-known';
|
||||
|
||||
import adminUserRoutes from './admin-user';
|
||||
import logRoutes from './log';
|
||||
import meRoutes from './me';
|
||||
import roleRoutes from './role';
|
||||
import { AnonymousRouter, AuthedRouter } from './types';
|
||||
|
||||
const createRouters = (provider: Provider) => {
|
||||
|
@ -44,17 +43,13 @@ const createRouters = (provider: Provider) => {
|
|||
roleRoutes(managementRouter);
|
||||
dashboardRoutes(managementRouter);
|
||||
|
||||
const meRouter: AuthedRouter = new Router();
|
||||
meRouter.use(koaAuth());
|
||||
meRoutes(meRouter);
|
||||
|
||||
const anonymousRouter: AnonymousRouter = new Router();
|
||||
wellKnownRoutes(anonymousRouter, provider);
|
||||
statusRoutes(anonymousRouter);
|
||||
// The swagger.json should contain all API routers.
|
||||
swaggerRoutes(anonymousRouter, [sessionRouter, managementRouter, meRouter, anonymousRouter]);
|
||||
swaggerRoutes(anonymousRouter, [sessionRouter, managementRouter, anonymousRouter]);
|
||||
|
||||
return [sessionRouter, managementRouter, meRouter, anonymousRouter];
|
||||
return [sessionRouter, managementRouter, anonymousRouter];
|
||||
};
|
||||
|
||||
export default function initRouter(app: Koa, provider: Provider) {
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
import { arbitraryObjectGuard, userInfoSelectFields } from '@logto/schemas';
|
||||
import { passwordRegEx } from '@logto/shared';
|
||||
import pick from 'lodash.pick';
|
||||
import { object, string } from 'zod';
|
||||
|
||||
import { encryptUserPassword } from '@/lib/user';
|
||||
import koaGuard from '@/middleware/koa-guard';
|
||||
import { findUserById, updateUserById } from '@/queries/user';
|
||||
|
||||
import { AuthedRouter } from './types';
|
||||
|
||||
export default function meRoutes<T extends AuthedRouter>(router: T) {
|
||||
router.get('/me', async (ctx, next) => {
|
||||
const user = await findUserById(ctx.auth);
|
||||
|
||||
ctx.body = pick(user, ...userInfoSelectFields);
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
router.get('/me/custom-data', async (ctx, next) => {
|
||||
const { customData } = await findUserById(ctx.auth);
|
||||
|
||||
ctx.body = customData;
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
router.patch(
|
||||
'/me/custom-data',
|
||||
koaGuard({ body: object({ customData: arbitraryObjectGuard }) }),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
body: { customData },
|
||||
} = ctx.guard;
|
||||
|
||||
await findUserById(ctx.auth);
|
||||
|
||||
const user = await updateUserById(ctx.auth, {
|
||||
customData,
|
||||
});
|
||||
|
||||
ctx.body = user.customData;
|
||||
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.patch(
|
||||
'/me/password',
|
||||
koaGuard({ body: object({ password: string().regex(passwordRegEx) }) }),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
body: { password },
|
||||
} = ctx.guard;
|
||||
|
||||
const { passwordEncrypted, passwordEncryptionMethod } = await encryptUserPassword(password);
|
||||
|
||||
await updateUserById(ctx.auth, {
|
||||
passwordEncrypted,
|
||||
passwordEncryptionMethod,
|
||||
});
|
||||
|
||||
ctx.status = 204;
|
||||
|
||||
return next();
|
||||
}
|
||||
);
|
||||
}
|
|
@ -11,11 +11,6 @@ paths:
|
|||
responses:
|
||||
'204':
|
||||
description: No Content
|
||||
/api/me/password:
|
||||
patch:
|
||||
responses:
|
||||
'204':
|
||||
description: No Content
|
||||
/api/resources/:id:
|
||||
delete:
|
||||
responses:
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
import { userInfoSelectFields } from '@logto/schemas';
|
||||
import { assert } from '@silverhand/essentials';
|
||||
|
||||
import {
|
||||
getCurrentUserInfo,
|
||||
getCurrentUserCustomData,
|
||||
updateCurrentUserCustomData,
|
||||
changeCurrentUserPassword,
|
||||
} from '@/api';
|
||||
import { createUserByAdmin, signIn } from '@/helpers';
|
||||
import { generatePassword } from '@/utils';
|
||||
|
||||
describe('api `/me`', () => {
|
||||
it('should get user info successfully', async () => {
|
||||
const user = await createUserByAdmin();
|
||||
|
||||
const userInfo = await getCurrentUserInfo(user.id);
|
||||
|
||||
expect(userInfo.id).toBe(user.id);
|
||||
|
||||
for (const field of userInfoSelectFields) {
|
||||
expect(userInfo).toHaveProperty(field);
|
||||
}
|
||||
});
|
||||
|
||||
it('should get user custom data successfully', async () => {
|
||||
const user = await createUserByAdmin();
|
||||
const customData = await getCurrentUserCustomData(user.id);
|
||||
expect(customData).toEqual({});
|
||||
});
|
||||
|
||||
it('should update user custom data successfully', async () => {
|
||||
const user = await createUserByAdmin();
|
||||
|
||||
const foo = 'bar';
|
||||
|
||||
await updateCurrentUserCustomData(user.id, { foo });
|
||||
|
||||
const customData = await getCurrentUserCustomData(user.id);
|
||||
|
||||
expect(customData).toEqual({ foo });
|
||||
});
|
||||
|
||||
it('should change user password successfully', async () => {
|
||||
const user = await createUserByAdmin();
|
||||
const password = generatePassword();
|
||||
|
||||
await changeCurrentUserPassword(user.id, password);
|
||||
|
||||
assert(user.username, new Error('empty username'));
|
||||
|
||||
void expect(signIn(user.username, password)).resolves.not.toThrow();
|
||||
});
|
||||
});
|
|
@ -39,6 +39,7 @@ const errors = {
|
|||
identity_not_exists: 'The social account has not been registered yet.',
|
||||
identity_exists: 'The social account has been registered.',
|
||||
invalid_role_names: 'role names ({{roleNames}}) are not valid',
|
||||
cannot_delete_self: 'You cannot delete yourself.',
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: 'The encryption method {{name}} is not supported.',
|
||||
|
|
|
@ -40,6 +40,7 @@ const errors = {
|
|||
identity_not_exists: "Le compte social n'a pas encore été enregistré.",
|
||||
identity_exists: 'Le compte social a été enregistré.',
|
||||
invalid_role_names: 'les noms de rôles ({{roleNames}}) ne sont pas valides',
|
||||
cannot_delete_self: 'You cannot delete yourself.',
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: "La méthode de cryptage {{name}} n'est pas prise en charge.",
|
||||
|
|
|
@ -38,6 +38,7 @@ const errors = {
|
|||
identity_not_exists: '소셜 계정이 아직 등록되지 않았어요.',
|
||||
identity_exists: '소셜 계정이 이미 등록되있어요.',
|
||||
invalid_role_names: '직책 명({{roleNames}})이 유효하지 않아요.',
|
||||
cannot_delete_self: 'You cannot delete yourself.',
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: '{{name}} 암호화 방법을 지원하지 않아요.',
|
||||
|
|
|
@ -39,6 +39,7 @@ const errors = {
|
|||
identity_not_exists: 'Sosyal platform hesabı henüz kaydedilmedi.',
|
||||
identity_exists: 'Sosyal platform hesabı kaydedildi.',
|
||||
invalid_role_names: '({{roleNames}}) rol adları geçerli değil.',
|
||||
cannot_delete_self: 'You cannot delete yourself.',
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: '{{name}} şifreleme metodu desteklenmiyor.',
|
||||
|
|
|
@ -39,6 +39,7 @@ const errors = {
|
|||
identity_not_exists: '该社交帐号尚未注册',
|
||||
identity_exists: '该社交帐号已被注册',
|
||||
invalid_role_names: '角色名称({{roleNames}})无效',
|
||||
cannot_delete_self: '你无法删除自己',
|
||||
},
|
||||
password: {
|
||||
unsupported_encryption_method: '不支持的加密方法 {{name}}',
|
||||
|
|
Loading…
Add table
Reference in a new issue