clawfire 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.
- package/README.md +182 -0
- package/dist/admin.cjs +309 -0
- package/dist/admin.cjs.map +1 -0
- package/dist/admin.d.cts +93 -0
- package/dist/admin.d.ts +93 -0
- package/dist/admin.js +274 -0
- package/dist/admin.js.map +1 -0
- package/dist/auth-DQ3cifhb.d.cts +55 -0
- package/dist/auth-DtnUPbXT.d.ts +55 -0
- package/dist/chunk-37Y2XI7X.js +75 -0
- package/dist/chunk-YGIPORYL.js +339 -0
- package/dist/cli.js +241 -0
- package/dist/client.cjs +97 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +4 -0
- package/dist/client.d.ts +4 -0
- package/dist/client.js +68 -0
- package/dist/client.js.map +1 -0
- package/dist/codegen.cjs +648 -0
- package/dist/codegen.cjs.map +1 -0
- package/dist/codegen.d.cts +25 -0
- package/dist/codegen.d.ts +25 -0
- package/dist/codegen.js +617 -0
- package/dist/codegen.js.map +1 -0
- package/dist/config-QMBJRn9G.d.cts +46 -0
- package/dist/config-QMBJRn9G.d.ts +46 -0
- package/dist/dev-server-QAVWINAT.js +973 -0
- package/dist/dev.cjs +1388 -0
- package/dist/dev.cjs.map +1 -0
- package/dist/dev.d.cts +111 -0
- package/dist/dev.d.ts +111 -0
- package/dist/dev.js +1349 -0
- package/dist/dev.js.map +1 -0
- package/dist/discover-BPMAZFBD.js +9 -0
- package/dist/discover-DYNqz_ym.d.cts +28 -0
- package/dist/discover-DYNqz_ym.d.ts +28 -0
- package/dist/errors-s_mP7rs9.d.cts +33 -0
- package/dist/errors-s_mP7rs9.d.ts +33 -0
- package/dist/functions.cjs +1156 -0
- package/dist/functions.cjs.map +1 -0
- package/dist/functions.d.cts +115 -0
- package/dist/functions.d.ts +115 -0
- package/dist/functions.js +1108 -0
- package/dist/functions.js.map +1 -0
- package/dist/hosting-7WVFHAYJ.js +85 -0
- package/dist/html-PCUCJGBH.js +7 -0
- package/dist/index.cjs +349 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +312 -0
- package/dist/index.js.map +1 -0
- package/dist/playground.cjs +364 -0
- package/dist/playground.cjs.map +1 -0
- package/dist/playground.d.cts +12 -0
- package/dist/playground.d.ts +12 -0
- package/dist/playground.js +337 -0
- package/dist/playground.js.map +1 -0
- package/dist/router-BVB_I-tu.d.ts +65 -0
- package/dist/router-Cikk8Heq.d.cts +65 -0
- package/dist/schema-BJsictSV.d.cts +172 -0
- package/dist/schema-BJsictSV.d.ts +172 -0
- package/package.json +150 -0
- package/templates/CLAUDE.md +71 -0
- package/templates/app/routes/auth/login.ts +35 -0
- package/templates/app/routes/health.ts +20 -0
- package/templates/app/schemas/user.ts +26 -0
- package/templates/clawfire.config.ts +25 -0
- package/templates/functions/index.ts +43 -0
- package/templates/starter/.claude/skills/clawfire-api/SKILL.md +131 -0
- package/templates/starter/.claude/skills/clawfire-auth/SKILL.md +111 -0
- package/templates/starter/.claude/skills/clawfire-deploy/SKILL.md +95 -0
- package/templates/starter/.claude/skills/clawfire-diagnose/SKILL.md +99 -0
- package/templates/starter/.claude/skills/clawfire-model/SKILL.md +128 -0
- package/templates/starter/CLAUDE.md +227 -0
- package/templates/starter/app/routes/health.ts +20 -0
- package/templates/starter/app/routes/todos/create.ts +25 -0
- package/templates/starter/app/routes/todos/delete.ts +20 -0
- package/templates/starter/app/routes/todos/list.ts +26 -0
- package/templates/starter/app/routes/todos/update.ts +32 -0
- package/templates/starter/app/schemas/todo.ts +16 -0
- package/templates/starter/app/store.ts +56 -0
- package/templates/starter/clawfire.config.ts +25 -0
- package/templates/starter/dev.ts +12 -0
- package/templates/starter/package.json +19 -0
- package/templates/starter/public/index.html +365 -0
- package/templates/starter/tsconfig.json +17 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clawfire Firebase Functions Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This is the main entry point for your Firebase Functions.
|
|
5
|
+
* Routes are automatically registered from app/routes/.
|
|
6
|
+
*/
|
|
7
|
+
import * as admin from "firebase-admin";
|
|
8
|
+
import * as functions from "firebase-functions";
|
|
9
|
+
import {
|
|
10
|
+
createRouter,
|
|
11
|
+
createAdminDB,
|
|
12
|
+
createSecurityMiddleware,
|
|
13
|
+
} from "clawfire/functions";
|
|
14
|
+
|
|
15
|
+
// Initialize Firebase Admin
|
|
16
|
+
admin.initializeApp();
|
|
17
|
+
|
|
18
|
+
// Create database wrapper
|
|
19
|
+
export const db = createAdminDB(admin.firestore());
|
|
20
|
+
|
|
21
|
+
// Create router with security middleware
|
|
22
|
+
const router = createRouter({
|
|
23
|
+
auth: admin.auth(),
|
|
24
|
+
cors: [], // Add your allowed origins here
|
|
25
|
+
middleware: createSecurityMiddleware({
|
|
26
|
+
logRequests: true,
|
|
27
|
+
sanitize: true,
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// ── Register Routes ─────────────────────────────────────────────
|
|
32
|
+
// Routes are registered here. Use /clawfire-api skill to auto-manage this.
|
|
33
|
+
|
|
34
|
+
import healthRoute from "../app/routes/health";
|
|
35
|
+
router.register("/health", healthRoute);
|
|
36
|
+
|
|
37
|
+
import loginRoute from "../app/routes/auth/login";
|
|
38
|
+
router.register("/auth/login", loginRoute);
|
|
39
|
+
|
|
40
|
+
// ── Export Cloud Function ────────────────────────────────────────
|
|
41
|
+
export const api = functions.https.onRequest((req, res) => {
|
|
42
|
+
router.handleRequest(req as any, res as any);
|
|
43
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clawfire-api
|
|
3
|
+
description: API 생성/수정 → 스키마 기반 라우트 + 클라이언트 자동 생성
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Glob
|
|
10
|
+
- Grep
|
|
11
|
+
- Bash
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# /clawfire-api
|
|
15
|
+
|
|
16
|
+
API를 생성하거나 수정하고, 타입 안전한 클라이언트를 자동 생성합니다.
|
|
17
|
+
|
|
18
|
+
## 사용 예시
|
|
19
|
+
|
|
20
|
+
사용자: "상품 CRUD API 만들어줘"
|
|
21
|
+
사용자: "주문 생성 API 추가"
|
|
22
|
+
사용자: "상품 목록 API에 카테고리 필터 추가"
|
|
23
|
+
|
|
24
|
+
## 수행 작업
|
|
25
|
+
|
|
26
|
+
### 1. 라우트 파일 생성/수정
|
|
27
|
+
|
|
28
|
+
`app/routes/<path>.ts` 에 라우트를 생성합니다.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { defineAPI, z, Errors } from "clawfire";
|
|
32
|
+
|
|
33
|
+
export default defineAPI({
|
|
34
|
+
input: z.object({
|
|
35
|
+
name: z.string().min(1),
|
|
36
|
+
price: z.number().positive(),
|
|
37
|
+
category: z.enum(["electronics", "clothing", "food"]),
|
|
38
|
+
}),
|
|
39
|
+
output: z.object({
|
|
40
|
+
product: z.object({
|
|
41
|
+
id: z.string(),
|
|
42
|
+
name: z.string(),
|
|
43
|
+
price: z.number(),
|
|
44
|
+
category: z.string(),
|
|
45
|
+
createdAt: z.string(),
|
|
46
|
+
}),
|
|
47
|
+
}),
|
|
48
|
+
meta: {
|
|
49
|
+
description: "상품 생성",
|
|
50
|
+
auth: "authenticated",
|
|
51
|
+
tags: ["products"],
|
|
52
|
+
exampleInput: {
|
|
53
|
+
name: "아이폰 16",
|
|
54
|
+
price: 1500000,
|
|
55
|
+
category: "electronics",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
handler: async (input, ctx) => {
|
|
59
|
+
const product = {
|
|
60
|
+
id: `prod_${Date.now()}`,
|
|
61
|
+
...input,
|
|
62
|
+
createdAt: new Date().toISOString(),
|
|
63
|
+
};
|
|
64
|
+
return { product };
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. 파일 라우팅 규칙
|
|
70
|
+
|
|
71
|
+
파일 경로가 곧 API 경로입니다:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
app/routes/health.ts → POST /api/health
|
|
75
|
+
app/routes/products/list.ts → POST /api/products/list
|
|
76
|
+
app/routes/products/create.ts → POST /api/products/create
|
|
77
|
+
app/routes/products/[id]/get.ts → POST /api/products/:id/get
|
|
78
|
+
app/routes/auth/login.ts → POST /api/auth/login
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 3. CRUD 패턴 (새 리소스 추가 시)
|
|
82
|
+
|
|
83
|
+
새로운 리소스(예: products)를 추가할 때 표준 파일 구조:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
app/routes/products/
|
|
87
|
+
list.ts ← 목록 조회 (필터, 페이지네이션)
|
|
88
|
+
create.ts ← 생성
|
|
89
|
+
update.ts ← 수정
|
|
90
|
+
delete.ts ← 삭제
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 4. 데이터 저장소 연결
|
|
94
|
+
|
|
95
|
+
현재 프로젝트가 인메모리 store를 사용하는 경우:
|
|
96
|
+
- `app/store.ts` 에 새 리소스의 저장소 추가
|
|
97
|
+
- Map 기반 CRUD 메서드 추가
|
|
98
|
+
|
|
99
|
+
Firestore를 사용하는 경우:
|
|
100
|
+
- `functions/index.ts` 에서 `createAdminDB`로 생성한 `db` 사용
|
|
101
|
+
- handler에서 `db.create()`, `db.query()` 등 사용
|
|
102
|
+
|
|
103
|
+
## 필수 규칙
|
|
104
|
+
|
|
105
|
+
- 모든 API는 **POST만** 사용 (HTTP 메서드 구분 없음)
|
|
106
|
+
- `input`과 `output`은 반드시 `z.object({...})`
|
|
107
|
+
- `meta.description`은 필수 (Playground/AI가 읽음)
|
|
108
|
+
- `meta.auth`를 명시하지 않으면 보안 위험 — 항상 지정
|
|
109
|
+
- handler는 반드시 `async` 함수
|
|
110
|
+
- 에러는 `Errors.notFound()`, `Errors.forbidden()` 등 사용
|
|
111
|
+
- `import`는 반드시 `from "clawfire"`
|
|
112
|
+
|
|
113
|
+
## 에러 처리 패턴
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { Errors } from "clawfire";
|
|
117
|
+
|
|
118
|
+
// 404
|
|
119
|
+
throw Errors.notFound("Product not found");
|
|
120
|
+
// 400
|
|
121
|
+
throw Errors.validation("Invalid input", details);
|
|
122
|
+
// 403
|
|
123
|
+
throw Errors.forbidden("Admin only");
|
|
124
|
+
// 409
|
|
125
|
+
throw Errors.conflict("Already exists");
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 응답 형식
|
|
129
|
+
|
|
130
|
+
성공: `{ "data": { ... } }`
|
|
131
|
+
실패: `{ "error": { "code": "NOT_FOUND", "message": "..." } }`
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clawfire-auth
|
|
3
|
+
description: Auth 정책 설정 → 로그인, 역할, 재인증, Guards 자동 반영
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Glob
|
|
10
|
+
- Grep
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# /clawfire-auth
|
|
14
|
+
|
|
15
|
+
인증/인가 정책을 대화 기반으로 설정하고 Rules/Guards에 자동 반영합니다.
|
|
16
|
+
|
|
17
|
+
## 사용 예시
|
|
18
|
+
|
|
19
|
+
사용자: "관리자만 상품 삭제할 수 있게 해줘"
|
|
20
|
+
사용자: "비밀번호 변경은 재인증 필요하게 해줘"
|
|
21
|
+
사용자: "비회원도 상품 목록 볼 수 있게 해줘"
|
|
22
|
+
|
|
23
|
+
## 수행 작업
|
|
24
|
+
|
|
25
|
+
### 1. API 메타데이터 업데이트
|
|
26
|
+
|
|
27
|
+
라우트 파일의 `meta.auth` 변경:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
meta: {
|
|
31
|
+
auth: "role",
|
|
32
|
+
roles: ["admin"],
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Firestore Rules 업데이트
|
|
37
|
+
|
|
38
|
+
모델 정의의 `rules` 속성 수정 후 `firestore.rules` 재생성.
|
|
39
|
+
|
|
40
|
+
### 3. 클라이언트 코드 갱신
|
|
41
|
+
|
|
42
|
+
인증 수준이 바뀌면 `generated/api-client.ts`도 업데이트.
|
|
43
|
+
|
|
44
|
+
## 인증 수준
|
|
45
|
+
|
|
46
|
+
| 수준 | 설명 | Header 필요 |
|
|
47
|
+
|------|------|------------|
|
|
48
|
+
| `public` | 인증 불필요 | 없음 |
|
|
49
|
+
| `authenticated` | 로그인 필수 | `Authorization: Bearer <token>` |
|
|
50
|
+
| `role` | 특정 역할 필요 | Bearer + 역할 매칭 |
|
|
51
|
+
| `reauth` | 최근 재인증 필요 (5분 이내) | Bearer + 최신 토큰 |
|
|
52
|
+
|
|
53
|
+
## 핸들러에서 auth 사용
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
handler: async (input, ctx) => {
|
|
57
|
+
// public: ctx.auth는 null일 수 있음
|
|
58
|
+
// authenticated: ctx.auth 보장 (non-null)
|
|
59
|
+
// role: ctx.auth.role 보장
|
|
60
|
+
// reauth: ctx.reauthenticated === true 보장
|
|
61
|
+
|
|
62
|
+
const userId = ctx.auth!.uid;
|
|
63
|
+
const userRole = ctx.auth!.role;
|
|
64
|
+
const email = ctx.auth!.email;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 역할 관리 (서버 측)
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { setUserRole, getUserRole } from "clawfire/functions";
|
|
72
|
+
|
|
73
|
+
await setUserRole(auth, uid, "admin");
|
|
74
|
+
const role = await getUserRole(auth, uid);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 클라이언트 측 인증
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { createClientAuth } from "clawfire/client";
|
|
81
|
+
import { getAuth } from "firebase/auth";
|
|
82
|
+
|
|
83
|
+
const auth = createClientAuth(getAuth());
|
|
84
|
+
const token = await auth.getIdToken();
|
|
85
|
+
|
|
86
|
+
// API 호출 시
|
|
87
|
+
fetch("/api/products/create", {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: {
|
|
90
|
+
"Content-Type": "application/json",
|
|
91
|
+
"Authorization": `Bearer ${token}`,
|
|
92
|
+
},
|
|
93
|
+
body: JSON.stringify({ name: "Widget" }),
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Firestore Rules 자동 생성 헬퍼
|
|
98
|
+
|
|
99
|
+
rules 파일에 자동 포함되는 헬퍼 함수:
|
|
100
|
+
- `isAuthenticated()` — 로그인 여부
|
|
101
|
+
- `isOwner(userId)` — 문서 소유자 여부
|
|
102
|
+
- `hasRole(role)` — 특정 역할 보유
|
|
103
|
+
- `hasAnyRole(roles)` — 역할 목록 중 하나 보유
|
|
104
|
+
- `isRecentAuth()` — 5분 이내 인증 여부
|
|
105
|
+
|
|
106
|
+
## 주의
|
|
107
|
+
|
|
108
|
+
- 기본값은 보안 우선 — 명시적으로 열어줘야 접근 가능
|
|
109
|
+
- `public` API도 rate limit은 적용됨
|
|
110
|
+
- `role` 사용 시 반드시 `roles` 배열 지정
|
|
111
|
+
- Firestore rules와 API auth를 동기화 유지
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clawfire-deploy
|
|
3
|
+
description: Firebase Hosting + Functions 배포 자동화
|
|
4
|
+
user-invocable: true
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
allowed-tools:
|
|
7
|
+
- Bash
|
|
8
|
+
- Read
|
|
9
|
+
- Glob
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# /clawfire-deploy
|
|
13
|
+
|
|
14
|
+
Firebase Hosting과 Functions를 배포합니다.
|
|
15
|
+
|
|
16
|
+
**이 스킬은 배포(side-effect)를 수행하므로 자동 실행이 비활성화되어 있습니다.**
|
|
17
|
+
사용자가 명시적으로 "/clawfire-deploy" 또는 "배포해줘"라고 요청해야 합니다.
|
|
18
|
+
|
|
19
|
+
## 배포 전 체크리스트
|
|
20
|
+
|
|
21
|
+
### 1. Firebase CLI 설치 및 로그인 확인
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
firebase --version
|
|
25
|
+
firebase login:list
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
설치 안 됐으면: `npm install -g firebase-tools`
|
|
29
|
+
|
|
30
|
+
### 2. Firebase 프로젝트 연결 확인
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
firebase use
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
연결 안 됐으면: `firebase use --add`
|
|
37
|
+
|
|
38
|
+
### 3. 빌드 확인
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# TypeScript 체크
|
|
42
|
+
npx tsc --noEmit
|
|
43
|
+
|
|
44
|
+
# Functions 빌드
|
|
45
|
+
cd functions && npm run build
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 4. Rules 동기화 확인
|
|
49
|
+
|
|
50
|
+
- `firestore.rules`가 최신 모델을 반영하는지 확인
|
|
51
|
+
- 필요 시 `/clawfire-model` 스킬로 재생성
|
|
52
|
+
|
|
53
|
+
### 5. 설정 확인
|
|
54
|
+
|
|
55
|
+
- `firebase.json` 존재 확인
|
|
56
|
+
- `clawfire.config.ts`에 실제 Firebase 설정값 확인 (YOUR_API_KEY 등 플레이스홀더 아닌지)
|
|
57
|
+
|
|
58
|
+
## 배포 실행
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 전체 배포 (Hosting + Functions + Rules + Indexes)
|
|
62
|
+
firebase deploy
|
|
63
|
+
|
|
64
|
+
# 개별 배포
|
|
65
|
+
firebase deploy --only hosting
|
|
66
|
+
firebase deploy --only functions
|
|
67
|
+
firebase deploy --only firestore:rules
|
|
68
|
+
firebase deploy --only firestore:indexes
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 배포 후 확인
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# 배포된 URL
|
|
75
|
+
firebase hosting:channel:list
|
|
76
|
+
|
|
77
|
+
# Functions 로그
|
|
78
|
+
firebase functions:log --only api
|
|
79
|
+
|
|
80
|
+
# 프로젝트 대시보드
|
|
81
|
+
echo "https://console.firebase.google.com/project/$(firebase use)/overview"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 문제 해결
|
|
85
|
+
|
|
86
|
+
- 빌드 실패: `npx tsc --noEmit`으로 타입 에러 확인
|
|
87
|
+
- Rules 배포 실패: `firebase deploy --only firestore:rules --debug`
|
|
88
|
+
- Functions 배포 실패: `firebase functions:log`에서 에러 확인
|
|
89
|
+
- 권한 문제: `firebase login`으로 재로그인
|
|
90
|
+
|
|
91
|
+
## 주의
|
|
92
|
+
|
|
93
|
+
- Rules 변경은 **즉시 적용** — 실수 시 데이터 접근 문제
|
|
94
|
+
- Functions 배포는 2-5분 소요
|
|
95
|
+
- 프로덕션 배포 전 에뮬레이터에서 먼저 테스트 권장
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clawfire-diagnose
|
|
3
|
+
description: 인덱스 누락, Rules 거부, 권한 문제 탐지 및 수정 제안
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Bash
|
|
7
|
+
- Read
|
|
8
|
+
- Glob
|
|
9
|
+
- Grep
|
|
10
|
+
- Edit
|
|
11
|
+
- Write
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# /clawfire-diagnose
|
|
15
|
+
|
|
16
|
+
프로젝트의 문제를 탐지하고 수정을 제안합니다.
|
|
17
|
+
|
|
18
|
+
## 진단 항목
|
|
19
|
+
|
|
20
|
+
### 1. 구조 검증
|
|
21
|
+
- `app/routes/` 내 파일이 올바른 `defineAPI` export를 가지는지
|
|
22
|
+
- `app/schemas/` 내 파일이 올바른 `defineModel` export를 가지는지
|
|
23
|
+
- 필수 파일 존재: `firebase.json`, `clawfire.config.ts`, `dev.ts`, `package.json`
|
|
24
|
+
|
|
25
|
+
### 2. Import 검증
|
|
26
|
+
- 모든 import가 `clawfire`를 사용하는지 (`clawfire` 아닌지)
|
|
27
|
+
- 필요한 import 누락 여부
|
|
28
|
+
|
|
29
|
+
### 3. API 계약 검증
|
|
30
|
+
- `input`과 `output`이 `z.object()`인지
|
|
31
|
+
- `meta.description` 존재 여부
|
|
32
|
+
- `meta.auth` 명시 여부 (없으면 보안 위험 경고)
|
|
33
|
+
- `meta.auth: "role"` 시 `meta.roles`가 비어있지 않은지
|
|
34
|
+
- `handler`가 async 함수인지
|
|
35
|
+
|
|
36
|
+
### 4. 모델 검증
|
|
37
|
+
- `collection` 이름이 소문자 복수형인지
|
|
38
|
+
- `rules`가 정의되어 있는지
|
|
39
|
+
- `ownerField` 사용 시 해당 필드가 `fields`에 존재하는지
|
|
40
|
+
|
|
41
|
+
### 5. Security Rules 검증
|
|
42
|
+
- `firestore.rules`가 모델 정의와 일치하는지
|
|
43
|
+
- 과도하게 열린 규칙 경고 (`allow read, write: if true`)
|
|
44
|
+
- 규칙 누락 컬렉션 감지
|
|
45
|
+
|
|
46
|
+
### 6. 인덱스 검증
|
|
47
|
+
- 모델의 `indexes`가 `firestore.indexes.json`에 반영되어 있는지
|
|
48
|
+
|
|
49
|
+
### 7. Firebase 설정 검증
|
|
50
|
+
- `firebase.json` 유효성
|
|
51
|
+
- `clawfire.config.ts`에 플레이스홀더 값(`YOUR_API_KEY`) 잔존 여부
|
|
52
|
+
|
|
53
|
+
### 8. 데이터 스토어 일관성
|
|
54
|
+
- `app/store.ts` 사용 시 → 라우트에서 올바르게 import하는지
|
|
55
|
+
- Firestore 사용 시 → `functions/index.ts`에 라우트 등록 여부
|
|
56
|
+
|
|
57
|
+
## 수행 방법
|
|
58
|
+
|
|
59
|
+
1. 모든 `app/schemas/*.ts` 파일 스캔
|
|
60
|
+
2. 모든 `app/routes/**/*.ts` 파일 스캔
|
|
61
|
+
3. 설정 파일 읽기 (`firebase.json`, `firestore.rules`, `firestore.indexes.json`, `clawfire.config.ts`)
|
|
62
|
+
4. 문제 목록 생성
|
|
63
|
+
5. 각 문제에 대한 구체적 수정 제안
|
|
64
|
+
6. 사용자 승인 시 자동 수정
|
|
65
|
+
|
|
66
|
+
## 출력 형식
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Clawfire Diagnosis Report
|
|
70
|
+
═════════════════════════
|
|
71
|
+
|
|
72
|
+
[OK] firebase.json exists and is valid
|
|
73
|
+
[OK] firestore.rules exists
|
|
74
|
+
[OK] 5 routes found in app/routes/
|
|
75
|
+
[WARN] clawfire.config.ts contains placeholder API key
|
|
76
|
+
[WARN] Model "Todo" rules allow public write — intentional?
|
|
77
|
+
[ERR] Route "products/delete" has auth: "role" but no roles specified
|
|
78
|
+
|
|
79
|
+
Suggested Fixes:
|
|
80
|
+
1. Update clawfire.config.ts with real Firebase config
|
|
81
|
+
2. Consider restricting Todo write rules to "authenticated"
|
|
82
|
+
3. Add roles: ["admin"] to products/delete route
|
|
83
|
+
|
|
84
|
+
Apply fixes? (y/n)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 자동 수정 가능 항목
|
|
88
|
+
|
|
89
|
+
- 누락된 `meta.auth` 추가 (기본: "authenticated")
|
|
90
|
+
- 비어있는 `roles` 채우기
|
|
91
|
+
- `firestore.rules` 재생성
|
|
92
|
+
- `firestore.indexes.json` 재생성
|
|
93
|
+
- import 경로 수정 (`"clawfire"` → `"clawfire"`)
|
|
94
|
+
|
|
95
|
+
## 주의
|
|
96
|
+
|
|
97
|
+
- 진단만 수행 — 수정은 사용자 승인 후 진행
|
|
98
|
+
- `public` 규칙은 경고만 (의도적일 수 있음)
|
|
99
|
+
- 에뮬레이터 로그가 있으면 추가 분석 가능
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clawfire-model
|
|
3
|
+
description: Firestore 모델(컬렉션) 정의 → 스키마 + Rules + 인덱스 자동 생성
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Edit
|
|
9
|
+
- Glob
|
|
10
|
+
- Grep
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# /clawfire-model
|
|
14
|
+
|
|
15
|
+
자연어로 모델을 선언하면 Firestore 구조, Security Rules, 인덱스를 자동 생성합니다.
|
|
16
|
+
|
|
17
|
+
## 사용 예시
|
|
18
|
+
|
|
19
|
+
사용자: "상품, 주문, 유저 모델 만들어줘"
|
|
20
|
+
사용자: "상품에 옵션 배열 추가해줘"
|
|
21
|
+
사용자: "주문에 배송 상태 필드 추가"
|
|
22
|
+
|
|
23
|
+
## 수행 작업
|
|
24
|
+
|
|
25
|
+
### 1. 모델 파일 생성/수정
|
|
26
|
+
|
|
27
|
+
`app/schemas/<model>.ts` 에 모델을 정의합니다:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { defineModel } from "clawfire";
|
|
31
|
+
|
|
32
|
+
export const Product = defineModel({
|
|
33
|
+
collection: "products",
|
|
34
|
+
fields: {
|
|
35
|
+
name: { type: "string", required: true, description: "상품명" },
|
|
36
|
+
price: { type: "number", required: true, description: "가격" },
|
|
37
|
+
description: { type: "string", description: "상품 설명" },
|
|
38
|
+
category: {
|
|
39
|
+
type: "string",
|
|
40
|
+
required: true,
|
|
41
|
+
enum: ["electronics", "clothing", "food"],
|
|
42
|
+
},
|
|
43
|
+
tags: { type: "array", items: { type: "string" } },
|
|
44
|
+
inStock: { type: "boolean", required: true },
|
|
45
|
+
},
|
|
46
|
+
timestamps: true,
|
|
47
|
+
rules: {
|
|
48
|
+
read: "public",
|
|
49
|
+
create: "role",
|
|
50
|
+
createRoles: ["admin"],
|
|
51
|
+
update: "role",
|
|
52
|
+
updateRoles: ["admin"],
|
|
53
|
+
delete: "role",
|
|
54
|
+
deleteRoles: ["admin"],
|
|
55
|
+
},
|
|
56
|
+
indexes: [
|
|
57
|
+
{ fields: [{ field: "category" }, { field: "price", order: "desc" }] },
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. Security Rules 업데이트
|
|
63
|
+
|
|
64
|
+
모델 생성/수정 후 반드시 `firestore.rules` 업데이트:
|
|
65
|
+
- 모든 `app/schemas/*.ts` 파일의 모델을 읽음
|
|
66
|
+
- 통합 Firestore Security Rules 생성
|
|
67
|
+
|
|
68
|
+
### 3. 인덱스 업데이트
|
|
69
|
+
|
|
70
|
+
모델에 `indexes`가 있으면 `firestore.indexes.json`에 반영.
|
|
71
|
+
|
|
72
|
+
## 필드 타입 레퍼런스
|
|
73
|
+
|
|
74
|
+
| 타입 | 설명 | 옵션 |
|
|
75
|
+
|------|------|------|
|
|
76
|
+
| `string` | 문자열 | `enum: ["a", "b"]` |
|
|
77
|
+
| `number` | 숫자 | - |
|
|
78
|
+
| `boolean` | 불리언 | - |
|
|
79
|
+
| `timestamp` | 날짜/시간 | - |
|
|
80
|
+
| `array` | 배열 | `items: { type: "string" }` |
|
|
81
|
+
| `map` | 객체/맵 | `values: { type: "string" }` |
|
|
82
|
+
| `reference` | 문서 참조 | `ref: "products"` |
|
|
83
|
+
| `geopoint` | 위치 좌표 | - |
|
|
84
|
+
|
|
85
|
+
## 필드 공통 옵션
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
{
|
|
89
|
+
type: "string",
|
|
90
|
+
required: true, // 필수 여부
|
|
91
|
+
description: "설명", // AI/문서용
|
|
92
|
+
default: "기본값", // 기본값
|
|
93
|
+
enum: ["a", "b", "c"], // 허용 값 목록 (string만)
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 보안 규칙 수준
|
|
98
|
+
|
|
99
|
+
| 수준 | 의미 | 예시 |
|
|
100
|
+
|------|------|------|
|
|
101
|
+
| `public` | 누구나 | 상품 목록 조회 |
|
|
102
|
+
| `authenticated` | 로그인 필수 | 프로필 수정 |
|
|
103
|
+
| `role` | 특정 역할 | 관리자 전용 (roles 필수) |
|
|
104
|
+
| `reauth` | 최근 재인증 | 비밀번호 변경 |
|
|
105
|
+
|
|
106
|
+
### ownerField 패턴
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
rules: {
|
|
110
|
+
read: "public",
|
|
111
|
+
update: "authenticated",
|
|
112
|
+
ownerField: "userId",
|
|
113
|
+
// → 본인 문서만 수정 가능 (userId == auth.uid)
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 명명 규칙
|
|
118
|
+
|
|
119
|
+
- **모델명**: PascalCase (Product, User, Order)
|
|
120
|
+
- **컬렉션명**: lowercase 복수형 (products, users, orders)
|
|
121
|
+
- **파일명**: 소문자 단수형 (product.ts, user.ts, order.ts)
|
|
122
|
+
- **필드명**: camelCase (createdAt, displayName)
|
|
123
|
+
|
|
124
|
+
## 주의
|
|
125
|
+
|
|
126
|
+
- `timestamps: true`가 기본 (createdAt, updatedAt 자동)
|
|
127
|
+
- 보안 규칙은 최소 권한 원칙 적용 — 필요 최소한만 열기
|
|
128
|
+
- `import`는 반드시 `from "clawfire"`
|