feat: implement automation key management with middleware and routes for organization API keys, invite improvements

This commit is contained in:
Rotimi Best
2026-03-13 18:40:07 +01:00
parent 77a1188573
commit 492f8dc5ed
71 changed files with 3655 additions and 456 deletions
@@ -0,0 +1,76 @@
import {
ZCreateOrganizationApiKey,
ZListOrganizationApiKeysQuery,
ZOrganizationApiKeyParam
} from '@cio/utils/validation/organization';
import {
createOrganizationApiKeyService,
listOrganizationApiKeysService,
revokeOrganizationApiKeyService,
rotateOrganizationApiKeyService
} from '@api/services/organization/automation-key';
import { Hono } from '@api/utils/hono';
import { authMiddleware } from '@api/middlewares/auth';
import { handleError } from '@api/utils/errors';
import { orgAdminMiddleware } from '@api/middlewares/org-admin';
import { zValidator } from '@hono/zod-validator';
export const automationRouter = new Hono()
.get('/keys', authMiddleware, orgAdminMiddleware, zValidator('query', ZListOrganizationApiKeysQuery), async (c) => {
try {
const orgId = c.req.header('cio-org-id')!;
const query = c.req.valid('query');
const keys = await listOrganizationApiKeysService(orgId, query);
return c.json({ success: true, data: keys }, 200);
} catch (error) {
return handleError(c, error, 'Failed to list organization automation keys');
}
})
.post('/keys', authMiddleware, orgAdminMiddleware, zValidator('json', ZCreateOrganizationApiKey), async (c) => {
try {
const orgId = c.req.header('cio-org-id')!;
const user = c.get('user')!;
const payload = c.req.valid('json');
const key = await createOrganizationApiKeyService(orgId, user.id, payload);
return c.json({ success: true, data: key }, 201);
} catch (error) {
return handleError(c, error, 'Failed to create organization automation key');
}
})
.delete(
'/keys/:keyId',
authMiddleware,
orgAdminMiddleware,
zValidator('param', ZOrganizationApiKeyParam),
async (c) => {
try {
const orgId = c.req.header('cio-org-id')!;
const { keyId } = c.req.valid('param');
const key = await revokeOrganizationApiKeyService(orgId, keyId);
return c.json({ success: true, data: key }, 200);
} catch (error) {
return handleError(c, error, 'Failed to revoke organization automation key');
}
}
)
.post(
'/keys/:keyId/rotate',
authMiddleware,
orgAdminMiddleware,
zValidator('param', ZOrganizationApiKeyParam),
async (c) => {
try {
const orgId = c.req.header('cio-org-id')!;
const { keyId } = c.req.valid('param');
const key = await rotateOrganizationApiKeyService(orgId, keyId);
return c.json({ success: true, data: key }, 200);
} catch (error) {
return handleError(c, error, 'Failed to rotate organization automation key');
}
}
);
@@ -0,0 +1,98 @@
import {
ZCourseImportDraftCreate,
ZCourseImportDraftGetParam,
ZCourseImportDraftPublish,
ZCourseImportDraftUpdate
} from '@cio/utils/validation/course-import';
import {
createCourseImportDraftService,
getCourseImportDraftService,
publishCourseImportDraftService,
updateCourseImportDraftService
} from '@api/services/course-import/course-import';
import { Hono } from '@api/utils/hono';
import { authOrAutomationKeyMiddleware } from '@api/middlewares/auth-or-automation-key';
import { handleError } from '@api/utils/errors';
import { orgAdminOrAutomationKeyMiddleware } from '@api/middlewares/org-admin-or-automation-key';
import { zValidator } from '@hono/zod-validator';
export const courseImportRouter = new Hono()
.post(
'/drafts',
authOrAutomationKeyMiddleware,
orgAdminOrAutomationKeyMiddleware(['course_import:draft:create']),
zValidator('json', ZCourseImportDraftCreate),
async (c) => {
try {
const orgId = c.get('orgId')!;
const actorId = c.get('actorId')!;
const payload = c.req.valid('json');
const draft = await createCourseImportDraftService(orgId, actorId, payload);
return c.json({ success: true, data: draft }, 201);
} catch (error) {
return handleError(c, error, 'Failed to create course import draft');
}
}
)
.get(
'/drafts/:draftId',
authOrAutomationKeyMiddleware,
orgAdminOrAutomationKeyMiddleware(['course_import:draft:read']),
zValidator('param', ZCourseImportDraftGetParam),
async (c) => {
try {
const orgId = c.get('orgId')!;
const { draftId } = c.req.valid('param');
const draft = await getCourseImportDraftService(orgId, draftId);
return c.json({ success: true, data: draft }, 200);
} catch (error) {
return handleError(c, error, 'Failed to fetch course import draft');
}
}
)
.put(
'/drafts/:draftId',
authOrAutomationKeyMiddleware,
orgAdminOrAutomationKeyMiddleware(['course_import:draft:update']),
zValidator('param', ZCourseImportDraftGetParam),
zValidator('json', ZCourseImportDraftUpdate),
async (c) => {
try {
const orgId = c.get('orgId')!;
const { draftId } = c.req.valid('param');
const payload = c.req.valid('json');
const draft = await updateCourseImportDraftService(orgId, draftId, payload);
return c.json({ success: true, data: draft }, 200);
} catch (error) {
return handleError(c, error, 'Failed to update course import draft');
}
}
)
.post(
'/drafts/:draftId/publish',
authOrAutomationKeyMiddleware,
orgAdminOrAutomationKeyMiddleware(['course_import:draft:publish']),
zValidator('param', ZCourseImportDraftGetParam),
zValidator('json', ZCourseImportDraftPublish),
async (c) => {
try {
const orgId = c.get('orgId')!;
const actorId = c.get('actorId')!;
const { draftId } = c.req.valid('param');
const payload = c.req.valid('json');
const result = await publishCourseImportDraftService(orgId, actorId, draftId, payload);
return c.json({ success: true, data: result }, 201);
} catch (error) {
return handleError(c, error, 'Failed to publish course import draft');
}
}
);
@@ -2,3 +2,5 @@ export * from './organization';
export * from './assets';
export * from './tags';
export * from './token-auth';
export * from './course-import';
export * from './automation';
@@ -34,9 +34,11 @@ import {
import { Hono } from '@api/utils/hono';
import { TOrganization } from '@db/types';
import { automationRouter } from '@api/routes/organization/automation';
import { assetsRouter } from '@api/routes/organization/assets';
import { authMiddleware } from '@api/middlewares/auth';
import { authOrApiKeyMiddleware } from '@api/middlewares/auth-or-api-key';
import { courseImportRouter } from '@api/routes/organization/course-import';
import { getLMSExercisesService } from '@api/services/exercise';
import { handleError } from '@api/utils/errors';
import { inviteTeamMembers } from '@api/services/organization/invite';
@@ -505,6 +507,8 @@ export const organizationRouter = new Hono()
return handleError(c, error, 'Failed to fetch LMS exercises');
}
})
.route('/automation', automationRouter)
.route('/course-import', courseImportRouter)
.route('/tags', tagsRouter)
.route('/assets', assetsRouter)
.route('/:orgId/quiz', quizRouter);