create-githat-app 1.6.0 → 1.8.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 (53) hide show
  1. package/dist/cli.js +5 -5
  2. package/package.json +6 -2
  3. package/templates/agent/app/(auth)/forgot-password/page.tsx.hbs +11 -0
  4. package/templates/agent/app/(auth)/reset-password/page.tsx.hbs +39 -0
  5. package/templates/agent/app/(auth)/verify-email/page.tsx.hbs +41 -0
  6. package/templates/agent/app/admin/agent/page.tsx.hbs +159 -62
  7. package/templates/agent/app/admin/layout.tsx.hbs +82 -0
  8. package/templates/agent/app/admin/mcp/page.tsx.hbs +156 -0
  9. package/templates/agent/app/dashboard/agents/page.tsx.hbs +9 -0
  10. package/templates/agent/app/dashboard/layout.tsx.hbs +9 -0
  11. package/templates/agent/app/dashboard/mcp/page.tsx.hbs +9 -0
  12. package/templates/agent/app/dashboard/page.tsx.hbs +128 -0
  13. package/templates/agent/app/globals.css.hbs +14 -9
  14. package/templates/agent/app/layout.tsx.hbs +7 -1
  15. package/templates/agent/app/page.tsx.hbs +127 -70
  16. package/templates/agent/app/verify/agent/page.tsx.hbs +124 -0
  17. package/templates/agent/next.config.ts.hbs +4 -5
  18. package/templates/agent/public/HERO_IMAGE.md +23 -0
  19. package/templates/base/githat/api/agents.ts.hbs +6 -6
  20. package/templates/base/githat/config.ts.hbs +7 -2
  21. package/templates/base/githat/dashboard/overview.tsx.hbs +106 -16
  22. package/templates/classroom/app/layout.tsx.hbs +6 -0
  23. package/templates/classroom/next.config.ts.hbs +4 -5
  24. package/templates/content/app/layout.tsx.hbs +6 -0
  25. package/templates/content/next.config.ts.hbs +4 -5
  26. package/templates/dashboard/app/admin/data/[entity]/page.tsx.hbs +2 -2
  27. package/templates/dashboard/app/layout.tsx.hbs +6 -0
  28. package/templates/dashboard/next.config.ts.hbs +4 -5
  29. package/templates/fullstack/apps-web-nextjs/app/layout.tsx.hbs +6 -0
  30. package/templates/fullstack/apps-web-nextjs/next.config.ts.hbs +5 -5
  31. package/templates/marketplace/app/layout.tsx.hbs +6 -0
  32. package/templates/marketplace/next.config.ts.hbs +4 -5
  33. package/templates/nextjs/app/(auth)/forgot-password/page.tsx.hbs +2 -54
  34. package/templates/nextjs/app/(auth)/reset-password/page.tsx.hbs +8 -75
  35. package/templates/nextjs/app/layout.tsx.hbs +6 -0
  36. package/templates/nextjs/next.config.ts.hbs +4 -5
  37. package/templates/plain/app/layout.tsx.hbs +6 -0
  38. package/templates/plain/next.config.ts.hbs +4 -5
  39. package/templates/portfolio/app/layout.tsx.hbs +6 -0
  40. package/templates/portfolio/next.config.ts.hbs +4 -5
  41. package/templates/saas/app/layout.tsx.hbs +6 -0
  42. package/templates/saas/next.config.ts.hbs +4 -5
  43. package/templates/agent/proxy.ts.hbs +0 -10
  44. package/templates/classroom/proxy.ts.hbs +0 -10
  45. package/templates/content/proxy.ts.hbs +0 -10
  46. package/templates/dashboard/proxy.ts.hbs +0 -10
  47. package/templates/marketplace/app/(shop)/[slug]/p/[productId]/page.tsx.hbs +0 -99
  48. package/templates/marketplace/app/(shop)/[slug]/page.tsx.hbs +0 -90
  49. package/templates/marketplace/proxy.ts.hbs +0 -10
  50. package/templates/nextjs/proxy.ts.hbs +0 -10
  51. package/templates/plain/proxy.ts.hbs +0 -10
  52. package/templates/portfolio/proxy.ts.hbs +0 -10
  53. package/templates/saas/proxy.ts.hbs +0 -10
@@ -0,0 +1,124 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Public agent verification page.
5
+ * Anyone can visit /verify/agent?wallet=<wallet> to confirm an agent's
6
+ * identity, check its current status, and review its capabilities.
7
+ *
8
+ * Wallet is a query param (not a path) so this is a single static page
9
+ * that handles any wallet — output: 'export' can't pre-render an unbounded
10
+ * dynamic-route param space.
11
+ */
12
+ import { Suspense, useEffect, useState } from 'react';
13
+ import { useSearchParams } from 'next/navigation';
14
+
15
+ const GITHAT_API = 'https://api.githat.io';
16
+
17
+ {{#if typescript}}
18
+ interface AgentVerify {
19
+ verified: boolean;
20
+ name: string;
21
+ wallet: string;
22
+ chainId: number;
23
+ status: 'active' | 'paused' | 'revoked' | 'pending';
24
+ verifiedSince: string;
25
+ lastActivity: string;
26
+ capabilities: string[];
27
+ actionsLast24h: number;
28
+ verificationUrl: string;
29
+ }
30
+ {{/if}}
31
+
32
+ function VerifyAgentContent() {
33
+ const searchParams = useSearchParams();
34
+ const wallet = searchParams.get('wallet') ?? '';
35
+ const [agent, setAgent] = useState{{#if typescript}}<AgentVerify | null>{{/if}}(null);
36
+ const [loading, setLoading] = useState(true);
37
+
38
+ useEffect(() => {
39
+ if (!wallet) { setLoading(false); return; }
40
+ let cancelled = false;
41
+ fetch(`${GITHAT_API}/verify/agent/${wallet}`)
42
+ .then((r) => (r.ok ? r.json() : null))
43
+ .then((data) => { if (!cancelled) setAgent(data); })
44
+ .catch(() => { if (!cancelled) setAgent(null); })
45
+ .finally(() => { if (!cancelled) setLoading(false); });
46
+ return () => { cancelled = true; };
47
+ }, [wallet]);
48
+
49
+ if (loading) {
50
+ return (
51
+ <main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#a1a1aa', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
52
+ <p style=\{{ fontSize: '0.875rem' }}>Loading…</p>
53
+ </main>
54
+ );
55
+ }
56
+
57
+ if (!agent?.verified) {
58
+ return (
59
+ <main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#fafafa', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '4rem 1rem' }}>
60
+ <div style=\{{ textAlign: 'center' }}>
61
+ <p style=\{{ fontSize: '1.125rem', color: '#a1a1aa', marginBottom: '0.5rem' }}>Agent not found</p>
62
+ <p style=\{{ fontSize: '0.875rem', color: '#52525b', fontFamily: "'JetBrains Mono', monospace", wordBreak: 'break-all' }}>{wallet || '(no wallet provided)'}</p>
63
+ </div>
64
+ </main>
65
+ );
66
+ }
67
+
68
+ const statusColor = ({
69
+ active: '#16a34a',
70
+ paused: '#d97706',
71
+ revoked: '#dc2626',
72
+ pending: '#6366f1',
73
+ }{{#if typescript}} as Record<string, string>{{/if}})[agent.status] ?? '#71717a';
74
+
75
+ return (
76
+ <main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#fafafa', display: 'flex', alignItems: 'flex-start', justifyContent: 'center', padding: '4rem 1rem' }}>
77
+ <div style=\{{ width: '100%', maxWidth: '36rem' }}>
78
+ <div style=\{{ marginBottom: '2rem', textAlign: 'center' }}>
79
+ <div style=\{{ display: 'inline-flex', alignItems: 'center', gap: '0.5rem', padding: '0.375rem 0.875rem', borderRadius: '9999px', background: statusColor + '22', border: `1px solid ${statusColor}44`, marginBottom: '1.5rem' }}>
80
+ <span style=\{{ width: '0.5rem', height: '0.5rem', borderRadius: '9999px', background: statusColor, display: 'inline-block' }} />
81
+ <span style=\{{ fontSize: '0.75rem', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.08em', color: statusColor }}>{agent.status}</span>
82
+ </div>
83
+ <h1 style=\{{ fontSize: '1.75rem', fontWeight: 700, marginBottom: '0.5rem' }}>{agent.name}</h1>
84
+ <p style=\{{ fontFamily: "'JetBrains Mono', monospace", fontSize: '0.8125rem', color: '#71717a', wordBreak: 'break-all' }}>{agent.wallet}</p>
85
+ </div>
86
+
87
+ <div style=\{{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', marginBottom: '1.5rem' }}>
88
+ {[
89
+ { label: 'Actions (24h)', value: agent.actionsLast24h },
90
+ { label: 'Verified since', value: new Date(agent.verifiedSince).toLocaleDateString() },
91
+ ].map(({ label, value }) => (
92
+ <div key={label} style=\{{ padding: '1.25rem', borderRadius: '0.75rem', border: '1px solid #27272a', background: '#18181b' }}>
93
+ <div style=\{{ fontSize: '0.75rem', color: '#71717a', marginBottom: '0.375rem' }}>{label}</div>
94
+ <div style=\{{ fontSize: '1.125rem', fontWeight: 600 }}>{value}</div>
95
+ </div>
96
+ ))}
97
+ </div>
98
+
99
+ {agent.capabilities.length > 0 && (
100
+ <div style=\{{ padding: '1.25rem', borderRadius: '0.75rem', border: '1px solid #27272a', background: '#18181b', marginBottom: '1.5rem' }}>
101
+ <h2 style=\{{ fontSize: '0.875rem', fontWeight: 600, color: '#a1a1aa', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '1rem' }}>Capabilities</h2>
102
+ <div style=\{{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
103
+ {agent.capabilities.map((cap{{#if typescript}}: string{{/if}}) => (
104
+ <span key={cap} style=\{{ padding: '0.375rem 0.75rem', borderRadius: '0.375rem', border: '1px solid #3f3f46', fontSize: '0.8125rem', fontFamily: "'JetBrains Mono', monospace", color: '#a1a1aa' }}>{cap}</span>
105
+ ))}
106
+ </div>
107
+ </div>
108
+ )}
109
+ </div>
110
+ </main>
111
+ );
112
+ }
113
+
114
+ export default function VerifyAgentPage() {
115
+ return (
116
+ <Suspense fallback={
117
+ <main style=\{{ minHeight: '100vh', background: '#0a0a0a', color: '#a1a1aa', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
118
+ <p style=\{{ fontSize: '0.875rem' }}>Loading…</p>
119
+ </main>
120
+ }>
121
+ <VerifyAgentContent />
122
+ </Suspense>
123
+ );
124
+ }
@@ -1,9 +1,8 @@
1
- {{#if typescript}}import type { NextConfig } from 'next';
2
- {{/if}}import { withGitHat } from '@githat/nextjs/server';
1
+ import { withGitHat } from '@githat/nextjs/server';
3
2
 
4
- {{#if typescript}}const nextConfig: NextConfig = {
5
- {{else}}const nextConfig = {
6
- {{/if}} output: 'standalone',
3
+ const nextConfig = {
4
+ output: 'export',
5
+ images: { unoptimized: true },
7
6
  };
8
7
 
9
8
  export default withGitHat(nextConfig);
@@ -0,0 +1,23 @@
1
+ # Hero Image
2
+
3
+ Drop your logo or hero image (`.png`, `.jpg`, `.webp`) in this folder.
4
+
5
+ Then in `app/page.tsx`, replace `<HeroImagePlaceholder />` with:
6
+
7
+ ```tsx
8
+ <Image
9
+ src="/your-image.png"
10
+ alt="{{businessName}}"
11
+ fill
12
+ style={{ objectFit: 'cover', borderRadius: '1rem' }}
13
+ priority
14
+ />
15
+ ```
16
+
17
+ Wrap it in a `position: 'relative'` container with the same `aspectRatio: '4 / 3'` as the placeholder.
18
+
19
+ The `Image` component from `next/image` is not yet imported — add it at the top of `page.tsx`:
20
+
21
+ ```tsx
22
+ import Image from 'next/image';
23
+ ```
@@ -5,13 +5,13 @@ import type { AiAgent } from './types';
5
5
  {{/if}}
6
6
 
7
7
  export const agentsApi = {
8
- register: (wallet{{#if typescript}}: string{{/if}}, chainId{{#if typescript}}: number{{/if}}, name{{#if typescript}}: string{{/if}}) =>
9
- githatApi.post{{#if typescript}}<{ agent: AiAgent }>{{/if}}('/agent/register', { wallet, chainId, name }),
8
+ register: (walletAddress{{#if typescript}}: string{{/if}}, chainId{{#if typescript}}: number{{/if}}, name{{#if typescript}}: string{{/if}}) =>
9
+ githatApi.post{{#if typescript}}<{ agent: AiAgent }>{{/if}}('/agent/register', { walletAddress, chainId, name }),
10
10
 
11
- challenge: (wallet{{#if typescript}}: string{{/if}}) =>
12
- githatApi.post{{#if typescript}}<{ nonce: string }>{{/if}}('/agent/challenge', { wallet }),
11
+ challenge: (walletAddress{{#if typescript}}: string{{/if}}) =>
12
+ githatApi.post{{#if typescript}}<{ nonce: string }>{{/if}}('/agent/challenge', { walletAddress }),
13
13
 
14
- getToken: (wallet{{#if typescript}}: string{{/if}}, signature{{#if typescript}}: string{{/if}}, nonce{{#if typescript}}: string{{/if}}) =>
15
- githatApi.post{{#if typescript}}<{ accessToken: string }>{{/if}}('/agent/token', { wallet, signature, nonce }),
14
+ getToken: (walletAddress{{#if typescript}}: string{{/if}}, signature{{#if typescript}}: string{{/if}}, nonce{{#if typescript}}: string{{/if}}) =>
15
+ githatApi.post{{#if typescript}}<{ accessToken: string }>{{/if}}('/agent/token', { walletAddress, signature, nonce }),
16
16
  };
17
17
  {{/if}}
@@ -1,8 +1,13 @@
1
1
  {{#if includeGithatFolder}}
2
2
  export const githatConfig = {
3
3
  appName: '{{businessName}}',
4
- publishableKey: {{#ifEquals framework "nextjs"}}process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY{{else}}import.meta.env.VITE_GITHAT_PUBLISHABLE_KEY{{/ifEquals}} || '',
5
- apiUrl: {{#ifEquals framework "nextjs"}}process.env.NEXT_PUBLIC_GITHAT_API_URL{{else}}import.meta.env.VITE_GITHAT_API_URL{{/ifEquals}} || '{{apiUrl}}',
4
+ publishableKey: {{#ifNext framework}}process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY{{else}}import.meta.env.VITE_GITHAT_PUBLISHABLE_KEY{{/ifNext}} || '',
5
+ // For Next.js-shaped frameworks we point at the same-origin proxy
6
+ // mounted at app/api/githat/[...path]/route.ts — that proxy forwards
7
+ // to api.githat.io and re-emits Set-Cookie on this app's domain so
8
+ // cookies are visible to proxy.ts and getAuth(). For react-vite we
9
+ // hit api.githat.io directly (browser-token flows only).
10
+ apiUrl: {{#ifNext framework}}process.env.NEXT_PUBLIC_GITHAT_API_URL || '/api/githat'{{else}}import.meta.env.VITE_GITHAT_API_URL || '{{apiUrl}}'{{/ifNext}},
6
11
  signInUrl: '/sign-in',
7
12
  signUpUrl: '/sign-up',
8
13
  afterSignInUrl: '/dashboard',
@@ -1,6 +1,7 @@
1
1
  {{#unless includeDashboard}}{{else}}
2
2
  'use client';
3
3
 
4
+ import Link from 'next/link';
4
5
  import { useAuth } from '@githat/nextjs';
5
6
 
6
7
  export function DashboardOverview() {
@@ -8,29 +9,118 @@ export function DashboardOverview() {
8
9
 
9
10
  return (
10
11
  <div>
11
- <h1 style=\{{ fontSize: '1.5rem', fontWeight: 600, color: '#fafafa', marginBottom: '0.5rem' }}>
12
- Welcome{user?.name ? `, ${user.name}` : ''}
13
- </h1>
14
- {org && (
15
- <p style=\{{ color: '#a1a1aa', marginBottom: '2rem' }}>
16
- Organization: <strong style=\{{ color: '#7c3aed' }}>{org.name}</strong> ({org.role})
17
- </p>
18
- )}
12
+ <div style=\{{ marginBottom: '2rem' }}>
13
+ <h1 style=\{{ fontSize: '1.5rem', fontWeight: 700, color: '#fafafa', marginBottom: '0.375rem' }}>
14
+ Welcome back{user?.name ? `, ${user.name}` : ''}
15
+ </h1>
16
+ {org && (
17
+ <p style=\{{ color: '#71717a', fontSize: '0.875rem' }}>
18
+ {org.name} · <span style=\{{ color: '#a1a1aa' }}>{org.role}</span>
19
+ </p>
20
+ )}
21
+ </div>
22
+
23
+ {/* Stats row */}
24
+ <div style=\{{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(14rem, 1fr))', gap: '1rem', marginBottom: '2.5rem' }}>
25
+ <StatCard label="Status" value="Active" sub="Account health" />
26
+ <StatCard label="Plan" value={org?.tier ?? 'Free'} sub="Current tier" />
27
+ <StatCard label="Identity" value="GitHat" sub="Auth provider" />
28
+ {{#if includeOrgManagement}}
29
+ <StatCard label="Members" value={String(org?.memberCount ?? 1)} sub="Team size" />
30
+ {{/if}}
31
+ </div>
19
32
 
20
- <div style=\{{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(16rem, 1fr))', gap: '1rem' }}>
21
- <StatCard title="Status" value="Active" />
22
- <StatCard title="Plan" value={org?.tier || 'Free'} />
23
- <StatCard title="Identity" value="GitHat" />
33
+ {/* Quick actions */}
34
+ <div style=\{{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(20rem, 1fr))', gap: '1rem' }}>
35
+ {{#if includeOrgManagement}}
36
+ <ActionCard
37
+ title="Invite team members"
38
+ desc="Add your business partner, accountant, or employee to your org."
39
+ cta="Invite"
40
+ href="/dashboard/members"
41
+ accent="#6366f1"
42
+ />
43
+ <ActionCard
44
+ title="Organization settings"
45
+ desc="Update your org name, billing plan, and member permissions."
46
+ cta="Settings"
47
+ href="/dashboard/settings"
48
+ accent="#8b5cf6"
49
+ />
50
+ {{/if}}
51
+ {{#if includeMcpModule}}
52
+ <ActionCard
53
+ title="Connect an MCP server"
54
+ desc="Hook your data into any AI assistant via the Model Context Protocol."
55
+ cta="Connect"
56
+ href="/dashboard/mcp"
57
+ accent="#0ea5e9"
58
+ />
59
+ {{/if}}
60
+ {{#if includeAgentModule}}
61
+ <ActionCard
62
+ title="Deploy an AI agent"
63
+ desc="Register a wallet-bound autonomous agent with capability scoping and a kill switch."
64
+ cta="Deploy"
65
+ href="/dashboard/agents"
66
+ accent="#10b981"
67
+ />
68
+ {{/if}}
69
+ <ActionCard
70
+ title="Manage apps"
71
+ desc="View and rotate your publishable keys, configure allowed origins."
72
+ cta="View apps"
73
+ href="/dashboard/apps"
74
+ accent="#f59e0b"
75
+ />
24
76
  </div>
25
77
  </div>
26
78
  );
27
79
  }
28
80
 
29
- function StatCard({ title, value }{{#if typescript}}: { title: string; value: string }{{/if}}) {
81
+ function StatCard({ label, value, sub }{{#if typescript}}: { label: string; value: string; sub: string }{{/if}}) {
82
+ return (
83
+ <div style=\{{
84
+ background: '#111113',
85
+ border: '1px solid #1e1e2e',
86
+ borderRadius: '0.75rem',
87
+ padding: '1.5rem',
88
+ }}>
89
+ <p style=\{{ fontSize: '0.8125rem', color: '#71717a', marginBottom: '0.375rem' }}>{label}</p>
90
+ <p style=\{{ fontSize: '1.5rem', fontWeight: 700, color: '#fafafa', marginBottom: '0.25rem' }}>{value}</p>
91
+ <p style=\{{ fontSize: '0.75rem', color: '#52525b' }}>{sub}</p>
92
+ </div>
93
+ );
94
+ }
95
+
96
+ function ActionCard({ title, desc, cta, href, accent }{{#if typescript}}: {
97
+ title: string; desc: string; cta: string; href: string; accent: string;
98
+ }{{/if}}) {
30
99
  return (
31
- <div style=\{{ padding: '1.5rem', background: '#111113', border: '1px solid #1e1e2e', borderRadius: '0.5rem' }}>
32
- <p style=\{{ fontSize: '0.875rem', color: '#71717a', marginBottom: '0.25rem' }}>{title}</p>
33
- <p style=\{{ fontSize: '1.25rem', fontWeight: 600, color: '#fafafa' }}>{value}</p>
100
+ <div style=\{{
101
+ background: '#111113',
102
+ border: '1px solid #1e1e2e',
103
+ borderRadius: '0.75rem',
104
+ padding: '1.5rem',
105
+ display: 'flex',
106
+ flexDirection: 'column',
107
+ gap: '0.75rem',
108
+ }}>
109
+ <h3 style=\{{ fontSize: '0.9375rem', fontWeight: 600, color: '#fafafa' }}>{title}</h3>
110
+ <p style=\{{ fontSize: '0.875rem', color: '#71717a', lineHeight: 1.6, flex: 1 }}>{desc}</p>
111
+ <Link href={href} style=\{{
112
+ display: 'inline-block',
113
+ background: accent,
114
+ color: '#fff',
115
+ textDecoration: 'none',
116
+ padding: '0.5rem 1rem',
117
+ borderRadius: '0.375rem',
118
+ fontSize: '0.875rem',
119
+ fontWeight: 600,
120
+ alignSelf: 'flex-start',
121
+ }}>
122
+ {cta} →
123
+ </Link>
34
124
  </div>
35
125
  );
36
126
  }
@@ -19,6 +19,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
19
19
  */}
20
20
  <GitHatProvider config=\{{
21
21
  publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
22
+ apiUrl: 'https://api.githat.io',
23
+ {{#if typescript}}
24
+ tokenStorage: 'localStorage' as const,
25
+ {{else}}
26
+ tokenStorage: 'localStorage',
27
+ {{/if}}
22
28
  signInUrl: '/sign-in',
23
29
  signUpUrl: '/sign-up',
24
30
  afterSignInUrl: '/',
@@ -1,9 +1,8 @@
1
- {{#if typescript}}import type { NextConfig } from 'next';
2
- {{/if}}import { withGitHat } from '@githat/nextjs/server';
1
+ import { withGitHat } from '@githat/nextjs/server';
3
2
 
4
- {{#if typescript}}const nextConfig: NextConfig = {
5
- {{else}}const nextConfig = {
6
- {{/if}} output: 'standalone',
3
+ const nextConfig = {
4
+ output: 'export',
5
+ images: { unoptimized: true },
7
6
  };
8
7
 
9
8
  export default withGitHat(nextConfig);
@@ -19,6 +19,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
19
19
  */}
20
20
  <GitHatProvider config=\{{
21
21
  publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
22
+ apiUrl: 'https://api.githat.io',
23
+ {{#if typescript}}
24
+ tokenStorage: 'localStorage' as const,
25
+ {{else}}
26
+ tokenStorage: 'localStorage',
27
+ {{/if}}
22
28
  signInUrl: '/sign-in',
23
29
  signUpUrl: '/sign-up',
24
30
  afterSignInUrl: '/',
@@ -1,9 +1,8 @@
1
- {{#if typescript}}import type { NextConfig } from 'next';
2
- {{/if}}import { withGitHat } from '@githat/nextjs/server';
1
+ import { withGitHat } from '@githat/nextjs/server';
3
2
 
4
- {{#if typescript}}const nextConfig: NextConfig = {
5
- {{else}}const nextConfig = {
6
- {{/if}} output: 'standalone',
3
+ const nextConfig = {
4
+ output: 'export',
5
+ images: { unoptimized: true },
7
6
  };
8
7
 
9
8
  export default withGitHat(nextConfig);
@@ -6,8 +6,8 @@ import { listEntities } from '../../../../src/lib/db';
6
6
  *
7
7
  * Server component — runs `listEntities(entityName)` (which you
8
8
  * implement in src/lib/db.ts) and renders rows as a basic table.
9
- * Auth-gating is enforced by the middleware in proxy.ts; if you
10
- * need entity-level RBAC, check `verifyToken` claims here.
9
+ * Auth-gating happens client-side via `useAuth` in the static export;
10
+ * if you need entity-level RBAC, check `verifyToken` claims here.
11
11
  */
12
12
  export default async function EntityPage({
13
13
  params,
@@ -19,6 +19,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
19
19
  */}
20
20
  <GitHatProvider config=\{{
21
21
  publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
22
+ apiUrl: 'https://api.githat.io',
23
+ {{#if typescript}}
24
+ tokenStorage: 'localStorage' as const,
25
+ {{else}}
26
+ tokenStorage: 'localStorage',
27
+ {{/if}}
22
28
  signInUrl: '/sign-in',
23
29
  signUpUrl: '/sign-up',
24
30
  afterSignInUrl: '/',
@@ -1,9 +1,8 @@
1
- {{#if typescript}}import type { NextConfig } from 'next';
2
- {{/if}}import { withGitHat } from '@githat/nextjs/server';
1
+ import { withGitHat } from '@githat/nextjs/server';
3
2
 
4
- {{#if typescript}}const nextConfig: NextConfig = {
5
- {{else}}const nextConfig = {
6
- {{/if}} output: 'standalone',
3
+ const nextConfig = {
4
+ output: 'export',
5
+ images: { unoptimized: true },
7
6
  };
8
7
 
9
8
  export default withGitHat(nextConfig);
@@ -14,6 +14,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
14
14
  <body>
15
15
  <GitHatProvider config=\{{
16
16
  publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
17
+ apiUrl: 'https://api.githat.io',
18
+ {{#if typescript}}
19
+ tokenStorage: 'localStorage' as const,
20
+ {{else}}
21
+ tokenStorage: 'localStorage',
22
+ {{/if}}
17
23
  signInUrl: '/sign-in',
18
24
  signUpUrl: '/sign-up',
19
25
  afterSignInUrl: '/dashboard',
@@ -1,9 +1,9 @@
1
- {{#if typescript}}import type { NextConfig } from 'next';
2
- {{/if}}import { withGitHat } from '@githat/nextjs/server';
1
+ import { withGitHat } from '@githat/nextjs/server';
3
2
 
4
- {{#if typescript}}const nextConfig: NextConfig = {
5
- {{else}}const nextConfig = {
6
- {{/if}} // Proxy API calls to the backend in development
3
+ const nextConfig = {
4
+ output: 'export',
5
+ images: { unoptimized: true },
6
+ // Proxy API calls to the backend in development
7
7
  async rewrites() {
8
8
  return [
9
9
  {
@@ -25,6 +25,12 @@ export default function RootLayout({ children }{{#if typescript}}: { children: R
25
25
  <body>
26
26
  <GitHatProvider config=\{{
27
27
  publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
28
+ apiUrl: 'https://api.githat.io',
29
+ {{#if typescript}}
30
+ tokenStorage: 'localStorage' as const,
31
+ {{else}}
32
+ tokenStorage: 'localStorage',
33
+ {{/if}}
28
34
  signInUrl: '/sign-in',
29
35
  signUpUrl: '/sign-up',
30
36
  afterSignInUrl: '/',
@@ -1,9 +1,8 @@
1
- {{#if typescript}}import type { NextConfig } from 'next';
2
- {{/if}}import { withGitHat } from '@githat/nextjs/server';
1
+ import { withGitHat } from '@githat/nextjs/server';
3
2
 
4
- {{#if typescript}}const nextConfig: NextConfig = {
5
- {{else}}const nextConfig = {
6
- {{/if}} output: 'standalone',
3
+ const nextConfig = {
4
+ output: 'export',
5
+ images: { unoptimized: true },
7
6
  };
8
7
 
9
8
  export default withGitHat(nextConfig);
@@ -1,62 +1,10 @@
1
1
  {{#if includeForgotPassword}}
2
- 'use client';
3
-
4
- import { useState } from 'react';
5
- {{#if includeGithatFolder}}
6
- import { authApi } from '../../../githat/api/auth{{#unless typescript}}.js{{/unless}}';
7
- {{/if}}
2
+ import { ForgotPasswordForm } from '@githat/nextjs';
8
3
 
9
4
  export default function ForgotPasswordPage() {
10
- const [email, setEmail] = useState('');
11
- const [sent, setSent] = useState(false);
12
- const [error, setError] = useState('');
13
-
14
- const handleSubmit = async (e{{#if typescript}}: React.FormEvent{{/if}}) => {
15
- e.preventDefault();
16
- setError('');
17
- try {
18
- {{#if includeGithatFolder}}
19
- await authApi.forgotPassword(email);
20
- {{else}}
21
- await fetch('{{apiUrl}}/auth/forgot-password', {
22
- method: 'POST',
23
- headers: { 'Content-Type': 'application/json' },
24
- body: JSON.stringify({ email }),
25
- });
26
- {{/if}}
27
- setSent(true);
28
- } catch (err) {
29
- setError('Something went wrong. Please try again.');
30
- }
31
- };
32
-
33
5
  return (
34
6
  <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}}>
35
- <div style=\{{ width: '100%', maxWidth: '24rem', padding: '2rem' }}>
36
- <h1 style=\{{ fontSize: '1.5rem', fontWeight: 600, color: '#fafafa', marginBottom: '0.5rem' }}>Reset password</h1>
37
- {sent ? (
38
- <p style=\{{ color: '#a1a1aa' }}>Check your email for a reset link.</p>
39
- ) : (
40
- <form onSubmit={handleSubmit}>
41
- <p style=\{{ color: '#a1a1aa', marginBottom: '1.5rem' }}>Enter your email to receive a reset link.</p>
42
- {error && <p style=\{{ color: '#ef4444', marginBottom: '1rem', fontSize: '0.875rem' }}>{error}</p>}
43
- <input
44
- type="email"
45
- value={email}
46
- onChange={(e) => setEmail(e.target.value)}
47
- placeholder="you@example.com"
48
- required
49
- style=\{{ width: '100%', padding: '0.625rem 0.75rem', background: '#111113', border: '1px solid #1e1e2e', borderRadius: '0.375rem', color: '#fafafa', marginBottom: '1rem', outline: 'none' }}
50
- />
51
- <button
52
- type="submit"
53
- style=\{{ width: '100%', padding: '0.625rem', background: '#7c3aed', color: '#fff', border: 'none', borderRadius: '0.375rem', fontWeight: 600, cursor: 'pointer' }}
54
- >
55
- Send reset link
56
- </button>
57
- </form>
58
- )}
59
- </div>
7
+ <ForgotPasswordForm signInUrl="/sign-in" />
60
8
  </main>
61
9
  );
62
10
  }