create-kuckit-app 0.3.5 → 0.4.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.
- package/dist/bin.js +1 -1
- package/dist/{create-project-CP-h4Ygi.js → create-project-geQBZ0Ru.js} +5 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/templates/base/.claude/skills/kuckit/SKILL.md +22 -2
- package/templates/base/.claude/skills/kuckit/references/MODULE-DEVELOPMENT.md +39 -28
- package/templates/base/.claude/skills/kuckit/references/PACKAGES.md +94 -74
- package/templates/base/AGENTS.md +44 -18
- package/templates/base/apps/server/AGENTS.md +35 -10
- package/templates/base/apps/server/package.json +7 -7
- package/templates/base/apps/server/src/auth.ts +1 -1
- package/templates/base/apps/server/src/container.ts +3 -1
- package/templates/base/apps/server/src/module-rest-routes.ts +47 -0
- package/templates/base/apps/server/src/rest-router-registry.ts +32 -0
- package/templates/base/apps/server/src/rpc.ts +1 -1
- package/templates/base/apps/server/src/server.ts +2 -0
- package/templates/base/apps/web/package.json +9 -9
- package/templates/base/apps/web/src/services/auth-client.ts +1 -1
- package/templates/base/{packages/db/src/migrations → drizzle}/0000_init.sql +31 -36
- package/templates/base/{packages/db/src/migrations → drizzle}/meta/_journal.json +1 -1
- package/templates/base/drizzle.config.ts +38 -0
- package/templates/base/package.json +14 -9
- package/templates/base/packages/items-module/package.json +7 -7
- package/templates/base/packages/items-module/src/api/items.router.ts +1 -1
- package/templates/base/packages/api/AGENTS.md +0 -66
- package/templates/base/packages/api/package.json +0 -35
- package/templates/base/packages/api/src/context.ts +0 -48
- package/templates/base/packages/api/src/index.ts +0 -22
- package/templates/base/packages/api/tsconfig.json +0 -8
- package/templates/base/packages/auth/AGENTS.md +0 -61
- package/templates/base/packages/auth/package.json +0 -27
- package/templates/base/packages/auth/src/index.ts +0 -22
- package/templates/base/packages/auth/tsconfig.json +0 -8
- package/templates/base/packages/db/AGENTS.md +0 -99
- package/templates/base/packages/db/drizzle.config.ts +0 -23
- package/templates/base/packages/db/package.json +0 -36
- package/templates/base/packages/db/src/connection.ts +0 -40
- package/templates/base/packages/db/src/index.ts +0 -4
- package/templates/base/packages/db/src/schema/auth.ts +0 -51
- package/templates/base/packages/db/tsconfig.json +0 -8
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Router } from 'express'
|
|
2
|
+
import type { ApiRegistration } from '@kuckit/sdk'
|
|
3
|
+
|
|
4
|
+
interface RestRouterEntry {
|
|
5
|
+
name: string
|
|
6
|
+
router: Router
|
|
7
|
+
basePath: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const restRouters: RestRouterEntry[] = []
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Collect REST routers from module registrations.
|
|
14
|
+
* Called from the SDK loader's onApiRegistrations callback.
|
|
15
|
+
*/
|
|
16
|
+
export const collectModuleRestRouters = (registrations: ApiRegistration[]): void => {
|
|
17
|
+
for (const reg of registrations) {
|
|
18
|
+
if (reg.type !== 'rest-router') continue
|
|
19
|
+
|
|
20
|
+
const basePath = reg.prefix ?? `/${reg.name}`
|
|
21
|
+
restRouters.push({
|
|
22
|
+
name: reg.name,
|
|
23
|
+
router: reg.router as Router,
|
|
24
|
+
basePath,
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get all collected REST routers for wiring to Express app.
|
|
31
|
+
*/
|
|
32
|
+
export const getRestRouters = (): readonly RestRouterEntry[] => restRouters
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RPCHandler } from '@orpc/server/node'
|
|
2
2
|
import { onError } from '@orpc/server'
|
|
3
|
-
import { createContext } from '@
|
|
3
|
+
import { createContext } from '@kuckit/api/context'
|
|
4
4
|
import type { Express } from 'express'
|
|
5
5
|
import { rootRpcRouter } from './rpc-router-registry'
|
|
6
6
|
|
|
@@ -3,6 +3,7 @@ import { createApp } from './app'
|
|
|
3
3
|
import { setupContainerMiddleware, getRootContainer } from './middleware/container'
|
|
4
4
|
import { setupAuth } from './auth'
|
|
5
5
|
import { setupRPC } from './rpc'
|
|
6
|
+
import { setupModuleRestRouters } from './module-rest-routes'
|
|
6
7
|
import { setupHealth } from './health'
|
|
7
8
|
import { disposeContainer } from './container'
|
|
8
9
|
|
|
@@ -17,6 +18,7 @@ const bootstrap = async () => {
|
|
|
17
18
|
|
|
18
19
|
setupAuth(app)
|
|
19
20
|
setupRPC(app)
|
|
21
|
+
setupModuleRestRouters(app)
|
|
20
22
|
setupHealth(app, rootContainer)
|
|
21
23
|
|
|
22
24
|
const port = process.env.PORT || 3000
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"check-types": "tsc --noEmit"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@kuckit/sdk-react": "^
|
|
12
|
+
"@kuckit/sdk-react": "^2.0.0",
|
|
13
|
+
"@kuckit/auth": "^2.0.0",
|
|
13
14
|
"@orpc/client": "^1.10.0",
|
|
14
15
|
"@orpc/tanstack-query": "^1.10.0",
|
|
15
16
|
"@radix-ui/react-avatar": "^1.1.10",
|
|
@@ -19,17 +20,16 @@
|
|
|
19
20
|
"@radix-ui/react-separator": "^1.1.7",
|
|
20
21
|
"@radix-ui/react-slot": "^1.2.3",
|
|
21
22
|
"@radix-ui/react-tooltip": "^1.2.7",
|
|
22
|
-
"@tanstack/react-query": "^5.
|
|
23
|
-
"@tanstack/react-router": "^1.114.
|
|
24
|
-
"better-auth": "^1.3.
|
|
23
|
+
"@tanstack/react-query": "^5.0.0",
|
|
24
|
+
"@tanstack/react-router": "^1.114.0",
|
|
25
|
+
"better-auth": "^1.3.0",
|
|
25
26
|
"class-variance-authority": "^0.7.1",
|
|
26
27
|
"clsx": "^2.1.1",
|
|
27
28
|
"lucide-react": "^0.511.0",
|
|
28
|
-
"react": "^19.
|
|
29
|
-
"react-dom": "^19.
|
|
30
|
-
"tailwind-merge": "^3.
|
|
31
|
-
"zod": "^
|
|
32
|
-
"@__APP_NAME_KEBAB__/auth": "workspace:*"
|
|
29
|
+
"react": "^19.0.0",
|
|
30
|
+
"react-dom": "^19.0.0",
|
|
31
|
+
"tailwind-merge": "^3.0.0",
|
|
32
|
+
"zod": "^3.23.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@tailwindcss/vite": "^4.0.15",
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
-- This migration is generated by drizzle-kit
|
|
3
|
-
-- Run: bun run db:migrate
|
|
4
|
-
|
|
5
|
-
CREATE TABLE IF NOT EXISTS "user" (
|
|
1
|
+
CREATE TABLE "account" (
|
|
6
2
|
"id" text PRIMARY KEY NOT NULL,
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
3
|
+
"account_id" text NOT NULL,
|
|
4
|
+
"provider_id" text NOT NULL,
|
|
5
|
+
"user_id" text NOT NULL,
|
|
6
|
+
"access_token" text,
|
|
7
|
+
"refresh_token" text,
|
|
8
|
+
"id_token" text,
|
|
9
|
+
"access_token_expires_at" timestamp,
|
|
10
|
+
"refresh_token_expires_at" timestamp,
|
|
11
|
+
"scope" text,
|
|
12
|
+
"password" text,
|
|
11
13
|
"created_at" timestamp NOT NULL,
|
|
12
|
-
"updated_at" timestamp NOT NULL
|
|
13
|
-
CONSTRAINT "user_email_unique" UNIQUE("email")
|
|
14
|
+
"updated_at" timestamp NOT NULL
|
|
14
15
|
);
|
|
15
|
-
|
|
16
|
-
CREATE TABLE
|
|
16
|
+
--> statement-breakpoint
|
|
17
|
+
CREATE TABLE "session" (
|
|
17
18
|
"id" text PRIMARY KEY NOT NULL,
|
|
18
19
|
"expires_at" timestamp NOT NULL,
|
|
19
20
|
"token" text NOT NULL,
|
|
@@ -24,24 +25,19 @@ CREATE TABLE IF NOT EXISTS "session" (
|
|
|
24
25
|
"user_id" text NOT NULL,
|
|
25
26
|
CONSTRAINT "session_token_unique" UNIQUE("token")
|
|
26
27
|
);
|
|
27
|
-
|
|
28
|
-
CREATE TABLE
|
|
28
|
+
--> statement-breakpoint
|
|
29
|
+
CREATE TABLE "user" (
|
|
29
30
|
"id" text PRIMARY KEY NOT NULL,
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"refresh_token" text,
|
|
35
|
-
"id_token" text,
|
|
36
|
-
"access_token_expires_at" timestamp,
|
|
37
|
-
"refresh_token_expires_at" timestamp,
|
|
38
|
-
"scope" text,
|
|
39
|
-
"password" text,
|
|
31
|
+
"name" text NOT NULL,
|
|
32
|
+
"email" text NOT NULL,
|
|
33
|
+
"email_verified" boolean NOT NULL,
|
|
34
|
+
"image" text,
|
|
40
35
|
"created_at" timestamp NOT NULL,
|
|
41
|
-
"updated_at" timestamp NOT NULL
|
|
36
|
+
"updated_at" timestamp NOT NULL,
|
|
37
|
+
CONSTRAINT "user_email_unique" UNIQUE("email")
|
|
42
38
|
);
|
|
43
|
-
|
|
44
|
-
CREATE TABLE
|
|
39
|
+
--> statement-breakpoint
|
|
40
|
+
CREATE TABLE "verification" (
|
|
45
41
|
"id" text PRIMARY KEY NOT NULL,
|
|
46
42
|
"identifier" text NOT NULL,
|
|
47
43
|
"value" text NOT NULL,
|
|
@@ -49,16 +45,15 @@ CREATE TABLE IF NOT EXISTS "verification" (
|
|
|
49
45
|
"created_at" timestamp,
|
|
50
46
|
"updated_at" timestamp
|
|
51
47
|
);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
|
|
55
|
-
|
|
56
|
-
-- Items module table
|
|
57
|
-
CREATE TABLE IF NOT EXISTS "items" (
|
|
48
|
+
--> statement-breakpoint
|
|
49
|
+
CREATE TABLE "items" (
|
|
58
50
|
"id" text PRIMARY KEY NOT NULL,
|
|
59
51
|
"name" text NOT NULL,
|
|
60
52
|
"description" text,
|
|
61
|
-
"created_at" timestamp
|
|
62
|
-
"updated_at" timestamp
|
|
53
|
+
"created_at" timestamp DEFAULT now() NOT NULL,
|
|
54
|
+
"updated_at" timestamp DEFAULT now() NOT NULL,
|
|
63
55
|
"user_id" text NOT NULL
|
|
64
56
|
);
|
|
57
|
+
--> statement-breakpoint
|
|
58
|
+
ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
|
59
|
+
ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineConfig } from 'drizzle-kit'
|
|
2
|
+
import dotenv from 'dotenv'
|
|
3
|
+
import { dirname, resolve } from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
|
|
6
|
+
const currentFilePath = fileURLToPath(import.meta.url)
|
|
7
|
+
const currentDirPath = dirname(currentFilePath)
|
|
8
|
+
|
|
9
|
+
dotenv.config({
|
|
10
|
+
path: resolve(currentDirPath, './apps/server/.env'),
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
function buildDatabaseUrl(): string {
|
|
14
|
+
if (process.env.DATABASE_URL) {
|
|
15
|
+
return process.env.DATABASE_URL
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { DB_HOST, DB_USER, DB_PASSWORD, DB_NAME } = process.env
|
|
19
|
+
if (DB_HOST && DB_USER && DB_PASSWORD && DB_NAME) {
|
|
20
|
+
return `postgresql://${DB_USER}:${encodeURIComponent(DB_PASSWORD)}@/${DB_NAME}?host=${DB_HOST}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
throw new Error(
|
|
24
|
+
'Missing database configuration: provide DATABASE_URL or DB_HOST, DB_USER, DB_PASSWORD, DB_NAME'
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default defineConfig({
|
|
29
|
+
schema: [
|
|
30
|
+
// Auth schema from @kuckit/db (resolved from node_modules)
|
|
31
|
+
'./node_modules/@kuckit/db/dist/schema/auth.js',
|
|
32
|
+
// Local module schemas
|
|
33
|
+
resolve(currentDirPath, './packages/items-module/src/adapters'),
|
|
34
|
+
],
|
|
35
|
+
out: './drizzle',
|
|
36
|
+
dialect: 'postgresql',
|
|
37
|
+
dbCredentials: { url: buildDatabaseUrl() },
|
|
38
|
+
})
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
"lint:fix": "eslint apps packages --fix",
|
|
15
15
|
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
|
|
16
16
|
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,yml,yaml}\"",
|
|
17
|
-
"db:
|
|
18
|
-
"db:
|
|
19
|
-
"db:
|
|
17
|
+
"db:push": "drizzle-kit push",
|
|
18
|
+
"db:generate": "drizzle-kit generate",
|
|
19
|
+
"db:migrate": "drizzle-kit migrate",
|
|
20
|
+
"db:studio": "drizzle-kit studio",
|
|
20
21
|
"prepare": "husky"
|
|
21
22
|
},
|
|
22
23
|
"lint-staged": {
|
|
@@ -29,21 +30,25 @@
|
|
|
29
30
|
]
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@kuckit/sdk": "^
|
|
33
|
+
"@kuckit/sdk": "^2.0.0",
|
|
34
|
+
"@kuckit/api": "^2.0.0",
|
|
35
|
+
"@kuckit/auth": "^2.0.0",
|
|
36
|
+
"@kuckit/db": "^2.0.0",
|
|
33
37
|
"awilix": "^12.0.5",
|
|
34
|
-
"dotenv": "^17.
|
|
35
|
-
"zod": "^
|
|
38
|
+
"dotenv": "^17.0.0",
|
|
39
|
+
"zod": "^3.23.0"
|
|
36
40
|
},
|
|
37
41
|
"devDependencies": {
|
|
38
42
|
"@eslint/js": "^9.17.0",
|
|
43
|
+
"drizzle-kit": "^0.31.0",
|
|
39
44
|
"eslint": "^9.17.0",
|
|
40
45
|
"eslint-config-prettier": "^10.0.1",
|
|
41
46
|
"husky": "^9.1.7",
|
|
42
47
|
"lint-staged": "^16.0.0",
|
|
43
48
|
"prettier": "^3.4.2",
|
|
44
|
-
"turbo": "^2.5.
|
|
45
|
-
"typescript": "^5.8.
|
|
46
|
-
"typescript-eslint": "^8.18.
|
|
49
|
+
"turbo": "^2.5.0",
|
|
50
|
+
"typescript": "^5.8.0",
|
|
51
|
+
"typescript-eslint": "^8.18.0"
|
|
47
52
|
},
|
|
48
53
|
"packageManager": "bun@1.2.21"
|
|
49
54
|
}
|
|
@@ -23,14 +23,14 @@
|
|
|
23
23
|
"typescript": "^5"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@kuckit/sdk": "^
|
|
27
|
-
"@kuckit/sdk-react": "^
|
|
26
|
+
"@kuckit/sdk": "^2.0.0",
|
|
27
|
+
"@kuckit/sdk-react": "^2.0.0",
|
|
28
|
+
"@kuckit/api": "^2.0.0",
|
|
28
29
|
"@orpc/server": "^1.10.0",
|
|
29
30
|
"@orpc/zod": "^1.10.0",
|
|
30
|
-
"drizzle-orm": "^0.44.
|
|
31
|
-
"zod": "^
|
|
32
|
-
"react": "^19.
|
|
33
|
-
"@
|
|
34
|
-
"@__APP_NAME_KEBAB__/db": "workspace:*"
|
|
31
|
+
"drizzle-orm": "^0.44.0",
|
|
32
|
+
"zod": "^3.23.0",
|
|
33
|
+
"react": "^19.0.0",
|
|
34
|
+
"@tanstack/react-query": "^5.0.0"
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# AGENTS.md - API Package
|
|
2
|
-
|
|
3
|
-
> See root [AGENTS.md](../../AGENTS.md) for SDK Module System patterns
|
|
4
|
-
|
|
5
|
-
## Purpose
|
|
6
|
-
|
|
7
|
-
Shared API context, types, and procedure definitions for oRPC.
|
|
8
|
-
|
|
9
|
-
## Key Files
|
|
10
|
-
|
|
11
|
-
| File | Purpose |
|
|
12
|
-
| ------------ | -------------------------------- |
|
|
13
|
-
| `context.ts` | Request context type definitions |
|
|
14
|
-
| `index.ts` | Public exports |
|
|
15
|
-
|
|
16
|
-
## Procedure Types
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
import { publicProcedure, protectedProcedure } from '@__APP_NAME_KEBAB__/api'
|
|
20
|
-
|
|
21
|
-
// Public - no authentication required
|
|
22
|
-
export const healthRouter = {
|
|
23
|
-
ping: publicProcedure.handler(() => 'pong'),
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Protected - requires authenticated user
|
|
27
|
-
export const itemsRouter = {
|
|
28
|
-
list: protectedProcedure.handler(async ({ context }) => {
|
|
29
|
-
const userId = context.session?.user?.id
|
|
30
|
-
// ...
|
|
31
|
-
}),
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Context Structure
|
|
36
|
-
|
|
37
|
-
The API context provides:
|
|
38
|
-
|
|
39
|
-
- `di` - Scoped DI container for the request
|
|
40
|
-
- `session` - Current user session (when using `protectedProcedure`)
|
|
41
|
-
|
|
42
|
-
## Typing DI Access in Routers
|
|
43
|
-
|
|
44
|
-
Use a module-local interface to type `context.di.cradle`:
|
|
45
|
-
|
|
46
|
-
```typescript
|
|
47
|
-
// In your module's router file
|
|
48
|
-
interface ItemsCradle {
|
|
49
|
-
itemRepository: ItemRepository
|
|
50
|
-
createItem: (input: CreateItemInput) => Promise<Item>
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export const itemsRouter = {
|
|
54
|
-
create: protectedProcedure.input(createItemSchema).handler(async ({ input, context }) => {
|
|
55
|
-
const { createItem } = context.di.cradle as ItemsCradle
|
|
56
|
-
return createItem(input)
|
|
57
|
-
}),
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
**Why module-local interfaces:**
|
|
62
|
-
|
|
63
|
-
- Type assertion stays within the module
|
|
64
|
-
- Module remains self-contained
|
|
65
|
-
- Server app doesn't need to know about module internals
|
|
66
|
-
- Scales to any number of modules without server changes
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@__APP_NAME_KEBAB__/api",
|
|
3
|
-
"version": "0.0.1",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "src/index.ts",
|
|
7
|
-
"types": "src/index.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./src/index.ts",
|
|
11
|
-
"default": "./src/index.ts"
|
|
12
|
-
},
|
|
13
|
-
"./*": {
|
|
14
|
-
"types": "./src/*.ts",
|
|
15
|
-
"default": "./src/*.ts"
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"@types/express": "^5.0.1"
|
|
20
|
-
},
|
|
21
|
-
"peerDependencies": {
|
|
22
|
-
"typescript": "^5"
|
|
23
|
-
},
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"@orpc/server": "^1.10.0",
|
|
26
|
-
"@orpc/client": "^1.10.0",
|
|
27
|
-
"@orpc/zod": "^1.10.0",
|
|
28
|
-
"better-auth": "^1.3.28",
|
|
29
|
-
"dotenv": "^17.2.2",
|
|
30
|
-
"zod": "^4.1.11",
|
|
31
|
-
"awilix": "^12.0.5",
|
|
32
|
-
"@__APP_NAME_KEBAB__/auth": "workspace:*",
|
|
33
|
-
"@__APP_NAME_KEBAB__/db": "workspace:*"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { Request } from 'express'
|
|
2
|
-
import { fromNodeHeaders } from 'better-auth/node'
|
|
3
|
-
import { auth } from '@__APP_NAME_KEBAB__/auth'
|
|
4
|
-
import { asValue, type AwilixContainer } from 'awilix'
|
|
5
|
-
|
|
6
|
-
declare global {
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
8
|
-
namespace Express {
|
|
9
|
-
interface Request {
|
|
10
|
-
scope?: AwilixContainer
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface CreateContextOptions {
|
|
16
|
-
req: Request
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Create per-request context with DI container
|
|
21
|
-
*/
|
|
22
|
-
export async function createContext(opts: CreateContextOptions) {
|
|
23
|
-
let session
|
|
24
|
-
try {
|
|
25
|
-
session = await auth.api.getSession({
|
|
26
|
-
headers: fromNodeHeaders(opts.req.headers),
|
|
27
|
-
})
|
|
28
|
-
} catch (error) {
|
|
29
|
-
console.error('[Auth] Failed to get session:', error)
|
|
30
|
-
session = null
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const requestId = crypto.randomUUID()
|
|
34
|
-
|
|
35
|
-
if (opts.req.scope) {
|
|
36
|
-
opts.req.scope.register({
|
|
37
|
-
session: asValue(session),
|
|
38
|
-
requestId: asValue(requestId),
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
session,
|
|
44
|
-
di: opts.req.scope!,
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export type Context = Awaited<ReturnType<typeof createContext>>
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { ORPCError, os } from '@orpc/server'
|
|
2
|
-
import type { Context } from './context'
|
|
3
|
-
|
|
4
|
-
export const o = os.$context<Context>()
|
|
5
|
-
|
|
6
|
-
export const publicProcedure = o
|
|
7
|
-
|
|
8
|
-
const requireAuth = o.middleware(async ({ context, next }) => {
|
|
9
|
-
if (!context.session?.user) {
|
|
10
|
-
throw new ORPCError('UNAUTHORIZED')
|
|
11
|
-
}
|
|
12
|
-
return next({
|
|
13
|
-
context: {
|
|
14
|
-
session: context.session,
|
|
15
|
-
},
|
|
16
|
-
})
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
export const protectedProcedure = publicProcedure.use(requireAuth)
|
|
20
|
-
|
|
21
|
-
export { createContext } from './context'
|
|
22
|
-
export type { Context } from './context'
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
# AGENTS.md - Auth Package
|
|
2
|
-
|
|
3
|
-
> See root [AGENTS.md](../../AGENTS.md) for SDK Module System patterns
|
|
4
|
-
|
|
5
|
-
## Purpose
|
|
6
|
-
|
|
7
|
-
Better-Auth configuration shared between server and web apps.
|
|
8
|
-
|
|
9
|
-
## Key Files
|
|
10
|
-
|
|
11
|
-
| File | Purpose |
|
|
12
|
-
| ---------- | -------------------------------------- |
|
|
13
|
-
| `index.ts` | Auth configuration and client creation |
|
|
14
|
-
|
|
15
|
-
## Server Usage
|
|
16
|
-
|
|
17
|
-
```typescript
|
|
18
|
-
import { auth } from '@__APP_NAME_KEBAB__/auth'
|
|
19
|
-
|
|
20
|
-
// In Express
|
|
21
|
-
app.all('/api/auth/*', auth.handler)
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Client Usage
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
import { authClient } from '@__APP_NAME_KEBAB__/auth'
|
|
28
|
-
|
|
29
|
-
// Sign in
|
|
30
|
-
await authClient.signIn.email({ email, password })
|
|
31
|
-
|
|
32
|
-
// Sign out
|
|
33
|
-
await authClient.signOut()
|
|
34
|
-
|
|
35
|
-
// Get session
|
|
36
|
-
const session = await authClient.getSession()
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Session in oRPC Context
|
|
40
|
-
|
|
41
|
-
The current user session is available in `protectedProcedure` handlers:
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
import { protectedProcedure } from '@__APP_NAME_KEBAB__/api'
|
|
45
|
-
|
|
46
|
-
export const myRouter = {
|
|
47
|
-
getProfile: protectedProcedure.handler(async ({ context }) => {
|
|
48
|
-
const userId = context.session?.user?.id
|
|
49
|
-
const userEmail = context.session?.user?.email
|
|
50
|
-
// ... use session data
|
|
51
|
-
}),
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Configuration
|
|
56
|
-
|
|
57
|
-
Auth requires these environment variables:
|
|
58
|
-
|
|
59
|
-
- `BETTER_AUTH_SECRET` - Session encryption key
|
|
60
|
-
- `BETTER_AUTH_URL` - Callback URL for OAuth
|
|
61
|
-
- `DATABASE_URL` - For session storage
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@__APP_NAME_KEBAB__/auth",
|
|
3
|
-
"version": "0.0.1",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "src/index.ts",
|
|
7
|
-
"types": "src/index.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./src/index.ts",
|
|
11
|
-
"default": "./src/index.ts"
|
|
12
|
-
},
|
|
13
|
-
"./*": {
|
|
14
|
-
"types": "./src/*.ts",
|
|
15
|
-
"default": "./src/*.ts"
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
"peerDependencies": {
|
|
19
|
-
"typescript": "^5"
|
|
20
|
-
},
|
|
21
|
-
"dependencies": {
|
|
22
|
-
"better-auth": "^1.3.28",
|
|
23
|
-
"dotenv": "^17.2.2",
|
|
24
|
-
"zod": "^4.1.11",
|
|
25
|
-
"@__APP_NAME_KEBAB__/db": "workspace:*"
|
|
26
|
-
}
|
|
27
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { betterAuth, type BetterAuthOptions } from 'better-auth'
|
|
2
|
-
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
|
|
3
|
-
import { db } from '@__APP_NAME_KEBAB__/db'
|
|
4
|
-
import * as schema from '@__APP_NAME_KEBAB__/db/schema/auth'
|
|
5
|
-
|
|
6
|
-
export const auth = betterAuth<BetterAuthOptions>({
|
|
7
|
-
database: drizzleAdapter(db, {
|
|
8
|
-
provider: 'pg',
|
|
9
|
-
schema: schema,
|
|
10
|
-
}),
|
|
11
|
-
trustedOrigins: [process.env.CORS_ORIGIN || ''],
|
|
12
|
-
emailAndPassword: {
|
|
13
|
-
enabled: true,
|
|
14
|
-
},
|
|
15
|
-
advanced: {
|
|
16
|
-
defaultCookieAttributes: {
|
|
17
|
-
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax',
|
|
18
|
-
secure: process.env.NODE_ENV === 'production',
|
|
19
|
-
httpOnly: true,
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
})
|