@superbuilds/create-supersaas-app 2.0.0 → 2.0.1
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/README.md +61 -86
- package/dist/index.js +150 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,138 +2,113 @@
|
|
|
2
2
|
|
|
3
3
|
> Scaffold production-ready SaaS apps in seconds
|
|
4
4
|
|
|
5
|
-
The fastest way to get your indie SaaS off the ground.
|
|
5
|
+
The fastest way to get your indie SaaS off the ground. One command, full source code, ready to ship.
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx create-supersaas-app my-saas
|
|
10
|
+
npx create-supersaas-app my-saas
|
|
11
11
|
cd my-saas
|
|
12
|
-
|
|
12
|
+
pnpm dev
|
|
13
|
+
# Open http://localhost:3000/setup to configure
|
|
13
14
|
```
|
|
14
15
|
|
|
15
16
|
## What You Get
|
|
16
17
|
|
|
17
|
-
- **Next.js 15** + React 19 + TypeScript
|
|
18
|
-
- **Authentication**
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **Teams**
|
|
23
|
-
- **Admin Dashboard**
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
18
|
+
- **Next.js 15** App Router + React 19 + TypeScript (strict mode)
|
|
19
|
+
- **Authentication** — Better Auth: email/password, magic links, 2FA, Google/GitHub/Twitter OAuth
|
|
20
|
+
- **4 Payment Providers** — Stripe, Polar, LemonSqueezy, DodoPayments (switch with one env var)
|
|
21
|
+
- **5 Email Providers** — Resend, Postmark, SendGrid, Mailgun, Nodemailer/SMTP
|
|
22
|
+
- **Database** — Drizzle ORM on PostgreSQL (Neon, Supabase, or local Docker)
|
|
23
|
+
- **Teams** — multi-tenant orgs with invites, owner/admin/member roles
|
|
24
|
+
- **Admin Dashboard** — user management, analytics, subscriptions, leads, feature flags
|
|
25
|
+
- **13 Themes** — complete UI vibes, switch with one line in globals.css
|
|
26
|
+
- **Blog + Changelog** — MDX with syntax highlighting, RSS, reading time
|
|
27
|
+
- **Rate Limiting** — Upstash Redis middleware
|
|
28
|
+
- **i18n** — locale detection, server + client translations
|
|
29
|
+
- **Setup Wizard** — browser-based `/setup` generates your .env in 2 minutes
|
|
30
|
+
- **AI-Ready** — ships with CLAUDE.md + AGENTS.md for Claude Code, Cursor, Copilot, Windsurf
|
|
26
31
|
|
|
27
32
|
## Usage
|
|
28
33
|
|
|
29
|
-
### Interactive Mode
|
|
34
|
+
### Interactive Mode
|
|
30
35
|
|
|
31
36
|
```bash
|
|
32
37
|
npx create-supersaas-app
|
|
33
38
|
```
|
|
34
39
|
|
|
35
40
|
You'll be prompted for:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
1. License key (from your purchase email)
|
|
42
|
+
2. App name
|
|
43
|
+
3. Database provider (Neon, Supabase, or local Docker Postgres)
|
|
44
|
+
4. Optional features (blog, admin dashboard)
|
|
45
|
+
5. Install dependencies (yes/no)
|
|
39
46
|
|
|
40
47
|
### Non-Interactive Mode
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
For CI/CD, automation, or AI agents:
|
|
43
50
|
|
|
44
51
|
```bash
|
|
45
52
|
npx create-supersaas-app my-saas \
|
|
46
53
|
--key YOUR_LICENSE_KEY \
|
|
47
54
|
--db neon \
|
|
48
|
-
--features blog,
|
|
55
|
+
--features blog,admin \
|
|
56
|
+
--install \
|
|
49
57
|
--yes
|
|
50
58
|
```
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
### Dev Mode
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
|------|-------------|---------|
|
|
56
|
-
| `--key` | License key (required) | `--key lic_abc123` |
|
|
57
|
-
| `--db` | Database provider | `--db neon` |
|
|
58
|
-
| `--features` | Comma-separated features | `--features blog,ai` |
|
|
59
|
-
| `--yes` | Skip prompts, use defaults | `--yes` |
|
|
62
|
+
For local development without a license:
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- Authentication (email + OAuth)
|
|
66
|
-
- Database with migrations
|
|
67
|
-
- Payment processing
|
|
68
|
-
- Email delivery
|
|
69
|
-
- Team management
|
|
70
|
-
- API routes
|
|
71
|
-
|
|
72
|
-
### Pro Tier
|
|
73
|
-
|
|
74
|
-
- AI routes (OpenAI/Anthropic)
|
|
75
|
-
- Admin dashboard
|
|
76
|
-
- Advanced themes
|
|
77
|
-
- Priority support
|
|
78
|
-
|
|
79
|
-
### Bundle Tier
|
|
64
|
+
```bash
|
|
65
|
+
npx create-supersaas-app my-saas --key dev
|
|
66
|
+
```
|
|
80
67
|
|
|
81
|
-
|
|
82
|
-
- Lifetime updates
|
|
83
|
-
- Team licenses
|
|
84
|
-
- Early access to new features
|
|
68
|
+
## CLI Options
|
|
85
69
|
|
|
86
|
-
|
|
70
|
+
| Flag | Description | Values |
|
|
71
|
+
|------|-------------|--------|
|
|
72
|
+
| `--key <key>` | License key | Your key from purchase email |
|
|
73
|
+
| `--db <provider>` | Database provider | `neon`, `supabase`, `local` |
|
|
74
|
+
| `--features <list>` | Optional features | `blog`, `admin` (comma-separated) |
|
|
75
|
+
| `--install` | Install dependencies after scaffolding | — |
|
|
76
|
+
| `--no-git` | Skip git repository initialization | — |
|
|
77
|
+
| `--yes` / `--defaults` | Skip prompts, use defaults | — |
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
- **Supabase** (Postgres) - Auth + DB + Storage
|
|
90
|
-
- **PlanetScale** (MySQL) - Serverless MySQL
|
|
91
|
-
- **SQLite** - Local development
|
|
79
|
+
## Deactivate License
|
|
92
80
|
|
|
93
|
-
|
|
81
|
+
Free up a machine activation slot:
|
|
94
82
|
|
|
95
83
|
```bash
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
# 1. Set up environment variables
|
|
99
|
-
cp .env.example .env.local
|
|
100
|
-
# Edit .env.local with your keys
|
|
101
|
-
|
|
102
|
-
# 2. Run migrations
|
|
103
|
-
npm run db:push
|
|
104
|
-
|
|
105
|
-
# 3. Start dev server
|
|
106
|
-
npm run dev
|
|
84
|
+
npx create-supersaas-app deactivate
|
|
107
85
|
```
|
|
108
86
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
## CLI Commands (built-in)
|
|
112
|
-
|
|
113
|
-
Once your app is created, use the built-in CLI from your project directory:
|
|
87
|
+
## After Scaffolding
|
|
114
88
|
|
|
115
89
|
```bash
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
90
|
+
cd my-saas
|
|
91
|
+
pnpm dev # deps already installed if you said yes during setup
|
|
92
|
+
# Open http://localhost:3000/setup — the setup wizard walks you through everything
|
|
119
93
|
```
|
|
120
94
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
95
|
+
The setup wizard configures:
|
|
96
|
+
- Database connection
|
|
97
|
+
- Auth secrets
|
|
98
|
+
- Payment provider
|
|
99
|
+
- Email provider
|
|
100
|
+
- App identity
|
|
124
101
|
|
|
125
|
-
|
|
102
|
+
After setup, manage runtime config at `/admin/settings`.
|
|
126
103
|
|
|
127
|
-
|
|
128
|
-
- **Issues:** https://github.com/bixblues/supersaas-kit/issues
|
|
129
|
-
- **Email:** hi@superbuilds.fun
|
|
104
|
+
## License
|
|
130
105
|
|
|
131
|
-
|
|
106
|
+
Requires a valid SuperSaaS Kit license. One purchase ($129), unlimited projects, 3 machine activations.
|
|
132
107
|
|
|
133
|
-
|
|
134
|
-
2. Join Discord: https://supersaas-kit.superbuilds.fun/discord
|
|
135
|
-
3. Follow updates: https://x.com/superbuilds
|
|
108
|
+
Get yours at [supersaas-kit.superbuilds.fun](https://supersaas-kit.superbuilds.fun)
|
|
136
109
|
|
|
137
|
-
|
|
110
|
+
## Support
|
|
138
111
|
|
|
139
|
-
|
|
112
|
+
- **Docs:** [supersaas-kit.superbuilds.fun/docs](https://supersaas-kit.superbuilds.fun/docs)
|
|
113
|
+
- **Discord:** [discord.gg/bM3w3RZYYH](https://discord.gg/bM3w3RZYYH)
|
|
114
|
+
- **Email:** hi@superbuilds.fun
|
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ async function downloadTemplate(licenseKey, targetDir) {
|
|
|
47
47
|
const msg = errorBody.error || `HTTP ${res.status}`;
|
|
48
48
|
p3.log.error(chalk.red(msg));
|
|
49
49
|
if (res.status === 403) {
|
|
50
|
-
p3.log.info(chalk.dim("Your license may
|
|
50
|
+
p3.log.info(chalk.dim("Your license may be invalid. Check https://supersaas-kit.superbuilds.fun"));
|
|
51
51
|
}
|
|
52
52
|
if (res.status === 429) {
|
|
53
53
|
p3.log.info(chalk.dim("Too many downloads. Wait an hour and try again."));
|
|
@@ -61,9 +61,22 @@ async function downloadTemplate(licenseKey, targetDir) {
|
|
|
61
61
|
await fs.writeFile(archivePath, Buffer.from(arrayBuffer));
|
|
62
62
|
spinner.text = "Extracting template...";
|
|
63
63
|
await fs.ensureDir(targetDir);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
try {
|
|
65
|
+
execSync(`tar -xzf "${archivePath}" -C "${targetDir}"`, {
|
|
66
|
+
stdio: "pipe"
|
|
67
|
+
});
|
|
68
|
+
} catch (tarErr) {
|
|
69
|
+
spinner.fail(chalk.red("Extraction failed"));
|
|
70
|
+
const isWindows = process.platform === "win32";
|
|
71
|
+
if (isWindows) {
|
|
72
|
+
p3.log.error(chalk.red("tar is not available on this system."));
|
|
73
|
+
p3.log.info(chalk.dim("Install tar via Windows Build Tools, or use WSL."));
|
|
74
|
+
} else {
|
|
75
|
+
p3.log.error(chalk.red(`tar extraction failed: ${tarErr.message}`));
|
|
76
|
+
}
|
|
77
|
+
await fs.remove(tmpDir);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
67
80
|
await fs.remove(tmpDir);
|
|
68
81
|
spinner.succeed(chalk.green("Template downloaded and extracted"));
|
|
69
82
|
return true;
|
|
@@ -86,6 +99,9 @@ async function createApp(config) {
|
|
|
86
99
|
}
|
|
87
100
|
const downloaded = await downloadTemplate(config.licenseKey, targetDir);
|
|
88
101
|
if (!downloaded) {
|
|
102
|
+
if (await fs.pathExists(targetDir)) {
|
|
103
|
+
await fs.remove(targetDir);
|
|
104
|
+
}
|
|
89
105
|
process.exit(1);
|
|
90
106
|
}
|
|
91
107
|
const excludePaths = /* @__PURE__ */ new Set();
|
|
@@ -112,11 +128,49 @@ async function createApp(config) {
|
|
|
112
128
|
const envExample = path.join(targetDir, ".env.example");
|
|
113
129
|
if (await fs.pathExists(envExample)) {
|
|
114
130
|
await fs.copy(envExample, path.join(targetDir, ".env"));
|
|
131
|
+
p3.log.info(chalk.dim("Created .env from template \u2014 configure via /setup after first run"));
|
|
115
132
|
}
|
|
116
133
|
await cleanupFeatureImports(targetDir, config);
|
|
117
134
|
const activeFeatures = ["auth", "payments", "email", "seo", ...config.features];
|
|
118
135
|
p3.log.info(chalk.dim(`Database: ${config.database}`));
|
|
119
136
|
p3.log.info(chalk.dim(`Features: ${activeFeatures.join(", ")}`));
|
|
137
|
+
if (config.install) {
|
|
138
|
+
const pm = detectPackageManager();
|
|
139
|
+
const spinner = ora(`Installing dependencies with ${pm}...`).start();
|
|
140
|
+
try {
|
|
141
|
+
execSync(`${pm} install`, { cwd: targetDir, stdio: "pipe" });
|
|
142
|
+
spinner.succeed(chalk.green("Dependencies installed"));
|
|
143
|
+
} catch {
|
|
144
|
+
spinner.fail(chalk.yellow("Install failed \u2014 run it manually after setup"));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!config.skipGit && hasGit()) {
|
|
148
|
+
try {
|
|
149
|
+
execSync("git init", { cwd: targetDir, stdio: "pipe" });
|
|
150
|
+
execSync("git add -A", { cwd: targetDir, stdio: "pipe" });
|
|
151
|
+
execSync('git commit -m "Initial commit from create-supersaas-app"', {
|
|
152
|
+
cwd: targetDir,
|
|
153
|
+
stdio: "pipe"
|
|
154
|
+
});
|
|
155
|
+
p3.log.info(chalk.dim("Initialized git repository"));
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function detectPackageManager() {
|
|
161
|
+
const userAgent = process.env.npm_config_user_agent || "";
|
|
162
|
+
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
163
|
+
if (userAgent.startsWith("yarn")) return "yarn";
|
|
164
|
+
if (userAgent.startsWith("bun")) return "bun";
|
|
165
|
+
return "pnpm";
|
|
166
|
+
}
|
|
167
|
+
function hasGit() {
|
|
168
|
+
try {
|
|
169
|
+
execSync("git --version", { stdio: "pipe" });
|
|
170
|
+
return true;
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
120
174
|
}
|
|
121
175
|
async function configureDatabase(targetDir, config) {
|
|
122
176
|
const dbConfig = DATABASE_CONFIGS[config.database];
|
|
@@ -131,14 +185,6 @@ DATABASE_URL=`);
|
|
|
131
185
|
await fs.writeFile(envPath, content);
|
|
132
186
|
}
|
|
133
187
|
}
|
|
134
|
-
if (config.database === "local") {
|
|
135
|
-
const envPath2 = path.join(targetDir, ".env.example");
|
|
136
|
-
if (await fs.pathExists(envPath2)) {
|
|
137
|
-
let content = await fs.readFile(envPath2, "utf-8");
|
|
138
|
-
if (content.includes("DATABASE_URL=postgres://postgres:postgres@localhost:5432/app")) ;
|
|
139
|
-
await fs.writeFile(envPath2, content);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
188
|
}
|
|
143
189
|
async function updateManifest(targetDir, config) {
|
|
144
190
|
const manifestPath = path.join(targetDir, ".supersaas", "manifest.json");
|
|
@@ -146,7 +192,6 @@ async function updateManifest(targetDir, config) {
|
|
|
146
192
|
const manifest = await fs.readJson(manifestPath);
|
|
147
193
|
manifest.database = config.database;
|
|
148
194
|
manifest.features = config.features;
|
|
149
|
-
manifest.tier = config.tier;
|
|
150
195
|
manifest.createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
151
196
|
if (manifest.providers) {
|
|
152
197
|
if (manifest.providers.db) manifest.providers.db = "postgres";
|
|
@@ -184,7 +229,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
|
|
184
229
|
const sidebarPath = path.join(targetDir, "src/components/layout/sidebar.tsx");
|
|
185
230
|
if (await fs.pathExists(sidebarPath)) {
|
|
186
231
|
let content = await fs.readFile(sidebarPath, "utf-8");
|
|
187
|
-
content = content.replace(/\s*\{\s*
|
|
232
|
+
content = content.replace(/\s*\{\s*name:\s*["']Admin["'].*?\},?\n?/gs, "");
|
|
188
233
|
await fs.writeFile(sidebarPath, content);
|
|
189
234
|
}
|
|
190
235
|
}
|
|
@@ -237,14 +282,11 @@ async function getAllFiles(dir) {
|
|
|
237
282
|
var SUPERSAAS_DIR = path.join(os2.homedir(), ".supersaas");
|
|
238
283
|
var LICENSE_PATH = path.join(SUPERSAAS_DIR, "license.json");
|
|
239
284
|
var DODO_BASE_URL = process.env.SUPERSAAS_DODO_URL || "https://test.dodopayments.com";
|
|
240
|
-
function inferTierFromActivation(_data) {
|
|
241
|
-
return "bundle";
|
|
242
|
-
}
|
|
243
285
|
async function loadLocalLicense() {
|
|
244
286
|
try {
|
|
245
287
|
if (await fs.pathExists(LICENSE_PATH)) {
|
|
246
288
|
const data = await fs.readJson(LICENSE_PATH);
|
|
247
|
-
if (data.key && data.
|
|
289
|
+
if (data.key && data.instance_id) {
|
|
248
290
|
return data;
|
|
249
291
|
}
|
|
250
292
|
}
|
|
@@ -291,10 +333,8 @@ async function activateOnline(key) {
|
|
|
291
333
|
return null;
|
|
292
334
|
}
|
|
293
335
|
const result = await res.json();
|
|
294
|
-
const tier = inferTierFromActivation(result);
|
|
295
336
|
const license = {
|
|
296
337
|
key,
|
|
297
|
-
tier,
|
|
298
338
|
instance_id: result.id || result.license_key_instance_id || "",
|
|
299
339
|
activated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
300
340
|
valid: true
|
|
@@ -315,14 +355,14 @@ async function activate(keyArg, nonInteractive) {
|
|
|
315
355
|
const check = await validateKey(existing.key).catch(() => ({ valid: true }));
|
|
316
356
|
if (check.valid) {
|
|
317
357
|
p3.log.success(
|
|
318
|
-
chalk.dim(
|
|
319
|
-
`Licensed (${existing.tier}) - ${existing.key.slice(0, 12)}...`
|
|
320
|
-
)
|
|
358
|
+
chalk.dim(`Licensed \u2014 ${existing.key.slice(0, 12)}...`)
|
|
321
359
|
);
|
|
322
360
|
return existing;
|
|
323
361
|
}
|
|
324
362
|
await fs.remove(LICENSE_PATH);
|
|
325
|
-
|
|
363
|
+
if (!nonInteractive) {
|
|
364
|
+
p3.log.warning(chalk.yellow("License expired or revoked. Please re-activate."));
|
|
365
|
+
}
|
|
326
366
|
}
|
|
327
367
|
if (!keyArg && nonInteractive) {
|
|
328
368
|
return null;
|
|
@@ -339,18 +379,51 @@ async function activate(keyArg, nonInteractive) {
|
|
|
339
379
|
p3.log.step("Activating license...");
|
|
340
380
|
const license = await activateOnline(key);
|
|
341
381
|
if (license) {
|
|
342
|
-
p3.log.success(chalk.green(
|
|
382
|
+
p3.log.success(chalk.green("License activated!"));
|
|
343
383
|
return license;
|
|
344
384
|
}
|
|
345
385
|
return null;
|
|
346
386
|
}
|
|
387
|
+
async function deactivate() {
|
|
388
|
+
const existing = await loadLocalLicense();
|
|
389
|
+
if (!existing) {
|
|
390
|
+
p3.log.warning(chalk.yellow("No active license found on this machine."));
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
try {
|
|
394
|
+
const res = await fetch(`${DODO_BASE_URL}/licenses/deactivate`, {
|
|
395
|
+
method: "POST",
|
|
396
|
+
headers: { "Content-Type": "application/json" },
|
|
397
|
+
body: JSON.stringify({
|
|
398
|
+
license_key: existing.key,
|
|
399
|
+
license_key_instance_id: existing.instance_id
|
|
400
|
+
})
|
|
401
|
+
});
|
|
402
|
+
if (res.ok) {
|
|
403
|
+
await fs.remove(LICENSE_PATH);
|
|
404
|
+
p3.log.success(chalk.green("License deactivated. Machine slot freed."));
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
const body = await res.json().catch(() => ({}));
|
|
408
|
+
p3.log.error(chalk.red(`Deactivation failed: ${body.message || "Unknown error"}`));
|
|
409
|
+
return false;
|
|
410
|
+
} catch {
|
|
411
|
+
p3.log.error(chalk.red("Could not reach license server."));
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
347
415
|
var require2 = createRequire(import.meta.url);
|
|
348
416
|
var { version } = require2("../package.json");
|
|
349
417
|
var VALID_DBS = ["neon", "supabase", "local"];
|
|
350
418
|
var VALID_FEATURES = ["blog", "admin"];
|
|
351
|
-
program.name("create-supersaas-app").description("Scaffold production-ready SaaS apps in seconds").version(version).argument("[name]", "Name of your app").option("--key <license-key>", "License key for activation").option("--defaults", "Skip prompts, use default settings (for agents)").option("--yes", "Alias for --defaults (non-interactive mode)").option("--db <provider>", "Database provider (neon|supabase|local)").option("--features <list>", "Comma-separated features (blog,admin)").action(async (name, options) => {
|
|
419
|
+
program.name("create-supersaas-app").description("Scaffold production-ready SaaS apps in seconds").version(version).argument("[name]", "Name of your app").option("--key <license-key>", "License key for activation").option("--defaults", "Skip prompts, use default settings (for agents)").option("--yes", "Alias for --defaults (non-interactive mode)").option("--db <provider>", "Database provider (neon|supabase|local)").option("--features <list>", "Comma-separated features (blog,admin)").option("--install", "Install dependencies after scaffolding").option("--no-git", "Skip git repository initialization").action(async (name, options) => {
|
|
352
420
|
const nonInteractive = options.defaults || options.yes || !process.stdin.isTTY;
|
|
353
|
-
if (!nonInteractive)
|
|
421
|
+
if (!nonInteractive) {
|
|
422
|
+
p3.intro(
|
|
423
|
+
`${chalk.bold("supersaas-kit")} ${chalk.dim(`v${version}`)}
|
|
424
|
+
` + chalk.dim(" Production-ready SaaS in one command")
|
|
425
|
+
);
|
|
426
|
+
}
|
|
354
427
|
let licenseResult = await activate(options.key, nonInteractive);
|
|
355
428
|
if (!licenseResult) {
|
|
356
429
|
if (!nonInteractive) p3.cancel("License activation failed.");
|
|
@@ -418,8 +491,8 @@ program.name("create-supersaas-app").description("Scaffold production-ready SaaS
|
|
|
418
491
|
features = await p3.multiselect({
|
|
419
492
|
message: "Include optional modules?",
|
|
420
493
|
options: [
|
|
421
|
-
{ value: "blog", label: "Blog (MDX)", hint: "SEO
|
|
422
|
-
{ value: "admin", label: "Admin Dashboard", hint: "
|
|
494
|
+
{ value: "blog", label: "Blog (MDX)", hint: "SEO-ready with RSS feed" },
|
|
495
|
+
{ value: "admin", label: "Admin Dashboard", hint: "user management, analytics" }
|
|
423
496
|
],
|
|
424
497
|
required: false
|
|
425
498
|
});
|
|
@@ -428,19 +501,62 @@ program.name("create-supersaas-app").description("Scaffold production-ready SaaS
|
|
|
428
501
|
process.exit(0);
|
|
429
502
|
}
|
|
430
503
|
}
|
|
504
|
+
let shouldInstall = options.install || false;
|
|
505
|
+
if (!nonInteractive && !options.install) {
|
|
506
|
+
shouldInstall = await p3.confirm({
|
|
507
|
+
message: "Install dependencies?",
|
|
508
|
+
initialValue: true
|
|
509
|
+
});
|
|
510
|
+
if (p3.isCancel(shouldInstall)) {
|
|
511
|
+
p3.cancel("Cancelled.");
|
|
512
|
+
process.exit(0);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
431
515
|
await createApp({
|
|
432
516
|
name: appName,
|
|
433
517
|
database: dbChoice,
|
|
434
518
|
features: features || [],
|
|
435
|
-
|
|
436
|
-
|
|
519
|
+
licenseKey: licenseResult.key,
|
|
520
|
+
install: shouldInstall,
|
|
521
|
+
skipGit: options.git === false
|
|
437
522
|
});
|
|
438
523
|
if (!nonInteractive) {
|
|
439
|
-
|
|
440
|
-
chalk.green("
|
|
524
|
+
const steps = [
|
|
525
|
+
chalk.green("Your app is ready!"),
|
|
526
|
+
"",
|
|
527
|
+
` ${chalk.cyan("cd")} ${appName}`
|
|
528
|
+
];
|
|
529
|
+
if (!shouldInstall) {
|
|
530
|
+
steps.push(` ${chalk.cyan("pnpm install")}`);
|
|
531
|
+
}
|
|
532
|
+
steps.push(
|
|
533
|
+
` ${chalk.cyan("pnpm dev")}`,
|
|
534
|
+
"",
|
|
535
|
+
chalk.dim(" Then visit /setup to configure your services."),
|
|
536
|
+
chalk.dim(" Docs: https://supersaas-kit.superbuilds.fun/docs")
|
|
441
537
|
);
|
|
538
|
+
p3.outro(steps.join("\n"));
|
|
442
539
|
} else {
|
|
443
|
-
|
|
540
|
+
const installCmd = shouldInstall ? "" : "pnpm install && ";
|
|
541
|
+
console.log(`\u2714 Created ${appName}. Run: cd ${appName} && ${installCmd}pnpm dev`);
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
program.command("deactivate").description("Deactivate your license on this machine (frees a machine slot)").action(async () => {
|
|
545
|
+
p3.intro(chalk.bold("supersaas-kit \u2014 deactivate"));
|
|
546
|
+
const confirmed = await p3.confirm({
|
|
547
|
+
message: "This will free one of your 3 machine activation slots. Continue?",
|
|
548
|
+
initialValue: false
|
|
549
|
+
});
|
|
550
|
+
if (p3.isCancel(confirmed) || !confirmed) {
|
|
551
|
+
p3.outro(chalk.dim("Deactivation cancelled."));
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const success = await deactivate();
|
|
555
|
+
if (success) {
|
|
556
|
+
p3.outro(chalk.green("License deactivated. You can activate on another machine now."));
|
|
557
|
+
} else {
|
|
558
|
+
p3.outro(chalk.red("Deactivation failed. Run again or contact hi@superbuilds.fun"));
|
|
559
|
+
process.exit(1);
|
|
444
560
|
}
|
|
445
561
|
});
|
|
446
562
|
program.parse();
|