create-githat-app 1.0.4 → 1.0.5
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 +21 -43
- package/package.json +1 -1
- package/templates/fullstack/apps-web-nextjs/app/(auth)/reset-password/page.tsx.hbs +30 -2
- package/templates/fullstack/apps-web-nextjs/app/dashboard/layout.tsx.hbs +10 -4
- package/templates/fullstack/apps-web-nextjs/app/dashboard/page.tsx.hbs +2 -2
- package/templates/fullstack/root/package.json.hbs +1 -1
- package/templates/nextjs/app/(auth)/reset-password/page.tsx.hbs +10 -2
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import gradient from "gradient-string";
|
|
|
11
11
|
import chalk from "chalk";
|
|
12
12
|
|
|
13
13
|
// src/constants.ts
|
|
14
|
-
var VERSION = "1.0.
|
|
14
|
+
var VERSION = "1.0.5";
|
|
15
15
|
var DEFAULT_API_URL = "https://api.githat.io";
|
|
16
16
|
var DASHBOARD_URL = "https://githat.io/dashboard/apps";
|
|
17
17
|
var BRAND_COLORS = ["#7c3aed", "#6366f1", "#8b5cf6"];
|
|
@@ -397,45 +397,22 @@ async function promptBackend() {
|
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
// src/prompts/githat.ts
|
|
400
|
-
import { execSync } from "child_process";
|
|
401
400
|
import * as p5 from "@clack/prompts";
|
|
402
|
-
function openBrowser(url) {
|
|
403
|
-
try {
|
|
404
|
-
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
405
|
-
execSync(`${cmd} "${url}"`, { stdio: "ignore" });
|
|
406
|
-
} catch {
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
401
|
async function promptGitHat(existingKey) {
|
|
410
402
|
let publishableKey = existingKey || "";
|
|
411
403
|
if (!publishableKey) {
|
|
412
404
|
const connectChoice = await p5.select({
|
|
413
405
|
message: "Connect to GitHat",
|
|
414
406
|
options: [
|
|
415
|
-
{ value: "
|
|
416
|
-
{ value: "paste", label: "I have a key", hint: "paste your pk_live_... key" }
|
|
417
|
-
{ value: "skip", label: "Skip for now", hint: "add key to .env later" }
|
|
407
|
+
{ value: "skip", label: "Skip for now", hint: "auth works on localhost \u2014 add key later" },
|
|
408
|
+
{ value: "paste", label: "I have a key", hint: "paste your pk_live_... key" }
|
|
418
409
|
]
|
|
419
410
|
});
|
|
420
411
|
if (p5.isCancel(connectChoice)) {
|
|
421
412
|
p5.cancel("Setup cancelled.");
|
|
422
413
|
process.exit(0);
|
|
423
414
|
}
|
|
424
|
-
if (connectChoice === "
|
|
425
|
-
p5.log.step("Opening githat.io in your browser...");
|
|
426
|
-
openBrowser("https://githat.io/sign-up");
|
|
427
|
-
p5.log.info("Sign up (or sign in), then go to Dashboard \u2192 Apps to copy your key.");
|
|
428
|
-
const pastedKey = await p5.text({
|
|
429
|
-
message: "Paste your publishable key",
|
|
430
|
-
placeholder: "pk_live_...",
|
|
431
|
-
validate: validatePublishableKey
|
|
432
|
-
});
|
|
433
|
-
if (p5.isCancel(pastedKey)) {
|
|
434
|
-
p5.cancel("Setup cancelled.");
|
|
435
|
-
process.exit(0);
|
|
436
|
-
}
|
|
437
|
-
publishableKey = pastedKey || "";
|
|
438
|
-
} else if (connectChoice === "paste") {
|
|
415
|
+
if (connectChoice === "paste") {
|
|
439
416
|
const pastedKey = await p5.text({
|
|
440
417
|
message: "Publishable key",
|
|
441
418
|
placeholder: `pk_live_... (get one at ${DASHBOARD_URL})`,
|
|
@@ -446,9 +423,6 @@ async function promptGitHat(existingKey) {
|
|
|
446
423
|
process.exit(0);
|
|
447
424
|
}
|
|
448
425
|
publishableKey = pastedKey || "";
|
|
449
|
-
} else if (connectChoice === "skip") {
|
|
450
|
-
p5.log.info("Auth works on localhost without a key (CORS bypass for development).");
|
|
451
|
-
p5.log.info("Sign up at githat.io \u2014 a publishable key is auto-created for you.");
|
|
452
426
|
}
|
|
453
427
|
}
|
|
454
428
|
const authFeatures = await p5.multiselect({
|
|
@@ -528,13 +502,15 @@ async function promptFinalize() {
|
|
|
528
502
|
function toDisplayName(projectName) {
|
|
529
503
|
return projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
530
504
|
}
|
|
531
|
-
function getDefaults(projectName, publishableKey, typescript) {
|
|
505
|
+
function getDefaults(projectName, publishableKey, typescript, fullstack, backendFramework) {
|
|
532
506
|
const displayName = toDisplayName(projectName);
|
|
507
|
+
const projectType = fullstack ? "fullstack" : "frontend";
|
|
533
508
|
return {
|
|
534
509
|
projectName,
|
|
535
510
|
businessName: displayName,
|
|
536
511
|
description: `${displayName} \u2014 Built with GitHat`,
|
|
537
|
-
projectType
|
|
512
|
+
projectType,
|
|
513
|
+
backendFramework: fullstack ? backendFramework || "hono" : void 0,
|
|
538
514
|
framework: "nextjs",
|
|
539
515
|
typescript: typescript ?? true,
|
|
540
516
|
packageManager: detectPackageManager(),
|
|
@@ -544,7 +520,7 @@ function getDefaults(projectName, publishableKey, typescript) {
|
|
|
544
520
|
databaseChoice: "none",
|
|
545
521
|
useTailwind: true,
|
|
546
522
|
includeDashboard: true,
|
|
547
|
-
includeGithatFolder:
|
|
523
|
+
includeGithatFolder: projectType === "frontend",
|
|
548
524
|
initGit: true,
|
|
549
525
|
installDeps: true
|
|
550
526
|
};
|
|
@@ -552,7 +528,7 @@ function getDefaults(projectName, publishableKey, typescript) {
|
|
|
552
528
|
async function runPrompts(args) {
|
|
553
529
|
if (args.yes && args.initialName) {
|
|
554
530
|
p8.log.info("Using defaults (--yes flag)");
|
|
555
|
-
return getDefaults(args.initialName, args.publishableKey, args.typescript);
|
|
531
|
+
return getDefaults(args.initialName, args.publishableKey, args.typescript, args.fullstack, args.backendFramework);
|
|
556
532
|
}
|
|
557
533
|
p8.intro("Let's set up your GitHat app");
|
|
558
534
|
sectionHeader("Project");
|
|
@@ -603,7 +579,7 @@ function answersToContext(answers) {
|
|
|
603
579
|
// src/scaffold/index.ts
|
|
604
580
|
import fs3 from "fs-extra";
|
|
605
581
|
import path3 from "path";
|
|
606
|
-
import { execSync as
|
|
582
|
+
import { execSync as execSync2 } from "child_process";
|
|
607
583
|
import * as p9 from "@clack/prompts";
|
|
608
584
|
import chalk2 from "chalk";
|
|
609
585
|
|
|
@@ -738,12 +714,12 @@ async function withSpinner(text4, fn, successText) {
|
|
|
738
714
|
}
|
|
739
715
|
|
|
740
716
|
// src/utils/git.ts
|
|
741
|
-
import { execSync
|
|
717
|
+
import { execSync } from "child_process";
|
|
742
718
|
function initGit(cwd) {
|
|
743
719
|
try {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
720
|
+
execSync("git init", { cwd, stdio: "ignore" });
|
|
721
|
+
execSync("git add -A", { cwd, stdio: "ignore" });
|
|
722
|
+
execSync('git commit -m "Initial commit from create-githat-app"', {
|
|
747
723
|
cwd,
|
|
748
724
|
stdio: "ignore"
|
|
749
725
|
});
|
|
@@ -800,7 +776,7 @@ async function scaffold(context, options) {
|
|
|
800
776
|
`Installing dependencies with ${context.packageManager}...`,
|
|
801
777
|
async () => {
|
|
802
778
|
try {
|
|
803
|
-
|
|
779
|
+
execSync2(installCmd, { cwd: root, stdio: "ignore", timeout: 12e4 });
|
|
804
780
|
} catch (err) {
|
|
805
781
|
const msg = err.message || "";
|
|
806
782
|
if (msg.includes("TIMEOUT")) {
|
|
@@ -823,7 +799,7 @@ async function scaffold(context, options) {
|
|
|
823
799
|
if (!p9.isCancel(starPrompt) && starPrompt) {
|
|
824
800
|
try {
|
|
825
801
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
826
|
-
|
|
802
|
+
execSync2(`${cmd} "https://github.com/GitHat-IO/githat"`, { stdio: "ignore" });
|
|
827
803
|
} catch {
|
|
828
804
|
p9.log.info("Visit https://github.com/GitHat-IO/githat to star us!");
|
|
829
805
|
}
|
|
@@ -1436,7 +1412,7 @@ skillsCommand.action(() => {
|
|
|
1436
1412
|
// src/cli.ts
|
|
1437
1413
|
var program = new Command7();
|
|
1438
1414
|
program.name("githat").description("GitHat CLI - Scaffold apps and manage skills").version(VERSION);
|
|
1439
|
-
program.command("create [project-name]", { isDefault: true }).description("Scaffold a new GitHat app").option("--key <key>", "GitHat publishable key (pk_live_...)").option("--ts", "Use TypeScript (default)").option("--js", "Use JavaScript").option("-y, --yes", "Skip prompts and use defaults").action(async (projectName, opts) => {
|
|
1415
|
+
program.command("create [project-name]", { isDefault: true }).description("Scaffold a new GitHat app").option("--key <key>", "GitHat publishable key (pk_live_...)").option("--ts", "Use TypeScript (default)").option("--js", "Use JavaScript").option("--fullstack", "Create fullstack project (Turborepo)").option("--backend <framework>", "Backend framework (hono, express, fastify)").option("-y, --yes", "Skip prompts and use defaults").action(async (projectName, opts) => {
|
|
1440
1416
|
try {
|
|
1441
1417
|
displayBanner();
|
|
1442
1418
|
const typescript = opts.js ? false : opts.ts ? true : void 0;
|
|
@@ -1448,7 +1424,9 @@ program.command("create [project-name]", { isDefault: true }).description("Scaff
|
|
|
1448
1424
|
initialName: projectName,
|
|
1449
1425
|
publishableKey: opts.key,
|
|
1450
1426
|
typescript,
|
|
1451
|
-
yes: opts.yes
|
|
1427
|
+
yes: opts.yes,
|
|
1428
|
+
fullstack: opts.fullstack,
|
|
1429
|
+
backendFramework: opts.backend
|
|
1452
1430
|
});
|
|
1453
1431
|
const context = answersToContext(answers);
|
|
1454
1432
|
await scaffold(context, {
|
package/package.json
CHANGED
|
@@ -1,11 +1,39 @@
|
|
|
1
1
|
{{#if includeForgotPassword}}
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import { Suspense } from 'react';
|
|
5
|
+
import { useSearchParams } from 'next/navigation';
|
|
2
6
|
import { ResetPasswordForm } from '@githat/nextjs';
|
|
3
7
|
|
|
4
|
-
|
|
8
|
+
function ResetPasswordContent() {
|
|
9
|
+
const searchParams = useSearchParams();
|
|
10
|
+
const token = searchParams.get('token');
|
|
11
|
+
|
|
12
|
+
if (!token) {
|
|
13
|
+
return (
|
|
14
|
+
<main {{#if useTailwind}}className="flex items-center justify-center min-h-screen bg-[#09090b]"{{else}}style=\{{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', background: '#09090b' }}{{/if}}>
|
|
15
|
+
<div {{#if useTailwind}}className="text-center"{{else}}style=\{{ textAlign: 'center' }}{{/if}}>
|
|
16
|
+
<h1 {{#if useTailwind}}className="text-xl font-semibold text-white mb-2"{{else}}style=\{{ fontSize: '1.25rem', fontWeight: 600, color: '#fafafa', marginBottom: '0.5rem' }}{{/if}}>Invalid reset link</h1>
|
|
17
|
+
<p {{#if useTailwind}}className="text-zinc-400"{{else}}style=\{{ color: '#a1a1aa' }}{{/if}}>
|
|
18
|
+
<a href="/forgot-password" {{#if useTailwind}}className="text-violet-500 hover:underline"{{else}}style=\{{ color: '#7c3aed' }}{{/if}}>Request a new one</a>
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
</main>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
5
25
|
return (
|
|
6
26
|
<main {{#if useTailwind}}className="flex items-center justify-center min-h-screen bg-[#09090b]"{{else}}style=\{{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', background: '#09090b' }}{{/if}}>
|
|
7
|
-
<ResetPasswordForm signInUrl="/sign-in" />
|
|
27
|
+
<ResetPasswordForm token={token} signInUrl="/sign-in" />
|
|
8
28
|
</main>
|
|
9
29
|
);
|
|
10
30
|
}
|
|
31
|
+
|
|
32
|
+
export default function ResetPasswordPage() {
|
|
33
|
+
return (
|
|
34
|
+
<Suspense fallback={<div {{#if useTailwind}}className="flex items-center justify-center min-h-screen bg-[#09090b] text-zinc-400"{{else}}style=\{{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', background: '#09090b', color: '#a1a1aa' }}{{/if}}>Loading...</div>}>
|
|
35
|
+
<ResetPasswordContent />
|
|
36
|
+
</Suspense>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
11
39
|
{{/if}}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{{#if includeDashboard}}
|
|
2
|
-
import {
|
|
2
|
+
import { redirect } from 'next/navigation';
|
|
3
|
+
import { cookies } from 'next/headers';
|
|
4
|
+
|
|
5
|
+
export default async function DashboardLayout({ children }{{#if typescript}}: { children: React.ReactNode }{{/if}}) {
|
|
6
|
+
const cookieStore = await cookies();
|
|
7
|
+
const token = cookieStore.get('githat_access')?.value;
|
|
8
|
+
|
|
9
|
+
if (!token) {
|
|
10
|
+
redirect('/sign-in');
|
|
11
|
+
}
|
|
3
12
|
|
|
4
|
-
async function DashboardLayout({ children }{{#if typescript}}: { children: React.ReactNode }{{/if}}) {
|
|
5
13
|
return <>{children}</>;
|
|
6
14
|
}
|
|
7
|
-
|
|
8
|
-
export default withAuth(DashboardLayout);
|
|
9
15
|
{{/if}}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{{#if includeDashboard}}
|
|
2
2
|
'use client';
|
|
3
|
-
import {
|
|
3
|
+
import { useAuth, UserButton } from '@githat/nextjs';
|
|
4
4
|
|
|
5
5
|
export default function DashboardPage() {
|
|
6
|
-
const { user, org } =
|
|
6
|
+
const { user, org } = useAuth();
|
|
7
7
|
|
|
8
8
|
return (
|
|
9
9
|
<div {{#if useTailwind}}className="p-8"{{else}}style=\{{ padding: '2rem' }}{{/if}}>
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{{#if includeForgotPassword}}
|
|
2
2
|
'use client';
|
|
3
3
|
|
|
4
|
-
import { useState } from 'react';
|
|
4
|
+
import { Suspense, useState } from 'react';
|
|
5
5
|
import { useSearchParams, useRouter } from 'next/navigation';
|
|
6
6
|
{{#if includeGithatFolder}}
|
|
7
7
|
import { authApi } from '../../../githat/api/auth{{#unless typescript}}.js{{/unless}}';
|
|
8
8
|
{{/if}}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
function ResetPasswordContent() {
|
|
11
11
|
const searchParams = useSearchParams();
|
|
12
12
|
const router = useRouter();
|
|
13
13
|
const token = searchParams.get('token');
|
|
@@ -95,4 +95,12 @@ export default function ResetPasswordPage() {
|
|
|
95
95
|
</main>
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
|
+
|
|
99
|
+
export default function ResetPasswordPage() {
|
|
100
|
+
return (
|
|
101
|
+
<Suspense fallback={<div {{#if useTailwind}}className="flex items-center justify-center min-h-screen bg-[#09090b] text-zinc-400"{{else}}style=\{{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', background: '#09090b', color: '#a1a1aa' }}{{/if}}>Loading...</div>}>
|
|
102
|
+
<ResetPasswordContent />
|
|
103
|
+
</Suspense>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
98
106
|
{{/if}}
|