iec-cli 0.6.6

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 (443) hide show
  1. package/README.md +159 -0
  2. package/dist/commands/backup.d.ts +11 -0
  3. package/dist/commands/backup.d.ts.map +1 -0
  4. package/dist/commands/backup.js +112 -0
  5. package/dist/commands/backup.js.map +1 -0
  6. package/dist/commands/config.d.ts +28 -0
  7. package/dist/commands/config.d.ts.map +1 -0
  8. package/dist/commands/config.js +190 -0
  9. package/dist/commands/config.js.map +1 -0
  10. package/dist/commands/deploy.d.ts +16 -0
  11. package/dist/commands/deploy.d.ts.map +1 -0
  12. package/dist/commands/deploy.js +235 -0
  13. package/dist/commands/deploy.js.map +1 -0
  14. package/dist/commands/destroy.d.ts +12 -0
  15. package/dist/commands/destroy.d.ts.map +1 -0
  16. package/dist/commands/destroy.js +62 -0
  17. package/dist/commands/destroy.js.map +1 -0
  18. package/dist/commands/domains.d.ts +36 -0
  19. package/dist/commands/domains.d.ts.map +1 -0
  20. package/dist/commands/domains.js +323 -0
  21. package/dist/commands/domains.js.map +1 -0
  22. package/dist/commands/env.d.ts +7 -0
  23. package/dist/commands/env.d.ts.map +1 -0
  24. package/dist/commands/env.js +42 -0
  25. package/dist/commands/env.js.map +1 -0
  26. package/dist/commands/git.d.ts +32 -0
  27. package/dist/commands/git.d.ts.map +1 -0
  28. package/dist/commands/git.js +340 -0
  29. package/dist/commands/git.js.map +1 -0
  30. package/dist/commands/init-ai.d.ts +7 -0
  31. package/dist/commands/init-ai.d.ts.map +1 -0
  32. package/dist/commands/init-ai.js +257 -0
  33. package/dist/commands/init-ai.js.map +1 -0
  34. package/dist/commands/init.d.ts +8 -0
  35. package/dist/commands/init.d.ts.map +1 -0
  36. package/dist/commands/init.js +228 -0
  37. package/dist/commands/init.js.map +1 -0
  38. package/dist/commands/link.d.ts +10 -0
  39. package/dist/commands/link.d.ts.map +1 -0
  40. package/dist/commands/link.js +193 -0
  41. package/dist/commands/link.js.map +1 -0
  42. package/dist/commands/login.d.ts +16 -0
  43. package/dist/commands/login.d.ts.map +1 -0
  44. package/dist/commands/login.js +468 -0
  45. package/dist/commands/login.js.map +1 -0
  46. package/dist/commands/logs.d.ts +16 -0
  47. package/dist/commands/logs.d.ts.map +1 -0
  48. package/dist/commands/logs.js +362 -0
  49. package/dist/commands/logs.js.map +1 -0
  50. package/dist/commands/oauth.d.ts +33 -0
  51. package/dist/commands/oauth.d.ts.map +1 -0
  52. package/dist/commands/oauth.js +181 -0
  53. package/dist/commands/oauth.js.map +1 -0
  54. package/dist/commands/preflight.d.ts +8 -0
  55. package/dist/commands/preflight.d.ts.map +1 -0
  56. package/dist/commands/preflight.js +190 -0
  57. package/dist/commands/preflight.js.map +1 -0
  58. package/dist/commands/program.d.ts +7 -0
  59. package/dist/commands/program.d.ts.map +1 -0
  60. package/dist/commands/program.js +59 -0
  61. package/dist/commands/program.js.map +1 -0
  62. package/dist/commands/programs.d.ts +7 -0
  63. package/dist/commands/programs.d.ts.map +1 -0
  64. package/dist/commands/programs.js +42 -0
  65. package/dist/commands/programs.js.map +1 -0
  66. package/dist/commands/push.d.ts +12 -0
  67. package/dist/commands/push.d.ts.map +1 -0
  68. package/dist/commands/push.js +166 -0
  69. package/dist/commands/push.js.map +1 -0
  70. package/dist/commands/register-scaffold.test.d.ts +2 -0
  71. package/dist/commands/register-scaffold.test.d.ts.map +1 -0
  72. package/dist/commands/register-scaffold.test.js +291 -0
  73. package/dist/commands/register-scaffold.test.js.map +1 -0
  74. package/dist/commands/register.d.ts +7 -0
  75. package/dist/commands/register.d.ts.map +1 -0
  76. package/dist/commands/register.js +304 -0
  77. package/dist/commands/register.js.map +1 -0
  78. package/dist/commands/rollback.d.ts +11 -0
  79. package/dist/commands/rollback.d.ts.map +1 -0
  80. package/dist/commands/rollback.js +147 -0
  81. package/dist/commands/rollback.js.map +1 -0
  82. package/dist/commands/sample.d.ts +15 -0
  83. package/dist/commands/sample.d.ts.map +1 -0
  84. package/dist/commands/sample.js +312 -0
  85. package/dist/commands/sample.js.map +1 -0
  86. package/dist/commands/scopes.d.ts +29 -0
  87. package/dist/commands/scopes.d.ts.map +1 -0
  88. package/dist/commands/scopes.js +203 -0
  89. package/dist/commands/scopes.js.map +1 -0
  90. package/dist/commands/services.d.ts +13 -0
  91. package/dist/commands/services.d.ts.map +1 -0
  92. package/dist/commands/services.js +95 -0
  93. package/dist/commands/services.js.map +1 -0
  94. package/dist/commands/settings.d.ts +10 -0
  95. package/dist/commands/settings.d.ts.map +1 -0
  96. package/dist/commands/settings.js +41 -0
  97. package/dist/commands/settings.js.map +1 -0
  98. package/dist/commands/signup.d.ts +9 -0
  99. package/dist/commands/signup.d.ts.map +1 -0
  100. package/dist/commands/signup.js +192 -0
  101. package/dist/commands/signup.js.map +1 -0
  102. package/dist/commands/status.d.ts +9 -0
  103. package/dist/commands/status.d.ts.map +1 -0
  104. package/dist/commands/status.js +170 -0
  105. package/dist/commands/status.js.map +1 -0
  106. package/dist/commands/troubleshoot.d.ts +19 -0
  107. package/dist/commands/troubleshoot.d.ts.map +1 -0
  108. package/dist/commands/troubleshoot.js +465 -0
  109. package/dist/commands/troubleshoot.js.map +1 -0
  110. package/dist/commands/update.d.ts +6 -0
  111. package/dist/commands/update.d.ts.map +1 -0
  112. package/dist/commands/update.js +64 -0
  113. package/dist/commands/update.js.map +1 -0
  114. package/dist/commands/user.d.ts +17 -0
  115. package/dist/commands/user.d.ts.map +1 -0
  116. package/dist/commands/user.js +126 -0
  117. package/dist/commands/user.js.map +1 -0
  118. package/dist/commands/versions.d.ts +16 -0
  119. package/dist/commands/versions.d.ts.map +1 -0
  120. package/dist/commands/versions.js +116 -0
  121. package/dist/commands/versions.js.map +1 -0
  122. package/dist/index.d.ts +3 -0
  123. package/dist/index.d.ts.map +1 -0
  124. package/dist/index.js +556 -0
  125. package/dist/index.js.map +1 -0
  126. package/dist/lib/__tests__/scaffold-integration.test.d.ts +2 -0
  127. package/dist/lib/__tests__/scaffold-integration.test.d.ts.map +1 -0
  128. package/dist/lib/__tests__/scaffold-integration.test.js +321 -0
  129. package/dist/lib/__tests__/scaffold-integration.test.js.map +1 -0
  130. package/dist/lib/ai/client.d.ts +39 -0
  131. package/dist/lib/ai/client.d.ts.map +1 -0
  132. package/dist/lib/ai/client.js +236 -0
  133. package/dist/lib/ai/client.js.map +1 -0
  134. package/dist/lib/ai/generator.d.ts +17 -0
  135. package/dist/lib/ai/generator.d.ts.map +1 -0
  136. package/dist/lib/ai/generator.js +338 -0
  137. package/dist/lib/ai/generator.js.map +1 -0
  138. package/dist/lib/ai/prompts.d.ts +47 -0
  139. package/dist/lib/ai/prompts.d.ts.map +1 -0
  140. package/dist/lib/ai/prompts.js +247 -0
  141. package/dist/lib/ai/prompts.js.map +1 -0
  142. package/dist/lib/ai/scanner.d.ts +50 -0
  143. package/dist/lib/ai/scanner.d.ts.map +1 -0
  144. package/dist/lib/ai/scanner.js +237 -0
  145. package/dist/lib/ai/scanner.js.map +1 -0
  146. package/dist/lib/api.d.ts +18 -0
  147. package/dist/lib/api.d.ts.map +1 -0
  148. package/dist/lib/api.js +67 -0
  149. package/dist/lib/api.js.map +1 -0
  150. package/dist/lib/auto-register.d.ts +12 -0
  151. package/dist/lib/auto-register.d.ts.map +1 -0
  152. package/dist/lib/auto-register.js +42 -0
  153. package/dist/lib/auto-register.js.map +1 -0
  154. package/dist/lib/bio.d.ts +90 -0
  155. package/dist/lib/bio.d.ts.map +1 -0
  156. package/dist/lib/bio.js +247 -0
  157. package/dist/lib/bio.js.map +1 -0
  158. package/dist/lib/builder.d.ts +65 -0
  159. package/dist/lib/builder.d.ts.map +1 -0
  160. package/dist/lib/builder.js +192 -0
  161. package/dist/lib/builder.js.map +1 -0
  162. package/dist/lib/catalog.d.ts +3 -0
  163. package/dist/lib/catalog.d.ts.map +1 -0
  164. package/dist/lib/catalog.js +17 -0
  165. package/dist/lib/catalog.js.map +1 -0
  166. package/dist/lib/config.d.ts +20 -0
  167. package/dist/lib/config.d.ts.map +1 -0
  168. package/dist/lib/config.js +124 -0
  169. package/dist/lib/config.js.map +1 -0
  170. package/dist/lib/forgejo.d.ts +188 -0
  171. package/dist/lib/forgejo.d.ts.map +1 -0
  172. package/dist/lib/forgejo.js +305 -0
  173. package/dist/lib/forgejo.js.map +1 -0
  174. package/dist/lib/output.d.ts +12 -0
  175. package/dist/lib/output.d.ts.map +1 -0
  176. package/dist/lib/output.js +39 -0
  177. package/dist/lib/output.js.map +1 -0
  178. package/dist/lib/scaffold.d.ts +30 -0
  179. package/dist/lib/scaffold.d.ts.map +1 -0
  180. package/dist/lib/scaffold.js +180 -0
  181. package/dist/lib/scaffold.js.map +1 -0
  182. package/dist/lib/scope-requests.d.ts +17 -0
  183. package/dist/lib/scope-requests.d.ts.map +1 -0
  184. package/dist/lib/scope-requests.js +78 -0
  185. package/dist/lib/scope-requests.js.map +1 -0
  186. package/dist/lib/scope-requests.test.d.ts +2 -0
  187. package/dist/lib/scope-requests.test.d.ts.map +1 -0
  188. package/dist/lib/scope-requests.test.js +239 -0
  189. package/dist/lib/scope-requests.test.js.map +1 -0
  190. package/dist/lib/troubleshoot/analyzer.d.ts +14 -0
  191. package/dist/lib/troubleshoot/analyzer.d.ts.map +1 -0
  192. package/dist/lib/troubleshoot/analyzer.js +541 -0
  193. package/dist/lib/troubleshoot/analyzer.js.map +1 -0
  194. package/dist/lib/troubleshoot/auto-fix.d.ts +29 -0
  195. package/dist/lib/troubleshoot/auto-fix.d.ts.map +1 -0
  196. package/dist/lib/troubleshoot/auto-fix.js +373 -0
  197. package/dist/lib/troubleshoot/auto-fix.js.map +1 -0
  198. package/dist/lib/troubleshoot/index.d.ts +5 -0
  199. package/dist/lib/troubleshoot/index.d.ts.map +1 -0
  200. package/dist/lib/troubleshoot/index.js +6 -0
  201. package/dist/lib/troubleshoot/index.js.map +1 -0
  202. package/dist/lib/troubleshoot/log-fetcher.d.ts +43 -0
  203. package/dist/lib/troubleshoot/log-fetcher.d.ts.map +1 -0
  204. package/dist/lib/troubleshoot/log-fetcher.js +431 -0
  205. package/dist/lib/troubleshoot/log-fetcher.js.map +1 -0
  206. package/dist/lib/troubleshoot/redactor.d.ts +35 -0
  207. package/dist/lib/troubleshoot/redactor.d.ts.map +1 -0
  208. package/dist/lib/troubleshoot/redactor.js +208 -0
  209. package/dist/lib/troubleshoot/redactor.js.map +1 -0
  210. package/dist/lib/validators/catalog-schemas.d.ts +59 -0
  211. package/dist/lib/validators/catalog-schemas.d.ts.map +1 -0
  212. package/dist/lib/validators/catalog-schemas.js +32 -0
  213. package/dist/lib/validators/catalog-schemas.js.map +1 -0
  214. package/dist/lib/validators/catalog-validator.d.ts +3 -0
  215. package/dist/lib/validators/catalog-validator.d.ts.map +1 -0
  216. package/dist/lib/validators/catalog-validator.js +247 -0
  217. package/dist/lib/validators/catalog-validator.js.map +1 -0
  218. package/dist/lib/validators/catalog-validator.test.d.ts +2 -0
  219. package/dist/lib/validators/catalog-validator.test.d.ts.map +1 -0
  220. package/dist/lib/validators/catalog-validator.test.js +420 -0
  221. package/dist/lib/validators/catalog-validator.test.js.map +1 -0
  222. package/dist/lib/validators/dockerfile-validator.d.ts +5 -0
  223. package/dist/lib/validators/dockerfile-validator.d.ts.map +1 -0
  224. package/dist/lib/validators/dockerfile-validator.js +295 -0
  225. package/dist/lib/validators/dockerfile-validator.js.map +1 -0
  226. package/dist/lib/validators/dockerfile-validator.test.d.ts +2 -0
  227. package/dist/lib/validators/dockerfile-validator.test.d.ts.map +1 -0
  228. package/dist/lib/validators/dockerfile-validator.test.js +161 -0
  229. package/dist/lib/validators/dockerfile-validator.test.js.map +1 -0
  230. package/dist/lib/validators/env-validator.d.ts +3 -0
  231. package/dist/lib/validators/env-validator.d.ts.map +1 -0
  232. package/dist/lib/validators/env-validator.js +268 -0
  233. package/dist/lib/validators/env-validator.js.map +1 -0
  234. package/dist/lib/validators/git-validator.d.ts +3 -0
  235. package/dist/lib/validators/git-validator.d.ts.map +1 -0
  236. package/dist/lib/validators/git-validator.js +236 -0
  237. package/dist/lib/validators/git-validator.js.map +1 -0
  238. package/dist/lib/validators/health-detector.d.ts +4 -0
  239. package/dist/lib/validators/health-detector.d.ts.map +1 -0
  240. package/dist/lib/validators/health-detector.js +172 -0
  241. package/dist/lib/validators/health-detector.js.map +1 -0
  242. package/dist/lib/validators/health-detector.test.d.ts +2 -0
  243. package/dist/lib/validators/health-detector.test.d.ts.map +1 -0
  244. package/dist/lib/validators/health-detector.test.js +118 -0
  245. package/dist/lib/validators/health-detector.test.js.map +1 -0
  246. package/dist/lib/validators/index.d.ts +8 -0
  247. package/dist/lib/validators/index.d.ts.map +1 -0
  248. package/dist/lib/validators/index.js +8 -0
  249. package/dist/lib/validators/index.js.map +1 -0
  250. package/dist/lib/validators/route-validator.d.ts +3 -0
  251. package/dist/lib/validators/route-validator.d.ts.map +1 -0
  252. package/dist/lib/validators/route-validator.js +238 -0
  253. package/dist/lib/validators/route-validator.js.map +1 -0
  254. package/dist/lib/validators/scope-validator.d.ts +3 -0
  255. package/dist/lib/validators/scope-validator.d.ts.map +1 -0
  256. package/dist/lib/validators/scope-validator.js +141 -0
  257. package/dist/lib/validators/scope-validator.js.map +1 -0
  258. package/dist/lib/watch.d.ts +35 -0
  259. package/dist/lib/watch.d.ts.map +1 -0
  260. package/dist/lib/watch.js +171 -0
  261. package/dist/lib/watch.js.map +1 -0
  262. package/dist/templates/api/.env.example +15 -0
  263. package/dist/templates/api/Dockerfile +45 -0
  264. package/dist/templates/api/README.md +85 -0
  265. package/dist/templates/api/catalog-info.yaml +22 -0
  266. package/dist/templates/api/gitignore +31 -0
  267. package/dist/templates/api/helm/{{name}}/Chart.yaml +9 -0
  268. package/dist/templates/api/helm/{{name}}/templates/deployment.yaml +72 -0
  269. package/dist/templates/api/helm/{{name}}/templates/ingress.yaml +43 -0
  270. package/dist/templates/api/helm/{{name}}/templates/service.yaml +17 -0
  271. package/dist/templates/api/helm/{{name}}/values.yaml +64 -0
  272. package/dist/templates/api/package.json +33 -0
  273. package/dist/templates/api/src/index.ts +61 -0
  274. package/dist/templates/api/src/routes/example.ts +165 -0
  275. package/dist/templates/api/tsconfig.json +18 -0
  276. package/dist/templates/crosspod/README.md +87 -0
  277. package/dist/templates/crosspod/service-a/Dockerfile +27 -0
  278. package/dist/templates/crosspod/service-a/catalog-info.yaml +54 -0
  279. package/dist/templates/crosspod/service-a/gitignore +5 -0
  280. package/dist/templates/crosspod/service-a/helm/{{name}}-service-a/Chart.yaml +6 -0
  281. package/dist/templates/crosspod/service-a/helm/{{name}}-service-a/templates/deployment.yaml +72 -0
  282. package/dist/templates/crosspod/service-a/helm/{{name}}-service-a/templates/ingress.yaml +43 -0
  283. package/dist/templates/crosspod/service-a/helm/{{name}}-service-a/templates/service.yaml +17 -0
  284. package/dist/templates/crosspod/service-a/helm/{{name}}-service-a/values.yaml +44 -0
  285. package/dist/templates/crosspod/service-a/package.json +29 -0
  286. package/dist/templates/crosspod/service-a/src/index.ts +89 -0
  287. package/dist/templates/crosspod/service-a/tsconfig.json +14 -0
  288. package/dist/templates/crosspod/service-b/Dockerfile +27 -0
  289. package/dist/templates/crosspod/service-b/catalog-info.yaml +27 -0
  290. package/dist/templates/crosspod/service-b/gitignore +5 -0
  291. package/dist/templates/crosspod/service-b/helm/{{name}}-service-b/Chart.yaml +6 -0
  292. package/dist/templates/crosspod/service-b/helm/{{name}}-service-b/templates/deployment.yaml +72 -0
  293. package/dist/templates/crosspod/service-b/helm/{{name}}-service-b/templates/ingress.yaml +43 -0
  294. package/dist/templates/crosspod/service-b/helm/{{name}}-service-b/templates/service.yaml +17 -0
  295. package/dist/templates/crosspod/service-b/helm/{{name}}-service-b/values.yaml +47 -0
  296. package/dist/templates/crosspod/service-b/package.json +26 -0
  297. package/dist/templates/crosspod/service-b/src/index.ts +143 -0
  298. package/dist/templates/crosspod/service-b/tsconfig.json +14 -0
  299. package/dist/templates/mga/{{name}}-api/.env.example +5 -0
  300. package/dist/templates/mga/{{name}}-api/Dockerfile +45 -0
  301. package/dist/templates/mga/{{name}}-api/catalog-info.yaml +44 -0
  302. package/dist/templates/mga/{{name}}-api/gitignore +31 -0
  303. package/dist/templates/mga/{{name}}-api/helm/{{name}}-api/Chart.yaml +9 -0
  304. package/dist/templates/mga/{{name}}-api/helm/{{name}}-api/templates/deployment.yaml +72 -0
  305. package/dist/templates/mga/{{name}}-api/helm/{{name}}-api/templates/ingress.yaml +43 -0
  306. package/dist/templates/mga/{{name}}-api/helm/{{name}}-api/templates/service.yaml +17 -0
  307. package/dist/templates/mga/{{name}}-api/helm/{{name}}-api/values.yaml +65 -0
  308. package/dist/templates/mga/{{name}}-api/package.json +37 -0
  309. package/dist/templates/mga/{{name}}-api/src/index.ts +82 -0
  310. package/dist/templates/mga/{{name}}-api/src/lib/mongo.ts +19 -0
  311. package/dist/templates/mga/{{name}}-api/src/middleware/auth.ts +133 -0
  312. package/dist/templates/mga/{{name}}-api/src/models/agent.ts +45 -0
  313. package/dist/templates/mga/{{name}}-api/src/models/application.ts +58 -0
  314. package/dist/templates/mga/{{name}}-api/src/models/invite.ts +39 -0
  315. package/dist/templates/mga/{{name}}-api/src/models/policy.ts +59 -0
  316. package/dist/templates/mga/{{name}}-api/src/models/product.ts +51 -0
  317. package/dist/templates/mga/{{name}}-api/src/models/quote.ts +68 -0
  318. package/dist/templates/mga/{{name}}-api/src/routes/agents.ts +98 -0
  319. package/dist/templates/mga/{{name}}-api/src/routes/applications.ts +170 -0
  320. package/dist/templates/mga/{{name}}-api/src/routes/invites.ts +146 -0
  321. package/dist/templates/mga/{{name}}-api/src/routes/policies.ts +70 -0
  322. package/dist/templates/mga/{{name}}-api/src/routes/products.ts +154 -0
  323. package/dist/templates/mga/{{name}}-api/src/routes/quotes.ts +263 -0
  324. package/dist/templates/mga/{{name}}-api/src/routes/reports.ts +199 -0
  325. package/dist/templates/mga/{{name}}-api/tsconfig.json +18 -0
  326. package/dist/templates/mga/{{name}}-web/.dockerignore +6 -0
  327. package/dist/templates/mga/{{name}}-web/.env.example +6 -0
  328. package/dist/templates/mga/{{name}}-web/Dockerfile +51 -0
  329. package/dist/templates/mga/{{name}}-web/catalog-info.yaml +24 -0
  330. package/dist/templates/mga/{{name}}-web/dockerignore +6 -0
  331. package/dist/templates/mga/{{name}}-web/gitignore +36 -0
  332. package/dist/templates/mga/{{name}}-web/helm/{{name}}-web/Chart.yaml +9 -0
  333. package/dist/templates/mga/{{name}}-web/helm/{{name}}-web/templates/_helpers.tpl +49 -0
  334. package/dist/templates/mga/{{name}}-web/helm/{{name}}-web/templates/configmap.yaml +10 -0
  335. package/dist/templates/mga/{{name}}-web/helm/{{name}}-web/templates/deployment.yaml +56 -0
  336. package/dist/templates/mga/{{name}}-web/helm/{{name}}-web/templates/ingress.yaml +41 -0
  337. package/dist/templates/mga/{{name}}-web/helm/{{name}}-web/templates/service.yaml +15 -0
  338. package/dist/templates/mga/{{name}}-web/helm/{{name}}-web/values.yaml +70 -0
  339. package/dist/templates/mga/{{name}}-web/next.config.js +8 -0
  340. package/dist/templates/mga/{{name}}-web/package.json +30 -0
  341. package/dist/templates/mga/{{name}}-web/public/.gitkeep +0 -0
  342. package/dist/templates/mga/{{name}}-web/public/gitkeep +0 -0
  343. package/dist/templates/mga/{{name}}-web/src/app/api/auth/callback/route.ts +77 -0
  344. package/dist/templates/mga/{{name}}-web/src/app/api/auth/login/route.ts +16 -0
  345. package/dist/templates/mga/{{name}}-web/src/app/api/auth/logout/route.ts +7 -0
  346. package/dist/templates/mga/{{name}}-web/src/app/api/auth/session/route.ts +42 -0
  347. package/dist/templates/mga/{{name}}-web/src/app/api/health/route.ts +10 -0
  348. package/dist/templates/mga/{{name}}-web/src/app/api/proxy/applications/route.ts +47 -0
  349. package/dist/templates/mga/{{name}}-web/src/app/applications/layout.tsx +17 -0
  350. package/dist/templates/mga/{{name}}-web/src/app/applications/new/page.tsx +374 -0
  351. package/dist/templates/mga/{{name}}-web/src/app/applications/page.tsx +109 -0
  352. package/dist/templates/mga/{{name}}-web/src/app/dashboard/admin/page.tsx +179 -0
  353. package/dist/templates/mga/{{name}}-web/src/app/dashboard/agent/page.tsx +125 -0
  354. package/dist/templates/mga/{{name}}-web/src/app/dashboard/insured/page.tsx +145 -0
  355. package/dist/templates/mga/{{name}}-web/src/app/dashboard/layout.tsx +17 -0
  356. package/dist/templates/mga/{{name}}-web/src/app/dashboard/page.tsx +27 -0
  357. package/dist/templates/mga/{{name}}-web/src/app/dashboard/shell.tsx +89 -0
  358. package/dist/templates/mga/{{name}}-web/src/app/globals.css +477 -0
  359. package/dist/templates/mga/{{name}}-web/src/app/layout.tsx +19 -0
  360. package/dist/templates/mga/{{name}}-web/src/app/page.tsx +74 -0
  361. package/dist/templates/mga/{{name}}-web/src/app/reports/layout.tsx +21 -0
  362. package/dist/templates/mga/{{name}}-web/src/app/reports/page.tsx +231 -0
  363. package/dist/templates/mga/{{name}}-web/src/lib/api.ts +79 -0
  364. package/dist/templates/mga/{{name}}-web/src/lib/auth.ts +285 -0
  365. package/dist/templates/mga/{{name}}-web/src/middleware.ts +71 -0
  366. package/dist/templates/mga/{{name}}-web/tsconfig.json +26 -0
  367. package/dist/templates/nextjs/.env.example +11 -0
  368. package/dist/templates/nextjs/Dockerfile +51 -0
  369. package/dist/templates/nextjs/README.md +87 -0
  370. package/dist/templates/nextjs/catalog-info.yaml +16 -0
  371. package/dist/templates/nextjs/gitignore +36 -0
  372. package/dist/templates/nextjs/helm/{{name}}/Chart.yaml +9 -0
  373. package/dist/templates/nextjs/helm/{{name}}/templates/deployment.yaml +72 -0
  374. package/dist/templates/nextjs/helm/{{name}}/templates/ingress.yaml +43 -0
  375. package/dist/templates/nextjs/helm/{{name}}/templates/service.yaml +17 -0
  376. package/dist/templates/nextjs/helm/{{name}}/values.yaml +60 -0
  377. package/dist/templates/nextjs/next.config.js +23 -0
  378. package/dist/templates/nextjs/package.json +29 -0
  379. package/dist/templates/nextjs/public/.gitkeep +0 -0
  380. package/dist/templates/nextjs/public/gitkeep +0 -0
  381. package/dist/templates/nextjs/src/app/api/example/route.ts +63 -0
  382. package/dist/templates/nextjs/src/app/api/health/route.ts +10 -0
  383. package/dist/templates/nextjs/src/app/layout.tsx +18 -0
  384. package/dist/templates/nextjs/src/app/page.tsx +49 -0
  385. package/dist/templates/nextjs/tsconfig.json +26 -0
  386. package/dist/templates/nextjs-oauth/.dockerignore +6 -0
  387. package/dist/templates/nextjs-oauth/.env.example +12 -0
  388. package/dist/templates/nextjs-oauth/Dockerfile +51 -0
  389. package/dist/templates/nextjs-oauth/README.md +115 -0
  390. package/dist/templates/nextjs-oauth/catalog-info.yaml +17 -0
  391. package/dist/templates/nextjs-oauth/dockerignore +6 -0
  392. package/dist/templates/nextjs-oauth/gitignore +36 -0
  393. package/dist/templates/nextjs-oauth/helm/Chart.yaml +6 -0
  394. package/dist/templates/nextjs-oauth/helm/templates/_helpers.tpl +49 -0
  395. package/dist/templates/nextjs-oauth/helm/templates/configmap.yaml +10 -0
  396. package/dist/templates/nextjs-oauth/helm/templates/deployment.yaml +56 -0
  397. package/dist/templates/nextjs-oauth/helm/templates/ingress.yaml +41 -0
  398. package/dist/templates/nextjs-oauth/helm/templates/service.yaml +15 -0
  399. package/dist/templates/nextjs-oauth/helm/values.yaml +69 -0
  400. package/dist/templates/nextjs-oauth/helm/{{name}}/Chart.yaml +9 -0
  401. package/dist/templates/nextjs-oauth/helm/{{name}}/templates/deployment.yaml +68 -0
  402. package/dist/templates/nextjs-oauth/helm/{{name}}/templates/service.yaml +17 -0
  403. package/dist/templates/nextjs-oauth/helm/{{name}}/values.yaml +51 -0
  404. package/dist/templates/nextjs-oauth/next.config.js +23 -0
  405. package/dist/templates/nextjs-oauth/package.json +30 -0
  406. package/dist/templates/nextjs-oauth/public/.gitkeep +0 -0
  407. package/dist/templates/nextjs-oauth/public/gitkeep +0 -0
  408. package/dist/templates/nextjs-oauth/src/app/api/auth/callback/route.ts +77 -0
  409. package/dist/templates/nextjs-oauth/src/app/api/auth/login/route.ts +16 -0
  410. package/dist/templates/nextjs-oauth/src/app/api/auth/logout/route.ts +7 -0
  411. package/dist/templates/nextjs-oauth/src/app/api/auth/session/route.ts +42 -0
  412. package/dist/templates/nextjs-oauth/src/app/api/example/route.ts +63 -0
  413. package/dist/templates/nextjs-oauth/src/app/api/health/route.ts +10 -0
  414. package/dist/templates/nextjs-oauth/src/app/dashboard/page.tsx +92 -0
  415. package/dist/templates/nextjs-oauth/src/app/layout.tsx +18 -0
  416. package/dist/templates/nextjs-oauth/src/app/page.tsx +110 -0
  417. package/dist/templates/nextjs-oauth/src/lib/auth.ts +285 -0
  418. package/dist/templates/nextjs-oauth/src/middleware.ts +71 -0
  419. package/dist/templates/nextjs-oauth/tsconfig.json +26 -0
  420. package/dist/templates/static/catalog-info.yaml +14 -0
  421. package/dist/templates/static/index.html +16 -0
  422. package/dist/templates/static/styles.css +39 -0
  423. package/dist/templates/worker/.env.example +13 -0
  424. package/dist/templates/worker/Dockerfile +26 -0
  425. package/dist/templates/worker/README.md +106 -0
  426. package/dist/templates/worker/catalog-info.yaml +19 -0
  427. package/dist/templates/worker/gitignore +5 -0
  428. package/dist/templates/worker/helm/{{name}}/Chart.yaml +9 -0
  429. package/dist/templates/worker/helm/{{name}}/templates/deployment.yaml +68 -0
  430. package/dist/templates/worker/helm/{{name}}/templates/ingress.yaml +43 -0
  431. package/dist/templates/worker/helm/{{name}}/values.yaml +64 -0
  432. package/dist/templates/worker/package.json +26 -0
  433. package/dist/templates/worker/src/index.ts +185 -0
  434. package/dist/templates/worker/tsconfig.json +14 -0
  435. package/dist/types/index.d.ts +596 -0
  436. package/dist/types/index.d.ts.map +1 -0
  437. package/dist/types/index.js +41 -0
  438. package/dist/types/index.js.map +1 -0
  439. package/dist/types/platform.d.ts +191 -0
  440. package/dist/types/platform.d.ts.map +1 -0
  441. package/dist/types/platform.js +17 -0
  442. package/dist/types/platform.js.map +1 -0
  443. package/package.json +62 -0
@@ -0,0 +1,231 @@
1
+ import { apiClient } from '@/lib/api'
2
+
3
+ interface ReportData {
4
+ totalPremium: number
5
+ policyCount: number
6
+ avgPremium: number
7
+ applicationCount: number
8
+ premiumTrend: Array<{
9
+ month: string
10
+ amount: number
11
+ }>
12
+ pipeline: {
13
+ submitted: number
14
+ review: number
15
+ quoted: number
16
+ bound: number
17
+ declined: number
18
+ }
19
+ topAgents: Array<{
20
+ name: string
21
+ policies: number
22
+ premium: number
23
+ }>
24
+ geography: Array<{
25
+ state: string
26
+ premium: number
27
+ policies: number
28
+ }>
29
+ }
30
+
31
+ function formatCurrency(value: number): string {
32
+ return new Intl.NumberFormat('en-US', {
33
+ style: 'currency',
34
+ currency: 'USD',
35
+ minimumFractionDigits: 0,
36
+ maximumFractionDigits: 0,
37
+ }).format(value)
38
+ }
39
+
40
+ export default async function ReportsPage() {
41
+ const res = await apiClient.get<ReportData>('/api/reports/full')
42
+
43
+ if (!res.success || !res.data) {
44
+ return (
45
+ <div>
46
+ <div className="page-header">
47
+ <h1 className="page-title">Reports</h1>
48
+ </div>
49
+ <div className="alert alert-error">
50
+ Failed to load reports: {res.error?.message || 'Unknown error'}
51
+ </div>
52
+ </div>
53
+ )
54
+ }
55
+
56
+ const data = res.data
57
+ const maxTrendAmount = Math.max(...data.premiumTrend.map((m) => m.amount), 1)
58
+ const pipelineTotal =
59
+ data.pipeline.submitted +
60
+ data.pipeline.review +
61
+ data.pipeline.quoted +
62
+ data.pipeline.bound +
63
+ data.pipeline.declined || 1
64
+
65
+ return (
66
+ <div>
67
+ <div className="page-header">
68
+ <h1 className="page-title">Reports</h1>
69
+ </div>
70
+
71
+ {/* KPI cards */}
72
+ <div className="kpi-grid">
73
+ <div className="card">
74
+ <div className="card-title">Total Premium</div>
75
+ <div className="card-value">{formatCurrency(data.totalPremium)}</div>
76
+ </div>
77
+ <div className="card">
78
+ <div className="card-title">Policy Count</div>
79
+ <div className="card-value">{data.policyCount.toLocaleString()}</div>
80
+ </div>
81
+ <div className="card">
82
+ <div className="card-title">Avg Premium</div>
83
+ <div className="card-value">{formatCurrency(data.avgPremium)}</div>
84
+ </div>
85
+ <div className="card">
86
+ <div className="card-title">Applications</div>
87
+ <div className="card-value">{data.applicationCount.toLocaleString()}</div>
88
+ </div>
89
+ </div>
90
+
91
+ {/* Premium Trend */}
92
+ <div className="card" style={{ marginBottom: 'var(--space-xl)' }}>
93
+ <div className="card-title">Premium Trend</div>
94
+ <div className="bar-chart">
95
+ {data.premiumTrend.map((month) => (
96
+ <div key={month.month} className="bar-row">
97
+ <div className="bar-label">{month.month}</div>
98
+ <div className="bar-track">
99
+ <div
100
+ className="bar-fill"
101
+ style={{ width: `${(month.amount / maxTrendAmount) * 100}%` }}
102
+ />
103
+ </div>
104
+ <div className="bar-value">{formatCurrency(month.amount)}</div>
105
+ </div>
106
+ ))}
107
+ </div>
108
+ </div>
109
+
110
+ {/* Pipeline */}
111
+ <div className="card" style={{ marginBottom: 'var(--space-xl)' }}>
112
+ <div className="card-title">Pipeline Breakdown</div>
113
+ <div className="pipeline-bar">
114
+ <div
115
+ className="pipeline-segment"
116
+ style={{ flex: data.pipeline.submitted, background: 'var(--color-warning)' }}
117
+ >
118
+ {data.pipeline.submitted > 0
119
+ ? `${Math.round((data.pipeline.submitted / pipelineTotal) * 100)}%`
120
+ : ''}
121
+ </div>
122
+ <div
123
+ className="pipeline-segment"
124
+ style={{ flex: data.pipeline.review, background: 'var(--color-pending)' }}
125
+ >
126
+ {data.pipeline.review > 0
127
+ ? `${Math.round((data.pipeline.review / pipelineTotal) * 100)}%`
128
+ : ''}
129
+ </div>
130
+ <div
131
+ className="pipeline-segment"
132
+ style={{ flex: data.pipeline.quoted, background: 'var(--color-info)' }}
133
+ >
134
+ {data.pipeline.quoted > 0
135
+ ? `${Math.round((data.pipeline.quoted / pipelineTotal) * 100)}%`
136
+ : ''}
137
+ </div>
138
+ <div
139
+ className="pipeline-segment"
140
+ style={{ flex: data.pipeline.bound, background: 'var(--color-success)' }}
141
+ >
142
+ {data.pipeline.bound > 0
143
+ ? `${Math.round((data.pipeline.bound / pipelineTotal) * 100)}%`
144
+ : ''}
145
+ </div>
146
+ <div
147
+ className="pipeline-segment"
148
+ style={{ flex: data.pipeline.declined, background: 'var(--color-danger)' }}
149
+ >
150
+ {data.pipeline.declined > 0
151
+ ? `${Math.round((data.pipeline.declined / pipelineTotal) * 100)}%`
152
+ : ''}
153
+ </div>
154
+ </div>
155
+ <div style={{ display: 'flex', gap: 'var(--space-md)', flexWrap: 'wrap', fontSize: '0.75rem' }}>
156
+ <span><span style={{ color: 'var(--color-warning)' }}>&#9632;</span> Submitted ({data.pipeline.submitted})</span>
157
+ <span><span style={{ color: 'var(--color-pending)' }}>&#9632;</span> Review ({data.pipeline.review})</span>
158
+ <span><span style={{ color: 'var(--color-info)' }}>&#9632;</span> Quoted ({data.pipeline.quoted})</span>
159
+ <span><span style={{ color: 'var(--color-success)' }}>&#9632;</span> Bound ({data.pipeline.bound})</span>
160
+ <span><span style={{ color: 'var(--color-danger)' }}>&#9632;</span> Declined ({data.pipeline.declined})</span>
161
+ </div>
162
+ </div>
163
+
164
+ <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--space-xl)' }}>
165
+ {/* Top Agents */}
166
+ <div className="table-wrapper">
167
+ <div style={{ padding: 'var(--space-md)' }}>
168
+ <div className="card-title">Top Agents</div>
169
+ </div>
170
+ <table>
171
+ <thead>
172
+ <tr>
173
+ <th>Agent</th>
174
+ <th>Policies</th>
175
+ <th>Premium</th>
176
+ </tr>
177
+ </thead>
178
+ <tbody>
179
+ {data.topAgents.map((agent) => (
180
+ <tr key={agent.name}>
181
+ <td>{agent.name}</td>
182
+ <td>{agent.policies}</td>
183
+ <td>{formatCurrency(agent.premium)}</td>
184
+ </tr>
185
+ ))}
186
+ {data.topAgents.length === 0 && (
187
+ <tr>
188
+ <td colSpan={3} style={{ color: 'var(--color-text-muted)', textAlign: 'center' }}>
189
+ No agent data available
190
+ </td>
191
+ </tr>
192
+ )}
193
+ </tbody>
194
+ </table>
195
+ </div>
196
+
197
+ {/* Geography */}
198
+ <div className="table-wrapper">
199
+ <div style={{ padding: 'var(--space-md)' }}>
200
+ <div className="card-title">By State</div>
201
+ </div>
202
+ <table>
203
+ <thead>
204
+ <tr>
205
+ <th>State</th>
206
+ <th>Premium</th>
207
+ <th>Policies</th>
208
+ </tr>
209
+ </thead>
210
+ <tbody>
211
+ {data.geography.map((geo) => (
212
+ <tr key={geo.state}>
213
+ <td>{geo.state}</td>
214
+ <td>{formatCurrency(geo.premium)}</td>
215
+ <td>{geo.policies}</td>
216
+ </tr>
217
+ ))}
218
+ {data.geography.length === 0 && (
219
+ <tr>
220
+ <td colSpan={3} style={{ color: 'var(--color-text-muted)', textAlign: 'center' }}>
221
+ No geographic data available
222
+ </td>
223
+ </tr>
224
+ )}
225
+ </tbody>
226
+ </table>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ )
231
+ }
@@ -0,0 +1,79 @@
1
+ import { cookies } from 'next/headers'
2
+
3
+ const COOKIE_PREFIX = '{{name}}-web'
4
+
5
+ function getApiUrl(): string {
6
+ return process.env.API_URL || 'http://localhost:3001'
7
+ }
8
+
9
+ export interface ApiResponse<T = unknown> {
10
+ success: boolean
11
+ data?: T
12
+ error?: {
13
+ code: string
14
+ message: string
15
+ }
16
+ }
17
+
18
+ async function getSessionToken(): Promise<string | undefined> {
19
+ const cookieStore = await cookies()
20
+ return cookieStore.get(`${COOKIE_PREFIX}_session`)?.value
21
+ }
22
+
23
+ async function request<T>(
24
+ method: string,
25
+ path: string,
26
+ body?: unknown
27
+ ): Promise<ApiResponse<T>> {
28
+ const token = await getSessionToken()
29
+ const url = `${getApiUrl()}${path}`
30
+
31
+ const headers: Record<string, string> = {
32
+ 'Content-Type': 'application/json',
33
+ }
34
+
35
+ if (token) {
36
+ headers['Authorization'] = `Bearer ${token}`
37
+ }
38
+
39
+ try {
40
+ const response = await fetch(url, {
41
+ method,
42
+ headers,
43
+ body: body ? JSON.stringify(body) : undefined,
44
+ cache: 'no-store',
45
+ })
46
+
47
+ const json = await response.json()
48
+
49
+ if (!response.ok) {
50
+ return {
51
+ success: false,
52
+ error: {
53
+ code: json.error?.code || `HTTP_${response.status}`,
54
+ message: json.error?.message || response.statusText,
55
+ },
56
+ }
57
+ }
58
+
59
+ return {
60
+ success: true,
61
+ data: json.data !== undefined ? json.data : json,
62
+ }
63
+ } catch (err) {
64
+ return {
65
+ success: false,
66
+ error: {
67
+ code: 'NETWORK_ERROR',
68
+ message: err instanceof Error ? err.message : 'Failed to reach API',
69
+ },
70
+ }
71
+ }
72
+ }
73
+
74
+ export const apiClient = {
75
+ get: <T>(path: string) => request<T>('GET', path),
76
+ post: <T>(path: string, body: unknown) => request<T>('POST', path, body),
77
+ put: <T>(path: string, body: unknown) => request<T>('PUT', path, body),
78
+ delete: <T>(path: string) => request<T>('DELETE', path),
79
+ }
@@ -0,0 +1,285 @@
1
+ import { cookies } from 'next/headers'
2
+ import { SignJWT, jwtVerify } from 'jose'
3
+ import crypto from 'crypto'
4
+
5
+ // Configuration — read lazily so `next build` doesn't fail when env vars are
6
+ // absent (they are injected at runtime via ConfigMap / Secret in K8s).
7
+ function getBioIdUrl(): string {
8
+ return process.env.BIO_ID_URL || 'http://localhost:6100'
9
+ }
10
+ function getAppUrl(): string {
11
+ return process.env.APP_URL || 'http://localhost:3000'
12
+ }
13
+ function getClientId(): string {
14
+ const id = process.env.OAUTH_CLIENT_ID
15
+ if (!id) throw new Error('OAUTH_CLIENT_ID is not configured')
16
+ return id
17
+ }
18
+ function getClientSecret(): string {
19
+ const secret = process.env.OAUTH_CLIENT_SECRET
20
+ if (!secret) throw new Error('OAUTH_CLIENT_SECRET is not configured')
21
+ return secret
22
+ }
23
+ function getJwtSecret(): Uint8Array {
24
+ const secret = process.env.JWT_SECRET
25
+ if (!secret && process.env.NODE_ENV === 'production') {
26
+ throw new Error('JWT_SECRET environment variable is required in production')
27
+ }
28
+ return new TextEncoder().encode(secret || 'dev-only-secret-do-not-use-in-production')
29
+ }
30
+
31
+ const COOKIE_PREFIX = '{{name}}-web'
32
+ const SESSION_MAX_AGE = 60 * 60 // 1 hour
33
+ const REFRESH_TOKEN_MAX_AGE = 60 * 60 * 24 * 30 // 30 days
34
+ const OAUTH_FLOW_MAX_AGE = 60 * 10 // 10 minutes
35
+
36
+ export interface User {
37
+ id: string
38
+ email: string
39
+ name: string
40
+ roles: string[]
41
+ }
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Helpers
45
+ // ---------------------------------------------------------------------------
46
+
47
+ export function sanitizeReturnTo(returnTo: string | undefined, fallback = '/dashboard'): string {
48
+ if (!returnTo || !returnTo.startsWith('/') || returnTo.startsWith('//')) {
49
+ return fallback
50
+ }
51
+ return returnTo
52
+ }
53
+
54
+ export function generatePKCE(): { codeVerifier: string; codeChallenge: string } {
55
+ const codeVerifier = crypto.randomBytes(32).toString('base64url')
56
+ const codeChallenge = crypto
57
+ .createHash('sha256')
58
+ .update(codeVerifier)
59
+ .digest('base64url')
60
+ return { codeVerifier, codeChallenge }
61
+ }
62
+
63
+ export function getAuthorizationUrl(state: string, codeChallenge: string): string {
64
+ const params = new URLSearchParams({
65
+ client_id: getClientId(),
66
+ redirect_uri: `${getAppUrl()}/api/auth/callback`,
67
+ response_type: 'code',
68
+ scope: 'openid profile email',
69
+ state,
70
+ code_challenge: codeChallenge,
71
+ code_challenge_method: 'S256',
72
+ })
73
+ return `${getBioIdUrl()}/oauth/authorize?${params.toString()}`
74
+ }
75
+
76
+ async function parseErrorResponse(response: Response, fallbackMessage: string): Promise<string> {
77
+ try {
78
+ const error = await response.json()
79
+ return error.error_description || error.error || fallbackMessage
80
+ } catch {
81
+ return fallbackMessage
82
+ }
83
+ }
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // Bio-id API calls
87
+ // ---------------------------------------------------------------------------
88
+
89
+ export async function exchangeCodeForTokens(
90
+ code: string,
91
+ codeVerifier: string
92
+ ): Promise<{
93
+ access_token: string
94
+ token_type: string
95
+ expires_in: number
96
+ refresh_token: string
97
+ scope: string
98
+ id_token?: string
99
+ }> {
100
+ const response = await fetch(`${getBioIdUrl()}/api/oauth/token`, {
101
+ method: 'POST',
102
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
103
+ body: new URLSearchParams({
104
+ grant_type: 'authorization_code',
105
+ code,
106
+ redirect_uri: `${getAppUrl()}/api/auth/callback`,
107
+ client_id: getClientId(),
108
+ client_secret: getClientSecret(),
109
+ code_verifier: codeVerifier,
110
+ }).toString(),
111
+ })
112
+
113
+ if (!response.ok) {
114
+ throw new Error(await parseErrorResponse(response, 'Token exchange failed'))
115
+ }
116
+
117
+ return response.json()
118
+ }
119
+
120
+ export async function refreshAccessToken(refreshToken: string): Promise<{
121
+ access_token: string
122
+ token_type: string
123
+ expires_in: number
124
+ refresh_token: string
125
+ scope: string
126
+ }> {
127
+ const response = await fetch(`${getBioIdUrl()}/api/oauth/token`, {
128
+ method: 'POST',
129
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
130
+ body: new URLSearchParams({
131
+ grant_type: 'refresh_token',
132
+ refresh_token: refreshToken,
133
+ client_id: getClientId(),
134
+ client_secret: getClientSecret(),
135
+ }).toString(),
136
+ })
137
+
138
+ if (!response.ok) {
139
+ throw new Error(await parseErrorResponse(response, 'Token refresh failed'))
140
+ }
141
+
142
+ return response.json()
143
+ }
144
+
145
+ export async function fetchUserInfo(accessToken: string): Promise<User> {
146
+ const response = await fetch(`${getBioIdUrl()}/api/oauth/userinfo`, {
147
+ headers: { Authorization: `Bearer ${accessToken}` },
148
+ })
149
+
150
+ if (!response.ok) {
151
+ throw new Error('Failed to fetch user info')
152
+ }
153
+
154
+ const data = await response.json()
155
+
156
+ return {
157
+ id: data.bio_id || data.sub,
158
+ email: data.email,
159
+ name: data.name,
160
+ roles: data.roles || [],
161
+ }
162
+ }
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // Session management
166
+ //
167
+ // After the OAuth callback, we create OUR OWN session JWT signed with
168
+ // JWT_SECRET. This avoids needing to verify bio-id's access token locally
169
+ // (bio-id uses HS256 signed with its own server secret).
170
+ // ---------------------------------------------------------------------------
171
+
172
+ export async function createSessionToken(user: User): Promise<string> {
173
+ return new SignJWT({
174
+ sub: user.id,
175
+ email: user.email,
176
+ name: user.name,
177
+ roles: user.roles,
178
+ })
179
+ .setProtectedHeader({ alg: 'HS256' })
180
+ .setIssuedAt()
181
+ .setExpirationTime(`${SESSION_MAX_AGE}s`)
182
+ .setIssuer(getAppUrl())
183
+ .sign(getJwtSecret())
184
+ }
185
+
186
+ export async function setSessionCookies(
187
+ sessionToken: string,
188
+ bioRefreshToken: string
189
+ ): Promise<void> {
190
+ const cookieStore = await cookies()
191
+ const secure = getAppUrl().startsWith('https://')
192
+
193
+ cookieStore.set(`${COOKIE_PREFIX}_session`, sessionToken, {
194
+ httpOnly: true,
195
+ secure,
196
+ sameSite: 'lax',
197
+ maxAge: SESSION_MAX_AGE,
198
+ path: '/',
199
+ })
200
+
201
+ cookieStore.set(`${COOKIE_PREFIX}_refresh_token`, bioRefreshToken, {
202
+ httpOnly: true,
203
+ secure,
204
+ sameSite: 'lax',
205
+ maxAge: REFRESH_TOKEN_MAX_AGE,
206
+ path: '/',
207
+ })
208
+ }
209
+
210
+ export async function clearAuthCookies(): Promise<void> {
211
+ const cookieStore = await cookies()
212
+ cookieStore.delete(`${COOKIE_PREFIX}_session`)
213
+ cookieStore.delete(`${COOKIE_PREFIX}_refresh_token`)
214
+ cookieStore.delete('oauth_state')
215
+ cookieStore.delete('oauth_code_verifier')
216
+ cookieStore.delete('oauth_return_to')
217
+ }
218
+
219
+ /**
220
+ * Get the current user from the session cookie (no network call).
221
+ * Verifies OUR session JWT — not bio-id's access token.
222
+ */
223
+ export async function getCurrentUser(): Promise<User | null> {
224
+ const cookieStore = await cookies()
225
+ const sessionToken = cookieStore.get(`${COOKIE_PREFIX}_session`)?.value
226
+
227
+ if (!sessionToken) {
228
+ return null
229
+ }
230
+
231
+ try {
232
+ const { payload } = await jwtVerify(sessionToken, getJwtSecret(), {
233
+ issuer: getAppUrl(),
234
+ })
235
+
236
+ return {
237
+ id: payload.sub || '',
238
+ email: payload.email as string,
239
+ name: payload.name as string,
240
+ roles: (payload.roles as string[]) || [],
241
+ }
242
+ } catch {
243
+ return null
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Generate a login URL with PKCE and state stored in cookies
249
+ */
250
+ export async function getLoginUrl(returnTo?: string): Promise<string> {
251
+ const cookieStore = await cookies()
252
+ const secure = getAppUrl().startsWith('https://')
253
+
254
+ const state = crypto.randomBytes(16).toString('hex')
255
+ const { codeVerifier, codeChallenge } = generatePKCE()
256
+
257
+ cookieStore.set('oauth_state', state, {
258
+ httpOnly: true,
259
+ secure,
260
+ sameSite: 'lax',
261
+ maxAge: OAUTH_FLOW_MAX_AGE,
262
+ path: '/',
263
+ })
264
+
265
+ cookieStore.set('oauth_code_verifier', codeVerifier, {
266
+ httpOnly: true,
267
+ secure,
268
+ sameSite: 'lax',
269
+ maxAge: OAUTH_FLOW_MAX_AGE,
270
+ path: '/',
271
+ })
272
+
273
+ const safeReturnTo = sanitizeReturnTo(returnTo)
274
+ cookieStore.set('oauth_return_to', safeReturnTo, {
275
+ httpOnly: true,
276
+ secure,
277
+ sameSite: 'lax',
278
+ maxAge: OAUTH_FLOW_MAX_AGE,
279
+ path: '/',
280
+ })
281
+
282
+ return getAuthorizationUrl(state, codeChallenge)
283
+ }
284
+
285
+ export { COOKIE_PREFIX, getBioIdUrl, getAppUrl }
@@ -0,0 +1,71 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import { jwtVerify } from 'jose'
3
+
4
+ const COOKIE_PREFIX = '{{name}}-web'
5
+
6
+ // Routes that require authentication
7
+ const PROTECTED_PATHS = ['/dashboard', '/applications', '/reports']
8
+
9
+ // Routes that should never be blocked
10
+ const PUBLIC_PATHS = [
11
+ '/',
12
+ '/api/auth',
13
+ '/api/health',
14
+ ]
15
+
16
+ function isPublicPath(pathname: string): boolean {
17
+ return PUBLIC_PATHS.some(
18
+ (path) => pathname === path || pathname.startsWith(`${path}/`)
19
+ )
20
+ }
21
+
22
+ function isProtectedPath(pathname: string): boolean {
23
+ return PROTECTED_PATHS.some(
24
+ (path) => pathname === path || pathname.startsWith(`${path}/`)
25
+ )
26
+ }
27
+
28
+ export async function middleware(request: NextRequest) {
29
+ const { pathname } = request.nextUrl
30
+
31
+ if (isPublicPath(pathname)) {
32
+ return NextResponse.next()
33
+ }
34
+
35
+ if (!isProtectedPath(pathname)) {
36
+ return NextResponse.next()
37
+ }
38
+
39
+ // Check for our session cookie (not bio-id's access token)
40
+ const sessionToken = request.cookies.get(`${COOKIE_PREFIX}_session`)?.value
41
+
42
+ if (!sessionToken) {
43
+ const loginUrl = new URL('/api/auth/login', request.url)
44
+ loginUrl.searchParams.set('returnTo', pathname)
45
+ return NextResponse.redirect(loginUrl)
46
+ }
47
+
48
+ // Verify our own session JWT (signed with JWT_SECRET, issued by APP_URL)
49
+ try {
50
+ const secret = new TextEncoder().encode(
51
+ process.env.JWT_SECRET || 'dev-only-secret-do-not-use-in-production'
52
+ )
53
+ await jwtVerify(sessionToken, secret, {
54
+ issuer: process.env.APP_URL || 'http://localhost:3000',
55
+ })
56
+ } catch {
57
+ // Session expired or invalid — redirect to login
58
+ const loginUrl = new URL('/api/auth/login', request.url)
59
+ loginUrl.searchParams.set('returnTo', pathname)
60
+ return NextResponse.redirect(loginUrl)
61
+ }
62
+
63
+ return NextResponse.next()
64
+ }
65
+
66
+ export const config = {
67
+ matcher: [
68
+ // Match all paths except static files and Next.js internals
69
+ '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
70
+ ],
71
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["dom", "dom.iterable", "esnext"],
4
+ "allowJs": true,
5
+ "skipLibCheck": true,
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "esModuleInterop": true,
9
+ "module": "esnext",
10
+ "moduleResolution": "bundler",
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "jsx": "preserve",
14
+ "incremental": true,
15
+ "plugins": [
16
+ {
17
+ "name": "next"
18
+ }
19
+ ],
20
+ "paths": {
21
+ "@/*": ["./src/*"]
22
+ }
23
+ },
24
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25
+ "exclude": ["node_modules"]
26
+ }
@@ -0,0 +1,11 @@
1
+ # Next.js Configuration
2
+ NEXT_PUBLIC_APP_NAME={{name}}
3
+
4
+ # API Configuration
5
+ # NEXT_PUBLIC_API_URL=https://api.example.com
6
+
7
+ # Analytics (if needed)
8
+ # NEXT_PUBLIC_GA_ID=UA-XXXXXXXXX-X
9
+
10
+ # Feature Flags
11
+ # NEXT_PUBLIC_FEATURE_X_ENABLED=false