create-skit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +36 -0
  2. package/bin/create-skit.mjs +1064 -0
  3. package/lib/module-application.mjs +281 -0
  4. package/lib/module-resolver.mjs +179 -0
  5. package/modules/README.md +22 -0
  6. package/modules/ai-dx/files/AGENTS.md +116 -0
  7. package/modules/ai-dx/files/ARCHITECTURE.md +103 -0
  8. package/modules/ai-dx/module.json +14 -0
  9. package/modules/ai-dx-claude/files/CLAUDE.md +8 -0
  10. package/modules/ai-dx-claude/module.json +13 -0
  11. package/modules/ai-dx-cursor/files/.cursor/rules/auth.mdc +53 -0
  12. package/modules/ai-dx-cursor/files/.cursor/rules/database.mdc +48 -0
  13. package/modules/ai-dx-cursor/files/.cursor/rules/env.mdc +43 -0
  14. package/modules/ai-dx-cursor/files/.cursor/rules/nextjs.mdc +58 -0
  15. package/modules/ai-dx-cursor/files/.cursor/rules/project.mdc +33 -0
  16. package/modules/ai-dx-cursor/files/.cursor/rules/testing.mdc +55 -0
  17. package/modules/ai-dx-cursor/module.json +18 -0
  18. package/modules/ai-dx-gemini/files/.gemini/GEMINI.md +5 -0
  19. package/modules/ai-dx-gemini/module.json +13 -0
  20. package/modules/auth-core/module.json +8 -0
  21. package/modules/auth-github/module.json +20 -0
  22. package/modules/billing-polar/module.json +20 -0
  23. package/modules/billing-stripe/module.json +23 -0
  24. package/modules/dashboard-shell/files/src/app/globals.css +756 -0
  25. package/modules/dashboard-shell/files/src/app/settings/page.tsx +67 -0
  26. package/modules/dashboard-shell/module.json +11 -0
  27. package/modules/db-pg/module.json +21 -0
  28. package/modules/db-postgresjs/module.json +21 -0
  29. package/modules/deploy-docker/files/.dockerignore +19 -0
  30. package/modules/deploy-docker/files/Dockerfile +25 -0
  31. package/modules/deploy-docker/module.json +11 -0
  32. package/modules/email-resend/module.json +21 -0
  33. package/modules/quality-baseline/module.json +8 -0
  34. package/modules/testing-baseline/module.json +8 -0
  35. package/package.json +40 -0
  36. package/presets/README.md +12 -0
  37. package/presets/blank.json +67 -0
  38. package/presets/dashboard.json +67 -0
  39. package/templates/base-web/.env.example +17 -0
  40. package/templates/base-web/.github/workflows/ci.yml +34 -0
  41. package/templates/base-web/.husky/pre-commit +3 -0
  42. package/templates/base-web/.husky/pre-push +3 -0
  43. package/templates/base-web/.prettierignore +3 -0
  44. package/templates/base-web/README.md +42 -0
  45. package/templates/base-web/drizzle.config.ts +16 -0
  46. package/templates/base-web/eslint.config.mjs +127 -0
  47. package/templates/base-web/manifest.json +5 -0
  48. package/templates/base-web/next-env.d.ts +4 -0
  49. package/templates/base-web/next.config.ts +5 -0
  50. package/templates/base-web/package.json +75 -0
  51. package/templates/base-web/playwright.config.ts +21 -0
  52. package/templates/base-web/prettier.config.mjs +9 -0
  53. package/templates/base-web/proxy.ts +23 -0
  54. package/templates/base-web/src/app/api/auth/[...all]/route.ts +5 -0
  55. package/templates/base-web/src/app/api/billing/checkout/route.ts +26 -0
  56. package/templates/base-web/src/app/api/billing/portal/route.ts +25 -0
  57. package/templates/base-web/src/app/api/email/test/route.ts +28 -0
  58. package/templates/base-web/src/app/api/webhooks/polar/route.ts +5 -0
  59. package/templates/base-web/src/app/api/webhooks/stripe/route.ts +5 -0
  60. package/templates/base-web/src/app/billing/page.tsx +55 -0
  61. package/templates/base-web/src/app/dashboard/page.tsx +15 -0
  62. package/templates/base-web/src/app/email/page.tsx +46 -0
  63. package/templates/base-web/src/app/error.tsx +27 -0
  64. package/templates/base-web/src/app/globals.css +534 -0
  65. package/templates/base-web/src/app/layout.tsx +19 -0
  66. package/templates/base-web/src/app/llms-full.txt/route.ts +158 -0
  67. package/templates/base-web/src/app/llms.txt/route.ts +59 -0
  68. package/templates/base-web/src/app/loading.tsx +24 -0
  69. package/templates/base-web/src/app/not-found.tsx +16 -0
  70. package/templates/base-web/src/app/page.tsx +5 -0
  71. package/templates/base-web/src/app/sign-in/page.tsx +14 -0
  72. package/templates/base-web/src/app/sign-up/page.tsx +14 -0
  73. package/templates/base-web/src/components/auth/email-auth-form.test.tsx +40 -0
  74. package/templates/base-web/src/components/auth/email-auth-form.tsx +128 -0
  75. package/templates/base-web/src/components/auth/sign-out-button.tsx +29 -0
  76. package/templates/base-web/src/db/index.ts +16 -0
  77. package/templates/base-web/src/db/schema/auth.ts +4 -0
  78. package/templates/base-web/src/db/schema/index.ts +2 -0
  79. package/templates/base-web/src/db/schema/projects.ts +17 -0
  80. package/templates/base-web/src/db/seeds/index.ts +32 -0
  81. package/templates/base-web/src/lib/auth-client.ts +5 -0
  82. package/templates/base-web/src/lib/auth-session.ts +21 -0
  83. package/templates/base-web/src/lib/auth.ts +23 -0
  84. package/templates/base-web/src/lib/billing/index.ts +37 -0
  85. package/templates/base-web/src/lib/billing/providers/polar.ts +80 -0
  86. package/templates/base-web/src/lib/billing/providers/stripe.ts +77 -0
  87. package/templates/base-web/src/lib/billing/types.ts +25 -0
  88. package/templates/base-web/src/lib/email/index.ts +19 -0
  89. package/templates/base-web/src/lib/email/templates.test.ts +12 -0
  90. package/templates/base-web/src/lib/email/templates.ts +40 -0
  91. package/templates/base-web/src/lib/env.ts +83 -0
  92. package/templates/base-web/tests/e2e/home.spec.ts +8 -0
  93. package/templates/base-web/tsconfig.json +34 -0
  94. package/templates/base-web/vitest.config.ts +19 -0
  95. package/templates/blank/.env.example +16 -0
  96. package/templates/blank/.github/workflows/ci.yml +34 -0
  97. package/templates/blank/.husky/pre-commit +3 -0
  98. package/templates/blank/.husky/pre-push +3 -0
  99. package/templates/blank/.prettierignore +3 -0
  100. package/templates/blank/drizzle.config.ts +16 -0
  101. package/templates/blank/eslint.config.mjs +127 -0
  102. package/templates/blank/next-env.d.ts +4 -0
  103. package/templates/blank/next.config.ts +5 -0
  104. package/templates/blank/package.json +75 -0
  105. package/templates/blank/playwright.config.ts +21 -0
  106. package/templates/blank/prettier.config.mjs +9 -0
  107. package/templates/blank/proxy.ts +28 -0
  108. package/templates/blank/src/app/api/auth/[...all]/route.ts +5 -0
  109. package/templates/blank/src/app/api/billing/checkout/route.ts +26 -0
  110. package/templates/blank/src/app/api/billing/portal/route.ts +25 -0
  111. package/templates/blank/src/app/api/email/test/route.ts +28 -0
  112. package/templates/blank/src/app/api/webhooks/polar/route.ts +5 -0
  113. package/templates/blank/src/app/api/webhooks/stripe/route.ts +5 -0
  114. package/templates/blank/src/app/billing/page.tsx +70 -0
  115. package/templates/blank/src/app/email/page.tsx +46 -0
  116. package/templates/blank/src/app/globals.css +394 -0
  117. package/templates/blank/src/app/layout.tsx +19 -0
  118. package/templates/blank/src/app/page.tsx +23 -0
  119. package/templates/blank/src/app/sign-in/page.tsx +18 -0
  120. package/templates/blank/src/app/sign-up/page.tsx +18 -0
  121. package/templates/blank/src/components/auth/email-auth-form.test.tsx +39 -0
  122. package/templates/blank/src/components/auth/email-auth-form.tsx +109 -0
  123. package/templates/blank/src/components/auth/sign-out-button.tsx +29 -0
  124. package/templates/blank/src/db/index.ts +16 -0
  125. package/templates/blank/src/db/schema/auth.ts +4 -0
  126. package/templates/blank/src/db/schema/index.ts +2 -0
  127. package/templates/blank/src/db/schema/projects.ts +17 -0
  128. package/templates/blank/src/db/seeds/index.ts +28 -0
  129. package/templates/blank/src/lib/auth-client.ts +5 -0
  130. package/templates/blank/src/lib/auth-session.ts +11 -0
  131. package/templates/blank/src/lib/auth.ts +23 -0
  132. package/templates/blank/src/lib/billing/index.ts +37 -0
  133. package/templates/blank/src/lib/billing/providers/polar.ts +80 -0
  134. package/templates/blank/src/lib/billing/providers/stripe.ts +77 -0
  135. package/templates/blank/src/lib/billing/types.ts +25 -0
  136. package/templates/blank/src/lib/email/index.ts +19 -0
  137. package/templates/blank/src/lib/email/templates.test.ts +15 -0
  138. package/templates/blank/src/lib/email/templates.ts +40 -0
  139. package/templates/blank/src/lib/env.ts +80 -0
  140. package/templates/blank/tsconfig.json +34 -0
  141. package/templates/blank/vitest.config.ts +19 -0
  142. package/templates/dashboard/.env.example +16 -0
  143. package/templates/dashboard/.github/workflows/ci.yml +34 -0
  144. package/templates/dashboard/.husky/pre-commit +3 -0
  145. package/templates/dashboard/.husky/pre-push +3 -0
  146. package/templates/dashboard/.prettierignore +3 -0
  147. package/templates/dashboard/drizzle.config.ts +16 -0
  148. package/templates/dashboard/eslint.config.mjs +127 -0
  149. package/templates/dashboard/next-env.d.ts +4 -0
  150. package/templates/dashboard/next.config.ts +5 -0
  151. package/templates/dashboard/package.json +75 -0
  152. package/templates/dashboard/playwright.config.ts +21 -0
  153. package/templates/dashboard/prettier.config.mjs +9 -0
  154. package/templates/dashboard/proxy.ts +36 -0
  155. package/templates/dashboard/src/app/api/auth/[...all]/route.ts +5 -0
  156. package/templates/dashboard/src/app/api/billing/checkout/route.ts +26 -0
  157. package/templates/dashboard/src/app/api/billing/portal/route.ts +25 -0
  158. package/templates/dashboard/src/app/api/email/test/route.ts +28 -0
  159. package/templates/dashboard/src/app/api/webhooks/polar/route.ts +5 -0
  160. package/templates/dashboard/src/app/api/webhooks/stripe/route.ts +5 -0
  161. package/templates/dashboard/src/app/billing/layout.tsx +22 -0
  162. package/templates/dashboard/src/app/billing/page.tsx +73 -0
  163. package/templates/dashboard/src/app/dashboard/layout.tsx +22 -0
  164. package/templates/dashboard/src/app/dashboard/page.tsx +104 -0
  165. package/templates/dashboard/src/app/email/layout.tsx +22 -0
  166. package/templates/dashboard/src/app/email/page.tsx +54 -0
  167. package/templates/dashboard/src/app/globals.css +1357 -0
  168. package/templates/dashboard/src/app/layout.tsx +25 -0
  169. package/templates/dashboard/src/app/page.tsx +154 -0
  170. package/templates/dashboard/src/app/settings/layout.tsx +22 -0
  171. package/templates/dashboard/src/app/settings/page.tsx +85 -0
  172. package/templates/dashboard/src/app/sign-in/page.tsx +47 -0
  173. package/templates/dashboard/src/app/sign-up/page.tsx +47 -0
  174. package/templates/dashboard/src/components/auth/email-auth-form.test.tsx +39 -0
  175. package/templates/dashboard/src/components/auth/email-auth-form.tsx +160 -0
  176. package/templates/dashboard/src/components/auth/sign-out-button.tsx +29 -0
  177. package/templates/dashboard/src/components/dashboard/shell.tsx +158 -0
  178. package/templates/dashboard/src/db/index.ts +16 -0
  179. package/templates/dashboard/src/db/schema/auth.ts +4 -0
  180. package/templates/dashboard/src/db/schema/index.ts +2 -0
  181. package/templates/dashboard/src/db/schema/projects.ts +17 -0
  182. package/templates/dashboard/src/db/seeds/index.ts +28 -0
  183. package/templates/dashboard/src/lib/auth-client.ts +5 -0
  184. package/templates/dashboard/src/lib/auth-session.ts +11 -0
  185. package/templates/dashboard/src/lib/auth.ts +41 -0
  186. package/templates/dashboard/src/lib/billing/index.ts +37 -0
  187. package/templates/dashboard/src/lib/billing/providers/polar.ts +80 -0
  188. package/templates/dashboard/src/lib/billing/providers/stripe.ts +77 -0
  189. package/templates/dashboard/src/lib/billing/types.ts +25 -0
  190. package/templates/dashboard/src/lib/email/index.ts +19 -0
  191. package/templates/dashboard/src/lib/email/templates.test.ts +15 -0
  192. package/templates/dashboard/src/lib/email/templates.ts +40 -0
  193. package/templates/dashboard/src/lib/env.ts +88 -0
  194. package/templates/dashboard/tsconfig.json +34 -0
  195. package/templates/dashboard/vitest.config.ts +19 -0
@@ -0,0 +1,127 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+
4
+ import { defineConfig, globalIgnores } from "eslint/config";
5
+ import prettier from "eslint-config-prettier/flat";
6
+ import nextTs from "eslint-config-next/typescript";
7
+ import nextVitals from "eslint-config-next/core-web-vitals";
8
+ import importPlugin from "eslint-plugin-import";
9
+ import unusedImports from "eslint-plugin-unused-imports";
10
+ import globals from "globals";
11
+
12
+ const SOURCE_FILES = ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"];
13
+ const dirname = path.dirname(fileURLToPath(import.meta.url));
14
+
15
+ export default defineConfig([
16
+ ...nextVitals,
17
+ ...nextTs,
18
+ {
19
+ files: SOURCE_FILES,
20
+ languageOptions: {
21
+ parserOptions: {
22
+ projectService: true,
23
+ tsconfigRootDir: dirname
24
+ },
25
+ globals: {
26
+ ...globals.browser,
27
+ ...globals.nodeBuiltin
28
+ }
29
+ },
30
+ plugins: {
31
+ import: importPlugin,
32
+ "unused-imports": unusedImports
33
+ },
34
+ settings: {
35
+ "import/resolver": {
36
+ typescript: true
37
+ }
38
+ },
39
+ rules: {
40
+ "@typescript-eslint/consistent-type-imports": [
41
+ "error",
42
+ {
43
+ prefer: "type-imports",
44
+ fixStyle: "inline-type-imports"
45
+ }
46
+ ],
47
+ "@typescript-eslint/no-floating-promises": "error",
48
+ "@typescript-eslint/no-misused-promises": [
49
+ "error",
50
+ {
51
+ checksVoidReturn: {
52
+ attributes: false
53
+ }
54
+ }
55
+ ],
56
+ "@typescript-eslint/no-unused-vars": "off",
57
+ "import/first": "error",
58
+ "import/newline-after-import": "error",
59
+ "import/no-duplicates": "error",
60
+ "import/order": [
61
+ "error",
62
+ {
63
+ groups: [
64
+ "builtin",
65
+ "external",
66
+ "internal",
67
+ ["parent", "sibling", "index"],
68
+ "object",
69
+ "type"
70
+ ],
71
+ alphabetize: {
72
+ order: "asc",
73
+ caseInsensitive: true
74
+ },
75
+ "newlines-between": "always",
76
+ pathGroups: [
77
+ {
78
+ pattern: "@/**",
79
+ group: "internal",
80
+ position: "before"
81
+ }
82
+ ],
83
+ pathGroupsExcludedImportTypes: ["builtin"]
84
+ }
85
+ ],
86
+ "unused-imports/no-unused-imports": "error",
87
+ "unused-imports/no-unused-vars": [
88
+ "warn",
89
+ {
90
+ vars: "all",
91
+ varsIgnorePattern: "^_",
92
+ args: "after-used",
93
+ argsIgnorePattern: "^_"
94
+ }
95
+ ]
96
+ }
97
+ },
98
+ {
99
+ files: SOURCE_FILES,
100
+ ignores: ["drizzle.config.ts", "src/lib/env.ts"],
101
+ rules: {
102
+ "no-restricted-properties": [
103
+ "error",
104
+ {
105
+ object: "process",
106
+ property: "env",
107
+ message: "Use the validated env object from src/lib/env.ts."
108
+ }
109
+ ]
110
+ }
111
+ },
112
+ prettier,
113
+ globalIgnores([
114
+ ".next/**",
115
+ "out/**",
116
+ "build/**",
117
+ "coverage/**",
118
+ "drizzle.config.ts",
119
+ "eslint.config.mjs",
120
+ "next.config.ts",
121
+ "playwright.config.ts",
122
+ "prettier.config.mjs",
123
+ "proxy.ts",
124
+ "next-env.d.ts",
125
+ "vitest.config.ts"
126
+ ])
127
+ ]);
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "base-web",
3
+ "sharedFileCount": 37,
4
+ "description": "Shared baseline extracted from identical files in blank and dashboard templates."
5
+ }
@@ -0,0 +1,4 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // This file is managed by Next.js.
@@ -0,0 +1,5 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {};
4
+
5
+ export default nextConfig;
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "__PROJECT_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "packageManager": "__PACKAGE_MANAGER__",
6
+ "type": "module",
7
+ "scripts": {
8
+ "dev": "next dev",
9
+ "build": "next build",
10
+ "start": "next start",
11
+ "lint": "eslint . --max-warnings=0",
12
+ "lint:fix": "eslint . --fix",
13
+ "typecheck": "tsc --noEmit",
14
+ "format": "prettier . --write",
15
+ "format:check": "prettier . --check",
16
+ "check": "__CHECK_COMMAND__",
17
+ "prepare": "husky",
18
+ "lint-staged": "lint-staged",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "test:e2e": "playwright test",
22
+ "test:e2e:headed": "playwright test --headed",
23
+ "db:generate": "drizzle-kit generate",
24
+ "db:migrate": "drizzle-kit migrate",
25
+ "db:push": "drizzle-kit push",
26
+ "db:studio": "drizzle-kit studio",
27
+ "db:seed": "tsx src/db/seeds/index.ts",
28
+ "auth:generate": "npx @better-auth/cli@latest generate --config src/lib/auth.ts --output src/db/schema/auth.ts",
29
+ "auth:secret": "npx @better-auth/cli@latest secret"
30
+ },
31
+ "dependencies": {
32
+ "better-auth": "1.3.8",
33
+ "drizzle-orm": "0.44.x",
34
+ "next": "16.2.x",
35
+ __DATABASE_DEPENDENCIES__
36
+ "react": "19.2.x",
37
+ "react-dom": "19.2.x",
38
+ __PROVIDER_PACKAGE_DEPENDENCIES__
39
+ "server-only": "^0.0.1",
40
+ "zod": "4.1.5"
41
+ },
42
+ "devDependencies": {
43
+ "@better-auth/cli": "1.3.8",
44
+ "@playwright/test": "1.55.0",
45
+ "@testing-library/react": "16.3.0",
46
+ "@types/node": "^24.0.0",
47
+ __DATABASE_DEV_DEPENDENCIES__
48
+ "@types/react": "^19.0.0",
49
+ "@types/react-dom": "^19.0.0",
50
+ "drizzle-kit": "0.31.x",
51
+ "eslint": "9.35.0",
52
+ "eslint-config-next": "16.2.x",
53
+ "eslint-config-prettier": "10.1.8",
54
+ "eslint-import-resolver-typescript": "^3.10.1",
55
+ "eslint-plugin-import": "2.32.0",
56
+ "eslint-plugin-unused-imports": "4.2.0",
57
+ "globals": "16.3.0",
58
+ "husky": "^9.1.7",
59
+ "jsdom": "26.1.0",
60
+ "lint-staged": "^16.1.6",
61
+ "playwright": "1.55.0",
62
+ "prettier": "3.6.2",
63
+ "tsx": "4.20.x",
64
+ "typescript": "5.9.x",
65
+ "vitest": "3.2.4"
66
+ },
67
+ "lint-staged": {
68
+ "*.{js,jsx,ts,tsx,mjs,cjs}": [
69
+ "eslint --fix"
70
+ ],
71
+ "*.{json,md,css,yml,yaml,html}": [
72
+ "prettier --write"
73
+ ]
74
+ }
75
+ }
@@ -0,0 +1,21 @@
1
+ import { defineConfig, devices } from "@playwright/test";
2
+
3
+ export default defineConfig({
4
+ testDir: "./tests/e2e",
5
+ fullyParallel: true,
6
+ use: {
7
+ baseURL: "http://127.0.0.1:3000",
8
+ trace: "on-first-retry"
9
+ },
10
+ webServer: {
11
+ command: "npm run dev",
12
+ url: "http://127.0.0.1:3000",
13
+ reuseExistingServer: !process.env.CI
14
+ },
15
+ projects: [
16
+ {
17
+ name: "chromium",
18
+ use: { ...devices["Desktop Chrome"] }
19
+ }
20
+ ]
21
+ });
@@ -0,0 +1,9 @@
1
+ /** @type {import("prettier").Config} */
2
+ const config = {
3
+ semi: true,
4
+ singleQuote: false,
5
+ trailingComma: "none",
6
+ printWidth: 88
7
+ };
8
+
9
+ export default config;
@@ -0,0 +1,23 @@
1
+ import { NextResponse } from "next/server";
2
+ import type { NextRequest } from "next/server";
3
+
4
+ import { getSessionCookie } from "better-auth/cookies";
5
+
6
+ export function proxy(request: NextRequest) {
7
+ const sessionCookie = getSessionCookie(request);
8
+ const { pathname } = request.nextUrl;
9
+
10
+ if (!sessionCookie && (__PROTECTED_ROUTE_CONDITION__)) {
11
+ return NextResponse.redirect(new URL("/sign-in", request.url));
12
+ }
13
+
14
+ if (sessionCookie && (pathname === "/sign-in" || pathname === "/sign-up")) {
15
+ return NextResponse.redirect(new URL("/dashboard", request.url));
16
+ }
17
+
18
+ return NextResponse.next();
19
+ }
20
+
21
+ export const config = {
22
+ matcher: [__PROTECTED_ROUTE_MATCHERS__, "/sign-in", "/sign-up"]
23
+ };
@@ -0,0 +1,5 @@
1
+ import { toNextJsHandler } from "better-auth/next-js";
2
+
3
+ import { auth } from "@/lib/auth";
4
+
5
+ export const { GET, POST } = toNextJsHandler(auth);
@@ -0,0 +1,26 @@
1
+ import { redirect } from "next/navigation";
2
+
3
+ import { getSession } from "@/lib/auth-session";
4
+ import { getBillingProvider, getBillingProviderName } from "@/lib/billing";
5
+ import { env } from "@/lib/env";
6
+
7
+ export async function POST(request: Request) {
8
+ const session = await getSession();
9
+
10
+ if (!session) {
11
+ redirect("/sign-in");
12
+ }
13
+
14
+ const formData = await request.formData();
15
+ const selected = String(formData.get("provider") ?? "");
16
+ const providerName = getBillingProviderName(selected);
17
+ const provider = getBillingProvider(providerName);
18
+ const checkout = await provider.createCheckoutSession({
19
+ userId: session.user.id,
20
+ customerEmail: session.user.email,
21
+ successUrl: `${env.NEXT_PUBLIC_APP_URL}/billing?success=1&provider=${providerName}`,
22
+ cancelUrl: `${env.NEXT_PUBLIC_APP_URL}/billing?canceled=1&provider=${providerName}`
23
+ });
24
+
25
+ redirect(checkout.url);
26
+ }
@@ -0,0 +1,25 @@
1
+ import { redirect } from "next/navigation";
2
+
3
+ import { getSession } from "@/lib/auth-session";
4
+ import { getBillingProvider, getBillingProviderName } from "@/lib/billing";
5
+ import { env } from "@/lib/env";
6
+
7
+ export async function POST(request: Request) {
8
+ const session = await getSession();
9
+
10
+ if (!session) {
11
+ redirect("/sign-in");
12
+ }
13
+
14
+ const formData = await request.formData();
15
+ const selected = String(formData.get("provider") ?? "");
16
+ const providerName = getBillingProviderName(selected);
17
+ const provider = getBillingProvider(providerName);
18
+ const portal = await provider.createCustomerPortalSession({
19
+ userId: session.user.id,
20
+ customerEmail: session.user.email,
21
+ returnUrl: `${env.NEXT_PUBLIC_APP_URL}/billing?provider=${providerName}`
22
+ });
23
+
24
+ redirect(portal.url);
25
+ }
@@ -0,0 +1,28 @@
1
+ import { redirect } from "next/navigation";
2
+
3
+ import { getSession } from "@/lib/auth-session";
4
+ import { sendEmail } from "@/lib/email";
5
+ import { createWelcomeEmailTemplate } from "@/lib/email/templates";
6
+ import { env } from "@/lib/env";
7
+
8
+ export async function POST() {
9
+ const session = await getSession();
10
+
11
+ if (!session) {
12
+ redirect("/sign-in");
13
+ }
14
+
15
+ if (env.EMAIL_PROVIDER === "none") {
16
+ redirect("/email");
17
+ }
18
+
19
+ await sendEmail({
20
+ to: session.user.email,
21
+ template: createWelcomeEmailTemplate({
22
+ appName: "Skit",
23
+ recipientName: session.user.name
24
+ })
25
+ });
26
+
27
+ redirect("/email?sent=1");
28
+ }
@@ -0,0 +1,5 @@
1
+ __POLAR_WEBHOOK_ROUTE_IMPORT__
2
+
3
+ export async function POST(__POLAR_WEBHOOK_ROUTE_SIGNATURE__) {
4
+ __POLAR_WEBHOOK_ROUTE_BODY__
5
+ }
@@ -0,0 +1,5 @@
1
+ __STRIPE_WEBHOOK_ROUTE_IMPORT__
2
+
3
+ export async function POST(__STRIPE_WEBHOOK_ROUTE_SIGNATURE__) {
4
+ __STRIPE_WEBHOOK_ROUTE_BODY__
5
+ }
@@ -0,0 +1,55 @@
1
+ __BILLING_REDIRECT_IMPORT__
2
+ import { getSession } from "@/lib/auth-session";
3
+ import { env } from "@/lib/env";
4
+
5
+ export default async function BillingPage({
6
+ searchParams
7
+ }: {
8
+ searchParams: Promise<Record<string, string | string[] | undefined>>;
9
+ }) {
10
+ const session = await getSession();
11
+
12
+ __BILLING_SESSION_SETUP__
13
+ __BILLING_AUTH_GUARD__
14
+
15
+ const params = await searchParams;
16
+ const success = params.success === "1";
17
+ const canceled = params.canceled === "1";
18
+ const providers =
19
+ env.BILLING_PROVIDER === "both"
20
+ ? ["stripe", "polar"]
21
+ : env.BILLING_PROVIDER === "none"
22
+ ? []
23
+ : [env.BILLING_PROVIDER];
24
+
25
+ return (
26
+ <main className="__AUTH_SCREEN_CLASS__">
27
+ <section className="__AUTH_PANEL_CLASS__">
28
+ <p className="eyebrow">Billing</p>
29
+ <h1>Choose your billing provider</h1>
30
+ <p className="lede">
31
+ The starter routes all billing actions through a provider abstraction. Current
32
+ default: <strong>{env.BILLING_PROVIDER}</strong>.
33
+ </p>
34
+
35
+ {success ? <p className="form-success">Checkout completed or redirected successfully.</p> : null}
36
+ {canceled ? <p className="form-error">Checkout was canceled.</p> : null}
37
+
38
+ __BILLING_DEMO_BANNER__
39
+
40
+ {providers.length === 0 ? (
41
+ <p className="form-error">
42
+ Billing is disabled for this generated app. Re-run the starter with a billing
43
+ provider to enable checkout flows.
44
+ </p>
45
+ ) : (
46
+ <div className="billing-grid">
47
+ {providers.map((provider) => (
48
+ __BILLING_PROVIDER_CARD__
49
+ ))}
50
+ </div>
51
+ )}
52
+ </section>
53
+ </main>
54
+ );
55
+ }
@@ -0,0 +1,15 @@
1
+ import Link from "next/link";
2
+ __DASHBOARD_REDIRECT_IMPORT__
3
+ import { SignOutButton } from "@/components/auth/sign-out-button";
4
+ import { getSession } from "@/lib/auth-session";
5
+
6
+ __DASHBOARD_PAGE_PRELUDE__
7
+
8
+ export default async function DashboardPage() {
9
+ const session = await getSession();
10
+
11
+ __DASHBOARD_SESSION_SETUP__
12
+ __DASHBOARD_AUTH_GUARD__
13
+
14
+ return __DASHBOARD_PAGE_CONTENT__;
15
+ }
@@ -0,0 +1,46 @@
1
+ import Link from "next/link";
2
+ __EMAIL_REDIRECT_IMPORT__
3
+ import { getSession } from "@/lib/auth-session";
4
+ import { env } from "@/lib/env";
5
+
6
+ export default async function EmailPage({
7
+ searchParams
8
+ }: {
9
+ searchParams: Promise<Record<string, string | string[] | undefined>>;
10
+ }) {
11
+ const session = await getSession();
12
+
13
+ __EMAIL_SESSION_SETUP__
14
+ __EMAIL_AUTH_GUARD__
15
+
16
+ const params = await searchParams;
17
+ const sent = params.sent === "1";
18
+ const emailDisabled = env.EMAIL_PROVIDER === "none";
19
+
20
+ return (
21
+ <main className="__AUTH_SCREEN_CLASS__">
22
+ <section className="__AUTH_PANEL_CLASS__">
23
+ <p className="eyebrow">Email</p>
24
+ <h1>Transactional email baseline</h1>
25
+ <p className="lede">
26
+ {emailDisabled
27
+ ? "Email provider is disabled for this generated app. Switch EMAIL_PROVIDER to resend when you want to wire transactional email."
28
+ : "The starter routes email sending through a single server-side Resend service. Use this page to verify the wiring and then connect real auth and billing emails."}
29
+ </p>
30
+
31
+ {sent ? <p className="form-success">Test email sent to {displayEmailRecipient}.</p> : null}
32
+
33
+ __EMAIL_DEMO_BANNER__
34
+
35
+ <div className="hero-actions">
36
+ <form action="/api/email/test" method="post">
37
+ <button type="submit" className="auth-button" disabled={emailDisabled || emailSendingDisabled}>
38
+ Send test email
39
+ </button>
40
+ </form>
41
+ <Link href="/dashboard">Back to dashboard</Link>
42
+ </div>
43
+ </section>
44
+ </main>
45
+ );
46
+ }
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import Link from "next/link";
4
+
5
+ export default function Error({
6
+ error,
7
+ reset
8
+ }: {
9
+ error: Error & { digest?: string };
10
+ reset: () => void;
11
+ }) {
12
+ return (
13
+ <main className="shell">
14
+ <section className="hero">
15
+ <p className="eyebrow">Error</p>
16
+ <h1>Something went wrong</h1>
17
+ <p className="lede">{error.message || "An unexpected error occurred."}</p>
18
+ <div className="hero-actions">
19
+ <button type="button" onClick={reset}>
20
+ Try again
21
+ </button>
22
+ <Link href="/">Go home</Link>
23
+ </div>
24
+ </section>
25
+ </main>
26
+ );
27
+ }