Apply full refactor updates plus pipeline/email UX confirmations
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
This commit is contained in:
@@ -1,159 +1,159 @@
|
||||
import { initTRPC, TRPCError } from '@trpc/server'
|
||||
import superjson from 'superjson'
|
||||
import { ZodError } from 'zod'
|
||||
import type { Context } from './context'
|
||||
import type { UserRole } from '@prisma/client'
|
||||
|
||||
/**
|
||||
* Initialize tRPC with context type and configuration
|
||||
*/
|
||||
const t = initTRPC.context<Context>().create({
|
||||
transformer: superjson,
|
||||
errorFormatter({ shape, error }) {
|
||||
return {
|
||||
...shape,
|
||||
data: {
|
||||
...shape.data,
|
||||
zodError:
|
||||
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Export reusable router and procedure helpers
|
||||
*/
|
||||
export const router = t.router
|
||||
export const publicProcedure = t.procedure
|
||||
export const middleware = t.middleware
|
||||
export const createCallerFactory = t.createCallerFactory
|
||||
|
||||
// =============================================================================
|
||||
// Middleware
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Middleware to require authenticated user
|
||||
*/
|
||||
const isAuthenticated = middleware(async ({ ctx, next }) => {
|
||||
if (!ctx.session?.user) {
|
||||
throw new TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'You must be logged in to perform this action',
|
||||
})
|
||||
}
|
||||
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
user: ctx.session.user,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Middleware to require specific role(s)
|
||||
*/
|
||||
const hasRole = (...roles: UserRole[]) =>
|
||||
middleware(async ({ ctx, next }) => {
|
||||
if (!ctx.session?.user) {
|
||||
throw new TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'You must be logged in to perform this action',
|
||||
})
|
||||
}
|
||||
|
||||
if (!roles.includes(ctx.session.user.role)) {
|
||||
throw new TRPCError({
|
||||
code: 'FORBIDDEN',
|
||||
message: 'You do not have permission to perform this action',
|
||||
})
|
||||
}
|
||||
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
user: ctx.session.user,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Middleware for audit logging
|
||||
*/
|
||||
const withAuditLog = middleware(async ({ ctx, next, path }) => {
|
||||
const result = await next()
|
||||
|
||||
// Log successful mutations
|
||||
if (result.ok && path.includes('.')) {
|
||||
const [, action] = path.split('.')
|
||||
const mutationActions = ['create', 'update', 'delete', 'import', 'submit', 'grant', 'revoke']
|
||||
|
||||
if (mutationActions.some((a) => action?.toLowerCase().includes(a))) {
|
||||
// Audit logging would happen here
|
||||
// We'll implement this in the audit service
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
// =============================================================================
|
||||
// Procedure Types
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Protected procedure - requires authenticated user
|
||||
*/
|
||||
export const protectedProcedure = t.procedure.use(isAuthenticated)
|
||||
|
||||
/**
|
||||
* Admin procedure - requires SUPER_ADMIN or PROGRAM_ADMIN role
|
||||
*/
|
||||
export const adminProcedure = t.procedure.use(
|
||||
hasRole('SUPER_ADMIN', 'PROGRAM_ADMIN')
|
||||
)
|
||||
|
||||
/**
|
||||
* Super admin procedure - requires SUPER_ADMIN role
|
||||
*/
|
||||
export const superAdminProcedure = t.procedure.use(hasRole('SUPER_ADMIN'))
|
||||
|
||||
/**
|
||||
* Jury procedure - requires JURY_MEMBER role
|
||||
*/
|
||||
export const juryProcedure = t.procedure.use(hasRole('JURY_MEMBER'))
|
||||
|
||||
/**
|
||||
* Mentor procedure - requires MENTOR role (or admin)
|
||||
*/
|
||||
export const mentorProcedure = t.procedure.use(
|
||||
hasRole('SUPER_ADMIN', 'PROGRAM_ADMIN', 'MENTOR')
|
||||
)
|
||||
|
||||
/**
|
||||
* Observer procedure - requires OBSERVER role (read-only access)
|
||||
*/
|
||||
export const observerProcedure = t.procedure.use(
|
||||
hasRole('SUPER_ADMIN', 'PROGRAM_ADMIN', 'OBSERVER')
|
||||
)
|
||||
|
||||
/**
|
||||
* Award master procedure - requires AWARD_MASTER role (or admin)
|
||||
*/
|
||||
export const awardMasterProcedure = t.procedure.use(
|
||||
hasRole('SUPER_ADMIN', 'PROGRAM_ADMIN', 'AWARD_MASTER')
|
||||
)
|
||||
|
||||
/**
|
||||
* Audience procedure - requires any authenticated user
|
||||
*/
|
||||
export const audienceProcedure = t.procedure.use(isAuthenticated)
|
||||
|
||||
/**
|
||||
* Protected procedure with audit logging
|
||||
*/
|
||||
export const auditedProcedure = t.procedure
|
||||
.use(isAuthenticated)
|
||||
.use(withAuditLog)
|
||||
import { initTRPC, TRPCError } from '@trpc/server'
|
||||
import superjson from 'superjson'
|
||||
import { ZodError } from 'zod'
|
||||
import type { Context } from './context'
|
||||
import type { UserRole } from '@prisma/client'
|
||||
|
||||
/**
|
||||
* Initialize tRPC with context type and configuration
|
||||
*/
|
||||
const t = initTRPC.context<Context>().create({
|
||||
transformer: superjson,
|
||||
errorFormatter({ shape, error }) {
|
||||
return {
|
||||
...shape,
|
||||
data: {
|
||||
...shape.data,
|
||||
zodError:
|
||||
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Export reusable router and procedure helpers
|
||||
*/
|
||||
export const router = t.router
|
||||
export const publicProcedure = t.procedure
|
||||
export const middleware = t.middleware
|
||||
export const createCallerFactory = t.createCallerFactory
|
||||
|
||||
// =============================================================================
|
||||
// Middleware
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Middleware to require authenticated user
|
||||
*/
|
||||
const isAuthenticated = middleware(async ({ ctx, next }) => {
|
||||
if (!ctx.session?.user) {
|
||||
throw new TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'You must be logged in to perform this action',
|
||||
})
|
||||
}
|
||||
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
user: ctx.session.user,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Middleware to require specific role(s)
|
||||
*/
|
||||
const hasRole = (...roles: UserRole[]) =>
|
||||
middleware(async ({ ctx, next }) => {
|
||||
if (!ctx.session?.user) {
|
||||
throw new TRPCError({
|
||||
code: 'UNAUTHORIZED',
|
||||
message: 'You must be logged in to perform this action',
|
||||
})
|
||||
}
|
||||
|
||||
if (!roles.includes(ctx.session.user.role)) {
|
||||
throw new TRPCError({
|
||||
code: 'FORBIDDEN',
|
||||
message: 'You do not have permission to perform this action',
|
||||
})
|
||||
}
|
||||
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
user: ctx.session.user,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
* Middleware for audit logging
|
||||
*/
|
||||
const withAuditLog = middleware(async ({ ctx, next, path }) => {
|
||||
const result = await next()
|
||||
|
||||
// Log successful mutations
|
||||
if (result.ok && path.includes('.')) {
|
||||
const [, action] = path.split('.')
|
||||
const mutationActions = ['create', 'update', 'delete', 'import', 'submit', 'grant', 'revoke']
|
||||
|
||||
if (mutationActions.some((a) => action?.toLowerCase().includes(a))) {
|
||||
// Audit logging would happen here
|
||||
// We'll implement this in the audit service
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
// =============================================================================
|
||||
// Procedure Types
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Protected procedure - requires authenticated user
|
||||
*/
|
||||
export const protectedProcedure = t.procedure.use(isAuthenticated)
|
||||
|
||||
/**
|
||||
* Admin procedure - requires SUPER_ADMIN or PROGRAM_ADMIN role
|
||||
*/
|
||||
export const adminProcedure = t.procedure.use(
|
||||
hasRole('SUPER_ADMIN', 'PROGRAM_ADMIN')
|
||||
)
|
||||
|
||||
/**
|
||||
* Super admin procedure - requires SUPER_ADMIN role
|
||||
*/
|
||||
export const superAdminProcedure = t.procedure.use(hasRole('SUPER_ADMIN'))
|
||||
|
||||
/**
|
||||
* Jury procedure - requires JURY_MEMBER role
|
||||
*/
|
||||
export const juryProcedure = t.procedure.use(hasRole('JURY_MEMBER'))
|
||||
|
||||
/**
|
||||
* Mentor procedure - requires MENTOR role (or admin)
|
||||
*/
|
||||
export const mentorProcedure = t.procedure.use(
|
||||
hasRole('SUPER_ADMIN', 'PROGRAM_ADMIN', 'MENTOR')
|
||||
)
|
||||
|
||||
/**
|
||||
* Observer procedure - requires OBSERVER role (read-only access)
|
||||
*/
|
||||
export const observerProcedure = t.procedure.use(
|
||||
hasRole('SUPER_ADMIN', 'PROGRAM_ADMIN', 'OBSERVER')
|
||||
)
|
||||
|
||||
/**
|
||||
* Award master procedure - requires AWARD_MASTER role (or admin)
|
||||
*/
|
||||
export const awardMasterProcedure = t.procedure.use(
|
||||
hasRole('SUPER_ADMIN', 'PROGRAM_ADMIN', 'AWARD_MASTER')
|
||||
)
|
||||
|
||||
/**
|
||||
* Audience procedure - requires any authenticated user
|
||||
*/
|
||||
export const audienceProcedure = t.procedure.use(isAuthenticated)
|
||||
|
||||
/**
|
||||
* Protected procedure with audit logging
|
||||
*/
|
||||
export const auditedProcedure = t.procedure
|
||||
.use(isAuthenticated)
|
||||
.use(withAuditLog)
|
||||
|
||||
Reference in New Issue
Block a user