@etus/bhono-app 0.1.4 → 0.1.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 (270) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.js +0 -0
  3. package/package.json +5 -1
  4. package/templates/base/.husky/pre-push +26 -0
  5. package/templates/base/CLAUDE.md +5 -5
  6. package/templates/base/README.md +31 -20
  7. package/templates/base/docs/app_spec.txt +13 -10
  8. package/templates/base/docs/architecture/README.md +3 -0
  9. package/templates/base/docs/architecture/data-requirements.md +4 -3
  10. package/templates/base/docs/architecture/db-bootstrap.md +39 -0
  11. package/templates/base/docs/architecture/drizzle-migration-plan.md +125 -0
  12. package/templates/base/docs/architecture/erd.md +1 -1
  13. package/templates/base/docs/architecture/sql-standards.md +100 -0
  14. package/templates/base/docs/testing.md +36 -29
  15. package/templates/base/package.json +6 -5
  16. package/templates/base/pnpm-lock.yaml +0 -123
  17. package/templates/base/schema.sql +84 -0
  18. package/templates/base/scripts/init.sh +271 -34
  19. package/templates/base/src/client/hooks/use-auth.ts +5 -0
  20. package/templates/base/src/client/routes/_authenticated/dashboard.tsx +1 -1
  21. package/templates/base/src/client/routes/index.tsx +1 -1
  22. package/templates/base/src/server/db/client.ts +3 -5
  23. package/templates/base/src/server/db/records.ts +81 -0
  24. package/templates/base/src/server/db/seed.ts +3 -2
  25. package/templates/base/src/server/db/sql.ts +96 -0
  26. package/templates/base/src/server/index.ts +16 -2
  27. package/templates/base/src/server/lib/audit.ts +74 -26
  28. package/templates/base/src/server/lib/audited-db.ts +219 -109
  29. package/templates/base/src/server/lib/transaction.ts +10 -16
  30. package/templates/base/src/server/middleware/account.ts +8 -15
  31. package/templates/base/src/server/middleware/auth.ts +102 -38
  32. package/templates/base/src/server/middleware/rate-limit.ts +6 -1
  33. package/templates/base/src/server/routes/accounts/handlers.ts +18 -6
  34. package/templates/base/src/server/routes/audits/handlers.ts +3 -1
  35. package/templates/base/src/server/routes/auth/handlers.ts +14 -9
  36. package/templates/base/src/server/routes/auth/test-login.ts +99 -45
  37. package/templates/base/src/server/routes/health/handlers.ts +4 -4
  38. package/templates/base/src/server/routes/invitations/handlers.ts +6 -3
  39. package/templates/base/src/server/routes/users/handlers.ts +21 -14
  40. package/templates/base/src/server/services/accounts.ts +242 -217
  41. package/templates/base/src/server/services/audits.ts +114 -61
  42. package/templates/base/src/server/services/auth.ts +310 -180
  43. package/templates/base/src/server/services/invitations.ts +282 -222
  44. package/templates/base/src/server/services/users.ts +383 -293
  45. package/templates/base/src/server/types/index.ts +1 -2
  46. package/templates/base/{src/server/__tests__/fixtures.ts → tests/fixtures/server.ts} +3 -3
  47. package/templates/base/{src/client/__tests__/setup-browser.ts → tests/helpers/client-setup-browser.ts} +2 -2
  48. package/templates/base/{src/client/__tests__/setup.ts → tests/helpers/client-setup.ts} +1 -1
  49. package/templates/base/{src/client/__tests__/test-utils.tsx → tests/helpers/client-test-utils.tsx} +2 -2
  50. package/templates/base/{src/server/__tests__/setup.ts → tests/helpers/server.ts} +9 -9
  51. package/templates/base/tests/integration/accounts/crud.test.ts +2 -11
  52. package/templates/base/tests/integration/audits/list.test.ts +2 -11
  53. package/templates/base/tests/integration/auth/auth-service.test.ts +1 -10
  54. package/templates/base/tests/integration/auth/invitation-token.test.ts +2 -11
  55. package/templates/base/tests/integration/auth/logout.test.ts +2 -11
  56. package/templates/base/tests/integration/auth/oauth.test.ts +23 -42
  57. package/templates/base/tests/integration/auth/refresh-token.test.ts +1 -9
  58. package/templates/base/tests/integration/auth/session-expiry.test.ts +1 -9
  59. package/templates/base/tests/integration/auth/session.test.ts +2 -11
  60. package/templates/base/tests/integration/auth/super-admin.test.ts +1 -9
  61. package/templates/base/tests/integration/authorization/analytics-role.test.ts +2 -11
  62. package/templates/base/tests/integration/authorization/billing-role.test.ts +2 -11
  63. package/templates/base/tests/integration/authorization/guards-roles.test.ts +1 -9
  64. package/templates/base/tests/integration/authorization/multi-tenancy.test.ts +2 -11
  65. package/templates/base/tests/integration/authorization/roles.test.ts +2 -11
  66. package/templates/base/tests/integration/config/production-behavior.test.ts +2 -11
  67. package/templates/base/tests/integration/health/health.test.ts +25 -44
  68. package/templates/base/tests/integration/invitations/crud.test.ts +2 -11
  69. package/templates/base/tests/integration/invitations/email.test.ts +1 -9
  70. package/templates/base/tests/integration/middleware/auth.test.ts +3 -12
  71. package/templates/base/tests/integration/middleware/request-logger.test.ts +1 -9
  72. package/templates/base/tests/integration/performance/response-times.test.ts +1 -9
  73. package/templates/base/tests/integration/security/cookie-security.test.ts +2 -11
  74. package/templates/base/tests/integration/security/csrf-protection.test.ts +2 -11
  75. package/templates/base/tests/integration/security/log-sanitization.test.ts +1 -9
  76. package/templates/base/tests/integration/security/rate-limiting.test.ts +1 -9
  77. package/templates/base/tests/integration/security/sql-injection.test.ts +7 -18
  78. package/templates/base/tests/integration/security/xss-prevention.test.ts +2 -11
  79. package/templates/base/tests/integration/setup.ts +13 -90
  80. package/templates/base/tests/integration/smoke.test.ts +3 -2
  81. package/templates/base/tests/integration/storage/upload.test.ts +2 -11
  82. package/templates/base/tests/integration/storage/validation.test.ts +2 -11
  83. package/templates/base/tests/integration/users/crud.test.ts +2 -11
  84. package/templates/base/tests/integration/users/list.test.ts +2 -11
  85. package/templates/base/tests/integration/vitest.config.ts +2 -9
  86. package/templates/base/{src/server/__tests__ → tests}/mocks/db.ts +1 -1
  87. package/templates/base/{src/server/__tests__ → tests}/mocks/index.ts +1 -1
  88. package/templates/base/{src/server/__tests__ → tests}/mocks/kv.ts +1 -1
  89. package/templates/base/{src/server/__tests__ → tests}/mocks/r2.ts +1 -1
  90. package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/sidebar.test.tsx +1 -1
  91. package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/avatar.test.tsx +1 -1
  92. package/templates/base/{src/client/__tests__ → tests/unit/client/components/ui}/button.test.tsx +1 -1
  93. package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/card.test.tsx +1 -1
  94. package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/dialog.test.tsx +1 -1
  95. package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/input.test.tsx +1 -1
  96. package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/loading-skeleton.test.tsx +1 -1
  97. package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/skeleton.test.tsx +1 -1
  98. package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/sonner.test.tsx +1 -1
  99. package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/tabs.test.tsx +1 -1
  100. package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/account.test.tsx +1 -1
  101. package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/integrations.test.tsx +1 -1
  102. package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/settings.test.tsx +1 -1
  103. package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/team.test.tsx +1 -1
  104. package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/authenticated-layout.test.tsx +1 -1
  105. package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/dashboard.test.tsx +1 -1
  106. package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/invite.test.tsx +1 -1
  107. package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/login.test.tsx +1 -1
  108. package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/navigation.test.tsx +1 -1
  109. package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/root-layout.test.tsx +1 -1
  110. package/templates/base/{src/server/auth/__tests__ → tests/unit/server/auth}/guards.test.ts +2 -2
  111. package/templates/base/{src → tests/unit}/server/auth/permissions.test.ts +1 -1
  112. package/templates/base/{src → tests/unit}/server/auth/roles.test.ts +1 -1
  113. package/templates/base/tests/unit/server/db/sql.test.ts +68 -0
  114. package/templates/base/{src → tests/unit}/server/env.test.ts +1 -1
  115. package/templates/base/tests/unit/server/lib/audited-db.test.ts +78 -0
  116. package/templates/base/{src → tests/unit}/server/lib/email.test.ts +1 -1
  117. package/templates/base/{src → tests/unit}/server/lib/errors.test.ts +1 -1
  118. package/templates/base/{src → tests/unit}/server/lib/oauth.test.ts +1 -1
  119. package/templates/base/{src → tests/unit}/server/lib/pagination.test.ts +1 -1
  120. package/templates/base/{src → tests/unit}/server/lib/password.test.ts +1 -1
  121. package/templates/base/{src → tests/unit}/server/lib/providers.test.ts +1 -1
  122. package/templates/base/{src → tests/unit}/server/lib/r2-storage.test.ts +2 -2
  123. package/templates/base/{src → tests/unit}/server/lib/session.test.ts +2 -2
  124. package/templates/base/{src → tests/unit}/server/lib/tokens.test.ts +1 -1
  125. package/templates/base/{src → tests/unit}/server/lib/transaction.test.ts +5 -14
  126. package/templates/base/{src → tests/unit}/server/middleware/account.test.ts +16 -24
  127. package/templates/base/{src → tests/unit}/server/middleware/auth.test.ts +71 -42
  128. package/templates/base/{src → tests/unit}/server/middleware/cors.test.ts +1 -1
  129. package/templates/base/{src → tests/unit}/server/middleware/error-handler.test.ts +2 -2
  130. package/templates/base/{src → tests/unit}/server/middleware/rate-limit.test.ts +3 -2
  131. package/templates/base/{src → tests/unit}/server/middleware/request-context.test.ts +1 -1
  132. package/templates/base/{src → tests/unit}/server/middleware/request-logger.test.ts +1 -1
  133. package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/db.test.ts +1 -1
  134. package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/kv.test.ts +1 -1
  135. package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/r2.test.ts +1 -1
  136. package/templates/base/{src/server/routes/accounts/__tests__ → tests/unit/server/routes/accounts}/handlers.test.ts +12 -12
  137. package/templates/base/{src/server/routes/audits/__tests__ → tests/unit/server/routes/audits}/handlers.test.ts +11 -11
  138. package/templates/base/{src/server/routes/auth/__tests__ → tests/unit/server/routes/auth}/handlers.test.ts +13 -13
  139. package/templates/base/{src/server/routes/health/__tests__ → tests/unit/server/routes/health}/handlers.test.ts +27 -23
  140. package/templates/base/{src/server/routes/invitations/__tests__ → tests/unit/server/routes/invitations}/handlers.test.ts +14 -17
  141. package/templates/base/{src/server/routes/storage/__tests__ → tests/unit/server/routes/storage}/handlers.test.ts +6 -6
  142. package/templates/base/{src/server/routes/users/__tests__ → tests/unit/server/routes/users}/handlers.test.ts +12 -12
  143. package/templates/base/tests/unit/server/services/accounts.test.ts +258 -0
  144. package/templates/base/tests/unit/server/services/audits.test.ts +141 -0
  145. package/templates/base/tests/unit/server/services/auth.test.ts +179 -0
  146. package/templates/base/tests/unit/server/services/invitations.test.ts +165 -0
  147. package/templates/base/tests/unit/server/services/users.test.ts +351 -0
  148. package/templates/base/tsconfig.json +2 -1
  149. package/templates/base/vitest.config.browser.ts +3 -2
  150. package/templates/base/vitest.config.frontend.ts +3 -2
  151. package/templates/base/vitest.config.ts +7 -14
  152. package/templates/base/.claude/settings.local.json +0 -11
  153. package/templates/base/config/drizzle.config.ts +0 -10
  154. package/templates/base/src/server/db/schema/accounts.ts +0 -20
  155. package/templates/base/src/server/db/schema/audit-logs.ts +0 -26
  156. package/templates/base/src/server/db/schema/index.ts +0 -7
  157. package/templates/base/src/server/db/schema/invitations.ts +0 -30
  158. package/templates/base/src/server/db/schema/refresh-tokens.ts +0 -22
  159. package/templates/base/src/server/db/schema/user-accounts.ts +0 -25
  160. package/templates/base/src/server/db/schema/users.ts +0 -33
  161. package/templates/base/src/server/lib/audited-db.test.ts +0 -107
  162. package/templates/base/src/server/lib/schema-helpers.ts +0 -16
  163. package/templates/base/src/server/services/__tests__/accounts.test.ts +0 -764
  164. package/templates/base/src/server/services/__tests__/audits.test.ts +0 -235
  165. package/templates/base/src/server/services/__tests__/auth.test.ts +0 -765
  166. package/templates/base/src/server/services/__tests__/invitations.test.ts +0 -704
  167. package/templates/base/src/server/services/__tests__/users.test.ts +0 -755
  168. package/templates/base/tests/integration/lib/schema-helpers.test.ts +0 -129
  169. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-can-be-collapsed-by-default-1.png +0 -0
  170. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-expands-when-collapsed-and-expand-button-is-clicked-1.png +0 -0
  171. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-handles-logout-button-click-1.png +0 -0
  172. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-handles-navigation-clicks-1.png +0 -0
  173. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-hides-navigation-labels-when-collapsed-1.png +0 -0
  174. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-highlights-active-route-1.png +0 -0
  175. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-renders-sidebar-navigation-items-1.png +0 -0
  176. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-shows-keyboard-shortcut-hint-when-expanded-1.png +0 -0
  177. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-shows-user-info-when-authenticated-1.png +0 -0
  178. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-shows-user-initials-in-avatar-fallback-1.png +0 -0
  179. /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/error-boundary.test.tsx +0 -0
  180. /package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/error-fallback.test.tsx +0 -0
  181. /package/templates/base/{src/client/hooks/__tests__ → tests/unit/client/hooks}/use-auth.test.tsx +0 -0
  182. /package/templates/base/{src/client/hooks/__tests__ → tests/unit/client/hooks}/use-theme.test.tsx +0 -0
  183. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-dashboard-stats-cards-1.png +0 -0
  184. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-quick-action-cards-1.png +0 -0
  185. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-recent-activity-section-1.png +0 -0
  186. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-user-first-name-in-welcome-message-1.png +0 -0
  187. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-user-information-in-sidebar-1.png +0 -0
  188. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-render-dashboard-when-authenticated-1.png +0 -0
  189. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-show-navigation-sidebar-1.png +0 -0
  190. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-unauthenticated-should-redirect-to-login-when-not-authenticated-1.png +0 -0
  191. /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-accept-invitation-button-1.png +0 -0
  192. /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-decline-button-linking-to-homepage-1.png +0 -0
  193. /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-invitation-details--email--workspace--role--1.png +0 -0
  194. /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-have-a-logo-link-to-homepage-1.png +0 -0
  195. /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-render-invitation-page-with-inviter-name-and-workspace-1.png +0 -0
  196. /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-show-terms-of-service-and-privacy-policy-links-1.png +0 -0
  197. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-display-Google-OAuth-login-button-1.png +0 -0
  198. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-have-a-link-back-to-home-page-1.png +0 -0
  199. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-render-login-content-without-waiting-for-authentication-1.png +0 -0
  200. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-render-login-page-at--login-route-1.png +0 -0
  201. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-show-Terms-of-Service-and-Privacy-Policy-links-1.png +0 -0
  202. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-trigger-OAuth-flow-when-clicking-login-button-1.png +0 -0
  203. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-display-404-text-on-not-found-page-1.png +0 -0
  204. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-have-navigation-options-on-404-page-1.png +0 -0
  205. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-render-404-page-for-unknown-routes-1.png +0 -0
  206. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-account-page-1.png +0 -0
  207. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-integrations-page-1.png +0 -0
  208. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-settings-page-1.png +0 -0
  209. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-team-page-1.png +0 -0
  210. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-home-page-navigation-should-display-navigation-links-on-home-page-1.png +0 -0
  211. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-home-page-navigation-should-have-correct-link-destinations-on-home-page-1.png +0 -0
  212. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-allow-access-to-home-page-without-authentication-1.png +0 -0
  213. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-allow-access-to-login-page-without-authentication-1.png +0 -0
  214. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-dashboard-to-login-1.png +0 -0
  215. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-settings-to-login-1.png +0 -0
  216. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-team-to-login-1.png +0 -0
  217. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-render-Active-Sessions-section-with-session-cards-1.png +0 -0
  218. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-render-page-with-correct-title--Account--1.png +0 -0
  219. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-API-Access-section-1.png +0 -0
  220. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-Connected-Accounts-section-with-Google-connected-1.png +0 -0
  221. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-Danger-Zone-section-with-delete-button-1.png +0 -0
  222. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-Security-section-with-Two-Factor-Authentication-1.png +0 -0
  223. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-API-documentation-section-should-display-API-documentation-link-section-1.png +0 -0
  224. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-Create-Webhook-Dialog-should-have-Add-Webhook-trigger-button-1.png +0 -0
  225. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-category-filters-should-display-all-category-buttons-1.png +0 -0
  226. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-display-all-integration-cards-with-names-1.png +0 -0
  227. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-display-category-badges-on-cards-1.png +0 -0
  228. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Configure-button-for-connected-integrations-1.png +0 -0
  229. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Connect-button-for-not-connected-integrations-1.png +0 -0
  230. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Connected-badge-for-connected-integrations-1.png +0 -0
  231. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-Available-Integrations-section-1.png +0 -0
  232. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-Webhooks-section-1.png +0 -0
  233. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-connected-count-1.png +0 -0
  234. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-page-description-1.png +0 -0
  235. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-search-input-1.png +0 -0
  236. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-render-with-correct-title--Integrations--1.png +0 -0
  237. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-filter-integrations-based-on-search-query-1.png +0 -0
  238. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-search-by-description-1.png +0 -0
  239. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-show-no-results-message-when-search-has-no-matches-1.png +0 -0
  240. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-existing-webhook-1.png +0 -0
  241. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-events-1.png +0 -0
  242. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-last-delivery-info-1.png +0 -0
  243. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-success-status-1.png +0 -0
  244. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-have-Add-Webhook-button-1.png +0 -0
  245. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Account-tab-should-display-connected-accounts-section-with-Google-provider-1.png +0 -0
  246. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Account-tab-should-display-sessions-and-danger-zone-sections-1.png +0 -0
  247. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-all-notification-toggle-options-1.png +0 -0
  248. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-email-notifications-section-1.png +0 -0
  249. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-toggle-switches-for-notification-options-1.png +0 -0
  250. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-have-toggles-checked-by-default-1.png +0 -0
  251. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display--Save-Changes--button-1.png +0 -0
  252. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-personal-information-form-1.png +0 -0
  253. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-profile-picture-section-1.png +0 -0
  254. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-email-in-disabled-email-input-1.png +0 -0
  255. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-initials-in-avatar-1.png +0 -0
  256. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-name-in-the-name-input-1.png +0 -0
  257. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-display-all-three-tabs-1.png +0 -0
  258. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-display-page-description-1.png +0 -0
  259. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-render-with-correct-title--Settings--1.png +0 -0
  260. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-show-Profile-tab-as-default-active-tab-1.png +0 -0
  261. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-switch-to-Account-tab-when-clicked-1.png +0 -0
  262. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-switch-to-Notifications-tab-when-clicked-1.png +0 -0
  263. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-display-Active-Members-section-with-member-count-1.png +0 -0
  264. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-display-Pending-Invitations-section-with-invitation-details-1.png +0 -0
  265. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-have-invite-member-button-that-can-be-clicked-1.png +0 -0
  266. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-render-page-with-correct-title-and-description-1.png +0 -0
  267. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-render-search-input-that-filters-team-members-1.png +0 -0
  268. /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-show-current-user-with---you---indicator-and-role-badge-1.png +0 -0
  269. /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/error-components.test.tsx +0 -0
  270. /package/templates/base/{src/shared/schemas/__tests__ → tests/unit/shared}/schemas.test.ts +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/bin/bash
2
2
 
3
3
  # BHono - Development Environment Setup
4
- # This script bootstraps dependencies, configures Cloudflare bindings,
4
+ # Bootstraps dependencies, configures Cloudflare bindings,
5
5
  # seeds the local D1 (sqlite) database, and starts the dev server.
6
6
 
7
7
  set -euo pipefail
@@ -19,6 +19,10 @@ cd "$ROOT_DIR"
19
19
  UPDATE_PACKAGES=0
20
20
  SKIP_DEV=0
21
21
  SKIP_PROVISION=0
22
+ SKIP_SEED=0
23
+ DEV_PORT=""
24
+ GOOGLE_CLIENT_ID_ARG=""
25
+ GOOGLE_CLIENT_SECRET_ARG=""
22
26
 
23
27
  while [[ $# -gt 0 ]]; do
24
28
  case "$1" in
@@ -31,6 +35,54 @@ while [[ $# -gt 0 ]]; do
31
35
  --no-provision)
32
36
  SKIP_PROVISION=1
33
37
  ;;
38
+ --skip-seed)
39
+ SKIP_SEED=1
40
+ ;;
41
+ --port)
42
+ DEV_PORT="$2"
43
+ shift
44
+ ;;
45
+ --port=*)
46
+ DEV_PORT="${1#*=}"
47
+ ;;
48
+ --google-id)
49
+ GOOGLE_CLIENT_ID_ARG="$2"
50
+ shift
51
+ ;;
52
+ --google-id=*)
53
+ GOOGLE_CLIENT_ID_ARG="${1#*=}"
54
+ ;;
55
+ --google-secret)
56
+ GOOGLE_CLIENT_SECRET_ARG="$2"
57
+ shift
58
+ ;;
59
+ --google-secret=*)
60
+ GOOGLE_CLIENT_SECRET_ARG="${1#*=}"
61
+ ;;
62
+ --help|-h)
63
+ echo ""
64
+ echo "BHono - Development Environment Setup"
65
+ echo ""
66
+ echo "Usage: ./scripts/init.sh [OPTIONS]"
67
+ echo ""
68
+ echo "Options:"
69
+ echo " --port PORT Set dev server port (default: 8787)"
70
+ echo " --google-id ID Set Google OAuth Client ID"
71
+ echo " --google-secret SEC Set Google OAuth Client Secret"
72
+ echo " --no-provision Skip Cloudflare resource provisioning"
73
+ echo " --skip-dev Don't start dev server after setup"
74
+ echo " --skip-seed Skip database seeding"
75
+ echo " --update Update dependencies"
76
+ echo " --help, -h Show this help message"
77
+ echo ""
78
+ echo "Examples:"
79
+ echo " ./scripts/init.sh"
80
+ echo " ./scripts/init.sh --port 3000"
81
+ echo " ./scripts/init.sh --port 8787 --google-id 'xxx.apps.googleusercontent.com' --google-secret 'GOCSPX-xxx'"
82
+ echo " CLOUDFLARE_ACCOUNT_ID=xxx ./scripts/init.sh"
83
+ echo ""
84
+ exit 0
85
+ ;;
34
86
  *)
35
87
  echo -e "${YELLOW}Ignoring unknown argument: $1${NC}"
36
88
  ;;
@@ -38,6 +90,9 @@ while [[ $# -gt 0 ]]; do
38
90
  shift
39
91
  done
40
92
 
93
+ # Set default port if not specified
94
+ DEV_PORT="${DEV_PORT:-8787}"
95
+
41
96
  log_info() { echo -e "${BLUE}$*${NC}"; }
42
97
  log_ok() { echo -e "${GREEN}$*${NC}"; }
43
98
  log_warn() { echo -e "${YELLOW}$*${NC}"; }
@@ -110,6 +165,38 @@ fi
110
165
 
111
166
  log_ok "Project name: $PROJECT_NAME"
112
167
 
168
+ # ============================================================================
169
+ # FIX PROJECT NAME IN ALL CONFIG FILES (handles "." issue from bhono-app)
170
+ # ============================================================================
171
+ log_info "Fixing project name in config files..."
172
+
173
+ # Fix package.json if name is "." or empty
174
+ if [[ -f package.json ]]; then
175
+ PROJECT_NAME="$PROJECT_NAME" node -e "
176
+ const fs = require('fs');
177
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
178
+ if (pkg.name === '.' || pkg.name === '' || !pkg.name) {
179
+ pkg.name = process.env.PROJECT_NAME;
180
+ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
181
+ console.log(' Fixed package.json name');
182
+ }
183
+ " 2>/dev/null || true
184
+ fi
185
+
186
+ # Fix etus.config.json if name is "." or empty
187
+ if [[ -f etus.config.json ]]; then
188
+ PROJECT_NAME="$PROJECT_NAME" node -e "
189
+ const fs = require('fs');
190
+ const data = JSON.parse(fs.readFileSync('etus.config.json', 'utf8'));
191
+ if (data.name === '.' || data.name === '' || !data.name) {
192
+ data.name = process.env.PROJECT_NAME;
193
+ data.domain = process.env.PROJECT_NAME + '.com';
194
+ fs.writeFileSync('etus.config.json', JSON.stringify(data, null, 2));
195
+ console.log(' Fixed etus.config.json name');
196
+ }
197
+ " 2>/dev/null || true
198
+ fi
199
+
113
200
  # Ensure wrangler.json exists
114
201
  if [[ ! -f config/wrangler.json ]]; then
115
202
  log_err "Missing config/wrangler.json"
@@ -147,9 +234,120 @@ if (!data.name || data.name.includes('{{projectName}}')) {
147
234
  fs.writeFileSync(path, JSON.stringify(data, null, 2));
148
235
  NODE
149
236
 
237
+ # ============================================================================
238
+ # FIX VITE.CONFIG.TS (configPath + server port)
239
+ # ============================================================================
240
+ log_info "Checking vite.config.ts..."
241
+
242
+ if [[ -f vite.config.ts ]]; then
243
+ VITE_UPDATED=0
244
+
245
+ # Add configPath to cloudflare plugin if not present
246
+ if ! grep -q "configPath:" vite.config.ts; then
247
+ sed -i.bak 's/cloudflare()/cloudflare({\n configPath: ".\\/config\\/wrangler.json",\n })/g' vite.config.ts
248
+ rm -f vite.config.ts.bak
249
+ VITE_UPDATED=1
250
+ log_ok " Added configPath to cloudflare plugin"
251
+ fi
252
+
253
+ # Add server port if not present
254
+ if ! grep -q "server:" vite.config.ts; then
255
+ sed -i.bak "s/export default defineConfig({/export default defineConfig({\n server: {\n port: $DEV_PORT,\n },/g" vite.config.ts
256
+ rm -f vite.config.ts.bak
257
+ VITE_UPDATED=1
258
+ log_ok " Added server port $DEV_PORT"
259
+ fi
260
+
261
+ [[ "$VITE_UPDATED" -eq 0 ]] && log_ok " vite.config.ts already configured"
262
+ fi
263
+
264
+ # ============================================================================
265
+ # CREATE/UPDATE CONFIG/.DEV.VARS
266
+ # ============================================================================
267
+ log_info "Checking config/.dev.vars..."
268
+
269
+ # Determine Google OAuth credentials
270
+ GOOGLE_ID="${GOOGLE_CLIENT_ID_ARG:-seu-google-client-id}"
271
+ GOOGLE_SECRET="${GOOGLE_CLIENT_SECRET_ARG:-seu-google-client-secret}"
272
+
273
+ if [[ ! -f config/.dev.vars ]]; then
274
+ # Generate a random JWT secret
275
+ JWT_RAND=$(openssl rand -hex 16 2>/dev/null || node -e "console.log(require('crypto').randomBytes(16).toString('hex'))")
276
+
277
+ cat > config/.dev.vars << EOF
278
+ # Environment
279
+ ENVIRONMENT=development
280
+ APP_URL=http://localhost:$DEV_PORT
281
+
282
+ # JWT Configuration (IMPORTANTE: mínimo 32 caracteres)
283
+ JWT_SECRET=super-secret-jwt-key-with-at-least-32-chars-${JWT_RAND}
284
+ JWT_EXPIRY_MINUTES=15
285
+
286
+ # Google OAuth
287
+ GOOGLE_CLIENT_ID=$GOOGLE_ID
288
+ GOOGLE_CLIENT_SECRET=$GOOGLE_SECRET
289
+ GOOGLE_REDIRECT_URI=http://localhost:$DEV_PORT/auth/callback
290
+
291
+ # Refresh Token
292
+ REFRESH_TOKEN_EXPIRY_DAYS=30
293
+
294
+ # SendGrid (opcional para desenvolvimento)
295
+ SENDGRID_API_KEY=your-sendgrid-api-key
296
+ SENDGRID_FROM_EMAIL=noreply@example.com
297
+ EOF
298
+ log_ok " config/.dev.vars created"
299
+
300
+ if [[ "$GOOGLE_ID" == "seu-google-client-id" ]]; then
301
+ log_warn " IMPORTANTE: Edite config/.dev.vars com suas credenciais Google OAuth!"
302
+ else
303
+ log_ok " Google OAuth credentials configured"
304
+ fi
305
+ else
306
+ # Update Google credentials if provided via arguments
307
+ if [[ -n "$GOOGLE_CLIENT_ID_ARG" ]]; then
308
+ sed -i.bak "s|GOOGLE_CLIENT_ID=.*|GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID_ARG|g" config/.dev.vars
309
+ rm -f config/.dev.vars.bak
310
+ log_ok " Updated GOOGLE_CLIENT_ID"
311
+ fi
312
+ if [[ -n "$GOOGLE_CLIENT_SECRET_ARG" ]]; then
313
+ sed -i.bak "s|GOOGLE_CLIENT_SECRET=.*|GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET_ARG|g" config/.dev.vars
314
+ rm -f config/.dev.vars.bak
315
+ log_ok " Updated GOOGLE_CLIENT_SECRET"
316
+ fi
317
+
318
+ # Ensure APP_URL is set for local development
319
+ if ! grep -q "APP_URL=http://localhost" config/.dev.vars; then
320
+ echo "" >> config/.dev.vars
321
+ echo "# Added by init.sh" >> config/.dev.vars
322
+ echo "APP_URL=http://localhost:$DEV_PORT" >> config/.dev.vars
323
+ log_ok " Added APP_URL to config/.dev.vars"
324
+ fi
325
+
326
+ # Update redirect URI if port changed
327
+ if ! grep -q "GOOGLE_REDIRECT_URI=http://localhost:$DEV_PORT" config/.dev.vars; then
328
+ sed -i.bak "s|GOOGLE_REDIRECT_URI=http://localhost:[0-9]*|GOOGLE_REDIRECT_URI=http://localhost:$DEV_PORT|g" config/.dev.vars
329
+ rm -f config/.dev.vars.bak
330
+ fi
331
+ fi
332
+
333
+ # ============================================================================
334
+ # UPDATE .GITIGNORE FOR SECURITY
335
+ # ============================================================================
336
+ log_info "Checking .gitignore..."
337
+
338
+ if [[ -f .gitignore ]]; then
339
+ if ! grep -q "config/.dev.vars" .gitignore; then
340
+ echo "config/.dev.vars" >> .gitignore
341
+ log_ok " Added config/.dev.vars to .gitignore"
342
+ fi
343
+ fi
344
+
150
345
  WRANGLER="pnpm exec wrangler"
346
+ WRANGLER_CONFIG="$WRANGLER --config config/wrangler.json"
347
+ WRANGLER_AVAILABLE=1
151
348
  if ! $WRANGLER --version >/dev/null 2>&1; then
152
- log_warn "Wrangler not available. Ensure dependencies are installed."
349
+ WRANGLER_AVAILABLE=0
350
+ log_warn "Wrangler not available. Cloudflare steps will be skipped."
153
351
  fi
154
352
 
155
353
  # Resolve names from wrangler.json
@@ -158,6 +356,8 @@ if [[ -z "$DB_NAME" ]]; then
158
356
  DB_NAME="${PROJECT_NAME}-db"
159
357
  fi
160
358
 
359
+ DB_BINDING=$(node -e "const c=require('./config/wrangler.json'); console.log((c.d1_databases&&c.d1_databases[0]&&c.d1_databases[0].binding)||'DB');")
360
+
161
361
  R2_BUCKET=$(node -e "const c=require('./config/wrangler.json'); console.log((c.r2_buckets&&c.r2_buckets[0]&&c.r2_buckets[0].bucket_name)||'');")
162
362
  if [[ -z "$R2_BUCKET" ]]; then
163
363
  R2_BUCKET="${PROJECT_NAME}-storage"
@@ -171,7 +371,7 @@ KV_ID=$(node -e "const c=require('./config/wrangler.json'); console.log((c.kv_na
171
371
  if [[ "$D1_ID" == "TO_BE_PROVISIONED" ]]; then D1_ID=""; fi
172
372
  if [[ "$KV_ID" == "TO_BE_PROVISIONED" ]]; then KV_ID=""; fi
173
373
 
174
- if [[ "$SKIP_PROVISION" -eq 0 ]]; then
374
+ if [[ "$SKIP_PROVISION" -eq 0 && "$WRANGLER_AVAILABLE" -eq 1 ]]; then
175
375
  if $WRANGLER whoami >/dev/null 2>&1; then
176
376
  log_info "Provisioning Cloudflare resources (D1, KV, R2)..."
177
377
 
@@ -239,47 +439,84 @@ NODE
239
439
  log_info "Generating Cloudflare types..."
240
440
  pnpm cf-typegen >/dev/null 2>&1 || log_warn "Skipping cf-typegen (wrangler not configured)"
241
441
 
242
- # Ensure local D1 exists and update drizzle config
442
+ # Ensure local D1 exists
243
443
  log_info "Preparing local D1 database..."
244
- $WRANGLER d1 execute "$DB_NAME" --local --command "SELECT 1;" >/dev/null 2>&1 || true
444
+ if [[ "$WRANGLER_AVAILABLE" -eq 1 ]]; then
445
+ $WRANGLER_CONFIG d1 execute "$DB_NAME" --local --command "SELECT 1;" >/dev/null 2>&1 || log_warn "Local D1 init failed (continuing)."
446
+ fi
245
447
 
246
- D1_ID_FROM_CONFIG=$(node -e "const c=require('./config/wrangler.json'); console.log((c.d1_databases&&c.d1_databases[0]&&c.d1_databases[0].database_id)||'');")
247
- if [[ -n "$D1_ID_FROM_CONFIG" ]]; then
248
- LOCAL_DB_PATH=".wrangler/state/v3/d1/miniflare-D1DatabaseObject/${D1_ID_FROM_CONFIG}.sqlite"
249
- DRIZZLE_DB_URL="../${LOCAL_DB_PATH}"
448
+ # Apply schema.sql and seed
449
+ DB_BOOTSTRAP_OK=0
450
+ SEED_OK=0
250
451
 
251
- if [[ -f config/drizzle.config.ts ]]; then
252
- DRIZZLE_DB_URL="$DRIZZLE_DB_URL" node - <<'NODE'
253
- const fs = require('fs');
254
- const path = 'config/drizzle.config.ts';
255
- const nextUrl = process.env.DRIZZLE_DB_URL;
256
- let content = fs.readFileSync(path, 'utf8');
257
- const pattern = /url:\s*['"][^'"]*\.sqlite['"]/;
258
- if (pattern.test(content)) {
259
- content = content.replace(pattern, `url: '${nextUrl}'`);
260
- } else {
261
- // Fallback: append url if not found
262
- content = content.replace('dbCredentials: {', `dbCredentials: {\n url: '${nextUrl}',`);
263
- }
264
- fs.writeFileSync(path, content);
265
- NODE
266
- log_ok "Updated config/drizzle.config.ts with local DB path."
452
+ if [[ "$WRANGLER_AVAILABLE" -eq 1 ]]; then
453
+ log_info "Applying schema.sql to local D1..."
454
+ if pnpm db:schema:local >/tmp/bhono-db-schema.log 2>&1; then
455
+ DB_BOOTSTRAP_OK=1
456
+ else
457
+ log_warn "Schema apply failed."
458
+ tail -n 20 /tmp/bhono-db-schema.log || true
267
459
  fi
268
460
  else
269
- log_warn "D1 id not found in wrangler.json. Skipping drizzle config update."
461
+ log_warn "Wrangler not available. Skipping schema apply."
270
462
  fi
271
463
 
272
- # Push schema (no migrations) and seed
273
- log_info "Pushing schema to local D1..."
274
- pnpm db:push >/dev/null 2>&1 || log_warn "Schema push skipped (check drizzle config and local D1)."
464
+ if [[ "$SKIP_SEED" -eq 0 ]]; then
465
+ if [[ "$WRANGLER_AVAILABLE" -eq 1 ]]; then
466
+ log_info "Seeding local D1..."
467
+ if pnpm db:seed:local >/tmp/bhono-seed.log 2>&1; then
468
+ SEED_OK=1
469
+ else
470
+ log_warn "Seed apply failed."
471
+ tail -n 20 /tmp/bhono-seed.log || true
472
+ fi
473
+ else
474
+ log_warn "Wrangler not available. Skipping seed apply."
475
+ fi
476
+ else
477
+ log_warn "Skipping seed step (--skip-seed)."
478
+ fi
275
479
 
276
- log_info "Generating seed data..."
277
- pnpm db:seed >/dev/null
480
+ rm -f /tmp/bhono-db-schema.log /tmp/bhono-seed.log >/dev/null 2>&1 || true
481
+
482
+ # ============================================================================
483
+ # SYNC DATABASES (handle plugin hash mismatch)
484
+ # ============================================================================
485
+ # The Cloudflare Vite plugin creates SQLite with a hash based on config,
486
+ # not the database_id. We need to sync all databases after seeding.
487
+ log_info "Synchronizing database files..."
488
+
489
+ SQLITE_DIR=".wrangler/state/v3/d1/miniflare-D1DatabaseObject"
490
+ if [[ -d "$SQLITE_DIR" ]]; then
491
+ SQLITE_FILES=$(find "$SQLITE_DIR" -name "*.sqlite" -not -name "*-shm" -not -name "*-wal" 2>/dev/null || true)
492
+ if [[ -n "$SQLITE_FILES" ]]; then
493
+ # Find the database with actual tables (largest file usually has data)
494
+ MAIN_DB=$(ls -S $SQLITE_FILES 2>/dev/null | head -1)
495
+
496
+ if [[ -n "$MAIN_DB" ]]; then
497
+ # Check if main DB has tables
498
+ HAS_TABLES=$(sqlite3 "$MAIN_DB" ".tables" 2>/dev/null | wc -w || echo "0")
499
+
500
+ if [[ "$HAS_TABLES" -gt 0 ]]; then
501
+ for DB in $SQLITE_FILES; do
502
+ if [[ "$DB" != "$MAIN_DB" ]]; then
503
+ cp "$MAIN_DB" "$DB" 2>/dev/null || true
504
+ fi
505
+ done
506
+ log_ok " Synced $(echo "$SQLITE_FILES" | wc -l | tr -d ' ') database files"
507
+ else
508
+ log_warn " Main database has no tables. Skipping sync."
509
+ fi
510
+ fi
511
+ fi
512
+ fi
278
513
 
279
- log_info "Seeding local D1..."
280
- $WRANGLER d1 execute "$DB_NAME" --local --file=seed.sql >/dev/null 2>&1 || log_warn "Seed failed (check local D1)."
514
+ if [[ "$DB_BOOTSTRAP_OK" -eq 1 && ( "$SEED_OK" -eq 1 || "$SKIP_SEED" -eq 1 ) ]]; then
515
+ log_ok "Local D1 ready with schema${SKIP_SEED:+ (seed skipped)}."
516
+ else
517
+ log_warn "Local D1 setup incomplete. Review warnings above."
518
+ fi
281
519
 
282
- log_ok "Local D1 ready with seed data."
283
520
  log_info "Seed data is defined in src/server/db/seed.ts (customize as needed)."
284
521
 
285
522
  if [[ "$SKIP_DEV" -eq 0 ]]; then
@@ -27,7 +27,12 @@ export function useAuth() {
27
27
  queryKey: ['auth', 'me'],
28
28
  queryFn: fetchMe,
29
29
  retry: false,
30
+ // Balance between reducing API calls and detecting expired sessions
30
31
  staleTime: 1000 * 60 * 5, // 5 minutes
32
+ gcTime: 1000 * 60 * 30, // 30 minutes
33
+ refetchOnMount: false, // Avoid redundant calls on component mount
34
+ refetchOnWindowFocus: true, // Re-check when user returns to tab (industry standard)
35
+ refetchOnReconnect: true, // Re-check after network reconnection
31
36
  })
32
37
 
33
38
  const logoutMutation = useMutation({
@@ -82,7 +82,7 @@ function DashboardPage() {
82
82
  </CardHeader>
83
83
  <CardContent>
84
84
  <p className="text-sm text-muted-foreground">
85
- Built with Drizzle ORM for type-safe database queries.
85
+ Built with SQL-first access to Cloudflare D1.
86
86
  </p>
87
87
  </CardContent>
88
88
  </Card>
@@ -100,7 +100,7 @@ function HomePage() {
100
100
  <TechBadge name="Hono" />
101
101
  <TechBadge name="React" />
102
102
  <TechBadge name="TypeScript" />
103
- <TechBadge name="Drizzle" />
103
+ <TechBadge name="SQL" />
104
104
  <TechBadge name="Tailwind" />
105
105
  <TechBadge name="Cloudflare" />
106
106
  </div>
@@ -1,12 +1,10 @@
1
1
  // src/server/db/client.ts
2
- import { drizzle } from 'drizzle-orm/d1'
3
- import * as schema from './schema'
4
2
 
5
3
  export function createDb(d1: D1Database) {
6
- return drizzle(d1, { schema })
4
+ return d1
7
5
  }
8
6
 
9
- export type Database = ReturnType<typeof createDb>
7
+ export type Database = D1Database
10
8
 
11
9
  // For use in middleware - db instance per request
12
- export type DbInstance = Database
10
+ export type DbInstance = D1Database
@@ -0,0 +1,81 @@
1
+ // src/server/db/records.ts
2
+
3
+ export type UserStatus = 'active' | 'inactive'
4
+
5
+ export interface UserRecord {
6
+ id: string
7
+ googleId: string
8
+ email: string
9
+ name: string
10
+ avatarUrl: string | null
11
+ status: UserStatus
12
+ providerIds: string[]
13
+ isSuperAdmin: boolean
14
+ createdAt: string
15
+ updatedAt: string
16
+ deletedAt: string | null
17
+ createdById?: string | null
18
+ updatedById?: string | null
19
+ deletedById?: string | null
20
+ }
21
+
22
+ export interface AccountRecord {
23
+ id: string
24
+ name: string
25
+ description: string | null
26
+ domain: string | null
27
+ createdAt: string
28
+ updatedAt: string
29
+ deletedAt: string | null
30
+ }
31
+
32
+ export interface UserAccountRecord {
33
+ userId: string
34
+ accountId: string
35
+ role: string
36
+ }
37
+
38
+ export interface RefreshTokenRecord {
39
+ id: string
40
+ userId: string
41
+ tokenHash: string
42
+ expiresAt: number
43
+ createdAt: number
44
+ revokedAt: number | null
45
+ }
46
+
47
+ export interface InvitationRecord {
48
+ id: string
49
+ accountId: string
50
+ email: string
51
+ role: string
52
+ token: string
53
+ invitedById: string
54
+ expiresAt: string
55
+ acceptedAt: string | null
56
+ createdAt: string
57
+ }
58
+
59
+ export type AuditAction =
60
+ | 'INSERT'
61
+ | 'UPDATE'
62
+ | 'DELETE'
63
+ | 'LOGIN'
64
+ | 'LOGOUT'
65
+ | 'SIGNUP'
66
+ | 'TOKEN_REFRESH'
67
+ | 'LOGIN_FAILED'
68
+
69
+ export interface AuditLogRecord {
70
+ id: string
71
+ transactionId: string
72
+ accountId: string | null
73
+ userId: string | null
74
+ entity: string
75
+ entityId: string
76
+ action: AuditAction
77
+ changes: Record<string, unknown> | null
78
+ ipAddress: string | null
79
+ userAgent: string | null
80
+ timestamp: string
81
+ }
@@ -4,7 +4,7 @@
4
4
  // Run with: npm run db:seed
5
5
  //
6
6
  // The generated SQL can be executed via wrangler:
7
- // npx wrangler d1 execute DB --local --file=seed.sql
7
+ // pnpm db:seed:local
8
8
 
9
9
  import { generateStrongPassword } from '../lib/password'
10
10
 
@@ -243,7 +243,8 @@ function printSummary(): void {
243
243
  console.log('\n🔐 Sample Strong Password:', generateStrongPassword())
244
244
 
245
245
  console.log('\n✅ Run the following command to seed the local database:')
246
- console.log(' npx wrangler d1 execute {{projectName}}-db --local --file=seed.sql\n')
246
+ console.log(' pnpm db:seed:local')
247
+ console.log(' # or: wrangler --config config/wrangler.json d1 execute <db-name> --local --file=seed.sql\n')
247
248
  }
248
249
 
249
250
  // ============================================================================
@@ -0,0 +1,96 @@
1
+ // src/server/db/sql.ts
2
+
3
+ export type SqlValue =
4
+ | string
5
+ | number
6
+ | boolean
7
+ | null
8
+ | undefined
9
+ | bigint
10
+ | Uint8Array
11
+ | ArrayBuffer
12
+ | Date
13
+
14
+ export type SqlParams = SqlValue[]
15
+
16
+ export type SqlRow = Record<string, unknown>
17
+ export type RowMapper<T> = (row: SqlRow) => T
18
+
19
+ function normalizeValue(value: SqlValue): unknown {
20
+ if (value === undefined) return null
21
+ if (value instanceof Date) return value.toISOString()
22
+ if (typeof value === 'boolean') return value ? 1 : 0
23
+ return value
24
+ }
25
+
26
+ function normalizeParams(params?: SqlParams): unknown[] {
27
+ return (params ?? []).map((value) => normalizeValue(value))
28
+ }
29
+
30
+ function prepareStatement(db: D1Database, statement: string, params?: SqlParams): D1PreparedStatement {
31
+ const prepared = db.prepare(statement)
32
+ const normalized = normalizeParams(params)
33
+ return normalized.length > 0 ? prepared.bind(...normalized) : prepared
34
+ }
35
+
36
+ export async function queryAll<T = SqlRow>(
37
+ db: D1Database,
38
+ statement: string,
39
+ params?: SqlParams,
40
+ mapper?: RowMapper<T>
41
+ ): Promise<T[]> {
42
+ const prepared = prepareStatement(db, statement, params)
43
+ const result = await prepared.all<SqlRow>()
44
+ const rows = result.results ?? []
45
+ return mapper ? rows.map(mapper) : (rows as T[])
46
+ }
47
+
48
+ export async function queryOne<T = SqlRow>(
49
+ db: D1Database,
50
+ statement: string,
51
+ params?: SqlParams,
52
+ mapper?: RowMapper<T>
53
+ ): Promise<T | null> {
54
+ const prepared = prepareStatement(db, statement, params)
55
+ const row = await prepared.first<SqlRow>()
56
+ if (!row) return null
57
+ return mapper ? mapper(row) : (row as T)
58
+ }
59
+
60
+ export async function queryValue<T = unknown>(
61
+ db: D1Database,
62
+ statement: string,
63
+ params?: SqlParams,
64
+ column?: string
65
+ ): Promise<T | null> {
66
+ const row = await queryOne<SqlRow>(db, statement, params)
67
+ if (!row) return null
68
+ if (column) return (row[column] as T) ?? null
69
+ const firstKey = Object.keys(row)[0]
70
+ if (!firstKey) return null
71
+ return row[firstKey] as T
72
+ }
73
+
74
+ export async function execute(
75
+ db: D1Database,
76
+ statement: string,
77
+ params?: SqlParams
78
+ ): Promise<D1Result<unknown>> {
79
+ const prepared = prepareStatement(db, statement, params)
80
+ return prepared.run()
81
+ }
82
+
83
+ export interface BatchStatement {
84
+ statement: string
85
+ params?: SqlParams
86
+ }
87
+
88
+ export async function executeBatch(
89
+ db: D1Database,
90
+ statements: BatchStatement[]
91
+ ): Promise<D1Result<unknown>[]> {
92
+ const preparedStatements = statements.map((item) =>
93
+ prepareStatement(db, item.statement, item.params)
94
+ )
95
+ return db.batch(preparedStatements)
96
+ }
@@ -54,8 +54,22 @@ app.use('*', secureHeaders())
54
54
  // 7. Rate limiting - global limit of 100 requests per minute
55
55
  app.use('*', rateLimit())
56
56
 
57
- // 8. Stricter rate limiting for auth endpoints (10 requests per minute)
58
- app.use('/auth/*', authRateLimit())
57
+ // 8. Stricter rate limiting for auth LOGIN endpoints only (10 requests per minute)
58
+ // These are the endpoints vulnerable to brute force attacks
59
+ // Note: /auth/me, /auth/refresh, /auth/logout use the global rate limit (100 req/min)
60
+ // because they are session verification/maintenance, not login attempts
61
+ // Create auth rate limiter instance once (not per-request)
62
+ const loginRateLimiter = authRateLimit()
63
+
64
+ app.use('/auth/*', async (c, next) => {
65
+ const path = c.req.path
66
+ // Only apply strict rate limit to login-related endpoints (brute force protection)
67
+ if (path === '/auth/login' || path === '/auth/callback') {
68
+ return loginRateLimiter(c, next)
69
+ }
70
+ // Other auth endpoints (/auth/me, /auth/refresh, /auth/logout) use global rate limit
71
+ return next()
72
+ })
59
73
 
60
74
  // 9. Database middleware - create db instance per request
61
75
  app.use('*', async (c, next) => {