@nebulit/embuilder 0.1.39
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 +254 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +138 -0
- package/package.json +49 -0
- package/templates/.claude/hooks/QUICKSTART.md +256 -0
- package/templates/.claude/hooks/README.md +533 -0
- package/templates/.claude/hooks/analyze-commit.sh +22 -0
- package/templates/.claude/hooks/analyze-commit.ts +518 -0
- package/templates/.claude/hooks/analyzers/README.md +198 -0
- package/templates/.claude/hooks/analyzers/code-quality-checker.ts +154 -0
- package/templates/.claude/hooks/analyzers/code-quality.md +54 -0
- package/templates/.claude/hooks/analyzers/commit-blocker-example.ts.disabled +110 -0
- package/templates/.claude/hooks/analyzers/commit-policy.md +49 -0
- package/templates/.claude/hooks/analyzers/event-model-validator.md +49 -0
- package/templates/.claude/hooks/analyzers/event-model-validator.ts +169 -0
- package/templates/.claude/hooks/analyzers/example-logger.ts +70 -0
- package/templates/.claude/hooks/analyzers/slice-scope-validator.md +81 -0
- package/templates/.claude/hooks/check-review-result.sh +47 -0
- package/templates/.claude/hooks/prepare-review.sh +34 -0
- package/templates/.claude/hooks/review-agent-prompt.md +42 -0
- package/templates/.claude/hooks/run-review-agent.sh +124 -0
- package/templates/.claude/settings.local.json +37 -0
- package/templates/.claude/skills/help/README.md +84 -0
- package/templates/.claude/skills/help/SKILL.md +393 -0
- package/templates/.claude/skills/help/templates/demo-config.json +6753 -0
- package/templates/.claude/skills/sample-slices/SKILL.md +8 -0
- package/templates/.claude/skills/sample-slices/templates/.slices/Library/addbook/code-slice.json +124 -0
- package/templates/.claude/skills/sample-slices/templates/.slices/Library/addbook/slice.json +255 -0
- package/templates/.claude/skills/sample-slices/templates/.slices/Library/availablebooks/slice.json +107 -0
- package/templates/.claude/skills/sample-slices/templates/.slices/index.json +20 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/additem/slice.json +979 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/archiveitem/slice.json +529 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/cartitems/slice.json +1072 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/cartwithproducts/slice.json +394 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/changedprices/slice.json +88 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/changeinventory/slice.json +264 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/changeprice/slice.json +308 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/clearcart/slice.json +358 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/inventories/slice.json +203 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/publishcart/slice.json +876 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/removeitem/slice.json +560 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/submitcart/slice.json +708 -0
- package/templates/.claude/skills/sample-slices/templates/Cart/submittedcartdata/slice.json +399 -0
- package/templates/.claude/skills/sample-slices/templates/index.json +108 -0
- package/templates/.claude/skills/slice-automation/SKILL.md +49 -0
- package/templates/.claude/skills/slice-state-change/SKILL.md +369 -0
- package/templates/.claude/skills/slice-state-change/templates/AddLocation/AddLocation.test.ts.sample +76 -0
- package/templates/.claude/skills/slice-state-change/templates/AddLocation/AddLocationCommand.ts.sample +84 -0
- package/templates/.claude/skills/slice-state-change/templates/AddLocation/routes.ts.sample +73 -0
- package/templates/.claude/skills/slice-state-change/templates/README.md +46 -0
- package/templates/.claude/skills/slice-state-view/SKILL.md +336 -0
- package/templates/.claude/skills/slice-state-view/templates/Locations/Locations.test.ts.sample +84 -0
- package/templates/.claude/skills/slice-state-view/templates/Locations/LocationsProjection.ts.sample +50 -0
- package/templates/.claude/skills/slice-state-view/templates/Locations/routes.ts.sample +46 -0
- package/templates/.claude/skills/slice-state-view/templates/README.md +109 -0
- package/templates/.claude/skills/slice-state-view/templates/Tables/Tables.test.ts.sample +104 -0
- package/templates/.claude/skills/slice-state-view/templates/Tables/TablesProjection.ts.sample +59 -0
- package/templates/.claude/skills/slice-state-view/templates/Tables/routes.ts.sample +46 -0
- package/templates/.claude/skills/slice-state-view/templates/V2__tables.sql +7 -0
- package/templates/.claude/skills/slice-state-view/templates/V8__locations.sql +7 -0
- package/templates/.claude/skills/test-analyzer/SKILL.md +373 -0
- package/templates/.claude/skills/test-analyzer/examples/specification-format.md +143 -0
- package/templates/.claude/skills/test-analyzer/examples/state-change-example.md +111 -0
- package/templates/.claude/skills/test-analyzer/examples/state-view-example.md +122 -0
- package/templates/AGENTS.md +110 -0
- package/templates/Claude.md +58 -0
- package/templates/README.md +178 -0
- package/templates/backend/.env +9 -0
- package/templates/backend/BACKEND_AUTH_SETUP.md +183 -0
- package/templates/backend/SWAGGER.md +213 -0
- package/templates/backend/eslint.config.mjs +31 -0
- package/templates/backend/flyway.conf +17 -0
- package/templates/backend/package.json +44 -0
- package/templates/backend/prd.json.example +64 -0
- package/templates/backend/public/assets/images/banner.png +0 -0
- package/templates/backend/public/assets/logo.png +0 -0
- package/templates/backend/public/file.svg +4 -0
- package/templates/backend/public/globe.svg +12 -0
- package/templates/backend/public/next.svg +6 -0
- package/templates/backend/public/vercel.svg +3 -0
- package/templates/backend/public/window.svg +5 -0
- package/templates/backend/server.ts +129 -0
- package/templates/backend/setup-env.sh +50 -0
- package/templates/backend/src/common/assertions.ts +6 -0
- package/templates/backend/src/common/db.ts +1 -0
- package/templates/backend/src/common/loadPostgresEventstore.ts +16 -0
- package/templates/backend/src/common/parseEndpoint.ts +51 -0
- package/templates/backend/src/common/replay.ts +9 -0
- package/templates/backend/src/common/routes.ts +19 -0
- package/templates/backend/src/common/testHelpers.ts +53 -0
- package/templates/backend/src/core/readmodel.ts +28 -0
- package/templates/backend/src/core/types.ts +26 -0
- package/templates/backend/src/process/process.ts +53 -0
- package/templates/backend/src/supabase/LoginHandler.ts +36 -0
- package/templates/backend/src/supabase/ProtectedPageProps.ts +21 -0
- package/templates/backend/src/supabase/README.md +171 -0
- package/templates/backend/src/supabase/api.ts +63 -0
- package/templates/backend/src/supabase/authMiddleware.ts +53 -0
- package/templates/backend/src/supabase/component.ts +12 -0
- package/templates/backend/src/supabase/requireUser.ts +72 -0
- package/templates/backend/src/supabase/serverProps.ts +25 -0
- package/templates/backend/src/supabase/staticProps.ts +10 -0
- package/templates/backend/src/swagger.ts +34 -0
- package/templates/backend/src/util/assertions.ts +6 -0
- package/templates/backend/supabase/config.toml +295 -0
- package/templates/backend/supabase/migrations/20260121155918593_catalogentries.sql.sample +23 -0
- package/templates/backend/supabase/seed.sql +1 -0
- package/templates/backend/tsconfig.json +31 -0
- package/templates/frontend/.env.development +3 -0
- package/templates/frontend/AGENTS.md +7 -0
- package/templates/frontend/README.md +73 -0
- package/templates/frontend/components.json +20 -0
- package/templates/frontend/eslint.config.js +26 -0
- package/templates/frontend/index.html +18 -0
- package/templates/frontend/package-lock.json +8347 -0
- package/templates/frontend/package.json +94 -0
- package/templates/frontend/postcss.config.js +6 -0
- package/templates/frontend/public/favicon.ico +0 -0
- package/templates/frontend/public/logo.png +0 -0
- package/templates/frontend/public/placeholder.svg +1 -0
- package/templates/frontend/public/robots.txt +14 -0
- package/templates/frontend/src/App.css +42 -0
- package/templates/frontend/src/App.tsx +47 -0
- package/templates/frontend/src/components/NavLink.tsx +28 -0
- package/templates/frontend/src/components/ProtectedRoute.tsx +24 -0
- package/templates/frontend/src/components/calendar/Calendar.tsx +302 -0
- package/templates/frontend/src/components/layout/DashboardLayout.tsx +21 -0
- package/templates/frontend/src/components/layout/Header.tsx +45 -0
- package/templates/frontend/src/components/layout/Sidebar.tsx +82 -0
- package/templates/frontend/src/components/tables/ReservationTemplates.tsx +189 -0
- package/templates/frontend/src/components/ui/accordion.tsx +52 -0
- package/templates/frontend/src/components/ui/alert-dialog.tsx +104 -0
- package/templates/frontend/src/components/ui/alert.tsx +43 -0
- package/templates/frontend/src/components/ui/aspect-ratio.tsx +5 -0
- package/templates/frontend/src/components/ui/avatar.tsx +38 -0
- package/templates/frontend/src/components/ui/badge.tsx +29 -0
- package/templates/frontend/src/components/ui/breadcrumb.tsx +90 -0
- package/templates/frontend/src/components/ui/button.tsx +47 -0
- package/templates/frontend/src/components/ui/calendar.tsx +54 -0
- package/templates/frontend/src/components/ui/card.tsx +43 -0
- package/templates/frontend/src/components/ui/carousel.tsx +224 -0
- package/templates/frontend/src/components/ui/chart.tsx +303 -0
- package/templates/frontend/src/components/ui/checkbox.tsx +26 -0
- package/templates/frontend/src/components/ui/collapsible.tsx +9 -0
- package/templates/frontend/src/components/ui/command.tsx +132 -0
- package/templates/frontend/src/components/ui/context-menu.tsx +178 -0
- package/templates/frontend/src/components/ui/dialog.tsx +95 -0
- package/templates/frontend/src/components/ui/drawer.tsx +87 -0
- package/templates/frontend/src/components/ui/dropdown-menu.tsx +179 -0
- package/templates/frontend/src/components/ui/form.tsx +129 -0
- package/templates/frontend/src/components/ui/hover-card.tsx +27 -0
- package/templates/frontend/src/components/ui/input-otp.tsx +61 -0
- package/templates/frontend/src/components/ui/input.tsx +22 -0
- package/templates/frontend/src/components/ui/label.tsx +17 -0
- package/templates/frontend/src/components/ui/menubar.tsx +207 -0
- package/templates/frontend/src/components/ui/navigation-menu.tsx +120 -0
- package/templates/frontend/src/components/ui/pagination.tsx +81 -0
- package/templates/frontend/src/components/ui/popover.tsx +29 -0
- package/templates/frontend/src/components/ui/progress.tsx +23 -0
- package/templates/frontend/src/components/ui/radio-group.tsx +36 -0
- package/templates/frontend/src/components/ui/resizable.tsx +37 -0
- package/templates/frontend/src/components/ui/scroll-area.tsx +38 -0
- package/templates/frontend/src/components/ui/select.tsx +143 -0
- package/templates/frontend/src/components/ui/separator.tsx +20 -0
- package/templates/frontend/src/components/ui/sheet.tsx +107 -0
- package/templates/frontend/src/components/ui/sidebar.tsx +637 -0
- package/templates/frontend/src/components/ui/skeleton.tsx +7 -0
- package/templates/frontend/src/components/ui/slider.tsx +23 -0
- package/templates/frontend/src/components/ui/sonner.tsx +27 -0
- package/templates/frontend/src/components/ui/stat-card.tsx +44 -0
- package/templates/frontend/src/components/ui/switch.tsx +27 -0
- package/templates/frontend/src/components/ui/table.tsx +72 -0
- package/templates/frontend/src/components/ui/tabs.tsx +53 -0
- package/templates/frontend/src/components/ui/textarea.tsx +21 -0
- package/templates/frontend/src/components/ui/toast.tsx +111 -0
- package/templates/frontend/src/components/ui/toaster.tsx +24 -0
- package/templates/frontend/src/components/ui/toggle-group.tsx +49 -0
- package/templates/frontend/src/components/ui/toggle.tsx +37 -0
- package/templates/frontend/src/components/ui/tooltip.tsx +28 -0
- package/templates/frontend/src/components/ui/use-toast.ts +3 -0
- package/templates/frontend/src/contexts/AuthContext.tsx +94 -0
- package/templates/frontend/src/contexts/RefreshContext.tsx +236 -0
- package/templates/frontend/src/hooks/api/index.ts +2 -0
- package/templates/frontend/src/hooks/api/useLocations.ts +15 -0
- package/templates/frontend/src/hooks/use-mobile.tsx +19 -0
- package/templates/frontend/src/hooks/use-toast.ts +186 -0
- package/templates/frontend/src/hooks/useApiContext.ts +11 -0
- package/templates/frontend/src/index.css +118 -0
- package/templates/frontend/src/integrations/supabase/client.ts +9 -0
- package/templates/frontend/src/lib/api-client.ts +136 -0
- package/templates/frontend/src/lib/api.ts +1028 -0
- package/templates/frontend/src/lib/utils.ts +6 -0
- package/templates/frontend/src/main.tsx +5 -0
- package/templates/frontend/src/pages/Auth.tsx +408 -0
- package/templates/frontend/src/pages/Dashboard.tsx +168 -0
- package/templates/frontend/src/pages/Menus.tsx +224 -0
- package/templates/frontend/src/pages/NotFound.tsx +24 -0
- package/templates/frontend/src/pages/Register.tsx +285 -0
- package/templates/frontend/src/test/example.test.ts +0 -0
- package/templates/frontend/src/test/setup.ts +15 -0
- package/templates/frontend/src/types/index.ts +8 -0
- package/templates/frontend/src/vite-env.d.ts +1 -0
- package/templates/frontend/tailwind.config.ts +101 -0
- package/templates/frontend/tsconfig.app.json +31 -0
- package/templates/frontend/tsconfig.json +16 -0
- package/templates/frontend/tsconfig.node.json +22 -0
- package/templates/frontend/vite.config.ts +21 -0
- package/templates/frontend/vitest.config.ts +16 -0
- package/templates/init.sh +1 -0
- package/templates/prompt.md +139 -0
- package/templates/ralph.sh +120 -0
- package/templates/server.mjs +505 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Event Model Project
|
|
2
|
+
|
|
3
|
+
This project uses event-model driven development with Claude Code and EMBuilder.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Available Commands
|
|
8
|
+
|
|
9
|
+
Use these commands in Claude Code to generate slices from your event model:
|
|
10
|
+
|
|
11
|
+
**Event Model Skills:**
|
|
12
|
+
```
|
|
13
|
+
/state-change-slice # Generate a command handler slice
|
|
14
|
+
/state-view-slice # Generate a read model/projection slice
|
|
15
|
+
/automation-slice # Generate a background automation slice
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Configuration:**
|
|
19
|
+
```
|
|
20
|
+
/fetch-config # Fetch config.json from event model app (localhost:3001)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Yeoman Generator Skills:**
|
|
24
|
+
```
|
|
25
|
+
/gen-skeleton # Generate Supabase backend skeleton app
|
|
26
|
+
/gen-state-change # Generate state change slices from config.json
|
|
27
|
+
/gen-state-view # Generate state view slices from config.json
|
|
28
|
+
/gen-automation # Generate automation slices from config.json
|
|
29
|
+
/gen-ui # Set up React UI with shadcn/ui and Supabase
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## How to Invoke
|
|
33
|
+
|
|
34
|
+
### Using Event Model Skills
|
|
35
|
+
|
|
36
|
+
1. **Generate a State Change Slice (Command Handler)**
|
|
37
|
+
```
|
|
38
|
+
In Claude Code, type: /state-change-slice
|
|
39
|
+
```
|
|
40
|
+
Claude will:
|
|
41
|
+
- Find your event model file
|
|
42
|
+
- Ask you which command to implement
|
|
43
|
+
- Generate CommandHandler.ts with business logic
|
|
44
|
+
- Create routes.ts for HTTP endpoints
|
|
45
|
+
- Generate tests
|
|
46
|
+
- Run the tests automatically
|
|
47
|
+
|
|
48
|
+
2. **Generate a State View Slice (Read Model)**
|
|
49
|
+
```
|
|
50
|
+
In Claude Code, type: /state-view-slice
|
|
51
|
+
```
|
|
52
|
+
Claude will:
|
|
53
|
+
- Find your event model
|
|
54
|
+
- Ask which read model to implement
|
|
55
|
+
- Generate Projection.ts with event handlers
|
|
56
|
+
- Create query endpoints
|
|
57
|
+
- Generate tests
|
|
58
|
+
- Run the tests
|
|
59
|
+
|
|
60
|
+
3. **Generate an Automation Slice (Background Process)**
|
|
61
|
+
```
|
|
62
|
+
In Claude Code, type: /automation-slice
|
|
63
|
+
```
|
|
64
|
+
Claude will:
|
|
65
|
+
- Find your event model
|
|
66
|
+
- Ask which automation to implement
|
|
67
|
+
- Generate processor.ts with CRON logic
|
|
68
|
+
- Create background job handlers
|
|
69
|
+
- Generate tests
|
|
70
|
+
- Run the tests
|
|
71
|
+
|
|
72
|
+
### Using Yeoman Generator Skills
|
|
73
|
+
|
|
74
|
+
1. **Fetch Configuration from Event Model App**
|
|
75
|
+
```
|
|
76
|
+
In Claude Code, type: /fetch-config
|
|
77
|
+
```
|
|
78
|
+
This will start a server and fetch config.json from your running event model app.
|
|
79
|
+
|
|
80
|
+
2. **Generate Multiple Slices from Config**
|
|
81
|
+
```
|
|
82
|
+
In Claude Code, type: /gen-state-change
|
|
83
|
+
```
|
|
84
|
+
Or use `/gen-state-view` or `/gen-automation` to generate slices based on your config.json.
|
|
85
|
+
|
|
86
|
+
3. **Bootstrap a New Project**
|
|
87
|
+
```
|
|
88
|
+
In Claude Code, type: /gen-skeleton
|
|
89
|
+
```
|
|
90
|
+
This will create a complete Supabase backend skeleton.
|
|
91
|
+
|
|
92
|
+
4. **Set Up UI**
|
|
93
|
+
```
|
|
94
|
+
In Claude Code, type: /gen-ui
|
|
95
|
+
```
|
|
96
|
+
This will scaffold a React UI with shadcn/ui and Supabase integration.
|
|
97
|
+
|
|
98
|
+
## Project Structure
|
|
99
|
+
|
|
100
|
+
Your generated slices will follow this structure:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
src/slices/{slice-name}/
|
|
104
|
+
├── CommandHandler.ts # Business logic (state-change slices)
|
|
105
|
+
├── Projection.ts # Read model (state-view slices)
|
|
106
|
+
├── processor.ts # CRON automation (automation slices)
|
|
107
|
+
├── routes.ts # HTTP API endpoints
|
|
108
|
+
├── {SliceName}.test.ts # Automated tests
|
|
109
|
+
└── ui/ # UI components (optional)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Event Model Location
|
|
113
|
+
|
|
114
|
+
Make sure your event model JSON files are in one of these locations:
|
|
115
|
+
- `src/events/`
|
|
116
|
+
- `events/`
|
|
117
|
+
- Root directory
|
|
118
|
+
|
|
119
|
+
Claude Code will automatically find and read your event model.
|
|
120
|
+
|
|
121
|
+
## Template Files
|
|
122
|
+
|
|
123
|
+
This project includes the following template files:
|
|
124
|
+
|
|
125
|
+
- **`Claude.md`** - Project-specific configuration for Claude Code
|
|
126
|
+
- **`AGENTS.md`** - Track learnings and patterns from development
|
|
127
|
+
- **`ralph.sh`** - Autonomous agent loop for continuous development
|
|
128
|
+
- **`prompt.md`** - Agent instructions for automated development
|
|
129
|
+
|
|
130
|
+
## Development Workflow
|
|
131
|
+
|
|
132
|
+
1. **Define your event model** in JSON format
|
|
133
|
+
2. **Run a Claude skill** (e.g., `/state-change-slice`)
|
|
134
|
+
3. **Claude generates the code** with tests
|
|
135
|
+
4. **Tests run automatically** to verify correctness
|
|
136
|
+
5. **Iterate** - modify the model and regenerate as needed
|
|
137
|
+
|
|
138
|
+
## Autonomous Development with ralph.sh
|
|
139
|
+
|
|
140
|
+
Run the autonomous agent loop:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
./ralph.sh
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This will:
|
|
147
|
+
- Read instructions from `prompt.md`
|
|
148
|
+
- Execute development tasks automatically
|
|
149
|
+
- Track learnings in `AGENTS.md`
|
|
150
|
+
- Continue until the task is complete
|
|
151
|
+
|
|
152
|
+
## Need Help?
|
|
153
|
+
|
|
154
|
+
- Check the main EMBuilder documentation: https://github.com/dilgerma/embuilder
|
|
155
|
+
- Review the event model specifications in `src/events/`
|
|
156
|
+
- Ask Claude Code for guidance on any command
|
|
157
|
+
|
|
158
|
+
## EMBuilder Commands
|
|
159
|
+
|
|
160
|
+
If you need to reinstall or update EMBuilder:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Install/update skills
|
|
164
|
+
npx @dilgerma/embuilder install
|
|
165
|
+
|
|
166
|
+
# Install with template files
|
|
167
|
+
npx @dilgerma/embuilder install --with-templates
|
|
168
|
+
|
|
169
|
+
# Check installation status
|
|
170
|
+
npx @dilgerma/embuilder status
|
|
171
|
+
|
|
172
|
+
# Uninstall
|
|
173
|
+
npx @dilgerma/embuilder uninstall
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
Generated by EMBuilder - Event-Model driven development for Claude Code
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
SUPABASE_URL=https://<project-id>.supabase.co
|
|
2
|
+
SUPABASE_PUBLISHABLE_KEY=<supabase-publishable-key>
|
|
3
|
+
SUPABASE_DB_URL=postgresql://postgres.<project-id>:<database.password>@aws-1-eu-central-1.pooler.supabase.com:5432/postgres?prepareThreshold=0
|
|
4
|
+
SUPABASE_SECRET_KEY=<supabase-secret-key>
|
|
5
|
+
|
|
6
|
+
# Flyway configuration
|
|
7
|
+
FLYWAY_URL=jdbc:postgresql://aws-1-eu-central-1.pooler.supabase.com:6543/postgres?prepareThreshold=0
|
|
8
|
+
FLYWAY_USER=postgres.<project-id>
|
|
9
|
+
FLYWAY_PASSWORD=<database.password>
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Backend JWT Authentication Setup - Summary
|
|
2
|
+
|
|
3
|
+
## What Was Done
|
|
4
|
+
|
|
5
|
+
Your Express.js backend has been configured to use **JWT token authentication** instead of cookie-based sessions.
|
|
6
|
+
|
|
7
|
+
### Changes Made
|
|
8
|
+
|
|
9
|
+
#### 1. **Removed Cookie-Based Auth**
|
|
10
|
+
|
|
11
|
+
- Removed `cookie-parser` from server.ts
|
|
12
|
+
- Removed OAuth callback route (`/api/auth/confirm`)
|
|
13
|
+
- Removed old cookie-based Supabase client setup
|
|
14
|
+
|
|
15
|
+
#### 2. **Added JWT Token Verification**
|
|
16
|
+
|
|
17
|
+
- **`src/supabase/api.ts`**: Stateless Supabase client for JWT verification
|
|
18
|
+
- **`src/supabase/requireUser.ts`**: Extracts and verifies JWT from `Authorization` header
|
|
19
|
+
- **`src/supabase/authMiddleware.ts`**: Express middleware for protecting routes
|
|
20
|
+
- **`src/supabase/component.ts`**: Browser client for test login page
|
|
21
|
+
|
|
22
|
+
#### 3. **Updated API Endpoints**
|
|
23
|
+
|
|
24
|
+
- **`GET /api/user`**: Now requires `Authorization: Bearer <token>` header
|
|
25
|
+
- Returns user info: `userId`, `email`, `metadata`
|
|
26
|
+
|
|
27
|
+
#### 4. **Frontend Cleanup**
|
|
28
|
+
|
|
29
|
+
- **Removed**: `register.tsx`, `reset-password.tsx`
|
|
30
|
+
- **Updated**: `login.tsx` → Simple test page that displays JWT token
|
|
31
|
+
- Access at: http://localhost:3000/auth/login
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## How to Use
|
|
36
|
+
|
|
37
|
+
### 1. Get a JWT Token
|
|
38
|
+
|
|
39
|
+
**Option A: Use Test Login Page**
|
|
40
|
+
|
|
41
|
+
1. Start server: `npm run dev`
|
|
42
|
+
2. Visit: http://localhost:3000/auth/login
|
|
43
|
+
3. Create account or login
|
|
44
|
+
4. Copy the JWT token displayed
|
|
45
|
+
|
|
46
|
+
**Option B: Get Token from Client**
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const { data } = await supabase.auth.signInWithPassword({ email, password })
|
|
50
|
+
const token = data.session.access_token
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 2. Make Authenticated Requests
|
|
54
|
+
|
|
55
|
+
**Using cURL:**
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
|
|
59
|
+
http://localhost:3000/api/user
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Using JavaScript fetch:**
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
fetch('/api/user', {
|
|
66
|
+
headers: {
|
|
67
|
+
'Authorization': `Bearer ${token}`
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
.then(res => res.json())
|
|
71
|
+
.then(data => console.log(data))
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 3. Protect Your Routes
|
|
75
|
+
|
|
76
|
+
**Method 1: Using authMiddleware**
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import {authMiddleware} from './src/supabase/authMiddleware'
|
|
80
|
+
|
|
81
|
+
app.get('/api/protected', authMiddleware, (req, res) => {
|
|
82
|
+
const user = (req as any).user
|
|
83
|
+
res.json({user})
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Method 2: Using requireUser function**
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import {requireUser} from './src/supabase/requireUser'
|
|
91
|
+
|
|
92
|
+
app.get('/api/protected', async (req, res) => {
|
|
93
|
+
const result = await requireUser(req, res, false)
|
|
94
|
+
|
|
95
|
+
if (result.error) {
|
|
96
|
+
return res.status(401).json({error: result.error})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const user = result.user
|
|
100
|
+
res.json({user})
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## File Structure
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
src/supabase/
|
|
110
|
+
├── api.ts # Supabase client creation
|
|
111
|
+
├── requireUser.ts # JWT verification function
|
|
112
|
+
├── authMiddleware.ts # Express middleware
|
|
113
|
+
├── component.ts # Browser client (for test page)
|
|
114
|
+
├── supabaseClient.ts # Environment constants
|
|
115
|
+
└── README.md # Complete documentation
|
|
116
|
+
|
|
117
|
+
src/pages/auth/
|
|
118
|
+
└── login.tsx # Test login page with JWT display
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Environment Variables
|
|
124
|
+
|
|
125
|
+
Add to `.env.local`:
|
|
126
|
+
|
|
127
|
+
```env
|
|
128
|
+
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
|
|
129
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## API Responses
|
|
135
|
+
|
|
136
|
+
### Success (200)
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"userId": "550e8400-e29b-41d4-a716-446655440000",
|
|
141
|
+
"email": "user@example.com",
|
|
142
|
+
"metadata": {
|
|
143
|
+
"name": "John Doe"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Unauthorized (401)
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"error": "Missing authorization token"
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
or
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"error": "Invalid or expired token"
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Security Notes
|
|
167
|
+
|
|
168
|
+
✅ JWT tokens are verified with Supabase on every request
|
|
169
|
+
✅ No session storage on backend (stateless)
|
|
170
|
+
✅ Tokens expire automatically
|
|
171
|
+
⚠️ Always use HTTPS in production
|
|
172
|
+
⚠️ Keep environment variables secure
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Next Steps
|
|
177
|
+
|
|
178
|
+
1. Add your Supabase anon key to `.env.local`
|
|
179
|
+
2. Use `authMiddleware` or `requireUser` in your slice routes
|
|
180
|
+
3. Remove test login page in production if not needed
|
|
181
|
+
4. Consider adding role-based authorization if needed
|
|
182
|
+
|
|
183
|
+
For complete documentation, see: `src/supabase/README.md`
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Swagger UI & OpenAPI Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This project includes Swagger UI for interactive API documentation and OpenAPI specification generation.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
1. Start the development server:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm run dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
2. Open Swagger UI in your browser:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
http://localhost:3000/api-docs
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
3. View the OpenAPI JSON specification:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
http://localhost:3000/swagger.json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Documentation
|
|
28
|
+
|
|
29
|
+
The API documentation is automatically generated from JSDoc comments in your route files using `swagger-jsdoc`.
|
|
30
|
+
|
|
31
|
+
### Adding Documentation to Routes
|
|
32
|
+
|
|
33
|
+
Add JSDoc comments above your route handlers with OpenAPI/Swagger syntax:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
/**
|
|
37
|
+
* @swagger
|
|
38
|
+
* /api/query/shifts-collection:
|
|
39
|
+
* get:
|
|
40
|
+
* summary: Get shifts collection
|
|
41
|
+
* description: Retrieve all shifts or a specific shift by ID
|
|
42
|
+
* tags:
|
|
43
|
+
* - Shifts
|
|
44
|
+
* parameters:
|
|
45
|
+
* - in: query
|
|
46
|
+
* name: _id
|
|
47
|
+
* schema:
|
|
48
|
+
* type: string
|
|
49
|
+
* description: Optional shift ID to fetch a specific shift
|
|
50
|
+
* responses:
|
|
51
|
+
* 200:
|
|
52
|
+
* description: Successfully retrieved shifts
|
|
53
|
+
* content:
|
|
54
|
+
* application/json:
|
|
55
|
+
* schema:
|
|
56
|
+
* type: array
|
|
57
|
+
* items:
|
|
58
|
+
* type: object
|
|
59
|
+
* 500:
|
|
60
|
+
* description: Server error
|
|
61
|
+
*/
|
|
62
|
+
router.get('/api/query/shifts-collection', async (req, res) => {
|
|
63
|
+
// route implementation
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Common Swagger Components
|
|
68
|
+
|
|
69
|
+
#### Authentication (Bearer Token)
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
/**
|
|
73
|
+
* @swagger
|
|
74
|
+
* /api/protected-route:
|
|
75
|
+
* get:
|
|
76
|
+
* summary: Protected endpoint
|
|
77
|
+
* security:
|
|
78
|
+
* - bearerAuth: []
|
|
79
|
+
* responses:
|
|
80
|
+
* 200:
|
|
81
|
+
* description: Success
|
|
82
|
+
* 401:
|
|
83
|
+
* description: Unauthorized
|
|
84
|
+
*/
|
|
85
|
+
router.get('/api/protected-route', (req, res) => {
|
|
86
|
+
// implementation
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### POST Request with Body
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
/**
|
|
94
|
+
* @swagger
|
|
95
|
+
* /api/shifts:
|
|
96
|
+
* post:
|
|
97
|
+
* summary: Create a new shift
|
|
98
|
+
* tags:
|
|
99
|
+
* - Shifts
|
|
100
|
+
* requestBody:
|
|
101
|
+
* required: true
|
|
102
|
+
* content:
|
|
103
|
+
* application/json:
|
|
104
|
+
* schema:
|
|
105
|
+
* type: object
|
|
106
|
+
* properties:
|
|
107
|
+
* name:
|
|
108
|
+
* type: string
|
|
109
|
+
* start_time:
|
|
110
|
+
* type: string
|
|
111
|
+
* format: date-time
|
|
112
|
+
* end_time:
|
|
113
|
+
* type: string
|
|
114
|
+
* format: date-time
|
|
115
|
+
* responses:
|
|
116
|
+
* 201:
|
|
117
|
+
* description: Shift created successfully
|
|
118
|
+
* 400:
|
|
119
|
+
* description: Bad request
|
|
120
|
+
*/
|
|
121
|
+
router.post('/api/shifts', (req, res) => {
|
|
122
|
+
// implementation
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### Path Parameters
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
/**
|
|
130
|
+
* @swagger
|
|
131
|
+
* /api/shifts/{id}:
|
|
132
|
+
* get:
|
|
133
|
+
* summary: Get a specific shift
|
|
134
|
+
* parameters:
|
|
135
|
+
* - in: path
|
|
136
|
+
* name: id
|
|
137
|
+
* required: true
|
|
138
|
+
* schema:
|
|
139
|
+
* type: string
|
|
140
|
+
* description: Shift ID
|
|
141
|
+
* responses:
|
|
142
|
+
* 200:
|
|
143
|
+
* description: Shift found
|
|
144
|
+
* 404:
|
|
145
|
+
* description: Shift not found
|
|
146
|
+
*/
|
|
147
|
+
router.get('/api/shifts/:id', (req, res) => {
|
|
148
|
+
// implementation
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## File Locations
|
|
153
|
+
|
|
154
|
+
- **Swagger Configuration**: `src/swagger.ts`
|
|
155
|
+
- **Server Integration**: `server.ts` (lines 81-97)
|
|
156
|
+
- **Route Documentation**: Add to any route file in `src/slices/*/routes.ts`
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
The Swagger configuration is in `src/swagger.ts`:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const options = {
|
|
164
|
+
definition: {
|
|
165
|
+
openapi: '3.0.0',
|
|
166
|
+
info: {
|
|
167
|
+
title: 'Context API',
|
|
168
|
+
version: '1.0.0',
|
|
169
|
+
description: 'Event-driven API...',
|
|
170
|
+
},
|
|
171
|
+
servers: [
|
|
172
|
+
{
|
|
173
|
+
url: 'http://localhost:3000',
|
|
174
|
+
description: 'Development server',
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
apis: ['./src/slices/**/routes.ts'], // Auto-discovers routes
|
|
179
|
+
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Testing Your API
|
|
183
|
+
|
|
184
|
+
1. **In Swagger UI**: Click "Try it out" button to test endpoints
|
|
185
|
+
2. **With cURL**:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
curl http://localhost:3000/api/query/shifts-collection
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
3. **With Postman**: Import the OpenAPI spec from `/swagger.json`
|
|
192
|
+
|
|
193
|
+
## OpenAPI Spec Export
|
|
194
|
+
|
|
195
|
+
Download the full OpenAPI specification:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
curl http://localhost:3000/swagger.json > openapi.json
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Use it with:
|
|
202
|
+
|
|
203
|
+
- [Swagger Editor](https://editor.swagger.io/)
|
|
204
|
+
- [OpenAPI Tools](https://openapi.tools/)
|
|
205
|
+
- [Postman](https://www.postman.com/)
|
|
206
|
+
- [ReDoc](https://redoc.ly/)
|
|
207
|
+
|
|
208
|
+
## Resources
|
|
209
|
+
|
|
210
|
+
- [Swagger/OpenAPI Documentation](https://swagger.io/specification/)
|
|
211
|
+
- [swagger-jsdoc](https://github.com/Surnet/swagger-jsdoc)
|
|
212
|
+
- [swagger-ui-express](https://github.com/scottie1984/swagger-ui-express)
|
|
213
|
+
- [OpenAPI 3.0 Specification](https://spec.openapis.org/oas/v3.0.3)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {dirname} from "path";
|
|
2
|
+
import {fileURLToPath} from "url";
|
|
3
|
+
import {FlatCompat} from "@eslint/eslintrc";
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
|
|
8
|
+
const compat = new FlatCompat({
|
|
9
|
+
baseDirectory: __dirname,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const eslintConfig = [
|
|
13
|
+
...compat.extends("next/core-web-vitals", "next/typescript"),
|
|
14
|
+
{
|
|
15
|
+
rules: {
|
|
16
|
+
"no-unused-vars": "off", // Disable unused variable warnings
|
|
17
|
+
"@typescript-eslint/no-unused-vars": "off", // Also disable the TypeScript-specific version
|
|
18
|
+
"@typescript-eslint/no-restricted-types": [
|
|
19
|
+
"error",
|
|
20
|
+
{
|
|
21
|
+
"extendDefaults": true,
|
|
22
|
+
"types": {
|
|
23
|
+
"{}": false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Flyway configuration file
|
|
2
|
+
# Database connection from .env file
|
|
3
|
+
flyway.url=${FLYWAY_URL}
|
|
4
|
+
flyway.user=${FLYWAY_USER}
|
|
5
|
+
flyway.password=${FLYWAY_PASSWORD}
|
|
6
|
+
|
|
7
|
+
# Migration files location
|
|
8
|
+
flyway.locations=filesystem:./supabase/migrations
|
|
9
|
+
|
|
10
|
+
# Default schema (Flyway will create flyway_schema_history table here)
|
|
11
|
+
flyway.schemas=public
|
|
12
|
+
|
|
13
|
+
# Placeholder replacement
|
|
14
|
+
flyway.placeholderReplacement=false
|
|
15
|
+
|
|
16
|
+
# Validate on migrate
|
|
17
|
+
flyway.validateOnMigrate=true
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "project",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"flyway:migrate": "dotenv flyway migrate",
|
|
7
|
+
"dev": "node --env-file=.env --require ts-node/register server.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "NODE_ENV=production node --env-file=.env --require ts-node/register server.ts",
|
|
10
|
+
"test": "tsx --test 'src/**/*.test.ts'"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@event-driven-io/emmett": "^0.41.0",
|
|
14
|
+
"@event-driven-io/emmett-expressjs": "^0.41.0",
|
|
15
|
+
"@event-driven-io/emmett-postgresql": "^0.41.0",
|
|
16
|
+
"@supabase/ssr": "^0.6.1",
|
|
17
|
+
"@supabase/supabase-js": "^2.50.0",
|
|
18
|
+
"cookie-parser": "^1.4.7",
|
|
19
|
+
"cors": "^2.8.6",
|
|
20
|
+
"express": "^4.18.2",
|
|
21
|
+
"glob": "^11.0.3",
|
|
22
|
+
"knex": "^3.1.0",
|
|
23
|
+
"node-cron": "^4.2.1",
|
|
24
|
+
"pg": "^8.17.2",
|
|
25
|
+
"swagger-jsdoc": "^6.2.8",
|
|
26
|
+
"swagger-ui-express": "^5.0.1",
|
|
27
|
+
"url": "^0.11.4"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@eslint/eslintrc": "^3",
|
|
31
|
+
"@testcontainers/postgresql": "^11.0.3",
|
|
32
|
+
"@types/cors": "^2.8.19",
|
|
33
|
+
"@types/express": "^4.17.21",
|
|
34
|
+
"@types/node": "^20",
|
|
35
|
+
"@types/swagger-jsdoc": "^6.0.4",
|
|
36
|
+
"@types/swagger-ui-express": "^4.1.8",
|
|
37
|
+
"dotenv-cli": "^11.0.0",
|
|
38
|
+
"eslint": "^9",
|
|
39
|
+
"sql-formatter": "^15.7.0",
|
|
40
|
+
"ts-node": "^10.9.2",
|
|
41
|
+
"tsx": "^4.20.3",
|
|
42
|
+
"typescript": "^5"
|
|
43
|
+
}
|
|
44
|
+
}
|