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.
- package/.claude/agents/wordpress-admin.md +271 -0
- package/.env.example +0 -1
- package/COOLIFY_DEPLOYMENT.md +1 -1
- package/DEV_WORKFLOW_GUIDE.md +1 -1
- package/PACKAGE_FIXES_SUMMARY.md +319 -0
- package/PAYLOADCMS_AUTH_UPDATE.md +248 -0
- package/PUBLISHING_GUIDE.md +1 -1
- package/README.md +7 -7
- package/USER_GUIDE.md +261 -1
- package/WORDPRESS_ADMIN_SCRIPTS.md +1 -1
- package/bin/cli.js +36 -0
- package/dist/server/.tsbuildinfo +1 -0
- package/dist/server/auth/controllers/AuthController.d.ts +34 -0
- package/dist/server/auth/controllers/AuthController.d.ts.map +1 -0
- package/dist/server/auth/controllers/AuthController.js +43 -0
- package/dist/server/auth/controllers/AuthController.js.map +1 -0
- package/dist/server/auth/example-usage.d.ts +53 -0
- package/dist/server/auth/example-usage.d.ts.map +1 -0
- package/dist/server/auth/example-usage.js +129 -0
- package/dist/server/auth/example-usage.js.map +1 -0
- package/dist/server/auth/index.d.ts +11 -0
- package/dist/server/auth/index.d.ts.map +1 -0
- package/dist/server/auth/index.js +15 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/layers.d.ts +19 -0
- package/dist/server/auth/layers.d.ts.map +1 -0
- package/dist/server/auth/layers.js +33 -0
- package/dist/server/auth/layers.js.map +1 -0
- package/dist/server/auth/middleware/authMiddleware.d.ts +24 -0
- package/dist/server/auth/middleware/authMiddleware.d.ts.map +1 -0
- package/dist/server/auth/middleware/authMiddleware.js +65 -0
- package/dist/server/auth/middleware/authMiddleware.js.map +1 -0
- package/dist/server/auth/routes/authRoutes.d.ts +11 -0
- package/dist/server/auth/routes/authRoutes.d.ts.map +1 -0
- package/dist/server/auth/routes/authRoutes.js +213 -0
- package/dist/server/auth/routes/authRoutes.js.map +1 -0
- package/dist/server/auth/services/AuditLogService.d.ts +21 -0
- package/dist/server/auth/services/AuditLogService.d.ts.map +1 -0
- package/dist/server/auth/services/AuditLogService.js +28 -0
- package/dist/server/auth/services/AuditLogService.js.map +1 -0
- package/dist/server/auth/services/AuthService.d.ts +27 -0
- package/dist/server/auth/services/AuthService.d.ts.map +1 -0
- package/dist/server/auth/services/AuthService.js +246 -0
- package/dist/server/auth/services/AuthService.js.map +1 -0
- package/dist/server/auth/services/PasswordService.d.ts +12 -0
- package/dist/server/auth/services/PasswordService.d.ts.map +1 -0
- package/dist/server/auth/services/PasswordService.js +31 -0
- package/dist/server/auth/services/PasswordService.js.map +1 -0
- package/dist/server/auth/services/SessionRepository.d.ts +24 -0
- package/dist/server/auth/services/SessionRepository.d.ts.map +1 -0
- package/dist/server/auth/services/SessionRepository.js +101 -0
- package/dist/server/auth/services/SessionRepository.js.map +1 -0
- package/dist/server/auth/services/TokenService.d.ts +12 -0
- package/dist/server/auth/services/TokenService.d.ts.map +1 -0
- package/dist/server/auth/services/TokenService.js +86 -0
- package/dist/server/auth/services/TokenService.js.map +1 -0
- package/dist/server/auth/services/UserRepository.d.ts +23 -0
- package/dist/server/auth/services/UserRepository.d.ts.map +1 -0
- package/dist/server/auth/services/UserRepository.js +168 -0
- package/dist/server/auth/services/UserRepository.js.map +1 -0
- package/dist/server/auth/services/example.d.ts +26 -0
- package/dist/server/auth/services/example.d.ts.map +1 -0
- package/dist/server/auth/services/example.js +221 -0
- package/dist/server/auth/services/example.js.map +1 -0
- package/dist/server/auth/services/index.d.ts +6 -0
- package/dist/server/auth/services/index.d.ts.map +1 -0
- package/dist/server/auth/services/index.js +7 -0
- package/dist/server/auth/services/index.js.map +1 -0
- package/dist/server/database/db.d.ts +28 -0
- package/dist/server/database/db.d.ts.map +1 -0
- package/dist/server/database/db.js +91 -0
- package/dist/server/database/db.js.map +1 -0
- package/dist/server/database/schema.sql +95 -0
- package/dist/server/hono/app.d.ts +10 -0
- package/dist/server/hono/app.d.ts.map +1 -0
- package/dist/server/hono/app.js +26 -0
- package/dist/server/hono/app.js.map +1 -0
- package/dist/server/hono/routes.d.ts +12 -0
- package/dist/server/hono/routes.d.ts.map +1 -0
- package/dist/server/hono/routes.js +40 -0
- package/dist/server/hono/routes.js.map +1 -0
- package/dist/server/main.d.ts +2 -0
- package/dist/server/main.d.ts.map +1 -0
- package/dist/server/main.js +94 -0
- package/dist/server/main.js.map +1 -0
- package/dist/server/user-management/DirectoryService.d.ts +62 -0
- package/dist/server/user-management/DirectoryService.d.ts.map +1 -0
- package/dist/server/user-management/DirectoryService.js +201 -0
- package/dist/server/user-management/DirectoryService.js.map +1 -0
- package/dist/server/user-management/LinuxUserService.d.ts +71 -0
- package/dist/server/user-management/LinuxUserService.d.ts.map +1 -0
- package/dist/server/user-management/LinuxUserService.js +192 -0
- package/dist/server/user-management/LinuxUserService.js.map +1 -0
- package/dist/server/user-management/QuotaService.d.ts +59 -0
- package/dist/server/user-management/QuotaService.d.ts.map +1 -0
- package/dist/server/user-management/QuotaService.js +148 -0
- package/dist/server/user-management/QuotaService.js.map +1 -0
- package/dist/server/user-management/UserManagementService.d.ts +74 -0
- package/dist/server/user-management/UserManagementService.d.ts.map +1 -0
- package/dist/server/user-management/UserManagementService.js +122 -0
- package/dist/server/user-management/UserManagementService.js.map +1 -0
- package/dist/server/user-management/index.d.ts +26 -0
- package/dist/server/user-management/index.d.ts.map +1 -0
- package/dist/server/user-management/index.js +26 -0
- package/dist/server/user-management/index.js.map +1 -0
- package/dist/server/user-management/layers.d.ts +27 -0
- package/dist/server/user-management/layers.d.ts.map +1 -0
- package/dist/server/user-management/layers.js +37 -0
- package/dist/server/user-management/layers.js.map +1 -0
- package/dist/shared/types.d.ts +94 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +32 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +26 -6
- package/src/lib/payloadcms-utils.js +5 -12
- package/src/server/auth/ARCHITECTURE.md +575 -0
- package/src/server/auth/IMPLEMENTATION_SUMMARY.md +287 -0
- package/src/server/auth/QUICK_START.md +283 -0
- package/src/server/auth/README.md +290 -0
- package/src/server/auth/controllers/AuthController.ts +129 -0
- package/src/server/auth/example-usage.ts +159 -0
- package/src/server/auth/index.ts +19 -0
- package/src/server/auth/layers.ts +57 -0
- package/src/server/auth/middleware/authMiddleware.ts +118 -0
- package/src/server/auth/routes/authRoutes.ts +319 -0
- package/src/server/auth/services/AuditLogService.ts +81 -0
- package/src/server/auth/services/AuthService.ts +408 -0
- package/src/server/auth/services/IMPLEMENTATION_SUMMARY.md +404 -0
- package/src/server/auth/services/PasswordService.ts +85 -0
- package/src/server/auth/services/README.md +361 -0
- package/src/server/auth/services/SessionRepository.ts +227 -0
- package/src/server/auth/services/TokenService.ts +174 -0
- package/src/server/auth/services/UserRepository.ts +318 -0
- package/src/server/auth/services/example.ts +346 -0
- package/src/server/auth/services/index.ts +6 -0
- package/src/server/database/db.ts +161 -0
- package/src/server/database/schema.sql +95 -0
- package/src/server/hono/app.ts +41 -0
- package/src/server/main.ts +115 -0
- package/src/server/user-management/DirectoryService.ts +348 -0
- package/src/server/user-management/LinuxUserService.ts +338 -0
- package/src/server/user-management/QuotaService.ts +256 -0
- package/src/server/user-management/README.md +333 -0
- package/src/server/user-management/UserManagementService.ts +335 -0
- package/src/server/user-management/index.ts +26 -0
- package/src/server/user-management/layers.ts +51 -0
- package/src/shared/types.ts +111 -0
- package/src/templates/claude/agents/payloadcms-publish.md +34 -14
- package/src/templates/codex/commands/myai-astro-publish.md +8 -2
- package/src/templates/codex/commands/myai-content-writer.md +8 -2
- package/src/templates/codex/commands/myai-coolify-deploy.md +8 -2
- package/src/templates/codex/commands/myai-dev-architect.md +8 -2
- package/src/templates/codex/commands/myai-dev-code.md +8 -2
- package/src/templates/codex/commands/myai-dev-docs.md +8 -2
- package/src/templates/codex/commands/myai-dev-review.md +8 -2
- package/src/templates/codex/commands/myai-dev-test.md +8 -2
- package/src/templates/codex/commands/myai-docusaurus-publish.md +8 -2
- package/src/templates/codex/commands/myai-mintlify-publish.md +8 -2
- package/src/templates/codex/commands/myai-payloadcms-publish.md +17 -3
- package/src/templates/codex/commands/myai-sparc-workflow.md +8 -2
- package/src/templates/codex/commands/myai-wordpress-admin.md +8 -2
- package/src/templates/codex/commands/myai-wordpress-publish.md +8 -2
- 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
|
+
}
|