@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.
Files changed (212) hide show
  1. package/README.md +254 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +138 -0
  4. package/package.json +49 -0
  5. package/templates/.claude/hooks/QUICKSTART.md +256 -0
  6. package/templates/.claude/hooks/README.md +533 -0
  7. package/templates/.claude/hooks/analyze-commit.sh +22 -0
  8. package/templates/.claude/hooks/analyze-commit.ts +518 -0
  9. package/templates/.claude/hooks/analyzers/README.md +198 -0
  10. package/templates/.claude/hooks/analyzers/code-quality-checker.ts +154 -0
  11. package/templates/.claude/hooks/analyzers/code-quality.md +54 -0
  12. package/templates/.claude/hooks/analyzers/commit-blocker-example.ts.disabled +110 -0
  13. package/templates/.claude/hooks/analyzers/commit-policy.md +49 -0
  14. package/templates/.claude/hooks/analyzers/event-model-validator.md +49 -0
  15. package/templates/.claude/hooks/analyzers/event-model-validator.ts +169 -0
  16. package/templates/.claude/hooks/analyzers/example-logger.ts +70 -0
  17. package/templates/.claude/hooks/analyzers/slice-scope-validator.md +81 -0
  18. package/templates/.claude/hooks/check-review-result.sh +47 -0
  19. package/templates/.claude/hooks/prepare-review.sh +34 -0
  20. package/templates/.claude/hooks/review-agent-prompt.md +42 -0
  21. package/templates/.claude/hooks/run-review-agent.sh +124 -0
  22. package/templates/.claude/settings.local.json +37 -0
  23. package/templates/.claude/skills/help/README.md +84 -0
  24. package/templates/.claude/skills/help/SKILL.md +393 -0
  25. package/templates/.claude/skills/help/templates/demo-config.json +6753 -0
  26. package/templates/.claude/skills/sample-slices/SKILL.md +8 -0
  27. package/templates/.claude/skills/sample-slices/templates/.slices/Library/addbook/code-slice.json +124 -0
  28. package/templates/.claude/skills/sample-slices/templates/.slices/Library/addbook/slice.json +255 -0
  29. package/templates/.claude/skills/sample-slices/templates/.slices/Library/availablebooks/slice.json +107 -0
  30. package/templates/.claude/skills/sample-slices/templates/.slices/index.json +20 -0
  31. package/templates/.claude/skills/sample-slices/templates/Cart/additem/slice.json +979 -0
  32. package/templates/.claude/skills/sample-slices/templates/Cart/archiveitem/slice.json +529 -0
  33. package/templates/.claude/skills/sample-slices/templates/Cart/cartitems/slice.json +1072 -0
  34. package/templates/.claude/skills/sample-slices/templates/Cart/cartwithproducts/slice.json +394 -0
  35. package/templates/.claude/skills/sample-slices/templates/Cart/changedprices/slice.json +88 -0
  36. package/templates/.claude/skills/sample-slices/templates/Cart/changeinventory/slice.json +264 -0
  37. package/templates/.claude/skills/sample-slices/templates/Cart/changeprice/slice.json +308 -0
  38. package/templates/.claude/skills/sample-slices/templates/Cart/clearcart/slice.json +358 -0
  39. package/templates/.claude/skills/sample-slices/templates/Cart/inventories/slice.json +203 -0
  40. package/templates/.claude/skills/sample-slices/templates/Cart/publishcart/slice.json +876 -0
  41. package/templates/.claude/skills/sample-slices/templates/Cart/removeitem/slice.json +560 -0
  42. package/templates/.claude/skills/sample-slices/templates/Cart/submitcart/slice.json +708 -0
  43. package/templates/.claude/skills/sample-slices/templates/Cart/submittedcartdata/slice.json +399 -0
  44. package/templates/.claude/skills/sample-slices/templates/index.json +108 -0
  45. package/templates/.claude/skills/slice-automation/SKILL.md +49 -0
  46. package/templates/.claude/skills/slice-state-change/SKILL.md +369 -0
  47. package/templates/.claude/skills/slice-state-change/templates/AddLocation/AddLocation.test.ts.sample +76 -0
  48. package/templates/.claude/skills/slice-state-change/templates/AddLocation/AddLocationCommand.ts.sample +84 -0
  49. package/templates/.claude/skills/slice-state-change/templates/AddLocation/routes.ts.sample +73 -0
  50. package/templates/.claude/skills/slice-state-change/templates/README.md +46 -0
  51. package/templates/.claude/skills/slice-state-view/SKILL.md +336 -0
  52. package/templates/.claude/skills/slice-state-view/templates/Locations/Locations.test.ts.sample +84 -0
  53. package/templates/.claude/skills/slice-state-view/templates/Locations/LocationsProjection.ts.sample +50 -0
  54. package/templates/.claude/skills/slice-state-view/templates/Locations/routes.ts.sample +46 -0
  55. package/templates/.claude/skills/slice-state-view/templates/README.md +109 -0
  56. package/templates/.claude/skills/slice-state-view/templates/Tables/Tables.test.ts.sample +104 -0
  57. package/templates/.claude/skills/slice-state-view/templates/Tables/TablesProjection.ts.sample +59 -0
  58. package/templates/.claude/skills/slice-state-view/templates/Tables/routes.ts.sample +46 -0
  59. package/templates/.claude/skills/slice-state-view/templates/V2__tables.sql +7 -0
  60. package/templates/.claude/skills/slice-state-view/templates/V8__locations.sql +7 -0
  61. package/templates/.claude/skills/test-analyzer/SKILL.md +373 -0
  62. package/templates/.claude/skills/test-analyzer/examples/specification-format.md +143 -0
  63. package/templates/.claude/skills/test-analyzer/examples/state-change-example.md +111 -0
  64. package/templates/.claude/skills/test-analyzer/examples/state-view-example.md +122 -0
  65. package/templates/AGENTS.md +110 -0
  66. package/templates/Claude.md +58 -0
  67. package/templates/README.md +178 -0
  68. package/templates/backend/.env +9 -0
  69. package/templates/backend/BACKEND_AUTH_SETUP.md +183 -0
  70. package/templates/backend/SWAGGER.md +213 -0
  71. package/templates/backend/eslint.config.mjs +31 -0
  72. package/templates/backend/flyway.conf +17 -0
  73. package/templates/backend/package.json +44 -0
  74. package/templates/backend/prd.json.example +64 -0
  75. package/templates/backend/public/assets/images/banner.png +0 -0
  76. package/templates/backend/public/assets/logo.png +0 -0
  77. package/templates/backend/public/file.svg +4 -0
  78. package/templates/backend/public/globe.svg +12 -0
  79. package/templates/backend/public/next.svg +6 -0
  80. package/templates/backend/public/vercel.svg +3 -0
  81. package/templates/backend/public/window.svg +5 -0
  82. package/templates/backend/server.ts +129 -0
  83. package/templates/backend/setup-env.sh +50 -0
  84. package/templates/backend/src/common/assertions.ts +6 -0
  85. package/templates/backend/src/common/db.ts +1 -0
  86. package/templates/backend/src/common/loadPostgresEventstore.ts +16 -0
  87. package/templates/backend/src/common/parseEndpoint.ts +51 -0
  88. package/templates/backend/src/common/replay.ts +9 -0
  89. package/templates/backend/src/common/routes.ts +19 -0
  90. package/templates/backend/src/common/testHelpers.ts +53 -0
  91. package/templates/backend/src/core/readmodel.ts +28 -0
  92. package/templates/backend/src/core/types.ts +26 -0
  93. package/templates/backend/src/process/process.ts +53 -0
  94. package/templates/backend/src/supabase/LoginHandler.ts +36 -0
  95. package/templates/backend/src/supabase/ProtectedPageProps.ts +21 -0
  96. package/templates/backend/src/supabase/README.md +171 -0
  97. package/templates/backend/src/supabase/api.ts +63 -0
  98. package/templates/backend/src/supabase/authMiddleware.ts +53 -0
  99. package/templates/backend/src/supabase/component.ts +12 -0
  100. package/templates/backend/src/supabase/requireUser.ts +72 -0
  101. package/templates/backend/src/supabase/serverProps.ts +25 -0
  102. package/templates/backend/src/supabase/staticProps.ts +10 -0
  103. package/templates/backend/src/swagger.ts +34 -0
  104. package/templates/backend/src/util/assertions.ts +6 -0
  105. package/templates/backend/supabase/config.toml +295 -0
  106. package/templates/backend/supabase/migrations/20260121155918593_catalogentries.sql.sample +23 -0
  107. package/templates/backend/supabase/seed.sql +1 -0
  108. package/templates/backend/tsconfig.json +31 -0
  109. package/templates/frontend/.env.development +3 -0
  110. package/templates/frontend/AGENTS.md +7 -0
  111. package/templates/frontend/README.md +73 -0
  112. package/templates/frontend/components.json +20 -0
  113. package/templates/frontend/eslint.config.js +26 -0
  114. package/templates/frontend/index.html +18 -0
  115. package/templates/frontend/package-lock.json +8347 -0
  116. package/templates/frontend/package.json +94 -0
  117. package/templates/frontend/postcss.config.js +6 -0
  118. package/templates/frontend/public/favicon.ico +0 -0
  119. package/templates/frontend/public/logo.png +0 -0
  120. package/templates/frontend/public/placeholder.svg +1 -0
  121. package/templates/frontend/public/robots.txt +14 -0
  122. package/templates/frontend/src/App.css +42 -0
  123. package/templates/frontend/src/App.tsx +47 -0
  124. package/templates/frontend/src/components/NavLink.tsx +28 -0
  125. package/templates/frontend/src/components/ProtectedRoute.tsx +24 -0
  126. package/templates/frontend/src/components/calendar/Calendar.tsx +302 -0
  127. package/templates/frontend/src/components/layout/DashboardLayout.tsx +21 -0
  128. package/templates/frontend/src/components/layout/Header.tsx +45 -0
  129. package/templates/frontend/src/components/layout/Sidebar.tsx +82 -0
  130. package/templates/frontend/src/components/tables/ReservationTemplates.tsx +189 -0
  131. package/templates/frontend/src/components/ui/accordion.tsx +52 -0
  132. package/templates/frontend/src/components/ui/alert-dialog.tsx +104 -0
  133. package/templates/frontend/src/components/ui/alert.tsx +43 -0
  134. package/templates/frontend/src/components/ui/aspect-ratio.tsx +5 -0
  135. package/templates/frontend/src/components/ui/avatar.tsx +38 -0
  136. package/templates/frontend/src/components/ui/badge.tsx +29 -0
  137. package/templates/frontend/src/components/ui/breadcrumb.tsx +90 -0
  138. package/templates/frontend/src/components/ui/button.tsx +47 -0
  139. package/templates/frontend/src/components/ui/calendar.tsx +54 -0
  140. package/templates/frontend/src/components/ui/card.tsx +43 -0
  141. package/templates/frontend/src/components/ui/carousel.tsx +224 -0
  142. package/templates/frontend/src/components/ui/chart.tsx +303 -0
  143. package/templates/frontend/src/components/ui/checkbox.tsx +26 -0
  144. package/templates/frontend/src/components/ui/collapsible.tsx +9 -0
  145. package/templates/frontend/src/components/ui/command.tsx +132 -0
  146. package/templates/frontend/src/components/ui/context-menu.tsx +178 -0
  147. package/templates/frontend/src/components/ui/dialog.tsx +95 -0
  148. package/templates/frontend/src/components/ui/drawer.tsx +87 -0
  149. package/templates/frontend/src/components/ui/dropdown-menu.tsx +179 -0
  150. package/templates/frontend/src/components/ui/form.tsx +129 -0
  151. package/templates/frontend/src/components/ui/hover-card.tsx +27 -0
  152. package/templates/frontend/src/components/ui/input-otp.tsx +61 -0
  153. package/templates/frontend/src/components/ui/input.tsx +22 -0
  154. package/templates/frontend/src/components/ui/label.tsx +17 -0
  155. package/templates/frontend/src/components/ui/menubar.tsx +207 -0
  156. package/templates/frontend/src/components/ui/navigation-menu.tsx +120 -0
  157. package/templates/frontend/src/components/ui/pagination.tsx +81 -0
  158. package/templates/frontend/src/components/ui/popover.tsx +29 -0
  159. package/templates/frontend/src/components/ui/progress.tsx +23 -0
  160. package/templates/frontend/src/components/ui/radio-group.tsx +36 -0
  161. package/templates/frontend/src/components/ui/resizable.tsx +37 -0
  162. package/templates/frontend/src/components/ui/scroll-area.tsx +38 -0
  163. package/templates/frontend/src/components/ui/select.tsx +143 -0
  164. package/templates/frontend/src/components/ui/separator.tsx +20 -0
  165. package/templates/frontend/src/components/ui/sheet.tsx +107 -0
  166. package/templates/frontend/src/components/ui/sidebar.tsx +637 -0
  167. package/templates/frontend/src/components/ui/skeleton.tsx +7 -0
  168. package/templates/frontend/src/components/ui/slider.tsx +23 -0
  169. package/templates/frontend/src/components/ui/sonner.tsx +27 -0
  170. package/templates/frontend/src/components/ui/stat-card.tsx +44 -0
  171. package/templates/frontend/src/components/ui/switch.tsx +27 -0
  172. package/templates/frontend/src/components/ui/table.tsx +72 -0
  173. package/templates/frontend/src/components/ui/tabs.tsx +53 -0
  174. package/templates/frontend/src/components/ui/textarea.tsx +21 -0
  175. package/templates/frontend/src/components/ui/toast.tsx +111 -0
  176. package/templates/frontend/src/components/ui/toaster.tsx +24 -0
  177. package/templates/frontend/src/components/ui/toggle-group.tsx +49 -0
  178. package/templates/frontend/src/components/ui/toggle.tsx +37 -0
  179. package/templates/frontend/src/components/ui/tooltip.tsx +28 -0
  180. package/templates/frontend/src/components/ui/use-toast.ts +3 -0
  181. package/templates/frontend/src/contexts/AuthContext.tsx +94 -0
  182. package/templates/frontend/src/contexts/RefreshContext.tsx +236 -0
  183. package/templates/frontend/src/hooks/api/index.ts +2 -0
  184. package/templates/frontend/src/hooks/api/useLocations.ts +15 -0
  185. package/templates/frontend/src/hooks/use-mobile.tsx +19 -0
  186. package/templates/frontend/src/hooks/use-toast.ts +186 -0
  187. package/templates/frontend/src/hooks/useApiContext.ts +11 -0
  188. package/templates/frontend/src/index.css +118 -0
  189. package/templates/frontend/src/integrations/supabase/client.ts +9 -0
  190. package/templates/frontend/src/lib/api-client.ts +136 -0
  191. package/templates/frontend/src/lib/api.ts +1028 -0
  192. package/templates/frontend/src/lib/utils.ts +6 -0
  193. package/templates/frontend/src/main.tsx +5 -0
  194. package/templates/frontend/src/pages/Auth.tsx +408 -0
  195. package/templates/frontend/src/pages/Dashboard.tsx +168 -0
  196. package/templates/frontend/src/pages/Menus.tsx +224 -0
  197. package/templates/frontend/src/pages/NotFound.tsx +24 -0
  198. package/templates/frontend/src/pages/Register.tsx +285 -0
  199. package/templates/frontend/src/test/example.test.ts +0 -0
  200. package/templates/frontend/src/test/setup.ts +15 -0
  201. package/templates/frontend/src/types/index.ts +8 -0
  202. package/templates/frontend/src/vite-env.d.ts +1 -0
  203. package/templates/frontend/tailwind.config.ts +101 -0
  204. package/templates/frontend/tsconfig.app.json +31 -0
  205. package/templates/frontend/tsconfig.json +16 -0
  206. package/templates/frontend/tsconfig.node.json +22 -0
  207. package/templates/frontend/vite.config.ts +21 -0
  208. package/templates/frontend/vitest.config.ts +16 -0
  209. package/templates/init.sh +1 -0
  210. package/templates/prompt.md +139 -0
  211. package/templates/ralph.sh +120 -0
  212. package/templates/server.mjs +505 -0
@@ -0,0 +1,171 @@
1
+ # Supabase JWT Authentication for Backend API
2
+
3
+ This backend API uses Supabase JWT tokens for authentication. Clients must include a valid JWT token in the
4
+ `Authorization` header.
5
+
6
+ ## Quick Start
7
+
8
+ 1. **Set up environment variables** in `.env.local`:
9
+ ```env
10
+ NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
11
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here
12
+ ```
13
+
14
+ 2. **Start the server**: `npm run dev`
15
+
16
+ 3. **Get a test JWT token**:
17
+ - Visit http://localhost:3000/auth/login
18
+ - Create an account or login
19
+ - Copy the JWT token displayed
20
+ - Use it in your API requests
21
+
22
+ ## Test Login Page
23
+
24
+ A simple test login page is available at `/auth/login` that:
25
+
26
+ - Allows you to create accounts or login
27
+ - Displays the JWT token after authentication
28
+ - Provides a "Test API Call" button
29
+ - Shows a cURL example for testing
30
+
31
+ This page is for testing purposes only.
32
+
33
+ ## How It Works
34
+
35
+ 1. **Client obtains JWT token** from Supabase (via your frontend app)
36
+ 2. **Client sends requests** with `Authorization: Bearer <jwt-token>` header
37
+ 3. **Backend verifies JWT** using Supabase and extracts user info
38
+ 4. **Protected routes** return user data or 401 Unauthorized
39
+
40
+ ## Usage Examples
41
+
42
+ ### Option 1: Using `requireUser` function
43
+
44
+ ```typescript
45
+ import {requireUser} from './src/supabase/requireUser';
46
+ import {Request, Response} from 'express';
47
+
48
+ app.get('/api/protected', async (req: Request, res: Response) => {
49
+ const result = await requireUser(req, res, false);
50
+
51
+ if (result.error) {
52
+ return res.status(401).json({error: result.error});
53
+ }
54
+
55
+ const user = result.user;
56
+ res.json({
57
+ message: 'Protected data',
58
+ userId: user.id,
59
+ email: user.email
60
+ });
61
+ });
62
+ ```
63
+
64
+ ### Option 2: Using `authMiddleware`
65
+
66
+ ```typescript
67
+ import {authMiddleware} from './src/supabase/authMiddleware';
68
+
69
+ app.get('/api/protected', authMiddleware, (req, res) => {
70
+ // User is available on req.user after middleware
71
+ const user = (req as any).user;
72
+
73
+ res.json({
74
+ message: 'Protected data',
75
+ user: user
76
+ });
77
+ });
78
+ ```
79
+
80
+ ### Testing with curl
81
+
82
+ ```bash
83
+ # Get JWT token from your Supabase client first
84
+ TOKEN="your-jwt-token-here"
85
+
86
+ # Test protected endpoint
87
+ curl -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/user
88
+
89
+ # Expected response:
90
+ # {
91
+ # "userId": "...",
92
+ # "email": "user@example.com",
93
+ # "metadata": { ... }
94
+ # }
95
+ ```
96
+
97
+ ### Testing with JavaScript fetch
98
+
99
+ ```javascript
100
+ const token = supabase.auth.session()?.access_token;
101
+
102
+ fetch('http://localhost:3000/api/user', {
103
+ headers: {
104
+ 'Authorization': `Bearer ${token}`
105
+ }
106
+ })
107
+ .then(res => res.json())
108
+ .then(data => console.log(data));
109
+ ```
110
+
111
+ ## API Endpoints
112
+
113
+ ### `GET /api/user`
114
+
115
+ Returns current authenticated user information.
116
+
117
+ **Headers:**
118
+
119
+ - `Authorization: Bearer <jwt-token>` (required)
120
+
121
+ **Success Response (200):**
122
+
123
+ ```json
124
+ {
125
+ "userId": "uuid",
126
+ "email": "user@example.com",
127
+ "metadata": { ... }
128
+ }
129
+ ```
130
+
131
+ **Error Responses:**
132
+
133
+ - `401 Unauthorized`: Missing or invalid token
134
+ - `500 Internal Server Error`: Server error
135
+
136
+ ## Files
137
+
138
+ - **`api.ts`**: Supabase client creation
139
+ - **`requireUser.ts`**: JWT verification function
140
+ - **`authMiddleware.ts`**: Express middleware for protecting routes
141
+ - **`README.md`**: This documentation
142
+
143
+ ## Architecture
144
+
145
+ ```
146
+ Client Request
147
+ |
148
+ v
149
+ Authorization: Bearer <JWT>
150
+ |
151
+ v
152
+ Express Route
153
+ |
154
+ v
155
+ requireUser() / authMiddleware
156
+ |
157
+ v
158
+ Supabase JWT Verification
159
+ |
160
+ +---> Valid: Continue with user data
161
+ |
162
+ +---> Invalid: Return 401 Unauthorized
163
+ ```
164
+
165
+ ## Security Notes
166
+
167
+ - JWT tokens are verified with Supabase on every request
168
+ - No session storage on the backend (stateless)
169
+ - Tokens expire based on Supabase configuration
170
+ - Always use HTTPS in production
171
+ - Store the anon key securely (use environment variables)
@@ -0,0 +1,63 @@
1
+ import {createClient as createSupabaseClient} from '@supabase/supabase-js'
2
+ import {Request} from 'express'
3
+
4
+ /**
5
+ * Creates a Supabase client for server-side API operations
6
+ * This client does not persist sessions and is used for JWT verification
7
+ */
8
+ export const createServiceClient = () => {
9
+ console.log(process.env.SUPABASE_URL)
10
+ return createSupabaseClient(
11
+ process.env.SUPABASE_URL!,
12
+ process.env.SUPABASE_SECRET_KEY!
13
+ )
14
+ }
15
+
16
+ /**
17
+ * Creates a Supabase client for verifying JWT tokens
18
+ * Used in backend API endpoints
19
+ */
20
+ export default function createClient() {
21
+ return createSupabaseClient(
22
+ process.env.SUPABASE_URL!,
23
+ process.env.SUPABASE_PUBLISHABLE_KEY!,
24
+ {
25
+ auth: {
26
+ persistSession: false,
27
+ autoRefreshToken: false,
28
+ detectSessionInUrl: false,
29
+ },
30
+ }
31
+ )
32
+ }
33
+
34
+ /**
35
+ * Creates an authenticated Supabase client with the user's JWT token
36
+ * This ensures Row Level Security (RLS) policies are applied with the user's context
37
+ */
38
+ export async function createAuthenticatedClient(req: Request) {
39
+ const token = req.headers.authorization?.replace('Bearer ', '') || '';
40
+
41
+ console.log(token)
42
+ console.log(process.env.SUPABASE_URL!)
43
+ const client = createSupabaseClient(
44
+ process.env.SUPABASE_URL!,
45
+ process.env.SUPABASE_PUBLISHABLE_KEY!,
46
+ {
47
+ auth: {
48
+ persistSession: false,
49
+ autoRefreshToken: false,
50
+ detectSessionInUrl: false,
51
+ },
52
+ }
53
+ );
54
+
55
+ if (token) {
56
+ await client.auth.setSession({
57
+ access_token: token,
58
+ refresh_token: '',
59
+ });
60
+ }
61
+
62
+ return client;
63
+ }
@@ -0,0 +1,53 @@
1
+ import {NextFunction, Request, Response} from 'express';
2
+ import {requireUser} from './requireUser';
3
+
4
+ /**
5
+ * Express middleware to protect routes with JWT authentication
6
+ * Verifies the JWT token from Authorization header and attaches user to request
7
+ *
8
+ * Usage:
9
+ * ```typescript
10
+ * import {authMiddleware} from './src/supabase/authMiddleware';
11
+ *
12
+ * app.get('/api/protected', authMiddleware, (req, res) => {
13
+ * const user = req.user; // TypeScript: use custom type definition
14
+ * res.json({ message: 'Protected data', user });
15
+ * });
16
+ * ```
17
+ */
18
+ export async function authMiddleware(req: Request, res: Response, next: NextFunction) {
19
+ try {
20
+ const result = await requireUser(req, res, false);
21
+
22
+ if (result.error) {
23
+ return res.status(401).json({
24
+ error: 'Unauthorized',
25
+ message: result.error
26
+ });
27
+ }
28
+
29
+ // Attach user to request for downstream handlers
30
+ (req as any).user = result.user;
31
+
32
+ next();
33
+ } catch (error) {
34
+ console.error('Auth middleware error:', error);
35
+ return res.status(500).json({
36
+ error: 'Internal server error',
37
+ message: 'Authentication failed'
38
+ });
39
+ }
40
+ }
41
+
42
+ /**
43
+ * TypeScript declaration to extend Express Request type
44
+ * Add this to your types file or use it directly:
45
+ *
46
+ * declare global {
47
+ * namespace Express {
48
+ * interface Request {
49
+ * user?: any; // Or use your User type
50
+ * }
51
+ * }
52
+ * }
53
+ */
@@ -0,0 +1,12 @@
1
+ import {createBrowserClient} from '@supabase/ssr'
2
+
3
+ /**
4
+ * Creates a Supabase client for browser/client-side operations
5
+ * Used in React components for authentication flows
6
+ */
7
+ export function createClient() {
8
+ return createBrowserClient(
9
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
10
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
11
+ )
12
+ }
@@ -0,0 +1,72 @@
1
+ import {createAuthenticatedClient} from "./api";
2
+ import {Request, Response} from "express"
3
+
4
+ type RequireUserResult = {
5
+ user: any;
6
+ error: null;
7
+ } | {
8
+ user: null;
9
+ error: string;
10
+ };
11
+
12
+ /**
13
+ * Extracts JWT token from Authorization header
14
+ * Supports "Bearer <token>" format
15
+ */
16
+ function extractTokenFromHeader(req: Request): string | null {
17
+ const authHeader = req.headers.authorization;
18
+
19
+ if (!authHeader) {
20
+ return null;
21
+ }
22
+
23
+ // Check for "Bearer <token>" format
24
+ const parts = authHeader.split(' ');
25
+ if (parts.length === 2 && parts[0].toLowerCase() === 'bearer') {
26
+ return parts[1];
27
+ }
28
+
29
+ // If no Bearer prefix, assume the entire header is the token
30
+ return authHeader;
31
+ }
32
+
33
+ /**
34
+ * Verifies JWT token from Authorization header and returns user info
35
+ * This is for backend API use - does not use cookies or redirects
36
+ */
37
+ export async function requireUser(req: Request, resp: Response, sendUnauthorized: boolean = true): Promise<RequireUserResult> {
38
+ const token = extractTokenFromHeader(req);
39
+
40
+ if (!token) {
41
+ if (sendUnauthorized) {
42
+ resp.status(401).json({error: 'Missing authorization token'});
43
+ }
44
+ return {
45
+ user: null,
46
+ error: 'MISSING_TOKEN',
47
+ };
48
+ }
49
+
50
+ const supabase = await createAuthenticatedClient(req);
51
+
52
+ // Verify the JWT token
53
+ const {
54
+ data: {user},
55
+ error
56
+ } = await supabase.auth.getUser(token);
57
+
58
+ if (error || !user) {
59
+ if (sendUnauthorized) {
60
+ resp.status(401).json({error: 'Invalid or expired token'});
61
+ }
62
+ return {
63
+ user: null,
64
+ error: error?.message || 'UNAUTHORIZED',
65
+ };
66
+ }
67
+
68
+ return {
69
+ user: user,
70
+ error: null
71
+ }
72
+ }
@@ -0,0 +1,25 @@
1
+ import {type Request, type Response} from 'express'
2
+ import {createServerClient, serializeCookieHeader} from '@supabase/ssr'
3
+
4
+ export function createClient(req: Request, res: Response) {
5
+ const supabase = createServerClient(
6
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
7
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
8
+ {
9
+ cookies: {
10
+ getAll() {
11
+ return Object.keys(req.cookies).map((name) => ({name, value: req.cookies[name] || ''}))
12
+ },
13
+ setAll(cookiesToSet: any[]) {
14
+ res.setHeader(
15
+ 'Set-Cookie',
16
+ cookiesToSet.map(({name, value, options}: any) =>
17
+ serializeCookieHeader(name, value, options)
18
+ )
19
+ )
20
+ },
21
+ },
22
+ }
23
+ )
24
+ return supabase
25
+ }
@@ -0,0 +1,10 @@
1
+ import {createClient as createClientPrimitive} from '@supabase/supabase-js'
2
+
3
+ export function createClient() {
4
+ const supabase = createClientPrimitive(
5
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
6
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
7
+ )
8
+
9
+ return supabase
10
+ }
@@ -0,0 +1,34 @@
1
+ import swaggerJsdoc from 'swagger-jsdoc';
2
+
3
+ const options = {
4
+ definition: {
5
+ openapi: '3.0.0',
6
+ info: {
7
+ title: 'Context API',
8
+ version: '1.0.0',
9
+ description: 'Event-driven API for shift, clerk, and task management',
10
+ },
11
+ servers: [
12
+ {
13
+ url: 'http://localhost:3000',
14
+ description: 'Development server',
15
+ },
16
+ {
17
+ url: process.env.API_URL || 'http://localhost:3000',
18
+ description: 'Production server',
19
+ },
20
+ ],
21
+ components: {
22
+ securitySchemes: {
23
+ bearerAuth: {
24
+ type: 'http',
25
+ scheme: 'bearer',
26
+ bearerFormat: 'JWT',
27
+ },
28
+ },
29
+ },
30
+ },
31
+ apis: ['./src/slices/**/routes.ts'],
32
+ };
33
+
34
+ export const specs = swaggerJsdoc(options);
@@ -0,0 +1,6 @@
1
+ export function assertNotEmpty<T>(value: T): NonNullable<T> {
2
+ if (value === null || value === undefined) {
3
+ throw new Error("Expected non-empty value");
4
+ }
5
+ return value!!;
6
+ }