mirror of
https://github.com/logto-io/logto.git
synced 2025-01-13 21:30:30 -05:00
refactor(core): reorg organization routes
This commit is contained in:
parent
33537ef1af
commit
ec6f1d39d8
2 changed files with 136 additions and 121 deletions
|
@ -1,15 +1,12 @@
|
|||
import {
|
||||
OrganizationRoles,
|
||||
type OrganizationWithFeatured,
|
||||
Organizations,
|
||||
featuredUserGuard,
|
||||
userWithOrganizationRolesGuard,
|
||||
OrganizationScopes,
|
||||
} from '@logto/schemas';
|
||||
import { yes } from '@silverhand/essentials';
|
||||
import { z } from 'zod';
|
||||
|
||||
import RequestError from '#src/errors/RequestError/index.js';
|
||||
import koaGuard from '#src/middleware/koa-guard.js';
|
||||
import koaPagination from '#src/middleware/koa-pagination.js';
|
||||
import koaQuotaGuard from '#src/middleware/koa-quota-guard.js';
|
||||
|
@ -19,6 +16,7 @@ import { parseSearchOptions } from '#src/utils/search.js';
|
|||
|
||||
import { type ManagementApiRouter, type RouterInitArgs } from '../types.js';
|
||||
|
||||
import userRoleRelationRoutes from './index.user-role-relations.js';
|
||||
import organizationInvitationRoutes from './invitations.js';
|
||||
import organizationRoleRoutes from './roles.js';
|
||||
import organizationScopeRoutes from './scopes.js';
|
||||
|
@ -135,125 +133,8 @@ export default function organizationRoutes<T extends ManagementApiRouter>(
|
|||
}
|
||||
);
|
||||
|
||||
// Manually add these routes since I don't want to over-engineer the `SchemaRouter`
|
||||
// MARK: Organization - user - organization role relation routes
|
||||
const params = Object.freeze({ id: z.string().min(1), userId: z.string().min(1) } as const);
|
||||
const pathname = '/:id/users/:userId/roles';
|
||||
|
||||
// The pathname of `.use()` will not match the end of the path, for example:
|
||||
// `.use('/foo', ...)` will match both `/foo` and `/foo/bar`.
|
||||
// See https://github.com/koajs/router/blob/02ad6eedf5ced6ec1eab2138380fd67c63e3f1d7/lib/router.js#L330-L333
|
||||
router.use(pathname, koaGuard({ params: z.object(params) }), async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
|
||||
// Ensure membership
|
||||
if (!(await organizations.relations.users.exists(id, userId))) {
|
||||
throw new RequestError({ code: 'organization.require_membership', status: 422 });
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
router.get(
|
||||
pathname,
|
||||
koaPagination(),
|
||||
koaGuard({
|
||||
params: z.object(params),
|
||||
response: OrganizationRoles.guard.array(),
|
||||
status: [200, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
|
||||
const [totalCount, entities] = await organizations.relations.rolesUsers.getEntities(
|
||||
OrganizationRoles,
|
||||
{
|
||||
organizationId: id,
|
||||
userId,
|
||||
},
|
||||
ctx.pagination
|
||||
);
|
||||
|
||||
ctx.pagination.totalCount = totalCount;
|
||||
ctx.body = entities;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.put(
|
||||
pathname,
|
||||
koaGuard({
|
||||
params: z.object(params),
|
||||
body: z.object({ organizationRoleIds: z.string().min(1).array() }),
|
||||
status: [204, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
const { organizationRoleIds } = ctx.guard.body;
|
||||
|
||||
await organizations.relations.rolesUsers.replace(id, userId, organizationRoleIds);
|
||||
|
||||
ctx.status = 204;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
pathname,
|
||||
koaGuard({
|
||||
params: z.object(params),
|
||||
body: z.object({ organizationRoleIds: z.string().min(1).array().nonempty() }),
|
||||
status: [201, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
const { organizationRoleIds } = ctx.guard.body;
|
||||
|
||||
await organizations.relations.rolesUsers.insert(
|
||||
...organizationRoleIds.map<[string, string, string]>((roleId) => [id, roleId, userId])
|
||||
);
|
||||
|
||||
ctx.status = 201;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.delete(
|
||||
`${pathname}/:roleId`,
|
||||
koaGuard({
|
||||
params: z.object({ ...params, roleId: z.string().min(1) }),
|
||||
status: [204, 422, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, roleId, userId } = ctx.guard.params;
|
||||
|
||||
await organizations.relations.rolesUsers.delete({
|
||||
organizationId: id,
|
||||
organizationRoleId: roleId,
|
||||
userId,
|
||||
});
|
||||
|
||||
ctx.status = 204;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:id/users/:userId/scopes',
|
||||
koaGuard({
|
||||
params: z.object(params),
|
||||
response: z.array(OrganizationScopes.guard),
|
||||
status: [200, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
|
||||
const scopes = await organizations.relations.rolesUsers.getUserScopes(id, userId);
|
||||
|
||||
ctx.body = scopes;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
userRoleRelationRoutes(router, organizations);
|
||||
|
||||
// MARK: Mount sub-routes
|
||||
organizationRoleRoutes(...args);
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
import { OrganizationRoles, OrganizationScopes } from '@logto/schemas';
|
||||
import type Router from 'koa-router';
|
||||
import { type IRouterParamContext } from 'koa-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
import RequestError from '#src/errors/RequestError/index.js';
|
||||
import koaGuard from '#src/middleware/koa-guard.js';
|
||||
import koaPagination from '#src/middleware/koa-pagination.js';
|
||||
import type OrganizationQueries from '#src/queries/organization/index.js';
|
||||
|
||||
// Manually add these routes since I don't want to over-engineer the `SchemaRouter`
|
||||
export default function userRoleRelationRoutes(
|
||||
router: Router<unknown, IRouterParamContext>,
|
||||
organizations: OrganizationQueries
|
||||
) {
|
||||
// MARK: Organization - user - organization role relation routes
|
||||
const params = Object.freeze({ id: z.string().min(1), userId: z.string().min(1) } as const);
|
||||
const pathname = '/:id/users/:userId/roles';
|
||||
|
||||
// The pathname of `.use()` will not match the end of the path, for example:
|
||||
// `.use('/foo', ...)` will match both `/foo` and `/foo/bar`.
|
||||
// See https://github.com/koajs/router/blob/02ad6eedf5ced6ec1eab2138380fd67c63e3f1d7/lib/router.js#L330-L333
|
||||
router.use(pathname, koaGuard({ params: z.object(params) }), async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
|
||||
// Ensure membership
|
||||
if (!(await organizations.relations.users.exists(id, userId))) {
|
||||
throw new RequestError({ code: 'organization.require_membership', status: 422 });
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
router.get(
|
||||
pathname,
|
||||
koaPagination(),
|
||||
koaGuard({
|
||||
params: z.object(params),
|
||||
response: OrganizationRoles.guard.array(),
|
||||
status: [200, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
|
||||
const [totalCount, entities] = await organizations.relations.rolesUsers.getEntities(
|
||||
OrganizationRoles,
|
||||
{
|
||||
organizationId: id,
|
||||
userId,
|
||||
},
|
||||
ctx.pagination
|
||||
);
|
||||
|
||||
ctx.pagination.totalCount = totalCount;
|
||||
ctx.body = entities;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.put(
|
||||
pathname,
|
||||
koaGuard({
|
||||
params: z.object(params),
|
||||
body: z.object({ organizationRoleIds: z.string().min(1).array() }),
|
||||
status: [204, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
const { organizationRoleIds } = ctx.guard.body;
|
||||
|
||||
await organizations.relations.rolesUsers.replace(id, userId, organizationRoleIds);
|
||||
|
||||
ctx.status = 204;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
pathname,
|
||||
koaGuard({
|
||||
params: z.object(params),
|
||||
body: z.object({ organizationRoleIds: z.string().min(1).array().nonempty() }),
|
||||
status: [201, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
const { organizationRoleIds } = ctx.guard.body;
|
||||
|
||||
await organizations.relations.rolesUsers.insert(
|
||||
...organizationRoleIds.map<[string, string, string]>((roleId) => [id, roleId, userId])
|
||||
);
|
||||
|
||||
ctx.status = 201;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.delete(
|
||||
`${pathname}/:roleId`,
|
||||
koaGuard({
|
||||
params: z.object({ ...params, roleId: z.string().min(1) }),
|
||||
status: [204, 422, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, roleId, userId } = ctx.guard.params;
|
||||
|
||||
await organizations.relations.rolesUsers.delete({
|
||||
organizationId: id,
|
||||
organizationRoleId: roleId,
|
||||
userId,
|
||||
});
|
||||
|
||||
ctx.status = 204;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:id/users/:userId/scopes',
|
||||
koaGuard({
|
||||
params: z.object(params),
|
||||
response: z.array(OrganizationScopes.guard),
|
||||
status: [200, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { id, userId } = ctx.guard.params;
|
||||
|
||||
const scopes = await organizations.relations.rolesUsers.getUserScopes(id, userId);
|
||||
|
||||
ctx.body = scopes;
|
||||
return next();
|
||||
}
|
||||
);
|
||||
}
|
Loading…
Add table
Reference in a new issue