aaspai-authx 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/express/index.ts","../src/express/auth.routes.ts","../src/config/loadConfig.ts","../src/config/index.ts","../src/core/utils.ts","../src/core/roles.config.ts","../src/core/session.ts","../src/models/rolePermission.model.ts","../src/models/user.model.ts","../src/utils/extract.ts","../src/utils/jwt.ts","../src/middlewares/auth.middleware.ts","../src/middlewares/validators.ts","../src/models/invite.model.ts","../src/services/auth-admin.service.ts","../src/models/client.model.ts","../src/services/email.service.ts","../src/templates/email.templates.ts","../src/express/dashboards.routes.ts","../src/express/email.routes.ts","../src/express/projects.routes.ts","../src/services/projects.service.ts","../src/models/moduleConnection.model.ts","../src/models/project.model.ts","../src/express/admin/admin.routes.ts","../src/middlewares/requireRole.ts","../src/models/permissions.model.ts","../src/nest/index.ts","../src/middlewares/permission.middleware.ts","../src/middlewares/requirePermission.ts","../src/services/upload.service.ts","../src/nest/authx.guard.ts","../src/nest/decorators/permissions.decorator.ts","../src/nest/decorators/roles.decorator.ts","../src/nest/decorators/session.decorator.ts","../src/passport/authx.strategy.ts"],"sourcesContent":["export { createAuthRouter } from './auth.routes';\r\nexport { createDashboardRouter } from './dashboards.routes';\r\nexport { createEmailRouter } from './email.routes';\r\nexport { createProjectsRouter } from './projects.routes';\r\nexport { createAdminRouter } from './admin/admin.routes';\r\n","import type { AuthCookieConfig, AuthRouterOptions } from 'aaspai-types';\r\nimport bcrypt from 'bcryptjs';\r\nimport { randomUUID } from 'crypto';\r\nimport express, {\r\n CookieOptions,\r\n Router,\r\n type Router as ExpressRouter,\r\n} from 'express';\r\nimport jwt from 'jsonwebtoken';\r\nimport { configureAuthX } from '../config/index.js';\r\nimport {\r\n baseProjectCookieOptionsFrom,\r\n buildClearCookieOptions,\r\n} from '../core/utils.js';\r\nimport { requireAuth } from '../middlewares/auth.middleware.js';\r\nimport {\r\n isPasswordStrong,\r\n validateLogin,\r\n validateResendEmail,\r\n validateResetPassword,\r\n validateSendInvite,\r\n validateSignup,\r\n} from '../middlewares/validators.js';\r\nimport { Invite } from '../models/invite.model.js';\r\nimport { OrgUser } from '../models/user.model.js';\r\nimport { AuthAdminService } from '../services/auth-admin.service.js';\r\nimport { EmailService } from '../services/email.service.js';\r\nimport {\r\n buildResetPasswordEmailTemplate,\r\n buildVerificationEmailTemplate,\r\n} from '../templates/email.templates.js';\r\n\r\nexport function createAuthRouter(\r\n options: AuthRouterOptions = {},\r\n): ExpressRouter {\r\n const googleClientId = process.env.GOOGLE_CLIENT_ID;\r\n const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;\r\n const googleRedirectUri = process.env.GOOGLE_REDIRECT_URI;\r\n const googleDefaultRedirect =\r\n process.env.GOOGLE_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || '/';\r\n\r\n const isGoogleEnabled =\r\n !!googleClientId && !!googleClientSecret && !!googleRedirectUri;\r\n\r\n if (options.config) {\r\n configureAuthX(options.config);\r\n }\r\n\r\n const r = Router();\r\n\r\n const email = new EmailService();\r\n const authAdmin = new AuthAdminService();\r\n\r\n const isProdEnv = process.env.NODE_ENV === 'production';\r\n\r\n const cookieConfig: AuthCookieConfig = {\r\n sameSite: options.cookie?.sameSite ?? (isProdEnv ? 'none' : 'lax'), // default if not provided\r\n secure: options.cookie?.secure ?? isProdEnv, // default: secure in prod\r\n domain: options.cookie?.domain ?? undefined,\r\n path: options.cookie?.path ?? '/',\r\n maxAgeMs: options.cookie?.maxAgeMs ?? 24 * 60 * 60 * 1000,\r\n };\r\n\r\n r.use(express.json());\r\n r.use(express.urlencoded({ extended: true }));\r\n\r\n r.get('/healthz', (_req, res) =>\r\n res.json({ status: 'ok', server: 'org-server' }),\r\n );\r\n\r\n r.post('/login', validateLogin, async (req, res) => {\r\n const { email: emailAddress, password } = req.body || {};\r\n\r\n try {\r\n // 1. Find user in your DB\r\n const user = await OrgUser.findOne({ email: emailAddress })\r\n .select('+password')\r\n .lean();\r\n\r\n if (!user) {\r\n return res.status(400).json({\r\n error: 'Invalid email or password',\r\n code: 'INVALID_CREDENTIALS',\r\n });\r\n }\r\n\r\n // 2. Check if email is verified\r\n if (!user.emailVerified) {\r\n return res.status(400).json({\r\n error: 'Please verify your email before logging in.',\r\n code: 'EMAIL_NOT_VERIFIED',\r\n });\r\n }\r\n\r\n // 3. CRITICAL: Verify password with bcrypt\r\n const isPasswordValid = user.passwordHash\r\n ? await bcrypt.compare(password, user.passwordHash)\r\n : false;\r\n\r\n if (!isPasswordValid) {\r\n return res.status(400).json({\r\n error: 'Invalid email or password',\r\n code: 'INVALID_CREDENTIALS',\r\n });\r\n }\r\n\r\n // 4. Generate tokens\r\n const tokens = generateTokens(user);\r\n setAuthCookies(res, tokens, cookieConfig);\r\n\r\n // 5. Set projectId cookie if exists\r\n if (user.projectId) {\r\n res.cookie(options.projectCookieName || 'projectId', user.projectId, {\r\n ...baseProjectCookieOptionsFrom(cookieConfig),\r\n httpOnly: true,\r\n } as any);\r\n }\r\n\r\n return res.json({\r\n message: 'Login successful',\r\n user: toUserResponse(user),\r\n });\r\n } catch (err: any) {\r\n console.error('Login error:', err);\r\n return res.status(500).json({ error: 'Internal server error' });\r\n }\r\n });\r\n\r\n r.post('/signup', validateSignup, async (req, res) => {\r\n const {\r\n firstName,\r\n lastName,\r\n email: emailAddress,\r\n password,\r\n projectId,\r\n metadata,\r\n } = req.body || {};\r\n\r\n try {\r\n const kcUser = await authAdmin.createUserInRealm({\r\n username: emailAddress,\r\n email: emailAddress,\r\n firstName,\r\n lastName,\r\n projectId,\r\n credentials: [{ type: 'password', value: password, temporary: false }],\r\n metadata,\r\n });\r\n\r\n await authAdmin.assignRealmRole(kcUser.id, 'platform_user');\r\n\r\n const user = await OrgUser.findOneAndUpdate(\r\n { email: kcUser.email },\r\n {\r\n email: kcUser.email,\r\n firstName,\r\n lastName,\r\n projectId,\r\n metadata,\r\n roles: ['platform_user'],\r\n emailVerified: false,\r\n },\r\n { upsert: true, new: true, setDefaultsOnInsert: true },\r\n );\r\n\r\n const emailResult = await sendRateLimitedEmail({\r\n emailService: email,\r\n user,\r\n subject: 'Verify your email',\r\n // html: buildVerificationTemplate(\r\n // email.sign({ userId: kcUser.id, email: kcUser.email }),\r\n // options,\r\n // ),\r\n html: buildVerificationEmailTemplate({\r\n firstName: user.firstName,\r\n verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(\r\n {\r\n userId: user.id,\r\n email: user.email,\r\n },\r\n )}`,\r\n expiresIn: '1 hour',\r\n }),\r\n });\r\n\r\n if (emailResult.rateLimited) {\r\n return res.status(429).json({\r\n ok: false,\r\n error: 'Too many verification emails sent. Please try again later.',\r\n waitMs: emailResult.waitMs,\r\n });\r\n }\r\n\r\n return res.json({\r\n id: user.id,\r\n email: user.email,\r\n message: 'Verification email sent. Please check your inbox.',\r\n });\r\n } catch (err: any) {\r\n return respondWithKeycloakError(res, err, 'Signup failed');\r\n }\r\n });\r\n\r\n r.get('/me', requireAuth(), (req, res) => {\r\n return res.json((req as any).user || null);\r\n });\r\n\r\n r.post('/logout', async (_req, res) => {\r\n const clearOptions = buildClearCookieOptions(cookieConfig);\r\n\r\n res.clearCookie('access_token', clearOptions);\r\n res.clearCookie('refresh_token', clearOptions);\r\n res.json({ ok: true });\r\n });\r\n\r\n r.put('/:userId/metadata', requireAuth(), async (req, res) => {\r\n const { userId } = req.params as any;\r\n const { metadata } = req.body || {};\r\n\r\n const user = await OrgUser.findOne({ id: userId });\r\n\r\n if (!user)\r\n return res.status(404).json({ ok: false, error: 'User not found' });\r\n\r\n const map = new Map<string, any>(\r\n ((user as any).metadata || []).map((m: any) => [m.key, m.value]),\r\n );\r\n\r\n for (const item of metadata || []) map.set(item.key, item.value);\r\n (user as any).metadata = Array.from(map.entries()).map(([key, value]) => ({\r\n key,\r\n value,\r\n }));\r\n\r\n await (user as any).save();\r\n res.json({ ok: true, metadata: (user as any).metadata });\r\n });\r\n\r\n r.get('/verify-email', async (req, res) => {\r\n const token = String(req.query.token || '');\r\n if (!token) {\r\n return res.status(400).json({ error: 'Verification token is required' });\r\n }\r\n try {\r\n const payload = email.verify<{ email: string; userId: string }>(token);\r\n await authAdmin.updateUserEmailVerified(payload.userId, true);\r\n await OrgUser.updateOne(\r\n { id: payload.userId },\r\n { $set: { emailVerified: true } },\r\n );\r\n res.json({ ok: true, message: 'Email verified' });\r\n } catch (err: any) {\r\n res\r\n .status(400)\r\n .json({ ok: false, error: err?.message || 'Invalid token' });\r\n }\r\n });\r\n\r\n r.post(\r\n '/resend-verification-email',\r\n validateResendEmail,\r\n async (req, res) => {\r\n const user = await OrgUser.findOne({ email: req.body.email });\r\n if (!user)\r\n return res.status(404).json({ ok: false, error: 'User not found' });\r\n\r\n const verified = await authAdmin.isUserEmailVerified(user.id);\r\n if (verified) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Email is already verified' });\r\n }\r\n\r\n const token = email.sign({\r\n email: user.email,\r\n userId: user.id,\r\n });\r\n\r\n const resendResult = await sendRateLimitedEmail({\r\n emailService: email,\r\n user,\r\n subject: 'Verify your email',\r\n // html: buildVerificationTemplate(token, options),\r\n html: buildVerificationEmailTemplate({\r\n firstName: user.firstName,\r\n verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(\r\n {\r\n userId: user.id,\r\n email: user.email,\r\n },\r\n )}`,\r\n expiresIn: '1 hour',\r\n }),\r\n });\r\n\r\n if (resendResult.rateLimited) {\r\n return res.status(429).json({\r\n ok: false,\r\n error: 'Too many verification emails sent. Please try again later.',\r\n waitMs: resendResult.waitMs,\r\n });\r\n }\r\n\r\n res.json({ ok: true });\r\n },\r\n );\r\n\r\n r.post('/forgot-password', validateResendEmail, async (req, res) => {\r\n const user = await OrgUser.findOne({ email: req.body.email });\r\n if (!user)\r\n return res.status(404).json({ ok: false, error: 'User not found' });\r\n\r\n const resetToken = email.sign(\r\n {\r\n userId: user.id,\r\n email: user.email,\r\n firstName: user.firstName,\r\n lastName: user.lastName,\r\n },\r\n 60 * 60,\r\n );\r\n\r\n const resetResult = await sendRateLimitedEmail({\r\n emailService: email,\r\n user,\r\n subject: 'Reset password',\r\n // html: buildResetTemplate(resetToken, options),\r\n html: buildResetPasswordEmailTemplate({\r\n firstName: user.firstName,\r\n resetUrl: `${getFrontendBaseUrl(options)}/reset-password?token=${email.sign(\r\n {\r\n userId: user.id,\r\n email: user.email,\r\n },\r\n )}`,\r\n expiresIn: '1 hour',\r\n }),\r\n });\r\n\r\n if (resetResult.rateLimited) {\r\n return res.status(429).json({\r\n ok: false,\r\n error: 'Please wait before requesting another password reset email.',\r\n waitMs: resetResult.waitMs,\r\n });\r\n }\r\n\r\n res.json({ ok: true, message: 'Password reset email sent' });\r\n });\r\n\r\n r.post('/reset-password', validateResetPassword, async (req, res) => {\r\n const { token, newPassword } = (req.body || {}) as any;\r\n\r\n if (!token || !newPassword) {\r\n return res.status(400).json({\r\n ok: false,\r\n error: 'Token and new password are required',\r\n code: 'MISSING_FIELDS',\r\n });\r\n }\r\n\r\n try {\r\n const payload = email.verify<{\r\n userId: string;\r\n email: string;\r\n iat: number;\r\n }>(token);\r\n\r\n const user = await OrgUser.findOne({ id: payload.userId });\r\n if (!user) {\r\n return res.status(404).json({ ok: false, error: 'User not found' });\r\n }\r\n\r\n if (\r\n user.lastPasswordReset &&\r\n payload.iat * 1000 < user.lastPasswordReset.getTime()\r\n ) {\r\n return res.status(400).json({\r\n ok: false,\r\n error:\r\n 'This reset link has already been used. Please request a new one.',\r\n });\r\n }\r\n\r\n await authAdmin.updateUserPassword(payload.userId, newPassword);\r\n\r\n user.lastPasswordReset = new Date();\r\n await user.save();\r\n\r\n res.json({ ok: true, message: 'Password updated successfully' });\r\n } catch (err: any) {\r\n res\r\n .status(400)\r\n .json({ ok: false, error: err?.message || 'Invalid or expired token' });\r\n }\r\n });\r\n\r\n r.post(\r\n '/send-invite',\r\n requireAuth(),\r\n validateSendInvite,\r\n async (req, res) => {\r\n const { email: emailAddress, role } = req.body || {};\r\n\r\n const existingUser = await OrgUser.findOne({ email: emailAddress });\r\n if (existingUser) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'User with this email already exists' });\r\n }\r\n\r\n const existingInvite = await Invite.findOne({\r\n email: emailAddress,\r\n isUsed: false,\r\n isExpired: false,\r\n });\r\n\r\n if (existingInvite) {\r\n return res.status(400).json({\r\n ok: false,\r\n error: 'An active invite already exists for this email',\r\n });\r\n }\r\n\r\n const token = email.sign({\r\n email: emailAddress,\r\n role,\r\n inviteId: randomUUID(),\r\n });\r\n\r\n const invite = await Invite.create({\r\n id: token,\r\n email: emailAddress,\r\n role,\r\n invitedBy: (req as any).user?.sub,\r\n isUsed: false,\r\n expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),\r\n });\r\n\r\n await email.send(\r\n emailAddress,\r\n 'You are invited',\r\n `<a href=\"${getFrontendBaseUrl(options)}/auth/accept-invite?token=${token}\">Accept</a>`,\r\n );\r\n\r\n res.json({\r\n ok: true,\r\n inviteId: invite.id,\r\n email: invite.email,\r\n role: invite.role,\r\n expiresAt: invite.expiresAt,\r\n });\r\n },\r\n );\r\n\r\n r.get('/accept-invite', async (req, res) => {\r\n const inv = await Invite.findOne({ id: String(req.query.token) });\r\n res.json({ ok: !!inv && !(inv as any).isUsed && !(inv as any).isExpired });\r\n });\r\n\r\n r.post('/accept-invite', async (req, res) => {\r\n const { token, firstName, lastName, password, projectId } = req.body || {};\r\n\r\n if (\r\n !token ||\r\n !firstName ||\r\n !lastName ||\r\n !isPasswordStrong(password || '')\r\n ) {\r\n return res.status(400).json({ ok: false, error: 'Invalid payload' });\r\n }\r\n\r\n const invite = await Invite.findOne({\r\n id: token,\r\n isUsed: false,\r\n isExpired: false,\r\n });\r\n\r\n if (!invite) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Invitation not found or already used' });\r\n }\r\n\r\n if (invite.expiresAt && invite.expiresAt.getTime() < Date.now()) {\r\n invite.isExpired = true;\r\n await invite.save();\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Invitation has expired' });\r\n }\r\n\r\n try {\r\n const kcUser = await authAdmin.createUserInRealm({\r\n username: invite.email,\r\n email: invite.email,\r\n firstName,\r\n lastName,\r\n projectId,\r\n emailVerified: true,\r\n credentials: [{ type: 'password', value: password, temporary: false }],\r\n });\r\n\r\n await authAdmin.assignRealmRole(kcUser.id, invite.role);\r\n\r\n await OrgUser.findOneAndUpdate(\r\n { email: invite.email },\r\n {\r\n id: kcUser.id,\r\n email: invite.email,\r\n firstName,\r\n lastName,\r\n roles: [invite.role],\r\n emailVerified: true,\r\n },\r\n { upsert: true, new: true, setDefaultsOnInsert: true },\r\n );\r\n\r\n invite.isUsed = true;\r\n invite.usedAt = new Date();\r\n invite.usedBy = kcUser.id;\r\n await invite.save();\r\n\r\n res.json({\r\n ok: true,\r\n message: 'Account created successfully.',\r\n email: invite.email,\r\n });\r\n } catch (err: any) {\r\n res.status(400).json({\r\n ok: false,\r\n error:\r\n err?.response?.data?.error_description ||\r\n err?.message ||\r\n 'Failed to create account',\r\n });\r\n }\r\n });\r\n\r\n r.get('/invites', requireAuth(), async (_req, res) => {\r\n const invites = await Invite.find().sort({ createdAt: -1 }).lean();\r\n res.json(invites);\r\n });\r\n\r\n r.delete('/invites/:inviteId', requireAuth(), async (req, res) => {\r\n await Invite.deleteOne({ id: req.params.inviteId });\r\n res.json({ ok: true });\r\n });\r\n\r\n r.get('/get-user-by-email', async (req, res) => {\r\n const user = await OrgUser.findOne({ email: req.query.email }).lean();\r\n res.json(user || null);\r\n });\r\n\r\n r.get('/google', (req, res) => {\r\n if (!isGoogleEnabled) {\r\n return res.status(500).json({ error: 'Google login not configured' });\r\n }\r\n\r\n // ✅ Encode BOTH redirectTo AND projectId in state as JSON\r\n const stateData = {\r\n redirectTo: req.query.redirectTo || '',\r\n projectId: req.query.projectId || process.env.DEFAULT_PROJECT_ID || '',\r\n };\r\n\r\n // const state = req.query.redirectTo\r\n // ? encodeURIComponent(String(req.query.redirectTo))\r\n // : '';\r\n\r\n // const projectId = req.query.projectId\r\n // ? encodeURIComponent(String(req.query.projectId))\r\n // : '';\r\n\r\n const state = encodeURIComponent(JSON.stringify(stateData));\r\n\r\n const params = new URLSearchParams({\r\n client_id: googleClientId!,\r\n redirect_uri: googleRedirectUri!,\r\n response_type: 'code',\r\n scope: 'openid email profile',\r\n access_type: 'offline',\r\n prompt: 'consent',\r\n state,\r\n });\r\n\r\n const url = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;\r\n\r\n console.log(url, 'url');\r\n\r\n // Easiest: just redirect user to Google\r\n res.redirect(url);\r\n });\r\n\r\n r.get('/google/callback', async (req, res) => {\r\n if (!isGoogleEnabled) {\r\n return res.status(500).json({ error: 'Google login not configured' });\r\n }\r\n\r\n const code = String(req.query.code || '');\r\n // const state = req.query.state ? String(req.query.state) : '';\r\n // const projectId = req.query.projectId ? String(req.query.projectId) : '';\r\n // console.log(projectId, 'projectId');\r\n\r\n let stateData = { redirectTo: '', projectId: '' };\r\n try {\r\n if (req.query.state) {\r\n stateData = JSON.parse(decodeURIComponent(String(req.query.state)));\r\n }\r\n } catch (err) {\r\n console.error('Failed to parse state:', err);\r\n // Use defaults\r\n }\r\n\r\n const { redirectTo, projectId } = stateData;\r\n console.log(\r\n 'Parsed state - redirectTo:',\r\n redirectTo,\r\n 'projectId:',\r\n projectId,\r\n );\r\n\r\n if (!code) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Missing authorization code' });\r\n }\r\n\r\n try {\r\n // 1. Exchange code for Google tokens\r\n const tokenRes = await fetch('https://oauth2.googleapis.com/token', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\r\n body: new URLSearchParams({\r\n code,\r\n client_id: googleClientId!,\r\n client_secret: googleClientSecret!,\r\n redirect_uri: googleRedirectUri!,\r\n grant_type: 'authorization_code',\r\n }),\r\n });\r\n\r\n if (!tokenRes.ok) {\r\n const errJson = await tokenRes.json().catch(() => ({}));\r\n console.error('Google token error', errJson);\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Failed to exchange Google code' });\r\n }\r\n\r\n const tokenJson = (await tokenRes.json()) as {\r\n id_token: string;\r\n access_token: string;\r\n refresh_token?: string;\r\n expires_in?: number;\r\n };\r\n\r\n if (!tokenJson.id_token) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Missing id_token from Google' });\r\n }\r\n\r\n // 2. Decode ID token (for production, verify signature against Google JWKs)\r\n const decoded: any = jwt.decode(tokenJson.id_token);\r\n\r\n const email = decoded?.email;\r\n if (!email) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Google account has no email' });\r\n }\r\n\r\n const emailVerified = decoded.email_verified ?? true;\r\n const firstName = decoded.given_name || '';\r\n const lastName = decoded.family_name || '';\r\n\r\n // 3. Find or create local user\r\n let user = await OrgUser.findOne({ email }).lean();\r\n\r\n if (!user) {\r\n const finalProjectId = projectId || process.env.DEFAULT_PROJECT_ID;\r\n\r\n if (!finalProjectId) {\r\n console.error('No projectId available for new user');\r\n const errorRedirect =\r\n (redirectTo || googleDefaultRedirect) +\r\n (redirectTo?.includes('?') ? '&' : '?') +\r\n 'error=missing_project_id';\r\n return res.redirect(errorRedirect);\r\n }\r\n\r\n const created = await OrgUser.create({\r\n email,\r\n firstName,\r\n lastName,\r\n emailVerified,\r\n roles: ['platform_user'],\r\n projectId: finalProjectId,\r\n metadata: [],\r\n // you can also store googleId: decoded.sub\r\n });\r\n\r\n user = created.toObject();\r\n }\r\n\r\n // 4. Generate your own tokens & set cookies with SAME config as normal login\r\n const tokens = generateTokens(user);\r\n setAuthCookies(res, tokens, cookieConfig);\r\n\r\n // 5. Set projectId cookie if exists\r\n if (user.projectId) {\r\n res.cookie(options.projectCookieName || 'projectId', user.projectId, {\r\n ...baseProjectCookieOptionsFrom(cookieConfig),\r\n httpOnly: true,\r\n } as any);\r\n }\r\n\r\n // 6. Redirect to frontend\r\n const finalRedirect = redirectTo || googleDefaultRedirect;\r\n\r\n res.redirect(finalRedirect);\r\n } catch (err: any) {\r\n console.error('Google callback error', err);\r\n const redirectError = googleDefaultRedirect.includes('?')\r\n ? `${googleDefaultRedirect}&error=google_login_failed`\r\n : `${googleDefaultRedirect}?error=google_login_failed`;\r\n\r\n res.redirect(redirectError);\r\n }\r\n });\r\n\r\n r.get('/github', (req, res) => {\r\n const githubClientId = process.env.GITHUB_CLIENT_ID;\r\n const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;\r\n\r\n if (!githubClientId || !githubRedirectUri) {\r\n return res.status(500).json({ error: 'GitHub login not configured' });\r\n }\r\n\r\n const state = req.query.redirectTo\r\n ? encodeURIComponent(String(req.query.redirectTo))\r\n : '';\r\n\r\n const params = new URLSearchParams({\r\n client_id: githubClientId,\r\n redirect_uri: githubRedirectUri,\r\n scope: 'user:email',\r\n state,\r\n allow_signup: 'true',\r\n });\r\n\r\n const url = `https://github.com/login/oauth/authorize?${params.toString()}`;\r\n res.redirect(url);\r\n });\r\n\r\n r.get('/github/callback', async (req, res) => {\r\n const githubClientId = process.env.GITHUB_CLIENT_ID;\r\n const githubClientSecret = process.env.GITHUB_CLIENT_SECRET;\r\n const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;\r\n const githubDefaultRedirect =\r\n process.env.GITHUB_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || '/';\r\n\r\n if (!githubClientId || !githubClientSecret || !githubRedirectUri) {\r\n return res.status(500).json({ error: 'GitHub login not configured' });\r\n }\r\n\r\n const code = String(req.query.code || '');\r\n const state = req.query.state ? String(req.query.state) : '';\r\n\r\n if (!code) {\r\n return res.status(400).json({ ok: false, error: 'Missing GitHub code' });\r\n }\r\n\r\n try {\r\n // ✅ 1. Exchange code for access token\r\n const tokenRes = await fetch(\r\n 'https://github.com/login/oauth/access_token',\r\n {\r\n method: 'POST',\r\n headers: {\r\n Accept: 'application/json',\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: new URLSearchParams({\r\n client_id: githubClientId,\r\n client_secret: githubClientSecret,\r\n code,\r\n redirect_uri: githubRedirectUri,\r\n }),\r\n },\r\n );\r\n\r\n const tokenJson = (await tokenRes.json()) as {\r\n access_token?: string;\r\n token_type?: string;\r\n scope?: string;\r\n error?: string;\r\n error_description?: string;\r\n };\r\n\r\n if (!tokenJson.access_token) {\r\n console.error('GitHub token error:', tokenJson);\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Failed to get GitHub access token' });\r\n }\r\n\r\n const accessToken = tokenJson.access_token;\r\n\r\n // ✅ 2. Get GitHub user profile\r\n const userRes = await fetch('https://api.github.com/user', {\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n Accept: 'application/vnd.github+json',\r\n },\r\n });\r\n\r\n const githubUser = (await userRes.json()) as {\r\n id: number;\r\n name?: string;\r\n login: string;\r\n };\r\n\r\n // ✅ 3. Get verified email (GitHub may not return it in profile)\r\n const emailRes = await fetch('https://api.github.com/user/emails', {\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n Accept: 'application/vnd.github+json',\r\n },\r\n });\r\n\r\n const emails = (await emailRes.json()) as {\r\n email: string;\r\n primary: boolean;\r\n verified: boolean;\r\n visibility?: string;\r\n }[];\r\n\r\n const primaryEmail = emails?.find(\r\n (e: any) => e.primary && e.verified,\r\n )?.email;\r\n\r\n if (!primaryEmail) {\r\n return res.status(400).json({\r\n ok: false,\r\n error: 'GitHub account has no verified email',\r\n });\r\n }\r\n\r\n const firstName = githubUser.name?.split(' ')[0] || '';\r\n const lastName = githubUser.name?.split(' ').slice(1).join(' ') || '';\r\n\r\n // ✅ 4. Find or create user in DB\r\n let user = await OrgUser.findOne({ email: primaryEmail }).lean();\r\n\r\n if (!user) {\r\n const created = await OrgUser.create({\r\n email: primaryEmail,\r\n firstName,\r\n lastName,\r\n emailVerified: true,\r\n roles: ['platform_user'],\r\n projectId: null,\r\n metadata: [],\r\n githubId: githubUser.id,\r\n });\r\n\r\n user = created.toObject();\r\n }\r\n\r\n // ✅ 5. Generate your JWT cookies (same as Google)\r\n const tokens = generateTokens(user);\r\n setAuthCookies(res, tokens, cookieConfig);\r\n\r\n // ✅ 6. Redirect to frontend\r\n const redirectTo = state\r\n ? decodeURIComponent(state)\r\n : githubDefaultRedirect;\r\n\r\n res.redirect(redirectTo);\r\n } catch (err: any) {\r\n console.error('GitHub callback error:', err);\r\n\r\n const redirectError = githubDefaultRedirect.includes('?')\r\n ? `${githubDefaultRedirect}&error=github_login_failed`\r\n : `${githubDefaultRedirect}?error=github_login_failed`;\r\n\r\n res.redirect(redirectError);\r\n }\r\n });\r\n\r\n r.get('/get-users', async (req, res) => {\r\n const user = await OrgUser.find({ projectId: req.query.projectId }).lean();\r\n res.json(user || null);\r\n });\r\n\r\n return r;\r\n}\r\n\r\nfunction setAuthCookies(\r\n res: any,\r\n tokens: { access_token: string; refresh_token: string },\r\n cookie: AuthCookieConfig,\r\n) {\r\n const base: CookieOptions = {\r\n httpOnly: true,\r\n secure: cookie.secure ?? false,\r\n sameSite: cookie.sameSite ?? 'lax',\r\n path: cookie.path ?? '/',\r\n maxAge: cookie.maxAgeMs,\r\n };\r\n\r\n if (cookie.domain) {\r\n base.domain = cookie.domain;\r\n }\r\n\r\n if (tokens?.access_token) {\r\n res.cookie('access_token', tokens.access_token, base);\r\n }\r\n if (tokens?.refresh_token) {\r\n res.cookie('refresh_token', tokens.refresh_token, base);\r\n }\r\n}\r\n\r\nfunction toUserResponse(user: any) {\r\n if (!user) return null;\r\n return {\r\n sub: user.id || user.keycloakId,\r\n email: user.email,\r\n firstName: user.firstName,\r\n lastName: user.lastName,\r\n projectId: user.projectId,\r\n metadata: user.metadata,\r\n roles: user.roles,\r\n };\r\n}\r\n\r\nfunction respondWithKeycloakError(\r\n res: any,\r\n err: any,\r\n fallback: string,\r\n status = 400,\r\n) {\r\n const description =\r\n err?.response?.data?.error_description ||\r\n err?.response?.data?.errorMessage ||\r\n err?.message ||\r\n fallback;\r\n return res.status(status).json({ ok: false, error: description });\r\n}\r\n\r\n// function buildVerificationTemplate(token: string, options: AuthRouterOptions) {\r\n// return `<a href=\"${getFrontendBaseUrl(options)}/verify-email?token=${token}\">Verify</a>`;\r\n// }\r\n\r\n// function buildResetTemplate(token: string, options: AuthRouterOptions) {\r\n// return `<a href=\"${getFrontendBaseUrl(options)}/reset-password?token=${token}\">Reset</a>`;\r\n// }\r\n\r\nfunction getFrontendBaseUrl(options: AuthRouterOptions) {\r\n if (options.frontendBaseUrl)\r\n return options.frontendBaseUrl.replace(/\\/$/, '');\r\n const domain = process.env.ORG_DOMAIN!?.replace(/\\/$/, '');\r\n if (!domain) return '';\r\n return domain.startsWith('http') ? domain : `https://${domain}`;\r\n}\r\n\r\nasync function sendRateLimitedEmail({\r\n emailService,\r\n user,\r\n subject,\r\n html,\r\n}: {\r\n emailService: EmailService;\r\n user: any;\r\n subject: string;\r\n html: string;\r\n}): Promise<{ rateLimited: boolean; waitMs?: number }> {\r\n const can = emailService.canSend(user?.lastEmailSent || []);\r\n if (!can.ok) {\r\n return { rateLimited: true, waitMs: (can as any).waitMs };\r\n }\r\n\r\n await emailService.send(user.email, subject, html);\r\n user.lastEmailSent = [...(user.lastEmailSent || []), new Date()];\r\n await user.save();\r\n return { rateLimited: false };\r\n}\r\n\r\nfunction generateTokens(user: any) {\r\n const accessPayload = {\r\n sub: user.id.toString(),\r\n email: user.email,\r\n roles: user.roles || [],\r\n orgId: user.orgId || null,\r\n org_id: user.orgId || null,\r\n projectId: user.projectId || null,\r\n firstName: user.firstName,\r\n lastName: user.lastName,\r\n emailVerified: user.emailVerified,\r\n createdAt: user.createdAt,\r\n metadata: user.metadata,\r\n type: 'user',\r\n };\r\n\r\n const accessToken = jwt.sign(accessPayload, process.env.JWT_SECRET!, {\r\n expiresIn: '1h',\r\n });\r\n\r\n const refreshToken = jwt.sign(\r\n { sub: user._id.toString() },\r\n process.env.JWT_SECRET!,\r\n { expiresIn: '30d' },\r\n );\r\n\r\n return { access_token: accessToken, refresh_token: refreshToken };\r\n}\r\n","import type {\n AuthXConfig,\n AwsConfig,\n CookieConfig,\n EmailConfig,\n OidcConfig,\n} from 'aaspai-types';\n\nexport function loadConfig(): AuthXConfig {\n return {\n orgDomain: process.env.ORG_DOMAIN!,\n orgId: process.env.ORG_ID!,\n email: {\n host: process.env.EMAIL_HOST || 'smtp.postmarkapp.com',\n port: process.env.EMAIL_PORT ? Number(process.env.EMAIL_PORT) : 587,\n secure: (process.env.EMAIL_SECURE || 'false') === 'true',\n user: process.env.EMAIL_USER!,\n pass: process.env.EMAIL_PASSWORD!,\n from: process.env.EMAIL_FROM!,\n jwtSecret: process.env.EMAIL_JWT_SECRET!,\n } as EmailConfig,\n cookies: {\n domain: process.env.COOKIE_DOMAIN!,\n secure: (process.env.COOKIE_SECURE || 'true') === 'true',\n accessTtlMs: 24 * 60 * 60 * 1000,\n refreshTtlMs: 7 * 24 * 60 * 60 * 1000,\n } as CookieConfig,\n oidc: {\n jwtSecret: process.env.JWT_SECRET!,\n } as OidcConfig,\n aws: {\n bucket: process.env.AWS_S3_BUCKET!,\n region: process.env.AWS_REGION!,\n accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n } as AwsConfig,\n };\n}\n","import type {\n AuthXConfig,\n AwsConfig,\n CookieConfig,\n DeepPartial,\n EmailConfig,\n OidcConfig,\n} from 'aaspai-types';\nimport { loadConfig } from './loadConfig.js';\n\nexport type {\n AuthXConfig,\n AwsConfig,\n CookieConfig,\n DeepPartial,\n EmailConfig,\n OidcConfig,\n};\n\nexport const config: AuthXConfig = loadConfig();\n\nexport function configureAuthX(\n overrides: DeepPartial<AuthXConfig> = {},\n): AuthXConfig {\n return deepMerge(config, overrides);\n}\n\nexport function getConfig(): AuthXConfig {\n return config;\n}\n\nfunction deepMerge<T extends Record<string, any>>(\n target: T,\n source: DeepPartial<T>,\n): T {\n if (!source) {\n return target;\n }\n\n for (const key of Object.keys(source) as (keyof T)[]) {\n const value = source[key];\n if (value === undefined) continue;\n\n if (Array.isArray(value)) {\n (target as any)[key] = [...value];\n continue;\n }\n\n if (isPlainObject(value)) {\n if (!isPlainObject(target[key])) {\n (target as any)[key] = {};\n }\n deepMerge(target[key] as Record<string, any>, value as any);\n continue;\n }\n\n (target as any)[key] = value;\n }\n\n return target;\n}\n\nfunction isPlainObject(value: any): value is Record<string, any> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import type { AuthCookieConfig, AuthXSession } from 'aaspai-types';\r\nimport { CookieOptions } from 'express';\r\n\r\n/**\r\n * Check if a session has a specific role\r\n */\r\nexport function hasRole(\r\n session: AuthXSession | null | undefined,\r\n role: string,\r\n): boolean {\r\n if (!session || !session.roles) return false;\r\n return session.roles.includes(role);\r\n}\r\n\r\nexport function baseProjectCookieOptionsFrom(\r\n cookie: AuthCookieConfig,\r\n): CookieOptions {\r\n const base: CookieOptions = {\r\n secure: cookie.secure ?? false,\r\n sameSite: cookie.sameSite ?? 'lax',\r\n path: cookie.path ?? '/',\r\n maxAge: cookie.maxAgeMs,\r\n };\r\n if (cookie.domain) base.domain = cookie.domain;\r\n return base;\r\n}\r\n\r\nexport function buildClearCookieOptions(cookie: AuthCookieConfig): CookieOptions {\r\n const opts: CookieOptions = {\r\n httpOnly: true, // not strictly required but fine\r\n secure: cookie.secure ?? false,\r\n sameSite: cookie.sameSite ?? 'lax',\r\n path: cookie.path ?? '/',\r\n };\r\n\r\n if (cookie.domain) {\r\n opts.domain = cookie.domain;\r\n }\r\n\r\n return opts;\r\n}\r\n\r\n\r\n/**\r\n * Check if a session has any of the specified roles\r\n */\r\nexport function hasAnyRole(\r\n session: AuthXSession | null | undefined,\r\n roles: string[],\r\n): boolean {\r\n if (\r\n !session ||\r\n !session.roles ||\r\n !Array.isArray(roles) ||\r\n roles.length === 0\r\n ) {\r\n return false;\r\n }\r\n return roles.some((role) => session.roles.includes(role));\r\n}\r\n\r\n/**\r\n * Check if a session has all of the specified roles\r\n */\r\nexport function hasAllRoles(\r\n session: AuthXSession | null | undefined,\r\n roles: string[],\r\n): boolean {\r\n if (\r\n !session ||\r\n !session.roles ||\r\n !Array.isArray(roles) ||\r\n roles.length === 0\r\n ) {\r\n return false;\r\n }\r\n return roles.every((role) => session.roles.includes(role));\r\n}\r\n\r\n/**\r\n * Check if a session has a specific permission\r\n */\r\nexport function hasPermission(\r\n session: AuthXSession | null | undefined,\r\n permission: string,\r\n): boolean {\r\n if (!session || !session.permissions) return false;\r\n return session.permissions.includes(permission);\r\n}\r\n\r\n/**\r\n * Check if a session has any of the specified permissions\r\n */\r\nexport function hasAnyPermission(\r\n session: AuthXSession | null | undefined,\r\n permissions: string[],\r\n): boolean {\r\n if (\r\n !session ||\r\n !session.permissions ||\r\n !Array.isArray(permissions) ||\r\n permissions.length === 0\r\n ) {\r\n return false;\r\n }\r\n return permissions.some((perm) => session.permissions.includes(perm));\r\n}\r\n\r\n/**\r\n * Check if a session has all of the specified permissions\r\n */\r\nexport function hasAllPermissions(\r\n session: AuthXSession | null | undefined,\r\n permissions: string[],\r\n): boolean {\r\n if (\r\n !session ||\r\n !session.permissions ||\r\n !Array.isArray(permissions) ||\r\n permissions.length === 0\r\n ) {\r\n return false;\r\n }\r\n return permissions.every((perm) => session.permissions.includes(perm));\r\n}\r\n","export const PLATFORM_ROLES = [\r\n {\r\n role: 'platform_admin',\r\n permissions: [],\r\n },\r\n {\r\n role: 'platform_manager',\r\n permissions: [],\r\n },\r\n {\r\n role: 'platform_user',\r\n permissions: [],\r\n },\r\n];\r\n\r\n/**\r\n * Get all permissions for a given set of roles\r\n * @param roles - Array of role names\r\n * @returns Array of unique permission strings\r\n */\r\nexport function getPermissionsForRoles(roles: string[]): string[] {\r\n if (!Array.isArray(roles) || roles.length === 0) {\r\n return [];\r\n }\r\n\r\n const permissionSet = new Set<string>();\r\n\r\n for (const roleName of roles) {\r\n const roleConfig = PLATFORM_ROLES.find((r) => r.role === roleName);\r\n if (roleConfig && Array.isArray(roleConfig.permissions)) {\r\n for (const perm of roleConfig.permissions) {\r\n permissionSet.add(perm);\r\n }\r\n }\r\n }\r\n\r\n return Array.from(permissionSet);\r\n}\r\n","import type { AuthXSession } from 'aaspai-types';\r\nimport { getPermissionsForRoles } from './roles.config.js';\r\n\r\n/**\r\n * Build a canonical AuthXSession from a JWT payload or user object\r\n * @param payload - JWT payload or user object from existing auth middleware\r\n * @returns AuthXSession with standardized shape\r\n */\r\nexport function buildSession(payload: any): AuthXSession {\r\n // Extract user ID (sub is standard JWT claim)\r\n const userId = payload?.sub || payload?.userId || payload?.id || '';\r\n\r\n // Extract email\r\n const email = payload?.email || payload?.email_address || '';\r\n\r\n // Extract roles from various possible locations\r\n const roles =\r\n payload?.realm_access?.roles ||\r\n payload?.roles ||\r\n payload?.['cognito:groups'] ||\r\n (Array.isArray(payload?.role) ? payload.role : []) ||\r\n [];\r\n\r\n // Ensure roles is an array of strings\r\n const normalizedRoles = Array.isArray(roles)\r\n ? roles.map(String).filter(Boolean)\r\n : [];\r\n\r\n // Derive permissions from roles using PLATFORM_ROLES config\r\n const permissions = getPermissionsForRoles(normalizedRoles);\r\n\r\n // Build base session\r\n const session: AuthXSession = {\r\n userId,\r\n email,\r\n roles: normalizedRoles,\r\n permissions,\r\n };\r\n\r\n // Preserve optional fields if present\r\n if (payload?.firstName) session.firstName = payload.firstName;\r\n if (payload?.lastName) session.lastName = payload.lastName;\r\n if (payload?.projectId) session.projectId = payload.projectId;\r\n if (payload?.orgId) session.orgId = payload.orgId;\r\n if (payload?.org_id) session.org_id = payload.org_id;\r\n if (payload?.authType) session.authType = payload.authType;\r\n if (payload?.createdAt) session.createdAt = payload.createdAt;\r\n if (payload?.metadata) session.metadata = payload.metadata;\r\n\r\n // Preserve any other custom fields\r\n Object.keys(payload || {}).forEach((key) => {\r\n if (\r\n ![\r\n 'sub',\r\n 'userId',\r\n 'id',\r\n 'email',\r\n 'email_address',\r\n 'realm_access',\r\n 'roles',\r\n 'cognito:groups',\r\n 'role',\r\n 'projectId',\r\n 'orgId',\r\n 'org_id',\r\n 'authType',\r\n ].includes(key)\r\n ) {\r\n session[key] = payload[key];\r\n }\r\n });\r\n\r\n return session;\r\n}\r\n","// src/models/rolePermission.model.ts\r\nimport mongoose, { Document, Schema } from 'mongoose';\r\n\r\nexport interface RolePermissionDocument extends Document {\r\n orgId?: string | null; // null => platform-wide\r\n role: string; // \"platform_admin\", \"platform_manager\", \"custom_role\"\r\n permissions: string[]; // list of permission keys\r\n createdAt: Date;\r\n updatedAt: Date;\r\n}\r\n\r\nconst RolePermissionSchema = new Schema<RolePermissionDocument>(\r\n {\r\n orgId: { type: String, default: null, index: true },\r\n role: { type: String, required: true },\r\n permissions: { type: [String], default: [] },\r\n },\r\n {\r\n timestamps: true,\r\n },\r\n);\r\n\r\nRolePermissionSchema.index({ orgId: 1, role: 1 }, { unique: true });\r\n\r\nexport const RolePermissionModel = mongoose.model<RolePermissionDocument>(\r\n 'RolePermission',\r\n RolePermissionSchema,\r\n 'role_permissions',\r\n);\r\n","import mongoose from 'mongoose';\nimport { v4 as uuid } from 'uuid';\n\nconst MetadataSchema = new mongoose.Schema(\n {\n key: { type: String, required: true },\n value: { type: mongoose.Schema.Types.Mixed, required: true },\n },\n { _id: false },\n);\n\nconst OrgUserSchema = new mongoose.Schema(\n {\n id: { type: String, default: uuid(), index: true, unique: true },\n email: { type: String, required: true, unique: true },\n firstName: { type: String, required: true },\n lastName: { type: String, required: true },\n orgId: { type: String },\n projectId: { type: String, required: true },\n roles: { type: [String], default: [] },\n emailVerified: { type: Boolean, default: false },\n lastEmailSent: { type: [Date], default: [] },\n lastPasswordReset: { type: Date },\n metadata: { type: [MetadataSchema], default: [] },\n passwordHash: { type: String },\n },\n { timestamps: true, collection: 'users' },\n);\n\nexport const OrgUser = mongoose.model('OrgUser', OrgUserSchema);\n","import { parse as parseCookie } from 'cookie';\nimport type { Request } from 'express';\n\nexport function extractToken(\n req: Request,\n opts?: {\n headerNames?: string[];\n cookieNames?: string[];\n queryNames?: string[];\n },\n) {\n const headerNames = opts?.headerNames ?? ['authorization', 'token'];\n const cookieNames = opts?.cookieNames ?? ['access_token', 'authorization'];\n const queryNames = opts?.queryNames ?? ['access_token', 'token'];\n\n for (const h of headerNames) {\n const raw = (req.headers as any)[h] as string | undefined;\n if (raw) {\n const lower = raw.toLowerCase();\n if (lower.startsWith('bearer ')) return raw.slice(7).trim();\n if (!raw.includes(' ')) return raw.trim();\n }\n }\n\n const ch = req.headers['cookie'];\n\n if (typeof ch === 'string') {\n const parsed = parseCookie(ch);\n for (const c of cookieNames) if (parsed[c]) return parsed[c];\n }\n\n for (const q of queryNames) {\n const v = (req.query as any)?.[q];\n if (typeof v === 'string' && v) return v;\n }\n\n return null;\n}\n\nexport function readProjectId(req: Request) {\n const ch = req.headers['cookie'];\n\n if (typeof ch === 'string') {\n try {\n const parsed = parseCookie(ch);\n return parsed['projectId'] || null;\n } catch {}\n }\n\n return null;\n}\n","import jwt from 'jsonwebtoken';\n\nexport function verifyJwt(token: string): Promise<any> {\n return new Promise((resolve, reject) => {\n jwt.verify(\n token,\n process.env.JWT_SECRET!, // This is your shared secret (string)\n {\n algorithms: ['HS256'], // Only allow HS256\n complete: false, // We only want payload\n },\n (err, decoded) => {\n if (err) {\n reject(err);\n } else {\n resolve(decoded);\n }\n },\n );\n });\n}\n\nexport function claimsToUser(payload: any) {\n return {\n sub: payload.sub,\n email: payload.email || payload.preferred_username,\n roles: payload.roles || [],\n name:\n payload.firstName ||\n payload.lastName ||\n `${payload.given_name || ''} ${payload.family_name || ''}`.trim(),\n emailVerified: payload.email_verified || false,\n };\n}\n","import type { AuthXSession } from 'aaspai-types';\r\nimport type { NextFunction, Request, Response } from 'express';\r\nimport { buildSession } from '../core/session.js';\r\nimport { RolePermissionModel } from '../models/rolePermission.model.js';\r\nimport { OrgUser } from '../models/user.model.js';\r\nimport { extractToken, readProjectId } from '../utils/extract.js';\r\nimport { verifyJwt } from '../utils/jwt.js';\r\n\r\nasync function mergeRolePermissions(session: AuthXSession): Promise<void> {\r\n const roles = Array.isArray(session.roles) ? session.roles : [];\r\n if (!roles.length) return;\r\n\r\n const orgContexts = new Set<string | null>();\r\n if (session.orgId) orgContexts.add(session.orgId);\r\n if (session.org_id) orgContexts.add(session.org_id);\r\n if (session.projectId) orgContexts.add(session.projectId);\r\n orgContexts.add(null);\r\n\r\n const docs = await RolePermissionModel.find({\r\n orgId: { $in: Array.from(orgContexts) },\r\n role: { $in: roles },\r\n })\r\n .lean()\r\n .exec();\r\n\r\n const dynamic = new Set<string>();\r\n for (const doc of docs) {\r\n for (const perm of doc.permissions || []) {\r\n if (perm) dynamic.add(perm);\r\n }\r\n }\r\n\r\n const existing = Array.isArray(session.permissions)\r\n ? session.permissions\r\n : [];\r\n session.permissions = Array.from(new Set([...existing, ...dynamic]));\r\n}\r\n\r\n/**\r\n * Express middleware to require authentication\r\n * Extracts and verifies JWT token, builds AuthXSession, and attaches to req.user\r\n */\r\nexport function requireAuth() {\r\n return async (req: Request, res: Response, next: NextFunction) => {\r\n try {\r\n // Check for API key first (for backward compatibility)\r\n const apiKey = (req.headers['x-api-key'] || req.headers['x-apikey']) as\r\n | string\r\n | undefined;\r\n\r\n const userId = (req.headers['x-user-id'] || req.headers['x-userId']) as\r\n | string\r\n | undefined;\r\n\r\n if (apiKey) {\r\n if (apiKey !== process.env.SERVER_API_KEY) {\r\n return res.status(401).json({ error: 'Invalid API key' });\r\n }\r\n\r\n if (!userId) {\r\n return res.status(401).json({ error: 'User Id is Required' });\r\n }\r\n\r\n // Fetch real user from DB\r\n const user = await OrgUser.findOne({ id: userId }).lean();\r\n\r\n if (!user) {\r\n return res.status(401).json({ error: 'User not found' });\r\n }\r\n\r\n const session = buildSession({\r\n sub: user.id.toString(),\r\n email: user.email,\r\n firstName: user.firstName,\r\n lastName: user.lastName,\r\n metadata: user.metadata || [],\r\n roles: user.roles || [],\r\n orgId: user.orgId,\r\n org_id: user.orgId,\r\n projectId: user.projectId,\r\n createdAt: user.createdAt,\r\n });\r\n\r\n session.authType = 'api-key';\r\n session.projectId = readProjectId(req) || user.projectId || undefined;\r\n await mergeRolePermissions(session);\r\n (req as any).user = session;\r\n return next();\r\n } else {\r\n // Extract and verify JWT token\r\n const token = extractToken(req);\r\n if (!token) {\r\n return res.status(401).json({ error: 'Missing token' });\r\n }\r\n const claims = await verifyJwt(token);\r\n const session = buildSession(claims);\r\n // Preserve projectId if present in cookie\r\n const pid = readProjectId(req);\r\n if (pid) session.projectId = pid;\r\n await mergeRolePermissions(session);\r\n\r\n // Attach session to request\r\n (req as any).user = session;\r\n next();\r\n }\r\n } catch (e: any) {\r\n res.status(401).json({ error: e?.message || 'Unauthorized' });\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Express middleware to require one or more roles\r\n * @param roles - Array of role names (user must have at least one)\r\n * @deprecated Use requireRole from middlewares instead. This is kept for backward compatibility.\r\n */\r\nexport function authorize(roles: string[] = []) {\r\n return (req: Request, res: Response, next: NextFunction) => {\r\n if (!roles || roles.length === 0) return next();\r\n const user = (req as any).user as AuthXSession | undefined;\r\n if (!user) {\r\n return res.status(401).json({ error: 'Unauthorized' });\r\n }\r\n const have = new Set<string>((user.roles || []).map(String));\r\n const ok = roles.some((r) => have.has(r));\r\n if (!ok) {\r\n return res\r\n .status(403)\r\n .json({ error: `Requires one of roles: ${roles.join(', ')}` });\r\n }\r\n next();\r\n };\r\n}\r\n","export function isEmail(v: string) {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v);\n}\n\nexport function isPasswordStrong(v: string) {\n return (\n typeof v === 'string' &&\n v.length >= 6 &&\n /[A-Z]/.test(v) &&\n /[^a-zA-Z0-9]/.test(v)\n );\n}\n\nexport function validateSignup(req: any, res: any, next: any) {\n const { firstName, lastName, email, password, projectId, metadata } =\n req.body || {};\n if (!firstName || !lastName)\n return res.status(400).json({ error: 'firstName,lastName required' });\n if (!isEmail(email)) return res.status(400).json({ error: 'invalid email' });\n if (!isPasswordStrong(password))\n return res.status(400).json({ error: 'weak password' });\n if (!projectId) return res.status(400).json({ error: 'projectId required' });\n if (!Array.isArray(metadata))\n return res.status(400).json({ error: 'metadata must be array' });\n next();\n}\n\nexport function validateLogin(req: any, res: any, next: any) {\n const { email, password } = req.body || {};\n if (!isEmail(email)) return res.status(400).json({ error: 'invalid email' });\n if (typeof password !== 'string')\n return res.status(400).json({ error: 'password required' });\n next();\n}\n\nexport function validateResetPassword(req: any, res: any, next: any) {\n const { token, newPassword } = req.body || {};\n if (!token) return res.status(400).json({ error: 'token required' });\n if (!isPasswordStrong(newPassword))\n return res.status(400).json({ error: 'weak password' });\n next();\n}\n\nexport function validateResendEmail(req: any, res: any, next: any) {\n const { email } = req.body || {};\n if (!isEmail(email)) return res.status(400).json({ error: 'invalid email' });\n next();\n}\n\nexport function validateSendInvite(req: any, res: any, next: any) {\n const { email, role } = req.body || {};\n if (!isEmail(email)) return res.status(400).json({ error: 'invalid email' });\n if (!['platform_user', 'org_admin'].includes(role))\n return res.status(400).json({ error: 'invalid role' });\n next();\n}\n","import mongoose from 'mongoose';\n\nconst InviteSchema = new mongoose.Schema(\n {\n id: { type: String, required: true, index: true },\n email: { type: String, required: true },\n role: {\n type: String,\n enum: ['platform_user', 'org_admin'],\n required: true,\n },\n invitedBy: { type: String },\n usedBy: { type: String },\n isUsed: { type: Boolean, default: false },\n usedAt: { type: Date },\n expiresAt: { type: Date },\n isExpired: { type: Boolean, default: false },\n },\n { timestamps: true, collection: 'invites' },\n);\n\nexport const Invite = mongoose.model('Invite', InviteSchema);\n","import bcrypt from 'bcrypt';\r\nimport jwt from 'jsonwebtoken';\r\nimport { v4 as uuid } from 'uuid';\r\nimport { ClientModel } from '../models/client.model.js';\r\nimport { RolePermissionModel } from '../models/rolePermission.model.js';\r\nimport { OrgUser } from '../models/user.model.js';\r\n\r\nexport class AuthAdminService {\r\n private token?: { accessToken: string; exp: number };\r\n\r\n async getAdminToken() {\r\n return this.ensureAdminToken();\r\n }\r\n\r\n // -------------------------------------------------------------------\r\n // CLIENTS\r\n // -------------------------------------------------------------------\r\n async createClient(\r\n clientId: string,\r\n redirectUris: string[] = [],\r\n publicClient = false,\r\n ) {\r\n const client = await ClientModel.create({\r\n clientId,\r\n redirectUris,\r\n publicClient,\r\n });\r\n return client;\r\n }\r\n\r\n async updateClient(id: string, patch: any) {\r\n await ClientModel.findByIdAndUpdate(id, patch);\r\n }\r\n\r\n // -------------------------------------------------------------------\r\n // USERS\r\n // -------------------------------------------------------------------\r\n async listUsersInRealm(\r\n _realm: string,\r\n filter?: { email?: string; username?: string },\r\n ) {\r\n return OrgUser.find(filter || {});\r\n }\r\n\r\n async getUserById(userId: string) {\r\n return OrgUser.findOne({ id: userId });\r\n }\r\n\r\n async isUserEmailVerified(userId: string) {\r\n const user: any = await OrgUser.findOne({ id: userId });\r\n return user?.emailVerified;\r\n }\r\n\r\n async createUserInRealm(payload: {\r\n username: string;\r\n email: string;\r\n firstName: string;\r\n projectId: string;\r\n lastName?: string;\r\n credentials?: any[];\r\n emailVerified?: boolean;\r\n metadata?: any[];\r\n }) {\r\n const hashedPassword = payload.credentials?.[0]?.value\r\n ? await bcrypt.hash(payload.credentials[0].value, 10)\r\n : undefined;\r\n\r\n const user = await OrgUser.create({\r\n id: crypto.randomUUID(),\r\n email: payload.email,\r\n firstName: payload.firstName,\r\n lastName: payload.lastName,\r\n projectId: payload.projectId,\r\n emailVerified: payload.emailVerified || false,\r\n passwordHash: hashedPassword,\r\n metadata: payload.metadata || [],\r\n });\r\n\r\n return user;\r\n }\r\n\r\n async assignRealmRole(userId: string, roleName: string) {\r\n const role = await RolePermissionModel.findOne({ role: roleName });\r\n if (!role) throw new Error(`Role not found: ${roleName}`);\r\n\r\n await OrgUser.findOneAndUpdate(\r\n { id: userId },\r\n {\r\n $addToSet: { roles: role._id },\r\n },\r\n );\r\n }\r\n\r\n async updateUserEmailVerified(userId: string, emailVerified: boolean) {\r\n await OrgUser.findOneAndUpdate({ id: userId }, { emailVerified });\r\n }\r\n\r\n async updateUserPassword(userId: string, newPassword: string) {\r\n const hashed = await bcrypt.hash(newPassword, 10);\r\n\r\n await OrgUser.findOneAndUpdate({ id: userId }, { passwordHash: hashed });\r\n }\r\n\r\n // -------------------------------------------------------------------\r\n // ADMIN TOKEN (self-issued JWT)\r\n // -------------------------------------------------------------------\r\n private async ensureAdminToken(): Promise<string> {\r\n const now = Math.floor(Date.now() / 1000);\r\n\r\n if (this.token && this.token.exp - 30 > now) {\r\n return this.token.accessToken;\r\n }\r\n\r\n const payload = {\r\n type: 'admin',\r\n system: true,\r\n };\r\n\r\n const accessToken = jwt.sign(payload, process.env.JWT_SECRET!, {\r\n expiresIn: '1h',\r\n });\r\n\r\n this.token = {\r\n accessToken,\r\n exp: now + 3600,\r\n };\r\n\r\n return this.token.accessToken;\r\n }\r\n}\r\n","import mongoose, { Document, Model, Schema } from 'mongoose';\r\n\r\nexport interface IClient extends Document {\r\n clientId: string;\r\n redirectUris: string[];\r\n publicClient: boolean;\r\n secret?: string; // optional — used only for confidential clients\r\n createdAt: Date;\r\n updatedAt: Date;\r\n}\r\n\r\nconst ClientSchema = new Schema<IClient>(\r\n {\r\n clientId: {\r\n type: String,\r\n required: true,\r\n unique: true,\r\n index: true,\r\n },\r\n\r\n redirectUris: {\r\n type: [String],\r\n default: [],\r\n },\r\n\r\n publicClient: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n\r\n // Optional: if you want confidential clients\r\n secret: {\r\n type: String,\r\n required: function (this: IClient) {\r\n return !this.publicClient;\r\n },\r\n },\r\n },\r\n {\r\n timestamps: true,\r\n },\r\n);\r\n\r\nexport const ClientModel: Model<IClient> =\r\n mongoose.models.Client || mongoose.model<IClient>('Client', ClientSchema);\r\n","import jwt from 'jsonwebtoken';\nimport nodemailer from 'nodemailer';\n\nexport class EmailService {\n private transporter;\n private MAX_EMAILS = 5;\n private WINDOW_MINUTES = 15;\n private BLOCK_HOURS = 1;\n constructor() {\n this.transporter = nodemailer.createTransport({\n host: process.env.EMAIL_HOST || 'smtp.postmarkapp.com',\n port: process.env.EMAIL_PORT ? Number(process.env.EMAIL_PORT) : 587,\n secure: (process.env.EMAIL_SECURE || 'false') === 'true',\n auth: {\n user: process.env.EMAIL_USER!,\n pass: process.env.EMAIL_PASSWORD!,\n },\n });\n }\n\n sign(payload: any, ttlSec = 60 * 60 * 24) {\n return jwt.sign(payload, process.env.EMAIL_JWT_SECRET!, {\n expiresIn: ttlSec,\n });\n }\n\n verify<T = any>(token: string): T {\n return jwt.verify(token, process.env.EMAIL_JWT_SECRET!) as T;\n }\n\n async send(to: string, subject: string, html: string) {\n try {\n const info = await this.transporter.sendMail({\n from: process.env.EMAIL_FROM!,\n to,\n subject,\n html,\n });\n console.log('[EmailService] ✅ Email sent successfully:', {\n messageId: info.messageId,\n response: info.response,\n accepted: info.accepted,\n rejected: info.rejected,\n });\n\n return info;\n } catch (error: any) {\n console.error('[EmailService] ❌ Failed to send email:', {\n message: error.message,\n code: error.code,\n command: error.command,\n responseCode: error.responseCode,\n response: error.response,\n stack: error.stack,\n });\n\n // Re-throw so caller knows it failed\n throw error;\n }\n }\n\n canSend(lastEmailSent: Date[]) {\n const now = Date.now();\n const windowStart = now - this.WINDOW_MINUTES * 60 * 1000;\n const emailsInWindow = (lastEmailSent || [])\n .map((d) => new Date(d))\n .filter((d) => d.getTime() >= windowStart);\n if (emailsInWindow.length >= this.MAX_EMAILS)\n return {\n ok: false,\n reason: 'RATE_LIMIT',\n waitMs: this.BLOCK_HOURS * 60 * 60 * 1000,\n };\n return { ok: true };\n }\n}\n","// templates/email.templates.ts\r\n\r\ninterface VerificationEmailData {\r\n firstName: string;\r\n verificationUrl: string;\r\n expiresIn: string;\r\n}\r\n\r\ninterface ResetPasswordEmailData {\r\n firstName: string;\r\n resetUrl: string;\r\n expiresIn: string;\r\n}\r\n\r\ninterface WelcomeEmailData {\r\n firstName: string;\r\n loginUrl: string;\r\n}\r\n\r\ninterface InviteEmailData {\r\n inviterName?: string;\r\n organizationName?: string;\r\n role: string;\r\n inviteUrl: string;\r\n expiresIn: string;\r\n}\r\n\r\n// Premium dark theme styles\r\nconst colors = {\r\n background: '#0a0a0a',\r\n cardBackground: '#111111',\r\n cardBorder: '#1a1a1a',\r\n accent: '#ffffff',\r\n accentMuted: 'rgba(255, 255, 255, 0.9)',\r\n textPrimary: '#ffffff',\r\n textSecondary: 'rgba(255, 255, 255, 0.7)',\r\n textMuted: 'rgba(255, 255, 255, 0.5)',\r\n divider: 'rgba(255, 255, 255, 0.1)',\r\n subtle: '#161616',\r\n highlight: 'rgba(255, 255, 255, 0.05)',\r\n};\r\n\r\nconst styles = {\r\n wrapper: `\r\n margin: 0;\r\n padding: 40px 20px;\r\n background-color: ${colors.background};\r\n background-image: \r\n radial-gradient(ellipse at top, rgba(255,255,255,0.03) 0%, transparent 50%),\r\n radial-gradient(ellipse at bottom, rgba(255,255,255,0.02) 0%, transparent 50%);\r\n min-height: 100vh;\r\n `,\r\n container: `\r\n max-width: 520px;\r\n margin: 0 auto;\r\n font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\r\n color: ${colors.textPrimary};\r\n line-height: 1.7;\r\n `,\r\n card: `\r\n background-color: ${colors.cardBackground};\r\n border: 1px solid ${colors.cardBorder};\r\n border-radius: 16px;\r\n overflow: hidden;\r\n box-shadow: \r\n 0 0 0 1px rgba(255,255,255,0.05),\r\n 0 20px 50px -20px rgba(0,0,0,0.5),\r\n 0 30px 60px -30px rgba(0,0,0,0.3);\r\n `,\r\n header: `\r\n padding: 48px 40px 32px;\r\n text-align: center;\r\n border-bottom: 1px solid ${colors.divider};\r\n `,\r\n iconWrapper: `\r\n width: 64px;\r\n height: 64px;\r\n margin: 0 auto 24px;\r\n background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);\r\n border: 1px solid rgba(255,255,255,0.1);\r\n border-radius: 16px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 28px;\r\n `,\r\n headerTitle: `\r\n color: ${colors.textPrimary};\r\n margin: 0;\r\n font-size: 24px;\r\n font-weight: 600;\r\n letter-spacing: -0.5px;\r\n `,\r\n headerSubtitle: `\r\n color: ${colors.textMuted};\r\n margin: 8px 0 0;\r\n font-size: 14px;\r\n font-weight: 400;\r\n `,\r\n body: `\r\n padding: 40px;\r\n `,\r\n greeting: `\r\n margin: 0 0 24px;\r\n color: ${colors.textPrimary};\r\n font-size: 18px;\r\n font-weight: 500;\r\n `,\r\n paragraph: `\r\n margin: 0 0 20px;\r\n color: ${colors.textSecondary};\r\n font-size: 15px;\r\n line-height: 1.7;\r\n `,\r\n buttonWrapper: `\r\n text-align: center;\r\n margin: 32px 0;\r\n `,\r\n button: `\r\n display: inline-block;\r\n background-color: ${colors.accent};\r\n color: #000000 !important;\r\n text-decoration: none;\r\n padding: 14px 36px;\r\n border-radius: 8px;\r\n font-weight: 600;\r\n font-size: 14px;\r\n letter-spacing: 0.3px;\r\n transition: all 0.2s ease;\r\n `,\r\n secondaryButton: `\r\n display: inline-block;\r\n background-color: transparent;\r\n color: ${colors.textPrimary} !important;\r\n text-decoration: none;\r\n padding: 12px 28px;\r\n border-radius: 8px;\r\n font-weight: 500;\r\n font-size: 14px;\r\n border: 1px solid ${colors.divider};\r\n `,\r\n infoCard: `\r\n background-color: ${colors.subtle};\r\n border: 1px solid ${colors.divider};\r\n border-radius: 12px;\r\n padding: 20px 24px;\r\n margin: 28px 0;\r\n `,\r\n infoCardTitle: `\r\n margin: 0 0 12px;\r\n color: ${colors.textPrimary};\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n `,\r\n infoCardText: `\r\n margin: 0;\r\n color: ${colors.textSecondary};\r\n font-size: 14px;\r\n line-height: 1.6;\r\n `,\r\n warningCard: `\r\n background: linear-gradient(135deg, rgba(255,180,0,0.1) 0%, rgba(255,140,0,0.05) 100%);\r\n border: 1px solid rgba(255,180,0,0.2);\r\n border-radius: 12px;\r\n padding: 20px 24px;\r\n margin: 28px 0;\r\n `,\r\n warningCardTitle: `\r\n margin: 0 0 12px;\r\n color: #ffc107;\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n `,\r\n warningCardText: `\r\n margin: 0;\r\n color: rgba(255,255,255,0.7);\r\n font-size: 14px;\r\n line-height: 1.6;\r\n `,\r\n successCard: `\r\n background: linear-gradient(135deg, rgba(0,255,150,0.1) 0%, rgba(0,200,100,0.05) 100%);\r\n border: 1px solid rgba(0,255,150,0.2);\r\n border-radius: 12px;\r\n padding: 20px 24px;\r\n margin: 28px 0;\r\n `,\r\n successCardTitle: `\r\n margin: 0 0 12px;\r\n color: #00ff96;\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n `,\r\n linkSection: `\r\n margin: 32px 0;\r\n padding: 20px;\r\n background-color: ${colors.subtle};\r\n border-radius: 8px;\r\n border: 1px solid ${colors.divider};\r\n `,\r\n linkLabel: `\r\n margin: 0 0 8px;\r\n color: ${colors.textMuted};\r\n font-size: 12px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n `,\r\n linkText: `\r\n word-break: break-all;\r\n color: ${colors.textSecondary};\r\n font-size: 13px;\r\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;\r\n margin: 0;\r\n `,\r\n divider: `\r\n border: none;\r\n border-top: 1px solid ${colors.divider};\r\n margin: 32px 0;\r\n `,\r\n footer: `\r\n padding: 24px 40px 32px;\r\n text-align: center;\r\n border-top: 1px solid ${colors.divider};\r\n `,\r\n footerText: `\r\n margin: 0;\r\n color: ${colors.textMuted};\r\n font-size: 12px;\r\n line-height: 1.8;\r\n `,\r\n footerLink: `\r\n color: ${colors.textSecondary};\r\n text-decoration: none;\r\n `,\r\n badge: `\r\n display: inline-block;\r\n background-color: rgba(255,255,255,0.1);\r\n color: ${colors.textSecondary};\r\n padding: 4px 12px;\r\n border-radius: 20px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n letter-spacing: 0.3px;\r\n `,\r\n listItem: `\r\n color: ${colors.textSecondary};\r\n font-size: 14px;\r\n margin: 8px 0;\r\n padding-left: 8px;\r\n `,\r\n metaRow: `\r\n display: flex;\r\n justify-content: space-between;\r\n padding: 12px 0;\r\n border-bottom: 1px solid ${colors.divider};\r\n `,\r\n metaLabel: `\r\n color: ${colors.textMuted};\r\n font-size: 13px;\r\n `,\r\n metaValue: `\r\n color: ${colors.textPrimary};\r\n font-size: 13px;\r\n font-weight: 500;\r\n `,\r\n};\r\n\r\n/**\r\n * Email Verification Template\r\n */\r\nexport function buildVerificationEmailTemplate(\r\n data: VerificationEmailData,\r\n): string {\r\n const { firstName, verificationUrl, expiresIn } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>Verify Your Email</title>\r\n <!--[if mso]>\r\n <style type=\"text/css\">\r\n body, table, td {font-family: Arial, Helvetica, sans-serif !important;}\r\n </style>\r\n <![endif]-->\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n ✉️\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">Verify your email</h1>\r\n <p style=\"${styles.headerSubtitle}\">One quick step to get started</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n Thanks for signing up. To complete your registration and unlock all features, \r\n please verify your email address by clicking the button below.\r\n </p>\r\n \r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${verificationUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Verify Email Address\r\n </a>\r\n </div>\r\n \r\n <div style=\"${styles.infoCard}\">\r\n <p style=\"${styles.infoCardTitle}\">⏱ Time Sensitive</p>\r\n <p style=\"${styles.infoCardText}\">\r\n This verification link will expire in <strong>${expiresIn}</strong>. \r\n If you didn't create an account, you can safely ignore this email.\r\n </p>\r\n </div>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Password Reset Template\r\n */\r\nexport function buildResetPasswordEmailTemplate(\r\n data: ResetPasswordEmailData,\r\n): string {\r\n const { firstName, resetUrl, expiresIn } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>Reset Your Password</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n 🔐\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">Reset your password</h1>\r\n <p style=\"${styles.headerSubtitle}\">We received a reset request</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n We received a request to reset the password for your account. \r\n Click the button below to create a new password.\r\n </p>\r\n \r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${resetUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Reset Password\r\n </a>\r\n </div>\r\n \r\n <div style=\"${styles.warningCard}\">\r\n <p style=\"${styles.warningCardTitle}\">⚠️ Security Notice</p>\r\n <p style=\"${styles.warningCardText}\">\r\n • This link expires in <strong>${expiresIn}</strong><br>\r\n • This link can only be used once<br>\r\n • If you didn't request this, ignore this email\r\n </p>\r\n </div>\r\n \r\n <hr style=\"${styles.divider}\" />\r\n \r\n <p style=\"${styles.paragraph}; font-size: 13px; color: ${colors.textMuted};\">\r\n <strong>Didn't request this?</strong><br>\r\n Your password remains unchanged. If you're concerned about your account \r\n security, please contact our support team immediately.\r\n </p>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Welcome Email Template (sent after verification)\r\n */\r\nexport function buildWelcomeEmailTemplate(data: WelcomeEmailData): string {\r\n const { firstName, loginUrl } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>Welcome!</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n ✨\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">You're all set</h1>\r\n <p style=\"${styles.headerSubtitle}\">Your account is now active</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Welcome, ${firstName}</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n Your email has been verified and your account is ready to go. \r\n We're excited to have you on board.\r\n </p>\r\n \r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${loginUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Get Started\r\n </a>\r\n </div>\r\n \r\n <div style=\"${styles.successCard}\">\r\n <p style=\"${styles.successCardTitle}\">✓ Quick Start</p>\r\n <p style=\"margin: 0; color: rgba(255,255,255,0.7); font-size: 14px; line-height: 1.8;\">\r\n • Complete your profile for a personalized experience<br>\r\n • Explore the dashboard to discover features<br>\r\n • Check out the documentation for guides\r\n </p>\r\n </div>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n If you have any questions, our support team is here to help you \r\n get the most out of your experience.\r\n </p>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Invite Email Template\r\n */\r\nexport function buildInviteEmailTemplate(data: InviteEmailData): string {\r\n const { inviterName, organizationName, role, inviteUrl, expiresIn } = data;\r\n\r\n const inviterText = inviterName\r\n ? `<strong>${inviterName}</strong> has invited you`\r\n : 'You have been invited';\r\n\r\n const orgText = organizationName\r\n ? ` to join <strong>${organizationName}</strong>`\r\n : '';\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>You're Invited</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n 💌\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">You're invited</h1>\r\n <p style=\"${styles.headerSubtitle}\">Join the team</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hello,</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n ${inviterText}${orgText} as a <span style=\"${styles.badge}\">${role}</span>\r\n </p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n Click the button below to accept the invitation and set up your account.\r\n </p>\r\n \r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${inviteUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Accept Invitation\r\n </a>\r\n </div>\r\n \r\n <div style=\"${styles.infoCard}\">\r\n <p style=\"${styles.infoCardTitle}\">📋 Invitation Details</p>\r\n <table style=\"width: 100%; border-collapse: collapse;\">\r\n <tr>\r\n <td style=\"padding: 8px 0; color: ${colors.textMuted}; font-size: 14px;\">Role</td>\r\n <td style=\"padding: 8px 0; color: ${colors.textPrimary}; font-size: 14px; text-align: right; font-weight: 500;\">${role}</td>\r\n </tr>\r\n <tr>\r\n <td style=\"padding: 8px 0; color: ${colors.textMuted}; font-size: 14px; border-top: 1px solid ${colors.divider};\">Expires in</td>\r\n <td style=\"padding: 8px 0; color: ${colors.textPrimary}; font-size: 14px; text-align: right; font-weight: 500; border-top: 1px solid ${colors.divider};\">${expiresIn}</td>\r\n </tr>\r\n </table>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Generic notification email template\r\n */\r\nexport function buildNotificationEmailTemplate(data: {\r\n title: string;\r\n subtitle?: string;\r\n icon?: string;\r\n firstName: string;\r\n message: string;\r\n actionUrl?: string;\r\n actionText?: string;\r\n secondaryActionUrl?: string;\r\n secondaryActionText?: string;\r\n}): string {\r\n const {\r\n title,\r\n subtitle,\r\n icon = '🔔',\r\n firstName,\r\n message,\r\n actionUrl,\r\n actionText,\r\n secondaryActionUrl,\r\n secondaryActionText,\r\n } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>${title}</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n ${icon}\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">${title}</h1>\r\n ${subtitle ? `<p style=\"${styles.headerSubtitle}\">${subtitle}</p>` : ''}\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <div style=\"${styles.paragraph}; white-space: pre-line;\">${message}</div>\r\n \r\n ${\r\n actionUrl\r\n ? `\r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${actionUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n ${actionText || 'Take Action'}\r\n </a>\r\n ${\r\n secondaryActionUrl\r\n ? `\r\n <br><br>\r\n <a href=\"${secondaryActionUrl}\" style=\"${styles.secondaryButton}\" target=\"_blank\">\r\n ${secondaryActionText || 'Learn More'}\r\n </a>\r\n `\r\n : ''\r\n }\r\n </div>\r\n `\r\n : ''\r\n }\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Two-Factor Authentication Code Template\r\n */\r\nexport function buildTwoFactorCodeEmailTemplate(data: {\r\n firstName: string;\r\n code: string;\r\n expiresIn: string;\r\n ipAddress?: string;\r\n location?: string;\r\n}): string {\r\n const { firstName, code, expiresIn, ipAddress, location } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>Your Verification Code</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n 🔑\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">Verification code</h1>\r\n <p style=\"${styles.headerSubtitle}\">Use this code to sign in</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n Enter this verification code to complete your sign-in:\r\n </p>\r\n \r\n <div style=\"\r\n text-align: center;\r\n margin: 32px 0;\r\n padding: 24px;\r\n background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);\r\n border: 1px solid rgba(255,255,255,0.15);\r\n border-radius: 12px;\r\n \">\r\n <p style=\"\r\n margin: 0;\r\n font-size: 36px;\r\n font-weight: 700;\r\n letter-spacing: 8px;\r\n color: ${colors.textPrimary};\r\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;\r\n \">${code}</p>\r\n </div>\r\n \r\n <div style=\"${styles.warningCard}\">\r\n <p style=\"${styles.warningCardTitle}\">⏱ Expires Soon</p>\r\n <p style=\"${styles.warningCardText}\">\r\n This code will expire in <strong>${expiresIn}</strong>. \r\n Never share this code with anyone.\r\n </p>\r\n </div>\r\n \r\n ${\r\n ipAddress || location\r\n ? `\r\n <div style=\"${styles.infoCard}\">\r\n <p style=\"${styles.infoCardTitle}\">📍 Sign-in Attempt</p>\r\n <table style=\"width: 100%; border-collapse: collapse;\">\r\n ${\r\n ipAddress\r\n ? `\r\n <tr>\r\n <td style=\"padding: 6px 0; color: ${colors.textMuted}; font-size: 13px;\">IP Address</td>\r\n <td style=\"padding: 6px 0; color: ${colors.textSecondary}; font-size: 13px; text-align: right; font-family: monospace;\">${ipAddress}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n ${\r\n location\r\n ? `\r\n <tr>\r\n <td style=\"padding: 6px 0; color: ${colors.textMuted}; font-size: 13px;\">Location</td>\r\n <td style=\"padding: 6px 0; color: ${colors.textSecondary}; font-size: 13px; text-align: right;\">${location}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n </table>\r\n </div>\r\n `\r\n : ''\r\n }\r\n \r\n <p style=\"${styles.paragraph}; font-size: 13px; color: ${colors.textMuted};\">\r\n If you didn't attempt to sign in, please secure your account immediately \r\n by changing your password.\r\n </p>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Account Activity Alert Template\r\n */\r\nexport function buildSecurityAlertEmailTemplate(data: {\r\n firstName: string;\r\n alertType:\r\n | 'new_login'\r\n | 'password_changed'\r\n | 'email_changed'\r\n | 'suspicious_activity';\r\n timestamp: string;\r\n ipAddress?: string;\r\n location?: string;\r\n device?: string;\r\n actionUrl?: string;\r\n}): string {\r\n const {\r\n firstName,\r\n alertType,\r\n timestamp,\r\n ipAddress,\r\n location,\r\n device,\r\n actionUrl,\r\n } = data;\r\n\r\n const alertConfig = {\r\n new_login: {\r\n icon: '🔓',\r\n title: 'New sign-in detected',\r\n subtitle: 'A new device signed into your account',\r\n message:\r\n 'We noticed a new sign-in to your account. If this was you, no action is needed.',\r\n },\r\n password_changed: {\r\n icon: '🔐',\r\n title: 'Password changed',\r\n subtitle: 'Your password was recently updated',\r\n message:\r\n 'Your account password was successfully changed. If you made this change, no further action is required.',\r\n },\r\n email_changed: {\r\n icon: '📧',\r\n title: 'Email address changed',\r\n subtitle: 'Your email was recently updated',\r\n message:\r\n 'The email address associated with your account was changed. If you made this change, no further action is required.',\r\n },\r\n suspicious_activity: {\r\n icon: '⚠️',\r\n title: 'Unusual activity detected',\r\n subtitle: 'We noticed something unusual',\r\n message:\r\n 'We detected unusual activity on your account. Please review the details below and secure your account if needed.',\r\n },\r\n };\r\n\r\n const config = alertConfig[alertType];\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>${config.title}</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n ${config.icon}\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">${config.title}</h1>\r\n <p style=\"${styles.headerSubtitle}\">${config.subtitle}</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <p style=\"${styles.paragraph}\">${config.message}</p>\r\n \r\n <div style=\"${styles.infoCard}\">\r\n <p style=\"${styles.infoCardTitle}\">📋 Activity Details</p>\r\n <table style=\"width: 100%; border-collapse: collapse;\">\r\n <tr>\r\n <td style=\"padding: 10px 0; color: ${colors.textMuted}; font-size: 14px; border-bottom: 1px solid ${colors.divider};\">Time</td>\r\n <td style=\"padding: 10px 0; color: ${colors.textPrimary}; font-size: 14px; text-align: right; border-bottom: 1px solid ${colors.divider};\">${timestamp}</td>\r\n </tr>\r\n ${\r\n ipAddress\r\n ? `\r\n <tr>\r\n <td style=\"padding: 10px 0; color: ${colors.textMuted}; font-size: 14px; border-bottom: 1px solid ${colors.divider};\">IP Address</td>\r\n <td style=\"padding: 10px 0; color: ${colors.textSecondary}; font-size: 14px; text-align: right; font-family: monospace; border-bottom: 1px solid ${colors.divider};\">${ipAddress}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n ${\r\n location\r\n ? `\r\n <tr>\r\n <td style=\"padding: 10px 0; color: ${colors.textMuted}; font-size: 14px; border-bottom: 1px solid ${colors.divider};\">Location</td>\r\n <td style=\"padding: 10px 0; color: ${colors.textSecondary}; font-size: 14px; text-align: right; border-bottom: 1px solid ${colors.divider};\">${location}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n ${\r\n device\r\n ? `\r\n <tr>\r\n <td style=\"padding: 10px 0; color: ${colors.textMuted}; font-size: 14px;\">Device</td>\r\n <td style=\"padding: 10px 0; color: ${colors.textSecondary}; font-size: 14px; text-align: right;\">${device}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n </table>\r\n </div>\r\n \r\n ${\r\n alertType === 'suspicious_activity' || alertType === 'new_login'\r\n ? `\r\n <div style=\"${styles.warningCard}\">\r\n <p style=\"${styles.warningCardTitle}\">🛡️ Not You?</p>\r\n <p style=\"${styles.warningCardText}\">\r\n If you don't recognize this activity, we recommend changing your password \r\n immediately and reviewing your account settings.\r\n </p>\r\n </div>\r\n `\r\n : ''\r\n }\r\n \r\n ${\r\n actionUrl\r\n ? `\r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${actionUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Review Account Activity\r\n </a>\r\n </div>\r\n `\r\n : ''\r\n }\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated security alert — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n","import express, { Router, type Router as ExpressRouter } from 'express';\nimport { AuthAdminService } from '../services/auth-admin.service.js';\nimport { requireAuth } from '../middlewares/auth.middleware.js';\n\nexport function createDashboardRouter(options: any): ExpressRouter {\n const r = Router();\n const kc = new AuthAdminService();\n r.use(express.json());\n\n r.post('/', requireAuth(), async (req, res, next) => {\n try {\n const { slug, isPublic, authFlow, orgDomain } = req.body || {};\n const redirectUris = [`https://${slug}.${orgDomain}/*`];\n const created = await kc.createClient(slug, redirectUris, !!isPublic);\n if (authFlow || isPublic != null) {\n await kc.updateClient(created.id, {\n authenticationFlowBindingOverrides: authFlow\n ? { browser: authFlow }\n : undefined,\n registrationAllowed: !!isPublic,\n });\n }\n res.json({ clientId: created.clientId });\n } catch (e) {\n next(e);\n }\n });\n\n return r;\n}\n","import { Router, type Router as ExpressRouter } from 'express';\n\nexport function createEmailRouter(options: any): ExpressRouter {\n const r = Router();\n\n r.get('/verify', (req, res) =>\n res.json({ ok: true, token: req.query.token }),\n );\n\n return r;\n}\n","import { Router, type Router as ExpressRouter } from 'express';\nimport { requireAuth } from '../middlewares/auth.middleware.js';\nimport { ProjectsService } from '../services/projects.service.js';\n\nexport function createProjectsRouter(options: any): ExpressRouter {\n const r = Router();\n const svc = new ProjectsService();\n\n r.post('/create', requireAuth(), async (req, res) => {\n const { org_id, name, description } = req.body || {};\n const p = await svc.create(org_id, name, description);\n res.json(p);\n });\n\n r.get('/:org_id', requireAuth(), async (req, res) => {\n res.json(await svc.list(req.params.org_id));\n });\n\n r.get('/:org_id/:id', requireAuth(), async (req, res) => {\n res.json(await svc.get(req.params.org_id, req.params.id));\n });\n\n r.put('/:org_id/:id', requireAuth(), async (req, res) => {\n res.json(\n await svc.update(req.params.org_id, req.params.id, req.body || {}),\n );\n });\n\n r.delete('/:org_id/:id', requireAuth(), async (req, res) => {\n res.json(await svc.remove(req.params.org_id, req.params.id));\n });\n\n return r;\n}\n","import { randomUUID } from 'crypto';\nimport { ModuleConnection } from '../models/moduleConnection.model.js';\nimport { Project } from '../models/project.model.js';\n\nexport class ProjectsService {\n async create(org_id: string, name: string, description?: string) {\n const _id = randomUUID();\n const secret = randomUUID();\n const p = await Project.create({ _id, org_id, name, description, secret });\n await ModuleConnection.create({\n projectId: _id,\n modules: { data: [], integration: [], storage: [] },\n });\n return p.toObject();\n }\n\n async list(org_id: string) {\n return Project.find({ org_id }).lean();\n }\n\n async get(org_id: string, id: string) {\n return Project.findOne({ org_id, _id: id }).lean();\n }\n\n async update(org_id: string, id: string, patch: any) {\n return Project.findOneAndUpdate(\n { org_id, _id: id },\n { $set: patch },\n { new: true },\n ).lean();\n }\n\n async remove(org_id: string, id: string) {\n await Project.deleteOne({ org_id, _id: id });\n await ModuleConnection.deleteMany({ projectId: id });\n return { ok: true };\n }\n}\n","import mongoose from 'mongoose';\n\nconst ModuleItemSchema = new mongoose.Schema(\n { id: { type: String, required: true } },\n { _id: false },\n);\n\nconst ModuleConnectionSchema = new mongoose.Schema(\n {\n projectId: { type: String, required: true, index: true },\n modules: {\n data: { type: [ModuleItemSchema], default: [] },\n integration: { type: [ModuleItemSchema], default: [] },\n storage: { type: [ModuleItemSchema], default: [] },\n },\n },\n { timestamps: true, collection: 'module_connection' },\n);\n\nexport const ModuleConnection = mongoose.model(\n 'ModuleConnection',\n ModuleConnectionSchema,\n);\n","import mongoose from 'mongoose';\n\nconst ProjectSchema = new mongoose.Schema(\n {\n _id: { type: String, required: true },\n org_id: { type: String, required: true, index: true },\n name: { type: String, required: true },\n description: { type: String },\n secret: { type: String, required: true },\n },\n { timestamps: true, collection: 'projects' },\n);\n\nexport const Project = mongoose.model('Project', ProjectSchema);\n","import type { AuthUser } from 'aaspai-types';\nimport bcrypt from 'bcryptjs';\nimport { randomUUID } from 'crypto';\nimport express, { Request, Response, Router } from 'express';\nimport { requireAuth } from '../../middlewares/auth.middleware.js';\nimport { requireRole } from '../../middlewares/requireRole.js';\nimport { PermissionsModel } from '../../models/permissions.model';\nimport { RolePermissionModel } from '../../models/rolePermission.model';\nimport { OrgUser } from '../../models/user.model.js';\n\ninterface AuthenticatedRequest extends Request {\n user?: AuthUser;\n}\n\nfunction resolveOrgId(req: AuthenticatedRequest): string | null {\n const user = req.user || {};\n const fromUser = (user.orgId as string) || (user.org_id as string) || null;\n\n const fromQuery = (req.query.orgId as string) || null;\n const fromBody = (req.body && (req.body.orgId as string)) || null;\n\n return fromQuery || fromBody || fromUser;\n}\n\nfunction resolveProjectId(req: AuthenticatedRequest): string | null {\n const user = req.user || {};\n const fromUser = (user.projectId as string) || null;\n\n const fromQuery = (req.query.projectId as string) || null;\n const fromBody = (req.body && (req.body.projectId as string)) || null;\n\n return fromQuery || fromBody || fromUser;\n}\n\nexport function createAdminRouter(_options: any = {}): Router {\n const r = Router();\n\n r.use(express.json());\n r.use(express.urlencoded({ extended: true }));\n\n // All routes require platform_admin (for now).\n const adminGuards = [requireAuth(), requireRole('platform_admin')];\n\n r.post(\n '/users',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n const {\n firstName,\n lastName,\n email: emailAddress,\n password,\n emailVerified = false,\n roles = [],\n } = req.body || {};\n\n const projectId = resolveProjectId(req);\n\n try {\n const hashedPassword = password\n ? await bcrypt.hash(password, 10)\n : undefined;\n\n const user = await OrgUser.create({\n id: randomUUID(),\n email: emailAddress,\n orgId: process.env.ORG_ID!,\n firstName,\n lastName,\n projectId,\n emailVerified,\n metadata: [],\n passwordHash: hashedPassword,\n roles,\n });\n\n return res.json({\n id: user.id,\n email: user.email,\n message: 'Verification email sent. Please check your inbox.',\n });\n } catch (err: any) {\n console.error('Create user error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.delete(\n '/users',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const userId = (req?.body?.id || req?.query?.id) as string | undefined;\n\n if (!userId) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'UserId is required (send in body.id or ?id=...)',\n });\n }\n\n const deleted = await OrgUser.findOneAndDelete({ id: userId }).exec();\n\n if (!deleted) {\n return res.status(404).json({\n error: 'NOT_FOUND',\n message: 'User not found or already deleted',\n });\n }\n\n return res.status(200).json({\n ok: true,\n message: 'User deleted successfully',\n deletedUser: {\n id: deleted.id,\n firstName: deleted.firstName,\n email: deleted.email,\n orgId: deleted.orgId,\n },\n });\n } catch (err: any) {\n console.error('Delete user error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.put(\n '/users/:id',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n const userId = req.params.id;\n\n const {\n firstName,\n lastName,\n email: emailAddress,\n password,\n emailVerified,\n roles,\n } = req.body || {};\n\n try {\n const existingUser = await OrgUser.findOne({\n id: userId,\n orgId: process.env.ORG_ID!,\n });\n\n if (!existingUser) {\n return res.status(404).json({ error: 'USER_NOT_FOUND' });\n }\n\n // Only update allowed fields\n if (firstName !== undefined) existingUser.firstName = firstName;\n if (lastName !== undefined) existingUser.lastName = lastName;\n if (emailAddress !== undefined) existingUser.email = emailAddress;\n if (emailVerified !== undefined)\n existingUser.emailVerified = emailVerified;\n if (roles !== undefined) existingUser.roles = roles;\n\n // Password (if provided)\n if (password) {\n existingUser.passwordHash = await bcrypt.hash(password, 10);\n }\n\n // DO NOT UPDATE projectId — keep original value forever\n // existingUser.projectId stays as-is\n\n await existingUser.save();\n\n return res.json({\n id: existingUser.id,\n email: existingUser.email,\n message: 'User updated successfully.',\n });\n } catch (err: any) {\n console.error('Update user error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * GET /permissions\n * List API configs (permissions) for an org (or platform/global when orgId is absent).\n */\n r.get(\n '/permissions',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n\n const filter: any = {};\n if (orgId !== null) {\n filter.orgId = orgId;\n } else {\n filter.orgId = null;\n }\n\n const items = await PermissionsModel.find(filter).lean().exec();\n return res.json(items);\n } catch (err) {\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * POST /permissions\n * Create a new API config (permission).\n * Body:\n * {\n * orgId?: string,\n * key: string,\n * method: string,\n * path: string,\n * module?: string,\n * description?: string,\n * isInternal?: boolean\n * }\n *\n * Also auto-assigns this permission to platform_admin for that org.\n */\n r.post(\n '/permissions',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n\n const {\n key,\n type,\n apiId,\n description,\n isInternal = false,\n } = req.body || {};\n\n if (!key || !type) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'permission key, and permission type are required',\n });\n }\n\n const id = randomUUID();\n\n const permission = await PermissionsModel.create({\n id,\n orgId: orgId ?? null,\n key,\n type,\n apiId,\n description,\n isInternal: !!isInternal,\n });\n\n // Auto-assign to platform_admin role for this org\n await RolePermissionModel.findOneAndUpdate(\n { orgId: orgId ?? null, role: 'platform_admin' },\n { $addToSet: { permissions: key } },\n { upsert: true, new: true },\n ).exec();\n\n return res.status(201).json(permission);\n } catch (err: any) {\n if (err && err.code === 11000) {\n return res.status(409).json({\n error: 'DUPLICATE_PERMISSION',\n message: 'Permission key already exists for this org',\n });\n }\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.put(\n '/permissions/:id',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n const permissionId = req.params.id;\n\n const { key, type, apiId, description, isInternal } = req.body || {};\n // Fetch existing permission\n const existing = await PermissionsModel.findOne({\n id: permissionId,\n orgId: orgId ?? null,\n });\n\n if (!existing) {\n return res.status(404).json({\n error: 'NOT_FOUND',\n message: 'Permission does not exist',\n });\n }\n\n const oldKey = existing.key;\n\n // Apply updates\n if (key !== undefined) existing.key = key;\n if (type !== undefined) existing.type = type;\n if (apiId !== undefined) existing.apiId = apiId;\n if (description !== undefined) existing.description = description;\n if (isInternal !== undefined) existing.isInternal = !!isInternal;\n\n await existing.save();\n\n if (oldKey !== key) {\n // 1. Remove old key\n await RolePermissionModel.updateMany(\n {\n orgId: orgId ?? null,\n permissions: oldKey,\n },\n {\n $pull: { permissions: oldKey },\n },\n );\n\n // 2. Add new key\n await RolePermissionModel.updateMany(\n {\n orgId: orgId ?? null,\n },\n {\n $addToSet: { permissions: key },\n },\n );\n }\n\n return res.json(existing);\n } catch (err: any) {\n if (err && err.code === 11000) {\n return res.status(409).json({\n error: 'DUPLICATE_PERMISSION',\n message: 'Permission key already exists for this org',\n });\n }\n\n console.error('Update permission error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.delete(\n '/permissions',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const permissionId = (req?.body?.id || req?.query?.id) as\n | string\n | undefined;\n\n if (!permissionId) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'Permission id is required (send in body.id or ?id=...)',\n });\n }\n\n // Fetch permission before deletion → we need key + orgId\n const existing = await PermissionsModel.findOne({ id: permissionId });\n if (!existing) {\n return res.status(404).json({\n error: 'NOT_FOUND',\n message: 'Permission not found or already deleted',\n });\n }\n\n const { key, orgId } = existing;\n\n // Delete permission\n await PermissionsModel.deleteOne({ id: permissionId });\n\n // Remove permission key from all roles in THE SAME org\n await RolePermissionModel.updateMany(\n { orgId: orgId ?? null },\n { $pull: { permissions: key } },\n );\n\n return res.status(200).json({\n ok: true,\n message: 'Permission deleted successfully',\n deletedPermission: {\n id: existing.id,\n key: existing.key,\n type: existing.type,\n apiId: existing.apiId,\n description: existing.description,\n isInternal: existing.isInternal,\n orgId: existing.orgId,\n },\n });\n } catch (err: any) {\n console.error('Delete permission error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * GET /roles\n * List role-permissions for org.\n * Query: ?orgId=...\n */\n r.get(\n '/roles',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n\n const filter: any = {};\n if (orgId !== null) {\n filter.orgId = orgId;\n } else {\n filter.orgId = null;\n }\n\n const roles = await RolePermissionModel.find(filter).lean().exec();\n return res.json(roles);\n } catch (err) {\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * POST /roles\n * Create or replace a role's permission set.\n *\n * Body:\n * {\n * orgId?: string,\n * role: string,\n * permissions: string[]\n * }\n */\n r.post(\n '/roles',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n const { role, permissions } = req.body || {};\n\n if (!role || !Array.isArray(permissions)) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'role and permissions[] are required',\n });\n }\n\n const id = randomUUID();\n\n const doc = await RolePermissionModel.findOneAndUpdate(\n { orgId: orgId ?? null, role },\n { $set: { permissions } },\n { upsert: true, new: true },\n ).exec();\n\n return res.status(200).json(doc);\n } catch (err) {\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.put(\n '/roles/:id',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n const roleId = req.params.id;\n\n const { role: newRoleName, permissions } = req.body || {};\n\n if (!newRoleName || !Array.isArray(permissions)) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'role and permissions are required',\n });\n }\n\n // 1. Find the existing role\n const existing = await RolePermissionModel.findById(roleId);\n\n if (!existing) {\n return res.status(404).json({\n error: 'ROLE_NOT_FOUND',\n message: 'Role does not exist',\n });\n }\n\n const oldRoleName = existing.role;\n\n // 2. Update the role document\n existing.role = newRoleName;\n existing.permissions = permissions;\n await existing.save();\n\n if (oldRoleName !== newRoleName) {\n // Step 1 — remove old role from all users\n await OrgUser.updateMany(\n {\n orgId: orgId ?? null,\n roles: oldRoleName,\n },\n {\n $pull: { roles: oldRoleName },\n },\n );\n\n // Step 2 — add new role to users that previously had the old role\n await OrgUser.updateMany(\n {\n orgId: orgId ?? null,\n roles: { $ne: newRoleName }, // avoid duplicates\n },\n {\n $addToSet: { roles: newRoleName },\n },\n );\n }\n\n return res.status(200).json(existing);\n } catch (err) {\n console.error('Update role error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * DELETE /roles\n * Delete a role completely (by MongoDB _id)\n *\n * Body:\n * {\n * \"id\": \"507f1f77bcf86cd799439011\" // MongoDB _id of the RolePermission document\n * }\n *\n * Query alternative (optional):\n * ?id=507f1f77bcf86cd799439011\n */\n r.delete(\n '/roles',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const roleId = (req?.body?.id || req?.query?.id) as string | undefined;\n\n if (!roleId) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'Role _id is required (send in body.id or ?id=...)',\n });\n }\n\n // Optional: validate it's a valid ObjectId\n if (!/^[0-9a-fA-F]{24}$/.test(roleId)) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'Invalid role _id format',\n });\n }\n\n const deleted =\n await RolePermissionModel.findByIdAndDelete(roleId).exec();\n\n if (!deleted) {\n return res.status(404).json({\n error: 'NOT_FOUND',\n message: 'Role not found or already deleted',\n });\n }\n\n return res.status(200).json({\n ok: true,\n message: 'Role deleted successfully',\n deletedRole: {\n _id: deleted._id,\n role: deleted.role,\n orgId: deleted.orgId,\n },\n });\n } catch (err: any) {\n console.error('Delete role error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n return r;\n}\n","import type { AuthXSession } from 'aaspai-types';\r\nimport type { NextFunction, Request, Response } from 'express';\r\nimport { hasAnyRole } from '../core/utils.js';\r\n\r\n/**\r\n * Express middleware to require one or more roles\r\n * @param roles - Array of role names (user must have at least one)\r\n */\r\nexport function requireRole(...roles: string[]) {\r\n return (req: Request, res: Response, next: NextFunction) => {\r\n const user = (req as any).user as AuthXSession | undefined;\r\n\r\n if (!user) {\r\n return res.status(401).json({ error: 'Unauthorized' });\r\n }\r\n\r\n if (!roles || roles.length === 0) {\r\n return next();\r\n }\r\n\r\n if (!hasAnyRole(user, roles)) {\r\n return res.status(403).json({\r\n error: `Requires one of roles: ${roles.join(', ')}`,\r\n required: roles,\r\n userRoles: user.roles,\r\n });\r\n }\r\n\r\n next();\r\n };\r\n}\r\n","import mongoose, { Document, Schema } from 'mongoose';\r\n\r\nexport interface PermissionsDocument extends Document {\r\n id: string; // stable UUID\r\n orgId?: string | null; // null => platform/global\r\n key: string; // permission key, e.g. \"users.read\"\r\n type: string; // \"GET\", \"POST\", ...\r\n apiId?: string; // \"/api/users\"\r\n description?: string;\r\n isInternal: boolean; // internal vs external\r\n createdAt: Date;\r\n updatedAt: Date;\r\n}\r\n\r\nconst PermissionsSchema = new Schema<PermissionsDocument>(\r\n {\r\n id: { type: String, required: true, index: true },\r\n orgId: { type: String, default: null, index: true },\r\n key: { type: String, required: true },\r\n type: { type: String, required: true },\r\n apiId: { type: String, required: false },\r\n description: { type: String },\r\n isInternal: { type: Boolean, default: false },\r\n },\r\n {\r\n timestamps: true,\r\n },\r\n);\r\n\r\n// One permission key per org\r\nPermissionsSchema.index({ orgId: 1, key: 1 }, { unique: true });\r\n\r\nexport const PermissionsModel = mongoose.model<PermissionsDocument>(\r\n 'Permissions',\r\n PermissionsSchema,\r\n 'permissions',\r\n);\r\n","import type { INestApplication } from '@nestjs/common';\nimport { Db } from 'mongodb';\nimport {\n createAdminRouter,\n createAuthRouter,\n createDashboardRouter,\n createEmailRouter,\n createProjectsRouter,\n} from '../express';\nimport type { AuthRouterOptions } from 'aaspai-types';\n\nexport interface NestAuthxOptions {\n routerOptions?: AuthRouterOptions;\n adminDb?: Db;\n adminBasePath?: string;\n authBasePath?: string;\n dashboardBasePath?: string;\n dnsBasePath?: string;\n emailBasePath?: string;\n uploadBasePath?: string;\n projectsBasePath?: string;\n}\n\nexport const nest = {\n mountAuthRouter(\n app: INestApplication,\n options: NestAuthxOptions,\n ): {\n authRouter: any;\n dashboardRouter?: any;\n dnsRouter?: any;\n emailRouter?: any;\n uploadRouter?: any;\n projectsRouter?: any;\n adminRouter?: any;\n } {\n const httpAdapter = app.getHttpAdapter().getInstance();\n\n const authRouter = createAuthRouter(options.routerOptions || {});\n const authPath = options.authBasePath || '/auth';\n httpAdapter.use(authPath, authRouter);\n\n const adminRouter = createAdminRouter(options);\n const adminPath = options.adminBasePath || '/admin';\n httpAdapter.use(adminPath, adminRouter);\n\n const dashboardRouter = createDashboardRouter(options);\n const dashboardPath = options.dashboardBasePath || '/dashboards';\n httpAdapter.use(dashboardPath, dashboardRouter);\n\n const emailRouter = createEmailRouter(options);\n const emailPath = options.emailBasePath || '/email';\n httpAdapter.use(emailPath, emailRouter);\n\n const projectsRouter = createProjectsRouter(options);\n const projectsPath = options.projectsBasePath || '/projects';\n httpAdapter.use(projectsPath, projectsRouter);\n\n return {\n authRouter,\n dashboardRouter,\n emailRouter,\n projectsRouter,\n adminRouter,\n };\n },\n};\n","// src/middlewares/permission.middleware.ts\r\nimport type { AuthXUser } from 'aaspai-types';\r\nimport { NextFunction, Request, Response } from 'express';\r\nimport { RolePermissionModel } from '../models/rolePermission.model';\r\n\r\ninterface AuthenticatedRequest extends Request {\r\n user?: AuthXUser;\r\n}\r\n\r\nexport function requirePermission(permissionKey: string) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction,\r\n ) => {\r\n try {\r\n console.log('INSIDE PERMISSION MIDDLEWARE', permissionKey);\r\n const user = req.user;\r\n\r\n if (!user) {\r\n return res.status(401).json({ error: 'Unauthorized' });\r\n }\r\n\r\n const roles = Array.isArray(user.roles) ? user.roles : [];\r\n\r\n if (!roles.length) {\r\n return res.status(403).json({ error: 'Forbidden', reason: 'NO_ROLES' });\r\n }\r\n\r\n // Global superuser shortcut\r\n if (roles.includes('platform_admin')) {\r\n return next();\r\n }\r\n\r\n // Try to determine org context (you can refine this later)\r\n const orgId =\r\n (user.orgId as string) ||\r\n (user.org_id as string) ||\r\n (user.projectId as string) ||\r\n null;\r\n\r\n const rolePermissions = await RolePermissionModel.find({\r\n orgId,\r\n role: { $in: roles },\r\n })\r\n .lean()\r\n .exec();\r\n\r\n const allowed = new Set<string>();\r\n\r\n for (const rp of rolePermissions) {\r\n if (Array.isArray(rp.permissions)) {\r\n for (const p of rp.permissions) {\r\n allowed.add(p);\r\n }\r\n }\r\n }\r\n\r\n if (!allowed.has(permissionKey)) {\r\n return res.status(403).json({\r\n error: 'Forbidden',\r\n reason: 'MISSING_PERMISSION',\r\n permission: permissionKey,\r\n });\r\n }\r\n\r\n return next();\r\n } catch (err) {\r\n return next(err);\r\n }\r\n };\r\n}\r\n","import type { NextFunction, Request, Response } from 'express';\r\nimport { hasPermission } from '../core/utils.js';\r\nimport type { AuthXSession } from 'aaspai-types';\r\n\r\n/**\r\n * Express middleware to require a specific permission\r\n * @param permission - Permission string (e.g., 'projects.create')\r\n */\r\nexport function requirePermission(permission: string) {\r\n return (req: Request, res: Response, next: NextFunction) => {\r\n const user = (req as any).user as AuthXSession | undefined;\r\n\r\n if (!user) {\r\n return res.status(401).json({ error: 'Unauthorized' });\r\n }\r\n\r\n if (!permission) {\r\n return next();\r\n }\r\n\r\n if (!hasPermission(user, permission)) {\r\n return res.status(403).json({\r\n error: 'Forbidden',\r\n reason: 'MISSING_PERMISSION',\r\n permission,\r\n userPermissions: user.permissions,\r\n });\r\n }\r\n\r\n next();\r\n };\r\n}\r\n\r\n","import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';\nimport { randomUUID } from 'crypto';\nimport { config } from '../config/index.js';\n\nexport class UploadsService {\n private s3: S3Client;\n\n constructor() {\n this.s3 = new S3Client({\n region: process.env.AWS_REGION!,\n credentials: {\n accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n },\n });\n }\n\n async uploadPublicImage(buffer: Buffer, mimetype: string, ext: string) {\n const key = `${randomUUID()}.${ext}`;\n await this.s3.send(\n new PutObjectCommand({\n Bucket: process.env.AWS_S3_BUCKET!,\n Key: key,\n Body: buffer,\n ACL: 'public-read',\n ContentType: mimetype,\n }),\n );\n return `https://${process.env.AWS_S3_BUCKET!}.s3.${process.env.AWS_REGION!}.amazonaws.com/${key}`;\n }\n}\n","import {\r\n ExecutionContext,\r\n ForbiddenException,\r\n Injectable,\r\n UnauthorizedException,\r\n} from '@nestjs/common';\r\nimport { Reflector } from '@nestjs/core';\r\nimport { AuthGuard } from '@nestjs/passport';\r\nimport type { AuthXSession } from 'aaspai-types';\r\nimport { hasAnyPermission, hasAnyRole } from '../core/utils.js';\r\nimport { PERMISSIONS_KEY } from './decorators/permissions.decorator.js';\r\nimport { ROLES_KEY } from './decorators/roles.decorator.js';\r\n\r\n/**\r\n * AuthX Guard for NestJS\r\n * Extends Passport's AuthGuard to authenticate requests using AuthXStrategy,\r\n * then validates role/permission requirements\r\n */\r\n@Injectable()\r\nexport class AuthXGuard extends AuthGuard('authx') {\r\n private readonly reflector = new Reflector();\r\n\r\n constructor() {\r\n super();\r\n }\r\n\r\n /**\r\n * Override handleRequest to convert Passport errors to NestJS exceptions\r\n * This prevents the \"Right-hand side of 'instanceof' is not an object\" error\r\n */\r\n handleRequest(err: any, user: any, info: any, context: any): any {\r\n // If there's an error or no user, throw UnauthorizedException\r\n if (err || !user) {\r\n const message =\r\n err?.message || info?.message || 'Authentication required';\r\n throw new UnauthorizedException(message);\r\n }\r\n return user;\r\n }\r\n\r\n async canActivate(context: ExecutionContext): Promise<boolean> {\r\n try {\r\n // First, trigger Passport authentication (this will set request.user via AuthXStrategy)\r\n // Wrap in try-catch to handle any errors from Passport\r\n let authenticated: boolean;\r\n try {\r\n authenticated = (await super.canActivate(context)) as boolean;\r\n } catch (passportError: any) {\r\n // Convert any Passport errors to UnauthorizedException\r\n const message = passportError?.message || 'Authentication required';\r\n throw new UnauthorizedException(message);\r\n }\r\n\r\n if (!authenticated) {\r\n throw new UnauthorizedException('Authentication required');\r\n }\r\n\r\n const request = context.switchToHttp().getRequest();\r\n const user = (request as any).user as AuthXSession | undefined;\r\n\r\n // Check if user is authenticated (should be set by Passport at this point)\r\n if (!user) {\r\n throw new UnauthorizedException('Authentication required');\r\n }\r\n\r\n // Get required roles from metadata\r\n const requiredRoles = this.reflector.getAllAndOverride<string[]>(\r\n ROLES_KEY,\r\n [context.getHandler(), context.getClass()],\r\n );\r\n \r\n // Get required permissions from metadata\r\n const requiredPermissions = this.reflector.getAllAndOverride<string[]>(\r\n PERMISSIONS_KEY,\r\n [context.getHandler(), context.getClass()],\r\n );\r\n\r\n // Check roles if specified\r\n if (requiredRoles && requiredRoles.length > 0) {\r\n if (!hasAnyRole(user, requiredRoles)) {\r\n throw new ForbiddenException(\r\n `Requires one of roles: ${requiredRoles.join(', ')}`,\r\n );\r\n }\r\n }\r\n\r\n // Check permissions if specified\r\n if (requiredPermissions && requiredPermissions.length > 0) {\r\n if (!hasAnyPermission(user, requiredPermissions)) {\r\n throw new ForbiddenException(\r\n `Requires one of permissions: ${requiredPermissions.join(', ')}`,\r\n );\r\n }\r\n }\r\n\r\n return true;\r\n } catch (error) {\r\n // If it's already a NestJS exception, rethrow it\r\n if (\r\n error instanceof UnauthorizedException ||\r\n error instanceof ForbiddenException\r\n ) {\r\n throw error;\r\n }\r\n // Otherwise, wrap it in UnauthorizedException\r\n const errorMessage =\r\n error instanceof Error ? error.message : String(error);\r\n throw new UnauthorizedException(errorMessage || 'Authentication failed');\r\n }\r\n }\r\n}\r\n","import { SetMetadata } from '@nestjs/common';\r\n\r\nexport const PERMISSIONS_KEY = 'permissions';\r\n\r\n/**\r\n * Decorator to specify required permissions for a route handler or controller\r\n * @param permissions - Array of permission strings (user must have at least one)\r\n */\r\nexport const Permissions = (...permissions: string[]) =>\r\n SetMetadata(PERMISSIONS_KEY, permissions);\r\n\r\n","import { SetMetadata } from '@nestjs/common';\r\n\r\nexport const ROLES_KEY = 'roles';\r\n\r\n/**\r\n * Decorator to specify required roles for a route handler or controller\r\n * @param roles - Array of role names (user must have at least one)\r\n */\r\nexport const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);\r\n\r\n","import { createParamDecorator, ExecutionContext } from '@nestjs/common';\r\nimport type { AuthXSession } from 'aaspai-types';\r\n\r\n/**\r\n * Decorator to inject the AuthXSession into a route handler parameter\r\n * Usage: @AuthXSessionDecorator() session: AuthXSession\r\n */\r\nexport const AuthXSessionDecorator = createParamDecorator(\r\n (data: unknown, ctx: ExecutionContext): AuthXSession | null => {\r\n const request = ctx.switchToHttp().getRequest();\r\n return (request as any).user || null;\r\n },\r\n);\r\n","import type { Request } from 'express';\r\nimport { Strategy } from 'passport';\r\nimport { OrgUser } from '../models/user.model.js';\r\nimport { buildSession } from '../core/session.js';\r\nimport { extractToken } from '../utils/extract.js';\r\nimport { verifyJwt } from '../utils/jwt.js';\r\n\r\n/**\r\n * AuthX Passport Strategy\r\n * Custom Passport strategy that validates JWT tokens using JWKS\r\n * and builds AuthXSession\r\n */\r\nexport class AuthXStrategy extends Strategy {\r\n name = 'authx';\r\n\r\n async authenticate(req: Request) {\r\n try {\r\n const apiKey = req.headers['x-api-key'] as string | undefined;\r\n const userId = req.headers['x-user-id'] as string | undefined;\r\n\r\n if (apiKey) {\r\n if (apiKey !== process.env.SERVER_API_KEY) {\r\n return this.fail({ message: 'Invalid API key' }, 401);\r\n }\r\n if (!userId) {\r\n return this.fail({ message: 'User Id is required' }, 401);\r\n }\r\n\r\n const user = await OrgUser.findOne({\r\n id: userId,\r\n orgId: process.env.ORG_ID || null,\r\n });\r\n\r\n if (!user) {\r\n return this.fail({ message: 'User not found' }, 401);\r\n }\r\n\r\n // Build a synthetic session\r\n const session = buildSession(user);\r\n\r\n // Attach to request\r\n (req as any).user = session;\r\n\r\n return this.success(session);\r\n } else {\r\n \r\n // Extract token from request using existing utility\r\n const token = extractToken(req);\r\n\r\n if (!token) {\r\n return this.fail({ message: 'Missing token' }, 401);\r\n }\r\n\r\n // Verify JWT asynchronously\r\n verifyJwt(token)\r\n .then((claims) => {\r\n // Build canonical session\r\n const session = buildSession(claims);\r\n\r\n // Attach to request\r\n (req as any).user = session;\r\n\r\n // Success\r\n return this.success(session);\r\n })\r\n .catch((error: any) => {\r\n // Verification failed\r\n return this.fail(\r\n { message: error?.message || 'Unauthorized' },\r\n 401,\r\n );\r\n });\r\n }\r\n } catch (error: any) {\r\n return this.fail({ message: error?.message || 'Unauthorized' }, 401);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Factory function to create AuthXStrategy instance\r\n */\r\nexport function createAuthXStrategy() {\r\n return new AuthXStrategy();\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,OAAOA,aAAY;AACnB,SAAS,kBAAkB;AAC3B,OAAO;AAAA,EAEL;AAAA,OAEK;AACP,OAAOC,UAAS;;;ACAT,SAAS,aAA0B;AACxC,SAAO;AAAA,IACL,WAAW,QAAQ,IAAI;AAAA,IACvB,OAAO,QAAQ,IAAI;AAAA,IACnB,OAAO;AAAA,MACL,MAAM,QAAQ,IAAI,cAAc;AAAA,MAChC,MAAM,QAAQ,IAAI,aAAa,OAAO,QAAQ,IAAI,UAAU,IAAI;AAAA,MAChE,SAAS,QAAQ,IAAI,gBAAgB,aAAa;AAAA,MAClD,MAAM,QAAQ,IAAI;AAAA,MAClB,MAAM,QAAQ,IAAI;AAAA,MAClB,MAAM,QAAQ,IAAI;AAAA,MAClB,WAAW,QAAQ,IAAI;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,MACP,QAAQ,QAAQ,IAAI;AAAA,MACpB,SAAS,QAAQ,IAAI,iBAAiB,YAAY;AAAA,MAClD,aAAa,KAAK,KAAK,KAAK;AAAA,MAC5B,cAAc,IAAI,KAAK,KAAK,KAAK;AAAA,IACnC;AAAA,IACA,MAAM;AAAA,MACJ,WAAW,QAAQ,IAAI;AAAA,IACzB;AAAA,IACA,KAAK;AAAA,MACH,QAAQ,QAAQ,IAAI;AAAA,MACpB,QAAQ,QAAQ,IAAI;AAAA,MACpB,aAAa,QAAQ,IAAI;AAAA,MACzB,iBAAiB,QAAQ,IAAI;AAAA,IAC/B;AAAA,EACF;AACF;;;AClBO,IAAM,SAAsB,WAAW;AAEvC,SAAS,eACd,YAAsC,CAAC,GAC1B;AACb,SAAO,UAAU,QAAQ,SAAS;AACpC;AAMA,SAAS,UACP,QACA,QACG;AACH,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,aAAW,OAAO,OAAO,KAAK,MAAM,GAAkB;AACpD,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,UAAU,OAAW;AAEzB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,MAAC,OAAe,GAAG,IAAI,CAAC,GAAG,KAAK;AAChC;AAAA,IACF;AAEA,QAAI,cAAc,KAAK,GAAG;AACxB,UAAI,CAAC,cAAc,OAAO,GAAG,CAAC,GAAG;AAC/B,QAAC,OAAe,GAAG,IAAI,CAAC;AAAA,MAC1B;AACA,gBAAU,OAAO,GAAG,GAA0B,KAAY;AAC1D;AAAA,IACF;AAEA,IAAC,OAAe,GAAG,IAAI;AAAA,EACzB;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,OAA0C;AAC/D,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;AC1DO,SAAS,QACd,SACA,MACS;AACT,MAAI,CAAC,WAAW,CAAC,QAAQ,MAAO,QAAO;AACvC,SAAO,QAAQ,MAAM,SAAS,IAAI;AACpC;AAEO,SAAS,6BACd,QACe;AACf,QAAM,OAAsB;AAAA,IAC1B,QAAQ,OAAO,UAAU;AAAA,IACzB,UAAU,OAAO,YAAY;AAAA,IAC7B,MAAM,OAAO,QAAQ;AAAA,IACrB,QAAQ,OAAO;AAAA,EACjB;AACA,MAAI,OAAO,OAAQ,MAAK,SAAS,OAAO;AACxC,SAAO;AACT;AAEO,SAAS,wBAAwB,QAAyC;AAC/E,QAAM,OAAsB;AAAA,IAC1B,UAAU;AAAA;AAAA,IACV,QAAQ,OAAO,UAAU;AAAA,IACzB,UAAU,OAAO,YAAY;AAAA,IAC7B,MAAM,OAAO,QAAQ;AAAA,EACvB;AAEA,MAAI,OAAO,QAAQ;AACjB,SAAK,SAAS,OAAO;AAAA,EACvB;AAEA,SAAO;AACT;AAMO,SAAS,WACd,SACA,OACS;AACT,MACE,CAAC,WACD,CAAC,QAAQ,SACT,CAAC,MAAM,QAAQ,KAAK,KACpB,MAAM,WAAW,GACjB;AACA,WAAO;AAAA,EACT;AACA,SAAO,MAAM,KAAK,CAAC,SAAS,QAAQ,MAAM,SAAS,IAAI,CAAC;AAC1D;AAKO,SAAS,YACd,SACA,OACS;AACT,MACE,CAAC,WACD,CAAC,QAAQ,SACT,CAAC,MAAM,QAAQ,KAAK,KACpB,MAAM,WAAW,GACjB;AACA,WAAO;AAAA,EACT;AACA,SAAO,MAAM,MAAM,CAAC,SAAS,QAAQ,MAAM,SAAS,IAAI,CAAC;AAC3D;AAKO,SAAS,cACd,SACA,YACS;AACT,MAAI,CAAC,WAAW,CAAC,QAAQ,YAAa,QAAO;AAC7C,SAAO,QAAQ,YAAY,SAAS,UAAU;AAChD;AAKO,SAAS,iBACd,SACA,aACS;AACT,MACE,CAAC,WACD,CAAC,QAAQ,eACT,CAAC,MAAM,QAAQ,WAAW,KAC1B,YAAY,WAAW,GACvB;AACA,WAAO;AAAA,EACT;AACA,SAAO,YAAY,KAAK,CAAC,SAAS,QAAQ,YAAY,SAAS,IAAI,CAAC;AACtE;AAKO,SAAS,kBACd,SACA,aACS;AACT,MACE,CAAC,WACD,CAAC,QAAQ,eACT,CAAC,MAAM,QAAQ,WAAW,KAC1B,YAAY,WAAW,GACvB;AACA,WAAO;AAAA,EACT;AACA,SAAO,YAAY,MAAM,CAAC,SAAS,QAAQ,YAAY,SAAS,IAAI,CAAC;AACvE;;;AC5HO,IAAM,iBAAiB;AAAA,EAC5B;AAAA,IACE,MAAM;AAAA,IACN,aAAa,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,aAAa,CAAC;AAAA,EAChB;AACF;AAOO,SAAS,uBAAuB,OAA2B;AAChE,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AAC/C,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,gBAAgB,oBAAI,IAAY;AAEtC,aAAW,YAAY,OAAO;AAC5B,UAAM,aAAa,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AACjE,QAAI,cAAc,MAAM,QAAQ,WAAW,WAAW,GAAG;AACvD,iBAAW,QAAQ,WAAW,aAAa;AACzC,sBAAc,IAAI,IAAI;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,aAAa;AACjC;;;AC7BO,SAAS,aAAa,SAA4B;AAEvD,QAAM,SAAS,SAAS,OAAO,SAAS,UAAU,SAAS,MAAM;AAGjE,QAAM,QAAQ,SAAS,SAAS,SAAS,iBAAiB;AAG1D,QAAM,QACJ,SAAS,cAAc,SACvB,SAAS,SACT,UAAU,gBAAgB,MACzB,MAAM,QAAQ,SAAS,IAAI,IAAI,QAAQ,OAAO,CAAC,MAChD,CAAC;AAGH,QAAM,kBAAkB,MAAM,QAAQ,KAAK,IACvC,MAAM,IAAI,MAAM,EAAE,OAAO,OAAO,IAChC,CAAC;AAGL,QAAM,cAAc,uBAAuB,eAAe;AAG1D,QAAM,UAAwB;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP;AAAA,EACF;AAGA,MAAI,SAAS,UAAW,SAAQ,YAAY,QAAQ;AACpD,MAAI,SAAS,SAAU,SAAQ,WAAW,QAAQ;AAClD,MAAI,SAAS,UAAW,SAAQ,YAAY,QAAQ;AACpD,MAAI,SAAS,MAAO,SAAQ,QAAQ,QAAQ;AAC5C,MAAI,SAAS,OAAQ,SAAQ,SAAS,QAAQ;AAC9C,MAAI,SAAS,SAAU,SAAQ,WAAW,QAAQ;AAClD,MAAI,SAAS,UAAW,SAAQ,YAAY,QAAQ;AACpD,MAAI,SAAS,SAAU,SAAQ,WAAW,QAAQ;AAGlD,SAAO,KAAK,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,QAAQ;AAC1C,QACE,CAAC;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,SAAS,GAAG,GACd;AACA,cAAQ,GAAG,IAAI,QAAQ,GAAG;AAAA,IAC5B;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;ACxEA,OAAO,YAAsB,cAAc;AAU3C,IAAM,uBAAuB,IAAI;AAAA,EAC/B;AAAA,IACE,OAAO,EAAE,MAAM,QAAQ,SAAS,MAAM,OAAO,KAAK;AAAA,IAClD,MAAM,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACrC,aAAa,EAAE,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE;AAAA,EAC7C;AAAA,EACA;AAAA,IACE,YAAY;AAAA,EACd;AACF;AAEA,qBAAqB,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,EAAE,QAAQ,KAAK,CAAC;AAE3D,IAAM,sBAAsB,SAAS;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AACF;;;AC5BA,OAAOC,eAAc;AACrB,SAAS,MAAM,YAAY;AAE3B,IAAM,iBAAiB,IAAIA,UAAS;AAAA,EAClC;AAAA,IACE,KAAK,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACpC,OAAO,EAAE,MAAMA,UAAS,OAAO,MAAM,OAAO,UAAU,KAAK;AAAA,EAC7D;AAAA,EACA,EAAE,KAAK,MAAM;AACf;AAEA,IAAM,gBAAgB,IAAIA,UAAS;AAAA,EACjC;AAAA,IACE,IAAI,EAAE,MAAM,QAAQ,SAAS,KAAK,GAAG,OAAO,MAAM,QAAQ,KAAK;AAAA,IAC/D,OAAO,EAAE,MAAM,QAAQ,UAAU,MAAM,QAAQ,KAAK;AAAA,IACpD,WAAW,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IAC1C,UAAU,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACzC,OAAO,EAAE,MAAM,OAAO;AAAA,IACtB,WAAW,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE;AAAA,IACrC,eAAe,EAAE,MAAM,SAAS,SAAS,MAAM;AAAA,IAC/C,eAAe,EAAE,MAAM,CAAC,IAAI,GAAG,SAAS,CAAC,EAAE;AAAA,IAC3C,mBAAmB,EAAE,MAAM,KAAK;AAAA,IAChC,UAAU,EAAE,MAAM,CAAC,cAAc,GAAG,SAAS,CAAC,EAAE;AAAA,IAChD,cAAc,EAAE,MAAM,OAAO;AAAA,EAC/B;AAAA,EACA,EAAE,YAAY,MAAM,YAAY,QAAQ;AAC1C;AAEO,IAAM,UAAUA,UAAS,MAAM,WAAW,aAAa;;;AC7B9D,SAAS,SAAS,mBAAmB;AAG9B,SAAS,aACd,KACA,MAKA;AACA,QAAM,cAAc,MAAM,eAAe,CAAC,iBAAiB,OAAO;AAClE,QAAM,cAAc,MAAM,eAAe,CAAC,gBAAgB,eAAe;AACzE,QAAM,aAAa,MAAM,cAAc,CAAC,gBAAgB,OAAO;AAE/D,aAAW,KAAK,aAAa;AAC3B,UAAM,MAAO,IAAI,QAAgB,CAAC;AAClC,QAAI,KAAK;AACP,YAAM,QAAQ,IAAI,YAAY;AAC9B,UAAI,MAAM,WAAW,SAAS,EAAG,QAAO,IAAI,MAAM,CAAC,EAAE,KAAK;AAC1D,UAAI,CAAC,IAAI,SAAS,GAAG,EAAG,QAAO,IAAI,KAAK;AAAA,IAC1C;AAAA,EACF;AAEA,QAAM,KAAK,IAAI,QAAQ,QAAQ;AAE/B,MAAI,OAAO,OAAO,UAAU;AAC1B,UAAM,SAAS,YAAY,EAAE;AAC7B,eAAW,KAAK,YAAa,KAAI,OAAO,CAAC,EAAG,QAAO,OAAO,CAAC;AAAA,EAC7D;AAEA,aAAW,KAAK,YAAY;AAC1B,UAAM,IAAK,IAAI,QAAgB,CAAC;AAChC,QAAI,OAAO,MAAM,YAAY,EAAG,QAAO;AAAA,EACzC;AAEA,SAAO;AACT;AAEO,SAAS,cAAc,KAAc;AAC1C,QAAM,KAAK,IAAI,QAAQ,QAAQ;AAE/B,MAAI,OAAO,OAAO,UAAU;AAC1B,QAAI;AACF,YAAM,SAAS,YAAY,EAAE;AAC7B,aAAO,OAAO,WAAW,KAAK;AAAA,IAChC,QAAQ;AAAA,IAAC;AAAA,EACX;AAEA,SAAO;AACT;;;AClDA,OAAO,SAAS;AAET,SAAS,UAAU,OAA6B;AACrD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI;AAAA,MACF;AAAA,MACA,QAAQ,IAAI;AAAA;AAAA,MACZ;AAAA,QACE,YAAY,CAAC,OAAO;AAAA;AAAA,QACpB,UAAU;AAAA;AAAA,MACZ;AAAA,MACA,CAAC,KAAK,YAAY;AAChB,YAAI,KAAK;AACP,iBAAO,GAAG;AAAA,QACZ,OAAO;AACL,kBAAQ,OAAO;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACZA,eAAe,qBAAqB,SAAsC;AACxE,QAAM,QAAQ,MAAM,QAAQ,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC9D,MAAI,CAAC,MAAM,OAAQ;AAEnB,QAAM,cAAc,oBAAI,IAAmB;AAC3C,MAAI,QAAQ,MAAO,aAAY,IAAI,QAAQ,KAAK;AAChD,MAAI,QAAQ,OAAQ,aAAY,IAAI,QAAQ,MAAM;AAClD,MAAI,QAAQ,UAAW,aAAY,IAAI,QAAQ,SAAS;AACxD,cAAY,IAAI,IAAI;AAEpB,QAAM,OAAO,MAAM,oBAAoB,KAAK;AAAA,IAC1C,OAAO,EAAE,KAAK,MAAM,KAAK,WAAW,EAAE;AAAA,IACtC,MAAM,EAAE,KAAK,MAAM;AAAA,EACrB,CAAC,EACE,KAAK,EACL,KAAK;AAER,QAAM,UAAU,oBAAI,IAAY;AAChC,aAAW,OAAO,MAAM;AACtB,eAAW,QAAQ,IAAI,eAAe,CAAC,GAAG;AACxC,UAAI,KAAM,SAAQ,IAAI,IAAI;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,WAAW,MAAM,QAAQ,QAAQ,WAAW,IAC9C,QAAQ,cACR,CAAC;AACL,UAAQ,cAAc,MAAM,KAAK,oBAAI,IAAI,CAAC,GAAG,UAAU,GAAG,OAAO,CAAC,CAAC;AACrE;AAMO,SAAS,cAAc;AAC5B,SAAO,OAAO,KAAc,KAAe,SAAuB;AAChE,QAAI;AAEF,YAAM,SAAU,IAAI,QAAQ,WAAW,KAAK,IAAI,QAAQ,UAAU;AAIlE,YAAM,SAAU,IAAI,QAAQ,WAAW,KAAK,IAAI,QAAQ,UAAU;AAIlE,UAAI,QAAQ;AACV,YAAI,WAAW,QAAQ,IAAI,gBAAgB;AACzC,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AAAA,QAC1D;AAEA,YAAI,CAAC,QAAQ;AACX,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,sBAAsB,CAAC;AAAA,QAC9D;AAGA,cAAM,OAAO,MAAM,QAAQ,QAAQ,EAAE,IAAI,OAAO,CAAC,EAAE,KAAK;AAExD,YAAI,CAAC,MAAM;AACT,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,QACzD;AAEA,cAAM,UAAU,aAAa;AAAA,UAC3B,KAAK,KAAK,GAAG,SAAS;AAAA,UACtB,OAAO,KAAK;AAAA,UACZ,WAAW,KAAK;AAAA,UAChB,UAAU,KAAK;AAAA,UACf,UAAU,KAAK,YAAY,CAAC;AAAA,UAC5B,OAAO,KAAK,SAAS,CAAC;AAAA,UACtB,OAAO,KAAK;AAAA,UACZ,QAAQ,KAAK;AAAA,UACb,WAAW,KAAK;AAAA,UAChB,WAAW,KAAK;AAAA,QAClB,CAAC;AAED,gBAAQ,WAAW;AACnB,gBAAQ,YAAY,cAAc,GAAG,KAAK,KAAK,aAAa;AAC5D,cAAM,qBAAqB,OAAO;AAClC,QAAC,IAAY,OAAO;AACpB,eAAO,KAAK;AAAA,MACd,OAAO;AAEL,cAAM,QAAQ,aAAa,GAAG;AAC9B,YAAI,CAAC,OAAO;AACV,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAAA,QACxD;AACA,cAAM,SAAS,MAAM,UAAU,KAAK;AACpC,cAAM,UAAU,aAAa,MAAM;AAEnC,cAAM,MAAM,cAAc,GAAG;AAC7B,YAAI,IAAK,SAAQ,YAAY;AAC7B,cAAM,qBAAqB,OAAO;AAGlC,QAAC,IAAY,OAAO;AACpB,aAAK;AAAA,MACP;AAAA,IACF,SAAS,GAAQ;AACf,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,GAAG,WAAW,eAAe,CAAC;AAAA,IAC9D;AAAA,EACF;AACF;AAOO,SAAS,UAAU,QAAkB,CAAC,GAAG;AAC9C,SAAO,CAAC,KAAc,KAAe,SAAuB;AAC1D,QAAI,CAAC,SAAS,MAAM,WAAW,EAAG,QAAO,KAAK;AAC9C,UAAM,OAAQ,IAAY;AAC1B,QAAI,CAAC,MAAM;AACT,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,CAAC;AAAA,IACvD;AACA,UAAM,OAAO,IAAI,KAAa,KAAK,SAAS,CAAC,GAAG,IAAI,MAAM,CAAC;AAC3D,UAAM,KAAK,MAAM,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;AACxC,QAAI,CAAC,IAAI;AACP,aAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,OAAO,0BAA0B,MAAM,KAAK,IAAI,CAAC,GAAG,CAAC;AAAA,IACjE;AACA,SAAK;AAAA,EACP;AACF;;;ACpIO,SAAS,QAAQ,GAAW;AACjC,SAAO,6BAA6B,KAAK,CAAC;AAC5C;AAEO,SAAS,iBAAiB,GAAW;AAC1C,SACE,OAAO,MAAM,YACb,EAAE,UAAU,KACZ,QAAQ,KAAK,CAAC,KACd,eAAe,KAAK,CAAC;AAEzB;AAEO,SAAS,eAAe,KAAU,KAAU,MAAW;AAC5D,QAAM,EAAE,WAAW,UAAU,OAAO,UAAU,WAAW,SAAS,IAChE,IAAI,QAAQ,CAAC;AACf,MAAI,CAAC,aAAa,CAAC;AACjB,WAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,8BAA8B,CAAC;AACtE,MAAI,CAAC,QAAQ,KAAK,EAAG,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC3E,MAAI,CAAC,iBAAiB,QAAQ;AAC5B,WAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AACxD,MAAI,CAAC,UAAW,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,qBAAqB,CAAC;AAC3E,MAAI,CAAC,MAAM,QAAQ,QAAQ;AACzB,WAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,yBAAyB,CAAC;AACjE,OAAK;AACP;AAEO,SAAS,cAAc,KAAU,KAAU,MAAW;AAC3D,QAAM,EAAE,OAAO,SAAS,IAAI,IAAI,QAAQ,CAAC;AACzC,MAAI,CAAC,QAAQ,KAAK,EAAG,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC3E,MAAI,OAAO,aAAa;AACtB,WAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AAC5D,OAAK;AACP;AAEO,SAAS,sBAAsB,KAAU,KAAU,MAAW;AACnE,QAAM,EAAE,OAAO,YAAY,IAAI,IAAI,QAAQ,CAAC;AAC5C,MAAI,CAAC,MAAO,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AACnE,MAAI,CAAC,iBAAiB,WAAW;AAC/B,WAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AACxD,OAAK;AACP;AAEO,SAAS,oBAAoB,KAAU,KAAU,MAAW;AACjE,QAAM,EAAE,MAAM,IAAI,IAAI,QAAQ,CAAC;AAC/B,MAAI,CAAC,QAAQ,KAAK,EAAG,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC3E,OAAK;AACP;AAEO,SAAS,mBAAmB,KAAU,KAAU,MAAW;AAChE,QAAM,EAAE,OAAO,KAAK,IAAI,IAAI,QAAQ,CAAC;AACrC,MAAI,CAAC,QAAQ,KAAK,EAAG,QAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC3E,MAAI,CAAC,CAAC,iBAAiB,WAAW,EAAE,SAAS,IAAI;AAC/C,WAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,CAAC;AACvD,OAAK;AACP;;;ACvDA,OAAOC,eAAc;AAErB,IAAM,eAAe,IAAIA,UAAS;AAAA,EAChC;AAAA,IACE,IAAI,EAAE,MAAM,QAAQ,UAAU,MAAM,OAAO,KAAK;AAAA,IAChD,OAAO,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACtC,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,MAAM,CAAC,iBAAiB,WAAW;AAAA,MACnC,UAAU;AAAA,IACZ;AAAA,IACA,WAAW,EAAE,MAAM,OAAO;AAAA,IAC1B,QAAQ,EAAE,MAAM,OAAO;AAAA,IACvB,QAAQ,EAAE,MAAM,SAAS,SAAS,MAAM;AAAA,IACxC,QAAQ,EAAE,MAAM,KAAK;AAAA,IACrB,WAAW,EAAE,MAAM,KAAK;AAAA,IACxB,WAAW,EAAE,MAAM,SAAS,SAAS,MAAM;AAAA,EAC7C;AAAA,EACA,EAAE,YAAY,MAAM,YAAY,UAAU;AAC5C;AAEO,IAAM,SAASA,UAAS,MAAM,UAAU,YAAY;;;ACrB3D,OAAO,YAAY;AACnB,OAAOC,UAAS;;;ACDhB,OAAOC,aAA6B,UAAAC,eAAc;AAWlD,IAAM,eAAe,IAAIA;AAAA,EACvB;AAAA,IACE,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,IAEA,cAAc;AAAA,MACZ,MAAM,CAAC,MAAM;AAAA,MACb,SAAS,CAAC;AAAA,IACZ;AAAA,IAEA,cAAc;AAAA,MACZ,MAAM;AAAA,MACN,SAAS;AAAA,IACX;AAAA;AAAA,IAGA,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,UAAU,WAAyB;AACjC,eAAO,CAAC,KAAK;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,YAAY;AAAA,EACd;AACF;AAEO,IAAM,cACXD,UAAS,OAAO,UAAUA,UAAS,MAAe,UAAU,YAAY;;;ADrCnE,IAAM,mBAAN,MAAuB;AAAA,EACpB;AAAA,EAER,MAAM,gBAAgB;AACpB,WAAO,KAAK,iBAAiB;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,UACA,eAAyB,CAAC,GAC1B,eAAe,OACf;AACA,UAAM,SAAS,MAAM,YAAY,OAAO;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAAa,IAAY,OAAY;AACzC,UAAM,YAAY,kBAAkB,IAAI,KAAK;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBACJ,QACA,QACA;AACA,WAAO,QAAQ,KAAK,UAAU,CAAC,CAAC;AAAA,EAClC;AAAA,EAEA,MAAM,YAAY,QAAgB;AAChC,WAAO,QAAQ,QAAQ,EAAE,IAAI,OAAO,CAAC;AAAA,EACvC;AAAA,EAEA,MAAM,oBAAoB,QAAgB;AACxC,UAAM,OAAY,MAAM,QAAQ,QAAQ,EAAE,IAAI,OAAO,CAAC;AACtD,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,kBAAkB,SASrB;AACD,UAAM,iBAAiB,QAAQ,cAAc,CAAC,GAAG,QAC7C,MAAM,OAAO,KAAK,QAAQ,YAAY,CAAC,EAAE,OAAO,EAAE,IAClD;AAEJ,UAAM,OAAO,MAAM,QAAQ,OAAO;AAAA,MAChC,IAAI,OAAO,WAAW;AAAA,MACtB,OAAO,QAAQ;AAAA,MACf,WAAW,QAAQ;AAAA,MACnB,UAAU,QAAQ;AAAA,MAClB,WAAW,QAAQ;AAAA,MACnB,eAAe,QAAQ,iBAAiB;AAAA,MACxC,cAAc;AAAA,MACd,UAAU,QAAQ,YAAY,CAAC;AAAA,IACjC,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,gBAAgB,QAAgB,UAAkB;AACtD,UAAM,OAAO,MAAM,oBAAoB,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjE,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,mBAAmB,QAAQ,EAAE;AAExD,UAAM,QAAQ;AAAA,MACZ,EAAE,IAAI,OAAO;AAAA,MACb;AAAA,QACE,WAAW,EAAE,OAAO,KAAK,IAAI;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,wBAAwB,QAAgB,eAAwB;AACpE,UAAM,QAAQ,iBAAiB,EAAE,IAAI,OAAO,GAAG,EAAE,cAAc,CAAC;AAAA,EAClE;AAAA,EAEA,MAAM,mBAAmB,QAAgB,aAAqB;AAC5D,UAAM,SAAS,MAAM,OAAO,KAAK,aAAa,EAAE;AAEhD,UAAM,QAAQ,iBAAiB,EAAE,IAAI,OAAO,GAAG,EAAE,cAAc,OAAO,CAAC;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,mBAAoC;AAChD,UAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAExC,QAAI,KAAK,SAAS,KAAK,MAAM,MAAM,KAAK,KAAK;AAC3C,aAAO,KAAK,MAAM;AAAA,IACpB;AAEA,UAAM,UAAU;AAAA,MACd,MAAM;AAAA,MACN,QAAQ;AAAA,IACV;AAEA,UAAM,cAAcE,KAAI,KAAK,SAAS,QAAQ,IAAI,YAAa;AAAA,MAC7D,WAAW;AAAA,IACb,CAAC;AAED,SAAK,QAAQ;AAAA,MACX;AAAA,MACA,KAAK,MAAM;AAAA,IACb;AAEA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;;;AEjIA,OAAOC,UAAS;AAChB,OAAO,gBAAgB;AAEhB,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EACA,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACtB,cAAc;AACZ,SAAK,cAAc,WAAW,gBAAgB;AAAA,MAC5C,MAAM,QAAQ,IAAI,cAAc;AAAA,MAChC,MAAM,QAAQ,IAAI,aAAa,OAAO,QAAQ,IAAI,UAAU,IAAI;AAAA,MAChE,SAAS,QAAQ,IAAI,gBAAgB,aAAa;AAAA,MAClD,MAAM;AAAA,QACJ,MAAM,QAAQ,IAAI;AAAA,QAClB,MAAM,QAAQ,IAAI;AAAA,MACpB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,KAAK,SAAc,SAAS,KAAK,KAAK,IAAI;AACxC,WAAOA,KAAI,KAAK,SAAS,QAAQ,IAAI,kBAAmB;AAAA,MACtD,WAAW;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,OAAgB,OAAkB;AAChC,WAAOA,KAAI,OAAO,OAAO,QAAQ,IAAI,gBAAiB;AAAA,EACxD;AAAA,EAEA,MAAM,KAAK,IAAY,SAAiB,MAAc;AACpD,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,YAAY,SAAS;AAAA,QAC3C,MAAM,QAAQ,IAAI;AAAA,QAClB;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,cAAQ,IAAI,kDAA6C;AAAA,QACvD,WAAW,KAAK;AAAA,QAChB,UAAU,KAAK;AAAA,QACf,UAAU,KAAK;AAAA,QACf,UAAU,KAAK;AAAA,MACjB,CAAC;AAED,aAAO;AAAA,IACT,SAAS,OAAY;AACnB,cAAQ,MAAM,+CAA0C;AAAA,QACtD,SAAS,MAAM;AAAA,QACf,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,QACf,cAAc,MAAM;AAAA,QACpB,UAAU,MAAM;AAAA,QAChB,OAAO,MAAM;AAAA,MACf,CAAC;AAGD,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,QAAQ,eAAuB;AAC7B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,cAAc,MAAM,KAAK,iBAAiB,KAAK;AACrD,UAAM,kBAAkB,iBAAiB,CAAC,GACvC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,EACtB,OAAO,CAAC,MAAM,EAAE,QAAQ,KAAK,WAAW;AAC3C,QAAI,eAAe,UAAU,KAAK;AAChC,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,QAAQ,KAAK,cAAc,KAAK,KAAK;AAAA,MACvC;AACF,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB;AACF;;;AC/CA,IAAM,SAAS;AAAA,EACb,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA,EACX,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,WAAW;AACb;AAEA,IAAM,SAAS;AAAA,EACb,SAAS;AAAA;AAAA;AAAA,wBAGa,OAAO,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMvC,WAAW;AAAA;AAAA;AAAA;AAAA,aAIA,OAAO,WAAW;AAAA;AAAA;AAAA,EAG7B,MAAM;AAAA,wBACgB,OAAO,cAAc;AAAA,wBACrB,OAAO,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvC,QAAQ;AAAA;AAAA;AAAA,+BAGqB,OAAO,OAAO;AAAA;AAAA,EAE3C,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYb,aAAa;AAAA,aACF,OAAO,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,gBAAgB;AAAA,aACL,OAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAK3B,MAAM;AAAA;AAAA;AAAA,EAGN,UAAU;AAAA;AAAA,aAEC,OAAO,WAAW;AAAA;AAAA;AAAA;AAAA,EAI7B,WAAW;AAAA;AAAA,aAEA,OAAO,aAAa;AAAA;AAAA;AAAA;AAAA,EAI/B,eAAe;AAAA;AAAA;AAAA;AAAA,EAIf,QAAQ;AAAA;AAAA,wBAEc,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUnC,iBAAiB;AAAA;AAAA;AAAA,aAGN,OAAO,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAMP,OAAO,OAAO;AAAA;AAAA,EAEpC,UAAU;AAAA,wBACY,OAAO,MAAM;AAAA,wBACb,OAAO,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpC,eAAe;AAAA;AAAA,aAEJ,OAAO,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,cAAc;AAAA;AAAA,aAEH,OAAO,aAAa;AAAA;AAAA;AAAA;AAAA,EAI/B,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOb,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQlB,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjB,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOb,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQlB,aAAa;AAAA;AAAA;AAAA,wBAGS,OAAO,MAAM;AAAA;AAAA,wBAEb,OAAO,OAAO;AAAA;AAAA,EAEpC,WAAW;AAAA;AAAA,aAEA,OAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,EAK3B,UAAU;AAAA;AAAA,aAEC,OAAO,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAK/B,SAAS;AAAA;AAAA,4BAEiB,OAAO,OAAO;AAAA;AAAA;AAAA,EAGxC,QAAQ;AAAA;AAAA;AAAA,4BAGkB,OAAO,OAAO;AAAA;AAAA,EAExC,YAAY;AAAA;AAAA,aAED,OAAO,SAAS;AAAA;AAAA;AAAA;AAAA,EAI3B,YAAY;AAAA,aACD,OAAO,aAAa;AAAA;AAAA;AAAA,EAG/B,OAAO;AAAA;AAAA;AAAA,aAGI,OAAO,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/B,UAAU;AAAA,aACC,OAAO,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAK/B,SAAS;AAAA;AAAA;AAAA;AAAA,+BAIoB,OAAO,OAAO;AAAA;AAAA,EAE3C,WAAW;AAAA,aACA,OAAO,SAAS;AAAA;AAAA;AAAA,EAG3B,WAAW;AAAA,aACA,OAAO,WAAW;AAAA;AAAA;AAAA;AAI/B;AAKO,SAAS,+BACd,MACQ;AACR,QAAM,EAAE,WAAW,iBAAiB,UAAU,IAAI;AAElD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAeU,OAAO,OAAO;AAAA,oBACb,OAAO,SAAS;AAAA,sBACd,OAAO,IAAI;AAAA;AAAA,wBAET,OAAO,MAAM;AAAA,0BACX,OAAO,WAAW;AAAA;AAAA;AAAA,yBAGnB,OAAO,WAAW;AAAA,wBACnB,OAAO,cAAc;AAAA;AAAA;AAAA;AAAA,wBAIrB,OAAO,IAAI;AAAA,wBACX,OAAO,QAAQ,QAAQ,SAAS;AAAA;AAAA,wBAEhC,OAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,0BAKd,OAAO,aAAa;AAAA,yBACrB,eAAe,YAAY,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,0BAKvC,OAAO,QAAQ;AAAA,0BACf,OAAO,aAAa;AAAA,0BACpB,OAAO,YAAY;AAAA,gEACmB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAOjD,OAAO,MAAM;AAAA,wBACb,OAAO,UAAU;AAAA;AAAA,sBAEvB,oBAAI,KAAK,GAAE,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ1C;AAKO,SAAS,gCACd,MACQ;AACR,QAAM,EAAE,WAAW,UAAU,UAAU,IAAI;AAE3C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAUU,OAAO,OAAO;AAAA,oBACb,OAAO,SAAS;AAAA,sBACd,OAAO,IAAI;AAAA;AAAA,wBAET,OAAO,MAAM;AAAA,0BACX,OAAO,WAAW;AAAA;AAAA;AAAA,yBAGnB,OAAO,WAAW;AAAA,wBACnB,OAAO,cAAc;AAAA;AAAA;AAAA;AAAA,wBAIrB,OAAO,IAAI;AAAA,wBACX,OAAO,QAAQ,QAAQ,SAAS;AAAA;AAAA,wBAEhC,OAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,0BAKd,OAAO,aAAa;AAAA,yBACrB,QAAQ,YAAY,OAAO,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,0BAKhC,OAAO,WAAW;AAAA,0BAClB,OAAO,gBAAgB;AAAA,0BACvB,OAAO,eAAe;AAAA,sDACC,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAMjC,OAAO,OAAO;AAAA;AAAA,wBAEf,OAAO,SAAS,6BAA6B,OAAO,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAQ7D,OAAO,MAAM;AAAA,wBACb,OAAO,UAAU;AAAA;AAAA,sBAEvB,oBAAI,KAAK,GAAE,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQ1C;;;AhBpYO,SAAS,iBACd,UAA6B,CAAC,GACf;AACf,QAAM,iBAAiB,QAAQ,IAAI;AACnC,QAAM,qBAAqB,QAAQ,IAAI;AACvC,QAAM,oBAAoB,QAAQ,IAAI;AACtC,QAAM,wBACJ,QAAQ,IAAI,2BAA2B,mBAAmB,OAAO,KAAK;AAExE,QAAM,kBACJ,CAAC,CAAC,kBAAkB,CAAC,CAAC,sBAAsB,CAAC,CAAC;AAEhD,MAAI,QAAQ,QAAQ;AAClB,mBAAe,QAAQ,MAAM;AAAA,EAC/B;AAEA,QAAM,IAAI,OAAO;AAEjB,QAAM,QAAQ,IAAI,aAAa;AAC/B,QAAM,YAAY,IAAI,iBAAiB;AAEvC,QAAM,YAAY,QAAQ,IAAI,aAAa;AAE3C,QAAM,eAAiC;AAAA,IACrC,UAAU,QAAQ,QAAQ,aAAa,YAAY,SAAS;AAAA;AAAA,IAC5D,QAAQ,QAAQ,QAAQ,UAAU;AAAA;AAAA,IAClC,QAAQ,QAAQ,QAAQ,UAAU;AAAA,IAClC,MAAM,QAAQ,QAAQ,QAAQ;AAAA,IAC9B,UAAU,QAAQ,QAAQ,YAAY,KAAK,KAAK,KAAK;AAAA,EACvD;AAEA,IAAE,IAAI,QAAQ,KAAK,CAAC;AACpB,IAAE,IAAI,QAAQ,WAAW,EAAE,UAAU,KAAK,CAAC,CAAC;AAE5C,IAAE;AAAA,IAAI;AAAA,IAAY,CAAC,MAAM,QACvB,IAAI,KAAK,EAAE,QAAQ,MAAM,QAAQ,aAAa,CAAC;AAAA,EACjD;AAEA,IAAE,KAAK,UAAU,eAAe,OAAO,KAAK,QAAQ;AAClD,UAAM,EAAE,OAAO,cAAc,SAAS,IAAI,IAAI,QAAQ,CAAC;AAEvD,QAAI;AAEF,YAAM,OAAO,MAAM,QAAQ,QAAQ,EAAE,OAAO,aAAa,CAAC,EACvD,OAAO,WAAW,EAClB,KAAK;AAER,UAAI,CAAC,MAAM;AACT,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAGA,UAAI,CAAC,KAAK,eAAe;AACvB,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAGA,YAAM,kBAAkB,KAAK,eACzB,MAAMC,QAAO,QAAQ,UAAU,KAAK,YAAY,IAChD;AAEJ,UAAI,CAAC,iBAAiB;AACpB,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,OAAO;AAAA,UACP,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAGA,YAAM,SAAS,eAAe,IAAI;AAClC,qBAAe,KAAK,QAAQ,YAAY;AAGxC,UAAI,KAAK,WAAW;AAClB,YAAI,OAAO,QAAQ,qBAAqB,aAAa,KAAK,WAAW;AAAA,UACnE,GAAG,6BAA6B,YAAY;AAAA,UAC5C,UAAU;AAAA,QACZ,CAAQ;AAAA,MACV;AAEA,aAAO,IAAI,KAAK;AAAA,QACd,SAAS;AAAA,QACT,MAAM,eAAe,IAAI;AAAA,MAC3B,CAAC;AAAA,IACH,SAAS,KAAU;AACjB,cAAQ,MAAM,gBAAgB,GAAG;AACjC,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,wBAAwB,CAAC;AAAA,IAChE;AAAA,EACF,CAAC;AAED,IAAE,KAAK,WAAW,gBAAgB,OAAO,KAAK,QAAQ;AACpD,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI,IAAI,QAAQ,CAAC;AAEjB,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,kBAAkB;AAAA,QAC/C,UAAU;AAAA,QACV,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa,CAAC,EAAE,MAAM,YAAY,OAAO,UAAU,WAAW,MAAM,CAAC;AAAA,QACrE;AAAA,MACF,CAAC;AAED,YAAM,UAAU,gBAAgB,OAAO,IAAI,eAAe;AAE1D,YAAM,OAAO,MAAM,QAAQ;AAAA,QACzB,EAAE,OAAO,OAAO,MAAM;AAAA,QACtB;AAAA,UACE,OAAO,OAAO;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO,CAAC,eAAe;AAAA,UACvB,eAAe;AAAA,QACjB;AAAA,QACA,EAAE,QAAQ,MAAM,KAAK,MAAM,qBAAqB,KAAK;AAAA,MACvD;AAEA,YAAM,cAAc,MAAM,qBAAqB;AAAA,QAC7C,cAAc;AAAA,QACd;AAAA,QACA,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,QAKT,MAAM,+BAA+B;AAAA,UACnC,WAAW,KAAK;AAAA,UAChB,iBAAiB,GAAG,mBAAmB,OAAO,CAAC,uBAAuB,MAAM;AAAA,YAC1E;AAAA,cACE,QAAQ,KAAK;AAAA,cACb,OAAO,KAAK;AAAA,YACd;AAAA,UACF,CAAC;AAAA,UACD,WAAW;AAAA,QACb,CAAC;AAAA,MACH,CAAC;AAED,UAAI,YAAY,aAAa;AAC3B,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,QAAQ,YAAY;AAAA,QACtB,CAAC;AAAA,MACH;AAEA,aAAO,IAAI,KAAK;AAAA,QACd,IAAI,KAAK;AAAA,QACT,OAAO,KAAK;AAAA,QACZ,SAAS;AAAA,MACX,CAAC;AAAA,IACH,SAAS,KAAU;AACjB,aAAO,yBAAyB,KAAK,KAAK,eAAe;AAAA,IAC3D;AAAA,EACF,CAAC;AAED,IAAE,IAAI,OAAO,YAAY,GAAG,CAAC,KAAK,QAAQ;AACxC,WAAO,IAAI,KAAM,IAAY,QAAQ,IAAI;AAAA,EAC3C,CAAC;AAED,IAAE,KAAK,WAAW,OAAO,MAAM,QAAQ;AACrC,UAAM,eAAe,wBAAwB,YAAY;AAEzD,QAAI,YAAY,gBAAgB,YAAY;AAC5C,QAAI,YAAY,iBAAiB,YAAY;AAC7C,QAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACvB,CAAC;AAED,IAAE,IAAI,qBAAqB,YAAY,GAAG,OAAO,KAAK,QAAQ;AAC5D,UAAM,EAAE,OAAO,IAAI,IAAI;AACvB,UAAM,EAAE,SAAS,IAAI,IAAI,QAAQ,CAAC;AAElC,UAAM,OAAO,MAAM,QAAQ,QAAQ,EAAE,IAAI,OAAO,CAAC;AAEjD,QAAI,CAAC;AACH,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,iBAAiB,CAAC;AAEpE,UAAM,MAAM,IAAI;AAAA,OACZ,KAAa,YAAY,CAAC,GAAG,IAAI,CAAC,MAAW,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC;AAAA,IACjE;AAEA,eAAW,QAAQ,YAAY,CAAC,EAAG,KAAI,IAAI,KAAK,KAAK,KAAK,KAAK;AAC/D,IAAC,KAAa,WAAW,MAAM,KAAK,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO;AAAA,MACxE;AAAA,MACA;AAAA,IACF,EAAE;AAEF,UAAO,KAAa,KAAK;AACzB,QAAI,KAAK,EAAE,IAAI,MAAM,UAAW,KAAa,SAAS,CAAC;AAAA,EACzD,CAAC;AAED,IAAE,IAAI,iBAAiB,OAAO,KAAK,QAAQ;AACzC,UAAM,QAAQ,OAAO,IAAI,MAAM,SAAS,EAAE;AAC1C,QAAI,CAAC,OAAO;AACV,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iCAAiC,CAAC;AAAA,IACzE;AACA,QAAI;AACF,YAAM,UAAU,MAAM,OAA0C,KAAK;AACrE,YAAM,UAAU,wBAAwB,QAAQ,QAAQ,IAAI;AAC5D,YAAM,QAAQ;AAAA,QACZ,EAAE,IAAI,QAAQ,OAAO;AAAA,QACrB,EAAE,MAAM,EAAE,eAAe,KAAK,EAAE;AAAA,MAClC;AACA,UAAI,KAAK,EAAE,IAAI,MAAM,SAAS,iBAAiB,CAAC;AAAA,IAClD,SAAS,KAAU;AACjB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,KAAK,WAAW,gBAAgB,CAAC;AAAA,IAC/D;AAAA,EACF,CAAC;AAED,IAAE;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,KAAK,QAAQ;AAClB,YAAM,OAAO,MAAM,QAAQ,QAAQ,EAAE,OAAO,IAAI,KAAK,MAAM,CAAC;AAC5D,UAAI,CAAC;AACH,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,iBAAiB,CAAC;AAEpE,YAAM,WAAW,MAAM,UAAU,oBAAoB,KAAK,EAAE;AAC5D,UAAI,UAAU;AACZ,eAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,4BAA4B,CAAC;AAAA,MAC3D;AAEA,YAAM,QAAQ,MAAM,KAAK;AAAA,QACvB,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,MACf,CAAC;AAED,YAAM,eAAe,MAAM,qBAAqB;AAAA,QAC9C,cAAc;AAAA,QACd;AAAA,QACA,SAAS;AAAA;AAAA,QAET,MAAM,+BAA+B;AAAA,UACnC,WAAW,KAAK;AAAA,UAChB,iBAAiB,GAAG,mBAAmB,OAAO,CAAC,uBAAuB,MAAM;AAAA,YAC1E;AAAA,cACE,QAAQ,KAAK;AAAA,cACb,OAAO,KAAK;AAAA,YACd;AAAA,UACF,CAAC;AAAA,UACD,WAAW;AAAA,QACb,CAAC;AAAA,MACH,CAAC;AAED,UAAI,aAAa,aAAa;AAC5B,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,IAAI;AAAA,UACJ,OAAO;AAAA,UACP,QAAQ,aAAa;AAAA,QACvB,CAAC;AAAA,MACH;AAEA,UAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,IACvB;AAAA,EACF;AAEA,IAAE,KAAK,oBAAoB,qBAAqB,OAAO,KAAK,QAAQ;AAClE,UAAM,OAAO,MAAM,QAAQ,QAAQ,EAAE,OAAO,IAAI,KAAK,MAAM,CAAC;AAC5D,QAAI,CAAC;AACH,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,iBAAiB,CAAC;AAEpE,UAAM,aAAa,MAAM;AAAA,MACvB;AAAA,QACE,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,WAAW,KAAK;AAAA,QAChB,UAAU,KAAK;AAAA,MACjB;AAAA,MACA,KAAK;AAAA,IACP;AAEA,UAAM,cAAc,MAAM,qBAAqB;AAAA,MAC7C,cAAc;AAAA,MACd;AAAA,MACA,SAAS;AAAA;AAAA,MAET,MAAM,gCAAgC;AAAA,QACpC,WAAW,KAAK;AAAA,QAChB,UAAU,GAAG,mBAAmB,OAAO,CAAC,yBAAyB,MAAM;AAAA,UACrE;AAAA,YACE,QAAQ,KAAK;AAAA,YACb,OAAO,KAAK;AAAA,UACd;AAAA,QACF,CAAC;AAAA,QACD,WAAW;AAAA,MACb,CAAC;AAAA,IACH,CAAC;AAED,QAAI,YAAY,aAAa;AAC3B,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,QAC1B,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,QAAQ,YAAY;AAAA,MACtB,CAAC;AAAA,IACH;AAEA,QAAI,KAAK,EAAE,IAAI,MAAM,SAAS,4BAA4B,CAAC;AAAA,EAC7D,CAAC;AAED,IAAE,KAAK,mBAAmB,uBAAuB,OAAO,KAAK,QAAQ;AACnE,UAAM,EAAE,OAAO,YAAY,IAAK,IAAI,QAAQ,CAAC;AAE7C,QAAI,CAAC,SAAS,CAAC,aAAa;AAC5B,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,QAC1B,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAEE,QAAI;AACF,YAAM,UAAU,MAAM,OAInB,KAAK;AAER,YAAM,OAAO,MAAM,QAAQ,QAAQ,EAAE,IAAI,QAAQ,OAAO,CAAC;AACzD,UAAI,CAAC,MAAM;AACT,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,iBAAiB,CAAC;AAAA,MACpE;AAEA,UACE,KAAK,qBACL,QAAQ,MAAM,MAAO,KAAK,kBAAkB,QAAQ,GACpD;AACA,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,IAAI;AAAA,UACJ,OACE;AAAA,QACJ,CAAC;AAAA,MACH;AAEA,YAAM,UAAU,mBAAmB,QAAQ,QAAQ,WAAW;AAE9D,WAAK,oBAAoB,oBAAI,KAAK;AAClC,YAAM,KAAK,KAAK;AAEhB,UAAI,KAAK,EAAE,IAAI,MAAM,SAAS,gCAAgC,CAAC;AAAA,IACjE,SAAS,KAAU;AACjB,UACG,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,KAAK,WAAW,2BAA2B,CAAC;AAAA,IAC1E;AAAA,EACF,CAAC;AAED,IAAE;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA,OAAO,KAAK,QAAQ;AAClB,YAAM,EAAE,OAAO,cAAc,KAAK,IAAI,IAAI,QAAQ,CAAC;AAEnD,YAAM,eAAe,MAAM,QAAQ,QAAQ,EAAE,OAAO,aAAa,CAAC;AAClE,UAAI,cAAc;AAChB,eAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,sCAAsC,CAAC;AAAA,MACrE;AAEA,YAAM,iBAAiB,MAAM,OAAO,QAAQ;AAAA,QAC1C,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW;AAAA,MACb,CAAC;AAED,UAAI,gBAAgB;AAClB,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,IAAI;AAAA,UACJ,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAEA,YAAM,QAAQ,MAAM,KAAK;AAAA,QACvB,OAAO;AAAA,QACP;AAAA,QACA,UAAU,WAAW;AAAA,MACvB,CAAC;AAED,YAAM,SAAS,MAAM,OAAO,OAAO;AAAA,QACjC,IAAI;AAAA,QACJ,OAAO;AAAA,QACP;AAAA,QACA,WAAY,IAAY,MAAM;AAAA,QAC9B,QAAQ;AAAA,QACR,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK,GAAI;AAAA,MAC1D,CAAC;AAED,YAAM,MAAM;AAAA,QACV;AAAA,QACA;AAAA,QACA,YAAY,mBAAmB,OAAO,CAAC,6BAA6B,KAAK;AAAA,MAC3E;AAEA,UAAI,KAAK;AAAA,QACP,IAAI;AAAA,QACJ,UAAU,OAAO;AAAA,QACjB,OAAO,OAAO;AAAA,QACd,MAAM,OAAO;AAAA,QACb,WAAW,OAAO;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,IAAE,IAAI,kBAAkB,OAAO,KAAK,QAAQ;AAC1C,UAAM,MAAM,MAAM,OAAO,QAAQ,EAAE,IAAI,OAAO,IAAI,MAAM,KAAK,EAAE,CAAC;AAChE,QAAI,KAAK,EAAE,IAAI,CAAC,CAAC,OAAO,CAAE,IAAY,UAAU,CAAE,IAAY,UAAU,CAAC;AAAA,EAC3E,CAAC;AAED,IAAE,KAAK,kBAAkB,OAAO,KAAK,QAAQ;AAC3C,UAAM,EAAE,OAAO,WAAW,UAAU,UAAU,UAAU,IAAI,IAAI,QAAQ,CAAC;AAEzE,QACE,CAAC,SACD,CAAC,aACD,CAAC,YACD,CAAC,iBAAiB,YAAY,EAAE,GAChC;AACA,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,kBAAkB,CAAC;AAAA,IACrE;AAEA,UAAM,SAAS,MAAM,OAAO,QAAQ;AAAA,MAClC,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,WAAW;AAAA,IACb,CAAC;AAED,QAAI,CAAC,QAAQ;AACX,aAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,uCAAuC,CAAC;AAAA,IACtE;AAEA,QAAI,OAAO,aAAa,OAAO,UAAU,QAAQ,IAAI,KAAK,IAAI,GAAG;AAC/D,aAAO,YAAY;AACnB,YAAM,OAAO,KAAK;AAClB,aAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,yBAAyB,CAAC;AAAA,IACxD;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,kBAAkB;AAAA,QAC/C,UAAU,OAAO;AAAA,QACjB,OAAO,OAAO;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe;AAAA,QACf,aAAa,CAAC,EAAE,MAAM,YAAY,OAAO,UAAU,WAAW,MAAM,CAAC;AAAA,MACvE,CAAC;AAED,YAAM,UAAU,gBAAgB,OAAO,IAAI,OAAO,IAAI;AAEtD,YAAM,QAAQ;AAAA,QACZ,EAAE,OAAO,OAAO,MAAM;AAAA,QACtB;AAAA,UACE,IAAI,OAAO;AAAA,UACX,OAAO,OAAO;AAAA,UACd;AAAA,UACA;AAAA,UACA,OAAO,CAAC,OAAO,IAAI;AAAA,UACnB,eAAe;AAAA,QACjB;AAAA,QACA,EAAE,QAAQ,MAAM,KAAK,MAAM,qBAAqB,KAAK;AAAA,MACvD;AAEA,aAAO,SAAS;AAChB,aAAO,SAAS,oBAAI,KAAK;AACzB,aAAO,SAAS,OAAO;AACvB,YAAM,OAAO,KAAK;AAElB,UAAI,KAAK;AAAA,QACP,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,OAAO,OAAO;AAAA,MAChB,CAAC;AAAA,IACH,SAAS,KAAU;AACjB,UAAI,OAAO,GAAG,EAAE,KAAK;AAAA,QACnB,IAAI;AAAA,QACJ,OACE,KAAK,UAAU,MAAM,qBACrB,KAAK,WACL;AAAA,MACJ,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,IAAE,IAAI,YAAY,YAAY,GAAG,OAAO,MAAM,QAAQ;AACpD,UAAM,UAAU,MAAM,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,GAAG,CAAC,EAAE,KAAK;AACjE,QAAI,KAAK,OAAO;AAAA,EAClB,CAAC;AAED,IAAE,OAAO,sBAAsB,YAAY,GAAG,OAAO,KAAK,QAAQ;AAChE,UAAM,OAAO,UAAU,EAAE,IAAI,IAAI,OAAO,SAAS,CAAC;AAClD,QAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACvB,CAAC;AAED,IAAE,IAAI,sBAAsB,OAAO,KAAK,QAAQ;AAC9C,UAAM,OAAO,MAAM,QAAQ,QAAQ,EAAE,OAAO,IAAI,MAAM,MAAM,CAAC,EAAE,KAAK;AACpE,QAAI,KAAK,QAAQ,IAAI;AAAA,EACvB,CAAC;AAED,IAAE,IAAI,WAAW,CAAC,KAAK,QAAQ;AAC7B,QAAI,CAAC,iBAAiB;AACpB,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,8BAA8B,CAAC;AAAA,IACtE;AAGA,UAAM,YAAY;AAAA,MAChB,YAAY,IAAI,MAAM,cAAc;AAAA,MACpC,WAAW,IAAI,MAAM,aAAa,QAAQ,IAAI,sBAAsB;AAAA,IACtE;AAUA,UAAM,QAAQ,mBAAmB,KAAK,UAAU,SAAS,CAAC;AAE1D,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC,WAAW;AAAA,MACX,cAAc;AAAA,MACd,eAAe;AAAA,MACf,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,MACR;AAAA,IACF,CAAC;AAED,UAAM,MAAM,gDAAgD,OAAO,SAAS,CAAC;AAE7E,YAAQ,IAAI,KAAK,KAAK;AAGtB,QAAI,SAAS,GAAG;AAAA,EAClB,CAAC;AAED,IAAE,IAAI,oBAAoB,OAAO,KAAK,QAAQ;AAC5C,QAAI,CAAC,iBAAiB;AACpB,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,8BAA8B,CAAC;AAAA,IACtE;AAEA,UAAM,OAAO,OAAO,IAAI,MAAM,QAAQ,EAAE;AAKxC,QAAI,YAAY,EAAE,YAAY,IAAI,WAAW,GAAG;AAChD,QAAI;AACF,UAAI,IAAI,MAAM,OAAO;AACnB,oBAAY,KAAK,MAAM,mBAAmB,OAAO,IAAI,MAAM,KAAK,CAAC,CAAC;AAAA,MACpE;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,MAAM,0BAA0B,GAAG;AAAA,IAE7C;AAEA,UAAM,EAAE,YAAY,UAAU,IAAI;AAClC,YAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,CAAC,MAAM;AACT,aAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,6BAA6B,CAAC;AAAA,IAC5D;AAEA,QAAI;AAEF,YAAM,WAAW,MAAM,MAAM,uCAAuC;AAAA,QAClE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,oCAAoC;AAAA,QAC/D,MAAM,IAAI,gBAAgB;AAAA,UACxB;AAAA,UACA,WAAW;AAAA,UACX,eAAe;AAAA,UACf,cAAc;AAAA,UACd,YAAY;AAAA,QACd,CAAC;AAAA,MACH,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,UAAU,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACtD,gBAAQ,MAAM,sBAAsB,OAAO;AAC3C,eAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,iCAAiC,CAAC;AAAA,MAChE;AAEA,YAAM,YAAa,MAAM,SAAS,KAAK;AAOvC,UAAI,CAAC,UAAU,UAAU;AACvB,eAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,+BAA+B,CAAC;AAAA,MAC9D;AAGA,YAAM,UAAeC,KAAI,OAAO,UAAU,QAAQ;AAElD,YAAMC,SAAQ,SAAS;AACvB,UAAI,CAACA,QAAO;AACV,eAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,8BAA8B,CAAC;AAAA,MAC7D;AAEA,YAAM,gBAAgB,QAAQ,kBAAkB;AAChD,YAAM,YAAY,QAAQ,cAAc;AACxC,YAAM,WAAW,QAAQ,eAAe;AAGxC,UAAI,OAAO,MAAM,QAAQ,QAAQ,EAAE,OAAAA,OAAM,CAAC,EAAE,KAAK;AAEjD,UAAI,CAAC,MAAM;AACT,cAAM,iBAAiB,aAAa,QAAQ,IAAI;AAEhD,YAAI,CAAC,gBAAgB;AACnB,kBAAQ,MAAM,qCAAqC;AACnD,gBAAM,iBACH,cAAc,0BACd,YAAY,SAAS,GAAG,IAAI,MAAM,OACnC;AACF,iBAAO,IAAI,SAAS,aAAa;AAAA,QACnC;AAEA,cAAM,UAAU,MAAM,QAAQ,OAAO;AAAA,UACnC,OAAAA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,OAAO,CAAC,eAAe;AAAA,UACvB,WAAW;AAAA,UACX,UAAU,CAAC;AAAA;AAAA,QAEb,CAAC;AAED,eAAO,QAAQ,SAAS;AAAA,MAC1B;AAGA,YAAM,SAAS,eAAe,IAAI;AAClC,qBAAe,KAAK,QAAQ,YAAY;AAGxC,UAAI,KAAK,WAAW;AAClB,YAAI,OAAO,QAAQ,qBAAqB,aAAa,KAAK,WAAW;AAAA,UACnE,GAAG,6BAA6B,YAAY;AAAA,UAC5C,UAAU;AAAA,QACZ,CAAQ;AAAA,MACV;AAGA,YAAM,gBAAgB,cAAc;AAEpC,UAAI,SAAS,aAAa;AAAA,IAC5B,SAAS,KAAU;AACjB,cAAQ,MAAM,yBAAyB,GAAG;AAC1C,YAAM,gBAAgB,sBAAsB,SAAS,GAAG,IACpD,GAAG,qBAAqB,+BACxB,GAAG,qBAAqB;AAE5B,UAAI,SAAS,aAAa;AAAA,IAC5B;AAAA,EACF,CAAC;AAED,IAAE,IAAI,WAAW,CAAC,KAAK,QAAQ;AAC7B,UAAM,iBAAiB,QAAQ,IAAI;AACnC,UAAM,oBAAoB,QAAQ,IAAI;AAEtC,QAAI,CAAC,kBAAkB,CAAC,mBAAmB;AACzC,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,8BAA8B,CAAC;AAAA,IACtE;AAEA,UAAM,QAAQ,IAAI,MAAM,aACpB,mBAAmB,OAAO,IAAI,MAAM,UAAU,CAAC,IAC/C;AAEJ,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC,WAAW;AAAA,MACX,cAAc;AAAA,MACd,OAAO;AAAA,MACP;AAAA,MACA,cAAc;AAAA,IAChB,CAAC;AAED,UAAM,MAAM,4CAA4C,OAAO,SAAS,CAAC;AACzE,QAAI,SAAS,GAAG;AAAA,EAClB,CAAC;AAED,IAAE,IAAI,oBAAoB,OAAO,KAAK,QAAQ;AAC5C,UAAM,iBAAiB,QAAQ,IAAI;AACnC,UAAM,qBAAqB,QAAQ,IAAI;AACvC,UAAM,oBAAoB,QAAQ,IAAI;AACtC,UAAM,wBACJ,QAAQ,IAAI,2BAA2B,mBAAmB,OAAO,KAAK;AAExE,QAAI,CAAC,kBAAkB,CAAC,sBAAsB,CAAC,mBAAmB;AAChE,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,8BAA8B,CAAC;AAAA,IACtE;AAEA,UAAM,OAAO,OAAO,IAAI,MAAM,QAAQ,EAAE;AACxC,UAAM,QAAQ,IAAI,MAAM,QAAQ,OAAO,IAAI,MAAM,KAAK,IAAI;AAE1D,QAAI,CAAC,MAAM;AACT,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,sBAAsB,CAAC;AAAA,IACzE;AAEA,QAAI;AAEF,YAAM,WAAW,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,QAAQ;AAAA,YACR,gBAAgB;AAAA,UAClB;AAAA,UACA,MAAM,IAAI,gBAAgB;AAAA,YACxB,WAAW;AAAA,YACX,eAAe;AAAA,YACf;AAAA,YACA,cAAc;AAAA,UAChB,CAAC;AAAA,QACH;AAAA,MACF;AAEA,YAAM,YAAa,MAAM,SAAS,KAAK;AAQvC,UAAI,CAAC,UAAU,cAAc;AAC3B,gBAAQ,MAAM,uBAAuB,SAAS;AAC9C,eAAO,IACJ,OAAO,GAAG,EACV,KAAK,EAAE,IAAI,OAAO,OAAO,oCAAoC,CAAC;AAAA,MACnE;AAEA,YAAM,cAAc,UAAU;AAG9B,YAAM,UAAU,MAAM,MAAM,+BAA+B;AAAA,QACzD,SAAS;AAAA,UACP,eAAe,UAAU,WAAW;AAAA,UACpC,QAAQ;AAAA,QACV;AAAA,MACF,CAAC;AAED,YAAM,aAAc,MAAM,QAAQ,KAAK;AAOvC,YAAM,WAAW,MAAM,MAAM,sCAAsC;AAAA,QACjE,SAAS;AAAA,UACP,eAAe,UAAU,WAAW;AAAA,UACpC,QAAQ;AAAA,QACV;AAAA,MACF,CAAC;AAED,YAAM,SAAU,MAAM,SAAS,KAAK;AAOpC,YAAM,eAAe,QAAQ;AAAA,QAC3B,CAAC,MAAW,EAAE,WAAW,EAAE;AAAA,MAC7B,GAAG;AAEH,UAAI,CAAC,cAAc;AACjB,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,IAAI;AAAA,UACJ,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAEA,YAAM,YAAY,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK;AACpD,YAAM,WAAW,WAAW,MAAM,MAAM,GAAG,EAAE,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK;AAGnE,UAAI,OAAO,MAAM,QAAQ,QAAQ,EAAE,OAAO,aAAa,CAAC,EAAE,KAAK;AAE/D,UAAI,CAAC,MAAM;AACT,cAAM,UAAU,MAAM,QAAQ,OAAO;AAAA,UACnC,OAAO;AAAA,UACP;AAAA,UACA;AAAA,UACA,eAAe;AAAA,UACf,OAAO,CAAC,eAAe;AAAA,UACvB,WAAW;AAAA,UACX,UAAU,CAAC;AAAA,UACX,UAAU,WAAW;AAAA,QACvB,CAAC;AAED,eAAO,QAAQ,SAAS;AAAA,MAC1B;AAGA,YAAM,SAAS,eAAe,IAAI;AAClC,qBAAe,KAAK,QAAQ,YAAY;AAGxC,YAAM,aAAa,QACf,mBAAmB,KAAK,IACxB;AAEJ,UAAI,SAAS,UAAU;AAAA,IACzB,SAAS,KAAU;AACjB,cAAQ,MAAM,0BAA0B,GAAG;AAE3C,YAAM,gBAAgB,sBAAsB,SAAS,GAAG,IACpD,GAAG,qBAAqB,+BACxB,GAAG,qBAAqB;AAE5B,UAAI,SAAS,aAAa;AAAA,IAC5B;AAAA,EACF,CAAC;AAED,IAAE,IAAI,cAAc,OAAO,KAAK,QAAQ;AACtC,UAAM,OAAO,MAAM,QAAQ,KAAK,EAAE,WAAW,IAAI,MAAM,UAAU,CAAC,EAAE,KAAK;AACzE,QAAI,KAAK,QAAQ,IAAI;AAAA,EACvB,CAAC;AAED,SAAO;AACT;AAEA,SAAS,eACP,KACA,QACA,QACA;AACA,QAAM,OAAsB;AAAA,IAC1B,UAAU;AAAA,IACV,QAAQ,OAAO,UAAU;AAAA,IACzB,UAAU,OAAO,YAAY;AAAA,IAC7B,MAAM,OAAO,QAAQ;AAAA,IACrB,QAAQ,OAAO;AAAA,EACjB;AAEA,MAAI,OAAO,QAAQ;AACjB,SAAK,SAAS,OAAO;AAAA,EACvB;AAEA,MAAI,QAAQ,cAAc;AACxB,QAAI,OAAO,gBAAgB,OAAO,cAAc,IAAI;AAAA,EACtD;AACA,MAAI,QAAQ,eAAe;AACzB,QAAI,OAAO,iBAAiB,OAAO,eAAe,IAAI;AAAA,EACxD;AACF;AAEA,SAAS,eAAe,MAAW;AACjC,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL,KAAK,KAAK,MAAM,KAAK;AAAA,IACrB,OAAO,KAAK;AAAA,IACZ,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,IACf,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,IACf,OAAO,KAAK;AAAA,EACd;AACF;AAEA,SAAS,yBACP,KACA,KACA,UACA,SAAS,KACT;AACA,QAAM,cACJ,KAAK,UAAU,MAAM,qBACrB,KAAK,UAAU,MAAM,gBACrB,KAAK,WACL;AACF,SAAO,IAAI,OAAO,MAAM,EAAE,KAAK,EAAE,IAAI,OAAO,OAAO,YAAY,CAAC;AAClE;AAUA,SAAS,mBAAmB,SAA4B;AACtD,MAAI,QAAQ;AACV,WAAO,QAAQ,gBAAgB,QAAQ,OAAO,EAAE;AAClD,QAAM,SAAS,QAAQ,IAAI,YAAa,QAAQ,OAAO,EAAE;AACzD,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,OAAO,WAAW,MAAM,IAAI,SAAS,WAAW,MAAM;AAC/D;AAEA,eAAe,qBAAqB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKuD;AACrD,QAAM,MAAM,aAAa,QAAQ,MAAM,iBAAiB,CAAC,CAAC;AAC1D,MAAI,CAAC,IAAI,IAAI;AACX,WAAO,EAAE,aAAa,MAAM,QAAS,IAAY,OAAO;AAAA,EAC1D;AAEA,QAAM,aAAa,KAAK,KAAK,OAAO,SAAS,IAAI;AACjD,OAAK,gBAAgB,CAAC,GAAI,KAAK,iBAAiB,CAAC,GAAI,oBAAI,KAAK,CAAC;AAC/D,QAAM,KAAK,KAAK;AAChB,SAAO,EAAE,aAAa,MAAM;AAC9B;AAEA,SAAS,eAAe,MAAW;AACjC,QAAM,gBAAgB;AAAA,IACpB,KAAK,KAAK,GAAG,SAAS;AAAA,IACtB,OAAO,KAAK;AAAA,IACZ,OAAO,KAAK,SAAS,CAAC;AAAA,IACtB,OAAO,KAAK,SAAS;AAAA,IACrB,QAAQ,KAAK,SAAS;AAAA,IACtB,WAAW,KAAK,aAAa;AAAA,IAC7B,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,IACf,eAAe,KAAK;AAAA,IACpB,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,IACf,MAAM;AAAA,EACR;AAEA,QAAM,cAAcD,KAAI,KAAK,eAAe,QAAQ,IAAI,YAAa;AAAA,IACnE,WAAW;AAAA,EACb,CAAC;AAED,QAAM,eAAeA,KAAI;AAAA,IACvB,EAAE,KAAK,KAAK,IAAI,SAAS,EAAE;AAAA,IAC3B,QAAQ,IAAI;AAAA,IACZ,EAAE,WAAW,MAAM;AAAA,EACrB;AAEA,SAAO,EAAE,cAAc,aAAa,eAAe,aAAa;AAClE;;;AiBx/BA,OAAOE,YAAW,UAAAC,eAA4C;AAIvD,SAAS,sBAAsB,SAA6B;AACjE,QAAM,IAAIC,QAAO;AACjB,QAAM,KAAK,IAAI,iBAAiB;AAChC,IAAE,IAAIC,SAAQ,KAAK,CAAC;AAEpB,IAAE,KAAK,KAAK,YAAY,GAAG,OAAO,KAAK,KAAK,SAAS;AACnD,QAAI;AACF,YAAM,EAAE,MAAM,UAAU,UAAU,UAAU,IAAI,IAAI,QAAQ,CAAC;AAC7D,YAAM,eAAe,CAAC,WAAW,IAAI,IAAI,SAAS,IAAI;AACtD,YAAM,UAAU,MAAM,GAAG,aAAa,MAAM,cAAc,CAAC,CAAC,QAAQ;AACpE,UAAI,YAAY,YAAY,MAAM;AAChC,cAAM,GAAG,aAAa,QAAQ,IAAI;AAAA,UAChC,oCAAoC,WAChC,EAAE,SAAS,SAAS,IACpB;AAAA,UACJ,qBAAqB,CAAC,CAAC;AAAA,QACzB,CAAC;AAAA,MACH;AACA,UAAI,KAAK,EAAE,UAAU,QAAQ,SAAS,CAAC;AAAA,IACzC,SAAS,GAAG;AACV,WAAK,CAAC;AAAA,IACR;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;AC7BA,SAAS,UAAAC,eAA4C;AAE9C,SAAS,kBAAkB,SAA6B;AAC7D,QAAM,IAAIA,QAAO;AAEjB,IAAE;AAAA,IAAI;AAAA,IAAW,CAAC,KAAK,QACrB,IAAI,KAAK,EAAE,IAAI,MAAM,OAAO,IAAI,MAAM,MAAM,CAAC;AAAA,EAC/C;AAEA,SAAO;AACT;;;ACVA,SAAS,UAAAC,eAA4C;;;ACArD,SAAS,cAAAC,mBAAkB;;;ACA3B,OAAOC,eAAc;AAErB,IAAM,mBAAmB,IAAIA,UAAS;AAAA,EACpC,EAAE,IAAI,EAAE,MAAM,QAAQ,UAAU,KAAK,EAAE;AAAA,EACvC,EAAE,KAAK,MAAM;AACf;AAEA,IAAM,yBAAyB,IAAIA,UAAS;AAAA,EAC1C;AAAA,IACE,WAAW,EAAE,MAAM,QAAQ,UAAU,MAAM,OAAO,KAAK;AAAA,IACvD,SAAS;AAAA,MACP,MAAM,EAAE,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC,EAAE;AAAA,MAC9C,aAAa,EAAE,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC,EAAE;AAAA,MACrD,SAAS,EAAE,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC,EAAE;AAAA,IACnD;AAAA,EACF;AAAA,EACA,EAAE,YAAY,MAAM,YAAY,oBAAoB;AACtD;AAEO,IAAM,mBAAmBA,UAAS;AAAA,EACvC;AAAA,EACA;AACF;;;ACtBA,OAAOC,eAAc;AAErB,IAAM,gBAAgB,IAAIA,UAAS;AAAA,EACjC;AAAA,IACE,KAAK,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACpC,QAAQ,EAAE,MAAM,QAAQ,UAAU,MAAM,OAAO,KAAK;AAAA,IACpD,MAAM,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACrC,aAAa,EAAE,MAAM,OAAO;AAAA,IAC5B,QAAQ,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,EACzC;AAAA,EACA,EAAE,YAAY,MAAM,YAAY,WAAW;AAC7C;AAEO,IAAM,UAAUA,UAAS,MAAM,WAAW,aAAa;;;AFTvD,IAAM,kBAAN,MAAsB;AAAA,EAC3B,MAAM,OAAO,QAAgB,MAAc,aAAsB;AAC/D,UAAM,MAAMC,YAAW;AACvB,UAAM,SAASA,YAAW;AAC1B,UAAM,IAAI,MAAM,QAAQ,OAAO,EAAE,KAAK,QAAQ,MAAM,aAAa,OAAO,CAAC;AACzE,UAAM,iBAAiB,OAAO;AAAA,MAC5B,WAAW;AAAA,MACX,SAAS,EAAE,MAAM,CAAC,GAAG,aAAa,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IACpD,CAAC;AACD,WAAO,EAAE,SAAS;AAAA,EACpB;AAAA,EAEA,MAAM,KAAK,QAAgB;AACzB,WAAO,QAAQ,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK;AAAA,EACvC;AAAA,EAEA,MAAM,IAAI,QAAgB,IAAY;AACpC,WAAO,QAAQ,QAAQ,EAAE,QAAQ,KAAK,GAAG,CAAC,EAAE,KAAK;AAAA,EACnD;AAAA,EAEA,MAAM,OAAO,QAAgB,IAAY,OAAY;AACnD,WAAO,QAAQ;AAAA,MACb,EAAE,QAAQ,KAAK,GAAG;AAAA,MAClB,EAAE,MAAM,MAAM;AAAA,MACd,EAAE,KAAK,KAAK;AAAA,IACd,EAAE,KAAK;AAAA,EACT;AAAA,EAEA,MAAM,OAAO,QAAgB,IAAY;AACvC,UAAM,QAAQ,UAAU,EAAE,QAAQ,KAAK,GAAG,CAAC;AAC3C,UAAM,iBAAiB,WAAW,EAAE,WAAW,GAAG,CAAC;AACnD,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB;AACF;;;ADjCO,SAAS,qBAAqB,SAA6B;AAChE,QAAM,IAAIC,QAAO;AACjB,QAAM,MAAM,IAAI,gBAAgB;AAEhC,IAAE,KAAK,WAAW,YAAY,GAAG,OAAO,KAAK,QAAQ;AACnD,UAAM,EAAE,QAAQ,MAAM,YAAY,IAAI,IAAI,QAAQ,CAAC;AACnD,UAAM,IAAI,MAAM,IAAI,OAAO,QAAQ,MAAM,WAAW;AACpD,QAAI,KAAK,CAAC;AAAA,EACZ,CAAC;AAED,IAAE,IAAI,YAAY,YAAY,GAAG,OAAO,KAAK,QAAQ;AACnD,QAAI,KAAK,MAAM,IAAI,KAAK,IAAI,OAAO,MAAM,CAAC;AAAA,EAC5C,CAAC;AAED,IAAE,IAAI,gBAAgB,YAAY,GAAG,OAAO,KAAK,QAAQ;AACvD,QAAI,KAAK,MAAM,IAAI,IAAI,IAAI,OAAO,QAAQ,IAAI,OAAO,EAAE,CAAC;AAAA,EAC1D,CAAC;AAED,IAAE,IAAI,gBAAgB,YAAY,GAAG,OAAO,KAAK,QAAQ;AACvD,QAAI;AAAA,MACF,MAAM,IAAI,OAAO,IAAI,OAAO,QAAQ,IAAI,OAAO,IAAI,IAAI,QAAQ,CAAC,CAAC;AAAA,IACnE;AAAA,EACF,CAAC;AAED,IAAE,OAAO,gBAAgB,YAAY,GAAG,OAAO,KAAK,QAAQ;AAC1D,QAAI,KAAK,MAAM,IAAI,OAAO,IAAI,OAAO,QAAQ,IAAI,OAAO,EAAE,CAAC;AAAA,EAC7D,CAAC;AAED,SAAO;AACT;;;AIhCA,OAAOC,aAAY;AACnB,SAAS,cAAAC,mBAAkB;AAC3B,OAAOC,YAA8B,UAAAC,eAAc;;;ACK5C,SAAS,eAAe,OAAiB;AAC9C,SAAO,CAAC,KAAc,KAAe,SAAuB;AAC1D,UAAM,OAAQ,IAAY;AAE1B,QAAI,CAAC,MAAM;AACT,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,CAAC;AAAA,IACvD;AAEA,QAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,aAAO,KAAK;AAAA,IACd;AAEA,QAAI,CAAC,WAAW,MAAM,KAAK,GAAG;AAC5B,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,QAC1B,OAAO,0BAA0B,MAAM,KAAK,IAAI,CAAC;AAAA,QACjD,UAAU;AAAA,QACV,WAAW,KAAK;AAAA,MAClB,CAAC;AAAA,IACH;AAEA,SAAK;AAAA,EACP;AACF;;;AC9BA,OAAOC,aAAsB,UAAAC,eAAc;AAc3C,IAAM,oBAAoB,IAAIA;AAAA,EAC5B;AAAA,IACE,IAAI,EAAE,MAAM,QAAQ,UAAU,MAAM,OAAO,KAAK;AAAA,IAChD,OAAO,EAAE,MAAM,QAAQ,SAAS,MAAM,OAAO,KAAK;AAAA,IAClD,KAAK,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACpC,MAAM,EAAE,MAAM,QAAQ,UAAU,KAAK;AAAA,IACrC,OAAO,EAAE,MAAM,QAAQ,UAAU,MAAM;AAAA,IACvC,aAAa,EAAE,MAAM,OAAO;AAAA,IAC5B,YAAY,EAAE,MAAM,SAAS,SAAS,MAAM;AAAA,EAC9C;AAAA,EACA;AAAA,IACE,YAAY;AAAA,EACd;AACF;AAGA,kBAAkB,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,EAAE,QAAQ,KAAK,CAAC;AAEvD,IAAM,mBAAmBD,UAAS;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AACF;;;AFtBA,SAAS,aAAa,KAA0C;AAC9D,QAAM,OAAO,IAAI,QAAQ,CAAC;AAC1B,QAAM,WAAY,KAAK,SAAqB,KAAK,UAAqB;AAEtE,QAAM,YAAa,IAAI,MAAM,SAAoB;AACjD,QAAM,WAAY,IAAI,QAAS,IAAI,KAAK,SAAqB;AAE7D,SAAO,aAAa,YAAY;AAClC;AAEA,SAAS,iBAAiB,KAA0C;AAClE,QAAM,OAAO,IAAI,QAAQ,CAAC;AAC1B,QAAM,WAAY,KAAK,aAAwB;AAE/C,QAAM,YAAa,IAAI,MAAM,aAAwB;AACrD,QAAM,WAAY,IAAI,QAAS,IAAI,KAAK,aAAyB;AAEjE,SAAO,aAAa,YAAY;AAClC;AAEO,SAAS,kBAAkB,WAAgB,CAAC,GAAW;AAC5D,QAAM,IAAIE,QAAO;AAEjB,IAAE,IAAIC,SAAQ,KAAK,CAAC;AACpB,IAAE,IAAIA,SAAQ,WAAW,EAAE,UAAU,KAAK,CAAC,CAAC;AAG5C,QAAM,cAAc,CAAC,YAAY,GAAG,YAAY,gBAAgB,CAAC;AAEjE,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP;AAAA,QACA,gBAAgB;AAAA,QAChB,QAAQ,CAAC;AAAA,MACX,IAAI,IAAI,QAAQ,CAAC;AAEjB,YAAM,YAAY,iBAAiB,GAAG;AAEtC,UAAI;AACF,cAAM,iBAAiB,WACnB,MAAMC,QAAO,KAAK,UAAU,EAAE,IAC9B;AAEJ,cAAM,OAAO,MAAM,QAAQ,OAAO;AAAA,UAChC,IAAIC,YAAW;AAAA,UACf,OAAO;AAAA,UACP,OAAO,QAAQ,IAAI;AAAA,UACnB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,UAAU,CAAC;AAAA,UACX,cAAc;AAAA,UACd;AAAA,QACF,CAAC;AAED,eAAO,IAAI,KAAK;AAAA,UACd,IAAI,KAAK;AAAA,UACT,OAAO,KAAK;AAAA,UACZ,SAAS;AAAA,QACX,CAAC;AAAA,MACH,SAAS,KAAU;AACjB,gBAAQ,MAAM,sBAAsB,GAAG;AACvC,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAEA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,UAAI;AACF,cAAM,SAAU,KAAK,MAAM,MAAM,KAAK,OAAO;AAE7C,YAAI,CAAC,QAAQ;AACX,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,UAAU,MAAM,QAAQ,iBAAiB,EAAE,IAAI,OAAO,CAAC,EAAE,KAAK;AAEpE,YAAI,CAAC,SAAS;AACZ,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,IAAI;AAAA,UACJ,SAAS;AAAA,UACT,aAAa;AAAA,YACX,IAAI,QAAQ;AAAA,YACZ,WAAW,QAAQ;AAAA,YACnB,OAAO,QAAQ;AAAA,YACf,OAAO,QAAQ;AAAA,UACjB;AAAA,QACF,CAAC;AAAA,MACH,SAAS,KAAU;AACjB,gBAAQ,MAAM,sBAAsB,GAAG;AACvC,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAEA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,YAAM,SAAS,IAAI,OAAO;AAE1B,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,MACF,IAAI,IAAI,QAAQ,CAAC;AAEjB,UAAI;AACF,cAAM,eAAe,MAAM,QAAQ,QAAQ;AAAA,UACzC,IAAI;AAAA,UACJ,OAAO,QAAQ,IAAI;AAAA,QACrB,CAAC;AAED,YAAI,CAAC,cAAc;AACjB,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,QACzD;AAGA,YAAI,cAAc,OAAW,cAAa,YAAY;AACtD,YAAI,aAAa,OAAW,cAAa,WAAW;AACpD,YAAI,iBAAiB,OAAW,cAAa,QAAQ;AACrD,YAAI,kBAAkB;AACpB,uBAAa,gBAAgB;AAC/B,YAAI,UAAU,OAAW,cAAa,QAAQ;AAG9C,YAAI,UAAU;AACZ,uBAAa,eAAe,MAAMD,QAAO,KAAK,UAAU,EAAE;AAAA,QAC5D;AAKA,cAAM,aAAa,KAAK;AAExB,eAAO,IAAI,KAAK;AAAA,UACd,IAAI,aAAa;AAAA,UACjB,OAAO,aAAa;AAAA,UACpB,SAAS;AAAA,QACX,CAAC;AAAA,MACH,SAAS,KAAU;AACjB,gBAAQ,MAAM,sBAAsB,GAAG;AACvC,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAMA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,UAAI;AACF,cAAM,QAAQ,aAAa,GAAG;AAE9B,cAAM,SAAc,CAAC;AACrB,YAAI,UAAU,MAAM;AAClB,iBAAO,QAAQ;AAAA,QACjB,OAAO;AACL,iBAAO,QAAQ;AAAA,QACjB;AAEA,cAAM,QAAQ,MAAM,iBAAiB,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK;AAC9D,eAAO,IAAI,KAAK,KAAK;AAAA,MACvB,SAAS,KAAK;AACZ,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAkBA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,UAAI;AACF,cAAM,QAAQ,aAAa,GAAG;AAE9B,cAAM;AAAA,UACJ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,aAAa;AAAA,QACf,IAAI,IAAI,QAAQ,CAAC;AAEjB,YAAI,CAAC,OAAO,CAAC,MAAM;AACjB,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,KAAKC,YAAW;AAEtB,cAAM,aAAa,MAAM,iBAAiB,OAAO;AAAA,UAC/C;AAAA,UACA,OAAO,SAAS;AAAA,UAChB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,YAAY,CAAC,CAAC;AAAA,QAChB,CAAC;AAGD,cAAM,oBAAoB;AAAA,UACxB,EAAE,OAAO,SAAS,MAAM,MAAM,iBAAiB;AAAA,UAC/C,EAAE,WAAW,EAAE,aAAa,IAAI,EAAE;AAAA,UAClC,EAAE,QAAQ,MAAM,KAAK,KAAK;AAAA,QAC5B,EAAE,KAAK;AAEP,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,UAAU;AAAA,MACxC,SAAS,KAAU;AACjB,YAAI,OAAO,IAAI,SAAS,MAAO;AAC7B,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AACA,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAEA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,UAAI;AACF,cAAM,QAAQ,aAAa,GAAG;AAC9B,cAAM,eAAe,IAAI,OAAO;AAEhC,cAAM,EAAE,KAAK,MAAM,OAAO,aAAa,WAAW,IAAI,IAAI,QAAQ,CAAC;AAEnE,cAAM,WAAW,MAAM,iBAAiB,QAAQ;AAAA,UAC9C,IAAI;AAAA,UACJ,OAAO,SAAS;AAAA,QAClB,CAAC;AAED,YAAI,CAAC,UAAU;AACb,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,SAAS,SAAS;AAGxB,YAAI,QAAQ,OAAW,UAAS,MAAM;AACtC,YAAI,SAAS,OAAW,UAAS,OAAO;AACxC,YAAI,UAAU,OAAW,UAAS,QAAQ;AAC1C,YAAI,gBAAgB,OAAW,UAAS,cAAc;AACtD,YAAI,eAAe,OAAW,UAAS,aAAa,CAAC,CAAC;AAEtD,cAAM,SAAS,KAAK;AAEpB,YAAI,WAAW,KAAK;AAElB,gBAAM,oBAAoB;AAAA,YACxB;AAAA,cACE,OAAO,SAAS;AAAA,cAChB,aAAa;AAAA,YACf;AAAA,YACA;AAAA,cACE,OAAO,EAAE,aAAa,OAAO;AAAA,YAC/B;AAAA,UACF;AAGA,gBAAM,oBAAoB;AAAA,YACxB;AAAA,cACE,OAAO,SAAS;AAAA,YAClB;AAAA,YACA;AAAA,cACE,WAAW,EAAE,aAAa,IAAI;AAAA,YAChC;AAAA,UACF;AAAA,QACF;AAEA,eAAO,IAAI,KAAK,QAAQ;AAAA,MAC1B,SAAS,KAAU;AACjB,YAAI,OAAO,IAAI,SAAS,MAAO;AAC7B,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,gBAAQ,MAAM,4BAA4B,GAAG;AAC7C,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAEA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,UAAI;AACF,cAAM,eAAgB,KAAK,MAAM,MAAM,KAAK,OAAO;AAInD,YAAI,CAAC,cAAc;AACjB,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAGA,cAAM,WAAW,MAAM,iBAAiB,QAAQ,EAAE,IAAI,aAAa,CAAC;AACpE,YAAI,CAAC,UAAU;AACb,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,EAAE,KAAK,MAAM,IAAI;AAGvB,cAAM,iBAAiB,UAAU,EAAE,IAAI,aAAa,CAAC;AAGrD,cAAM,oBAAoB;AAAA,UACxB,EAAE,OAAO,SAAS,KAAK;AAAA,UACvB,EAAE,OAAO,EAAE,aAAa,IAAI,EAAE;AAAA,QAChC;AAEA,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,IAAI;AAAA,UACJ,SAAS;AAAA,UACT,mBAAmB;AAAA,YACjB,IAAI,SAAS;AAAA,YACb,KAAK,SAAS;AAAA,YACd,MAAM,SAAS;AAAA,YACf,OAAO,SAAS;AAAA,YAChB,aAAa,SAAS;AAAA,YACtB,YAAY,SAAS;AAAA,YACrB,OAAO,SAAS;AAAA,UAClB;AAAA,QACF,CAAC;AAAA,MACH,SAAS,KAAU;AACjB,gBAAQ,MAAM,4BAA4B,GAAG;AAC7C,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAOA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,UAAI;AACF,cAAM,QAAQ,aAAa,GAAG;AAE9B,cAAM,SAAc,CAAC;AACrB,YAAI,UAAU,MAAM;AAClB,iBAAO,QAAQ;AAAA,QACjB,OAAO;AACL,iBAAO,QAAQ;AAAA,QACjB;AAEA,cAAM,QAAQ,MAAM,oBAAoB,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK;AACjE,eAAO,IAAI,KAAK,KAAK;AAAA,MACvB,SAAS,KAAK;AACZ,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAaA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,UAAI;AACF,cAAM,QAAQ,aAAa,GAAG;AAC9B,cAAM,EAAE,MAAM,YAAY,IAAI,IAAI,QAAQ,CAAC;AAE3C,YAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,WAAW,GAAG;AACxC,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,KAAKA,YAAW;AAEtB,cAAM,MAAM,MAAM,oBAAoB;AAAA,UACpC,EAAE,OAAO,SAAS,MAAM,KAAK;AAAA,UAC7B,EAAE,MAAM,EAAE,YAAY,EAAE;AAAA,UACxB,EAAE,QAAQ,MAAM,KAAK,KAAK;AAAA,QAC5B,EAAE,KAAK;AAEP,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,GAAG;AAAA,MACjC,SAAS,KAAK;AACZ,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAEA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,UAAI;AACF,cAAM,QAAQ,aAAa,GAAG;AAC9B,cAAM,SAAS,IAAI,OAAO;AAE1B,cAAM,EAAE,MAAM,aAAa,YAAY,IAAI,IAAI,QAAQ,CAAC;AAExD,YAAI,CAAC,eAAe,CAAC,MAAM,QAAQ,WAAW,GAAG;AAC/C,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAGA,cAAM,WAAW,MAAM,oBAAoB,SAAS,MAAM;AAE1D,YAAI,CAAC,UAAU;AACb,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,cAAc,SAAS;AAG7B,iBAAS,OAAO;AAChB,iBAAS,cAAc;AACvB,cAAM,SAAS,KAAK;AAEpB,YAAI,gBAAgB,aAAa;AAE/B,gBAAM,QAAQ;AAAA,YACZ;AAAA,cACE,OAAO,SAAS;AAAA,cAChB,OAAO;AAAA,YACT;AAAA,YACA;AAAA,cACE,OAAO,EAAE,OAAO,YAAY;AAAA,YAC9B;AAAA,UACF;AAGA,gBAAM,QAAQ;AAAA,YACZ;AAAA,cACE,OAAO,SAAS;AAAA,cAChB,OAAO,EAAE,KAAK,YAAY;AAAA;AAAA,YAC5B;AAAA,YACA;AAAA,cACE,WAAW,EAAE,OAAO,YAAY;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAEA,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,QAAQ;AAAA,MACtC,SAAS,KAAK;AACZ,gBAAQ,MAAM,sBAAsB,GAAG;AACvC,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAcA,IAAE;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,OAAO,KAA2B,QAAkB;AAClD,UAAI;AACF,cAAM,SAAU,KAAK,MAAM,MAAM,KAAK,OAAO;AAE7C,YAAI,CAAC,QAAQ;AACX,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAGA,YAAI,CAAC,oBAAoB,KAAK,MAAM,GAAG;AACrC,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,UACJ,MAAM,oBAAoB,kBAAkB,MAAM,EAAE,KAAK;AAE3D,YAAI,CAAC,SAAS;AACZ,iBAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YAC1B,OAAO;AAAA,YACP,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,IAAI;AAAA,UACJ,SAAS;AAAA,UACT,aAAa;AAAA,YACX,KAAK,QAAQ;AAAA,YACb,MAAM,QAAQ;AAAA,YACd,OAAO,QAAQ;AAAA,UACjB;AAAA,QACF,CAAC;AAAA,MACH,SAAS,KAAU;AACjB,gBAAQ,MAAM,sBAAsB,GAAG;AACvC,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AGlkBO,IAAM,OAAO;AAAA,EAClB,gBACE,KACA,SASA;AACA,UAAM,cAAc,IAAI,eAAe,EAAE,YAAY;AAErD,UAAM,aAAa,iBAAiB,QAAQ,iBAAiB,CAAC,CAAC;AAC/D,UAAM,WAAW,QAAQ,gBAAgB;AACzC,gBAAY,IAAI,UAAU,UAAU;AAEpC,UAAM,cAAc,kBAAkB,OAAO;AAC7C,UAAM,YAAY,QAAQ,iBAAiB;AAC3C,gBAAY,IAAI,WAAW,WAAW;AAEtC,UAAM,kBAAkB,sBAAsB,OAAO;AACrD,UAAM,gBAAgB,QAAQ,qBAAqB;AACnD,gBAAY,IAAI,eAAe,eAAe;AAE9C,UAAM,cAAc,kBAAkB,OAAO;AAC7C,UAAM,YAAY,QAAQ,iBAAiB;AAC3C,gBAAY,IAAI,WAAW,WAAW;AAEtC,UAAM,iBAAiB,qBAAqB,OAAO;AACnD,UAAM,eAAe,QAAQ,oBAAoB;AACjD,gBAAY,IAAI,cAAc,cAAc;AAE5C,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;ACzDO,SAAS,kBAAkB,eAAuB;AACvD,SAAO,OACL,KACA,KACA,SACG;AACH,QAAI;AACF,cAAQ,IAAI,gCAAgC,aAAa;AACzD,YAAM,OAAO,IAAI;AAEjB,UAAI,CAAC,MAAM;AACT,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,CAAC;AAAA,MACvD;AAEA,YAAM,QAAQ,MAAM,QAAQ,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC;AAExD,UAAI,CAAC,MAAM,QAAQ;AACjB,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,aAAa,QAAQ,WAAW,CAAC;AAAA,MACxE;AAGA,UAAI,MAAM,SAAS,gBAAgB,GAAG;AACpC,eAAO,KAAK;AAAA,MACd;AAGA,YAAM,QACH,KAAK,SACL,KAAK,UACL,KAAK,aACN;AAEF,YAAM,kBAAkB,MAAM,oBAAoB,KAAK;AAAA,QACrD;AAAA,QACA,MAAM,EAAE,KAAK,MAAM;AAAA,MACrB,CAAC,EACE,KAAK,EACL,KAAK;AAER,YAAM,UAAU,oBAAI,IAAY;AAEhC,iBAAW,MAAM,iBAAiB;AAChC,YAAI,MAAM,QAAQ,GAAG,WAAW,GAAG;AACjC,qBAAW,KAAK,GAAG,aAAa;AAC9B,oBAAQ,IAAI,CAAC;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,QAAQ,IAAI,aAAa,GAAG;AAC/B,eAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UAC1B,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAEA,aAAO,KAAK;AAAA,IACd,SAAS,KAAK;AACZ,aAAO,KAAK,GAAG;AAAA,IACjB;AAAA,EACF;AACF;;;AC/DO,SAASC,mBAAkB,YAAoB;AACpD,SAAO,CAAC,KAAc,KAAe,SAAuB;AAC1D,UAAM,OAAQ,IAAY;AAE1B,QAAI,CAAC,MAAM;AACT,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,CAAC;AAAA,IACvD;AAEA,QAAI,CAAC,YAAY;AACf,aAAO,KAAK;AAAA,IACd;AAEA,QAAI,CAAC,cAAc,MAAM,UAAU,GAAG;AACpC,aAAO,IAAI,OAAO,GAAG,EAAE,KAAK;AAAA,QAC1B,OAAO;AAAA,QACP,QAAQ;AAAA,QACR;AAAA,QACA,iBAAiB,KAAK;AAAA,MACxB,CAAC;AAAA,IACH;AAEA,SAAK;AAAA,EACP;AACF;;;AC/BA,SAAS,kBAAkB,gBAAgB;AAC3C,SAAS,cAAAC,mBAAkB;AAGpB,IAAM,iBAAN,MAAqB;AAAA,EAClB;AAAA,EAER,cAAc;AACZ,SAAK,KAAK,IAAI,SAAS;AAAA,MACrB,QAAQ,QAAQ,IAAI;AAAA,MACpB,aAAa;AAAA,QACX,aAAa,QAAQ,IAAI;AAAA,QACzB,iBAAiB,QAAQ,IAAI;AAAA,MAC/B;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,kBAAkB,QAAgB,UAAkB,KAAa;AACrE,UAAM,MAAM,GAAGA,YAAW,CAAC,IAAI,GAAG;AAClC,UAAM,KAAK,GAAG;AAAA,MACZ,IAAI,iBAAiB;AAAA,QACnB,QAAQ,QAAQ,IAAI;AAAA,QACpB,KAAK;AAAA,QACL,MAAM;AAAA,QACN,KAAK;AAAA,QACL,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AACA,WAAO,WAAW,QAAQ,IAAI,aAAc,OAAO,QAAQ,IAAI,UAAW,kBAAkB,GAAG;AAAA,EACjG;AACF;;;AC9BA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;;;ACP1B,SAAS,mBAAmB;AAErB,IAAM,kBAAkB;AAMxB,IAAM,cAAc,IAAI,gBAC7B,YAAY,iBAAiB,WAAW;;;ACT1C,SAAS,eAAAC,oBAAmB;AAErB,IAAM,YAAY;AAMlB,IAAM,QAAQ,IAAI,UAAoBA,aAAY,WAAW,KAAK;;;AFWlE,IAAM,aAAN,cAAyB,UAAU,OAAO,EAAE;AAAA,EAChC,YAAY,IAAI,UAAU;AAAA,EAE3C,cAAc;AACZ,UAAM;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAc,KAAU,MAAW,MAAW,SAAmB;AAE/D,QAAI,OAAO,CAAC,MAAM;AAChB,YAAM,UACJ,KAAK,WAAW,MAAM,WAAW;AACnC,YAAM,IAAI,sBAAsB,OAAO;AAAA,IACzC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,SAA6C;AAC7D,QAAI;AAGF,UAAI;AACJ,UAAI;AACF,wBAAiB,MAAM,MAAM,YAAY,OAAO;AAAA,MAClD,SAAS,eAAoB;AAE3B,cAAM,UAAU,eAAe,WAAW;AAC1C,cAAM,IAAI,sBAAsB,OAAO;AAAA,MACzC;AAEA,UAAI,CAAC,eAAe;AAClB,cAAM,IAAI,sBAAsB,yBAAyB;AAAA,MAC3D;AAEA,YAAM,UAAU,QAAQ,aAAa,EAAE,WAAW;AAClD,YAAM,OAAQ,QAAgB;AAG9B,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,sBAAsB,yBAAyB;AAAA,MAC3D;AAGA,YAAM,gBAAgB,KAAK,UAAU;AAAA,QACnC;AAAA,QACA,CAAC,QAAQ,WAAW,GAAG,QAAQ,SAAS,CAAC;AAAA,MAC3C;AAGA,YAAM,sBAAsB,KAAK,UAAU;AAAA,QACzC;AAAA,QACA,CAAC,QAAQ,WAAW,GAAG,QAAQ,SAAS,CAAC;AAAA,MAC3C;AAGA,UAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,YAAI,CAAC,WAAW,MAAM,aAAa,GAAG;AACpC,gBAAM,IAAI;AAAA,YACR,0BAA0B,cAAc,KAAK,IAAI,CAAC;AAAA,UACpD;AAAA,QACF;AAAA,MACF;AAGA,UAAI,uBAAuB,oBAAoB,SAAS,GAAG;AACzD,YAAI,CAAC,iBAAiB,MAAM,mBAAmB,GAAG;AAChD,gBAAM,IAAI;AAAA,YACR,gCAAgC,oBAAoB,KAAK,IAAI,CAAC;AAAA,UAChE;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,UACE,iBAAiB,yBACjB,iBAAiB,oBACjB;AACA,cAAM;AAAA,MACR;AAEA,YAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACvD,YAAM,IAAI,sBAAsB,gBAAgB,uBAAuB;AAAA,IACzE;AAAA,EACF;AACF;AA3Fa,aAAN;AAAA,EADN,WAAW;AAAA,GACC;;;AGnBb,SAAS,4BAA8C;AAOhD,IAAM,wBAAwB;AAAA,EACnC,CAAC,MAAe,QAA+C;AAC7D,UAAM,UAAU,IAAI,aAAa,EAAE,WAAW;AAC9C,WAAQ,QAAgB,QAAQ;AAAA,EAClC;AACF;;;ACXA,SAAS,gBAAgB;AAWlB,IAAM,gBAAN,cAA4B,SAAS;AAAA,EAC1C,OAAO;AAAA,EAEP,MAAM,aAAa,KAAc;AAC/B,QAAI;AACF,YAAM,SAAS,IAAI,QAAQ,WAAW;AACtC,YAAM,SAAS,IAAI,QAAQ,WAAW;AAEtC,UAAI,QAAQ;AACV,YAAI,WAAW,QAAQ,IAAI,gBAAgB;AACzC,iBAAO,KAAK,KAAK,EAAE,SAAS,kBAAkB,GAAG,GAAG;AAAA,QACtD;AACA,YAAI,CAAC,QAAQ;AACX,iBAAO,KAAK,KAAK,EAAE,SAAS,sBAAsB,GAAG,GAAG;AAAA,QAC1D;AAEA,cAAM,OAAO,MAAM,QAAQ,QAAQ;AAAA,UACjC,IAAI;AAAA,UACJ,OAAO,QAAQ,IAAI,UAAU;AAAA,QAC/B,CAAC;AAED,YAAI,CAAC,MAAM;AACT,iBAAO,KAAK,KAAK,EAAE,SAAS,iBAAiB,GAAG,GAAG;AAAA,QACrD;AAGA,cAAM,UAAU,aAAa,IAAI;AAGjC,QAAC,IAAY,OAAO;AAEpB,eAAO,KAAK,QAAQ,OAAO;AAAA,MAC7B,OAAO;AAGL,cAAM,QAAQ,aAAa,GAAG;AAE9B,YAAI,CAAC,OAAO;AACV,iBAAO,KAAK,KAAK,EAAE,SAAS,gBAAgB,GAAG,GAAG;AAAA,QACpD;AAGA,kBAAU,KAAK,EACZ,KAAK,CAAC,WAAW;AAEhB,gBAAM,UAAU,aAAa,MAAM;AAGnC,UAAC,IAAY,OAAO;AAGpB,iBAAO,KAAK,QAAQ,OAAO;AAAA,QAC7B,CAAC,EACA,MAAM,CAAC,UAAe;AAErB,iBAAO,KAAK;AAAA,YACV,EAAE,SAAS,OAAO,WAAW,eAAe;AAAA,YAC5C;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACL;AAAA,IACF,SAAS,OAAY;AACnB,aAAO,KAAK,KAAK,EAAE,SAAS,OAAO,WAAW,eAAe,GAAG,GAAG;AAAA,IACrE;AAAA,EACF;AACF;AAKO,SAAS,sBAAsB;AACpC,SAAO,IAAI,cAAc;AAC3B;","names":["bcrypt","jwt","mongoose","mongoose","jwt","mongoose","Schema","jwt","jwt","bcrypt","jwt","email","express","Router","Router","express","Router","Router","randomUUID","mongoose","mongoose","randomUUID","Router","bcrypt","randomUUID","express","Router","mongoose","Schema","Router","express","bcrypt","randomUUID","requirePermission","randomUUID","SetMetadata"]}
1
+ {"version":3,"sources":["../src/express/index.ts","../src/express/auth.routes.ts","../src/config/loadConfig.ts","../src/config/index.ts","../src/core/utils.ts","../src/core/roles.config.ts","../src/core/session.ts","../src/models/rolePermission.model.ts","../src/models/user.model.ts","../src/utils/extract.ts","../src/utils/jwt.ts","../src/middlewares/auth.middleware.ts","../src/middlewares/validators.ts","../src/models/invite.model.ts","../src/services/auth-admin.service.ts","../src/models/client.model.ts","../src/services/email.service.ts","../src/templates/email.templates.ts","../src/express/dashboards.routes.ts","../src/express/email.routes.ts","../src/express/projects.routes.ts","../src/services/projects.service.ts","../src/models/moduleConnection.model.ts","../src/models/project.model.ts","../src/express/admin/admin.routes.ts","../src/middlewares/requireRole.ts","../src/models/permissions.model.ts","../src/nest/index.ts","../src/middlewares/permission.middleware.ts","../src/middlewares/requirePermission.ts","../src/services/upload.service.ts","../src/nest/authx.guard.ts","../src/nest/decorators/permissions.decorator.ts","../src/nest/decorators/roles.decorator.ts","../src/nest/decorators/session.decorator.ts","../src/passport/authx.strategy.ts"],"sourcesContent":["export { createAuthRouter } from './auth.routes';\r\nexport { createDashboardRouter } from './dashboards.routes';\r\nexport { createEmailRouter } from './email.routes';\r\nexport { createProjectsRouter } from './projects.routes';\r\nexport { createAdminRouter } from './admin/admin.routes';\r\n","import type { AuthCookieConfig, AuthRouterOptions } from 'aaspai-types';\r\nimport bcrypt from 'bcryptjs';\r\nimport { randomUUID } from 'crypto';\r\nimport express, {\r\n CookieOptions,\r\n Router,\r\n type Router as ExpressRouter,\r\n} from 'express';\r\nimport jwt from 'jsonwebtoken';\r\nimport { configureAuthX } from '../config/index.js';\r\nimport {\r\n baseProjectCookieOptionsFrom,\r\n buildClearCookieOptions,\r\n} from '../core/utils.js';\r\nimport { requireAuth } from '../middlewares/auth.middleware.js';\r\nimport {\r\n isPasswordStrong,\r\n validateLogin,\r\n validateResendEmail,\r\n validateResetPassword,\r\n validateSendInvite,\r\n validateSignup,\r\n} from '../middlewares/validators.js';\r\nimport { Invite } from '../models/invite.model.js';\r\nimport { OrgUser } from '../models/user.model.js';\r\nimport { AuthAdminService } from '../services/auth-admin.service.js';\r\nimport { EmailService } from '../services/email.service.js';\r\nimport {\r\n buildResetPasswordEmailTemplate,\r\n buildVerificationEmailTemplate,\r\n} from '../templates/email.templates.js';\r\n\r\nexport function createAuthRouter(\r\n options: AuthRouterOptions = {},\r\n): ExpressRouter {\r\n const googleClientId = process.env.GOOGLE_CLIENT_ID;\r\n const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;\r\n const googleRedirectUri = process.env.GOOGLE_REDIRECT_URI;\r\n const googleDefaultRedirect =\r\n process.env.GOOGLE_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || '/';\r\n\r\n const isGoogleEnabled =\r\n !!googleClientId && !!googleClientSecret && !!googleRedirectUri;\r\n\r\n if (options.config) {\r\n configureAuthX(options.config);\r\n }\r\n\r\n const r = Router();\r\n\r\n const email = new EmailService();\r\n const authAdmin = new AuthAdminService();\r\n\r\n const isProdEnv = process.env.NODE_ENV === 'production';\r\n\r\n const cookieConfig: AuthCookieConfig = {\r\n sameSite: options.cookie?.sameSite ?? (isProdEnv ? 'none' : 'lax'), // default if not provided\r\n secure: options.cookie?.secure ?? isProdEnv, // default: secure in prod\r\n domain: options.cookie?.domain ?? undefined,\r\n path: options.cookie?.path ?? '/',\r\n maxAgeMs: options.cookie?.maxAgeMs ?? 30 * 24 * 60 * 60 * 1000,\r\n };\r\n\r\n r.use(express.json());\r\n r.use(express.urlencoded({ extended: true }));\r\n\r\n r.get('/healthz', (_req, res) =>\r\n res.json({ status: 'ok', server: 'org-server' }),\r\n );\r\n\r\n r.post('/login', validateLogin, async (req, res) => {\r\n const { email: emailAddress, password } = req.body || {};\r\n\r\n try {\r\n // 1. Find user in your DB\r\n const user = await OrgUser.findOne({ email: emailAddress })\r\n .select('+password')\r\n .lean();\r\n\r\n if (!user) {\r\n return res.status(400).json({\r\n error: 'Invalid email or password',\r\n code: 'INVALID_CREDENTIALS',\r\n });\r\n }\r\n\r\n // 2. Check if email is verified\r\n if (!user.emailVerified) {\r\n return res.status(400).json({\r\n error: 'Please verify your email before logging in.',\r\n code: 'EMAIL_NOT_VERIFIED',\r\n });\r\n }\r\n\r\n // 3. CRITICAL: Verify password with bcrypt\r\n const isPasswordValid = user.passwordHash\r\n ? await bcrypt.compare(password, user.passwordHash)\r\n : false;\r\n\r\n if (!isPasswordValid) {\r\n return res.status(400).json({\r\n error: 'Invalid email or password',\r\n code: 'INVALID_CREDENTIALS',\r\n });\r\n }\r\n\r\n // 4. Generate tokens\r\n const tokens = generateTokens(user);\r\n setAuthCookies(res, tokens, cookieConfig);\r\n\r\n // 5. Set projectId cookie if exists\r\n if (user.projectId) {\r\n res.cookie(options.projectCookieName || 'projectId', user.projectId, {\r\n ...baseProjectCookieOptionsFrom(cookieConfig),\r\n httpOnly: true,\r\n } as any);\r\n }\r\n\r\n return res.json({\r\n message: 'Login successful',\r\n user: toUserResponse(user),\r\n });\r\n } catch (err: any) {\r\n console.error('Login error:', err);\r\n return res.status(500).json({ error: 'Internal server error' });\r\n }\r\n });\r\n\r\n r.post('/signup', validateSignup, async (req, res) => {\r\n const {\r\n firstName,\r\n lastName,\r\n email: emailAddress,\r\n password,\r\n projectId,\r\n metadata,\r\n } = req.body || {};\r\n\r\n const COMPANY_NAME = process.env.COMPANY_NAME;\r\n\r\n try {\r\n const kcUser = await authAdmin.createUserInRealm({\r\n username: emailAddress,\r\n email: emailAddress,\r\n firstName,\r\n lastName,\r\n projectId,\r\n credentials: [{ type: 'password', value: password, temporary: false }],\r\n metadata,\r\n });\r\n\r\n await authAdmin.assignRealmRole(kcUser.id, 'platform_user');\r\n\r\n const user = await OrgUser.findOneAndUpdate(\r\n { email: kcUser.email },\r\n {\r\n email: kcUser.email,\r\n firstName,\r\n lastName,\r\n projectId,\r\n metadata,\r\n roles: ['platform_user'],\r\n emailVerified: false,\r\n },\r\n { upsert: true, new: true, setDefaultsOnInsert: true },\r\n );\r\n\r\n const emailResult = await sendRateLimitedEmail({\r\n emailService: email,\r\n user,\r\n subject: 'Verify your email',\r\n // html: buildVerificationTemplate(\r\n // email.sign({ userId: kcUser.id, email: kcUser.email }),\r\n // options,\r\n // ),\r\n html: buildVerificationEmailTemplate({\r\n firstName: user.firstName,\r\n verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(\r\n {\r\n userId: user.id,\r\n email: user.email,\r\n },\r\n )}`,\r\n expiresIn: '1 hour',\r\n }),\r\n from: COMPANY_NAME,\r\n });\r\n\r\n if (emailResult.rateLimited) {\r\n return res.status(429).json({\r\n ok: false,\r\n error: 'Too many verification emails sent. Please try again later.',\r\n waitMs: emailResult.waitMs,\r\n });\r\n }\r\n\r\n return res.json({\r\n id: user.id,\r\n email: user.email,\r\n message: 'Verification email sent. Please check your inbox.',\r\n });\r\n } catch (err: any) {\r\n return respondWithKeycloakError(res, err, 'Signup failed');\r\n }\r\n });\r\n\r\n r.get('/me', requireAuth(), (req, res) => {\r\n return res.json((req as any).user || null);\r\n });\r\n\r\n r.post('/logout', async (_req, res) => {\r\n const clearOptions = buildClearCookieOptions(cookieConfig);\r\n\r\n res.clearCookie('access_token', clearOptions);\r\n res.clearCookie('refresh_token', clearOptions);\r\n res.json({ ok: true });\r\n });\r\n\r\n r.put('/:userId/metadata', requireAuth(), async (req, res) => {\r\n const { userId } = req.params as any;\r\n const { metadata } = req.body || {};\r\n\r\n const user = await OrgUser.findOne({ id: userId });\r\n\r\n if (!user)\r\n return res.status(404).json({ ok: false, error: 'User not found' });\r\n\r\n const map = new Map<string, any>(\r\n ((user as any).metadata || []).map((m: any) => [m.key, m.value]),\r\n );\r\n\r\n for (const item of metadata || []) map.set(item.key, item.value);\r\n (user as any).metadata = Array.from(map.entries()).map(([key, value]) => ({\r\n key,\r\n value,\r\n }));\r\n\r\n await (user as any).save();\r\n res.json({ ok: true, metadata: (user as any).metadata });\r\n });\r\n\r\n r.get('/verify-email', async (req, res) => {\r\n const token = String(req.query.token || '');\r\n if (!token) {\r\n return res.status(400).json({ error: 'Verification token is required' });\r\n }\r\n try {\r\n const payload = email.verify<{ email: string; userId: string }>(token);\r\n await authAdmin.updateUserEmailVerified(payload.userId, true);\r\n await OrgUser.updateOne(\r\n { id: payload.userId },\r\n { $set: { emailVerified: true } },\r\n );\r\n res.json({ ok: true, message: 'Email verified' });\r\n } catch (err: any) {\r\n res\r\n .status(400)\r\n .json({ ok: false, error: err?.message || 'Invalid token' });\r\n }\r\n });\r\n\r\n r.post(\r\n '/resend-verification-email',\r\n validateResendEmail,\r\n async (req, res) => {\r\n const COMPANY_NAME = process.env.COMPANY_NAME;\r\n\r\n const user = await OrgUser.findOne({ email: req.body.email });\r\n if (!user)\r\n return res.status(404).json({ ok: false, error: 'User not found' });\r\n\r\n const verified = await authAdmin.isUserEmailVerified(user.id);\r\n if (verified) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Email is already verified' });\r\n }\r\n\r\n const token = email.sign({\r\n email: user.email,\r\n userId: user.id,\r\n });\r\n\r\n const resendResult = await sendRateLimitedEmail({\r\n emailService: email,\r\n user,\r\n subject: 'Verify your email',\r\n // html: buildVerificationTemplate(token, options),\r\n html: buildVerificationEmailTemplate({\r\n firstName: user.firstName,\r\n verificationUrl: `${getFrontendBaseUrl(options)}/verify-email?token=${email.sign(\r\n {\r\n userId: user.id,\r\n email: user.email,\r\n },\r\n )}`,\r\n expiresIn: '1 hour',\r\n }),\r\n from: COMPANY_NAME,\r\n });\r\n\r\n if (resendResult.rateLimited) {\r\n return res.status(429).json({\r\n ok: false,\r\n error: 'Too many verification emails sent. Please try again later.',\r\n waitMs: resendResult.waitMs,\r\n });\r\n }\r\n\r\n res.json({ ok: true });\r\n },\r\n );\r\n\r\n r.post('/forgot-password', validateResendEmail, async (req, res) => {\r\n const COMPANY_NAME = process.env.COMPANY_NAME;\r\n\r\n const user = await OrgUser.findOne({ email: req.body.email });\r\n if (!user)\r\n return res.status(404).json({ ok: false, error: 'User not found' });\r\n\r\n const resetToken = email.sign(\r\n {\r\n userId: user.id,\r\n email: user.email,\r\n firstName: user.firstName,\r\n lastName: user.lastName,\r\n },\r\n 60 * 60,\r\n );\r\n\r\n const resetResult = await sendRateLimitedEmail({\r\n emailService: email,\r\n user,\r\n subject: 'Reset password',\r\n // html: buildResetTemplate(resetToken, options),\r\n html: buildResetPasswordEmailTemplate({\r\n firstName: user.firstName,\r\n resetUrl: `${getFrontendBaseUrl(options)}/reset-password?token=${email.sign(\r\n {\r\n userId: user.id,\r\n email: user.email,\r\n },\r\n )}`,\r\n expiresIn: '1 hour',\r\n }),\r\n from: COMPANY_NAME,\r\n });\r\n\r\n if (resetResult.rateLimited) {\r\n return res.status(429).json({\r\n ok: false,\r\n error: 'Please wait before requesting another password reset email.',\r\n waitMs: resetResult.waitMs,\r\n });\r\n }\r\n\r\n res.json({ ok: true, message: 'Password reset email sent' });\r\n });\r\n\r\n r.post('/reset-password', validateResetPassword, async (req, res) => {\r\n const { token, newPassword } = (req.body || {}) as any;\r\n\r\n if (!token || !newPassword) {\r\n return res.status(400).json({\r\n ok: false,\r\n error: 'Token and new password are required',\r\n code: 'MISSING_FIELDS',\r\n });\r\n }\r\n\r\n try {\r\n const payload = email.verify<{\r\n userId: string;\r\n email: string;\r\n iat: number;\r\n }>(token);\r\n\r\n const user = await OrgUser.findOne({ id: payload.userId });\r\n if (!user) {\r\n return res.status(404).json({ ok: false, error: 'User not found' });\r\n }\r\n\r\n if (\r\n user.lastPasswordReset &&\r\n payload.iat * 1000 < user.lastPasswordReset.getTime()\r\n ) {\r\n return res.status(400).json({\r\n ok: false,\r\n error:\r\n 'This reset link has already been used. Please request a new one.',\r\n });\r\n }\r\n\r\n await authAdmin.updateUserPassword(payload.userId, newPassword);\r\n\r\n user.lastPasswordReset = new Date();\r\n await user.save();\r\n\r\n res.json({ ok: true, message: 'Password updated successfully' });\r\n } catch (err: any) {\r\n res\r\n .status(400)\r\n .json({ ok: false, error: err?.message || 'Invalid or expired token' });\r\n }\r\n });\r\n\r\n r.post(\r\n '/send-invite',\r\n requireAuth(),\r\n validateSendInvite,\r\n async (req, res) => {\r\n const { email: emailAddress, role } = req.body || {};\r\n\r\n const existingUser = await OrgUser.findOne({ email: emailAddress });\r\n if (existingUser) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'User with this email already exists' });\r\n }\r\n\r\n const existingInvite = await Invite.findOne({\r\n email: emailAddress,\r\n isUsed: false,\r\n isExpired: false,\r\n });\r\n\r\n if (existingInvite) {\r\n return res.status(400).json({\r\n ok: false,\r\n error: 'An active invite already exists for this email',\r\n });\r\n }\r\n\r\n const token = email.sign({\r\n email: emailAddress,\r\n role,\r\n inviteId: randomUUID(),\r\n });\r\n\r\n const invite = await Invite.create({\r\n id: token,\r\n email: emailAddress,\r\n role,\r\n invitedBy: (req as any).user?.sub,\r\n isUsed: false,\r\n expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),\r\n });\r\n\r\n await email.send(\r\n emailAddress,\r\n 'You are invited',\r\n `<a href=\"${getFrontendBaseUrl(options)}/auth/accept-invite?token=${token}\">Accept</a>`,\r\n );\r\n\r\n res.json({\r\n ok: true,\r\n inviteId: invite.id,\r\n email: invite.email,\r\n role: invite.role,\r\n expiresAt: invite.expiresAt,\r\n });\r\n },\r\n );\r\n\r\n r.get('/accept-invite', async (req, res) => {\r\n const inv = await Invite.findOne({ id: String(req.query.token) });\r\n res.json({ ok: !!inv && !(inv as any).isUsed && !(inv as any).isExpired });\r\n });\r\n\r\n r.post('/accept-invite', async (req, res) => {\r\n const { token, firstName, lastName, password, projectId } = req.body || {};\r\n\r\n if (\r\n !token ||\r\n !firstName ||\r\n !lastName ||\r\n !isPasswordStrong(password || '')\r\n ) {\r\n return res.status(400).json({ ok: false, error: 'Invalid payload' });\r\n }\r\n\r\n const invite = await Invite.findOne({\r\n id: token,\r\n isUsed: false,\r\n isExpired: false,\r\n });\r\n\r\n if (!invite) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Invitation not found or already used' });\r\n }\r\n\r\n if (invite.expiresAt && invite.expiresAt.getTime() < Date.now()) {\r\n invite.isExpired = true;\r\n await invite.save();\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Invitation has expired' });\r\n }\r\n\r\n try {\r\n const kcUser = await authAdmin.createUserInRealm({\r\n username: invite.email,\r\n email: invite.email,\r\n firstName,\r\n lastName,\r\n projectId,\r\n emailVerified: true,\r\n credentials: [{ type: 'password', value: password, temporary: false }],\r\n });\r\n\r\n await authAdmin.assignRealmRole(kcUser.id, invite.role);\r\n\r\n await OrgUser.findOneAndUpdate(\r\n { email: invite.email },\r\n {\r\n id: kcUser.id,\r\n email: invite.email,\r\n firstName,\r\n lastName,\r\n roles: [invite.role],\r\n emailVerified: true,\r\n },\r\n { upsert: true, new: true, setDefaultsOnInsert: true },\r\n );\r\n\r\n invite.isUsed = true;\r\n invite.usedAt = new Date();\r\n invite.usedBy = kcUser.id;\r\n await invite.save();\r\n\r\n res.json({\r\n ok: true,\r\n message: 'Account created successfully.',\r\n email: invite.email,\r\n });\r\n } catch (err: any) {\r\n res.status(400).json({\r\n ok: false,\r\n error:\r\n err?.response?.data?.error_description ||\r\n err?.message ||\r\n 'Failed to create account',\r\n });\r\n }\r\n });\r\n\r\n r.get('/invites', requireAuth(), async (_req, res) => {\r\n const invites = await Invite.find().sort({ createdAt: -1 }).lean();\r\n res.json(invites);\r\n });\r\n\r\n r.delete('/invites/:inviteId', requireAuth(), async (req, res) => {\r\n await Invite.deleteOne({ id: req.params.inviteId });\r\n res.json({ ok: true });\r\n });\r\n\r\n r.get('/get-user-by-email', async (req, res) => {\r\n const user = await OrgUser.findOne({ email: req.query.email }).lean();\r\n res.json(user || null);\r\n });\r\n\r\n r.get('/google', (req, res) => {\r\n if (!isGoogleEnabled) {\r\n return res.status(500).json({ error: 'Google login not configured' });\r\n }\r\n\r\n // ✅ Encode BOTH redirectTo AND projectId in state as JSON\r\n const stateData = {\r\n redirectTo: req.query.redirectTo || '',\r\n projectId: req.query.projectId || process.env.DEFAULT_PROJECT_ID || '',\r\n };\r\n\r\n // const state = req.query.redirectTo\r\n // ? encodeURIComponent(String(req.query.redirectTo))\r\n // : '';\r\n\r\n // const projectId = req.query.projectId\r\n // ? encodeURIComponent(String(req.query.projectId))\r\n // : '';\r\n\r\n const state = encodeURIComponent(JSON.stringify(stateData));\r\n\r\n const params = new URLSearchParams({\r\n client_id: googleClientId!,\r\n redirect_uri: googleRedirectUri!,\r\n response_type: 'code',\r\n scope: 'openid email profile',\r\n access_type: 'offline',\r\n prompt: 'consent',\r\n state,\r\n });\r\n\r\n const url = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;\r\n\r\n console.log(url, 'url');\r\n\r\n // Easiest: just redirect user to Google\r\n res.redirect(url);\r\n });\r\n\r\n r.get('/google/callback', async (req, res) => {\r\n if (!isGoogleEnabled) {\r\n return res.status(500).json({ error: 'Google login not configured' });\r\n }\r\n\r\n const code = String(req.query.code || '');\r\n // const state = req.query.state ? String(req.query.state) : '';\r\n // const projectId = req.query.projectId ? String(req.query.projectId) : '';\r\n // console.log(projectId, 'projectId');\r\n\r\n let stateData = { redirectTo: '', projectId: '' };\r\n try {\r\n if (req.query.state) {\r\n stateData = JSON.parse(decodeURIComponent(String(req.query.state)));\r\n }\r\n } catch (err) {\r\n console.error('Failed to parse state:', err);\r\n // Use defaults\r\n }\r\n\r\n const { redirectTo, projectId } = stateData;\r\n console.log(\r\n 'Parsed state - redirectTo:',\r\n redirectTo,\r\n 'projectId:',\r\n projectId,\r\n );\r\n\r\n if (!code) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Missing authorization code' });\r\n }\r\n\r\n try {\r\n // 1. Exchange code for Google tokens\r\n const tokenRes = await fetch('https://oauth2.googleapis.com/token', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\r\n body: new URLSearchParams({\r\n code,\r\n client_id: googleClientId!,\r\n client_secret: googleClientSecret!,\r\n redirect_uri: googleRedirectUri!,\r\n grant_type: 'authorization_code',\r\n }),\r\n });\r\n\r\n if (!tokenRes.ok) {\r\n const errJson = await tokenRes.json().catch(() => ({}));\r\n console.error('Google token error', errJson);\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Failed to exchange Google code' });\r\n }\r\n\r\n const tokenJson = (await tokenRes.json()) as {\r\n id_token: string;\r\n access_token: string;\r\n refresh_token?: string;\r\n expires_in?: number;\r\n };\r\n\r\n if (!tokenJson.id_token) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Missing id_token from Google' });\r\n }\r\n\r\n // 2. Decode ID token (for production, verify signature against Google JWKs)\r\n const decoded: any = jwt.decode(tokenJson.id_token);\r\n\r\n const email = decoded?.email;\r\n if (!email) {\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Google account has no email' });\r\n }\r\n\r\n const emailVerified = decoded.email_verified ?? true;\r\n const firstName = decoded.given_name || '';\r\n const lastName = decoded.family_name || '';\r\n\r\n // 3. Find or create local user\r\n let user = await OrgUser.findOne({ email }).lean();\r\n\r\n if (!user) {\r\n const finalProjectId = projectId || process.env.DEFAULT_PROJECT_ID;\r\n\r\n if (!finalProjectId) {\r\n console.error('No projectId available for new user');\r\n const errorRedirect =\r\n (redirectTo || googleDefaultRedirect) +\r\n (redirectTo?.includes('?') ? '&' : '?') +\r\n 'error=missing_project_id';\r\n return res.redirect(errorRedirect);\r\n }\r\n\r\n const created = await OrgUser.create({\r\n email,\r\n firstName,\r\n lastName,\r\n emailVerified,\r\n roles: ['platform_user'],\r\n projectId: finalProjectId,\r\n metadata: [],\r\n // you can also store googleId: decoded.sub\r\n });\r\n\r\n user = created.toObject();\r\n }\r\n\r\n // 4. Generate your own tokens & set cookies with SAME config as normal login\r\n const tokens = generateTokens(user);\r\n setAuthCookies(res, tokens, cookieConfig);\r\n\r\n // 5. Set projectId cookie if exists\r\n if (user.projectId) {\r\n res.cookie(options.projectCookieName || 'projectId', user.projectId, {\r\n ...baseProjectCookieOptionsFrom(cookieConfig),\r\n httpOnly: true,\r\n } as any);\r\n }\r\n\r\n // 6. Redirect to frontend\r\n const finalRedirect = redirectTo || googleDefaultRedirect;\r\n\r\n res.redirect(finalRedirect);\r\n } catch (err: any) {\r\n console.error('Google callback error', err);\r\n const redirectError = googleDefaultRedirect.includes('?')\r\n ? `${googleDefaultRedirect}&error=google_login_failed`\r\n : `${googleDefaultRedirect}?error=google_login_failed`;\r\n\r\n res.redirect(redirectError);\r\n }\r\n });\r\n\r\n r.get('/github', (req, res) => {\r\n const githubClientId = process.env.GITHUB_CLIENT_ID;\r\n const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;\r\n\r\n if (!githubClientId || !githubRedirectUri) {\r\n return res.status(500).json({ error: 'GitHub login not configured' });\r\n }\r\n\r\n const state = req.query.redirectTo\r\n ? encodeURIComponent(String(req.query.redirectTo))\r\n : '';\r\n\r\n const params = new URLSearchParams({\r\n client_id: githubClientId,\r\n redirect_uri: githubRedirectUri,\r\n scope: 'user:email',\r\n state,\r\n allow_signup: 'true',\r\n });\r\n\r\n const url = `https://github.com/login/oauth/authorize?${params.toString()}`;\r\n res.redirect(url);\r\n });\r\n\r\n r.get('/github/callback', async (req, res) => {\r\n const githubClientId = process.env.GITHUB_CLIENT_ID;\r\n const githubClientSecret = process.env.GITHUB_CLIENT_SECRET;\r\n const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;\r\n const githubDefaultRedirect =\r\n process.env.GITHUB_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || '/';\r\n\r\n if (!githubClientId || !githubClientSecret || !githubRedirectUri) {\r\n return res.status(500).json({ error: 'GitHub login not configured' });\r\n }\r\n\r\n const code = String(req.query.code || '');\r\n const state = req.query.state ? String(req.query.state) : '';\r\n\r\n if (!code) {\r\n return res.status(400).json({ ok: false, error: 'Missing GitHub code' });\r\n }\r\n\r\n try {\r\n // ✅ 1. Exchange code for access token\r\n const tokenRes = await fetch(\r\n 'https://github.com/login/oauth/access_token',\r\n {\r\n method: 'POST',\r\n headers: {\r\n Accept: 'application/json',\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n body: new URLSearchParams({\r\n client_id: githubClientId,\r\n client_secret: githubClientSecret,\r\n code,\r\n redirect_uri: githubRedirectUri,\r\n }),\r\n },\r\n );\r\n\r\n const tokenJson = (await tokenRes.json()) as {\r\n access_token?: string;\r\n token_type?: string;\r\n scope?: string;\r\n error?: string;\r\n error_description?: string;\r\n };\r\n\r\n if (!tokenJson.access_token) {\r\n console.error('GitHub token error:', tokenJson);\r\n return res\r\n .status(400)\r\n .json({ ok: false, error: 'Failed to get GitHub access token' });\r\n }\r\n\r\n const accessToken = tokenJson.access_token;\r\n\r\n // ✅ 2. Get GitHub user profile\r\n const userRes = await fetch('https://api.github.com/user', {\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n Accept: 'application/vnd.github+json',\r\n },\r\n });\r\n\r\n const githubUser = (await userRes.json()) as {\r\n id: number;\r\n name?: string;\r\n login: string;\r\n };\r\n\r\n // ✅ 3. Get verified email (GitHub may not return it in profile)\r\n const emailRes = await fetch('https://api.github.com/user/emails', {\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n Accept: 'application/vnd.github+json',\r\n },\r\n });\r\n\r\n const emails = (await emailRes.json()) as {\r\n email: string;\r\n primary: boolean;\r\n verified: boolean;\r\n visibility?: string;\r\n }[];\r\n\r\n const primaryEmail = emails?.find(\r\n (e: any) => e.primary && e.verified,\r\n )?.email;\r\n\r\n if (!primaryEmail) {\r\n return res.status(400).json({\r\n ok: false,\r\n error: 'GitHub account has no verified email',\r\n });\r\n }\r\n\r\n const firstName = githubUser.name?.split(' ')[0] || '';\r\n const lastName = githubUser.name?.split(' ').slice(1).join(' ') || '';\r\n\r\n // ✅ 4. Find or create user in DB\r\n let user = await OrgUser.findOne({ email: primaryEmail }).lean();\r\n\r\n if (!user) {\r\n const created = await OrgUser.create({\r\n email: primaryEmail,\r\n firstName,\r\n lastName,\r\n emailVerified: true,\r\n roles: ['platform_user'],\r\n projectId: null,\r\n metadata: [],\r\n githubId: githubUser.id,\r\n });\r\n\r\n user = created.toObject();\r\n }\r\n\r\n // ✅ 5. Generate your JWT cookies (same as Google)\r\n const tokens = generateTokens(user);\r\n setAuthCookies(res, tokens, cookieConfig);\r\n\r\n // ✅ 6. Redirect to frontend\r\n const redirectTo = state\r\n ? decodeURIComponent(state)\r\n : githubDefaultRedirect;\r\n\r\n res.redirect(redirectTo);\r\n } catch (err: any) {\r\n console.error('GitHub callback error:', err);\r\n\r\n const redirectError = githubDefaultRedirect.includes('?')\r\n ? `${githubDefaultRedirect}&error=github_login_failed`\r\n : `${githubDefaultRedirect}?error=github_login_failed`;\r\n\r\n res.redirect(redirectError);\r\n }\r\n });\r\n\r\n r.get('/get-users', async (req, res) => {\r\n const user = await OrgUser.find({ projectId: req.query.projectId }).lean();\r\n res.json(user || null);\r\n });\r\n\r\n return r;\r\n}\r\n\r\nfunction setAuthCookies(\r\n res: any,\r\n tokens: { access_token: string; refresh_token: string },\r\n cookie: AuthCookieConfig,\r\n) {\r\n const base: CookieOptions = {\r\n httpOnly: true,\r\n secure: cookie.secure ?? false,\r\n sameSite: cookie.sameSite ?? 'lax',\r\n path: cookie.path ?? '/',\r\n maxAge: cookie.maxAgeMs,\r\n };\r\n\r\n if (cookie.domain) {\r\n base.domain = cookie.domain;\r\n }\r\n\r\n if (tokens?.access_token) {\r\n res.cookie('access_token', tokens.access_token, base);\r\n }\r\n if (tokens?.refresh_token) {\r\n res.cookie('refresh_token', tokens.refresh_token, base);\r\n }\r\n}\r\n\r\nfunction toUserResponse(user: any) {\r\n if (!user) return null;\r\n return {\r\n sub: user.id || user.keycloakId,\r\n email: user.email,\r\n firstName: user.firstName,\r\n lastName: user.lastName,\r\n projectId: user.projectId,\r\n metadata: user.metadata,\r\n roles: user.roles,\r\n };\r\n}\r\n\r\nfunction respondWithKeycloakError(\r\n res: any,\r\n err: any,\r\n fallback: string,\r\n status = 400,\r\n) {\r\n const description =\r\n err?.response?.data?.error_description ||\r\n err?.response?.data?.errorMessage ||\r\n err?.message ||\r\n fallback;\r\n return res.status(status).json({ ok: false, error: description });\r\n}\r\n\r\n// function buildVerificationTemplate(token: string, options: AuthRouterOptions) {\r\n// return `<a href=\"${getFrontendBaseUrl(options)}/verify-email?token=${token}\">Verify</a>`;\r\n// }\r\n\r\n// function buildResetTemplate(token: string, options: AuthRouterOptions) {\r\n// return `<a href=\"${getFrontendBaseUrl(options)}/reset-password?token=${token}\">Reset</a>`;\r\n// }\r\n\r\nfunction getFrontendBaseUrl(options: AuthRouterOptions) {\r\n if (options.frontendBaseUrl)\r\n return options.frontendBaseUrl.replace(/\\/$/, '');\r\n const domain = process.env.ORG_DOMAIN!?.replace(/\\/$/, '');\r\n if (!domain) return '';\r\n return domain.startsWith('http') ? domain : `https://${domain}`;\r\n}\r\n\r\nasync function sendRateLimitedEmail({\r\n emailService,\r\n user,\r\n subject,\r\n html,\r\n from,\r\n}: {\r\n emailService: EmailService;\r\n user: any;\r\n subject: string;\r\n html: string;\r\n from?: string;\r\n}): Promise<{ rateLimited: boolean; waitMs?: number }> {\r\n const can = emailService.canSend(user?.lastEmailSent || []);\r\n if (!can.ok) {\r\n return { rateLimited: true, waitMs: (can as any).waitMs };\r\n }\r\n\r\n await emailService.send(user.email, subject, html, from);\r\n user.lastEmailSent = [...(user.lastEmailSent || []), new Date()];\r\n await user.save();\r\n return { rateLimited: false };\r\n}\r\n\r\nfunction generateTokens(user: any) {\r\n const accessPayload = {\r\n sub: user.id.toString(),\r\n email: user.email,\r\n roles: user.roles || [],\r\n orgId: user.orgId || null,\r\n org_id: user.orgId || null,\r\n projectId: user.projectId || null,\r\n firstName: user.firstName,\r\n lastName: user.lastName,\r\n emailVerified: user.emailVerified,\r\n createdAt: user.createdAt,\r\n metadata: user.metadata,\r\n type: 'user',\r\n };\r\n\r\n const accessToken = jwt.sign(accessPayload, process.env.JWT_SECRET!, {\r\n expiresIn: '1d',\r\n });\r\n\r\n const refreshToken = jwt.sign(\r\n { sub: user._id.toString() },\r\n process.env.JWT_SECRET!,\r\n { expiresIn: '30d' },\r\n );\r\n\r\n return { access_token: accessToken, refresh_token: refreshToken };\r\n}\r\n","import type {\n AuthXConfig,\n AwsConfig,\n CookieConfig,\n EmailConfig,\n OidcConfig,\n} from 'aaspai-types';\n\nexport function loadConfig(): AuthXConfig {\n return {\n orgDomain: process.env.ORG_DOMAIN!,\n orgId: process.env.ORG_ID!,\n email: {\n host: process.env.EMAIL_HOST || 'smtp.postmarkapp.com',\n port: process.env.EMAIL_PORT ? Number(process.env.EMAIL_PORT) : 587,\n secure: (process.env.EMAIL_SECURE || 'false') === 'true',\n user: process.env.EMAIL_USER!,\n pass: process.env.EMAIL_PASSWORD!,\n from: process.env.EMAIL_FROM!,\n jwtSecret: process.env.EMAIL_JWT_SECRET!,\n } as EmailConfig,\n cookies: {\n domain: process.env.COOKIE_DOMAIN!,\n secure: (process.env.COOKIE_SECURE || 'true') === 'true',\n accessTtlMs: 7 * 24 * 60 * 60 * 1000,\n refreshTtlMs: 30 * 24 * 60 * 60 * 1000,\n } as CookieConfig,\n oidc: {\n jwtSecret: process.env.JWT_SECRET!,\n } as OidcConfig,\n aws: {\n bucket: process.env.AWS_S3_BUCKET!,\n region: process.env.AWS_REGION!,\n accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n } as AwsConfig,\n };\n}\n","import type {\n AuthXConfig,\n AwsConfig,\n CookieConfig,\n DeepPartial,\n EmailConfig,\n OidcConfig,\n} from 'aaspai-types';\nimport { loadConfig } from './loadConfig.js';\n\nexport type {\n AuthXConfig,\n AwsConfig,\n CookieConfig,\n DeepPartial,\n EmailConfig,\n OidcConfig,\n};\n\nexport const config: AuthXConfig = loadConfig();\n\nexport function configureAuthX(\n overrides: DeepPartial<AuthXConfig> = {},\n): AuthXConfig {\n return deepMerge(config, overrides);\n}\n\nexport function getConfig(): AuthXConfig {\n return config;\n}\n\nfunction deepMerge<T extends Record<string, any>>(\n target: T,\n source: DeepPartial<T>,\n): T {\n if (!source) {\n return target;\n }\n\n for (const key of Object.keys(source) as (keyof T)[]) {\n const value = source[key];\n if (value === undefined) continue;\n\n if (Array.isArray(value)) {\n (target as any)[key] = [...value];\n continue;\n }\n\n if (isPlainObject(value)) {\n if (!isPlainObject(target[key])) {\n (target as any)[key] = {};\n }\n deepMerge(target[key] as Record<string, any>, value as any);\n continue;\n }\n\n (target as any)[key] = value;\n }\n\n return target;\n}\n\nfunction isPlainObject(value: any): value is Record<string, any> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import type { AuthCookieConfig, AuthXSession } from 'aaspai-types';\r\nimport { CookieOptions } from 'express';\r\n\r\n/**\r\n * Check if a session has a specific role\r\n */\r\nexport function hasRole(\r\n session: AuthXSession | null | undefined,\r\n role: string,\r\n): boolean {\r\n if (!session || !session.roles) return false;\r\n return session.roles.includes(role);\r\n}\r\n\r\nexport function baseProjectCookieOptionsFrom(\r\n cookie: AuthCookieConfig,\r\n): CookieOptions {\r\n const base: CookieOptions = {\r\n secure: cookie.secure ?? false,\r\n sameSite: cookie.sameSite ?? 'lax',\r\n path: cookie.path ?? '/',\r\n maxAge: cookie.maxAgeMs,\r\n };\r\n if (cookie.domain) base.domain = cookie.domain;\r\n return base;\r\n}\r\n\r\nexport function buildClearCookieOptions(cookie: AuthCookieConfig): CookieOptions {\r\n const opts: CookieOptions = {\r\n httpOnly: true, // not strictly required but fine\r\n secure: cookie.secure ?? false,\r\n sameSite: cookie.sameSite ?? 'lax',\r\n path: cookie.path ?? '/',\r\n };\r\n\r\n if (cookie.domain) {\r\n opts.domain = cookie.domain;\r\n }\r\n\r\n return opts;\r\n}\r\n\r\n\r\n/**\r\n * Check if a session has any of the specified roles\r\n */\r\nexport function hasAnyRole(\r\n session: AuthXSession | null | undefined,\r\n roles: string[],\r\n): boolean {\r\n if (\r\n !session ||\r\n !session.roles ||\r\n !Array.isArray(roles) ||\r\n roles.length === 0\r\n ) {\r\n return false;\r\n }\r\n return roles.some((role) => session.roles.includes(role));\r\n}\r\n\r\n/**\r\n * Check if a session has all of the specified roles\r\n */\r\nexport function hasAllRoles(\r\n session: AuthXSession | null | undefined,\r\n roles: string[],\r\n): boolean {\r\n if (\r\n !session ||\r\n !session.roles ||\r\n !Array.isArray(roles) ||\r\n roles.length === 0\r\n ) {\r\n return false;\r\n }\r\n return roles.every((role) => session.roles.includes(role));\r\n}\r\n\r\n/**\r\n * Check if a session has a specific permission\r\n */\r\nexport function hasPermission(\r\n session: AuthXSession | null | undefined,\r\n permission: string,\r\n): boolean {\r\n if (!session || !session.permissions) return false;\r\n return session.permissions.includes(permission);\r\n}\r\n\r\n/**\r\n * Check if a session has any of the specified permissions\r\n */\r\nexport function hasAnyPermission(\r\n session: AuthXSession | null | undefined,\r\n permissions: string[],\r\n): boolean {\r\n if (\r\n !session ||\r\n !session.permissions ||\r\n !Array.isArray(permissions) ||\r\n permissions.length === 0\r\n ) {\r\n return false;\r\n }\r\n return permissions.some((perm) => session.permissions.includes(perm));\r\n}\r\n\r\n/**\r\n * Check if a session has all of the specified permissions\r\n */\r\nexport function hasAllPermissions(\r\n session: AuthXSession | null | undefined,\r\n permissions: string[],\r\n): boolean {\r\n if (\r\n !session ||\r\n !session.permissions ||\r\n !Array.isArray(permissions) ||\r\n permissions.length === 0\r\n ) {\r\n return false;\r\n }\r\n return permissions.every((perm) => session.permissions.includes(perm));\r\n}\r\n","export const PLATFORM_ROLES = [\r\n {\r\n role: 'platform_admin',\r\n permissions: [],\r\n },\r\n {\r\n role: 'platform_manager',\r\n permissions: [],\r\n },\r\n {\r\n role: 'platform_user',\r\n permissions: [],\r\n },\r\n];\r\n\r\n/**\r\n * Get all permissions for a given set of roles\r\n * @param roles - Array of role names\r\n * @returns Array of unique permission strings\r\n */\r\nexport function getPermissionsForRoles(roles: string[]): string[] {\r\n if (!Array.isArray(roles) || roles.length === 0) {\r\n return [];\r\n }\r\n\r\n const permissionSet = new Set<string>();\r\n\r\n for (const roleName of roles) {\r\n const roleConfig = PLATFORM_ROLES.find((r) => r.role === roleName);\r\n if (roleConfig && Array.isArray(roleConfig.permissions)) {\r\n for (const perm of roleConfig.permissions) {\r\n permissionSet.add(perm);\r\n }\r\n }\r\n }\r\n\r\n return Array.from(permissionSet);\r\n}\r\n","import type { AuthXSession } from 'aaspai-types';\r\nimport { getPermissionsForRoles } from './roles.config.js';\r\n\r\n/**\r\n * Build a canonical AuthXSession from a JWT payload or user object\r\n * @param payload - JWT payload or user object from existing auth middleware\r\n * @returns AuthXSession with standardized shape\r\n */\r\nexport function buildSession(payload: any): AuthXSession {\r\n // Extract user ID (sub is standard JWT claim)\r\n const userId = payload?.sub || payload?.userId || payload?.id || '';\r\n\r\n // Extract email\r\n const email = payload?.email || payload?.email_address || '';\r\n\r\n // Extract roles from various possible locations\r\n const roles =\r\n payload?.realm_access?.roles ||\r\n payload?.roles ||\r\n payload?.['cognito:groups'] ||\r\n (Array.isArray(payload?.role) ? payload.role : []) ||\r\n [];\r\n\r\n // Ensure roles is an array of strings\r\n const normalizedRoles = Array.isArray(roles)\r\n ? roles.map(String).filter(Boolean)\r\n : [];\r\n\r\n // Derive permissions from roles using PLATFORM_ROLES config\r\n const permissions = getPermissionsForRoles(normalizedRoles);\r\n\r\n // Build base session\r\n const session: AuthXSession = {\r\n userId,\r\n email,\r\n roles: normalizedRoles,\r\n permissions,\r\n };\r\n\r\n // Preserve optional fields if present\r\n if (payload?.firstName) session.firstName = payload.firstName;\r\n if (payload?.lastName) session.lastName = payload.lastName;\r\n if (payload?.projectId) session.projectId = payload.projectId;\r\n if (payload?.orgId) session.orgId = payload.orgId;\r\n if (payload?.org_id) session.org_id = payload.org_id;\r\n if (payload?.authType) session.authType = payload.authType;\r\n if (payload?.createdAt) session.createdAt = payload.createdAt;\r\n if (payload?.metadata) session.metadata = payload.metadata;\r\n\r\n // Preserve any other custom fields\r\n Object.keys(payload || {}).forEach((key) => {\r\n if (\r\n ![\r\n 'sub',\r\n 'userId',\r\n 'id',\r\n 'email',\r\n 'email_address',\r\n 'realm_access',\r\n 'roles',\r\n 'cognito:groups',\r\n 'role',\r\n 'projectId',\r\n 'orgId',\r\n 'org_id',\r\n 'authType',\r\n ].includes(key)\r\n ) {\r\n session[key] = payload[key];\r\n }\r\n });\r\n\r\n return session;\r\n}\r\n","// src/models/rolePermission.model.ts\r\nimport mongoose, { Document, Schema } from 'mongoose';\r\n\r\nexport interface RolePermissionDocument extends Document {\r\n orgId?: string | null; // null => platform-wide\r\n role: string; // \"platform_admin\", \"platform_manager\", \"custom_role\"\r\n permissions: string[]; // list of permission keys\r\n createdAt: Date;\r\n updatedAt: Date;\r\n}\r\n\r\nconst RolePermissionSchema = new Schema<RolePermissionDocument>(\r\n {\r\n orgId: { type: String, default: null, index: true },\r\n role: { type: String, required: true },\r\n permissions: { type: [String], default: [] },\r\n },\r\n {\r\n timestamps: true,\r\n },\r\n);\r\n\r\nRolePermissionSchema.index({ orgId: 1, role: 1 }, { unique: true });\r\n\r\nexport const RolePermissionModel = mongoose.model<RolePermissionDocument>(\r\n 'RolePermission',\r\n RolePermissionSchema,\r\n 'role_permissions',\r\n);\r\n","import mongoose from 'mongoose';\nimport { v4 as uuid } from 'uuid';\n\nconst MetadataSchema = new mongoose.Schema(\n {\n key: { type: String, required: true },\n value: { type: mongoose.Schema.Types.Mixed, required: true },\n },\n { _id: false },\n);\n\nconst OrgUserSchema = new mongoose.Schema(\n {\n id: { type: String, default: uuid(), index: true, unique: true },\n email: { type: String, required: true, unique: true },\n firstName: { type: String, required: true },\n lastName: { type: String, required: true },\n orgId: { type: String },\n projectId: { type: String, required: true },\n roles: { type: [String], default: [] },\n emailVerified: { type: Boolean, default: false },\n lastEmailSent: { type: [Date], default: [] },\n lastPasswordReset: { type: Date },\n metadata: { type: [MetadataSchema], default: [] },\n passwordHash: { type: String },\n },\n { timestamps: true, collection: 'users' },\n);\n\nexport const OrgUser = mongoose.model('OrgUser', OrgUserSchema);\n","import { parse as parseCookie } from 'cookie';\nimport type { Request } from 'express';\n\nexport function extractToken(\n req: Request,\n opts?: {\n headerNames?: string[];\n cookieNames?: string[];\n queryNames?: string[];\n },\n) {\n const headerNames = opts?.headerNames ?? ['authorization', 'token'];\n const cookieNames = opts?.cookieNames ?? ['access_token', 'authorization'];\n const queryNames = opts?.queryNames ?? ['access_token', 'token'];\n\n for (const h of headerNames) {\n const raw = (req.headers as any)[h] as string | undefined;\n if (raw) {\n const lower = raw.toLowerCase();\n if (lower.startsWith('bearer ')) return raw.slice(7).trim();\n if (!raw.includes(' ')) return raw.trim();\n }\n }\n\n const ch = req.headers['cookie'];\n\n if (typeof ch === 'string') {\n const parsed = parseCookie(ch);\n for (const c of cookieNames) if (parsed[c]) return parsed[c];\n }\n\n for (const q of queryNames) {\n const v = (req.query as any)?.[q];\n if (typeof v === 'string' && v) return v;\n }\n\n return null;\n}\n\nexport function readProjectId(req: Request) {\n const ch = req.headers['cookie'];\n\n if (typeof ch === 'string') {\n try {\n const parsed = parseCookie(ch);\n return parsed['projectId'] || null;\n } catch {}\n }\n\n return null;\n}\n","import jwt from 'jsonwebtoken';\r\n\r\nexport function verifyJwt(token: string): Promise<any> {\r\n return new Promise((resolve, reject) => {\r\n jwt.verify(\r\n token,\r\n process.env.JWT_SECRET!, // This is your shared secret (string)\r\n {\r\n algorithms: ['HS256'], // Only allow HS256\r\n complete: false, // We only want payload\r\n },\r\n (err, decoded) => {\r\n if (err) {\r\n reject(err);\r\n } else {\r\n resolve(decoded);\r\n }\r\n },\r\n );\r\n });\r\n}\r\n\r\nexport function claimsToUser(payload: any) {\r\n return {\r\n sub: payload.sub,\r\n email: payload.email || payload.preferred_username,\r\n roles: payload.roles || [],\r\n name:\r\n payload.firstName ||\r\n payload.lastName ||\r\n `${payload.given_name || ''} ${payload.family_name || ''}`.trim(),\r\n emailVerified: payload.email_verified || false,\r\n };\r\n}\r\n","import type { AuthXSession } from 'aaspai-types';\r\nimport type { NextFunction, Request, Response } from 'express';\r\nimport { buildSession } from '../core/session.js';\r\nimport { RolePermissionModel } from '../models/rolePermission.model.js';\r\nimport { OrgUser } from '../models/user.model.js';\r\nimport { extractToken, readProjectId } from '../utils/extract.js';\r\nimport { verifyJwt } from '../utils/jwt.js';\r\n\r\nasync function mergeRolePermissions(session: AuthXSession): Promise<void> {\r\n const roles = Array.isArray(session.roles) ? session.roles : [];\r\n if (!roles.length) return;\r\n\r\n const orgContexts = new Set<string | null>();\r\n if (session.orgId) orgContexts.add(session.orgId);\r\n if (session.org_id) orgContexts.add(session.org_id);\r\n if (session.projectId) orgContexts.add(session.projectId);\r\n orgContexts.add(null);\r\n\r\n const docs = await RolePermissionModel.find({\r\n orgId: { $in: Array.from(orgContexts) },\r\n role: { $in: roles },\r\n })\r\n .lean()\r\n .exec();\r\n\r\n const dynamic = new Set<string>();\r\n for (const doc of docs) {\r\n for (const perm of doc.permissions || []) {\r\n if (perm) dynamic.add(perm);\r\n }\r\n }\r\n\r\n const existing = Array.isArray(session.permissions)\r\n ? session.permissions\r\n : [];\r\n session.permissions = Array.from(new Set([...existing, ...dynamic]));\r\n}\r\n\r\n/**\r\n * Express middleware to require authentication\r\n * Extracts and verifies JWT token, builds AuthXSession, and attaches to req.user\r\n */\r\nexport function requireAuth() {\r\n return async (req: Request, res: Response, next: NextFunction) => {\r\n try {\r\n // Check for API key first (for backward compatibility)\r\n const apiKey = (req.headers['x-api-key'] || req.headers['x-apikey']) as\r\n | string\r\n | undefined;\r\n\r\n const userId = (req.headers['x-user-id'] || req.headers['x-userId']) as\r\n | string\r\n | undefined;\r\n\r\n if (apiKey) {\r\n if (apiKey !== process.env.SERVER_API_KEY) {\r\n return res.status(401).json({ error: 'Invalid API key' });\r\n }\r\n\r\n if (!userId) {\r\n return res.status(401).json({ error: 'User Id is Required' });\r\n }\r\n\r\n // Fetch real user from DB\r\n const user = await OrgUser.findOne({ id: userId }).lean();\r\n\r\n if (!user) {\r\n return res.status(401).json({ error: 'User not found' });\r\n }\r\n\r\n const session = buildSession({\r\n sub: user.id.toString(),\r\n email: user.email,\r\n firstName: user.firstName,\r\n lastName: user.lastName,\r\n metadata: user.metadata || [],\r\n roles: user.roles || [],\r\n orgId: user.orgId,\r\n org_id: user.orgId,\r\n projectId: user.projectId,\r\n createdAt: user.createdAt,\r\n });\r\n\r\n session.authType = 'api-key';\r\n session.projectId = readProjectId(req) || user.projectId || undefined;\r\n await mergeRolePermissions(session);\r\n (req as any).user = session;\r\n return next();\r\n } else {\r\n // Extract and verify JWT token\r\n const token = extractToken(req);\r\n if (!token) {\r\n return res.status(401).json({ error: 'Missing token' });\r\n }\r\n const claims = await verifyJwt(token);\r\n const session = buildSession(claims);\r\n // Preserve projectId if present in cookie\r\n const pid = readProjectId(req);\r\n if (pid) session.projectId = pid;\r\n await mergeRolePermissions(session);\r\n\r\n // Attach session to request\r\n (req as any).user = session;\r\n next();\r\n }\r\n } catch (e: any) {\r\n res.status(401).json({ error: e?.message || 'Unauthorized' });\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Express middleware to require one or more roles\r\n * @param roles - Array of role names (user must have at least one)\r\n * @deprecated Use requireRole from middlewares instead. This is kept for backward compatibility.\r\n */\r\nexport function authorize(roles: string[] = []) {\r\n return (req: Request, res: Response, next: NextFunction) => {\r\n if (!roles || roles.length === 0) return next();\r\n const user = (req as any).user as AuthXSession | undefined;\r\n if (!user) {\r\n return res.status(401).json({ error: 'Unauthorized' });\r\n }\r\n const have = new Set<string>((user.roles || []).map(String));\r\n const ok = roles.some((r) => have.has(r));\r\n if (!ok) {\r\n return res\r\n .status(403)\r\n .json({ error: `Requires one of roles: ${roles.join(', ')}` });\r\n }\r\n next();\r\n };\r\n}\r\n","export function isEmail(v: string) {\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v);\n}\n\nexport function isPasswordStrong(v: string) {\n return (\n typeof v === 'string' &&\n v.length >= 6 &&\n /[A-Z]/.test(v) &&\n /[^a-zA-Z0-9]/.test(v)\n );\n}\n\nexport function validateSignup(req: any, res: any, next: any) {\n const { firstName, lastName, email, password, projectId, metadata } =\n req.body || {};\n if (!firstName || !lastName)\n return res.status(400).json({ error: 'firstName,lastName required' });\n if (!isEmail(email)) return res.status(400).json({ error: 'invalid email' });\n if (!isPasswordStrong(password))\n return res.status(400).json({ error: 'weak password' });\n if (!projectId) return res.status(400).json({ error: 'projectId required' });\n if (!Array.isArray(metadata))\n return res.status(400).json({ error: 'metadata must be array' });\n next();\n}\n\nexport function validateLogin(req: any, res: any, next: any) {\n const { email, password } = req.body || {};\n if (!isEmail(email)) return res.status(400).json({ error: 'invalid email' });\n if (typeof password !== 'string')\n return res.status(400).json({ error: 'password required' });\n next();\n}\n\nexport function validateResetPassword(req: any, res: any, next: any) {\n const { token, newPassword } = req.body || {};\n if (!token) return res.status(400).json({ error: 'token required' });\n if (!isPasswordStrong(newPassword))\n return res.status(400).json({ error: 'weak password' });\n next();\n}\n\nexport function validateResendEmail(req: any, res: any, next: any) {\n const { email } = req.body || {};\n if (!isEmail(email)) return res.status(400).json({ error: 'invalid email' });\n next();\n}\n\nexport function validateSendInvite(req: any, res: any, next: any) {\n const { email, role } = req.body || {};\n if (!isEmail(email)) return res.status(400).json({ error: 'invalid email' });\n if (!['platform_user', 'org_admin'].includes(role))\n return res.status(400).json({ error: 'invalid role' });\n next();\n}\n","import mongoose from 'mongoose';\n\nconst InviteSchema = new mongoose.Schema(\n {\n id: { type: String, required: true, index: true },\n email: { type: String, required: true },\n role: {\n type: String,\n enum: ['platform_user', 'org_admin'],\n required: true,\n },\n invitedBy: { type: String },\n usedBy: { type: String },\n isUsed: { type: Boolean, default: false },\n usedAt: { type: Date },\n expiresAt: { type: Date },\n isExpired: { type: Boolean, default: false },\n },\n { timestamps: true, collection: 'invites' },\n);\n\nexport const Invite = mongoose.model('Invite', InviteSchema);\n","import bcrypt from 'bcrypt';\r\nimport jwt from 'jsonwebtoken';\r\nimport { v4 as uuid } from 'uuid';\r\nimport { ClientModel } from '../models/client.model.js';\r\nimport { RolePermissionModel } from '../models/rolePermission.model.js';\r\nimport { OrgUser } from '../models/user.model.js';\r\n\r\nexport class AuthAdminService {\r\n private token?: { accessToken: string; exp: number };\r\n\r\n async getAdminToken() {\r\n return this.ensureAdminToken();\r\n }\r\n\r\n // -------------------------------------------------------------------\r\n // CLIENTS\r\n // -------------------------------------------------------------------\r\n async createClient(\r\n clientId: string,\r\n redirectUris: string[] = [],\r\n publicClient = false,\r\n ) {\r\n const client = await ClientModel.create({\r\n clientId,\r\n redirectUris,\r\n publicClient,\r\n });\r\n return client;\r\n }\r\n\r\n async updateClient(id: string, patch: any) {\r\n await ClientModel.findByIdAndUpdate(id, patch);\r\n }\r\n\r\n // -------------------------------------------------------------------\r\n // USERS\r\n // -------------------------------------------------------------------\r\n async listUsersInRealm(\r\n _realm: string,\r\n filter?: { email?: string; username?: string },\r\n ) {\r\n return OrgUser.find(filter || {});\r\n }\r\n\r\n async getUserById(userId: string) {\r\n return OrgUser.findOne({ id: userId });\r\n }\r\n\r\n async isUserEmailVerified(userId: string) {\r\n const user: any = await OrgUser.findOne({ id: userId });\r\n return user?.emailVerified;\r\n }\r\n\r\n async createUserInRealm(payload: {\r\n username: string;\r\n email: string;\r\n firstName: string;\r\n projectId: string;\r\n lastName?: string;\r\n credentials?: any[];\r\n emailVerified?: boolean;\r\n metadata?: any[];\r\n }) {\r\n const hashedPassword = payload.credentials?.[0]?.value\r\n ? await bcrypt.hash(payload.credentials[0].value, 10)\r\n : undefined;\r\n\r\n const user = await OrgUser.create({\r\n id: crypto.randomUUID(),\r\n email: payload.email,\r\n firstName: payload.firstName,\r\n lastName: payload.lastName,\r\n projectId: payload.projectId,\r\n emailVerified: payload.emailVerified || false,\r\n passwordHash: hashedPassword,\r\n metadata: payload.metadata || [],\r\n });\r\n\r\n return user;\r\n }\r\n\r\n async assignRealmRole(userId: string, roleName: string) {\r\n const role = await RolePermissionModel.findOne({ role: roleName });\r\n if (!role) throw new Error(`Role not found: ${roleName}`);\r\n\r\n await OrgUser.findOneAndUpdate(\r\n { id: userId },\r\n {\r\n $addToSet: { roles: role._id },\r\n },\r\n );\r\n }\r\n\r\n async updateUserEmailVerified(userId: string, emailVerified: boolean) {\r\n await OrgUser.findOneAndUpdate({ id: userId }, { emailVerified });\r\n }\r\n\r\n async updateUserPassword(userId: string, newPassword: string) {\r\n const hashed = await bcrypt.hash(newPassword, 10);\r\n\r\n await OrgUser.findOneAndUpdate({ id: userId }, { passwordHash: hashed });\r\n }\r\n\r\n // -------------------------------------------------------------------\r\n // ADMIN TOKEN (self-issued JWT)\r\n // -------------------------------------------------------------------\r\n private async ensureAdminToken(): Promise<string> {\r\n const now = Math.floor(Date.now() / 1000);\r\n\r\n if (this.token && this.token.exp - 30 > now) {\r\n return this.token.accessToken;\r\n }\r\n\r\n const payload = {\r\n type: 'admin',\r\n system: true,\r\n };\r\n\r\n const accessToken = jwt.sign(payload, process.env.JWT_SECRET!, {\r\n expiresIn: '1d',\r\n });\r\n\r\n this.token = {\r\n accessToken,\r\n exp: now + 84800,\r\n };\r\n\r\n return this.token.accessToken;\r\n }\r\n}\r\n","import mongoose, { Document, Model, Schema } from 'mongoose';\r\n\r\nexport interface IClient extends Document {\r\n clientId: string;\r\n redirectUris: string[];\r\n publicClient: boolean;\r\n secret?: string; // optional — used only for confidential clients\r\n createdAt: Date;\r\n updatedAt: Date;\r\n}\r\n\r\nconst ClientSchema = new Schema<IClient>(\r\n {\r\n clientId: {\r\n type: String,\r\n required: true,\r\n unique: true,\r\n index: true,\r\n },\r\n\r\n redirectUris: {\r\n type: [String],\r\n default: [],\r\n },\r\n\r\n publicClient: {\r\n type: Boolean,\r\n default: false,\r\n },\r\n\r\n // Optional: if you want confidential clients\r\n secret: {\r\n type: String,\r\n required: function (this: IClient) {\r\n return !this.publicClient;\r\n },\r\n },\r\n },\r\n {\r\n timestamps: true,\r\n },\r\n);\r\n\r\nexport const ClientModel: Model<IClient> =\r\n mongoose.models.Client || mongoose.model<IClient>('Client', ClientSchema);\r\n","import jwt from 'jsonwebtoken';\nimport nodemailer from 'nodemailer';\n\nexport class EmailService {\n private transporter;\n private MAX_EMAILS = 5;\n private WINDOW_MINUTES = 15;\n private BLOCK_HOURS = 1;\n constructor() {\n this.transporter = nodemailer.createTransport({\n host: process.env.EMAIL_HOST || 'smtp.postmarkapp.com',\n port: process.env.EMAIL_PORT ? Number(process.env.EMAIL_PORT) : 587,\n secure: (process.env.EMAIL_SECURE || 'false') === 'true',\n auth: {\n user: process.env.EMAIL_USER!,\n pass: process.env.EMAIL_PASSWORD!,\n },\n });\n }\n\n sign(payload: any, ttlSec = 60 * 60 * 24 * 30) {\n return jwt.sign(payload, process.env.EMAIL_JWT_SECRET!, {\n expiresIn: ttlSec,\n });\n }\n\n verify<T = any>(token: string): T {\n return jwt.verify(token, process.env.EMAIL_JWT_SECRET!) as T;\n }\n\n async send(to: string, subject: string, html: string, from?: string) {\n try {\n const info = await this.transporter.sendMail({\n from: from\n ? `${from} ` + process.env.EMAIL_FROM!\n : process.env.EMAIL_FROM!,\n to,\n subject,\n html,\n });\n console.log('[EmailService] ✅ Email sent successfully:', {\n messageId: info.messageId,\n response: info.response,\n accepted: info.accepted,\n rejected: info.rejected,\n });\n\n return info;\n } catch (error: any) {\n console.error('[EmailService] ❌ Failed to send email:', {\n message: error.message,\n code: error.code,\n command: error.command,\n responseCode: error.responseCode,\n response: error.response,\n stack: error.stack,\n });\n\n // Re-throw so caller knows it failed\n throw error;\n }\n }\n\n canSend(lastEmailSent: Date[]) {\n const now = Date.now();\n const windowStart = now - this.WINDOW_MINUTES * 60 * 1000;\n const emailsInWindow = (lastEmailSent || [])\n .map((d) => new Date(d))\n .filter((d) => d.getTime() >= windowStart);\n if (emailsInWindow.length >= this.MAX_EMAILS)\n return {\n ok: false,\n reason: 'RATE_LIMIT',\n waitMs: this.BLOCK_HOURS * 60 * 60 * 1000,\n };\n return { ok: true };\n }\n}\n","// templates/email.templates.ts\r\n\r\ninterface VerificationEmailData {\r\n firstName: string;\r\n verificationUrl: string;\r\n expiresIn: string;\r\n}\r\n\r\ninterface ResetPasswordEmailData {\r\n firstName: string;\r\n resetUrl: string;\r\n expiresIn: string;\r\n}\r\n\r\ninterface WelcomeEmailData {\r\n firstName: string;\r\n loginUrl: string;\r\n}\r\n\r\ninterface InviteEmailData {\r\n inviterName?: string;\r\n organizationName?: string;\r\n role: string;\r\n inviteUrl: string;\r\n expiresIn: string;\r\n}\r\n\r\n// Premium dark theme styles\r\nconst colors = {\r\n background: '#0a0a0a',\r\n cardBackground: '#111111',\r\n cardBorder: '#1a1a1a',\r\n accent: '#ffffff',\r\n accentMuted: 'rgba(255, 255, 255, 0.9)',\r\n textPrimary: '#ffffff',\r\n textSecondary: 'rgba(255, 255, 255, 0.7)',\r\n textMuted: 'rgba(255, 255, 255, 0.5)',\r\n divider: 'rgba(255, 255, 255, 0.1)',\r\n subtle: '#161616',\r\n highlight: 'rgba(255, 255, 255, 0.05)',\r\n};\r\n\r\nconst styles = {\r\n wrapper: `\r\n margin: 0;\r\n padding: 40px 20px;\r\n background-color: ${colors.background};\r\n background-image: \r\n radial-gradient(ellipse at top, rgba(255,255,255,0.03) 0%, transparent 50%),\r\n radial-gradient(ellipse at bottom, rgba(255,255,255,0.02) 0%, transparent 50%);\r\n min-height: 100vh;\r\n `,\r\n container: `\r\n max-width: 520px;\r\n margin: 0 auto;\r\n font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\r\n color: ${colors.textPrimary};\r\n line-height: 1.7;\r\n `,\r\n card: `\r\n background-color: ${colors.cardBackground};\r\n border: 1px solid ${colors.cardBorder};\r\n border-radius: 16px;\r\n overflow: hidden;\r\n box-shadow: \r\n 0 0 0 1px rgba(255,255,255,0.05),\r\n 0 20px 50px -20px rgba(0,0,0,0.5),\r\n 0 30px 60px -30px rgba(0,0,0,0.3);\r\n `,\r\n header: `\r\n padding: 48px 40px 32px;\r\n text-align: center;\r\n border-bottom: 1px solid ${colors.divider};\r\n `,\r\n iconWrapper: `\r\n width: 64px;\r\n height: 64px;\r\n margin: 0 auto 24px;\r\n background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);\r\n border: 1px solid rgba(255,255,255,0.1);\r\n border-radius: 16px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 28px;\r\n `,\r\n headerTitle: `\r\n color: ${colors.textPrimary};\r\n margin: 0;\r\n font-size: 24px;\r\n font-weight: 600;\r\n letter-spacing: -0.5px;\r\n `,\r\n headerSubtitle: `\r\n color: ${colors.textMuted};\r\n margin: 8px 0 0;\r\n font-size: 14px;\r\n font-weight: 400;\r\n `,\r\n body: `\r\n padding: 40px;\r\n `,\r\n greeting: `\r\n margin: 0 0 24px;\r\n color: ${colors.textPrimary};\r\n font-size: 18px;\r\n font-weight: 500;\r\n `,\r\n paragraph: `\r\n margin: 0 0 20px;\r\n color: ${colors.textSecondary};\r\n font-size: 15px;\r\n line-height: 1.7;\r\n `,\r\n buttonWrapper: `\r\n text-align: center;\r\n margin: 32px 0;\r\n `,\r\n button: `\r\n display: inline-block;\r\n background-color: ${colors.accent};\r\n color: #000000 !important;\r\n text-decoration: none;\r\n padding: 14px 36px;\r\n border-radius: 8px;\r\n font-weight: 600;\r\n font-size: 14px;\r\n letter-spacing: 0.3px;\r\n transition: all 0.2s ease;\r\n `,\r\n secondaryButton: `\r\n display: inline-block;\r\n background-color: transparent;\r\n color: ${colors.textPrimary} !important;\r\n text-decoration: none;\r\n padding: 12px 28px;\r\n border-radius: 8px;\r\n font-weight: 500;\r\n font-size: 14px;\r\n border: 1px solid ${colors.divider};\r\n `,\r\n infoCard: `\r\n background-color: ${colors.subtle};\r\n border: 1px solid ${colors.divider};\r\n border-radius: 12px;\r\n padding: 20px 24px;\r\n margin: 28px 0;\r\n `,\r\n infoCardTitle: `\r\n margin: 0 0 12px;\r\n color: ${colors.textPrimary};\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n `,\r\n infoCardText: `\r\n margin: 0;\r\n color: ${colors.textSecondary};\r\n font-size: 14px;\r\n line-height: 1.6;\r\n `,\r\n warningCard: `\r\n background: linear-gradient(135deg, rgba(255,180,0,0.1) 0%, rgba(255,140,0,0.05) 100%);\r\n border: 1px solid rgba(255,180,0,0.2);\r\n border-radius: 12px;\r\n padding: 20px 24px;\r\n margin: 28px 0;\r\n `,\r\n warningCardTitle: `\r\n margin: 0 0 12px;\r\n color: #ffc107;\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n `,\r\n warningCardText: `\r\n margin: 0;\r\n color: rgba(255,255,255,0.7);\r\n font-size: 14px;\r\n line-height: 1.6;\r\n `,\r\n successCard: `\r\n background: linear-gradient(135deg, rgba(0,255,150,0.1) 0%, rgba(0,200,100,0.05) 100%);\r\n border: 1px solid rgba(0,255,150,0.2);\r\n border-radius: 12px;\r\n padding: 20px 24px;\r\n margin: 28px 0;\r\n `,\r\n successCardTitle: `\r\n margin: 0 0 12px;\r\n color: #00ff96;\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n `,\r\n linkSection: `\r\n margin: 32px 0;\r\n padding: 20px;\r\n background-color: ${colors.subtle};\r\n border-radius: 8px;\r\n border: 1px solid ${colors.divider};\r\n `,\r\n linkLabel: `\r\n margin: 0 0 8px;\r\n color: ${colors.textMuted};\r\n font-size: 12px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n `,\r\n linkText: `\r\n word-break: break-all;\r\n color: ${colors.textSecondary};\r\n font-size: 13px;\r\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;\r\n margin: 0;\r\n `,\r\n divider: `\r\n border: none;\r\n border-top: 1px solid ${colors.divider};\r\n margin: 32px 0;\r\n `,\r\n footer: `\r\n padding: 24px 40px 32px;\r\n text-align: center;\r\n border-top: 1px solid ${colors.divider};\r\n `,\r\n footerText: `\r\n margin: 0;\r\n color: ${colors.textMuted};\r\n font-size: 12px;\r\n line-height: 1.8;\r\n `,\r\n footerLink: `\r\n color: ${colors.textSecondary};\r\n text-decoration: none;\r\n `,\r\n badge: `\r\n display: inline-block;\r\n background-color: rgba(255,255,255,0.1);\r\n color: ${colors.textSecondary};\r\n padding: 4px 12px;\r\n border-radius: 20px;\r\n font-size: 12px;\r\n font-weight: 500;\r\n letter-spacing: 0.3px;\r\n `,\r\n listItem: `\r\n color: ${colors.textSecondary};\r\n font-size: 14px;\r\n margin: 8px 0;\r\n padding-left: 8px;\r\n `,\r\n metaRow: `\r\n display: flex;\r\n justify-content: space-between;\r\n padding: 12px 0;\r\n border-bottom: 1px solid ${colors.divider};\r\n `,\r\n metaLabel: `\r\n color: ${colors.textMuted};\r\n font-size: 13px;\r\n `,\r\n metaValue: `\r\n color: ${colors.textPrimary};\r\n font-size: 13px;\r\n font-weight: 500;\r\n `,\r\n};\r\n\r\n/**\r\n * Email Verification Template\r\n */\r\nexport function buildVerificationEmailTemplate(\r\n data: VerificationEmailData,\r\n): string {\r\n const { firstName, verificationUrl, expiresIn } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>Verify Your Email</title>\r\n <!--[if mso]>\r\n <style type=\"text/css\">\r\n body, table, td {font-family: Arial, Helvetica, sans-serif !important;}\r\n </style>\r\n <![endif]-->\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n ✉️\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">Verify your email</h1>\r\n <p style=\"${styles.headerSubtitle}\">One quick step to get started</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n Thanks for signing up. To complete your registration and unlock all features, \r\n please verify your email address by clicking the button below.\r\n </p>\r\n \r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${verificationUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Verify Email Address\r\n </a>\r\n </div>\r\n \r\n <div style=\"${styles.infoCard}\">\r\n <p style=\"${styles.infoCardTitle}\">⏱ Time Sensitive</p>\r\n <p style=\"${styles.infoCardText}\">\r\n This verification link will expire in <strong>${expiresIn}</strong>. \r\n If you didn't create an account, you can safely ignore this email.\r\n </p>\r\n </div>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Password Reset Template\r\n */\r\nexport function buildResetPasswordEmailTemplate(\r\n data: ResetPasswordEmailData,\r\n): string {\r\n const { firstName, resetUrl, expiresIn } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>Reset Your Password</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n 🔐\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">Reset your password</h1>\r\n <p style=\"${styles.headerSubtitle}\">We received a reset request</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n We received a request to reset the password for your account. \r\n Click the button below to create a new password.\r\n </p>\r\n \r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${resetUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Reset Password\r\n </a>\r\n </div>\r\n \r\n <div style=\"${styles.warningCard}\">\r\n <p style=\"${styles.warningCardTitle}\">⚠️ Security Notice</p>\r\n <p style=\"${styles.warningCardText}\">\r\n • This link expires in <strong>${expiresIn}</strong><br>\r\n • This link can only be used once<br>\r\n • If you didn't request this, ignore this email\r\n </p>\r\n </div>\r\n \r\n <hr style=\"${styles.divider}\" />\r\n \r\n <p style=\"${styles.paragraph}; font-size: 13px; color: ${colors.textMuted};\">\r\n <strong>Didn't request this?</strong><br>\r\n Your password remains unchanged. If you're concerned about your account \r\n security, please contact our support team immediately.\r\n </p>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Welcome Email Template (sent after verification)\r\n */\r\nexport function buildWelcomeEmailTemplate(data: WelcomeEmailData): string {\r\n const { firstName, loginUrl } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>Welcome!</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n ✨\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">You're all set</h1>\r\n <p style=\"${styles.headerSubtitle}\">Your account is now active</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Welcome, ${firstName}</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n Your email has been verified and your account is ready to go. \r\n We're excited to have you on board.\r\n </p>\r\n \r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${loginUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Get Started\r\n </a>\r\n </div>\r\n \r\n <div style=\"${styles.successCard}\">\r\n <p style=\"${styles.successCardTitle}\">✓ Quick Start</p>\r\n <p style=\"margin: 0; color: rgba(255,255,255,0.7); font-size: 14px; line-height: 1.8;\">\r\n • Complete your profile for a personalized experience<br>\r\n • Explore the dashboard to discover features<br>\r\n • Check out the documentation for guides\r\n </p>\r\n </div>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n If you have any questions, our support team is here to help you \r\n get the most out of your experience.\r\n </p>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Invite Email Template\r\n */\r\nexport function buildInviteEmailTemplate(data: InviteEmailData): string {\r\n const { inviterName, organizationName, role, inviteUrl, expiresIn } = data;\r\n\r\n const inviterText = inviterName\r\n ? `<strong>${inviterName}</strong> has invited you`\r\n : 'You have been invited';\r\n\r\n const orgText = organizationName\r\n ? ` to join <strong>${organizationName}</strong>`\r\n : '';\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>You're Invited</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n 💌\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">You're invited</h1>\r\n <p style=\"${styles.headerSubtitle}\">Join the team</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hello,</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n ${inviterText}${orgText} as a <span style=\"${styles.badge}\">${role}</span>\r\n </p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n Click the button below to accept the invitation and set up your account.\r\n </p>\r\n \r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${inviteUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Accept Invitation\r\n </a>\r\n </div>\r\n \r\n <div style=\"${styles.infoCard}\">\r\n <p style=\"${styles.infoCardTitle}\">📋 Invitation Details</p>\r\n <table style=\"width: 100%; border-collapse: collapse;\">\r\n <tr>\r\n <td style=\"padding: 8px 0; color: ${colors.textMuted}; font-size: 14px;\">Role</td>\r\n <td style=\"padding: 8px 0; color: ${colors.textPrimary}; font-size: 14px; text-align: right; font-weight: 500;\">${role}</td>\r\n </tr>\r\n <tr>\r\n <td style=\"padding: 8px 0; color: ${colors.textMuted}; font-size: 14px; border-top: 1px solid ${colors.divider};\">Expires in</td>\r\n <td style=\"padding: 8px 0; color: ${colors.textPrimary}; font-size: 14px; text-align: right; font-weight: 500; border-top: 1px solid ${colors.divider};\">${expiresIn}</td>\r\n </tr>\r\n </table>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Generic notification email template\r\n */\r\nexport function buildNotificationEmailTemplate(data: {\r\n title: string;\r\n subtitle?: string;\r\n icon?: string;\r\n firstName: string;\r\n message: string;\r\n actionUrl?: string;\r\n actionText?: string;\r\n secondaryActionUrl?: string;\r\n secondaryActionText?: string;\r\n}): string {\r\n const {\r\n title,\r\n subtitle,\r\n icon = '🔔',\r\n firstName,\r\n message,\r\n actionUrl,\r\n actionText,\r\n secondaryActionUrl,\r\n secondaryActionText,\r\n } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>${title}</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n ${icon}\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">${title}</h1>\r\n ${subtitle ? `<p style=\"${styles.headerSubtitle}\">${subtitle}</p>` : ''}\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <div style=\"${styles.paragraph}; white-space: pre-line;\">${message}</div>\r\n \r\n ${\r\n actionUrl\r\n ? `\r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${actionUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n ${actionText || 'Take Action'}\r\n </a>\r\n ${\r\n secondaryActionUrl\r\n ? `\r\n <br><br>\r\n <a href=\"${secondaryActionUrl}\" style=\"${styles.secondaryButton}\" target=\"_blank\">\r\n ${secondaryActionText || 'Learn More'}\r\n </a>\r\n `\r\n : ''\r\n }\r\n </div>\r\n `\r\n : ''\r\n }\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Two-Factor Authentication Code Template\r\n */\r\nexport function buildTwoFactorCodeEmailTemplate(data: {\r\n firstName: string;\r\n code: string;\r\n expiresIn: string;\r\n ipAddress?: string;\r\n location?: string;\r\n}): string {\r\n const { firstName, code, expiresIn, ipAddress, location } = data;\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>Your Verification Code</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n 🔑\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">Verification code</h1>\r\n <p style=\"${styles.headerSubtitle}\">Use this code to sign in</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <p style=\"${styles.paragraph}\">\r\n Enter this verification code to complete your sign-in:\r\n </p>\r\n \r\n <div style=\"\r\n text-align: center;\r\n margin: 32px 0;\r\n padding: 24px;\r\n background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);\r\n border: 1px solid rgba(255,255,255,0.15);\r\n border-radius: 12px;\r\n \">\r\n <p style=\"\r\n margin: 0;\r\n font-size: 36px;\r\n font-weight: 700;\r\n letter-spacing: 8px;\r\n color: ${colors.textPrimary};\r\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;\r\n \">${code}</p>\r\n </div>\r\n \r\n <div style=\"${styles.warningCard}\">\r\n <p style=\"${styles.warningCardTitle}\">⏱ Expires Soon</p>\r\n <p style=\"${styles.warningCardText}\">\r\n This code will expire in <strong>${expiresIn}</strong>. \r\n Never share this code with anyone.\r\n </p>\r\n </div>\r\n \r\n ${\r\n ipAddress || location\r\n ? `\r\n <div style=\"${styles.infoCard}\">\r\n <p style=\"${styles.infoCardTitle}\">📍 Sign-in Attempt</p>\r\n <table style=\"width: 100%; border-collapse: collapse;\">\r\n ${\r\n ipAddress\r\n ? `\r\n <tr>\r\n <td style=\"padding: 6px 0; color: ${colors.textMuted}; font-size: 13px;\">IP Address</td>\r\n <td style=\"padding: 6px 0; color: ${colors.textSecondary}; font-size: 13px; text-align: right; font-family: monospace;\">${ipAddress}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n ${\r\n location\r\n ? `\r\n <tr>\r\n <td style=\"padding: 6px 0; color: ${colors.textMuted}; font-size: 13px;\">Location</td>\r\n <td style=\"padding: 6px 0; color: ${colors.textSecondary}; font-size: 13px; text-align: right;\">${location}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n </table>\r\n </div>\r\n `\r\n : ''\r\n }\r\n \r\n <p style=\"${styles.paragraph}; font-size: 13px; color: ${colors.textMuted};\">\r\n If you didn't attempt to sign in, please secure your account immediately \r\n by changing your password.\r\n </p>\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated message — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n\r\n/**\r\n * Account Activity Alert Template\r\n */\r\nexport function buildSecurityAlertEmailTemplate(data: {\r\n firstName: string;\r\n alertType:\r\n | 'new_login'\r\n | 'password_changed'\r\n | 'email_changed'\r\n | 'suspicious_activity';\r\n timestamp: string;\r\n ipAddress?: string;\r\n location?: string;\r\n device?: string;\r\n actionUrl?: string;\r\n}): string {\r\n const {\r\n firstName,\r\n alertType,\r\n timestamp,\r\n ipAddress,\r\n location,\r\n device,\r\n actionUrl,\r\n } = data;\r\n\r\n const alertConfig = {\r\n new_login: {\r\n icon: '🔓',\r\n title: 'New sign-in detected',\r\n subtitle: 'A new device signed into your account',\r\n message:\r\n 'We noticed a new sign-in to your account. If this was you, no action is needed.',\r\n },\r\n password_changed: {\r\n icon: '🔐',\r\n title: 'Password changed',\r\n subtitle: 'Your password was recently updated',\r\n message:\r\n 'Your account password was successfully changed. If you made this change, no further action is required.',\r\n },\r\n email_changed: {\r\n icon: '📧',\r\n title: 'Email address changed',\r\n subtitle: 'Your email was recently updated',\r\n message:\r\n 'The email address associated with your account was changed. If you made this change, no further action is required.',\r\n },\r\n suspicious_activity: {\r\n icon: '⚠️',\r\n title: 'Unusual activity detected',\r\n subtitle: 'We noticed something unusual',\r\n message:\r\n 'We detected unusual activity on your account. Please review the details below and secure your account if needed.',\r\n },\r\n };\r\n\r\n const config = alertConfig[alertType];\r\n\r\n return `\r\n <!DOCTYPE html>\r\n <html lang=\"en\">\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <meta name=\"color-scheme\" content=\"dark\">\r\n <meta name=\"supported-color-schemes\" content=\"dark\">\r\n <title>${config.title}</title>\r\n </head>\r\n <body style=\"${styles.wrapper}\">\r\n <div style=\"${styles.container}\">\r\n <div style=\"${styles.card}\">\r\n <!-- Header -->\r\n <div style=\"${styles.header}\">\r\n <div style=\"${styles.iconWrapper}\">\r\n ${config.icon}\r\n </div>\r\n <h1 style=\"${styles.headerTitle}\">${config.title}</h1>\r\n <p style=\"${styles.headerSubtitle}\">${config.subtitle}</p>\r\n </div>\r\n \r\n <!-- Body -->\r\n <div style=\"${styles.body}\">\r\n <p style=\"${styles.greeting}\">Hi ${firstName},</p>\r\n \r\n <p style=\"${styles.paragraph}\">${config.message}</p>\r\n \r\n <div style=\"${styles.infoCard}\">\r\n <p style=\"${styles.infoCardTitle}\">📋 Activity Details</p>\r\n <table style=\"width: 100%; border-collapse: collapse;\">\r\n <tr>\r\n <td style=\"padding: 10px 0; color: ${colors.textMuted}; font-size: 14px; border-bottom: 1px solid ${colors.divider};\">Time</td>\r\n <td style=\"padding: 10px 0; color: ${colors.textPrimary}; font-size: 14px; text-align: right; border-bottom: 1px solid ${colors.divider};\">${timestamp}</td>\r\n </tr>\r\n ${\r\n ipAddress\r\n ? `\r\n <tr>\r\n <td style=\"padding: 10px 0; color: ${colors.textMuted}; font-size: 14px; border-bottom: 1px solid ${colors.divider};\">IP Address</td>\r\n <td style=\"padding: 10px 0; color: ${colors.textSecondary}; font-size: 14px; text-align: right; font-family: monospace; border-bottom: 1px solid ${colors.divider};\">${ipAddress}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n ${\r\n location\r\n ? `\r\n <tr>\r\n <td style=\"padding: 10px 0; color: ${colors.textMuted}; font-size: 14px; border-bottom: 1px solid ${colors.divider};\">Location</td>\r\n <td style=\"padding: 10px 0; color: ${colors.textSecondary}; font-size: 14px; text-align: right; border-bottom: 1px solid ${colors.divider};\">${location}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n ${\r\n device\r\n ? `\r\n <tr>\r\n <td style=\"padding: 10px 0; color: ${colors.textMuted}; font-size: 14px;\">Device</td>\r\n <td style=\"padding: 10px 0; color: ${colors.textSecondary}; font-size: 14px; text-align: right;\">${device}</td>\r\n </tr>\r\n `\r\n : ''\r\n }\r\n </table>\r\n </div>\r\n \r\n ${\r\n alertType === 'suspicious_activity' || alertType === 'new_login'\r\n ? `\r\n <div style=\"${styles.warningCard}\">\r\n <p style=\"${styles.warningCardTitle}\">🛡️ Not You?</p>\r\n <p style=\"${styles.warningCardText}\">\r\n If you don't recognize this activity, we recommend changing your password \r\n immediately and reviewing your account settings.\r\n </p>\r\n </div>\r\n `\r\n : ''\r\n }\r\n \r\n ${\r\n actionUrl\r\n ? `\r\n <div style=\"${styles.buttonWrapper}\">\r\n <a href=\"${actionUrl}\" style=\"${styles.button}\" target=\"_blank\">\r\n Review Account Activity\r\n </a>\r\n </div>\r\n `\r\n : ''\r\n }\r\n </div>\r\n \r\n <!-- Footer -->\r\n <div style=\"${styles.footer}\">\r\n <p style=\"${styles.footerText}\">\r\n This is an automated security alert — please do not reply.<br>\r\n © ${new Date().getFullYear()} All rights reserved.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n </body>\r\n </html>\r\n `;\r\n}\r\n","import express, { Router, type Router as ExpressRouter } from 'express';\nimport { AuthAdminService } from '../services/auth-admin.service.js';\nimport { requireAuth } from '../middlewares/auth.middleware.js';\n\nexport function createDashboardRouter(options: any): ExpressRouter {\n const r = Router();\n const kc = new AuthAdminService();\n r.use(express.json());\n\n r.post('/', requireAuth(), async (req, res, next) => {\n try {\n const { slug, isPublic, authFlow, orgDomain } = req.body || {};\n const redirectUris = [`https://${slug}.${orgDomain}/*`];\n const created = await kc.createClient(slug, redirectUris, !!isPublic);\n if (authFlow || isPublic != null) {\n await kc.updateClient(created.id, {\n authenticationFlowBindingOverrides: authFlow\n ? { browser: authFlow }\n : undefined,\n registrationAllowed: !!isPublic,\n });\n }\n res.json({ clientId: created.clientId });\n } catch (e) {\n next(e);\n }\n });\n\n return r;\n}\n","import express, { Router, type Router as ExpressRouter } from 'express';\nimport { OrgUser } from '../models/user.model';\nimport { EmailService } from '../services/email.service';\n\nexport function createEmailRouter(options: any): ExpressRouter {\n const r = Router();\n\n const emailService = new EmailService();\n\n r.use(express.json());\n r.use(express.urlencoded({ extended: true }));\n\n r.get('/verify', (req, res) =>\n res.json({ ok: true, token: req.query.token }),\n );\n\n /**\n * Generic email sender.\n * Body: { userId?: string, to: string, subject: string, html: string }\n * - If userId provided, enforce canSend() using that user's lastEmailSent and update it on success.\n */\n r.post('/send', async (req, res) => {\n try {\n const { userId, to, subject, html, from } = req.body ?? {};\n\n if (!to || !subject || !html) {\n return res.status(400).json({\n ok: false,\n error: 'BAD_REQUEST',\n message: '`to`, `subject`, and `html` are required.',\n });\n }\n\n // Rate-limit per user if userId is provided\n if (userId) {\n const user: any = await OrgUser.findOne({ id: userId }).lean();\n if (!user) {\n return res.status(404).json({\n ok: false,\n error: 'NOT_FOUND',\n message: 'User not found.',\n });\n }\n\n const can = emailService.canSend(user?.lastEmailSent || []);\n if (!can.ok) {\n return res.status(429).json({\n ok: false,\n error: can.reason,\n waitMs: can.waitMs,\n message: 'Too many emails sent recently. Please retry later.',\n });\n }\n }\n\n await emailService.send(to, subject, html, from);\n\n if (userId) {\n await OrgUser.updateOne(\n { id: userId },\n { $push: { lastEmailSent: new Date() } },\n );\n }\n\n return res.json({ ok: true });\n } catch (err: any) {\n return res.status(500).json({\n ok: false,\n error: 'INTERNAL',\n message: err?.message ?? 'Error',\n });\n }\n });\n\n return r;\n}\n","import { Router, type Router as ExpressRouter } from 'express';\nimport { requireAuth } from '../middlewares/auth.middleware.js';\nimport { ProjectsService } from '../services/projects.service.js';\n\nexport function createProjectsRouter(options: any): ExpressRouter {\n const r = Router();\n const svc = new ProjectsService();\n\n r.post('/create', requireAuth(), async (req, res) => {\n const { org_id, name, description } = req.body || {};\n const p = await svc.create(org_id, name, description);\n res.json(p);\n });\n\n r.get('/:org_id', requireAuth(), async (req, res) => {\n res.json(await svc.list(req.params.org_id));\n });\n\n r.get('/:org_id/:id', requireAuth(), async (req, res) => {\n res.json(await svc.get(req.params.org_id, req.params.id));\n });\n\n r.put('/:org_id/:id', requireAuth(), async (req, res) => {\n res.json(\n await svc.update(req.params.org_id, req.params.id, req.body || {}),\n );\n });\n\n r.delete('/:org_id/:id', requireAuth(), async (req, res) => {\n res.json(await svc.remove(req.params.org_id, req.params.id));\n });\n\n return r;\n}\n","import { randomUUID } from 'crypto';\nimport { ModuleConnection } from '../models/moduleConnection.model.js';\nimport { Project } from '../models/project.model.js';\n\nexport class ProjectsService {\n async create(org_id: string, name: string, description?: string) {\n const _id = randomUUID();\n const secret = randomUUID();\n const p = await Project.create({ _id, org_id, name, description, secret });\n await ModuleConnection.create({\n projectId: _id,\n modules: { data: [], integration: [], storage: [] },\n });\n return p.toObject();\n }\n\n async list(org_id: string) {\n return Project.find({ org_id }).lean();\n }\n\n async get(org_id: string, id: string) {\n return Project.findOne({ org_id, _id: id }).lean();\n }\n\n async update(org_id: string, id: string, patch: any) {\n return Project.findOneAndUpdate(\n { org_id, _id: id },\n { $set: patch },\n { new: true },\n ).lean();\n }\n\n async remove(org_id: string, id: string) {\n await Project.deleteOne({ org_id, _id: id });\n await ModuleConnection.deleteMany({ projectId: id });\n return { ok: true };\n }\n}\n","import mongoose from 'mongoose';\n\nconst ModuleItemSchema = new mongoose.Schema(\n { id: { type: String, required: true } },\n { _id: false },\n);\n\nconst ModuleConnectionSchema = new mongoose.Schema(\n {\n projectId: { type: String, required: true, index: true },\n modules: {\n data: { type: [ModuleItemSchema], default: [] },\n integration: { type: [ModuleItemSchema], default: [] },\n storage: { type: [ModuleItemSchema], default: [] },\n },\n },\n { timestamps: true, collection: 'module_connection' },\n);\n\nexport const ModuleConnection = mongoose.model(\n 'ModuleConnection',\n ModuleConnectionSchema,\n);\n","import mongoose from 'mongoose';\n\nconst ProjectSchema = new mongoose.Schema(\n {\n _id: { type: String, required: true },\n org_id: { type: String, required: true, index: true },\n name: { type: String, required: true },\n description: { type: String },\n secret: { type: String, required: true },\n },\n { timestamps: true, collection: 'projects' },\n);\n\nexport const Project = mongoose.model('Project', ProjectSchema);\n","import type { AuthUser } from 'aaspai-types';\nimport bcrypt from 'bcryptjs';\nimport { randomUUID } from 'crypto';\nimport express, { Request, Response, Router } from 'express';\nimport { requireAuth } from '../../middlewares/auth.middleware.js';\nimport { requireRole } from '../../middlewares/requireRole.js';\nimport { PermissionsModel } from '../../models/permissions.model';\nimport { RolePermissionModel } from '../../models/rolePermission.model';\nimport { OrgUser } from '../../models/user.model.js';\n\ninterface AuthenticatedRequest extends Request {\n user?: AuthUser;\n}\n\nfunction resolveOrgId(req: AuthenticatedRequest): string | null {\n const user = req.user || {};\n const fromUser = (user.orgId as string) || (user.org_id as string) || null;\n\n const fromQuery = (req.query.orgId as string) || null;\n const fromBody = (req.body && (req.body.orgId as string)) || null;\n\n return fromQuery || fromBody || fromUser;\n}\n\nfunction resolveProjectId(req: AuthenticatedRequest): string | null {\n const user = req.user || {};\n const fromUser = (user.projectId as string) || null;\n\n const fromQuery = (req.query.projectId as string) || null;\n const fromBody = (req.body && (req.body.projectId as string)) || null;\n\n return fromQuery || fromBody || fromUser;\n}\n\nexport function createAdminRouter(_options: any = {}): Router {\n const r = Router();\n\n r.use(express.json());\n r.use(express.urlencoded({ extended: true }));\n\n // All routes require platform_admin (for now).\n const adminGuards = [requireAuth(), requireRole('platform_admin')];\n\n r.post(\n '/users',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n const {\n firstName,\n lastName,\n email: emailAddress,\n password,\n emailVerified = false,\n roles = [],\n } = req.body || {};\n\n const projectId = resolveProjectId(req);\n\n try {\n const hashedPassword = password\n ? await bcrypt.hash(password, 10)\n : undefined;\n\n const user = await OrgUser.create({\n id: randomUUID(),\n email: emailAddress,\n orgId: process.env.ORG_ID!,\n firstName,\n lastName,\n projectId,\n emailVerified,\n metadata: [],\n passwordHash: hashedPassword,\n roles,\n });\n\n return res.json({\n id: user.id,\n email: user.email,\n message: 'Verification email sent. Please check your inbox.',\n });\n } catch (err: any) {\n console.error('Create user error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.delete(\n '/users',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const userId = (req?.body?.id || req?.query?.id) as string | undefined;\n\n if (!userId) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'UserId is required (send in body.id or ?id=...)',\n });\n }\n\n const deleted = await OrgUser.findOneAndDelete({ id: userId }).exec();\n\n if (!deleted) {\n return res.status(404).json({\n error: 'NOT_FOUND',\n message: 'User not found or already deleted',\n });\n }\n\n return res.status(200).json({\n ok: true,\n message: 'User deleted successfully',\n deletedUser: {\n id: deleted.id,\n firstName: deleted.firstName,\n email: deleted.email,\n orgId: deleted.orgId,\n },\n });\n } catch (err: any) {\n console.error('Delete user error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.put(\n '/users/:id',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n const userId = req.params.id;\n\n const {\n firstName,\n lastName,\n email: emailAddress,\n password,\n emailVerified,\n roles,\n } = req.body || {};\n\n try {\n const existingUser = await OrgUser.findOne({\n id: userId,\n orgId: process.env.ORG_ID!,\n });\n\n if (!existingUser) {\n return res.status(404).json({ error: 'USER_NOT_FOUND' });\n }\n\n // Only update allowed fields\n if (firstName !== undefined) existingUser.firstName = firstName;\n if (lastName !== undefined) existingUser.lastName = lastName;\n if (emailAddress !== undefined) existingUser.email = emailAddress;\n if (emailVerified !== undefined)\n existingUser.emailVerified = emailVerified;\n if (roles !== undefined) existingUser.roles = roles;\n\n // Password (if provided)\n if (password) {\n existingUser.passwordHash = await bcrypt.hash(password, 10);\n }\n\n // DO NOT UPDATE projectId — keep original value forever\n // existingUser.projectId stays as-is\n\n await existingUser.save();\n\n return res.json({\n id: existingUser.id,\n email: existingUser.email,\n message: 'User updated successfully.',\n });\n } catch (err: any) {\n console.error('Update user error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * GET /permissions\n * List API configs (permissions) for an org (or platform/global when orgId is absent).\n */\n r.get(\n '/permissions',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n\n const filter: any = {};\n if (orgId !== null) {\n filter.orgId = orgId;\n } else {\n filter.orgId = null;\n }\n\n const items = await PermissionsModel.find(filter).lean().exec();\n return res.json(items);\n } catch (err) {\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * POST /permissions\n * Create a new API config (permission).\n * Body:\n * {\n * orgId?: string,\n * key: string,\n * method: string,\n * path: string,\n * module?: string,\n * description?: string,\n * isInternal?: boolean\n * }\n *\n * Also auto-assigns this permission to platform_admin for that org.\n */\n r.post(\n '/permissions',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n\n const {\n key,\n type,\n apiId,\n description,\n isInternal = false,\n } = req.body || {};\n\n if (!key || !type) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'permission key, and permission type are required',\n });\n }\n\n const id = randomUUID();\n\n const permission = await PermissionsModel.create({\n id,\n orgId: orgId ?? null,\n key,\n type,\n apiId,\n description,\n isInternal: !!isInternal,\n });\n\n // Auto-assign to platform_admin role for this org\n await RolePermissionModel.findOneAndUpdate(\n { orgId: orgId ?? null, role: 'platform_admin' },\n { $addToSet: { permissions: key } },\n { upsert: true, new: true },\n ).exec();\n\n return res.status(201).json(permission);\n } catch (err: any) {\n if (err && err.code === 11000) {\n return res.status(409).json({\n error: 'DUPLICATE_PERMISSION',\n message: 'Permission key already exists for this org',\n });\n }\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.put(\n '/permissions/:id',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n const permissionId = req.params.id;\n\n const { key, type, apiId, description, isInternal } = req.body || {};\n // Fetch existing permission\n const existing = await PermissionsModel.findOne({\n id: permissionId,\n orgId: orgId ?? null,\n });\n\n if (!existing) {\n return res.status(404).json({\n error: 'NOT_FOUND',\n message: 'Permission does not exist',\n });\n }\n\n const oldKey = existing.key;\n\n // Apply updates\n if (key !== undefined) existing.key = key;\n if (type !== undefined) existing.type = type;\n if (apiId !== undefined) existing.apiId = apiId;\n if (description !== undefined) existing.description = description;\n if (isInternal !== undefined) existing.isInternal = !!isInternal;\n\n await existing.save();\n\n if (oldKey !== key) {\n // 1. Remove old key\n await RolePermissionModel.updateMany(\n {\n orgId: orgId ?? null,\n permissions: oldKey,\n },\n {\n $pull: { permissions: oldKey },\n },\n );\n\n // 2. Add new key\n await RolePermissionModel.updateMany(\n {\n orgId: orgId ?? null,\n },\n {\n $addToSet: { permissions: key },\n },\n );\n }\n\n return res.json(existing);\n } catch (err: any) {\n if (err && err.code === 11000) {\n return res.status(409).json({\n error: 'DUPLICATE_PERMISSION',\n message: 'Permission key already exists for this org',\n });\n }\n\n console.error('Update permission error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.delete(\n '/permissions',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const permissionId = (req?.body?.id || req?.query?.id) as\n | string\n | undefined;\n\n if (!permissionId) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'Permission id is required (send in body.id or ?id=...)',\n });\n }\n\n // Fetch permission before deletion → we need key + orgId\n const existing = await PermissionsModel.findOne({ id: permissionId });\n if (!existing) {\n return res.status(404).json({\n error: 'NOT_FOUND',\n message: 'Permission not found or already deleted',\n });\n }\n\n const { key, orgId } = existing;\n\n // Delete permission\n await PermissionsModel.deleteOne({ id: permissionId });\n\n // Remove permission key from all roles in THE SAME org\n await RolePermissionModel.updateMany(\n { orgId: orgId ?? null },\n { $pull: { permissions: key } },\n );\n\n return res.status(200).json({\n ok: true,\n message: 'Permission deleted successfully',\n deletedPermission: {\n id: existing.id,\n key: existing.key,\n type: existing.type,\n apiId: existing.apiId,\n description: existing.description,\n isInternal: existing.isInternal,\n orgId: existing.orgId,\n },\n });\n } catch (err: any) {\n console.error('Delete permission error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * GET /roles\n * List role-permissions for org.\n * Query: ?orgId=...\n */\n r.get(\n '/roles',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n\n const filter: any = {};\n if (orgId !== null) {\n filter.orgId = orgId;\n } else {\n filter.orgId = null;\n }\n\n const roles = await RolePermissionModel.find(filter).lean().exec();\n return res.json(roles);\n } catch (err) {\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * POST /roles\n * Create or replace a role's permission set.\n *\n * Body:\n * {\n * orgId?: string,\n * role: string,\n * permissions: string[]\n * }\n */\n r.post(\n '/roles',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n const { role, permissions } = req.body || {};\n\n if (!role || !Array.isArray(permissions)) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'role and permissions[] are required',\n });\n }\n\n const id = randomUUID();\n\n const doc = await RolePermissionModel.findOneAndUpdate(\n { orgId: orgId ?? null, role },\n { $set: { permissions } },\n { upsert: true, new: true },\n ).exec();\n\n return res.status(200).json(doc);\n } catch (err) {\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n r.put(\n '/roles/:id',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const orgId = resolveOrgId(req);\n const roleId = req.params.id;\n\n const { role: newRoleName, permissions } = req.body || {};\n\n if (!newRoleName || !Array.isArray(permissions)) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'role and permissions are required',\n });\n }\n\n // 1. Find the existing role\n const existing = await RolePermissionModel.findById(roleId);\n\n if (!existing) {\n return res.status(404).json({\n error: 'ROLE_NOT_FOUND',\n message: 'Role does not exist',\n });\n }\n\n const oldRoleName = existing.role;\n\n // 2. Update the role document\n existing.role = newRoleName;\n existing.permissions = permissions;\n await existing.save();\n\n if (oldRoleName !== newRoleName) {\n // Step 1 — remove old role from all users\n await OrgUser.updateMany(\n {\n orgId: orgId ?? null,\n roles: oldRoleName,\n },\n {\n $pull: { roles: oldRoleName },\n },\n );\n\n // Step 2 — add new role to users that previously had the old role\n await OrgUser.updateMany(\n {\n orgId: orgId ?? null,\n roles: { $ne: newRoleName }, // avoid duplicates\n },\n {\n $addToSet: { roles: newRoleName },\n },\n );\n }\n\n return res.status(200).json(existing);\n } catch (err) {\n console.error('Update role error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n /**\n * DELETE /roles\n * Delete a role completely (by MongoDB _id)\n *\n * Body:\n * {\n * \"id\": \"507f1f77bcf86cd799439011\" // MongoDB _id of the RolePermission document\n * }\n *\n * Query alternative (optional):\n * ?id=507f1f77bcf86cd799439011\n */\n r.delete(\n '/roles',\n ...adminGuards,\n async (req: AuthenticatedRequest, res: Response) => {\n try {\n const roleId = (req?.body?.id || req?.query?.id) as string | undefined;\n\n if (!roleId) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'Role _id is required (send in body.id or ?id=...)',\n });\n }\n\n // Optional: validate it's a valid ObjectId\n if (!/^[0-9a-fA-F]{24}$/.test(roleId)) {\n return res.status(400).json({\n error: 'VALIDATION_ERROR',\n message: 'Invalid role _id format',\n });\n }\n\n const deleted =\n await RolePermissionModel.findByIdAndDelete(roleId).exec();\n\n if (!deleted) {\n return res.status(404).json({\n error: 'NOT_FOUND',\n message: 'Role not found or already deleted',\n });\n }\n\n return res.status(200).json({\n ok: true,\n message: 'Role deleted successfully',\n deletedRole: {\n _id: deleted._id,\n role: deleted.role,\n orgId: deleted.orgId,\n },\n });\n } catch (err: any) {\n console.error('Delete role error:', err);\n return res.status(500).json({ error: 'INTERNAL_ERROR' });\n }\n },\n );\n\n return r;\n}\n","import type { AuthXSession } from 'aaspai-types';\r\nimport type { NextFunction, Request, Response } from 'express';\r\nimport { hasAnyRole } from '../core/utils.js';\r\n\r\n/**\r\n * Express middleware to require one or more roles\r\n * @param roles - Array of role names (user must have at least one)\r\n */\r\nexport function requireRole(...roles: string[]) {\r\n return (req: Request, res: Response, next: NextFunction) => {\r\n const user = (req as any).user as AuthXSession | undefined;\r\n\r\n if (!user) {\r\n return res.status(401).json({ error: 'Unauthorized' });\r\n }\r\n\r\n if (!roles || roles.length === 0) {\r\n return next();\r\n }\r\n\r\n if (!hasAnyRole(user, roles)) {\r\n return res.status(403).json({\r\n error: `Requires one of roles: ${roles.join(', ')}`,\r\n required: roles,\r\n userRoles: user.roles,\r\n });\r\n }\r\n\r\n next();\r\n };\r\n}\r\n","import mongoose, { Document, Schema } from 'mongoose';\r\n\r\nexport interface PermissionsDocument extends Document {\r\n id: string; // stable UUID\r\n orgId?: string | null; // null => platform/global\r\n key: string; // permission key, e.g. \"users.read\"\r\n type: string; // \"GET\", \"POST\", ...\r\n apiId?: string; // \"/api/users\"\r\n description?: string;\r\n isInternal: boolean; // internal vs external\r\n createdAt: Date;\r\n updatedAt: Date;\r\n}\r\n\r\nconst PermissionsSchema = new Schema<PermissionsDocument>(\r\n {\r\n id: { type: String, required: true, index: true },\r\n orgId: { type: String, default: null, index: true },\r\n key: { type: String, required: true },\r\n type: { type: String, required: true },\r\n apiId: { type: String, required: false },\r\n description: { type: String },\r\n isInternal: { type: Boolean, default: false },\r\n },\r\n {\r\n timestamps: true,\r\n },\r\n);\r\n\r\n// One permission key per org\r\nPermissionsSchema.index({ orgId: 1, key: 1 }, { unique: true });\r\n\r\nexport const PermissionsModel = mongoose.model<PermissionsDocument>(\r\n 'Permissions',\r\n PermissionsSchema,\r\n 'permissions',\r\n);\r\n","import type { INestApplication } from '@nestjs/common';\nimport { Db } from 'mongodb';\nimport {\n createAdminRouter,\n createAuthRouter,\n createDashboardRouter,\n createEmailRouter,\n createProjectsRouter,\n} from '../express';\nimport type { AuthRouterOptions } from 'aaspai-types';\n\nexport interface NestAuthxOptions {\n routerOptions?: AuthRouterOptions;\n adminDb?: Db;\n adminBasePath?: string;\n authBasePath?: string;\n dashboardBasePath?: string;\n dnsBasePath?: string;\n emailBasePath?: string;\n uploadBasePath?: string;\n projectsBasePath?: string;\n}\n\nexport const nest = {\n mountAuthRouter(\n app: INestApplication,\n options: NestAuthxOptions,\n ): {\n authRouter: any;\n dashboardRouter?: any;\n dnsRouter?: any;\n emailRouter?: any;\n uploadRouter?: any;\n projectsRouter?: any;\n adminRouter?: any;\n } {\n const httpAdapter = app.getHttpAdapter().getInstance();\n\n const authRouter = createAuthRouter(options.routerOptions || {});\n const authPath = options.authBasePath || '/auth';\n httpAdapter.use(authPath, authRouter);\n\n const adminRouter = createAdminRouter(options);\n const adminPath = options.adminBasePath || '/admin';\n httpAdapter.use(adminPath, adminRouter);\n\n const dashboardRouter = createDashboardRouter(options);\n const dashboardPath = options.dashboardBasePath || '/dashboards';\n httpAdapter.use(dashboardPath, dashboardRouter);\n\n const emailRouter = createEmailRouter(options);\n const emailPath = options.emailBasePath || '/email';\n httpAdapter.use(emailPath, emailRouter);\n\n const projectsRouter = createProjectsRouter(options);\n const projectsPath = options.projectsBasePath || '/projects';\n httpAdapter.use(projectsPath, projectsRouter);\n\n return {\n authRouter,\n dashboardRouter,\n emailRouter,\n projectsRouter,\n adminRouter,\n };\n },\n};\n","// src/middlewares/permission.middleware.ts\r\nimport type { AuthXUser } from 'aaspai-types';\r\nimport { NextFunction, Request, Response } from 'express';\r\nimport { RolePermissionModel } from '../models/rolePermission.model';\r\n\r\ninterface AuthenticatedRequest extends Request {\r\n user?: AuthXUser;\r\n}\r\n\r\nexport function requirePermission(permissionKey: string) {\r\n return async (\r\n req: AuthenticatedRequest,\r\n res: Response,\r\n next: NextFunction,\r\n ) => {\r\n try {\r\n console.log('INSIDE PERMISSION MIDDLEWARE', permissionKey);\r\n const user = req.user;\r\n\r\n if (!user) {\r\n return res.status(401).json({ error: 'Unauthorized' });\r\n }\r\n\r\n const roles = Array.isArray(user.roles) ? user.roles : [];\r\n\r\n if (!roles.length) {\r\n return res.status(403).json({ error: 'Forbidden', reason: 'NO_ROLES' });\r\n }\r\n\r\n // Global superuser shortcut\r\n if (roles.includes('platform_admin')) {\r\n return next();\r\n }\r\n\r\n // Try to determine org context (you can refine this later)\r\n const orgId =\r\n (user.orgId as string) ||\r\n (user.org_id as string) ||\r\n (user.projectId as string) ||\r\n null;\r\n\r\n const rolePermissions = await RolePermissionModel.find({\r\n orgId,\r\n role: { $in: roles },\r\n })\r\n .lean()\r\n .exec();\r\n\r\n const allowed = new Set<string>();\r\n\r\n for (const rp of rolePermissions) {\r\n if (Array.isArray(rp.permissions)) {\r\n for (const p of rp.permissions) {\r\n allowed.add(p);\r\n }\r\n }\r\n }\r\n\r\n if (!allowed.has(permissionKey)) {\r\n return res.status(403).json({\r\n error: 'Forbidden',\r\n reason: 'MISSING_PERMISSION',\r\n permission: permissionKey,\r\n });\r\n }\r\n\r\n return next();\r\n } catch (err) {\r\n return next(err);\r\n }\r\n };\r\n}\r\n","import type { NextFunction, Request, Response } from 'express';\r\nimport { hasPermission } from '../core/utils.js';\r\nimport type { AuthXSession } from 'aaspai-types';\r\n\r\n/**\r\n * Express middleware to require a specific permission\r\n * @param permission - Permission string (e.g., 'projects.create')\r\n */\r\nexport function requirePermission(permission: string) {\r\n return (req: Request, res: Response, next: NextFunction) => {\r\n const user = (req as any).user as AuthXSession | undefined;\r\n\r\n if (!user) {\r\n return res.status(401).json({ error: 'Unauthorized' });\r\n }\r\n\r\n if (!permission) {\r\n return next();\r\n }\r\n\r\n if (!hasPermission(user, permission)) {\r\n return res.status(403).json({\r\n error: 'Forbidden',\r\n reason: 'MISSING_PERMISSION',\r\n permission,\r\n userPermissions: user.permissions,\r\n });\r\n }\r\n\r\n next();\r\n };\r\n}\r\n\r\n","import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';\nimport { randomUUID } from 'crypto';\nimport { config } from '../config/index.js';\n\nexport class UploadsService {\n private s3: S3Client;\n\n constructor() {\n this.s3 = new S3Client({\n region: process.env.AWS_REGION!,\n credentials: {\n accessKeyId: process.env.AWS_ACCESS_KEY_ID!,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,\n },\n });\n }\n\n async uploadPublicImage(buffer: Buffer, mimetype: string, ext: string) {\n const key = `${randomUUID()}.${ext}`;\n await this.s3.send(\n new PutObjectCommand({\n Bucket: process.env.AWS_S3_BUCKET!,\n Key: key,\n Body: buffer,\n ACL: 'public-read',\n ContentType: mimetype,\n }),\n );\n return `https://${process.env.AWS_S3_BUCKET!}.s3.${process.env.AWS_REGION!}.amazonaws.com/${key}`;\n }\n}\n","import {\r\n ExecutionContext,\r\n ForbiddenException,\r\n Injectable,\r\n UnauthorizedException,\r\n} from '@nestjs/common';\r\nimport { Reflector } from '@nestjs/core';\r\nimport { AuthGuard } from '@nestjs/passport';\r\nimport type { AuthXSession } from 'aaspai-types';\r\nimport { hasAnyPermission, hasAnyRole } from '../core/utils.js';\r\nimport { PERMISSIONS_KEY } from './decorators/permissions.decorator.js';\r\nimport { ROLES_KEY } from './decorators/roles.decorator.js';\r\n\r\n/**\r\n * AuthX Guard for NestJS\r\n * Extends Passport's AuthGuard to authenticate requests using AuthXStrategy,\r\n * then validates role/permission requirements\r\n */\r\n@Injectable()\r\nexport class AuthXGuard extends AuthGuard('authx') {\r\n private readonly reflector = new Reflector();\r\n\r\n constructor() {\r\n super();\r\n }\r\n\r\n /**\r\n * Override handleRequest to convert Passport errors to NestJS exceptions\r\n * This prevents the \"Right-hand side of 'instanceof' is not an object\" error\r\n */\r\n handleRequest(err: any, user: any, info: any, context: any): any {\r\n // If there's an error or no user, throw UnauthorizedException\r\n if (err || !user) {\r\n const message =\r\n err?.message || info?.message || 'Authentication required';\r\n throw new UnauthorizedException(message);\r\n }\r\n return user;\r\n }\r\n\r\n async canActivate(context: ExecutionContext): Promise<boolean> {\r\n try {\r\n // First, trigger Passport authentication (this will set request.user via AuthXStrategy)\r\n // Wrap in try-catch to handle any errors from Passport\r\n let authenticated: boolean;\r\n try {\r\n authenticated = (await super.canActivate(context)) as boolean;\r\n } catch (passportError: any) {\r\n // Convert any Passport errors to UnauthorizedException\r\n const message = passportError?.message || 'Authentication required';\r\n throw new UnauthorizedException(message);\r\n }\r\n\r\n if (!authenticated) {\r\n throw new UnauthorizedException('Authentication required');\r\n }\r\n\r\n const request = context.switchToHttp().getRequest();\r\n const user = (request as any).user as AuthXSession | undefined;\r\n\r\n // Check if user is authenticated (should be set by Passport at this point)\r\n if (!user) {\r\n throw new UnauthorizedException('Authentication required');\r\n }\r\n\r\n // Get required roles from metadata\r\n const requiredRoles = this.reflector.getAllAndOverride<string[]>(\r\n ROLES_KEY,\r\n [context.getHandler(), context.getClass()],\r\n );\r\n \r\n // Get required permissions from metadata\r\n const requiredPermissions = this.reflector.getAllAndOverride<string[]>(\r\n PERMISSIONS_KEY,\r\n [context.getHandler(), context.getClass()],\r\n );\r\n\r\n // Check roles if specified\r\n if (requiredRoles && requiredRoles.length > 0) {\r\n if (!hasAnyRole(user, requiredRoles)) {\r\n throw new ForbiddenException(\r\n `Requires one of roles: ${requiredRoles.join(', ')}`,\r\n );\r\n }\r\n }\r\n\r\n // Check permissions if specified\r\n if (requiredPermissions && requiredPermissions.length > 0) {\r\n if (!hasAnyPermission(user, requiredPermissions)) {\r\n throw new ForbiddenException(\r\n `Requires one of permissions: ${requiredPermissions.join(', ')}`,\r\n );\r\n }\r\n }\r\n\r\n return true;\r\n } catch (error) {\r\n // If it's already a NestJS exception, rethrow it\r\n if (\r\n error instanceof UnauthorizedException ||\r\n error instanceof ForbiddenException\r\n ) {\r\n throw error;\r\n }\r\n // Otherwise, wrap it in UnauthorizedException\r\n const errorMessage =\r\n error instanceof Error ? error.message : String(error);\r\n throw new UnauthorizedException(errorMessage || 'Authentication failed');\r\n }\r\n }\r\n}\r\n","import { SetMetadata } from '@nestjs/common';\r\n\r\nexport const PERMISSIONS_KEY = 'permissions';\r\n\r\n/**\r\n * Decorator to specify required permissions for a route handler or controller\r\n * @param permissions - Array of permission strings (user must have at least one)\r\n */\r\nexport const Permissions = (...permissions: string[]) =>\r\n SetMetadata(PERMISSIONS_KEY, permissions);\r\n\r\n","import { SetMetadata } from '@nestjs/common';\r\n\r\nexport const ROLES_KEY = 'roles';\r\n\r\n/**\r\n * Decorator to specify required roles for a route handler or controller\r\n * @param roles - Array of role names (user must have at least one)\r\n */\r\nexport const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);\r\n\r\n","import { createParamDecorator, ExecutionContext } from '@nestjs/common';\r\nimport type { AuthXSession } from 'aaspai-types';\r\n\r\n/**\r\n * Decorator to inject the AuthXSession into a route handler parameter\r\n * Usage: @AuthXSessionDecorator() session: AuthXSession\r\n */\r\nexport const AuthXSessionDecorator = createParamDecorator(\r\n (data: unknown, ctx: ExecutionContext): AuthXSession | null => {\r\n const request = ctx.switchToHttp().getRequest();\r\n return (request as any).user || null;\r\n },\r\n);\r\n","import type { Request } from 'express';\r\nimport { Strategy } from 'passport';\r\nimport { OrgUser } from '../models/user.model.js';\r\nimport { buildSession } from '../core/session.js';\r\nimport { extractToken } from '../utils/extract.js';\r\nimport { verifyJwt } from '../utils/jwt.js';\r\n\r\n/**\r\n * AuthX Passport Strategy\r\n * Custom Passport strategy that validates JWT tokens using JWKS\r\n * and builds AuthXSession\r\n */\r\nexport class AuthXStrategy extends Strategy {\r\n name = 'authx';\r\n\r\n async authenticate(req: Request) {\r\n try {\r\n const apiKey = req.headers['x-api-key'] as string | undefined;\r\n const userId = req.headers['x-user-id'] as string | undefined;\r\n\r\n if (apiKey) {\r\n if (apiKey !== process.env.SERVER_API_KEY) {\r\n return this.fail({ message: 'Invalid API key' }, 401);\r\n }\r\n if (!userId) {\r\n return this.fail({ message: 'User Id is required' }, 401);\r\n }\r\n\r\n const user = await OrgUser.findOne({\r\n id: userId,\r\n orgId: process.env.ORG_ID || null,\r\n });\r\n\r\n if (!user) {\r\n return this.fail({ message: 'User not found' }, 401);\r\n }\r\n\r\n // Build a synthetic session\r\n const session = buildSession(user);\r\n\r\n // Attach to request\r\n (req as any).user = session;\r\n\r\n return this.success(session);\r\n } else {\r\n \r\n // Extract token from request using existing utility\r\n const token = extractToken(req);\r\n\r\n if (!token) {\r\n return this.fail({ message: 'Missing token' }, 401);\r\n }\r\n\r\n // Verify JWT asynchronously\r\n verifyJwt(token)\r\n .then((claims) => {\r\n // Build canonical session\r\n const session = buildSession(claims);\r\n\r\n // Attach to request\r\n (req as any).user = session;\r\n\r\n // Success\r\n return this.success(session);\r\n })\r\n .catch((error: any) => {\r\n // Verification failed\r\n return this.fail(\r\n { message: error?.message || 'Unauthorized' },\r\n 401,\r\n );\r\n });\r\n }\r\n } catch (error: any) {\r\n return this.fail({ message: error?.message || 'Unauthorized' }, 401);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Factory function to create AuthXStrategy instance\r\n */\r\nexport function createAuthXStrategy() {\r\n return new AuthXStrategy();\r\n}\r\n"],"mappings":"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","names":["bcrypt","jwt","mongoose","mongoose","jwt","mongoose","Schema","jwt","jwt","bcrypt","jwt","email","express","Router","Router","express","express","Router","Router","express","Router","randomUUID","mongoose","mongoose","randomUUID","Router","bcrypt","randomUUID","express","Router","mongoose","Schema","Router","express","bcrypt","randomUUID","requirePermission","randomUUID","SetMetadata"]}