myaidev-method 0.2.7 → 0.2.9

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 (163) hide show
  1. package/.claude/agents/wordpress-admin.md +271 -0
  2. package/.env.example +0 -1
  3. package/COOLIFY_DEPLOYMENT.md +1 -1
  4. package/DEV_WORKFLOW_GUIDE.md +1 -1
  5. package/PACKAGE_FIXES_SUMMARY.md +319 -0
  6. package/PAYLOADCMS_AUTH_UPDATE.md +248 -0
  7. package/PUBLISHING_GUIDE.md +1 -1
  8. package/README.md +7 -7
  9. package/USER_GUIDE.md +261 -1
  10. package/WORDPRESS_ADMIN_SCRIPTS.md +1 -1
  11. package/bin/cli.js +36 -0
  12. package/dist/server/.tsbuildinfo +1 -0
  13. package/dist/server/auth/controllers/AuthController.d.ts +34 -0
  14. package/dist/server/auth/controllers/AuthController.d.ts.map +1 -0
  15. package/dist/server/auth/controllers/AuthController.js +43 -0
  16. package/dist/server/auth/controllers/AuthController.js.map +1 -0
  17. package/dist/server/auth/example-usage.d.ts +53 -0
  18. package/dist/server/auth/example-usage.d.ts.map +1 -0
  19. package/dist/server/auth/example-usage.js +129 -0
  20. package/dist/server/auth/example-usage.js.map +1 -0
  21. package/dist/server/auth/index.d.ts +11 -0
  22. package/dist/server/auth/index.d.ts.map +1 -0
  23. package/dist/server/auth/index.js +15 -0
  24. package/dist/server/auth/index.js.map +1 -0
  25. package/dist/server/auth/layers.d.ts +19 -0
  26. package/dist/server/auth/layers.d.ts.map +1 -0
  27. package/dist/server/auth/layers.js +33 -0
  28. package/dist/server/auth/layers.js.map +1 -0
  29. package/dist/server/auth/middleware/authMiddleware.d.ts +24 -0
  30. package/dist/server/auth/middleware/authMiddleware.d.ts.map +1 -0
  31. package/dist/server/auth/middleware/authMiddleware.js +65 -0
  32. package/dist/server/auth/middleware/authMiddleware.js.map +1 -0
  33. package/dist/server/auth/routes/authRoutes.d.ts +11 -0
  34. package/dist/server/auth/routes/authRoutes.d.ts.map +1 -0
  35. package/dist/server/auth/routes/authRoutes.js +213 -0
  36. package/dist/server/auth/routes/authRoutes.js.map +1 -0
  37. package/dist/server/auth/services/AuditLogService.d.ts +21 -0
  38. package/dist/server/auth/services/AuditLogService.d.ts.map +1 -0
  39. package/dist/server/auth/services/AuditLogService.js +28 -0
  40. package/dist/server/auth/services/AuditLogService.js.map +1 -0
  41. package/dist/server/auth/services/AuthService.d.ts +27 -0
  42. package/dist/server/auth/services/AuthService.d.ts.map +1 -0
  43. package/dist/server/auth/services/AuthService.js +246 -0
  44. package/dist/server/auth/services/AuthService.js.map +1 -0
  45. package/dist/server/auth/services/PasswordService.d.ts +12 -0
  46. package/dist/server/auth/services/PasswordService.d.ts.map +1 -0
  47. package/dist/server/auth/services/PasswordService.js +31 -0
  48. package/dist/server/auth/services/PasswordService.js.map +1 -0
  49. package/dist/server/auth/services/SessionRepository.d.ts +24 -0
  50. package/dist/server/auth/services/SessionRepository.d.ts.map +1 -0
  51. package/dist/server/auth/services/SessionRepository.js +101 -0
  52. package/dist/server/auth/services/SessionRepository.js.map +1 -0
  53. package/dist/server/auth/services/TokenService.d.ts +12 -0
  54. package/dist/server/auth/services/TokenService.d.ts.map +1 -0
  55. package/dist/server/auth/services/TokenService.js +86 -0
  56. package/dist/server/auth/services/TokenService.js.map +1 -0
  57. package/dist/server/auth/services/UserRepository.d.ts +23 -0
  58. package/dist/server/auth/services/UserRepository.d.ts.map +1 -0
  59. package/dist/server/auth/services/UserRepository.js +168 -0
  60. package/dist/server/auth/services/UserRepository.js.map +1 -0
  61. package/dist/server/auth/services/example.d.ts +26 -0
  62. package/dist/server/auth/services/example.d.ts.map +1 -0
  63. package/dist/server/auth/services/example.js +221 -0
  64. package/dist/server/auth/services/example.js.map +1 -0
  65. package/dist/server/auth/services/index.d.ts +6 -0
  66. package/dist/server/auth/services/index.d.ts.map +1 -0
  67. package/dist/server/auth/services/index.js +7 -0
  68. package/dist/server/auth/services/index.js.map +1 -0
  69. package/dist/server/database/db.d.ts +28 -0
  70. package/dist/server/database/db.d.ts.map +1 -0
  71. package/dist/server/database/db.js +91 -0
  72. package/dist/server/database/db.js.map +1 -0
  73. package/dist/server/database/schema.sql +95 -0
  74. package/dist/server/hono/app.d.ts +10 -0
  75. package/dist/server/hono/app.d.ts.map +1 -0
  76. package/dist/server/hono/app.js +26 -0
  77. package/dist/server/hono/app.js.map +1 -0
  78. package/dist/server/hono/routes.d.ts +12 -0
  79. package/dist/server/hono/routes.d.ts.map +1 -0
  80. package/dist/server/hono/routes.js +40 -0
  81. package/dist/server/hono/routes.js.map +1 -0
  82. package/dist/server/main.d.ts +2 -0
  83. package/dist/server/main.d.ts.map +1 -0
  84. package/dist/server/main.js +94 -0
  85. package/dist/server/main.js.map +1 -0
  86. package/dist/server/user-management/DirectoryService.d.ts +62 -0
  87. package/dist/server/user-management/DirectoryService.d.ts.map +1 -0
  88. package/dist/server/user-management/DirectoryService.js +201 -0
  89. package/dist/server/user-management/DirectoryService.js.map +1 -0
  90. package/dist/server/user-management/LinuxUserService.d.ts +71 -0
  91. package/dist/server/user-management/LinuxUserService.d.ts.map +1 -0
  92. package/dist/server/user-management/LinuxUserService.js +192 -0
  93. package/dist/server/user-management/LinuxUserService.js.map +1 -0
  94. package/dist/server/user-management/QuotaService.d.ts +59 -0
  95. package/dist/server/user-management/QuotaService.d.ts.map +1 -0
  96. package/dist/server/user-management/QuotaService.js +148 -0
  97. package/dist/server/user-management/QuotaService.js.map +1 -0
  98. package/dist/server/user-management/UserManagementService.d.ts +74 -0
  99. package/dist/server/user-management/UserManagementService.d.ts.map +1 -0
  100. package/dist/server/user-management/UserManagementService.js +122 -0
  101. package/dist/server/user-management/UserManagementService.js.map +1 -0
  102. package/dist/server/user-management/index.d.ts +26 -0
  103. package/dist/server/user-management/index.d.ts.map +1 -0
  104. package/dist/server/user-management/index.js +26 -0
  105. package/dist/server/user-management/index.js.map +1 -0
  106. package/dist/server/user-management/layers.d.ts +27 -0
  107. package/dist/server/user-management/layers.d.ts.map +1 -0
  108. package/dist/server/user-management/layers.js +37 -0
  109. package/dist/server/user-management/layers.js.map +1 -0
  110. package/dist/shared/types.d.ts +94 -0
  111. package/dist/shared/types.d.ts.map +1 -0
  112. package/dist/shared/types.js +32 -0
  113. package/dist/shared/types.js.map +1 -0
  114. package/package.json +26 -6
  115. package/src/lib/payloadcms-utils.js +5 -12
  116. package/src/server/auth/ARCHITECTURE.md +575 -0
  117. package/src/server/auth/IMPLEMENTATION_SUMMARY.md +287 -0
  118. package/src/server/auth/QUICK_START.md +283 -0
  119. package/src/server/auth/README.md +290 -0
  120. package/src/server/auth/controllers/AuthController.ts +129 -0
  121. package/src/server/auth/example-usage.ts +159 -0
  122. package/src/server/auth/index.ts +19 -0
  123. package/src/server/auth/layers.ts +57 -0
  124. package/src/server/auth/middleware/authMiddleware.ts +118 -0
  125. package/src/server/auth/routes/authRoutes.ts +319 -0
  126. package/src/server/auth/services/AuditLogService.ts +81 -0
  127. package/src/server/auth/services/AuthService.ts +408 -0
  128. package/src/server/auth/services/IMPLEMENTATION_SUMMARY.md +404 -0
  129. package/src/server/auth/services/PasswordService.ts +85 -0
  130. package/src/server/auth/services/README.md +361 -0
  131. package/src/server/auth/services/SessionRepository.ts +227 -0
  132. package/src/server/auth/services/TokenService.ts +174 -0
  133. package/src/server/auth/services/UserRepository.ts +318 -0
  134. package/src/server/auth/services/example.ts +346 -0
  135. package/src/server/auth/services/index.ts +6 -0
  136. package/src/server/database/db.ts +161 -0
  137. package/src/server/database/schema.sql +95 -0
  138. package/src/server/hono/app.ts +41 -0
  139. package/src/server/main.ts +115 -0
  140. package/src/server/user-management/DirectoryService.ts +348 -0
  141. package/src/server/user-management/LinuxUserService.ts +338 -0
  142. package/src/server/user-management/QuotaService.ts +256 -0
  143. package/src/server/user-management/README.md +333 -0
  144. package/src/server/user-management/UserManagementService.ts +335 -0
  145. package/src/server/user-management/index.ts +26 -0
  146. package/src/server/user-management/layers.ts +51 -0
  147. package/src/shared/types.ts +111 -0
  148. package/src/templates/claude/agents/payloadcms-publish.md +34 -14
  149. package/src/templates/codex/commands/myai-astro-publish.md +8 -2
  150. package/src/templates/codex/commands/myai-content-writer.md +8 -2
  151. package/src/templates/codex/commands/myai-coolify-deploy.md +8 -2
  152. package/src/templates/codex/commands/myai-dev-architect.md +8 -2
  153. package/src/templates/codex/commands/myai-dev-code.md +8 -2
  154. package/src/templates/codex/commands/myai-dev-docs.md +8 -2
  155. package/src/templates/codex/commands/myai-dev-review.md +8 -2
  156. package/src/templates/codex/commands/myai-dev-test.md +8 -2
  157. package/src/templates/codex/commands/myai-docusaurus-publish.md +8 -2
  158. package/src/templates/codex/commands/myai-mintlify-publish.md +8 -2
  159. package/src/templates/codex/commands/myai-payloadcms-publish.md +17 -3
  160. package/src/templates/codex/commands/myai-sparc-workflow.md +8 -2
  161. package/src/templates/codex/commands/myai-wordpress-admin.md +8 -2
  162. package/src/templates/codex/commands/myai-wordpress-publish.md +8 -2
  163. package/src/templates/docs/wordpress-troubleshoot.js +2 -2
@@ -0,0 +1,118 @@
1
+ import { MiddlewareHandler } from "hono";
2
+ import { getCookie } from "hono/cookie";
3
+ import { Effect, ManagedRuntime } from "effect";
4
+ import { AuthService } from "../services/AuthService.js";
5
+ import { PasswordService } from "../services/PasswordService.js";
6
+ import { TokenService } from "../services/TokenService.js";
7
+ import { UserRepository } from "../services/UserRepository.js";
8
+ import { SessionRepository } from "../services/SessionRepository.js";
9
+ import { AuditLogService } from "../services/AuditLogService.js";
10
+ import { DatabaseService } from "../../database/db.js";
11
+ import { Session, User } from "../../../shared/types.js";
12
+
13
+ // Extend Hono Context with user and session
14
+ declare module "hono" {
15
+ interface ContextVariableMap {
16
+ user: User;
17
+ session: Session;
18
+ }
19
+ }
20
+
21
+ // Type for the application runtime context
22
+ export type AppRuntimeContext =
23
+ | PasswordService
24
+ | TokenService
25
+ | UserRepository
26
+ | SessionRepository
27
+ | AuditLogService
28
+ | AuthService
29
+ | DatabaseService;
30
+
31
+ /**
32
+ * Factory function to create auth middleware with proper Effect-TS ManagedRuntime injection
33
+ * @param runtime - The Effect-TS ManagedRuntime with all required services
34
+ * @returns Configured Hono middleware handler
35
+ */
36
+ export const createAuthMiddleware = (
37
+ runtime: ManagedRuntime.ManagedRuntime<AppRuntimeContext, never>
38
+ ): MiddlewareHandler => {
39
+ return async (c, next) => {
40
+ // Extract token from Authorization header or cookie
41
+ let token: string | undefined;
42
+
43
+ const authHeader = c.req.header("Authorization");
44
+ if (authHeader?.startsWith("Bearer ")) {
45
+ token = authHeader.substring(7);
46
+ } else {
47
+ token = getCookie(c, "auth_token");
48
+ }
49
+
50
+ if (!token) {
51
+ return c.json(
52
+ {
53
+ error: "UNAUTHORIZED",
54
+ message: "No authentication token provided",
55
+ },
56
+ 401
57
+ );
58
+ }
59
+
60
+ // Verify token using AuthService with runtime
61
+ const verifyEffect = Effect.gen(function* () {
62
+ const authService = yield* AuthService;
63
+ return yield* authService.verifyToken(token);
64
+ });
65
+
66
+ const result = await runtime.runPromiseExit(verifyEffect);
67
+
68
+ if (result._tag === "Failure") {
69
+ const cause = result.cause;
70
+
71
+ if (cause._tag === "Fail") {
72
+ const failureError = cause.error;
73
+
74
+ if (failureError && typeof failureError === "object" && "_tag" in failureError) {
75
+ const typedError = failureError as {
76
+ _tag: string;
77
+ message: string;
78
+ };
79
+
80
+ switch (typedError._tag) {
81
+ case "AuthError":
82
+ return c.json(
83
+ {
84
+ error: "AUTH_ERROR",
85
+ message: typedError.message,
86
+ },
87
+ 401
88
+ );
89
+ case "DatabaseError":
90
+ return c.json(
91
+ {
92
+ error: "INTERNAL_ERROR",
93
+ message: "Database error occurred",
94
+ },
95
+ 500
96
+ );
97
+ }
98
+ }
99
+ }
100
+
101
+ // Handle unknown errors
102
+ return c.json(
103
+ {
104
+ error: "INTERNAL_ERROR",
105
+ message: "An unexpected error occurred",
106
+ },
107
+ 500
108
+ );
109
+ }
110
+
111
+ // Success - inject user and session into context
112
+ const verifyResult = result.value;
113
+ c.set("user", verifyResult.user);
114
+ c.set("session", verifyResult.session);
115
+
116
+ return await next();
117
+ };
118
+ };
@@ -0,0 +1,319 @@
1
+ import { Hono } from "hono";
2
+ import { setCookie } from "hono/cookie";
3
+ import { Effect, ManagedRuntime } from "effect";
4
+ import { AuthService } from "../services/AuthService.js";
5
+ import { AppRuntimeContext } from "../middleware/authMiddleware.js";
6
+ import {
7
+ AuthResponse,
8
+ LoginRequest,
9
+ RegisterRequest,
10
+ } from "../../../shared/types.js";
11
+
12
+ // Cookie configuration
13
+ const COOKIE_OPTIONS = {
14
+ httpOnly: true,
15
+ secure: process.env["NODE_ENV"] === "production",
16
+ sameSite: "strict" as const,
17
+ maxAge: 7 * 24 * 60 * 60, // 7 days in seconds
18
+ path: "/",
19
+ };
20
+
21
+ /**
22
+ * Factory function to create auth routes with proper Effect-TS ManagedRuntime injection
23
+ * @param runtime - The Effect-TS ManagedRuntime with all required services
24
+ * @param authMiddleware - The configured auth middleware instance
25
+ * @returns Configured Hono router instance
26
+ */
27
+ export const createAuthRoutes = (
28
+ runtime: ManagedRuntime.ManagedRuntime<AppRuntimeContext, never>,
29
+ authMiddleware: ReturnType<typeof import("../middleware/authMiddleware.js").createAuthMiddleware>
30
+ ) => {
31
+ const authRouter = new Hono();
32
+
33
+ /**
34
+ * POST /api/auth/register
35
+ * Register a new user
36
+ */
37
+ authRouter.post("/register", async (c) => {
38
+ const body = await c.req.json<RegisterRequest>();
39
+ const { username, email, password } = body;
40
+
41
+ if (!username || !email || !password) {
42
+ return c.json(
43
+ {
44
+ error: "VALIDATION_ERROR",
45
+ message: "Username, email, and password are required",
46
+ },
47
+ 400
48
+ );
49
+ }
50
+
51
+ // Extract IP address and user agent
52
+ const ipAddress = c.req.header("x-forwarded-for") || c.req.header("x-real-ip") || null;
53
+ const userAgent = c.req.header("user-agent") || null;
54
+
55
+ // Register user
56
+ const registerEffect = Effect.gen(function* () {
57
+ const authService = yield* AuthService;
58
+ return yield* authService.register(
59
+ username,
60
+ email,
61
+ password,
62
+ ipAddress,
63
+ userAgent
64
+ );
65
+ });
66
+
67
+ const result = await runtime.runPromiseExit(registerEffect);
68
+
69
+ if (result._tag === "Failure") {
70
+ const cause = result.cause;
71
+
72
+ if (cause._tag === "Fail") {
73
+ const failureError = cause.error;
74
+
75
+ if (failureError && typeof failureError === "object" && "_tag" in failureError) {
76
+ const typedError = failureError as {
77
+ _tag: string;
78
+ message: string;
79
+ field?: string;
80
+ };
81
+
82
+ switch (typedError._tag) {
83
+ case "ValidationError":
84
+ return c.json(
85
+ {
86
+ error: "VALIDATION_ERROR",
87
+ field: typedError.field,
88
+ message: typedError.message,
89
+ },
90
+ 400
91
+ );
92
+ case "AuthError":
93
+ return c.json(
94
+ {
95
+ error: "AUTH_ERROR",
96
+ message: typedError.message,
97
+ },
98
+ 400
99
+ );
100
+ case "DatabaseError":
101
+ return c.json(
102
+ {
103
+ error: "INTERNAL_ERROR",
104
+ message: "Database error occurred",
105
+ },
106
+ 500
107
+ );
108
+ }
109
+ }
110
+ }
111
+
112
+ return c.json(
113
+ {
114
+ error: "INTERNAL_ERROR",
115
+ message: "An unexpected error occurred",
116
+ },
117
+ 500
118
+ );
119
+ }
120
+
121
+ const user = result.value;
122
+
123
+ return c.json(
124
+ {
125
+ user: {
126
+ id: user.id,
127
+ username: user.username,
128
+ email: user.email,
129
+ emailVerified: user.emailVerified,
130
+ },
131
+ },
132
+ 201
133
+ );
134
+ });
135
+
136
+ /**
137
+ * POST /api/auth/login
138
+ * Login user and create session
139
+ */
140
+ authRouter.post("/login", async (c) => {
141
+ const body = await c.req.json<LoginRequest>();
142
+ const { email, password } = body;
143
+
144
+ if (!email || !password) {
145
+ return c.json(
146
+ {
147
+ error: "VALIDATION_ERROR",
148
+ message: "Email and password are required",
149
+ },
150
+ 400
151
+ );
152
+ }
153
+
154
+ // Extract IP address and user agent
155
+ const ipAddress = c.req.header("x-forwarded-for") || c.req.header("x-real-ip") || null;
156
+ const userAgent = c.req.header("user-agent") || null;
157
+
158
+ // Login user
159
+ const loginEffect = Effect.gen(function* () {
160
+ const authService = yield* AuthService;
161
+ return yield* authService.login(email, password, ipAddress, userAgent);
162
+ });
163
+
164
+ const result = await runtime.runPromiseExit(loginEffect);
165
+
166
+ if (result._tag === "Failure") {
167
+ const cause = result.cause;
168
+
169
+ if (cause._tag === "Fail") {
170
+ const failureError = cause.error;
171
+
172
+ if (failureError && typeof failureError === "object" && "_tag" in failureError) {
173
+ const typedError = failureError as {
174
+ _tag: string;
175
+ message: string;
176
+ };
177
+
178
+ switch (typedError._tag) {
179
+ case "AuthError":
180
+ return c.json(
181
+ {
182
+ error: "AUTH_ERROR",
183
+ message: typedError.message,
184
+ },
185
+ 401
186
+ );
187
+ case "DatabaseError":
188
+ return c.json(
189
+ {
190
+ error: "INTERNAL_ERROR",
191
+ message: "Database error occurred",
192
+ },
193
+ 500
194
+ );
195
+ }
196
+ }
197
+ }
198
+
199
+ return c.json(
200
+ {
201
+ error: "INTERNAL_ERROR",
202
+ message: "An unexpected error occurred",
203
+ },
204
+ 500
205
+ );
206
+ }
207
+
208
+ const loginResult = result.value;
209
+
210
+ // Set httpOnly cookie with token
211
+ setCookie(c, "auth_token", loginResult.token, COOKIE_OPTIONS);
212
+
213
+ const response: AuthResponse = {
214
+ user: {
215
+ id: loginResult.user.id,
216
+ username: loginResult.user.username,
217
+ email: loginResult.user.email,
218
+ emailVerified: loginResult.user.emailVerified,
219
+ },
220
+ token: loginResult.token,
221
+ };
222
+
223
+ return c.json(response, 200);
224
+ });
225
+
226
+ /**
227
+ * POST /api/auth/logout
228
+ * Logout user and revoke session
229
+ * Requires authentication
230
+ */
231
+ authRouter.post("/logout", authMiddleware, async (c) => {
232
+ const user = c.get("user");
233
+ const session = c.get("session");
234
+
235
+ // Logout user
236
+ const logoutEffect = Effect.gen(function* () {
237
+ const authService = yield* AuthService;
238
+ return yield* authService.logout(session.id, user.id);
239
+ });
240
+
241
+ const result = await runtime.runPromiseExit(logoutEffect);
242
+
243
+ if (result._tag === "Failure") {
244
+ const cause = result.cause;
245
+
246
+ if (cause._tag === "Fail") {
247
+ const failureError = cause.error;
248
+
249
+ if (failureError && typeof failureError === "object" && "_tag" in failureError) {
250
+ const typedError = failureError as {
251
+ _tag: string;
252
+ message: string;
253
+ };
254
+
255
+ if (typedError._tag === "DatabaseError") {
256
+ return c.json(
257
+ {
258
+ error: "INTERNAL_ERROR",
259
+ message: "Database error occurred",
260
+ },
261
+ 500
262
+ );
263
+ }
264
+ }
265
+ }
266
+
267
+ return c.json(
268
+ {
269
+ error: "INTERNAL_ERROR",
270
+ message: "An unexpected error occurred",
271
+ },
272
+ 500
273
+ );
274
+ }
275
+
276
+ // Clear cookie
277
+ setCookie(c, "auth_token", "", {
278
+ ...COOKIE_OPTIONS,
279
+ maxAge: 0,
280
+ });
281
+
282
+ return c.json({ message: "Logged out successfully" }, 200);
283
+ });
284
+
285
+ /**
286
+ * GET /api/auth/me
287
+ * Get current authenticated user
288
+ * Requires authentication
289
+ */
290
+ authRouter.get("/me", authMiddleware, async (c) => {
291
+ try {
292
+ const user = c.get("user");
293
+
294
+ return c.json(
295
+ {
296
+ user: {
297
+ id: user.id,
298
+ username: user.username,
299
+ email: user.email,
300
+ emailVerified: user.emailVerified,
301
+ createdAt: user.createdAt,
302
+ lastLoginAt: user.lastLoginAt,
303
+ },
304
+ },
305
+ 200
306
+ );
307
+ } catch (error) {
308
+ return c.json(
309
+ {
310
+ error: "INTERNAL_ERROR",
311
+ message: "An unexpected error occurred",
312
+ },
313
+ 500
314
+ );
315
+ }
316
+ });
317
+
318
+ return authRouter;
319
+ };
@@ -0,0 +1,81 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import { randomUUID } from "node:crypto";
3
+ import { DatabaseService } from "../../database/db.js";
4
+ import { DatabaseError } from "../../../shared/types.js";
5
+
6
+ export interface CreateAuditLogData {
7
+ userId: string | null;
8
+ action: AuditAction;
9
+ resourceType?: string | null;
10
+ resourceId?: string | null;
11
+ ipAddress?: string | null;
12
+ userAgent?: string | null;
13
+ details?: string | null;
14
+ }
15
+
16
+ export type AuditAction =
17
+ | "USER_REGISTERED"
18
+ | "USER_LOGIN"
19
+ | "USER_LOGOUT"
20
+ | "LOGIN_FAILED"
21
+ | "PASSWORD_CHANGED"
22
+ | "PASSWORD_RESET_REQUESTED"
23
+ | "PASSWORD_RESET_COMPLETED"
24
+ | "EMAIL_VERIFIED"
25
+ | "EMAIL_CHANGED"
26
+ | "PROFILE_UPDATED"
27
+ | "ACCOUNT_LOCKED"
28
+ | "ACCOUNT_UNLOCKED"
29
+ | "SESSION_CREATED"
30
+ | "SESSION_REVOKED"
31
+ | "TOKEN_REFRESHED"
32
+ | "OAUTH_LINKED"
33
+ | "OAUTH_UNLINKED"
34
+ | "TWO_FACTOR_ENABLED"
35
+ | "TWO_FACTOR_DISABLED";
36
+
37
+ export class AuditLogService extends Context.Tag("AuditLogService")<
38
+ AuditLogService,
39
+ {
40
+ readonly log: (
41
+ data: CreateAuditLogData
42
+ ) => Effect.Effect<void, DatabaseError>;
43
+ }
44
+ >() {
45
+ static Live = Layer.effect(
46
+ this,
47
+ Effect.gen(function* (_) {
48
+ const db = yield* _(DatabaseService);
49
+
50
+ const log = (
51
+ data: CreateAuditLogData
52
+ ): Effect.Effect<void, DatabaseError> =>
53
+ Effect.gen(function* (_) {
54
+ const id = randomUUID();
55
+ const now = Date.now();
56
+
57
+ yield* _(
58
+ db.run(
59
+ `INSERT INTO audit_logs (
60
+ id, user_id, action, resource_type, resource_id,
61
+ ip_address, user_agent, details, created_at
62
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
63
+ [
64
+ id,
65
+ data.userId,
66
+ data.action,
67
+ data.resourceType ?? null,
68
+ data.resourceId ?? null,
69
+ data.ipAddress ?? null,
70
+ data.userAgent ?? null,
71
+ data.details ?? null,
72
+ now,
73
+ ]
74
+ )
75
+ );
76
+ });
77
+
78
+ return { log };
79
+ })
80
+ );
81
+ }