@mars-stack/cli 0.2.0 → 1.0.2

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 (175) hide show
  1. package/dist/index.js +137 -12
  2. package/dist/index.js.map +1 -1
  3. package/package.json +4 -3
  4. package/template/.cursor/rules/composition-patterns.mdc +186 -0
  5. package/template/.cursor/rules/data-access.mdc +29 -0
  6. package/template/.cursor/rules/project-structure.mdc +34 -0
  7. package/template/.cursor/rules/security.mdc +25 -0
  8. package/template/.cursor/rules/testing.mdc +24 -0
  9. package/template/.cursor/rules/ui-conventions.mdc +29 -0
  10. package/template/.cursor/skills/add-api-route/SKILL.md +122 -0
  11. package/template/.cursor/skills/add-audit-log/SKILL.md +375 -0
  12. package/template/.cursor/skills/add-blog/SKILL.md +447 -0
  13. package/template/.cursor/skills/add-command-palette/SKILL.md +438 -0
  14. package/template/.cursor/skills/add-component/SKILL.md +158 -0
  15. package/template/.cursor/skills/add-crud-routes/SKILL.md +221 -0
  16. package/template/.cursor/skills/add-e2e-test/SKILL.md +227 -0
  17. package/template/.cursor/skills/add-error-boundary/SKILL.md +472 -0
  18. package/template/.cursor/skills/add-feature/SKILL.md +174 -0
  19. package/template/.cursor/skills/add-middleware/SKILL.md +135 -0
  20. package/template/.cursor/skills/add-page/SKILL.md +151 -0
  21. package/template/.cursor/skills/add-prisma-model/SKILL.md +148 -0
  22. package/template/.cursor/skills/add-protected-resource/SKILL.md +192 -0
  23. package/template/.cursor/skills/add-role/SKILL.md +156 -0
  24. package/template/.cursor/skills/add-server-action/SKILL.md +167 -0
  25. package/template/.cursor/skills/add-webhook/SKILL.md +192 -0
  26. package/template/.cursor/skills/build-complete-feature/SKILL.md +227 -0
  27. package/template/.cursor/skills/build-dashboard/SKILL.md +211 -0
  28. package/template/.cursor/skills/build-data-table/SKILL.md +283 -0
  29. package/template/.cursor/skills/build-form/SKILL.md +231 -0
  30. package/template/.cursor/skills/build-landing-page/SKILL.md +248 -0
  31. package/template/.cursor/skills/configure-ai/SKILL.md +617 -0
  32. package/template/.cursor/skills/configure-analytics/SKILL.md +413 -0
  33. package/template/.cursor/skills/configure-dark-mode/SKILL.md +309 -0
  34. package/template/.cursor/skills/configure-email/SKILL.md +170 -0
  35. package/template/.cursor/skills/configure-email-verification/SKILL.md +333 -0
  36. package/template/.cursor/skills/configure-feature-flags/SKILL.md +361 -0
  37. package/template/.cursor/skills/configure-i18n/SKILL.md +518 -0
  38. package/template/.cursor/skills/configure-jobs/SKILL.md +500 -0
  39. package/template/.cursor/skills/configure-magic-links/SKILL.md +385 -0
  40. package/template/.cursor/skills/configure-multi-tenancy/SKILL.md +611 -0
  41. package/template/.cursor/skills/configure-notifications/SKILL.md +569 -0
  42. package/template/.cursor/skills/configure-oauth/SKILL.md +217 -0
  43. package/template/.cursor/skills/configure-onboarding/SKILL.md +483 -0
  44. package/template/.cursor/skills/configure-payments/SKILL.md +243 -0
  45. package/template/.cursor/skills/configure-realtime/SKILL.md +733 -0
  46. package/template/.cursor/skills/configure-search/SKILL.md +581 -0
  47. package/template/.cursor/skills/configure-storage/SKILL.md +273 -0
  48. package/template/.cursor/skills/configure-two-factor/SKILL.md +518 -0
  49. package/template/.cursor/skills/create-execution-plan/SKILL.md +204 -0
  50. package/template/.cursor/skills/create-seed/SKILL.md +191 -0
  51. package/template/.cursor/skills/deploy-to-vercel/SKILL.md +300 -0
  52. package/template/.cursor/skills/design-tokens/SKILL.md +138 -0
  53. package/template/.cursor/skills/mars-capture-conversation-context/SKILL.md +119 -0
  54. package/template/.cursor/skills/setup-billing/SKILL.md +322 -0
  55. package/template/.cursor/skills/setup-project/SKILL.md +104 -0
  56. package/template/.cursor/skills/setup-teams/SKILL.md +682 -0
  57. package/template/.cursor/skills/test-api-route/SKILL.md +219 -0
  58. package/template/.cursor/skills/update-architecture-docs/SKILL.md +99 -0
  59. package/template/AGENTS.md +104 -0
  60. package/template/ARCHITECTURE.md +102 -0
  61. package/template/docs/QUALITY_SCORE.md +20 -0
  62. package/template/docs/design-docs/conversation-as-system-record.md +70 -0
  63. package/template/docs/design-docs/core-beliefs.md +43 -0
  64. package/template/docs/design-docs/index.md +8 -0
  65. package/template/docs/exec-plans/active/.gitkeep +0 -0
  66. package/template/docs/exec-plans/completed/.gitkeep +0 -0
  67. package/template/docs/exec-plans/tech-debt.md +7 -0
  68. package/template/docs/generated/.gitkeep +0 -0
  69. package/template/docs/product-specs/index.md +7 -0
  70. package/template/docs/references/index.md +18 -0
  71. package/template/e2e/api.spec.ts +20 -0
  72. package/template/e2e/auth.spec.ts +24 -0
  73. package/template/e2e/public.spec.ts +25 -0
  74. package/template/eslint.config.mjs +24 -0
  75. package/template/next-env.d.ts +6 -0
  76. package/template/next.config.ts +45 -0
  77. package/template/package.json +80 -0
  78. package/template/playwright.config.ts +31 -0
  79. package/template/postcss.config.mjs +8 -0
  80. package/template/prisma/generated/prisma/browser.ts +49 -0
  81. package/template/prisma/generated/prisma/client.ts +73 -0
  82. package/template/prisma/generated/prisma/commonInputTypes.ts +406 -0
  83. package/template/prisma/generated/prisma/enums.ts +15 -0
  84. package/template/prisma/generated/prisma/internal/class.ts +254 -0
  85. package/template/prisma/generated/prisma/internal/prismaNamespace.ts +1240 -0
  86. package/template/prisma/generated/prisma/internal/prismaNamespaceBrowser.ts +190 -0
  87. package/template/prisma/generated/prisma/models/Account.ts +1543 -0
  88. package/template/prisma/generated/prisma/models/File.ts +1529 -0
  89. package/template/prisma/generated/prisma/models/Session.ts +1415 -0
  90. package/template/prisma/generated/prisma/models/Subscription.ts +1455 -0
  91. package/template/prisma/generated/prisma/models/User.ts +2235 -0
  92. package/template/prisma/generated/prisma/models/VerificationToken.ts +1099 -0
  93. package/template/prisma/generated/prisma/models.ts +17 -0
  94. package/template/prisma/schema/auth.prisma +69 -0
  95. package/template/prisma/schema/base.prisma +8 -0
  96. package/template/prisma/schema/file.prisma +15 -0
  97. package/template/prisma/schema/subscription.prisma +17 -0
  98. package/template/prisma.config.ts +13 -0
  99. package/template/scripts/check-architecture.ts +221 -0
  100. package/template/scripts/check-doc-freshness.ts +242 -0
  101. package/template/scripts/ensure-db.mjs +291 -0
  102. package/template/scripts/generate-docs.ts +143 -0
  103. package/template/scripts/generate-env-example.ts +89 -0
  104. package/template/scripts/seed.ts +56 -0
  105. package/template/scripts/update-quality-score.ts +263 -0
  106. package/template/src/__tests__/architecture.test.ts +114 -0
  107. package/template/src/app/(auth)/forgotten-password/page.tsx +92 -0
  108. package/template/src/app/(auth)/layout.tsx +11 -0
  109. package/template/src/app/(auth)/register/page.tsx +162 -0
  110. package/template/src/app/(auth)/reset-password/page.tsx +109 -0
  111. package/template/src/app/(auth)/sign-in/page.tsx +122 -0
  112. package/template/src/app/(auth)/verify/[token]/page.tsx +87 -0
  113. package/template/src/app/(auth)/verify/page.tsx +56 -0
  114. package/template/src/app/(protected)/admin/page.tsx +108 -0
  115. package/template/src/app/(protected)/dashboard/loading.tsx +20 -0
  116. package/template/src/app/(protected)/dashboard/page.tsx +22 -0
  117. package/template/src/app/(protected)/layout.tsx +262 -0
  118. package/template/src/app/(protected)/settings/page.tsx +370 -0
  119. package/template/src/app/api/auth/forgot/route.ts +63 -0
  120. package/template/src/app/api/auth/login/route.ts +121 -0
  121. package/template/src/app/api/auth/logout/route.ts +19 -0
  122. package/template/src/app/api/auth/me/route.ts +30 -0
  123. package/template/src/app/api/auth/reset/route.ts +45 -0
  124. package/template/src/app/api/auth/signup/route.ts +85 -0
  125. package/template/src/app/api/auth/verify/route.ts +46 -0
  126. package/template/src/app/api/csrf/route.ts +12 -0
  127. package/template/src/app/api/health/route.ts +10 -0
  128. package/template/src/app/api/protected/admin/users/route.ts +24 -0
  129. package/template/src/app/api/protected/billing/checkout/route.ts +83 -0
  130. package/template/src/app/api/protected/billing/portal/route.ts +39 -0
  131. package/template/src/app/api/protected/files/[fileId]/route.ts +86 -0
  132. package/template/src/app/api/protected/files/upload/route.ts +64 -0
  133. package/template/src/app/api/protected/user/password/route.ts +63 -0
  134. package/template/src/app/api/protected/user/profile/route.ts +35 -0
  135. package/template/src/app/api/protected/user/sessions/[sessionId]/route.ts +33 -0
  136. package/template/src/app/api/protected/user/sessions/route.ts +22 -0
  137. package/template/src/app/api/readiness/route.ts +15 -0
  138. package/template/src/app/api/webhooks/stripe/route.ts +166 -0
  139. package/template/src/app/error.tsx +33 -0
  140. package/template/src/app/layout.tsx +29 -0
  141. package/template/src/app/not-found.tsx +20 -0
  142. package/template/src/app/page.tsx +136 -0
  143. package/template/src/app/privacy/page.tsx +178 -0
  144. package/template/src/app/providers.tsx +8 -0
  145. package/template/src/app/terms/page.tsx +139 -0
  146. package/template/src/config/app.config.ts +70 -0
  147. package/template/src/config/routes.ts +17 -0
  148. package/template/src/features/admin/index.ts +11 -0
  149. package/template/src/features/admin/permissions.ts +64 -0
  150. package/template/src/features/auth/context/AuthContext.tsx +96 -0
  151. package/template/src/features/auth/context/index.ts +2 -0
  152. package/template/src/features/auth/index.ts +3 -0
  153. package/template/src/features/auth/server/consent.ts +66 -0
  154. package/template/src/features/auth/server/session-revocation.ts +20 -0
  155. package/template/src/features/auth/server/sessions.ts +66 -0
  156. package/template/src/features/auth/server/user.ts +166 -0
  157. package/template/src/features/auth/types.ts +19 -0
  158. package/template/src/features/auth/validators.ts +29 -0
  159. package/template/src/features/billing/server/index.ts +66 -0
  160. package/template/src/features/billing/types.ts +43 -0
  161. package/template/src/features/uploads/server/index.ts +49 -0
  162. package/template/src/features/uploads/types.ts +26 -0
  163. package/template/src/lib/core/email/templates/base-layout.ts +122 -0
  164. package/template/src/lib/core/email/templates/index.ts +4 -0
  165. package/template/src/lib/core/email/templates/password-reset-email.ts +42 -0
  166. package/template/src/lib/core/email/templates/verification-email.ts +41 -0
  167. package/template/src/lib/core/email/templates/welcome-email.ts +40 -0
  168. package/template/src/lib/mars.ts +56 -0
  169. package/template/src/lib/prisma.ts +19 -0
  170. package/template/src/proxy.ts +92 -0
  171. package/template/src/styles/brand.css +15 -0
  172. package/template/src/styles/globals.css +7 -0
  173. package/template/tsconfig.json +59 -0
  174. package/template/vitest.config.ts +41 -0
  175. package/template/vitest.setup.ts +24 -0
@@ -0,0 +1,178 @@
1
+ import Link from 'next/link';
2
+ import { appConfig } from '@/config/app.config';
3
+ import { routes } from '@/config/routes';
4
+
5
+ export default function PrivacyPage() {
6
+ const companyName = appConfig.legal.companyName || appConfig.name;
7
+
8
+ return (
9
+ <main className="mx-auto max-w-3xl px-6 py-16">
10
+ <h1 className="text-4xl font-bold leading-tight text-text-primary mb-2">Privacy Policy</h1>
11
+ <p className="text-base leading-relaxed text-text-muted mb-4">
12
+ Last updated: {'[Replace this content: add date]'}
13
+ </p>
14
+
15
+ <hr className="my-8 border-border-default" />
16
+
17
+ <section className="mb-10">
18
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">1. Introduction</h2>
19
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
20
+ {/* Replace this content: describe your privacy commitment */}
21
+ {companyName} (&quot;we&quot;, &quot;us&quot;, or &quot;our&quot;) respects your privacy and
22
+ is committed to protecting your personal data. This Privacy Policy explains how we collect,
23
+ use, disclose, and safeguard your information when you use our service.
24
+ </p>
25
+ </section>
26
+
27
+ <section className="mb-10">
28
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">2. Data We Collect</h2>
29
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
30
+ {/* Replace this content: list all categories of data you collect */}
31
+ We may collect the following types of information:
32
+ </p>
33
+ <ul className="mb-4 ml-6 list-disc space-y-2 text-text-secondary">
34
+ <li>
35
+ <strong className="text-text-primary">Account information</strong> — name, email address,
36
+ and password when you register.
37
+ </li>
38
+ <li>
39
+ <strong className="text-text-primary">Usage data</strong> — pages visited, features used,
40
+ timestamps, and referring URLs.
41
+ </li>
42
+ <li>
43
+ <strong className="text-text-primary">Device information</strong> — browser type,
44
+ operating system, IP address, and device identifiers.
45
+ </li>
46
+ <li>
47
+ <strong className="text-text-primary">Cookies</strong> — session cookies and preferences.
48
+ See our cookie policy for details.
49
+ </li>
50
+ {/* Replace this content: add or remove data categories as needed */}
51
+ </ul>
52
+ </section>
53
+
54
+ <section className="mb-10">
55
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">3. How We Use Your Data</h2>
56
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
57
+ {/* Replace this content: describe each purpose for data processing */}
58
+ We use the information we collect for the following purposes:
59
+ </p>
60
+ <ul className="mb-4 ml-6 list-disc space-y-2 text-text-secondary">
61
+ <li>To provide, maintain, and improve our service.</li>
62
+ <li>To authenticate users and manage accounts.</li>
63
+ <li>To communicate with you about updates, security alerts, and support.</li>
64
+ <li>To detect, prevent, and address technical issues and abuse.</li>
65
+ <li>To comply with legal obligations.</li>
66
+ {/* Replace this content: add or remove purposes as needed */}
67
+ </ul>
68
+ </section>
69
+
70
+ <section className="mb-10">
71
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">4. Data Sharing &amp; Disclosure</h2>
72
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
73
+ {/* Replace this content: list third parties and data processors */}
74
+ We do not sell your personal data. We may share your information with third-party service
75
+ providers who assist us in operating our service, subject to confidentiality obligations.
76
+ We may also disclose your information if required by law or to protect our rights.
77
+ </p>
78
+ </section>
79
+
80
+ <section className="mb-10">
81
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">5. Data Retention</h2>
82
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
83
+ {/* Replace this content: specify your data retention periods */}
84
+ We retain your personal data only for as long as necessary to fulfil the purposes for which
85
+ it was collected, including to satisfy any legal, accounting, or reporting requirements.
86
+ [Replace this content: add specific retention periods for each data category.]
87
+ </p>
88
+ </section>
89
+
90
+ <section className="mb-10">
91
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">6. Data Security</h2>
92
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
93
+ {/* Replace this content: describe your security measures */}
94
+ We implement appropriate technical and organisational measures to protect your personal data
95
+ against unauthorised access, alteration, disclosure, or destruction. However, no method of
96
+ transmission over the Internet is 100% secure.
97
+ </p>
98
+ </section>
99
+
100
+ <section className="mb-10">
101
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">7. Your Rights</h2>
102
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
103
+ {/* Replace this content: list rights applicable in your jurisdiction (GDPR, CCPA, etc.) */}
104
+ Depending on your location, you may have the following rights regarding your personal data:
105
+ </p>
106
+ <ul className="mb-4 ml-6 list-disc space-y-2 text-text-secondary">
107
+ <li>The right to access the personal data we hold about you.</li>
108
+ <li>The right to request correction of inaccurate data.</li>
109
+ <li>The right to request deletion of your data.</li>
110
+ <li>The right to restrict or object to processing.</li>
111
+ <li>The right to data portability.</li>
112
+ <li>The right to withdraw consent at any time.</li>
113
+ {/* Replace this content: add jurisdiction-specific rights */}
114
+ </ul>
115
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
116
+ To exercise any of these rights, please contact us at{' '}
117
+ <a
118
+ href={`mailto:${appConfig.support.email}`}
119
+ className="text-text-link hover:text-text-link-hover transition-colors hover:underline"
120
+ >
121
+ {appConfig.support.email}
122
+ </a>
123
+ .
124
+ </p>
125
+ </section>
126
+
127
+ <section className="mb-10">
128
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">8. International Transfers</h2>
129
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
130
+ {/* Replace this content: describe your data transfer mechanisms */}
131
+ [Replace this content: describe where data is stored and any international transfer
132
+ mechanisms you use, such as Standard Contractual Clauses or adequacy decisions.]
133
+ </p>
134
+ </section>
135
+
136
+ <section className="mb-10">
137
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">9. Changes to This Policy</h2>
138
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
139
+ We may update this Privacy Policy from time to time. We will notify you of any changes by
140
+ posting the new Privacy Policy on this page and updating the &quot;Last updated&quot; date.
141
+ You are advised to review this page periodically for any changes.
142
+ </p>
143
+ </section>
144
+
145
+ <section>
146
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">10. Contact Us</h2>
147
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
148
+ If you have any questions about this Privacy Policy, please contact us at{' '}
149
+ <a
150
+ href={`mailto:${appConfig.support.email}`}
151
+ className="text-text-link hover:text-text-link-hover transition-colors hover:underline"
152
+ >
153
+ {appConfig.support.email}
154
+ </a>
155
+ .
156
+ </p>
157
+ {appConfig.legal.address && (
158
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
159
+ {companyName}
160
+ <br />
161
+ {appConfig.legal.address}
162
+ </p>
163
+ )}
164
+ </section>
165
+
166
+ <hr className="my-8 border-border-default" />
167
+
168
+ <nav className="flex gap-6">
169
+ <Link href={routes.terms} className="text-sm text-text-link hover:text-text-link-hover transition-colors hover:underline">
170
+ Terms of Service
171
+ </Link>
172
+ <Link href={routes.home} className="text-sm text-text-link hover:text-text-link-hover transition-colors hover:underline">
173
+ Home
174
+ </Link>
175
+ </nav>
176
+ </main>
177
+ );
178
+ }
@@ -0,0 +1,8 @@
1
+ 'use client';
2
+
3
+ import { AuthProvider } from '@/features/auth/context/AuthContext';
4
+ import type { ReactNode } from 'react';
5
+
6
+ export function Providers({ children }: { children: ReactNode }) {
7
+ return <AuthProvider>{children}</AuthProvider>;
8
+ }
@@ -0,0 +1,139 @@
1
+ import Link from 'next/link';
2
+ import { appConfig } from '@/config/app.config';
3
+ import { routes } from '@/config/routes';
4
+
5
+ export default function TermsPage() {
6
+ const companyName = appConfig.legal.companyName || appConfig.name;
7
+
8
+ return (
9
+ <main className="mx-auto max-w-3xl px-6 py-16">
10
+ <h1 className="text-4xl font-bold leading-tight text-text-primary mb-2">Terms of Service</h1>
11
+ <p className="text-base leading-relaxed text-text-muted mb-4">
12
+ Last updated: {'[Replace this content: add date]'}
13
+ </p>
14
+
15
+ <hr className="my-8 border-border-default" />
16
+
17
+ <section className="mb-10">
18
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">1. Agreement to Terms</h2>
19
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
20
+ {/* Replace this content: describe your terms of service agreement */}
21
+ By accessing or using the services provided by {companyName} (&quot;we&quot;, &quot;us&quot;, or &quot;our&quot;),
22
+ you agree to be bound by these Terms of Service. If you do not agree to these terms, do not
23
+ use our services.
24
+ </p>
25
+ </section>
26
+
27
+ <section className="mb-10">
28
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">2. User Accounts</h2>
29
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
30
+ {/* Replace this content: describe your account requirements */}
31
+ When you create an account with us, you must provide accurate, complete, and current
32
+ information. You are responsible for safeguarding the password that you use to access our
33
+ service and for any activities or actions under your password.
34
+ </p>
35
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
36
+ {/* Replace this content: describe account termination policy */}
37
+ We reserve the right to suspend or terminate your account if any information provided during
38
+ the registration process or thereafter proves to be inaccurate, not current, or incomplete.
39
+ </p>
40
+ </section>
41
+
42
+ <section className="mb-10">
43
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">3. Acceptable Use</h2>
44
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
45
+ {/* Replace this content: describe your acceptable use policy */}
46
+ You agree not to use the service for any unlawful purpose or in any way that could damage,
47
+ disable, overburden, or impair our servers or networks. You must not attempt to gain
48
+ unauthorized access to any part of the service, other accounts, or computer systems through
49
+ hacking, password mining, or any other means.
50
+ </p>
51
+ </section>
52
+
53
+ <section className="mb-10">
54
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">4. Intellectual Property</h2>
55
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
56
+ {/* Replace this content: describe IP ownership and licensing */}
57
+ The service and its original content, features, and functionality are and will remain the
58
+ exclusive property of {companyName}. Our service is protected by copyright, trademark, and
59
+ other laws. Our trademarks may not be used in connection with any product or service without
60
+ prior written consent.
61
+ </p>
62
+ </section>
63
+
64
+ <section className="mb-10">
65
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">5. Payment Terms</h2>
66
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
67
+ {/* Replace this content: describe your billing and payment terms, or remove this section if not applicable */}
68
+ [Replace this content: add your payment terms, billing cycles, refund policy, and
69
+ cancellation procedures.]
70
+ </p>
71
+ </section>
72
+
73
+ <section className="mb-10">
74
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">6. Limitation of Liability</h2>
75
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
76
+ {/* Replace this content: consult a lawyer for your jurisdiction */}
77
+ In no event shall {companyName}, nor its directors, employees, partners, agents, suppliers,
78
+ or affiliates, be liable for any indirect, incidental, special, consequential, or punitive
79
+ damages, including without limitation, loss of profits, data, use, goodwill, or other
80
+ intangible losses, resulting from your access to or use of, or inability to access or use,
81
+ the service.
82
+ </p>
83
+ </section>
84
+
85
+ <section className="mb-10">
86
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">7. Termination</h2>
87
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
88
+ {/* Replace this content: describe termination conditions */}
89
+ We may terminate or suspend your account immediately, without prior notice or liability, for
90
+ any reason whatsoever, including without limitation if you breach the Terms. Upon
91
+ termination, your right to use the service will immediately cease.
92
+ </p>
93
+ </section>
94
+
95
+ <section className="mb-10">
96
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">8. Governing Law</h2>
97
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
98
+ {/* Replace this content: specify your legal jurisdiction */}
99
+ These Terms shall be governed and construed in accordance with the laws of [Replace this
100
+ content: add your jurisdiction], without regard to its conflict of law provisions.
101
+ </p>
102
+ </section>
103
+
104
+ <section className="mb-10">
105
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">9. Changes to Terms</h2>
106
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
107
+ We reserve the right to modify or replace these Terms at any time. If a revision is
108
+ material, we will provide at least 30 days&apos; notice prior to any new terms taking
109
+ effect. What constitutes a material change will be determined at our sole discretion.
110
+ </p>
111
+ </section>
112
+
113
+ <section>
114
+ <h2 className="text-3xl font-bold leading-tight text-text-primary mb-2">10. Contact Us</h2>
115
+ <p className="text-base leading-relaxed text-text-secondary mb-4">
116
+ If you have any questions about these Terms, please contact us at{' '}
117
+ <a
118
+ href={`mailto:${appConfig.support.email}`}
119
+ className="text-text-link hover:text-text-link-hover transition-colors hover:underline"
120
+ >
121
+ {appConfig.support.email}
122
+ </a>
123
+ .
124
+ </p>
125
+ </section>
126
+
127
+ <hr className="my-8 border-border-default" />
128
+
129
+ <nav className="flex gap-6">
130
+ <Link href={routes.privacy} className="text-sm text-text-link hover:text-text-link-hover transition-colors hover:underline">
131
+ Privacy Policy
132
+ </Link>
133
+ <Link href={routes.home} className="text-sm text-text-link hover:text-text-link-hover transition-colors hover:underline">
134
+ Home
135
+ </Link>
136
+ </nav>
137
+ </main>
138
+ );
139
+ }
@@ -0,0 +1,70 @@
1
+ export const appConfig = {
2
+ name: 'My App',
3
+ tagline: 'A short description',
4
+ description: 'A longer description for meta tags and SEO',
5
+ url: 'https://myapp.com',
6
+ support: { email: 'support@myapp.com', phone: '' },
7
+ social: {
8
+ twitter: '',
9
+ tiktok: '',
10
+ linkedin: '',
11
+ github: '',
12
+ instagram: '',
13
+ },
14
+ legal: {
15
+ companyName: '',
16
+ registrationNumber: '',
17
+ address: '',
18
+ },
19
+ theme: {
20
+ primaryColor: 'blue-600' as string,
21
+ secondaryColor: 'amber-400' as string,
22
+ font: 'Inter' as string,
23
+ designDirection: 'modern-saas' as
24
+ | 'modern-saas'
25
+ | 'minimal'
26
+ | 'enterprise'
27
+ | 'creative'
28
+ | 'dashboard',
29
+ },
30
+ features: {
31
+ auth: true,
32
+ googleOAuth: false,
33
+ emailVerification: true,
34
+ magicLinks: false,
35
+ twoFactor: false,
36
+ admin: true,
37
+ darkMode: false,
38
+ notifications: false,
39
+ onboarding: false,
40
+ multiTenancy: false,
41
+ teams: false,
42
+ billing: false,
43
+ blog: false,
44
+ seo: true,
45
+ comingSoon: false,
46
+ cookieConsent: true,
47
+ analytics: false,
48
+ sentry: false,
49
+ ai: false,
50
+ fileUpload: false,
51
+ search: false,
52
+ realtime: false,
53
+ commandPalette: false,
54
+ featureFlags: false,
55
+ },
56
+ services: {
57
+ email: { provider: 'console' as 'sendgrid' | 'resend' | 'console' },
58
+ storage: { provider: 'local' as 'vercel' | 's3' | 'local' },
59
+ database: { provider: 'local' as 'vercel' | 'supabase' | 'local' },
60
+ payments: { provider: 'none' as 'stripe' | 'none' },
61
+ analytics: { provider: 'none' as 'vercel' | 'posthog' | 'google' | 'none' },
62
+ monitoring: { provider: 'none' as 'sentry' | 'none' },
63
+ ai: { provider: 'none' as 'openai' | 'anthropic' | 'none' },
64
+ search: { provider: 'none' as 'postgres' | 'algolia' | 'meilisearch' | 'none' },
65
+ realtime: { provider: 'none' as 'sse' | 'pusher' | 'ably' | 'none' },
66
+ jobs: { provider: 'none' as 'inngest' | 'trigger' | 'db' | 'none' },
67
+ },
68
+ } as const;
69
+
70
+ export type AppConfig = typeof appConfig;
@@ -0,0 +1,17 @@
1
+ export const routes = {
2
+ home: '/',
3
+ signIn: '/sign-in',
4
+ signUp: '/register',
5
+ forgotPassword: '/forgotten-password',
6
+ resetPassword: '/reset-password',
7
+ verifyEmail: '/verify',
8
+ dashboard: '/dashboard',
9
+ settings: '/settings',
10
+ admin: '/admin',
11
+ about: '/about',
12
+ privacy: '/privacy',
13
+ terms: '/terms',
14
+ blog: '/blog',
15
+ } as const;
16
+
17
+ export type RoutePath = (typeof routes)[keyof typeof routes];
@@ -0,0 +1,11 @@
1
+ export {
2
+ hasRole,
3
+ assertRole,
4
+ assertRoles,
5
+ canAccessResource,
6
+ assertCanAccessResource,
7
+ getRoleHierarchy,
8
+ hasRoleOrHigher,
9
+ assertRoleOrHigher,
10
+ } from './permissions';
11
+ export type { Role } from './permissions';
@@ -0,0 +1,64 @@
1
+ import { getCurrentUser } from '@/lib/mars';
2
+
3
+ export type Role = 'user' | 'editor' | 'admin';
4
+
5
+ export async function hasRole(requiredRole: Role): Promise<boolean> {
6
+ const user = await getCurrentUser();
7
+ if (!user) return false;
8
+ if (user.role === 'admin') return true;
9
+ return user.role === requiredRole;
10
+ }
11
+
12
+ export async function assertRole(requiredRole: Role): Promise<void> {
13
+ const hasPermission = await hasRole(requiredRole);
14
+ if (!hasPermission) throw new Error(`Unauthorized: ${requiredRole} role required`);
15
+ }
16
+
17
+ export async function assertRoles(requiredRoles: Role[]): Promise<void> {
18
+ const user = await getCurrentUser();
19
+ if (!user) throw new Error('Unauthorized: Authentication required');
20
+ if (user.role === 'admin') return;
21
+
22
+ const hasPermission = requiredRoles.includes(user.role as Role);
23
+ if (!hasPermission) {
24
+ throw new Error(`Unauthorized: One of [${requiredRoles.join(', ')}] roles required`);
25
+ }
26
+ }
27
+
28
+ export async function canAccessResource(
29
+ resourceUserId: string,
30
+ requiredRole?: Role,
31
+ ): Promise<boolean> {
32
+ const user = await getCurrentUser();
33
+ if (!user) return false;
34
+ if (user.id === resourceUserId) return true;
35
+ if (requiredRole) return hasRole(requiredRole);
36
+ return user.role === 'admin';
37
+ }
38
+
39
+ export async function assertCanAccessResource(
40
+ resourceUserId: string,
41
+ requiredRole?: Role,
42
+ ): Promise<void> {
43
+ const canAccess = await canAccessResource(resourceUserId, requiredRole);
44
+ if (!canAccess) throw new Error('Unauthorized: Cannot access this resource');
45
+ }
46
+
47
+ export function getRoleHierarchy(): Record<Role, number> {
48
+ return { user: 1, editor: 2, admin: 3 };
49
+ }
50
+
51
+ export async function hasRoleOrHigher(minimumRole: Role): Promise<boolean> {
52
+ const user = await getCurrentUser();
53
+ if (!user) return false;
54
+
55
+ const hierarchy = getRoleHierarchy();
56
+ const userLevel = hierarchy[user.role as Role] || 0;
57
+ const requiredLevel = hierarchy[minimumRole];
58
+ return userLevel >= requiredLevel;
59
+ }
60
+
61
+ export async function assertRoleOrHigher(minimumRole: Role): Promise<void> {
62
+ const hasPermission = await hasRoleOrHigher(minimumRole);
63
+ if (!hasPermission) throw new Error(`Unauthorized: ${minimumRole} role or higher required`);
64
+ }
@@ -0,0 +1,96 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
4
+
5
+ export interface User {
6
+ id: string;
7
+ name: string;
8
+ role: string;
9
+ emailVerified: boolean;
10
+ }
11
+
12
+ interface AuthContextType {
13
+ user: User | null;
14
+ isLoading: boolean;
15
+ isAuthenticated: boolean;
16
+ login: (user: User) => void;
17
+ logout: () => Promise<void>;
18
+ updateUser: (updates: Partial<User>) => void;
19
+ refreshAuth: () => Promise<void>;
20
+ }
21
+
22
+ const AuthContext = createContext<AuthContextType | undefined>(undefined);
23
+
24
+ export function useAuth() {
25
+ const context = useContext(AuthContext);
26
+ if (context === undefined) {
27
+ throw new Error('useAuth must be used within an AuthProvider');
28
+ }
29
+ return context;
30
+ }
31
+
32
+ interface AuthProviderProps {
33
+ children: ReactNode;
34
+ initialUser?: User | null;
35
+ }
36
+
37
+ export function AuthProvider({ children, initialUser = null }: AuthProviderProps) {
38
+ const [user, setUser] = useState<User | null>(initialUser);
39
+ const [isLoading, setIsLoading] = useState(!initialUser);
40
+
41
+ const refreshAuth = async () => {
42
+ try {
43
+ setIsLoading(true);
44
+ const response = await fetch('/api/auth/me', { credentials: 'include' });
45
+
46
+ if (!response.ok) {
47
+ setUser(null);
48
+ return;
49
+ }
50
+
51
+ const userData: { user: User | null } = await response.json();
52
+ setUser(userData.user ?? null);
53
+ } catch (error) {
54
+ console.error('Failed to refresh auth:', error);
55
+ setUser(null);
56
+ } finally {
57
+ setIsLoading(false);
58
+ }
59
+ };
60
+
61
+ const login = (userData: User) => {
62
+ setUser(userData);
63
+ };
64
+
65
+ const logout = async () => {
66
+ try {
67
+ await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' });
68
+ } catch (error) {
69
+ console.error('Logout error:', error);
70
+ } finally {
71
+ setUser(null);
72
+ }
73
+ };
74
+
75
+ const updateUser = (updates: Partial<User>) => {
76
+ setUser((current) => (current ? { ...current, ...updates } : null));
77
+ };
78
+
79
+ useEffect(() => {
80
+ if (!initialUser) {
81
+ refreshAuth();
82
+ }
83
+ }, [initialUser]);
84
+
85
+ const value: AuthContextType = {
86
+ user,
87
+ isLoading,
88
+ isAuthenticated: !!user,
89
+ login,
90
+ logout,
91
+ updateUser,
92
+ refreshAuth,
93
+ };
94
+
95
+ return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
96
+ }
@@ -0,0 +1,2 @@
1
+ export { AuthProvider, useAuth } from './AuthContext';
2
+ export type { User } from './AuthContext';
@@ -0,0 +1,3 @@
1
+ export type { AuthError, AuthUser, AuthErrorCode } from '@mars-stack/core/auth/types';
2
+ export { validateEmail, validatePassword, validateRequired } from '@mars-stack/core/auth/validators';
3
+ export { passwordSchema, hashPassword, verifyPassword } from '@mars-stack/core/auth/password';
@@ -0,0 +1,66 @@
1
+ import 'server-only';
2
+
3
+ import { prisma } from '@/lib/prisma';
4
+ import { verifySession } from '@/lib/mars';
5
+
6
+ export interface ConsentPreferences {
7
+ termsAcceptedAt: Date | null;
8
+ privacyAcceptedAt: Date | null;
9
+ marketingOptIn: boolean;
10
+ marketingOptInAt: Date | null;
11
+ }
12
+
13
+ export async function getUserConsent(userId: string): Promise<ConsentPreferences | null> {
14
+ return prisma.user.findUnique({
15
+ where: { id: userId },
16
+ select: {
17
+ termsAcceptedAt: true,
18
+ privacyAcceptedAt: true,
19
+ marketingOptIn: true,
20
+ marketingOptInAt: true,
21
+ },
22
+ });
23
+ }
24
+
25
+ export async function updateMarketingConsent(marketingOptIn: boolean): Promise<void> {
26
+ const session = await verifySession();
27
+ const now = new Date();
28
+
29
+ await prisma.user.update({
30
+ where: { id: session.userId },
31
+ data: {
32
+ marketingOptIn,
33
+ marketingOptInAt: marketingOptIn ? now : null,
34
+ },
35
+ });
36
+ }
37
+
38
+ export async function recordTermsAcceptance(): Promise<void> {
39
+ const session = await verifySession();
40
+ const now = new Date();
41
+
42
+ await prisma.user.update({
43
+ where: { id: session.userId },
44
+ data: { termsAcceptedAt: now, privacyAcceptedAt: now },
45
+ });
46
+ }
47
+
48
+ export async function getMarketingOptInUsers(): Promise<
49
+ Array<{ id: string; email: string; name: string | null }>
50
+ > {
51
+ const session = await verifySession();
52
+
53
+ const dbUser = await prisma.user.findUnique({
54
+ where: { id: session.userId },
55
+ select: { role: true },
56
+ });
57
+
58
+ if (!dbUser || dbUser.role !== 'admin') {
59
+ throw new Error('Unauthorized: Admin access required');
60
+ }
61
+
62
+ return prisma.user.findMany({
63
+ where: { marketingOptIn: true, emailVerified: { not: null } },
64
+ select: { id: true, email: true, name: true },
65
+ });
66
+ }