create-githat-app 1.0.2 → 1.0.4
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 +316 -99
- package/package.json +1 -1
- package/templates/fullstack/apps-api-express/.env.example.hbs +6 -0
- package/templates/fullstack/apps-api-express/.env.local.hbs +6 -0
- package/templates/fullstack/apps-api-express/package.json.hbs +24 -0
- package/templates/fullstack/apps-api-express/src/index.ts.hbs +41 -0
- package/templates/fullstack/apps-api-express/src/routes/health.ts.hbs +11 -0
- package/templates/fullstack/apps-api-express/src/routes/users.ts.hbs +43 -0
- package/templates/fullstack/apps-api-express/tsconfig.json.hbs +16 -0
- package/templates/fullstack/apps-api-fastify/.env.example.hbs +6 -0
- package/templates/fullstack/apps-api-fastify/.env.local.hbs +6 -0
- package/templates/fullstack/apps-api-fastify/package.json.hbs +22 -0
- package/templates/fullstack/apps-api-fastify/src/index.ts.hbs +28 -0
- package/templates/fullstack/apps-api-fastify/src/routes/health.ts.hbs +11 -0
- package/templates/fullstack/apps-api-fastify/src/routes/users.ts.hbs +43 -0
- package/templates/fullstack/apps-api-fastify/tsconfig.json.hbs +16 -0
- package/templates/fullstack/apps-api-hono/.env.example.hbs +6 -0
- package/templates/fullstack/apps-api-hono/.env.local.hbs +6 -0
- package/templates/fullstack/apps-api-hono/package.json.hbs +22 -0
- package/templates/fullstack/apps-api-hono/src/index.ts.hbs +35 -0
- package/templates/fullstack/apps-api-hono/src/routes/health.ts.hbs +11 -0
- package/templates/fullstack/apps-api-hono/src/routes/users.ts.hbs +43 -0
- package/templates/fullstack/apps-api-hono/tsconfig.json.hbs +16 -0
- package/templates/fullstack/apps-web-nextjs/.env.example.hbs +5 -0
- package/templates/fullstack/apps-web-nextjs/.env.local.hbs +5 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/forgot-password/page.tsx.hbs +11 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/reset-password/page.tsx.hbs +11 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/sign-in/page.tsx.hbs +9 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/sign-up/page.tsx.hbs +9 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/verify-email/page.tsx.hbs +11 -0
- package/templates/fullstack/apps-web-nextjs/app/dashboard/layout.tsx.hbs +9 -0
- package/templates/fullstack/apps-web-nextjs/app/dashboard/page.tsx.hbs +27 -0
- package/templates/fullstack/apps-web-nextjs/app/globals.css.hbs +20 -0
- package/templates/fullstack/apps-web-nextjs/app/layout.tsx.hbs +26 -0
- package/templates/fullstack/apps-web-nextjs/app/page.tsx.hbs +18 -0
- package/templates/fullstack/apps-web-nextjs/next.config.ts.hbs +15 -0
- package/templates/fullstack/apps-web-nextjs/package.json.hbs +33 -0
- package/templates/fullstack/apps-web-nextjs/postcss.config.mjs.hbs +9 -0
- package/templates/fullstack/apps-web-nextjs/tsconfig.json.hbs +21 -0
- package/templates/fullstack/root/.gitignore.hbs +42 -0
- package/templates/fullstack/root/githat.yaml.hbs +17 -0
- package/templates/fullstack/root/package.json.hbs +15 -0
- package/templates/fullstack/root/turbo.json.hbs +20 -0
package/package.json
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/index.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"typecheck": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@githat/nextjs": "^0.2.2",
|
|
14
|
+
"cors": "^2.8.5",
|
|
15
|
+
"express": "^5.0.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/cors": "^2.8.17",
|
|
19
|
+
"@types/express": "^5.0.0",
|
|
20
|
+
"@types/node": "^22.0.0",
|
|
21
|
+
"tsx": "^4.19.0",
|
|
22
|
+
"typescript": "^5.9.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import { healthRoutes } from './routes/health.js';
|
|
4
|
+
import { userRoutes } from './routes/users.js';
|
|
5
|
+
|
|
6
|
+
const app = express();
|
|
7
|
+
|
|
8
|
+
// Middleware
|
|
9
|
+
app.use(express.json());
|
|
10
|
+
app.use(cors({
|
|
11
|
+
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
|
12
|
+
credentials: true,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
// Request logging
|
|
16
|
+
app.use((req, res, next) => {
|
|
17
|
+
console.log(`${req.method} ${req.path}`);
|
|
18
|
+
next();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Routes
|
|
22
|
+
app.use('/health', healthRoutes);
|
|
23
|
+
app.use('/users', userRoutes);
|
|
24
|
+
|
|
25
|
+
// 404 handler
|
|
26
|
+
app.use((req, res) => {
|
|
27
|
+
res.status(404).json({ error: 'Not found' });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Error handler
|
|
31
|
+
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
32
|
+
console.error(err);
|
|
33
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const port = Number(process.env.PORT) || 3001;
|
|
37
|
+
app.listen(port, () => {
|
|
38
|
+
console.log(`🚀 API server running at http://localhost:${port}`);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export default app;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
|
|
3
|
+
export const userRoutes = Router();
|
|
4
|
+
|
|
5
|
+
// Example protected route - verify GitHat tokens here
|
|
6
|
+
userRoutes.get('/me', async (req, res) => {
|
|
7
|
+
// Get the Authorization header
|
|
8
|
+
const authHeader = req.headers.authorization;
|
|
9
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
10
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const token = authHeader.slice(7);
|
|
14
|
+
|
|
15
|
+
// Verify the token with GitHat API
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetch('{{apiUrl}}/auth/verify', {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
'X-Publishable-Key': process.env.GITHAT_PUBLISHABLE_KEY || '',
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify({ token }),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
return res.status(401).json({ error: 'Invalid token' });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { user, org } = await response.json();
|
|
31
|
+
return res.json({ user, org });
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('Token verification failed:', error);
|
|
34
|
+
return res.status(500).json({ error: 'Authentication failed' });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Example: Get user profile
|
|
39
|
+
userRoutes.get('/:id', async (req, res) => {
|
|
40
|
+
const { id } = req.params;
|
|
41
|
+
// TODO: Implement user lookup
|
|
42
|
+
res.json({ id, message: 'User lookup not implemented' });
|
|
43
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/index.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"typecheck": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@fastify/cors": "^10.0.0",
|
|
14
|
+
"@githat/nextjs": "^0.2.2",
|
|
15
|
+
"fastify": "^5.2.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^22.0.0",
|
|
19
|
+
"tsx": "^4.19.0",
|
|
20
|
+
"typescript": "^5.9.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Fastify from 'fastify';
|
|
2
|
+
import cors from '@fastify/cors';
|
|
3
|
+
import { healthRoutes } from './routes/health.js';
|
|
4
|
+
import { userRoutes } from './routes/users.js';
|
|
5
|
+
|
|
6
|
+
const app = Fastify({ logger: true });
|
|
7
|
+
|
|
8
|
+
// Middleware
|
|
9
|
+
await app.register(cors, {
|
|
10
|
+
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
|
11
|
+
credentials: true,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Routes
|
|
15
|
+
await app.register(healthRoutes, { prefix: '/health' });
|
|
16
|
+
await app.register(userRoutes, { prefix: '/users' });
|
|
17
|
+
|
|
18
|
+
// Start server
|
|
19
|
+
const port = Number(process.env.PORT) || 3001;
|
|
20
|
+
try {
|
|
21
|
+
await app.listen({ port, host: '0.0.0.0' });
|
|
22
|
+
console.log(`🚀 API server running at http://localhost:${port}`);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
app.log.error(err);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default app;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FastifyPluginAsync } from 'fastify';
|
|
2
|
+
|
|
3
|
+
export const healthRoutes: FastifyPluginAsync = async (app) => {
|
|
4
|
+
app.get('/', async () => {
|
|
5
|
+
return {
|
|
6
|
+
status: 'ok',
|
|
7
|
+
timestamp: new Date().toISOString(),
|
|
8
|
+
service: '{{projectName}}-api',
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { FastifyPluginAsync } from 'fastify';
|
|
2
|
+
|
|
3
|
+
export const userRoutes: FastifyPluginAsync = async (app) => {
|
|
4
|
+
// Example protected route - verify GitHat tokens here
|
|
5
|
+
app.get('/me', async (request, reply) => {
|
|
6
|
+
// Get the Authorization header
|
|
7
|
+
const authHeader = request.headers.authorization;
|
|
8
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
9
|
+
return reply.status(401).send({ error: 'Unauthorized' });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const token = authHeader.slice(7);
|
|
13
|
+
|
|
14
|
+
// Verify the token with GitHat API
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch('{{apiUrl}}/auth/verify', {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: {
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
'X-Publishable-Key': process.env.GITHAT_PUBLISHABLE_KEY || '',
|
|
21
|
+
},
|
|
22
|
+
body: JSON.stringify({ token }),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
return reply.status(401).send({ error: 'Invalid token' });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { user, org } = await response.json();
|
|
30
|
+
return { user, org };
|
|
31
|
+
} catch (error) {
|
|
32
|
+
app.log.error('Token verification failed:', error);
|
|
33
|
+
return reply.status(500).send({ error: 'Authentication failed' });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Example: Get user profile
|
|
38
|
+
app.get<{ Params: { id: string } }>('/:id', async (request) => {
|
|
39
|
+
const { id } = request.params;
|
|
40
|
+
// TODO: Implement user lookup
|
|
41
|
+
return { id, message: 'User lookup not implemented' };
|
|
42
|
+
});
|
|
43
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/index.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"typecheck": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@githat/nextjs": "^0.2.2",
|
|
14
|
+
"@hono/node-server": "^1.13.0",
|
|
15
|
+
"hono": "^4.6.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^22.0.0",
|
|
19
|
+
"tsx": "^4.19.0",
|
|
20
|
+
"typescript": "^5.9.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { serve } from '@hono/node-server';
|
|
2
|
+
import { Hono } from 'hono';
|
|
3
|
+
import { cors } from 'hono/cors';
|
|
4
|
+
import { logger } from 'hono/logger';
|
|
5
|
+
import { healthRoutes } from './routes/health.js';
|
|
6
|
+
import { userRoutes } from './routes/users.js';
|
|
7
|
+
|
|
8
|
+
const app = new Hono();
|
|
9
|
+
|
|
10
|
+
// Middleware
|
|
11
|
+
app.use('*', logger());
|
|
12
|
+
app.use('*', cors({
|
|
13
|
+
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
|
14
|
+
credentials: true,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// Routes
|
|
18
|
+
app.route('/health', healthRoutes);
|
|
19
|
+
app.route('/users', userRoutes);
|
|
20
|
+
|
|
21
|
+
// 404 handler
|
|
22
|
+
app.notFound((c) => c.json({ error: 'Not found' }, 404));
|
|
23
|
+
|
|
24
|
+
// Error handler
|
|
25
|
+
app.onError((err, c) => {
|
|
26
|
+
console.error(err);
|
|
27
|
+
return c.json({ error: 'Internal server error' }, 500);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const port = Number(process.env.PORT) || 3001;
|
|
31
|
+
console.log(`🚀 API server running at http://localhost:${port}`);
|
|
32
|
+
|
|
33
|
+
serve({ fetch: app.fetch, port });
|
|
34
|
+
|
|
35
|
+
export default app;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
|
|
3
|
+
// Example protected route - verify GitHat tokens here
|
|
4
|
+
export const userRoutes = new Hono();
|
|
5
|
+
|
|
6
|
+
userRoutes.get('/me', async (c) => {
|
|
7
|
+
// Get the Authorization header
|
|
8
|
+
const authHeader = c.req.header('Authorization');
|
|
9
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
10
|
+
return c.json({ error: 'Unauthorized' }, 401);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const token = authHeader.slice(7);
|
|
14
|
+
|
|
15
|
+
// Verify the token with GitHat API
|
|
16
|
+
try {
|
|
17
|
+
const response = await fetch('{{apiUrl}}/auth/verify', {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
'X-Publishable-Key': process.env.GITHAT_PUBLISHABLE_KEY || '',
|
|
22
|
+
},
|
|
23
|
+
body: JSON.stringify({ token }),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
return c.json({ error: 'Invalid token' }, 401);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { user, org } = await response.json();
|
|
31
|
+
return c.json({ user, org });
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('Token verification failed:', error);
|
|
34
|
+
return c.json({ error: 'Authentication failed' }, 500);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Example: Get user profile
|
|
39
|
+
userRoutes.get('/:id', async (c) => {
|
|
40
|
+
const id = c.req.param('id');
|
|
41
|
+
// TODO: Implement user lookup
|
|
42
|
+
return c.json({ id, message: 'User lookup not implemented' });
|
|
43
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"resolveJsonModule": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{{#if includeForgotPassword}}
|
|
2
|
+
import { ForgotPasswordForm } from '@githat/nextjs';
|
|
3
|
+
|
|
4
|
+
export default function ForgotPasswordPage() {
|
|
5
|
+
return (
|
|
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}}>
|
|
7
|
+
<ForgotPasswordForm signInUrl="/sign-in" />
|
|
8
|
+
</main>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
{{/if}}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{{#if includeForgotPassword}}
|
|
2
|
+
import { ResetPasswordForm } from '@githat/nextjs';
|
|
3
|
+
|
|
4
|
+
export default function ResetPasswordPage() {
|
|
5
|
+
return (
|
|
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}}>
|
|
7
|
+
<ResetPasswordForm signInUrl="/sign-in" />
|
|
8
|
+
</main>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
{{/if}}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SignInForm } from '@githat/nextjs';
|
|
2
|
+
|
|
3
|
+
export default function SignInPage() {
|
|
4
|
+
return (
|
|
5
|
+
<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}}>
|
|
6
|
+
<SignInForm signUpUrl="/sign-up" {{#if includeForgotPassword}}forgotPasswordUrl="/forgot-password"{{/if}} />
|
|
7
|
+
</main>
|
|
8
|
+
);
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SignUpForm } from '@githat/nextjs';
|
|
2
|
+
|
|
3
|
+
export default function SignUpPage() {
|
|
4
|
+
return (
|
|
5
|
+
<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}}>
|
|
6
|
+
<SignUpForm signInUrl="/sign-in" />
|
|
7
|
+
</main>
|
|
8
|
+
);
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{{#if includeEmailVerification}}
|
|
2
|
+
import { VerifyEmailStatus } from '@githat/nextjs';
|
|
3
|
+
|
|
4
|
+
export default function VerifyEmailPage() {
|
|
5
|
+
return (
|
|
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}}>
|
|
7
|
+
<VerifyEmailStatus afterVerifyUrl="/dashboard" />
|
|
8
|
+
</main>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
{{/if}}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{{#if includeDashboard}}
|
|
2
|
+
import { withAuth } from '@githat/nextjs/server';
|
|
3
|
+
|
|
4
|
+
async function DashboardLayout({ children }{{#if typescript}}: { children: React.ReactNode }{{/if}}) {
|
|
5
|
+
return <>{children}</>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default withAuth(DashboardLayout);
|
|
9
|
+
{{/if}}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{{#if includeDashboard}}
|
|
2
|
+
'use client';
|
|
3
|
+
import { useGitHat, UserButton } from '@githat/nextjs';
|
|
4
|
+
|
|
5
|
+
export default function DashboardPage() {
|
|
6
|
+
const { user, org } = useGitHat();
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div {{#if useTailwind}}className="p-8"{{else}}style=\{{ padding: '2rem' }}{{/if}}>
|
|
10
|
+
<div {{#if useTailwind}}className="flex justify-between items-center mb-8"{{else}}style=\{{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '2rem' }}{{/if}}>
|
|
11
|
+
<h1 {{#if useTailwind}}className="text-2xl font-bold"{{else}}style=\{{ fontSize: '1.5rem', fontWeight: 700 }}{{/if}}>Dashboard</h1>
|
|
12
|
+
<UserButton />
|
|
13
|
+
</div>
|
|
14
|
+
<div {{#if useTailwind}}className="grid gap-4 md:grid-cols-2"{{else}}style=\{{ display: 'grid', gap: '1rem' }}{{/if}}>
|
|
15
|
+
<div {{#if useTailwind}}className="p-4 rounded-lg border border-zinc-800 bg-zinc-900"{{else}}style=\{{ padding: '1rem', borderRadius: '0.5rem', border: '1px solid #27272a', background: '#18181b' }}{{/if}}>
|
|
16
|
+
<h2 {{#if useTailwind}}className="font-semibold mb-2"{{else}}style=\{{ fontWeight: 600, marginBottom: '0.5rem' }}{{/if}}>User</h2>
|
|
17
|
+
<p {{#if useTailwind}}className="text-zinc-400"{{else}}style=\{{ color: '#a1a1aa' }}{{/if}}>{user?.email}</p>
|
|
18
|
+
</div>
|
|
19
|
+
<div {{#if useTailwind}}className="p-4 rounded-lg border border-zinc-800 bg-zinc-900"{{else}}style=\{{ padding: '1rem', borderRadius: '0.5rem', border: '1px solid #27272a', background: '#18181b' }}{{/if}}>
|
|
20
|
+
<h2 {{#if useTailwind}}className="font-semibold mb-2"{{else}}style=\{{ fontWeight: 600, marginBottom: '0.5rem' }}{{/if}}>Organization</h2>
|
|
21
|
+
<p {{#if useTailwind}}className="text-zinc-400"{{else}}style=\{{ color: '#a1a1aa' }}{{/if}}>{org?.name || 'Personal'}</p>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
{{/if}}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{{#if useTailwind}}
|
|
2
|
+
@import "tailwindcss";
|
|
3
|
+
{{/if}}
|
|
4
|
+
|
|
5
|
+
* {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
margin: 0;
|
|
8
|
+
padding: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
body {
|
|
12
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
13
|
+
background: #09090b;
|
|
14
|
+
color: #fafafa;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
a {
|
|
18
|
+
color: inherit;
|
|
19
|
+
text-decoration: none;
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { GitHatProvider } from '@githat/nextjs';
|
|
2
|
+
import '@githat/nextjs/styles';
|
|
3
|
+
import './globals.css';
|
|
4
|
+
|
|
5
|
+
export const metadata = {
|
|
6
|
+
title: '{{businessName}}',
|
|
7
|
+
description: '{{description}}',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default function RootLayout({ children }{{#if typescript}}: { children: React.ReactNode }{{/if}}) {
|
|
11
|
+
return (
|
|
12
|
+
<html lang="en">
|
|
13
|
+
<body>
|
|
14
|
+
<GitHatProvider config=\{{
|
|
15
|
+
publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY || '',
|
|
16
|
+
signInUrl: '/sign-in',
|
|
17
|
+
signUpUrl: '/sign-up',
|
|
18
|
+
afterSignInUrl: '/dashboard',
|
|
19
|
+
afterSignOutUrl: '/',
|
|
20
|
+
}}>
|
|
21
|
+
{children}
|
|
22
|
+
</GitHatProvider>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SignInButton, SignUpButton } from '@githat/nextjs';
|
|
2
|
+
|
|
3
|
+
export default function Home() {
|
|
4
|
+
return (
|
|
5
|
+
<main {{#if useTailwind}}className="flex flex-col items-center justify-center min-h-screen gap-6 bg-[#09090b] text-[#fafafa]"{{else}}style=\{{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', gap: '1.5rem', background: '#09090b', color: '#fafafa' }}{{/if}}>
|
|
6
|
+
<h1 {{#if useTailwind}}className="text-4xl font-bold"{{else}}style=\{{ fontSize: '2.5rem', fontWeight: 700 }}{{/if}}>
|
|
7
|
+
Welcome to {{businessName}}
|
|
8
|
+
</h1>
|
|
9
|
+
<p {{#if useTailwind}}className="text-zinc-400 max-w-lg text-center"{{else}}style=\{{ color: '#a1a1aa', maxWidth: '32rem', textAlign: 'center' }}{{/if}}>
|
|
10
|
+
{{description}}
|
|
11
|
+
</p>
|
|
12
|
+
<div {{#if useTailwind}}className="flex gap-4"{{else}}style=\{{ display: 'flex', gap: '1rem' }}{{/if}}>
|
|
13
|
+
<SignInButton />
|
|
14
|
+
<SignUpButton />
|
|
15
|
+
</div>
|
|
16
|
+
</main>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NextConfig } from 'next';
|
|
2
|
+
|
|
3
|
+
const nextConfig: NextConfig = {
|
|
4
|
+
// Proxy API calls to the backend in development
|
|
5
|
+
async rewrites() {
|
|
6
|
+
return [
|
|
7
|
+
{
|
|
8
|
+
source: '/api/:path*',
|
|
9
|
+
destination: `${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001'}/:path*`,
|
|
10
|
+
},
|
|
11
|
+
];
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default nextConfig;
|