umpordez 0.0.2 → 1.0.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.
Files changed (151) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/build.mjs +409 -0
  3. package/cli.mjs +87 -109
  4. package/create.mjs +334 -0
  5. package/package.json +2 -7
  6. package/template/.claude/settings.local.json +20 -0
  7. package/template/.editorconfig +11 -0
  8. package/template/.github/copilot-instructions.md +186 -0
  9. package/template/.nvimrc +7 -0
  10. package/template/.vscode/extensions.json +9 -0
  11. package/template/.vscode/settings.json +28 -0
  12. package/template/AGENTS.md +305 -0
  13. package/template/CLAUDE.md +140 -0
  14. package/template/architecture.md +518 -0
  15. package/template/code.md +211 -0
  16. package/template/dev.sh +45 -0
  17. package/template/install.sh +47 -0
  18. package/template/misc/nginx.conf +151 -0
  19. package/template/misc/systemd/{{PROJECT_SLUG}}-api.service +31 -0
  20. package/template/misc/systemd/{{PROJECT_SLUG}}-site-api.service +31 -0
  21. package/template/misc/systemd/{{PROJECT_SLUG}}-site.service +30 -0
  22. package/template/seed.sh +23 -0
  23. package/template/server/.env.sample +40 -0
  24. package/template/server/.prettierrc +4 -0
  25. package/template/server/apps/api/app.ts +42 -0
  26. package/template/server/apps/api/routes/account.ts +98 -0
  27. package/template/server/apps/api/routes/admin.ts +80 -0
  28. package/template/server/apps/api/routes/auth.ts +222 -0
  29. package/template/server/apps/api/routes/files.ts +89 -0
  30. package/template/server/apps/api/routes/index.ts +19 -0
  31. package/template/server/apps/api/routes/user.ts +17 -0
  32. package/template/server/apps/shared/middlewares/demand-account-access.ts +52 -0
  33. package/template/server/apps/shared/middlewares/demand-admin-user.ts +25 -0
  34. package/template/server/apps/shared/middlewares/demand-user.ts +17 -0
  35. package/template/server/apps/shared/middlewares/error-handler.ts +28 -0
  36. package/template/server/apps/shared/middlewares/request-logger.ts +40 -0
  37. package/template/server/apps/shared/middlewares/try-set-user-by-token.ts +26 -0
  38. package/template/server/apps/shared/utils.ts +71 -0
  39. package/template/server/apps/site-api/app.ts +42 -0
  40. package/template/server/apps/site-api/routes/index.ts +10 -0
  41. package/template/server/apps/site-api/routes/public.ts +25 -0
  42. package/template/server/console/index.ts +26 -0
  43. package/template/server/console/runner.ts +67 -0
  44. package/template/server/console/tasks/example.ts +29 -0
  45. package/template/server/console/tasks/seed-admin.ts +71 -0
  46. package/template/server/core/context.ts +49 -0
  47. package/template/server/core/db.ts +12 -0
  48. package/template/server/core/email.ts +87 -0
  49. package/template/server/core/errors.ts +44 -0
  50. package/template/server/core/knexfile.ts +28 -0
  51. package/template/server/core/logger.ts +60 -0
  52. package/template/server/core/models/account.ts +319 -0
  53. package/template/server/core/models/auth.ts +317 -0
  54. package/template/server/core/models/base.ts +19 -0
  55. package/template/server/core/models/user.ts +343 -0
  56. package/template/server/core/s3.ts +183 -0
  57. package/template/server/emails/_styles.css +5 -0
  58. package/template/server/emails/_template.html +28 -0
  59. package/template/server/emails/accountWelcome.html +4 -0
  60. package/template/server/emails/forgetPassword.html +5 -0
  61. package/template/server/eslint.config.js +16 -0
  62. package/template/server/knex.sh +4 -0
  63. package/template/server/migrations/20260208000000_initial-schema.ts +56 -0
  64. package/template/server/migrations/20260208000001_seed-admin.ts +42 -0
  65. package/template/server/migrations/20260208000002_add-user-avatar.ts +13 -0
  66. package/template/server/package.json +58 -0
  67. package/template/server/tsconfig.json +27 -0
  68. package/template/server/types/api.ts +20 -0
  69. package/template/server/types/express.d.ts +35 -0
  70. package/template/ui/admin/.prettierrc +4 -0
  71. package/template/ui/admin/components.json +20 -0
  72. package/template/ui/admin/eslint.config.js +16 -0
  73. package/template/ui/admin/index.html +12 -0
  74. package/template/ui/admin/package.json +57 -0
  75. package/template/ui/admin/postcss.config.js +6 -0
  76. package/template/ui/admin/src/app.tsx +13 -0
  77. package/template/ui/admin/src/components/nav-user.tsx +95 -0
  78. package/template/ui/admin/src/components/theme-provider.tsx +65 -0
  79. package/template/ui/admin/src/components/theme-toggle.tsx +17 -0
  80. package/template/ui/admin/src/components/ui/badge.tsx +36 -0
  81. package/template/ui/admin/src/components/ui/button.tsx +56 -0
  82. package/template/ui/admin/src/components/ui/calendar.tsx +68 -0
  83. package/template/ui/admin/src/components/ui/card.tsx +79 -0
  84. package/template/ui/admin/src/components/ui/checkbox.tsx +28 -0
  85. package/template/ui/admin/src/components/ui/date-picker.tsx +78 -0
  86. package/template/ui/admin/src/components/ui/date-range-picker.tsx +208 -0
  87. package/template/ui/admin/src/components/ui/dialog.tsx +121 -0
  88. package/template/ui/admin/src/components/ui/dropdown-menu.tsx +200 -0
  89. package/template/ui/admin/src/components/ui/input.tsx +22 -0
  90. package/template/ui/admin/src/components/ui/label.tsx +24 -0
  91. package/template/ui/admin/src/components/ui/popover.tsx +29 -0
  92. package/template/ui/admin/src/components/ui/select.tsx +158 -0
  93. package/template/ui/admin/src/components/ui/separator.tsx +29 -0
  94. package/template/ui/admin/src/components/ui/sheet.tsx +136 -0
  95. package/template/ui/admin/src/components/ui/sidebar.tsx +759 -0
  96. package/template/ui/admin/src/components/ui/skeleton.tsx +15 -0
  97. package/template/ui/admin/src/components/ui/sonner.tsx +26 -0
  98. package/template/ui/admin/src/components/ui/switch.tsx +27 -0
  99. package/template/ui/admin/src/components/ui/table.tsx +117 -0
  100. package/template/ui/admin/src/components/ui/tabs.tsx +53 -0
  101. package/template/ui/admin/src/components/ui/textarea.tsx +22 -0
  102. package/template/ui/admin/src/components/ui/tooltip.tsx +28 -0
  103. package/template/ui/admin/src/hooks/queries/use-accounts.ts +68 -0
  104. package/template/ui/admin/src/hooks/queries/use-auth.ts +61 -0
  105. package/template/ui/admin/src/hooks/queries/use-dashboard.ts +70 -0
  106. package/template/ui/admin/src/hooks/queries/use-members.ts +90 -0
  107. package/template/ui/admin/src/hooks/queries/use-users.ts +99 -0
  108. package/template/ui/admin/src/hooks/use-confirm.tsx +92 -0
  109. package/template/ui/admin/src/hooks/use-mobile.tsx +22 -0
  110. package/template/ui/admin/src/hooks/use-sidebar.tsx +4 -0
  111. package/template/ui/admin/src/hooks/use-user.tsx +86 -0
  112. package/template/ui/admin/src/index.css +22 -0
  113. package/template/ui/admin/src/layouts/account.tsx +278 -0
  114. package/template/ui/admin/src/layouts/admin.tsx +182 -0
  115. package/template/ui/admin/src/layouts/public.tsx +9 -0
  116. package/template/ui/admin/src/layouts/user.tsx +107 -0
  117. package/template/ui/admin/src/lib/config.ts +9 -0
  118. package/template/ui/admin/src/lib/fetch-api.ts +101 -0
  119. package/template/ui/admin/src/lib/query-client.ts +12 -0
  120. package/template/ui/admin/src/lib/query-keys.ts +44 -0
  121. package/template/ui/admin/src/lib/utils.ts +24 -0
  122. package/template/ui/admin/src/main.tsx +17 -0
  123. package/template/ui/admin/src/pages/account/dashboard.tsx +128 -0
  124. package/template/ui/admin/src/pages/account/members.tsx +414 -0
  125. package/template/ui/admin/src/pages/account/settings.tsx +210 -0
  126. package/template/ui/admin/src/pages/admin/accounts.tsx +164 -0
  127. package/template/ui/admin/src/pages/admin/dashboard.tsx +243 -0
  128. package/template/ui/admin/src/pages/admin/users.tsx +395 -0
  129. package/template/ui/admin/src/pages/public/error.tsx +24 -0
  130. package/template/ui/admin/src/pages/public/forgot-password.tsx +74 -0
  131. package/template/ui/admin/src/pages/public/login.tsx +102 -0
  132. package/template/ui/admin/src/pages/public/reset-password.tsx +106 -0
  133. package/template/ui/admin/src/pages/public/signup.tsx +118 -0
  134. package/template/ui/admin/src/pages/user/profile.tsx +313 -0
  135. package/template/ui/admin/src/pages/user/select-account.tsx +114 -0
  136. package/template/ui/admin/src/router.tsx +71 -0
  137. package/template/ui/admin/tailwind.config.ts +71 -0
  138. package/template/ui/admin/tsconfig.json +24 -0
  139. package/template/ui/admin/vite.config.ts +15 -0
  140. package/template/ui/site/.env.sample +3 -0
  141. package/template/ui/site/.prettierrc +4 -0
  142. package/template/ui/site/app.ts +50 -0
  143. package/template/ui/site/eslint.config.js +16 -0
  144. package/template/ui/site/package.json +33 -0
  145. package/template/ui/site/styles/input.css +3 -0
  146. package/template/ui/site/tailwind.config.js +8 -0
  147. package/template/ui/site/tsconfig.json +15 -0
  148. package/template/ui/site/views/pages/home.ejs +305 -0
  149. package/template/ui/site/views/partials/footer.ejs +44 -0
  150. package/template/ui/site/views/partials/header.ejs +83 -0
  151. package/template-variables.mjs +186 -0
@@ -0,0 +1,211 @@
1
+ # Code Standards
2
+
3
+ This document defines the coding standards for the project. All code
4
+ must follow these rules. Read `architecture.md` for architecture
5
+ patterns and how-tos.
6
+
7
+ ---
8
+
9
+ ## 1. Formatting
10
+
11
+ - **4-space indentation**, semicolons, LF line endings
12
+ - **No trailing commas** — not in arrays `[foo]`, objects `{foo}`,
13
+ function calls `(foo)`, or parameters. Never `(foo,)` or `[foo,]`
14
+ - **Max 80 columns** per line — especially for `if` conditions,
15
+ function signatures, and long strings. Break across lines:
16
+
17
+ ```typescript
18
+ // bad
19
+ if (userRole !== 'owner' && userRole !== 'admin' && userRole !== 'manager') {
20
+
21
+ // good
22
+ if (
23
+ userRole !== 'owner'
24
+ && userRole !== 'admin'
25
+ && userRole !== 'manager'
26
+ ) {
27
+ ```
28
+
29
+ ## 2. Braces
30
+
31
+ **Always use braces** for `if`, `else`, `for`, `while`.
32
+ No single-line bodies without braces.
33
+
34
+ ```typescript
35
+ // bad
36
+ if (foo) bar();
37
+ if (foo)
38
+ bar();
39
+ for (const x of arr) doSomething(x);
40
+
41
+ // good
42
+ if (foo) {
43
+ bar();
44
+ }
45
+
46
+ for (const x of arr) {
47
+ doSomething(x);
48
+ }
49
+ ```
50
+
51
+ ## 3. Error Handling
52
+
53
+ **Never silently ignore errors.** Every `catch` block must at
54
+ minimum log the error with `logger.error()`:
55
+
56
+ ```typescript
57
+ // bad
58
+ try {
59
+ await deleteFromS3(key);
60
+ } catch {
61
+ // ignore
62
+ }
63
+
64
+ // bad
65
+ try {
66
+ await deleteFromS3(key);
67
+ } catch (ex) { }
68
+
69
+ // good
70
+ try {
71
+ await deleteFromS3(key);
72
+ } catch (err) {
73
+ logger.error('{context} failed to delete file', err);
74
+ }
75
+ ```
76
+
77
+ In route handlers, let errors propagate to `buildHandler()` — it
78
+ catches and formats them automatically. Only use try/catch in routes
79
+ when you need to handle a specific error differently.
80
+
81
+ ## 4. TypeScript
82
+
83
+ - **TypeScript strict** in all packages
84
+ - Use explicit types for function parameters and return types on
85
+ exported functions
86
+ - Prefer `interface` over `type` for object shapes
87
+ - Use `unknown` over `any` when the type is truly unknown
88
+ - Use Zod schemas for runtime validation of external input
89
+ (request bodies, query params)
90
+
91
+ ## 5. Language
92
+
93
+ - **Code in English** — variable names, function names, comments,
94
+ logs, error messages in code
95
+ - **UI text in Portuguese** — user-facing strings, labels, toast
96
+ messages, validation messages shown to users
97
+ - Error messages thrown by models should be in Portuguese (they're
98
+ sent to the frontend as-is)
99
+
100
+ ## 6. Naming
101
+
102
+ | Type | Convention | Example |
103
+ |-------------|--------------|-------------------------------|
104
+ | Components | PascalCase | `NavUser.tsx` |
105
+ | Hooks | kebab-case | `use-user.tsx` |
106
+ | Query hooks | kebab-case | `use-accounts.ts` |
107
+ | Utils/Lib | kebab-case | `fetch-api.ts` |
108
+ | Routes | kebab-case | `demand-user.ts` |
109
+ | Models | kebab-case | `account.ts` |
110
+ | Migrations | timestamp | `20260208000000_description.ts`|
111
+ | DB columns | snake_case | `utc_created_on` |
112
+ | TS vars | camelCase | `accountRole` |
113
+
114
+ ## 7. Path Aliases
115
+
116
+ - Server: `#core/*`, `#shared/*`, `#api/*`
117
+ - Admin UI: `@/*` (maps to `src/`)
118
+
119
+ Always use path aliases — never relative paths with `../../`.
120
+
121
+ ## 8. Database
122
+
123
+ - **Raw SQL** in migrations (use `knex.schema.raw()`)
124
+ - Every table must have: `id` (UUID), `utc_created_on`, `utc_updated_on`
125
+ - Multi-tenant tables must have `account_id` with a foreign key
126
+ - Use `generate_series()` for date-based aggregations
127
+ - Filename format: `YYYYMMDDHHMMSS_description.ts`
128
+ - Never store sensitive data unencrypted (passwords use bcrypt)
129
+
130
+ ## 9. Models
131
+
132
+ - Extend `BaseModel`, access DB via `this.knex`
133
+ - Access other models via `this.context.otherModel`
134
+ - Always filter by `account_id` in multi-tenant queries
135
+ - Use `PaginatedResult<T>`: `{ rows, total, page, limit, totalPages }`
136
+ - Use `whereILike` for case-insensitive search
137
+ - Parse JSON fields when reading, `JSON.stringify()` when writing
138
+ - Throw `AppError` subclasses: `ValidationError` (400),
139
+ `UnauthorizedError` (401), `ForbiddenError` (403),
140
+ `NotFoundError` (404), `ConflictError` (409)
141
+
142
+ ## 10. Routes
143
+
144
+ - Use `buildHandler()` wrapper for all async handlers
145
+ - Export `makeEndpoint(app)` or `makeRoutes(app)`
146
+ - Use Zod schemas for all request body validation
147
+ - Middleware chain order:
148
+ `trySetUserByToken` → `demandUser` → `[demandAdmin|demandAccountAccess]`
149
+ - Never do raw DB queries in routes — use models
150
+
151
+ ## 11. Frontend
152
+
153
+ - Use `fetchApi()` for all API calls (handles cookies, errors, FormData)
154
+ - Use React Query (`useQuery` / `useMutation`) for all server state
155
+ - Use `keepPreviousData` on paginated/searchable queries
156
+ - Add query keys to `lib/query-keys.ts`
157
+ - Invalidate related queries in mutation `onSuccess`
158
+ - Use `useConfirm()` instead of `window.confirm()`
159
+ - Use `toast` from sonner for feedback messages
160
+
161
+ ## 12. File Uploads (S3)
162
+
163
+ - Store only the S3 **key** in the database (`avatar_key`,
164
+ `document_key`), never the full URL
165
+ - Serve files via **signed URLs** with short expiry (1 hour)
166
+ - Never expose raw S3 URLs to the frontend — always resolve to
167
+ signed URLs on the backend before sending
168
+ - Use `createS3Upload()` with explicit `allowedMimeTypes` and
169
+ `maxFileSize` — never accept all file types
170
+ - Delete old files from S3 when replacing (e.g. avatar update)
171
+
172
+ ## 13. Charts
173
+
174
+ Use pure CSS/Tailwind bars with Radix Tooltip — no recharts library
175
+ needed (even though it's installed). Pattern:
176
+
177
+ ```tsx
178
+ <TooltipProvider delayDuration={0}>
179
+ <div className="flex items-end gap-1 h-32">
180
+ {data.map((point) => (
181
+ <Tooltip key={point.id}>
182
+ <TooltipTrigger asChild>
183
+ <div
184
+ className="flex-1 rounded-sm bg-primary
185
+ cursor-pointer transition-opacity
186
+ hover:opacity-80"
187
+ style={{ height: `${height}%` }}
188
+ />
189
+ </TooltipTrigger>
190
+ <TooltipContent>
191
+ <p>{point.label}</p>
192
+ <p className="text-lg font-bold">{point.value}</p>
193
+ </TooltipContent>
194
+ </Tooltip>
195
+ ))}
196
+ </div>
197
+ </TooltipProvider>
198
+ ```
199
+
200
+ Calculate `maxValue` from data, derive bar heights as percentages
201
+ with a minimum of 4% so empty bars are still visible.
202
+
203
+ ## 14. Security
204
+
205
+ - Auth via httpOnly cookies only — no tokens in localStorage
206
+ - All S3 files served through signed URLs (never public-read)
207
+ - Validate all input with Zod before processing
208
+ - Use parameterized queries (Knex handles this) — never
209
+ string-concatenate SQL
210
+ - Admin users bypass `user_in_accounts` but still go through
211
+ `demand-account-access` middleware which sets `req.accountRole`
@@ -0,0 +1,45 @@
1
+ #!/bin/bash
2
+
3
+ echo "Starting All Services"
4
+ echo "====================="
5
+ echo ""
6
+ echo "Starting services:"
7
+ echo " - Admin API (port {{API_PORT}})"
8
+ echo " - Site API (port {{SITE_API_PORT}})"
9
+ echo " - Admin Panel (port {{ADMIN_UI_PORT}})"
10
+ echo " - Site (port {{SITE_PORT}})"
11
+ echo ""
12
+ echo "Press Ctrl+C to stop all services"
13
+ echo ""
14
+
15
+ trap 'kill $(jobs -p) 2>/dev/null' EXIT
16
+
17
+ if [ ! -f server/.env ]; then
18
+ echo "Warning: server/.env not found"
19
+ echo "Run ./install.sh first"
20
+ exit 1
21
+ fi
22
+
23
+ echo "Starting Admin API..."
24
+ (cd server && npm run dev) &
25
+
26
+ echo "Starting Site API..."
27
+ (cd server && npm run dev:site-api) &
28
+
29
+ echo "Starting Admin Panel..."
30
+ (cd ui/admin && npm run dev) &
31
+
32
+ echo "Starting Site..."
33
+ (cd ui/site && npm run dev) &
34
+
35
+ echo ""
36
+ echo "All services started!"
37
+ echo ""
38
+ echo "URLs:"
39
+ echo " - Site: http://localhost:{{SITE_PORT}}"
40
+ echo " - Admin Panel: http://localhost:{{ADMIN_UI_PORT}}"
41
+ echo " - Admin API: http://localhost:{{API_PORT}}"
42
+ echo " - Site API: http://localhost:{{SITE_API_PORT}}"
43
+ echo ""
44
+
45
+ wait
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+
3
+ echo "Installing Dependencies"
4
+ echo "======================="
5
+ echo ""
6
+
7
+ echo "Installing server dependencies..."
8
+ (cd server && npm install)
9
+ echo "Server dependencies installed"
10
+ echo ""
11
+
12
+ echo "Installing admin UI dependencies..."
13
+ (cd ui/admin && npm install)
14
+ echo "Admin UI dependencies installed"
15
+ echo ""
16
+
17
+ echo "Installing site UI dependencies..."
18
+ (cd ui/site && npm install)
19
+ echo "Site UI dependencies installed"
20
+ echo ""
21
+
22
+ echo "Setting up environment files..."
23
+ echo ""
24
+
25
+ if [ ! -f server/.env ]; then
26
+ cp server/.env.sample server/.env
27
+ echo "Created server/.env"
28
+ else
29
+ echo "server/.env already exists"
30
+ fi
31
+
32
+ if [ ! -f ui/site/.env ]; then
33
+ cp ui/site/.env.sample ui/site/.env
34
+ echo "Created ui/site/.env"
35
+ else
36
+ echo "ui/site/.env already exists"
37
+ fi
38
+
39
+ echo ""
40
+ echo "======================="
41
+ echo "Installation Complete!"
42
+ echo ""
43
+ echo "Next steps:"
44
+ echo "1. Edit server/.env with your database credentials"
45
+ echo "2. ./seed.sh (create db + migrate + seed admin)"
46
+ echo "3. ./dev.sh (start all services)"
47
+ echo ""
@@ -0,0 +1,151 @@
1
+ # {{PROJECT_NAME}} — Nginx Configuration
2
+ #
3
+ # Setup:
4
+ # 1. Copy this file to /etc/nginx/sites-available/{{PROJECT_SLUG}}
5
+ # 2. ln -s /etc/nginx/sites-available/{{PROJECT_SLUG}} /etc/nginx/sites-enabled/
6
+ # 3. Replace {{DOMAIN}} with your actual domain
7
+ # 4. Run: sudo certbot --nginx -d {{DOMAIN}} -d app.{{DOMAIN}} -d api.{{DOMAIN}}
8
+ # 5. sudo nginx -t && sudo systemctl reload nginx
9
+
10
+ # ─── Upstreams ───────────────────────────────────────────────────────────────
11
+
12
+ upstream api {
13
+ ip_hash;
14
+ server 127.0.0.1:{{API_PORT}};
15
+ }
16
+
17
+ upstream site_api {
18
+ ip_hash;
19
+ server 127.0.0.1:{{SITE_API_PORT}};
20
+ }
21
+
22
+ upstream site {
23
+ ip_hash;
24
+ server 127.0.0.1:{{SITE_PORT}};
25
+ }
26
+
27
+ # ─── Admin API (api.{{DOMAIN}}) ──────────────────────────────────────────────
28
+
29
+ server {
30
+ listen 443 ssl;
31
+ server_name api.{{DOMAIN}};
32
+
33
+ ssl_certificate /etc/letsencrypt/live/{{DOMAIN}}/fullchain.pem;
34
+ ssl_certificate_key /etc/letsencrypt/live/{{DOMAIN}}/privkey.pem;
35
+ include /etc/letsencrypt/options-ssl-nginx.conf;
36
+ ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
37
+
38
+ client_max_body_size 50M;
39
+
40
+ location / {
41
+ proxy_pass http://api;
42
+ proxy_http_version 1.1;
43
+ proxy_set_header Upgrade $http_upgrade;
44
+ proxy_set_header Connection "upgrade";
45
+ proxy_set_header Host $host;
46
+ proxy_set_header X-Real-IP $remote_addr;
47
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
48
+ proxy_set_header X-Forwarded-Proto $scheme;
49
+ proxy_read_timeout 86400s;
50
+ proxy_send_timeout 86400s;
51
+ }
52
+ }
53
+
54
+ # ─── Site API (api-site.{{DOMAIN}}) ──────────────────────────────────────────
55
+
56
+ server {
57
+ listen 443 ssl;
58
+ server_name api-site.{{DOMAIN}};
59
+
60
+ ssl_certificate /etc/letsencrypt/live/{{DOMAIN}}/fullchain.pem;
61
+ ssl_certificate_key /etc/letsencrypt/live/{{DOMAIN}}/privkey.pem;
62
+ include /etc/letsencrypt/options-ssl-nginx.conf;
63
+ ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
64
+
65
+ client_max_body_size 50M;
66
+
67
+ location / {
68
+ proxy_pass http://site_api;
69
+ proxy_http_version 1.1;
70
+ proxy_set_header Upgrade $http_upgrade;
71
+ proxy_set_header Connection "upgrade";
72
+ proxy_set_header Host $host;
73
+ proxy_set_header X-Real-IP $remote_addr;
74
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
75
+ proxy_set_header X-Forwarded-Proto $scheme;
76
+ }
77
+ }
78
+
79
+ # ─── Admin Panel (app.{{DOMAIN}}) ────────────────────────────────────────────
80
+ # React SPA — static files served by nginx directly
81
+
82
+ server {
83
+ listen 443 ssl;
84
+ server_name app.{{DOMAIN}};
85
+
86
+ ssl_certificate /etc/letsencrypt/live/{{DOMAIN}}/fullchain.pem;
87
+ ssl_certificate_key /etc/letsencrypt/live/{{DOMAIN}}/privkey.pem;
88
+ include /etc/letsencrypt/options-ssl-nginx.conf;
89
+ ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
90
+
91
+ root /apps/{{PROJECT_SLUG}}/builds/ui/admin/dist;
92
+ index index.html;
93
+
94
+ location / {
95
+ try_files $uri $uri/ /index.html;
96
+ }
97
+
98
+ # Cache static assets
99
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
100
+ expires 1y;
101
+ add_header Cache-Control "public, immutable";
102
+ }
103
+ }
104
+
105
+ # ─── Public Site ({{DOMAIN}}) ────────────────────────────────────────────────
106
+
107
+ server {
108
+ listen 443 ssl;
109
+ server_name {{DOMAIN}};
110
+
111
+ ssl_certificate /etc/letsencrypt/live/{{DOMAIN}}/fullchain.pem;
112
+ ssl_certificate_key /etc/letsencrypt/live/{{DOMAIN}}/privkey.pem;
113
+ include /etc/letsencrypt/options-ssl-nginx.conf;
114
+ ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
115
+
116
+ location / {
117
+ proxy_pass http://site;
118
+ proxy_http_version 1.1;
119
+ proxy_set_header Host $host;
120
+ proxy_set_header X-Real-IP $remote_addr;
121
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
122
+ proxy_set_header X-Forwarded-Proto $scheme;
123
+ }
124
+ }
125
+
126
+ # ─── WWW Redirect ────────────────────────────────────────────────────────────
127
+
128
+ server {
129
+ listen 443 ssl;
130
+ server_name www.{{DOMAIN}};
131
+
132
+ ssl_certificate /etc/letsencrypt/live/{{DOMAIN}}/fullchain.pem;
133
+ ssl_certificate_key /etc/letsencrypt/live/{{DOMAIN}}/privkey.pem;
134
+
135
+ return 301 https://{{DOMAIN}}$request_uri;
136
+ }
137
+
138
+ # ─── HTTP → HTTPS Redirect ──────────────────────────────────────────────────
139
+
140
+ server {
141
+ listen 80;
142
+ server_name {{DOMAIN}} www.{{DOMAIN}} app.{{DOMAIN}} api.{{DOMAIN}} api-site.{{DOMAIN}};
143
+
144
+ location /.well-known/acme-challenge/ {
145
+ root /var/www/html;
146
+ }
147
+
148
+ location / {
149
+ return 301 https://$host$request_uri;
150
+ }
151
+ }
@@ -0,0 +1,31 @@
1
+ # {{PROJECT_NAME}} — Admin API Service
2
+ #
3
+ # Setup:
4
+ # 1. Copy to /etc/systemd/system/{{PROJECT_SLUG}}-api.service
5
+ # 2. sudo systemctl daemon-reload
6
+ # 3. sudo systemctl enable {{PROJECT_SLUG}}-api
7
+ # 4. sudo systemctl start {{PROJECT_SLUG}}-api
8
+ #
9
+ # Logs:
10
+ # journalctl -u {{PROJECT_SLUG}}-api -f
11
+ # tail -f /apps/{{PROJECT_SLUG}}/log/api.log
12
+
13
+ [Unit]
14
+ Description={{PROJECT_NAME}} Admin API
15
+ After=network.target postgresql.service
16
+ Wants=postgresql.service
17
+
18
+ [Service]
19
+ Type=simple
20
+ User=ubuntu
21
+ Group=ubuntu
22
+ WorkingDirectory=/apps/{{PROJECT_SLUG}}/builds/server
23
+ ExecStart=/usr/bin/node apps/api/app.js
24
+ Restart=always
25
+ RestartSec=5
26
+ StandardOutput=append:/apps/{{PROJECT_SLUG}}/log/api.log
27
+ StandardError=append:/apps/{{PROJECT_SLUG}}/log/api.log
28
+ Environment=NODE_ENV=production
29
+
30
+ [Install]
31
+ WantedBy=multi-user.target
@@ -0,0 +1,31 @@
1
+ # {{PROJECT_NAME}} — Site API Service
2
+ #
3
+ # Setup:
4
+ # 1. Copy to /etc/systemd/system/{{PROJECT_SLUG}}-site-api.service
5
+ # 2. sudo systemctl daemon-reload
6
+ # 3. sudo systemctl enable {{PROJECT_SLUG}}-site-api
7
+ # 4. sudo systemctl start {{PROJECT_SLUG}}-site-api
8
+ #
9
+ # Logs:
10
+ # journalctl -u {{PROJECT_SLUG}}-site-api -f
11
+ # tail -f /apps/{{PROJECT_SLUG}}/log/site-api.log
12
+
13
+ [Unit]
14
+ Description={{PROJECT_NAME}} Site API
15
+ After=network.target postgresql.service
16
+ Wants=postgresql.service
17
+
18
+ [Service]
19
+ Type=simple
20
+ User=ubuntu
21
+ Group=ubuntu
22
+ WorkingDirectory=/apps/{{PROJECT_SLUG}}/builds/server
23
+ ExecStart=/usr/bin/node apps/site-api/app.js
24
+ Restart=always
25
+ RestartSec=5
26
+ StandardOutput=append:/apps/{{PROJECT_SLUG}}/log/site-api.log
27
+ StandardError=append:/apps/{{PROJECT_SLUG}}/log/site-api.log
28
+ Environment=NODE_ENV=production
29
+
30
+ [Install]
31
+ WantedBy=multi-user.target
@@ -0,0 +1,30 @@
1
+ # {{PROJECT_NAME}} — Public Site Service
2
+ #
3
+ # Setup:
4
+ # 1. Copy to /etc/systemd/system/{{PROJECT_SLUG}}-site.service
5
+ # 2. sudo systemctl daemon-reload
6
+ # 3. sudo systemctl enable {{PROJECT_SLUG}}-site
7
+ # 4. sudo systemctl start {{PROJECT_SLUG}}-site
8
+ #
9
+ # Logs:
10
+ # journalctl -u {{PROJECT_SLUG}}-site -f
11
+ # tail -f /apps/{{PROJECT_SLUG}}/log/site.log
12
+
13
+ [Unit]
14
+ Description={{PROJECT_NAME}} Public Site
15
+ After=network.target
16
+
17
+ [Service]
18
+ Type=simple
19
+ User=ubuntu
20
+ Group=ubuntu
21
+ WorkingDirectory=/apps/{{PROJECT_SLUG}}/builds/ui/site
22
+ ExecStart=/usr/bin/node app.js
23
+ Restart=always
24
+ RestartSec=5
25
+ StandardOutput=append:/apps/{{PROJECT_SLUG}}/log/site.log
26
+ StandardError=append:/apps/{{PROJECT_SLUG}}/log/site.log
27
+ Environment=NODE_ENV=production
28
+
29
+ [Install]
30
+ WantedBy=multi-user.target
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+
3
+ echo "Database Setup"
4
+ echo "=============="
5
+ echo ""
6
+
7
+ DB_NAME="{{DB_NAME}}"
8
+
9
+ echo "Creating database '$DB_NAME'..."
10
+ createdb "$DB_NAME" 2>/dev/null && echo "Database created" || echo "Database already exists (or failed)"
11
+ echo ""
12
+
13
+ echo "Running migrations..."
14
+ (cd server && npm run migrate:latest)
15
+ echo ""
16
+
17
+ echo "Seeding admin user..."
18
+ (cd server && npm run console -- --task=seed-admin)
19
+ echo ""
20
+
21
+ echo "=============="
22
+ echo "Database ready!"
23
+ echo ""
@@ -0,0 +1,40 @@
1
+ # Server — Admin API
2
+ PORT={{API_PORT}}
3
+ NODE_ENV=development
4
+
5
+ # Server — Site API (optional second API)
6
+ SITE_API_PORT={{SITE_API_PORT}}
7
+
8
+ # URLs
9
+ DOMAIN=http://localhost:{{ADMIN_UI_PORT}}
10
+ SITE_DOMAIN=http://localhost:{{SITE_PORT}}
11
+ BASE_UI_URL=http://localhost:{{ADMIN_UI_PORT}}
12
+ BASE_API_URL=http://localhost:{{API_PORT}}
13
+ BASE_SITE_API_URL=http://localhost:{{SITE_API_PORT}}
14
+
15
+ # Database (PostgreSQL)
16
+ DB_HOST=localhost
17
+ DB_PORT=5432
18
+ DB_NAME={{DB_NAME}}
19
+ DB_USER=postgres
20
+ DB_PASSWORD=postgres
21
+
22
+ # Authentication
23
+ JWT_TOKEN_SECRET=your-secret-key-change-this-in-production
24
+ COOKIE_DOMAIN=
25
+
26
+ # Email
27
+ EMAIL_HOST=smtp.example.com
28
+ EMAIL_PORT=465
29
+ EMAIL_SECURE=true
30
+ EMAIL_USER=your-email@example.com
31
+ EMAIL_PASSWORD=your-email-password
32
+ EMAIL_FROM=noreply@{{DOMAIN}}
33
+ EMAIL_NAME={{PROJECT_NAME}}
34
+ EMAIL_REPLY_TO=suporte@{{DOMAIN}}
35
+
36
+ # AWS S3
37
+ AWS_REGION=us-east-1
38
+ AWS_ACCESS_KEY_ID=your-access-key
39
+ AWS_SECRET_ACCESS_KEY=your-secret-key
40
+ S3_BUCKET={{PROJECT_SLUG}}-files
@@ -0,0 +1,4 @@
1
+ {
2
+ "tabWidth": 4,
3
+ "semi": true
4
+ }
@@ -0,0 +1,42 @@
1
+ import dotenv from 'dotenv';
2
+ dotenv.config();
3
+
4
+ import express from 'express';
5
+ import cors from 'cors';
6
+ import cookieParser from 'cookie-parser';
7
+ import Context from '#core/context.js';
8
+ import logger from '#core/logger.js';
9
+ import makeRoutes from './routes/index.js';
10
+ import requestLoggerMiddleware from '#shared/middlewares/request-logger.js';
11
+ import errorHandler from '#shared/middlewares/error-handler.js';
12
+
13
+ const app = express();
14
+ const port = parseInt(process.env.PORT || '{{API_PORT}}', 10);
15
+
16
+ app.use(cors({
17
+ origin: process.env.DOMAIN || 'http://localhost:{{ADMIN_UI_PORT}}',
18
+ credentials: true
19
+ }));
20
+
21
+ app.use(cookieParser());
22
+ app.use(express.json({ limit: '10mb' }));
23
+ app.use(express.urlencoded({ extended: true }));
24
+ app.use(requestLoggerMiddleware);
25
+
26
+ // Context injection — fresh Context per request
27
+ app.use((req: express.Request, _res: express.Response, next: express.NextFunction) => {
28
+ req.context = new Context();
29
+ next();
30
+ });
31
+
32
+ // Mount all routes
33
+ makeRoutes(app);
34
+
35
+ // Error handler (must be last)
36
+ app.use(errorHandler);
37
+
38
+ app.listen(port, () => {
39
+ logger.info(`Admin API server running on port ${port}`);
40
+ });
41
+
42
+ export default app;