@boopkit/cap-users 0.1.0

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.
Files changed (58) hide show
  1. package/capability.json +50 -0
  2. package/dist/account-management-service.d.ts +52 -0
  3. package/dist/account-management-service.d.ts.map +1 -0
  4. package/dist/account-management-service.js +140 -0
  5. package/dist/account-management-service.js.map +1 -0
  6. package/dist/account-property-service.d.ts +18 -0
  7. package/dist/account-property-service.d.ts.map +1 -0
  8. package/dist/account-property-service.js +19 -0
  9. package/dist/account-property-service.js.map +1 -0
  10. package/dist/account-service.d.ts +84 -0
  11. package/dist/account-service.d.ts.map +1 -0
  12. package/dist/account-service.js +136 -0
  13. package/dist/account-service.js.map +1 -0
  14. package/dist/auth-middleware.d.ts +50 -0
  15. package/dist/auth-middleware.d.ts.map +1 -0
  16. package/dist/auth-middleware.js +149 -0
  17. package/dist/auth-middleware.js.map +1 -0
  18. package/dist/auth-service.d.ts +112 -0
  19. package/dist/auth-service.d.ts.map +1 -0
  20. package/dist/auth-service.js +575 -0
  21. package/dist/auth-service.js.map +1 -0
  22. package/dist/identity-service.d.ts +48 -0
  23. package/dist/identity-service.d.ts.map +1 -0
  24. package/dist/identity-service.js +98 -0
  25. package/dist/identity-service.js.map +1 -0
  26. package/dist/index.d.ts +27 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +40 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/team-invitation-service.d.ts +61 -0
  31. package/dist/team-invitation-service.d.ts.map +1 -0
  32. package/dist/team-invitation-service.js +237 -0
  33. package/dist/team-invitation-service.js.map +1 -0
  34. package/dist/types.d.ts +92 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +24 -0
  37. package/dist/types.js.map +1 -0
  38. package/dist/user-property-service.d.ts +18 -0
  39. package/dist/user-property-service.d.ts.map +1 -0
  40. package/dist/user-property-service.js +19 -0
  41. package/dist/user-property-service.js.map +1 -0
  42. package/dist/user-service.d.ts +47 -0
  43. package/dist/user-service.d.ts.map +1 -0
  44. package/dist/user-service.js +39 -0
  45. package/dist/user-service.js.map +1 -0
  46. package/package.json +31 -0
  47. package/scaffold/prisma/schema/accounts.prisma +47 -0
  48. package/scaffold/prisma/schema/users.prisma +47 -0
  49. package/scaffold/reference/user-auth.ref.md +237 -0
  50. package/scaffold/src/controllers/auth.ts +158 -0
  51. package/scaffold/src/middleware/auth.ts +28 -0
  52. package/scaffold/src/routes/auth.ts +20 -0
  53. package/scaffold/src/services/accounts.service.ts +53 -0
  54. package/scaffold/src/services/users.service.ts +46 -0
  55. package/scaffold/styles/auth.css +120 -0
  56. package/scaffold/views/login.hbs +28 -0
  57. package/scaffold/views/partials/auth-nav.hbs +32 -0
  58. package/scaffold/views/signup.hbs +29 -0
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "users-and-accounts",
3
+ "description": "Users, identities, properties, and authentication via Prisma",
4
+ "type": "application",
5
+ "requires": ["database", "services", "web"],
6
+ "package": "@boopkit/cap-users",
7
+ "dependencies": ["bcryptjs", "cookie-parser"],
8
+ "devDependencies": [],
9
+ "files": [
10
+ "prisma/schema/users.prisma",
11
+ "prisma/schema/accounts.prisma",
12
+ "src/services/users.service.ts",
13
+ "src/services/accounts.service.ts",
14
+ "src/middleware/auth.ts",
15
+ "src/controllers/auth.ts",
16
+ "src/routes/auth.ts",
17
+ "views/signup.hbs",
18
+ "views/login.hbs",
19
+ "views/partials/auth-nav.hbs",
20
+ "styles/auth.css",
21
+ "reference/user-auth.ref.md"
22
+ ],
23
+ "skills": [],
24
+ "hooks": [
25
+ { "phase": "imports", "code": "import cookieParser from 'cookie-parser';", "description": "cookie parsing for session tokens" },
26
+ { "phase": "imports", "code": "import UserService from './services/users.service';", "description": "user service wiring" },
27
+ { "phase": "imports", "code": "import { ensureVisitor, addAuthContext } from './middleware/auth';", "description": "auth middleware — session resolution and trust context" },
28
+ { "phase": "imports", "code": "import authRoutes from './routes/auth';", "description": "auth route definitions" },
29
+ { "phase": "imports", "code": "import AccountService from './services/accounts.service';", "description": "account service wiring" },
30
+ { "phase": "preRoutes", "order": 14, "code": "app.use(cookieParser());", "description": "parse cookies (required for auth session resolution)" },
31
+ { "phase": "preRoutes", "order": 15, "code": "app.use(ensureVisitor);", "description": "resolve visitor identity from auth_id cookie" },
32
+ { "phase": "preRoutes", "order": 16, "code": "app.use(addAuthContext);", "description": "populate res.locals.auth for view templates" },
33
+ { "phase": "preRoutes", "order": 52, "code": "app.use(authRoutes({ authService: UserService.authService }));", "description": "mount auth routes (signup, login, logout)" }
34
+ ],
35
+ "env": [
36
+ { "name": "SESSION_DURATION", "description": "Session token lifetime in seconds (default: 7 days)", "default": "604800", "required": "runtime" },
37
+ { "name": "INVITATION_TTL", "description": "Team invitation expiry in milliseconds (default: 7 days)", "default": "604800000", "required": "runtime" }
38
+ ],
39
+ "postInstall": {
40
+ "steps": [
41
+ "Run database migration to create user and account tables: npm run db:migrate -- --name add-${capabilityName}",
42
+ "Generate updated Prisma client: npm run db:generate",
43
+ "Add {{> auth-nav}} to your main.hbs layout — inside <body> before your main content."
44
+ ],
45
+ "setupChecks": [
46
+ { "type": "prisma-migrate", "label": "Run database migrations", "hint": "npm run db:migrate -- --name add-${capabilityName}" },
47
+ { "type": "prisma-generate", "label": "Generate Prisma client", "hint": "npm run db:generate" }
48
+ ]
49
+ }
50
+ }
@@ -0,0 +1,52 @@
1
+ import type { AccountService } from './account-service.js';
2
+ import type { UserService } from './user-service.js';
3
+ import type { AccountPropertyService } from './account-property-service.js';
4
+ import type { IdentityService } from './identity-service.js';
5
+ import type { BaseAccountInfo, BaseAccountUserInfo, BaseUserInfo, Account, AccountUser, User } from './types.js';
6
+ export declare class AccountManagementError extends Error {
7
+ constructor(message: string);
8
+ }
9
+ export interface AccountManagementServiceDeps {
10
+ accountService: AccountService<BaseAccountInfo>;
11
+ userService: UserService<BaseUserInfo>;
12
+ accountPropertyService: AccountPropertyService;
13
+ identityService: IdentityService;
14
+ }
15
+ export interface AccountManagementServiceOptions {
16
+ onAccountCreated?(account: Account<BaseAccountInfo>, owner: User<BaseUserInfo>): void;
17
+ onUserAdded?(params: {
18
+ accountId: number;
19
+ userId: number;
20
+ role: string;
21
+ }): void;
22
+ }
23
+ export interface ProvisionAccountInput {
24
+ name: string;
25
+ }
26
+ export interface ProvisionAccountResult {
27
+ account: Account<BaseAccountInfo>;
28
+ membership: AccountUser;
29
+ }
30
+ export interface TeamMember {
31
+ userId: number;
32
+ name?: string;
33
+ email?: string;
34
+ role: string;
35
+ status: string;
36
+ }
37
+ export interface AccountManagementService {
38
+ validateAccountInput(input: ProvisionAccountInput): void;
39
+ createAccount(input: ProvisionAccountInput): Promise<Account<BaseAccountInfo>>;
40
+ linkOwner(accountId: number, userId: number): Promise<AccountUser>;
41
+ provisionAccount(userId: number, input: ProvisionAccountInput): Promise<ProvisionAccountResult>;
42
+ addUser(accountId: number, userId: number, role?: BaseAccountUserInfo['role'], status?: BaseAccountUserInfo['status']): Promise<AccountUser>;
43
+ activateUser(accountId: number, userId: number): Promise<AccountUser>;
44
+ changeRole(accountId: number, userId: number, newRole: BaseAccountUserInfo['role']): Promise<AccountUser>;
45
+ removeUser(accountId: number, userId: number): Promise<void>;
46
+ disableAccount(accountId: number): Promise<Account<BaseAccountInfo>>;
47
+ getUserAccounts(userId: number): Promise<Account<BaseAccountInfo>[]>;
48
+ hasRole(accountId: number, userId: number, role: BaseAccountUserInfo['role']): boolean | Promise<boolean>;
49
+ getTeamMembers(accountId: number): Promise<TeamMember[]>;
50
+ }
51
+ export declare function createAccountManagementService(deps: AccountManagementServiceDeps, options?: AccountManagementServiceOptions): AccountManagementService;
52
+ //# sourceMappingURL=account-management-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-management-service.d.ts","sourceRoot":"","sources":["../src/account-management-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AccountManagementError = void 0;
4
+ exports.createAccountManagementService = createAccountManagementService;
5
+ // ---------- Errors ----------
6
+ class AccountManagementError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = 'AccountManagementError';
10
+ }
11
+ }
12
+ exports.AccountManagementError = AccountManagementError;
13
+ // ---------- Role hierarchy ----------
14
+ const ROLE_ORDER = ['viewer', 'editor', 'admin', 'owner'];
15
+ function roleLevel(role) {
16
+ return ROLE_ORDER.indexOf(role);
17
+ }
18
+ // ---------- Validation ----------
19
+ const MAX_ACCOUNT_NAME_LENGTH = 100;
20
+ // ---------- Factory ----------
21
+ function createAccountManagementService(deps, options) {
22
+ const { accountService, userService, identityService } = deps;
23
+ // ---- Decomposed steps ----
24
+ function validateAccountInput(input) {
25
+ if (!input.name || input.name.trim().length === 0) {
26
+ throw new AccountManagementError('Account name is required');
27
+ }
28
+ if (input.name.length > MAX_ACCOUNT_NAME_LENGTH) {
29
+ throw new AccountManagementError(`Account name must be ${MAX_ACCOUNT_NAME_LENGTH} characters or fewer`);
30
+ }
31
+ }
32
+ async function createAccount(input) {
33
+ return accountService.create({ name: input.name.trim(), status: 'enabled' });
34
+ }
35
+ async function linkOwner(accountId, userId) {
36
+ return accountService.addUser(accountId, userId, { role: 'owner', status: 'enabled' });
37
+ }
38
+ // ---- Convenience ----
39
+ async function provisionAccount(userId, input) {
40
+ validateAccountInput(input);
41
+ const account = await createAccount(input);
42
+ const membership = await linkOwner(account.id, userId);
43
+ const user = await userService.findById(userId);
44
+ if (user) {
45
+ options?.onAccountCreated?.(account, user);
46
+ }
47
+ return { account, membership };
48
+ }
49
+ // ---- Membership management ----
50
+ async function addUser(accountId, userId, role = 'viewer', status = 'enabled') {
51
+ const au = await accountService.addUser(accountId, userId, { role, status });
52
+ options?.onUserAdded?.({ accountId, userId, role });
53
+ return au;
54
+ }
55
+ async function activateUser(accountId, userId) {
56
+ const users = await accountService.getUsers(accountId);
57
+ const member = users.find((u) => u.userId === userId);
58
+ if (!member) {
59
+ throw new AccountManagementError(`User ${userId} is not a member of account ${accountId}`);
60
+ }
61
+ if (member.info.status !== 'pending') {
62
+ throw new AccountManagementError(`User ${userId} is not in pending status (current: ${member.info.status})`);
63
+ }
64
+ return accountService.updateUser(accountId, userId, { role: member.info.role, status: 'enabled' });
65
+ }
66
+ async function changeRole(accountId, userId, newRole) {
67
+ const users = await accountService.getUsers(accountId);
68
+ const member = users.find((u) => u.userId === userId);
69
+ if (!member) {
70
+ throw new AccountManagementError(`User ${userId} is not a member of account ${accountId}`);
71
+ }
72
+ return accountService.updateUser(accountId, userId, { role: newRole, status: member.info.status });
73
+ }
74
+ async function removeUser(accountId, userId) {
75
+ const users = await accountService.getUsers(accountId);
76
+ const member = users.find((u) => u.userId === userId);
77
+ if (member && member.info.role === 'owner') {
78
+ const otherOwners = users.filter((u) => u.userId !== userId && u.info.role === 'owner');
79
+ if (otherOwners.length === 0) {
80
+ throw new AccountManagementError('Cannot remove the last owner of an account');
81
+ }
82
+ }
83
+ return accountService.removeUser(accountId, userId);
84
+ }
85
+ // ---- Account state ----
86
+ async function disableAccount(accountId) {
87
+ const account = await accountService.findById(accountId);
88
+ if (!account) {
89
+ throw new AccountManagementError(`Account ${accountId} not found`);
90
+ }
91
+ return accountService.put(accountId, { ...account.info, status: 'disabled' });
92
+ }
93
+ // ---- Queries ----
94
+ async function getUserAccounts(userId) {
95
+ return accountService.findAccountsByUser(userId);
96
+ }
97
+ async function hasRole(accountId, userId, role) {
98
+ const userRole = await accountService.getUserRole(accountId, userId);
99
+ if (!userRole)
100
+ return false;
101
+ return roleLevel(userRole) >= roleLevel(role);
102
+ }
103
+ async function getTeamMembers(accountId) {
104
+ const accountUsers = await accountService.getUsers(accountId);
105
+ const members = [];
106
+ for (const au of accountUsers) {
107
+ const user = await userService.findById(au.userId);
108
+ let email;
109
+ // Resolve email from identity service
110
+ const identities = await identityService.findAllByUser(au.userId);
111
+ const emailIdentity = identities.find((i) => i.domain === 'email');
112
+ if (emailIdentity) {
113
+ email = emailIdentity.key;
114
+ }
115
+ members.push({
116
+ userId: au.userId,
117
+ name: user?.info.name,
118
+ email,
119
+ role: au.info.role,
120
+ status: au.info.status,
121
+ });
122
+ }
123
+ return members;
124
+ }
125
+ return {
126
+ validateAccountInput,
127
+ createAccount,
128
+ linkOwner,
129
+ provisionAccount,
130
+ addUser,
131
+ activateUser,
132
+ changeRole,
133
+ removeUser,
134
+ disableAccount,
135
+ getUserAccounts,
136
+ hasRole,
137
+ getTeamMembers,
138
+ };
139
+ }
140
+ //# sourceMappingURL=account-management-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-management-service.js","sourceRoot":"","sources":["../src/account-management-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { createPropertyService, type PropertyService, type PropertyRecord } from '@boopkit/cap-services';
2
+ /** Options for creating an account property service. */
3
+ export interface AccountPropertyServiceOptions {
4
+ onCreated?(record: PropertyRecord): void;
5
+ onUpdated?(record: PropertyRecord): void;
6
+ onRemoved?(record: PropertyRecord): void;
7
+ }
8
+ /** The service object returned by createAccountPropertyService. */
9
+ export type AccountPropertyService = PropertyService;
10
+ /**
11
+ * Create a property service pre-configured for account properties.
12
+ *
13
+ * Thin wrapper over createPropertyService with:
14
+ * - parentIdField set to 'accountId'
15
+ * - sensitiveProperties set to ['keys']
16
+ */
17
+ export declare function createAccountPropertyService(model: Parameters<typeof createPropertyService>[0], options?: AccountPropertyServiceOptions): AccountPropertyService;
18
+ //# sourceMappingURL=account-property-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-property-service.d.ts","sourceRoot":"","sources":["../src/account-property-service.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,uBAAuB,CAAC;AAE/B,wDAAwD;AACxD,MAAM,WAAW,6BAA6B;IAC5C,SAAS,CAAC,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,SAAS,CAAC,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,SAAS,CAAC,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;CAC1C;AAED,mEAAmE;AACnE,MAAM,MAAM,sBAAsB,GAAG,eAAe,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC,EAClD,OAAO,CAAC,EAAE,6BAA6B,GACtC,sBAAsB,CAMxB"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAccountPropertyService = createAccountPropertyService;
4
+ const cap_services_1 = require("@boopkit/cap-services");
5
+ /**
6
+ * Create a property service pre-configured for account properties.
7
+ *
8
+ * Thin wrapper over createPropertyService with:
9
+ * - parentIdField set to 'accountId'
10
+ * - sensitiveProperties set to ['keys']
11
+ */
12
+ function createAccountPropertyService(model, options) {
13
+ return (0, cap_services_1.createPropertyService)(model, {
14
+ parentIdField: 'accountId',
15
+ sensitiveProperties: ['keys'],
16
+ ...options,
17
+ });
18
+ }
19
+ //# sourceMappingURL=account-property-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-property-service.js","sourceRoot":"","sources":["../src/account-property-service.ts"],"names":[],"mappings":";;AAuBA,oEASC;AAhCD,wDAI+B;AAY/B;;;;;;GAMG;AACH,SAAgB,4BAA4B,CAC1C,KAAkD,EAClD,OAAuC;IAEvC,OAAO,IAAA,oCAAqB,EAAC,KAAK,EAAE;QAClC,aAAa,EAAE,WAAW;QAC1B,mBAAmB,EAAE,CAAC,MAAM,CAAC;QAC7B,GAAG,OAAO;KACX,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,84 @@
1
+ import type { EntityService } from '@boopkit/cap-services';
2
+ import type { BaseAccountInfo, BaseAccountUserInfo, Account, AccountUser } from './types.js';
3
+ /** Lifecycle callbacks for account mutations. */
4
+ export interface AccountServiceOptions<TInfo extends BaseAccountInfo> {
5
+ onCreated?(account: Account<TInfo>): void;
6
+ onUpdated?(account: Account<TInfo>): void;
7
+ onRemoved?(account: Account<TInfo>): void;
8
+ onUserAdded?(accountUser: AccountUser): void;
9
+ onUserUpdated?(accountUser: AccountUser): void;
10
+ onUserRemoved?(accountUser: AccountUser): void;
11
+ }
12
+ /** The service object returned by createAccountService. */
13
+ export interface AccountService<TInfo extends BaseAccountInfo> extends EntityService<TInfo> {
14
+ findByPublicId(publicId: string): Promise<Account<TInfo> | null>;
15
+ findByName(name: string): Promise<Account<TInfo> | null>;
16
+ getUsers(accountId: number): Promise<AccountUser[]>;
17
+ addUser(accountId: number, userId: number, info?: BaseAccountUserInfo): Promise<AccountUser>;
18
+ updateUser(accountId: number, userId: number, info: BaseAccountUserInfo): Promise<AccountUser>;
19
+ removeUser(accountId: number, userId: number): Promise<void>;
20
+ getUserRole(accountId: number, userId: number): Promise<string | null>;
21
+ findAccountsByUser(userId: number): Promise<Account<TInfo>[]>;
22
+ }
23
+ /**
24
+ * Minimal delegate shape for the accounts table.
25
+ * Extends the entity ModelDelegate with publicId lookups and JSON field queries.
26
+ */
27
+ interface AccountModelDelegate {
28
+ create(args: {
29
+ data: Record<string, unknown>;
30
+ }): Promise<Record<string, unknown>>;
31
+ findUnique(args: {
32
+ where: Record<string, unknown>;
33
+ }): Promise<Record<string, unknown> | null>;
34
+ findMany(args?: {
35
+ skip?: number;
36
+ take?: number;
37
+ where?: Record<string, unknown>;
38
+ }): Promise<Record<string, unknown>[]>;
39
+ update(args: {
40
+ where: {
41
+ id: number;
42
+ };
43
+ data: Record<string, unknown>;
44
+ }): Promise<Record<string, unknown>>;
45
+ delete(args: {
46
+ where: {
47
+ id: number;
48
+ };
49
+ }): Promise<Record<string, unknown>>;
50
+ }
51
+ /**
52
+ * Minimal delegate shape for the account_users join table.
53
+ */
54
+ interface AccountUserModelDelegate {
55
+ create(args: {
56
+ data: Record<string, unknown>;
57
+ }): Promise<Record<string, unknown>>;
58
+ findUnique(args: {
59
+ where: Record<string, unknown>;
60
+ }): Promise<Record<string, unknown> | null>;
61
+ findMany(args: {
62
+ where: Record<string, unknown>;
63
+ }): Promise<Record<string, unknown>[]>;
64
+ update(args: {
65
+ where: {
66
+ id: number;
67
+ };
68
+ data: Record<string, unknown>;
69
+ }): Promise<Record<string, unknown>>;
70
+ delete(args: {
71
+ where: {
72
+ id: number;
73
+ };
74
+ }): Promise<Record<string, unknown>>;
75
+ }
76
+ /**
77
+ * Create a typed account service composing on top of createEntityService.
78
+ *
79
+ * Inherits CRUD from entity-service and adds account-specific methods:
80
+ * findByPublicId, findByName, getUsers, addUser, removeUser, getUserRole.
81
+ */
82
+ export declare function createAccountService<TInfo extends BaseAccountInfo = BaseAccountInfo>(accountModel: AccountModelDelegate, accountUserModel: AccountUserModelDelegate, options?: AccountServiceOptions<TInfo>): AccountService<TInfo>;
83
+ export {};
84
+ //# sourceMappingURL=account-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-service.d.ts","sourceRoot":"","sources":["../src/account-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAccountService = createAccountService;
4
+ const cap_services_1 = require("@boopkit/cap-services");
5
+ const types_js_1 = require("./types.js");
6
+ /**
7
+ * Map a raw delegate row to a typed Account entity.
8
+ */
9
+ function toAccount(row) {
10
+ return {
11
+ id: row.id,
12
+ publicId: row.publicId,
13
+ info: row.info,
14
+ infoSchema: row.infoSchema,
15
+ revision: row.revision,
16
+ createdAt: row.createdAt instanceof Date ? row.createdAt : new Date(row.createdAt),
17
+ updatedAt: row.updatedAt instanceof Date ? row.updatedAt : new Date(row.updatedAt),
18
+ };
19
+ }
20
+ /**
21
+ * Map a raw delegate row to a typed AccountUser.
22
+ */
23
+ function toAccountUser(row) {
24
+ return {
25
+ id: row.id,
26
+ accountId: row.accountId,
27
+ userId: row.userId,
28
+ info: row.info,
29
+ createdAt: row.createdAt instanceof Date ? row.createdAt : new Date(row.createdAt),
30
+ updatedAt: row.updatedAt instanceof Date ? row.updatedAt : new Date(row.updatedAt),
31
+ };
32
+ }
33
+ /**
34
+ * Create a typed account service composing on top of createEntityService.
35
+ *
36
+ * Inherits CRUD from entity-service and adds account-specific methods:
37
+ * findByPublicId, findByName, getUsers, addUser, removeUser, getUserRole.
38
+ */
39
+ function createAccountService(accountModel, accountUserModel, options) {
40
+ const base = (0, cap_services_1.createEntityService)(accountModel, {
41
+ infoSchema: 'account-01',
42
+ onCreated: options?.onCreated,
43
+ onUpdated: options?.onUpdated,
44
+ onRemoved: options?.onRemoved,
45
+ });
46
+ return {
47
+ ...base,
48
+ async findByPublicId(publicId) {
49
+ const row = await accountModel.findUnique({ where: { publicId } });
50
+ return row ? toAccount(row) : null;
51
+ },
52
+ async findByName(name) {
53
+ // Query accounts and find one whose info.name matches.
54
+ // Uses findMany with JSON field filtering if the delegate supports it,
55
+ // otherwise falls back to client-side filtering.
56
+ const rows = await accountModel.findMany();
57
+ const match = rows.find((row) => {
58
+ const info = row.info;
59
+ return info && info.name === name;
60
+ });
61
+ return match ? toAccount(match) : null;
62
+ },
63
+ async getUsers(accountId) {
64
+ const rows = await accountUserModel.findMany({ where: { accountId } });
65
+ return rows.map((row) => toAccountUser(row));
66
+ },
67
+ async addUser(accountId, userId, info) {
68
+ // Check for duplicate
69
+ const existing = await accountUserModel.findUnique({
70
+ where: { accountId_userId: { accountId, userId } },
71
+ });
72
+ if (existing) {
73
+ throw new types_js_1.DuplicateAccountUserError(accountId, userId);
74
+ }
75
+ const now = new Date();
76
+ const row = await accountUserModel.create({
77
+ data: {
78
+ accountId,
79
+ userId,
80
+ info: (info ?? { status: 'enabled', role: 'viewer' }),
81
+ createdAt: now,
82
+ updatedAt: now,
83
+ },
84
+ });
85
+ const accountUser = toAccountUser(row);
86
+ options?.onUserAdded?.(accountUser);
87
+ return accountUser;
88
+ },
89
+ async updateUser(accountId, userId, info) {
90
+ const existing = await accountUserModel.findUnique({
91
+ where: { accountId_userId: { accountId, userId } },
92
+ });
93
+ if (!existing) {
94
+ throw new Error(`User ${userId} is not a member of account ${accountId}`);
95
+ }
96
+ const now = new Date();
97
+ const row = await accountUserModel.update({
98
+ where: { id: existing.id },
99
+ data: { info: info, updatedAt: now },
100
+ });
101
+ const accountUser = toAccountUser(row);
102
+ options?.onUserUpdated?.(accountUser);
103
+ return accountUser;
104
+ },
105
+ async removeUser(accountId, userId) {
106
+ const existing = await accountUserModel.findUnique({
107
+ where: { accountId_userId: { accountId, userId } },
108
+ });
109
+ if (!existing) {
110
+ throw new Error(`User ${userId} is not a member of account ${accountId}`);
111
+ }
112
+ await accountUserModel.delete({ where: { id: existing.id } });
113
+ options?.onUserRemoved?.(toAccountUser(existing));
114
+ },
115
+ async findAccountsByUser(userId) {
116
+ const rows = await accountUserModel.findMany({ where: { userId } });
117
+ const accounts = [];
118
+ for (const row of rows) {
119
+ const accountRow = await accountModel.findUnique({ where: { id: row.accountId } });
120
+ if (accountRow)
121
+ accounts.push(toAccount(accountRow));
122
+ }
123
+ return accounts;
124
+ },
125
+ async getUserRole(accountId, userId) {
126
+ const existing = await accountUserModel.findUnique({
127
+ where: { accountId_userId: { accountId, userId } },
128
+ });
129
+ if (!existing)
130
+ return null;
131
+ const info = existing.info;
132
+ return info ? info.role : null;
133
+ },
134
+ };
135
+ }
136
+ //# sourceMappingURL=account-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-service.js","sourceRoot":"","sources":["../src/account-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Auth middleware factories for Express.
3
+ *
4
+ * Provides three middleware functions:
5
+ * - `ensureVisitor` — resolves session from auth_id cookie, creates anonymous visitor if missing
6
+ * - `requireAuth` — gates routes by trust tier, redirects web / 401s API
7
+ * - `addAuthContext` — populates res.locals.auth for view templates
8
+ */
9
+ import type { Request, Response, NextFunction } from 'express';
10
+ import type { AuthService } from './auth-service.js';
11
+ import type { IdentityService } from './identity-service.js';
12
+ import type { UserService } from './user-service.js';
13
+ import type { BaseUserInfo, TrustTier } from './types.js';
14
+ /** Dependencies injected into createAuthMiddleware. */
15
+ export interface AuthMiddlewareDeps {
16
+ authService: AuthService;
17
+ identityService: IdentityService;
18
+ userService: UserService<BaseUserInfo>;
19
+ }
20
+ /** Options for createAuthMiddleware. */
21
+ export interface AuthMiddlewareOptions {
22
+ /** Cookie name for session token. Default: 'auth_id' */
23
+ cookieName?: string;
24
+ /** Cookie max-age in milliseconds. Default: 365 days */
25
+ cookieMaxAge?: number;
26
+ /** Cookie name for visitor tracking. Default: 'visitor_id' */
27
+ visitorCookieName?: string;
28
+ /** Visitor cookie max-age in milliseconds. Default: 10 years */
29
+ visitorCookieMaxAge?: number;
30
+ /** Login path for redirects. Default: '/login' */
31
+ loginPath?: string;
32
+ }
33
+ /** Shape of res.locals.auth populated by addAuthContext. */
34
+ export interface AuthLocals {
35
+ trustTier: TrustTier | null;
36
+ userName: string | null;
37
+ userEmail: string | null;
38
+ userInitial: string | null;
39
+ }
40
+ /** The middleware functions returned by createAuthMiddleware. */
41
+ export interface AuthMiddleware {
42
+ ensureVisitor: (req: Request, res: Response, next: NextFunction) => void;
43
+ requireAuth: (requiredTrust?: TrustTier) => (req: Request, res: Response, next: NextFunction) => void;
44
+ addAuthContext: (req: Request, res: Response, next: NextFunction) => void;
45
+ }
46
+ /**
47
+ * Create auth middleware functions wired to the auth, identity, and user services.
48
+ */
49
+ export declare function createAuthMiddleware(deps: AuthMiddlewareDeps, options?: AuthMiddlewareOptions): AuthMiddleware;
50
+ //# sourceMappingURL=auth-middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-middleware.d.ts","sourceRoot":"","sources":["../src/auth-middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAQ,MAAM,YAAY,CAAC;AAIhE,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,WAAW,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;CACxC;AAED,wCAAwC;AACxC,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,iEAAiE;AACjE,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACzE,WAAW,EAAE,CAAC,aAAa,CAAC,EAAE,SAAS,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACtG,cAAc,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CAC3E;AASD;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,kBAAkB,EACxB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAsJhB"}