@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.
- package/dist/cli.js +1 -1
- package/dist/index.js +0 -0
- package/package.json +5 -1
- package/templates/base/.husky/pre-push +26 -0
- package/templates/base/CLAUDE.md +5 -5
- package/templates/base/README.md +31 -20
- package/templates/base/docs/app_spec.txt +13 -10
- package/templates/base/docs/architecture/README.md +3 -0
- package/templates/base/docs/architecture/data-requirements.md +4 -3
- package/templates/base/docs/architecture/db-bootstrap.md +39 -0
- package/templates/base/docs/architecture/drizzle-migration-plan.md +125 -0
- package/templates/base/docs/architecture/erd.md +1 -1
- package/templates/base/docs/architecture/sql-standards.md +100 -0
- package/templates/base/docs/testing.md +36 -29
- package/templates/base/package.json +6 -5
- package/templates/base/pnpm-lock.yaml +0 -123
- package/templates/base/schema.sql +84 -0
- package/templates/base/scripts/init.sh +271 -34
- package/templates/base/src/client/hooks/use-auth.ts +5 -0
- package/templates/base/src/client/routes/_authenticated/dashboard.tsx +1 -1
- package/templates/base/src/client/routes/index.tsx +1 -1
- package/templates/base/src/server/db/client.ts +3 -5
- package/templates/base/src/server/db/records.ts +81 -0
- package/templates/base/src/server/db/seed.ts +3 -2
- package/templates/base/src/server/db/sql.ts +96 -0
- package/templates/base/src/server/index.ts +16 -2
- package/templates/base/src/server/lib/audit.ts +74 -26
- package/templates/base/src/server/lib/audited-db.ts +219 -109
- package/templates/base/src/server/lib/transaction.ts +10 -16
- package/templates/base/src/server/middleware/account.ts +8 -15
- package/templates/base/src/server/middleware/auth.ts +102 -38
- package/templates/base/src/server/middleware/rate-limit.ts +6 -1
- package/templates/base/src/server/routes/accounts/handlers.ts +18 -6
- package/templates/base/src/server/routes/audits/handlers.ts +3 -1
- package/templates/base/src/server/routes/auth/handlers.ts +14 -9
- package/templates/base/src/server/routes/auth/test-login.ts +99 -45
- package/templates/base/src/server/routes/health/handlers.ts +4 -4
- package/templates/base/src/server/routes/invitations/handlers.ts +6 -3
- package/templates/base/src/server/routes/users/handlers.ts +21 -14
- package/templates/base/src/server/services/accounts.ts +242 -217
- package/templates/base/src/server/services/audits.ts +114 -61
- package/templates/base/src/server/services/auth.ts +310 -180
- package/templates/base/src/server/services/invitations.ts +282 -222
- package/templates/base/src/server/services/users.ts +383 -293
- package/templates/base/src/server/types/index.ts +1 -2
- package/templates/base/{src/server/__tests__/fixtures.ts → tests/fixtures/server.ts} +3 -3
- package/templates/base/{src/client/__tests__/setup-browser.ts → tests/helpers/client-setup-browser.ts} +2 -2
- package/templates/base/{src/client/__tests__/setup.ts → tests/helpers/client-setup.ts} +1 -1
- package/templates/base/{src/client/__tests__/test-utils.tsx → tests/helpers/client-test-utils.tsx} +2 -2
- package/templates/base/{src/server/__tests__/setup.ts → tests/helpers/server.ts} +9 -9
- package/templates/base/tests/integration/accounts/crud.test.ts +2 -11
- package/templates/base/tests/integration/audits/list.test.ts +2 -11
- package/templates/base/tests/integration/auth/auth-service.test.ts +1 -10
- package/templates/base/tests/integration/auth/invitation-token.test.ts +2 -11
- package/templates/base/tests/integration/auth/logout.test.ts +2 -11
- package/templates/base/tests/integration/auth/oauth.test.ts +23 -42
- package/templates/base/tests/integration/auth/refresh-token.test.ts +1 -9
- package/templates/base/tests/integration/auth/session-expiry.test.ts +1 -9
- package/templates/base/tests/integration/auth/session.test.ts +2 -11
- package/templates/base/tests/integration/auth/super-admin.test.ts +1 -9
- package/templates/base/tests/integration/authorization/analytics-role.test.ts +2 -11
- package/templates/base/tests/integration/authorization/billing-role.test.ts +2 -11
- package/templates/base/tests/integration/authorization/guards-roles.test.ts +1 -9
- package/templates/base/tests/integration/authorization/multi-tenancy.test.ts +2 -11
- package/templates/base/tests/integration/authorization/roles.test.ts +2 -11
- package/templates/base/tests/integration/config/production-behavior.test.ts +2 -11
- package/templates/base/tests/integration/health/health.test.ts +25 -44
- package/templates/base/tests/integration/invitations/crud.test.ts +2 -11
- package/templates/base/tests/integration/invitations/email.test.ts +1 -9
- package/templates/base/tests/integration/middleware/auth.test.ts +3 -12
- package/templates/base/tests/integration/middleware/request-logger.test.ts +1 -9
- package/templates/base/tests/integration/performance/response-times.test.ts +1 -9
- package/templates/base/tests/integration/security/cookie-security.test.ts +2 -11
- package/templates/base/tests/integration/security/csrf-protection.test.ts +2 -11
- package/templates/base/tests/integration/security/log-sanitization.test.ts +1 -9
- package/templates/base/tests/integration/security/rate-limiting.test.ts +1 -9
- package/templates/base/tests/integration/security/sql-injection.test.ts +7 -18
- package/templates/base/tests/integration/security/xss-prevention.test.ts +2 -11
- package/templates/base/tests/integration/setup.ts +13 -90
- package/templates/base/tests/integration/smoke.test.ts +3 -2
- package/templates/base/tests/integration/storage/upload.test.ts +2 -11
- package/templates/base/tests/integration/storage/validation.test.ts +2 -11
- package/templates/base/tests/integration/users/crud.test.ts +2 -11
- package/templates/base/tests/integration/users/list.test.ts +2 -11
- package/templates/base/tests/integration/vitest.config.ts +2 -9
- package/templates/base/{src/server/__tests__ → tests}/mocks/db.ts +1 -1
- package/templates/base/{src/server/__tests__ → tests}/mocks/index.ts +1 -1
- package/templates/base/{src/server/__tests__ → tests}/mocks/kv.ts +1 -1
- package/templates/base/{src/server/__tests__ → tests}/mocks/r2.ts +1 -1
- package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/sidebar.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/avatar.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client/components/ui}/button.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/card.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/dialog.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/input.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/loading-skeleton.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/skeleton.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/sonner.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/tabs.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/account.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/integrations.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/settings.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/team.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/authenticated-layout.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/dashboard.test.tsx +1 -1
- package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/invite.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/login.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/navigation.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/root-layout.test.tsx +1 -1
- package/templates/base/{src/server/auth/__tests__ → tests/unit/server/auth}/guards.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/auth/permissions.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/auth/roles.test.ts +1 -1
- package/templates/base/tests/unit/server/db/sql.test.ts +68 -0
- package/templates/base/{src → tests/unit}/server/env.test.ts +1 -1
- package/templates/base/tests/unit/server/lib/audited-db.test.ts +78 -0
- package/templates/base/{src → tests/unit}/server/lib/email.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/errors.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/oauth.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/pagination.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/password.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/providers.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/r2-storage.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/lib/session.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/lib/tokens.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/transaction.test.ts +5 -14
- package/templates/base/{src → tests/unit}/server/middleware/account.test.ts +16 -24
- package/templates/base/{src → tests/unit}/server/middleware/auth.test.ts +71 -42
- package/templates/base/{src → tests/unit}/server/middleware/cors.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/middleware/error-handler.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/middleware/rate-limit.test.ts +3 -2
- package/templates/base/{src → tests/unit}/server/middleware/request-context.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/middleware/request-logger.test.ts +1 -1
- package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/db.test.ts +1 -1
- package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/kv.test.ts +1 -1
- package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/r2.test.ts +1 -1
- package/templates/base/{src/server/routes/accounts/__tests__ → tests/unit/server/routes/accounts}/handlers.test.ts +12 -12
- package/templates/base/{src/server/routes/audits/__tests__ → tests/unit/server/routes/audits}/handlers.test.ts +11 -11
- package/templates/base/{src/server/routes/auth/__tests__ → tests/unit/server/routes/auth}/handlers.test.ts +13 -13
- package/templates/base/{src/server/routes/health/__tests__ → tests/unit/server/routes/health}/handlers.test.ts +27 -23
- package/templates/base/{src/server/routes/invitations/__tests__ → tests/unit/server/routes/invitations}/handlers.test.ts +14 -17
- package/templates/base/{src/server/routes/storage/__tests__ → tests/unit/server/routes/storage}/handlers.test.ts +6 -6
- package/templates/base/{src/server/routes/users/__tests__ → tests/unit/server/routes/users}/handlers.test.ts +12 -12
- package/templates/base/tests/unit/server/services/accounts.test.ts +258 -0
- package/templates/base/tests/unit/server/services/audits.test.ts +141 -0
- package/templates/base/tests/unit/server/services/auth.test.ts +179 -0
- package/templates/base/tests/unit/server/services/invitations.test.ts +165 -0
- package/templates/base/tests/unit/server/services/users.test.ts +351 -0
- package/templates/base/tsconfig.json +2 -1
- package/templates/base/vitest.config.browser.ts +3 -2
- package/templates/base/vitest.config.frontend.ts +3 -2
- package/templates/base/vitest.config.ts +7 -14
- package/templates/base/.claude/settings.local.json +0 -11
- package/templates/base/config/drizzle.config.ts +0 -10
- package/templates/base/src/server/db/schema/accounts.ts +0 -20
- package/templates/base/src/server/db/schema/audit-logs.ts +0 -26
- package/templates/base/src/server/db/schema/index.ts +0 -7
- package/templates/base/src/server/db/schema/invitations.ts +0 -30
- package/templates/base/src/server/db/schema/refresh-tokens.ts +0 -22
- package/templates/base/src/server/db/schema/user-accounts.ts +0 -25
- package/templates/base/src/server/db/schema/users.ts +0 -33
- package/templates/base/src/server/lib/audited-db.test.ts +0 -107
- package/templates/base/src/server/lib/schema-helpers.ts +0 -16
- package/templates/base/src/server/services/__tests__/accounts.test.ts +0 -764
- package/templates/base/src/server/services/__tests__/audits.test.ts +0 -235
- package/templates/base/src/server/services/__tests__/auth.test.ts +0 -765
- package/templates/base/src/server/services/__tests__/invitations.test.ts +0 -704
- package/templates/base/src/server/services/__tests__/users.test.ts +0 -755
- package/templates/base/tests/integration/lib/schema-helpers.test.ts +0 -129
- /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
- /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
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-handles-logout-button-click-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-handles-navigation-clicks-1.png +0 -0
- /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
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-highlights-active-route-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-renders-sidebar-navigation-items-1.png +0 -0
- /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
- /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
- /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
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/error-boundary.test.tsx +0 -0
- /package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/error-fallback.test.tsx +0 -0
- /package/templates/base/{src/client/hooks/__tests__ → tests/unit/client/hooks}/use-auth.test.tsx +0 -0
- /package/templates/base/{src/client/hooks/__tests__ → tests/unit/client/hooks}/use-theme.test.tsx +0 -0
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /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
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/error-components.test.tsx +0 -0
- /package/templates/base/{src/shared/schemas/__tests__ → tests/unit/shared}/schemas.test.ts +0 -0
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ function buildProgram() {
|
|
|
13
13
|
return new Command()
|
|
14
14
|
.name('bhono-app')
|
|
15
15
|
.description('Create a new project from the Etus boilerplate')
|
|
16
|
-
.version('0.1.
|
|
16
|
+
.version('0.1.5')
|
|
17
17
|
.argument('<project-name>', 'Name of the project')
|
|
18
18
|
.option('-d, --domain <domain>', 'Production domain')
|
|
19
19
|
.option('-m, --modules <modules>', 'Comma-separated modules to include')
|
package/dist/index.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
|
|
3
|
+
echo "🔍 Running pre-push checks..."
|
|
4
|
+
|
|
5
|
+
# Type checking
|
|
6
|
+
echo "📝 Type checking..."
|
|
7
|
+
pnpm typecheck || {
|
|
8
|
+
echo "❌ Type check failed. Push aborted."
|
|
9
|
+
exit 1
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
# Server unit tests only (faster, more reliable)
|
|
13
|
+
echo "🧪 Running server unit tests..."
|
|
14
|
+
pnpm test:unit:server || {
|
|
15
|
+
echo "❌ Server unit tests failed. Push aborted."
|
|
16
|
+
exit 1
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# Build verification
|
|
20
|
+
echo "🏗️ Verifying build..."
|
|
21
|
+
pnpm build || {
|
|
22
|
+
echo "❌ Build failed. Push aborted."
|
|
23
|
+
exit 1
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
echo "✅ All pre-push checks passed!"
|
package/templates/base/CLAUDE.md
CHANGED
|
@@ -5,7 +5,7 @@ Production-ready multi-tenant SaaS boilerplate with Hono.js backend and React fr
|
|
|
5
5
|
## Tech Stack
|
|
6
6
|
|
|
7
7
|
- **Runtime**: Cloudflare Workers (D1 database, KV sessions, R2 storage)
|
|
8
|
-
- **Backend**: Hono.js 4.6 +
|
|
8
|
+
- **Backend**: Hono.js 4.6 + SQL (D1) + Zod validation
|
|
9
9
|
- **Frontend**: React 19 + TanStack Router + Tailwind CSS 4.0
|
|
10
10
|
- **Testing**: Vitest (unit/integration) + Playwright (E2E)
|
|
11
11
|
- **Auth**: Google OAuth 2.0 with session-based cookies
|
|
@@ -33,7 +33,7 @@ Production-ready multi-tenant SaaS boilerplate with Hono.js backend and React fr
|
|
|
33
33
|
│ │ │ └── health/ # Health check
|
|
34
34
|
│ │ ├── services/ # Business logic layer
|
|
35
35
|
│ │ ├── middleware/ # Auth, CORS, logging, rate-limit
|
|
36
|
-
│ │ ├── db/
|
|
36
|
+
│ │ ├── db/records.ts # DB record types for SQL mapping
|
|
37
37
|
│ │ ├── lib/ # Utilities (oauth, session, tokens)
|
|
38
38
|
│ │ └── auth/ # Roles, permissions, guards
|
|
39
39
|
│ │
|
|
@@ -213,6 +213,6 @@ Defined in `config/wrangler.json`:
|
|
|
213
213
|
|
|
214
214
|
### Database Changes
|
|
215
215
|
|
|
216
|
-
1.
|
|
217
|
-
2.
|
|
218
|
-
3.
|
|
216
|
+
1. Update `schema.sql`
|
|
217
|
+
2. Apply with `pnpm db:schema:local`
|
|
218
|
+
3. (Optional) Seed with `pnpm db:seed:local`
|
package/templates/base/README.md
CHANGED
|
@@ -52,7 +52,7 @@ This boilerplate provides everything you need to build a modern, secure, and sca
|
|
|
52
52
|
- Rate limiting middleware (in-memory with lazy cleanup)
|
|
53
53
|
- CSRF protection via SameSite cookies
|
|
54
54
|
- XSS prevention with secure headers
|
|
55
|
-
- SQL injection protection via
|
|
55
|
+
- SQL injection protection via parameterized queries
|
|
56
56
|
|
|
57
57
|
### Testing
|
|
58
58
|
- Unit tests with Vitest (94%+ coverage)
|
|
@@ -73,7 +73,7 @@ This boilerplate provides everything you need to build a modern, secure, and sca
|
|
|
73
73
|
| **Backend** | Hono.js 4.6 |
|
|
74
74
|
| **Frontend** | React 19 + TanStack Router |
|
|
75
75
|
| **Database** | Cloudflare D1 (SQLite) |
|
|
76
|
-
| **
|
|
76
|
+
| **Data Access** | SQL helpers (D1) |
|
|
77
77
|
| **Sessions** | Cloudflare KV |
|
|
78
78
|
| **Storage** | Cloudflare R2 |
|
|
79
79
|
| **Styling** | Tailwind CSS 4.0 |
|
|
@@ -105,11 +105,11 @@ pnpm install
|
|
|
105
105
|
# Copy environment variables
|
|
106
106
|
cp .env.example .env
|
|
107
107
|
|
|
108
|
-
# Apply database
|
|
109
|
-
pnpm db:
|
|
108
|
+
# Apply database schema
|
|
109
|
+
pnpm db:schema:local
|
|
110
110
|
|
|
111
111
|
# Seed test data (optional)
|
|
112
|
-
pnpm db:seed
|
|
112
|
+
pnpm db:seed:local
|
|
113
113
|
|
|
114
114
|
# Start development server
|
|
115
115
|
pnpm dev
|
|
@@ -152,7 +152,6 @@ LOG_LEVEL=info
|
|
|
152
152
|
├── config/ # Configuration files
|
|
153
153
|
│ ├── eslint.config.js # ESLint configuration
|
|
154
154
|
│ ├── wrangler.json # Cloudflare Workers config
|
|
155
|
-
│ ├── drizzle.config.ts # Drizzle ORM config
|
|
156
155
|
│ └── ...
|
|
157
156
|
│
|
|
158
157
|
├── src/
|
|
@@ -166,8 +165,11 @@ LOG_LEVEL=info
|
|
|
166
165
|
│ │ │ └── storage/ # File storage (R2)
|
|
167
166
|
│ │ ├── services/ # Business logic
|
|
168
167
|
│ │ ├── middleware/ # Request middleware
|
|
169
|
-
│ │ ├── db/ # Database (
|
|
170
|
-
│ │ │
|
|
168
|
+
│ │ ├── db/ # Database (SQL helpers)
|
|
169
|
+
│ │ │ ├── client.ts # D1 client wrapper
|
|
170
|
+
│ │ │ ├── records.ts # Record typings (SQL results)
|
|
171
|
+
│ │ │ ├── sql.ts # Query helpers (queryOne/queryAll/execute)
|
|
172
|
+
│ │ │ └── seed.ts # Seed generator (seed.sql)
|
|
171
173
|
│ │ ├── auth/ # Roles, permissions, guards
|
|
172
174
|
│ │ └── lib/ # Utilities
|
|
173
175
|
│ │
|
|
@@ -209,7 +211,8 @@ LOG_LEVEL=info
|
|
|
209
211
|
├── docs/ # Documentation
|
|
210
212
|
│ └── testing.md # Testing guide
|
|
211
213
|
│
|
|
212
|
-
|
|
214
|
+
├── schema.sql # D1 schema (source of truth)
|
|
215
|
+
└── seed.sql # Generated seed data
|
|
213
216
|
```
|
|
214
217
|
|
|
215
218
|
---
|
|
@@ -300,14 +303,20 @@ invitations (
|
|
|
300
303
|
)
|
|
301
304
|
```
|
|
302
305
|
|
|
303
|
-
###
|
|
306
|
+
### Database Schema
|
|
304
307
|
|
|
305
308
|
```bash
|
|
306
|
-
# Apply
|
|
307
|
-
pnpm db:
|
|
309
|
+
# Apply schema.sql locally
|
|
310
|
+
pnpm db:schema:local
|
|
308
311
|
|
|
309
|
-
# Apply
|
|
310
|
-
pnpm db:
|
|
312
|
+
# Apply schema.sql to production (optional)
|
|
313
|
+
pnpm db:schema:remote
|
|
314
|
+
|
|
315
|
+
# Generate + seed locally
|
|
316
|
+
pnpm db:seed:local
|
|
317
|
+
|
|
318
|
+
# Full reset (schema + seed)
|
|
319
|
+
pnpm db:reset:local
|
|
311
320
|
|
|
312
321
|
# Generate Cloudflare types
|
|
313
322
|
pnpm cf-typegen
|
|
@@ -556,7 +565,7 @@ Client (React SPA)
|
|
|
556
565
|
│ │ │
|
|
557
566
|
│ ▼ │
|
|
558
567
|
│ ┌─────────────────────────────┐ │
|
|
559
|
-
│ │
|
|
568
|
+
│ │ SQL Helpers │ │
|
|
560
569
|
│ └─────────────────────────────┘ │
|
|
561
570
|
│ │ │
|
|
562
571
|
└──────────────┼────────────────────┘
|
|
@@ -586,9 +595,11 @@ import { createUser } from '@server/services/users' // Server
|
|
|
586
595
|
| `pnpm lint` | Run ESLint |
|
|
587
596
|
| `pnpm typecheck` | Run TypeScript checks |
|
|
588
597
|
| **Database** | |
|
|
589
|
-
| `pnpm db:
|
|
590
|
-
| `pnpm db:
|
|
591
|
-
| `pnpm db:seed` |
|
|
598
|
+
| `pnpm db:schema:local` | Apply schema.sql locally |
|
|
599
|
+
| `pnpm db:schema:remote` | Apply schema.sql remotely |
|
|
600
|
+
| `pnpm db:seed` | Generate seed.sql |
|
|
601
|
+
| `pnpm db:seed:local` | Generate + seed locally |
|
|
602
|
+
| `pnpm db:reset:local` | Apply schema + seed locally |
|
|
592
603
|
| `pnpm cf-typegen` | Generate Cloudflare types |
|
|
593
604
|
| **API** | |
|
|
594
605
|
| `pnpm api:spec` | Generate OpenAPI spec (docs/openapi.json) |
|
|
@@ -614,7 +625,7 @@ import { createUser } from '@server/services/users' // Server
|
|
|
614
625
|
- **CSRF**: SameSite cookies + CORS validation
|
|
615
626
|
- **XSS**: Secure headers + React's built-in escaping
|
|
616
627
|
- **Session Hijacking**: httpOnly cookies, secure flag in production
|
|
617
|
-
- **SQL Injection**: Parameterized queries via
|
|
628
|
+
- **SQL Injection**: Parameterized queries via SQL helpers
|
|
618
629
|
- **Rate Limiting**: In-memory store with configurable limits per route
|
|
619
630
|
- **Fingerprint Validation**: User-agent validation for sessions
|
|
620
631
|
|
|
@@ -663,7 +674,7 @@ MIT License - see [LICENSE](LICENSE) for details.
|
|
|
663
674
|
## Acknowledgments
|
|
664
675
|
|
|
665
676
|
- [Hono.js](https://hono.dev/) - Ultrafast web framework
|
|
666
|
-
- [
|
|
677
|
+
- [Cloudflare D1](https://developers.cloudflare.com/d1/) - Serverless SQLite
|
|
667
678
|
- [TanStack Router](https://tanstack.com/router) - Type-safe routing
|
|
668
679
|
- [Tailwind CSS](https://tailwindcss.com/) - Utility-first CSS
|
|
669
680
|
- [Cloudflare Workers](https://workers.cloudflare.com/) - Edge computing platform
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
|
|
43
43
|
<backend>
|
|
44
44
|
<framework>Hono.js 4.6</framework>
|
|
45
|
-
<orm>
|
|
45
|
+
<orm>SQL (D1/SQLite)</orm>
|
|
46
46
|
<validation>Zod</validation>
|
|
47
47
|
<documentation>OpenAPI 3.0 / Swagger UI</documentation>
|
|
48
48
|
<description>
|
|
49
49
|
Type-safe API development with automatic schema generation.
|
|
50
|
-
|
|
50
|
+
SQL-first access with Cloudflare D1 (SQLite under the hood).
|
|
51
51
|
OpenAPI spec and TypeScript types can be generated locally (api:spec, api:types).
|
|
52
52
|
</description>
|
|
53
53
|
</backend>
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
→ Fetch API
|
|
121
121
|
→ Hono Routes
|
|
122
122
|
→ Services
|
|
123
|
-
→
|
|
123
|
+
→ SQL (D1/SQLite)
|
|
124
124
|
→ D1 Database
|
|
125
125
|
</data_flow>
|
|
126
126
|
</architecture>
|
|
@@ -769,9 +769,11 @@
|
|
|
769
769
|
npm run build # Build for production
|
|
770
770
|
npm run deploy # Deploy to Cloudflare Workers
|
|
771
771
|
|
|
772
|
-
npm run db:
|
|
773
|
-
npm run db:
|
|
774
|
-
npm run db:seed #
|
|
772
|
+
npm run db:schema:local # Apply schema.sql locally
|
|
773
|
+
npm run db:schema:remote # Apply schema.sql to production (optional)
|
|
774
|
+
npm run db:seed # Generate seed.sql
|
|
775
|
+
npm run db:seed:local # Generate + seed locally
|
|
776
|
+
npm run db:reset:local # Apply schema.sql + seed (local)
|
|
775
777
|
|
|
776
778
|
npm run test:unit:server # Backend unit tests
|
|
777
779
|
npm run test:unit:client # Frontend tests
|
|
@@ -813,8 +815,8 @@
|
|
|
813
815
|
│ ├── services/ # Business logic
|
|
814
816
|
│ ├── middleware/ # Auth, CORS, logging
|
|
815
817
|
│ ├── db/
|
|
816
|
-
│ │ ├──
|
|
817
|
-
│ │ └──
|
|
818
|
+
│ │ ├── records.ts # DB record types for SQL mapping
|
|
819
|
+
│ │ └── seed.ts # Seed generator (outputs seed.sql)
|
|
818
820
|
│ ├── lib/ # Utilities
|
|
819
821
|
│ └── auth/ # RBAC guards
|
|
820
822
|
│
|
|
@@ -836,11 +838,12 @@
|
|
|
836
838
|
<key_files>
|
|
837
839
|
src/server/index.ts # Hono app entry
|
|
838
840
|
src/server/routes/index.ts # API router
|
|
839
|
-
src/server/db/
|
|
841
|
+
src/server/db/records.ts # Database record types
|
|
840
842
|
src/client/routes/__root.tsx # React app root
|
|
841
843
|
src/client/routes/_authenticated.tsx # Auth layout
|
|
842
844
|
wrangler.json # Cloudflare config
|
|
843
|
-
|
|
845
|
+
schema.sql # SQL schema (source of truth)
|
|
846
|
+
seed.sql # Generated seed data (output)
|
|
844
847
|
playwright.config.ts # E2E test config
|
|
845
848
|
vitest.config.ts # Unit test config
|
|
846
849
|
</key_files>
|
|
@@ -6,3 +6,6 @@ Indice rapido dos documentos de arquitetura.
|
|
|
6
6
|
|
|
7
7
|
- `docs/architecture/erd.md` - ERD e dicionario de dados.
|
|
8
8
|
- `docs/architecture/data-requirements.md` - Data Requirements Document (DRD).
|
|
9
|
+
- `docs/architecture/db-bootstrap.md` - Bootstrap do schema.sql e seed.
|
|
10
|
+
- `docs/architecture/drizzle-migration-plan.md` - Mapeamento do Drizzle e plano de migracao para SQL puro.
|
|
11
|
+
- `docs/architecture/sql-standards.md` - Padrao de SQL puro e mapeamento de resultados.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Data Requirements Document (DRD)
|
|
2
2
|
|
|
3
3
|
## 1. Objetivo e escopo
|
|
4
|
-
Este documento define os requisitos de dados do projeto, cobrindo o modelo relacional (D1/SQLite), armazenamento de sessao (KV) e objetos (R2). Serve como referencia para manutencao, auditoria e
|
|
4
|
+
Este documento define os requisitos de dados do projeto, cobrindo o modelo relacional (D1/SQLite), armazenamento de sessao (KV) e objetos (R2). Serve como referencia para manutencao, auditoria e evolucao do SQL puro.
|
|
5
5
|
|
|
6
6
|
## 2. Componentes de armazenamento
|
|
7
7
|
- D1 (SQLite): armazenamento relacional principal.
|
|
@@ -102,8 +102,9 @@ Principais entidades:
|
|
|
102
102
|
- Requisito: manter procedimento de backup/restore do D1 (ex.: exportacao periodica) e verificar restauracao.
|
|
103
103
|
- KV e R2 devem ter estrategia de recuperacao alinhada com RPO/RTO desejados.
|
|
104
104
|
|
|
105
|
-
## 12. Consideracoes para
|
|
105
|
+
## 12. Consideracoes para SQL puro (sem migrations)
|
|
106
106
|
- Todas as queries devem ser parametrizadas para evitar SQL injection.
|
|
107
107
|
- Manter mapeadores de resultado (ex.: validacao via Zod) para preservar contratos tipados.
|
|
108
108
|
- Centralizar SQL em um modulo `db` e reutilizar modelos do ERD.
|
|
109
|
-
-
|
|
109
|
+
- `schema.sql` e a fonte de verdade do schema e deve ser aplicado via `wrangler d1 execute`.
|
|
110
|
+
- `seed.sql` e gerado a partir de `src/server/db/seed.ts` e aplicado apos o bootstrap quando necessario.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Database Bootstrap (schema.sql)
|
|
2
|
+
|
|
3
|
+
## Objetivo
|
|
4
|
+
O boilerplate nao usa migrations. O schema do D1 e versionado em `schema.sql` e aplicado via `wrangler d1 execute`. O seed e gerado em `seed.sql` a partir de `src/server/db/seed.ts`.
|
|
5
|
+
|
|
6
|
+
## Arquivos
|
|
7
|
+
- `schema.sql` - fonte de verdade do schema
|
|
8
|
+
- `src/server/db/seed.ts` - gerador de seed
|
|
9
|
+
- `seed.sql` - output do seed (gerado)
|
|
10
|
+
|
|
11
|
+
## Fluxos recomendados
|
|
12
|
+
### Local
|
|
13
|
+
```bash
|
|
14
|
+
# aplicar schema
|
|
15
|
+
pnpm db:schema:local
|
|
16
|
+
|
|
17
|
+
# gerar e aplicar seed
|
|
18
|
+
pnpm db:seed:local
|
|
19
|
+
|
|
20
|
+
# reset completo (schema + seed)
|
|
21
|
+
pnpm db:reset:local
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Remoto (opcional)
|
|
25
|
+
```bash
|
|
26
|
+
# aplicar schema
|
|
27
|
+
pnpm db:schema:remote
|
|
28
|
+
|
|
29
|
+
# gerar e aplicar seed
|
|
30
|
+
pnpm db:seed:remote
|
|
31
|
+
|
|
32
|
+
# reset completo (schema + seed)
|
|
33
|
+
pnpm db:reset:remote
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Notas
|
|
37
|
+
- `schema.sql` deve ser atualizado a cada mudanca estrutural.
|
|
38
|
+
- `seed.sql` deve ser re-gerado apos alteracoes em `seed.ts`.
|
|
39
|
+
- As migrations nao sao usadas neste boilerplate para manter o fluxo simples.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Mapeamento do Drizzle e plano de migracao para SQL puro
|
|
2
|
+
|
|
3
|
+
> Status: Drizzle removido (PDB-823, Jan 2026). Este documento permanece como historico do plano.
|
|
4
|
+
|
|
5
|
+
Contexto canonico: `docs/app_spec.txt`.
|
|
6
|
+
|
|
7
|
+
## Objetivo
|
|
8
|
+
|
|
9
|
+
Documentar onde o Drizzle e utilizado hoje e propor uma sequencia de migracao para SQL puro (D1/SQLite) com menor risco.
|
|
10
|
+
|
|
11
|
+
## Inventario de uso do Drizzle
|
|
12
|
+
|
|
13
|
+
### Tooling e configuracao
|
|
14
|
+
|
|
15
|
+
- `config/drizzle.config.ts`
|
|
16
|
+
- `package.json` (scripts `db:push`, `db:generate`, `db:studio`; dependencias `drizzle-orm`, `drizzle-kit`)
|
|
17
|
+
- `packages/bhono-app/templates/base/config/drizzle.config.ts`
|
|
18
|
+
- `packages/bhono-app/templates/base/package.json` (mesmos scripts/dependencias)
|
|
19
|
+
|
|
20
|
+
### Schema e helpers
|
|
21
|
+
|
|
22
|
+
- `src/server/db/schema/index.ts`
|
|
23
|
+
- `src/server/db/schema/*.ts` (users, accounts, user-accounts, invitations, refresh-tokens, audit-logs)
|
|
24
|
+
- `src/server/lib/schema-helpers.ts`
|
|
25
|
+
- `packages/bhono-app/templates/base/src/server/db/schema/*`
|
|
26
|
+
- `packages/bhono-app/templates/base/src/server/lib/schema-helpers.ts`
|
|
27
|
+
|
|
28
|
+
### Client e cross-cutting
|
|
29
|
+
|
|
30
|
+
- `src/server/db/client.ts` (createDb com `drizzle-orm/d1`)
|
|
31
|
+
- `src/server/lib/transaction.ts` (types baseados em `Database['transaction']`)
|
|
32
|
+
- `src/server/lib/audited-db.ts` (Table/SQL do Drizzle, `.returning()`)
|
|
33
|
+
- `src/server/lib/audit.ts` (grava em `audit_logs` usando `db.insert`)
|
|
34
|
+
- `packages/bhono-app/templates/base/src/server/db/client.ts`
|
|
35
|
+
- `packages/bhono-app/templates/base/src/server/lib/transaction.ts`
|
|
36
|
+
- `packages/bhono-app/templates/base/src/server/lib/audited-db.ts`
|
|
37
|
+
- `packages/bhono-app/templates/base/src/server/lib/audit.ts`
|
|
38
|
+
|
|
39
|
+
### Services (queries principais)
|
|
40
|
+
|
|
41
|
+
- `src/server/services/auth.ts` (insert/update/returning, tokens, refresh)
|
|
42
|
+
- `src/server/services/users.ts` (filtros, subquery IN, soft delete, paginacao)
|
|
43
|
+
- `src/server/services/accounts.ts` (filtros, subquery IN, soft delete, paginacao)
|
|
44
|
+
- `src/server/services/invitations.ts` (joins, expiracao, create/accept)
|
|
45
|
+
- `src/server/services/audits.ts` (filtros, paginacao)
|
|
46
|
+
- `packages/bhono-app/templates/base/src/server/services/*` (espelho)
|
|
47
|
+
|
|
48
|
+
### Middleware e rotas
|
|
49
|
+
|
|
50
|
+
- `src/server/middleware/auth.ts` (select por id + soft delete)
|
|
51
|
+
- `src/server/middleware/account.ts` (select por membership)
|
|
52
|
+
- `src/server/routes/health/handlers.ts` (db.run com `sql` do Drizzle)
|
|
53
|
+
- `src/server/routes/auth/test-login.ts` (select simples)
|
|
54
|
+
- `packages/bhono-app/templates/base/src/server/middleware/*`
|
|
55
|
+
- `packages/bhono-app/templates/base/src/server/routes/*`
|
|
56
|
+
|
|
57
|
+
### Testes
|
|
58
|
+
|
|
59
|
+
- `tests/integration/setup.ts` (drizzle + better-sqlite3, schema SQL inline)
|
|
60
|
+
- `packages/bhono-app/templates/base/tests/integration/*` (espelho com drizzle)
|
|
61
|
+
- `packages/bhono-app/templates/base/tests/integration/security/*.test.ts` (comentarios e wrappers de drizzle)
|
|
62
|
+
|
|
63
|
+
## Padroes de query usados hoje
|
|
64
|
+
|
|
65
|
+
- Select simples por id + soft delete (`deleted_at IS NULL`).
|
|
66
|
+
- Select com join (`invitations` + `users`/`accounts`).
|
|
67
|
+
- Subquery com `IN` (multi-tenancy em users/accounts).
|
|
68
|
+
- Paginacao com `LIMIT/OFFSET` + `count(*)`.
|
|
69
|
+
- `LIKE` para filtros por nome/email/dominio.
|
|
70
|
+
- Inserts/updates com `returning()`.
|
|
71
|
+
- Soft delete (update de `deleted_at` e audit).
|
|
72
|
+
- Transacoes (create user + account + membership; depende de `db.transaction`).
|
|
73
|
+
- Uso pontual de SQL raw (`sql\`SELECT 1\`` na healthcheck).
|
|
74
|
+
|
|
75
|
+
## Sequencia recomendada de migracao
|
|
76
|
+
|
|
77
|
+
1) Padroes e convencoes de SQL puro (PDB-813)
|
|
78
|
+
- Definir placeholders, naming, soft delete, mapeamento/validacao, tratamento de erros.
|
|
79
|
+
|
|
80
|
+
2) Helper SQL para D1 (PDB-814)
|
|
81
|
+
- API de execucao (queryOne/queryAll/exec), bind de parametros e typing.
|
|
82
|
+
- Confirmar comportamento de transacoes no D1 (ex.: `batch`) antes de migrar flows criticos.
|
|
83
|
+
|
|
84
|
+
3) Camadas cross-cutting (PDB-815)
|
|
85
|
+
- Reimplementar `audited-db` e `audit` com SQL puro.
|
|
86
|
+
- Ajustar `transaction.ts` para o novo helper.
|
|
87
|
+
|
|
88
|
+
4) Modulos de negocio (PDB-816 a PDB-821)
|
|
89
|
+
- Auth -> Users -> Accounts -> Invitations -> Audits -> Middleware.
|
|
90
|
+
- Sempre migrar junto com testes afetados.
|
|
91
|
+
|
|
92
|
+
5) Estrategia sem migrations (PDB-822)
|
|
93
|
+
- Consolidar `schema.sql` versionado e bootstrap/reset com `wrangler d1 execute`.
|
|
94
|
+
- Alinhar `tests/integration/setup.ts` com `schema.sql`.
|
|
95
|
+
|
|
96
|
+
6) Remocao de Drizzle (PDB-823)
|
|
97
|
+
- Limpar dependencias, scripts e tipos remanescentes.
|
|
98
|
+
|
|
99
|
+
## Riscos e mitigacoes
|
|
100
|
+
|
|
101
|
+
- Consistencia multi-tenant: garantir `account_id` em todas as queries.
|
|
102
|
+
- Mitigacao: helper com scoping obrigatorio e testes de autorizacao.
|
|
103
|
+
|
|
104
|
+
- Perda de audit trail: `audited-*` depende de `returning()`.
|
|
105
|
+
- Mitigacao: selecionar estado anterior antes do update e usar `RETURNING` quando suportado.
|
|
106
|
+
|
|
107
|
+
- SQL injection: migracao aumenta risco manual.
|
|
108
|
+
- Mitigacao: prepared statements, helpers obrigatorios e proibicao de string interpolation.
|
|
109
|
+
|
|
110
|
+
- Diferencas de comportamento entre D1 e sqlite local.
|
|
111
|
+
- Mitigacao: alinhar `schema.sql` e tests com D1 (usar `wrangler d1` para smoke tests).
|
|
112
|
+
|
|
113
|
+
- Quebra de testes e mocks.
|
|
114
|
+
- Mitigacao: atualizar `tests/integration/setup.ts` e mocks ao mesmo tempo que cada modulo.
|
|
115
|
+
|
|
116
|
+
## Entregaveis desta etapa (PDB-812)
|
|
117
|
+
|
|
118
|
+
- Inventario de arquivos e padroes de query.
|
|
119
|
+
- Sequencia recomendada e dependencias entre modulos.
|
|
120
|
+
- Riscos e mitigacoes para orientar as proximas issues.
|
|
121
|
+
|
|
122
|
+
## Notas
|
|
123
|
+
|
|
124
|
+
- Tudo que for alterado no `src/` deve ser espelhado em `packages/bhono-app/templates/base/`.
|
|
125
|
+
- O seed ja gera SQL puro; pode ser mantido e ajustado conforme o `schema.sql`.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# ERD - Hono Boilerplate (D1/SQLite)
|
|
2
2
|
|
|
3
3
|
## Visao geral
|
|
4
|
-
Este documento descreve o modelo relacional atual utilizado no banco D1 (SQLite) do projeto. Ele foi derivado diretamente
|
|
4
|
+
Este documento descreve o modelo relacional atual utilizado no banco D1 (SQLite) do projeto. Ele foi derivado diretamente de `schema.sql`.
|
|
5
5
|
|
|
6
6
|
## Diagrama (Mermaid)
|
|
7
7
|
```mermaid
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Padrao de SQL puro e mapeamento de resultados
|
|
2
|
+
|
|
3
|
+
Contexto canonico: `docs/app_spec.txt`.
|
|
4
|
+
|
|
5
|
+
## Objetivo
|
|
6
|
+
|
|
7
|
+
Definir convencoes para SQL puro no D1/SQLite, incluindo bind de parametros,
|
|
8
|
+
mapa de resultados e tratamento de erros para manter consistencia e seguranca.
|
|
9
|
+
|
|
10
|
+
## Escopo
|
|
11
|
+
|
|
12
|
+
- SQL usado no backend (services, middleware, lib).
|
|
13
|
+
- Mapeamento de resultados para tipos de dominio.
|
|
14
|
+
- Soft delete, multi-tenancy, auditoria e paginacao.
|
|
15
|
+
|
|
16
|
+
## Convencoes de SQL
|
|
17
|
+
|
|
18
|
+
### Estilo
|
|
19
|
+
|
|
20
|
+
- Keywords em UPPERCASE (SELECT, FROM, WHERE).
|
|
21
|
+
- Tabelas e colunas em snake_case (como no schema atual).
|
|
22
|
+
- Evitar `SELECT *`: listar colunas para mapear explicitamente.
|
|
23
|
+
- Preferir alias curtos (`users u`, `accounts a`) quando houver join.
|
|
24
|
+
|
|
25
|
+
### Parametros e seguranca
|
|
26
|
+
|
|
27
|
+
- Nunca interpolar strings diretamente em SQL.
|
|
28
|
+
- Usar placeholders posicionais `?` com `stmt.bind(...)`.
|
|
29
|
+
- Proibir concatenacao de SQL com valores de usuario.
|
|
30
|
+
|
|
31
|
+
### Soft delete
|
|
32
|
+
|
|
33
|
+
- Queries de leitura devem sempre filtrar `deleted_at IS NULL` quando aplicavel.
|
|
34
|
+
- Remocoes devem ser soft delete (update de `deleted_at`, `deleted_by_id`, `updated_at`).
|
|
35
|
+
- Restauracao deve limpar `deleted_at` e `deleted_by_id`.
|
|
36
|
+
|
|
37
|
+
### Multi-tenancy
|
|
38
|
+
|
|
39
|
+
- Toda query de dados multi-tenant deve filtrar `account_id`.
|
|
40
|
+
- Para super-admin, permitir bypass explicito em query (flag de contexto).
|
|
41
|
+
|
|
42
|
+
### Paginacao e filtros
|
|
43
|
+
|
|
44
|
+
- Paginacao via `LIMIT`/`OFFSET`.
|
|
45
|
+
- Contagem total via `SELECT count(*)`.
|
|
46
|
+
- Filtros textuais com `LIKE` e `%`.
|
|
47
|
+
|
|
48
|
+
### Datas e tipos
|
|
49
|
+
|
|
50
|
+
- Datas em TEXT ISO (ex.: `created_at`) ou INTEGER unix (ex.: `refresh_tokens`).
|
|
51
|
+
- Mapear `INTEGER` para boolean quando necessario (0/1).
|
|
52
|
+
- Campos JSON em TEXT devem ser parseados ao ler.
|
|
53
|
+
|
|
54
|
+
## Mapeamento de resultados
|
|
55
|
+
|
|
56
|
+
### Regra geral
|
|
57
|
+
|
|
58
|
+
- Toda query deve ter um mapper explicito (funcao ou schema) que:
|
|
59
|
+
- valida shape (preferencialmente com Zod),
|
|
60
|
+
- converte tipos (INTEGER -> boolean, TEXT JSON -> objeto),
|
|
61
|
+
- normaliza campos opcionais.
|
|
62
|
+
|
|
63
|
+
### Exemplo (conceitual)
|
|
64
|
+
|
|
65
|
+
- Query retorna `is_super_admin` (INTEGER).
|
|
66
|
+
- Mapper converte para boolean e retorna `isSuperAdmin`.
|
|
67
|
+
- Campo `provider_ids` (TEXT JSON) vira `string[]`.
|
|
68
|
+
|
|
69
|
+
## Erros e tratamento
|
|
70
|
+
|
|
71
|
+
- `NotFoundError` quando `SELECT` nao retorna registros esperados.
|
|
72
|
+
- `ConflictError` quando violar unicidade (dominio/email/token).
|
|
73
|
+
- `ForbiddenError` para acesso indevido a `account_id`.
|
|
74
|
+
- Propagar erro de SQL com mensagem generica (evitar leak de detalhes).
|
|
75
|
+
|
|
76
|
+
## Transacoes
|
|
77
|
+
|
|
78
|
+
- D1 nao oferece transacoes nativas. Operacoes multi-step devem ser planejadas
|
|
79
|
+
para serem idempotentes ou dividir em `batch` quando possivel.
|
|
80
|
+
- Para fluxos criticos, validar resultados intermediarios e registrar auditoria
|
|
81
|
+
com `transaction_id` compartilhado.
|
|
82
|
+
|
|
83
|
+
## Auditoria
|
|
84
|
+
|
|
85
|
+
- Toda mutacao relevante deve registrar `audit_logs`.
|
|
86
|
+
- Usar `transaction_id` comum para agrupar operacoes.
|
|
87
|
+
- Registrar `changes` apenas com campos relevantes.
|
|
88
|
+
|
|
89
|
+
## Checklist de migracao por modulo
|
|
90
|
+
|
|
91
|
+
- Listar queries atuais.
|
|
92
|
+
- Reescrever SQL com bind e filtros `account_id`/`deleted_at`.
|
|
93
|
+
- Adicionar mapper/validator.
|
|
94
|
+
- Atualizar testes afetados.
|
|
95
|
+
|
|
96
|
+
## Referencias
|
|
97
|
+
|
|
98
|
+
- `docs/architecture/erd.md`
|
|
99
|
+
- `docs/architecture/data-requirements.md`
|
|
100
|
+
- `docs/architecture/drizzle-migration-plan.md`
|