leedstack 3.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 (136) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +364 -0
  3. package/bin/create-stack.js +277 -0
  4. package/package.json +60 -0
  5. package/tools/templates/backend/go-echo/backend/.env.example +10 -0
  6. package/tools/templates/backend/go-echo/backend/cmd/server/main.go.ejs +57 -0
  7. package/tools/templates/backend/go-echo/backend/go.mod.ejs +10 -0
  8. package/tools/templates/backend/go-echo/backend/internal/handlers/example.go +15 -0
  9. package/tools/templates/backend/go-echo/backend/internal/handlers/health.go +13 -0
  10. package/tools/templates/backend/java-spring/backend/.env.example +10 -0
  11. package/tools/templates/backend/java-spring/backend/pom.xml.ejs +64 -0
  12. package/tools/templates/backend/java-spring/backend/src/main/java/com/app/Application.java.ejs +11 -0
  13. package/tools/templates/backend/java-spring/backend/src/main/java/com/app/config/SecurityConfig.java.ejs +64 -0
  14. package/tools/templates/backend/java-spring/backend/src/main/java/com/app/controller/ExampleController.java +19 -0
  15. package/tools/templates/backend/java-spring/backend/src/main/java/com/app/controller/HealthController.java +15 -0
  16. package/tools/templates/backend/java-spring/backend/src/main/resources/application.yml.ejs +20 -0
  17. package/tools/templates/backend/node-express/backend/.env.example +10 -0
  18. package/tools/templates/backend/node-express/backend/.eslintrc.json +21 -0
  19. package/tools/templates/backend/node-express/backend/.prettierrc +10 -0
  20. package/tools/templates/backend/node-express/backend/Dockerfile +52 -0
  21. package/tools/templates/backend/node-express/backend/README.md +68 -0
  22. package/tools/templates/backend/node-express/backend/package.json.ejs +37 -0
  23. package/tools/templates/backend/node-express/backend/src/index.ts.ejs +75 -0
  24. package/tools/templates/backend/node-express/backend/src/routes/health.ts +54 -0
  25. package/tools/templates/backend/node-express/backend/tsconfig.json +17 -0
  26. package/tools/templates/backend/python-fastapi/backend/.env.example +18 -0
  27. package/tools/templates/backend/python-fastapi/backend/README.md +73 -0
  28. package/tools/templates/backend/python-fastapi/backend/app/__init__.py +1 -0
  29. package/tools/templates/backend/python-fastapi/backend/app/main.py.ejs +68 -0
  30. package/tools/templates/backend/python-fastapi/backend/requirements.txt.ejs +22 -0
  31. package/tools/templates/base/.dockerignore +16 -0
  32. package/tools/templates/base/.env.example +31 -0
  33. package/tools/templates/base/.github/workflows/ci.yml.ejs +124 -0
  34. package/tools/templates/base/.github/workflows/deploy-separate.yml.example +144 -0
  35. package/tools/templates/base/.vscode/extensions.json +17 -0
  36. package/tools/templates/base/.vscode/settings.json +49 -0
  37. package/tools/templates/base/Makefile +98 -0
  38. package/tools/templates/base/README.md.ejs +118 -0
  39. package/tools/templates/base/docker-compose.yml.ejs +49 -0
  40. package/tools/templates/base/package.json.ejs +30 -0
  41. package/tools/templates/base/scripts/split-repos.sh +189 -0
  42. package/tools/templates/db/postgres/backend/java-spring/backend/pom.xml.ejs +81 -0
  43. package/tools/templates/db/postgres/backend/java-spring/backend/src/main/resources/application.yml.ejs +39 -0
  44. package/tools/templates/db/postgres/backend/java-spring/backend/src/main/resources/db/migration/V1__init.sql +17 -0
  45. package/tools/templates/db/postgres/backend/node-express/backend/.env.example +10 -0
  46. package/tools/templates/db/postgres/backend/node-express/backend/package.json.ejs +32 -0
  47. package/tools/templates/db/postgres/backend/node-express/backend/prisma/schema.prisma.ejs +39 -0
  48. package/tools/templates/frontend/angular/frontend/.env.example +9 -0
  49. package/tools/templates/frontend/angular/frontend/angular.json +66 -0
  50. package/tools/templates/frontend/angular/frontend/package.json.ejs +31 -0
  51. package/tools/templates/frontend/angular/frontend/src/app/app.component.ts +30 -0
  52. package/tools/templates/frontend/angular/frontend/src/app/app.routes.ts +18 -0
  53. package/tools/templates/frontend/angular/frontend/src/app/components/home.component.ts +24 -0
  54. package/tools/templates/frontend/angular/frontend/src/app/services/api.service.ts +48 -0
  55. package/tools/templates/frontend/angular/frontend/src/favicon.ico +1 -0
  56. package/tools/templates/frontend/angular/frontend/src/index.html +13 -0
  57. package/tools/templates/frontend/angular/frontend/src/main.ts +10 -0
  58. package/tools/templates/frontend/angular/frontend/src/styles.css +31 -0
  59. package/tools/templates/frontend/angular/frontend/tsconfig.app.json +9 -0
  60. package/tools/templates/frontend/angular/frontend/tsconfig.json +27 -0
  61. package/tools/templates/frontend/nextjs/frontend/.env.example +9 -0
  62. package/tools/templates/frontend/nextjs/frontend/next.config.js +37 -0
  63. package/tools/templates/frontend/nextjs/frontend/package.json.ejs +25 -0
  64. package/tools/templates/frontend/nextjs/frontend/src/app/globals.css +31 -0
  65. package/tools/templates/frontend/nextjs/frontend/src/app/layout.tsx +36 -0
  66. package/tools/templates/frontend/nextjs/frontend/src/app/page.tsx +19 -0
  67. package/tools/templates/frontend/nextjs/frontend/src/lib/api.ts +45 -0
  68. package/tools/templates/frontend/nextjs/frontend/tsconfig.json +27 -0
  69. package/tools/templates/frontend/react/frontend/.env.example +9 -0
  70. package/tools/templates/frontend/react/frontend/.eslintrc.json +32 -0
  71. package/tools/templates/frontend/react/frontend/.prettierrc +10 -0
  72. package/tools/templates/frontend/react/frontend/Dockerfile +37 -0
  73. package/tools/templates/frontend/react/frontend/README.md +54 -0
  74. package/tools/templates/frontend/react/frontend/index.html +13 -0
  75. package/tools/templates/frontend/react/frontend/nginx.conf +35 -0
  76. package/tools/templates/frontend/react/frontend/package.json.ejs +41 -0
  77. package/tools/templates/frontend/react/frontend/public/vite.svg +4 -0
  78. package/tools/templates/frontend/react/frontend/src/App.css +65 -0
  79. package/tools/templates/frontend/react/frontend/src/App.jsx +41 -0
  80. package/tools/templates/frontend/react/frontend/src/assets/react.svg +7 -0
  81. package/tools/templates/frontend/react/frontend/src/components/ErrorBoundary.jsx +62 -0
  82. package/tools/templates/frontend/react/frontend/src/components/Home.jsx +58 -0
  83. package/tools/templates/frontend/react/frontend/src/components/__tests__/Home.test.jsx +74 -0
  84. package/tools/templates/frontend/react/frontend/src/index.css +31 -0
  85. package/tools/templates/frontend/react/frontend/src/lib/api.js +42 -0
  86. package/tools/templates/frontend/react/frontend/src/lib/env.js +58 -0
  87. package/tools/templates/frontend/react/frontend/src/main.jsx +16 -0
  88. package/tools/templates/frontend/react/frontend/src/setupTests.js +8 -0
  89. package/tools/templates/frontend/react/frontend/vite.config.js +30 -0
  90. package/tools/templates/frontend/react/frontend/vitest.config.js +20 -0
  91. package/tools/templates/frontend/svelte/frontend/.env.example +9 -0
  92. package/tools/templates/frontend/svelte/frontend/package.json.ejs +21 -0
  93. package/tools/templates/frontend/svelte/frontend/src/app.html +12 -0
  94. package/tools/templates/frontend/svelte/frontend/src/lib/api.ts +45 -0
  95. package/tools/templates/frontend/svelte/frontend/src/routes/+layout.svelte +56 -0
  96. package/tools/templates/frontend/svelte/frontend/src/routes/+page.svelte +20 -0
  97. package/tools/templates/frontend/svelte/frontend/static/favicon.png +1 -0
  98. package/tools/templates/frontend/svelte/frontend/svelte.config.js +10 -0
  99. package/tools/templates/frontend/svelte/frontend/vite.config.js +9 -0
  100. package/tools/templates/frontend/vue/frontend/.env.example +9 -0
  101. package/tools/templates/frontend/vue/frontend/index.html +13 -0
  102. package/tools/templates/frontend/vue/frontend/package.json.ejs +20 -0
  103. package/tools/templates/frontend/vue/frontend/src/App.vue +60 -0
  104. package/tools/templates/frontend/vue/frontend/src/lib/api.js +42 -0
  105. package/tools/templates/frontend/vue/frontend/src/main.js +33 -0
  106. package/tools/templates/frontend/vue/frontend/src/views/ApiTest.vue +39 -0
  107. package/tools/templates/frontend/vue/frontend/src/views/Home.vue +30 -0
  108. package/tools/templates/frontend/vue/frontend/vite.config.js +9 -0
  109. package/tools/templates/modules/admin/backend/java-spring/backend/src/main/java/com/app/controller/AdminController.java +41 -0
  110. package/tools/templates/modules/admin/backend/java-spring/backend/src/main/java/com/app/entity/User.java +55 -0
  111. package/tools/templates/modules/admin/backend/java-spring/backend/src/main/java/com/app/repository/UserRepository.java +8 -0
  112. package/tools/templates/modules/admin/frontend/svelte/frontend/src/routes/dashboard/+page.svelte +93 -0
  113. package/tools/templates/modules/auth/backend/node-express/backend/src/middleware/auth.ts +42 -0
  114. package/tools/templates/modules/auth/frontend/svelte/frontend/src/hooks.client.ts +3 -0
  115. package/tools/templates/modules/auth/frontend/svelte/frontend/src/lib/auth.ts +104 -0
  116. package/tools/templates/modules/auth/frontend/svelte/frontend/src/routes/callback/+page.svelte +18 -0
  117. package/tools/templates/modules/auth/frontend/svelte/frontend/src/routes/login/+page.svelte +12 -0
  118. package/tools/templates/modules/chatbot/backend/node-express/backend/src/index.ts.ejs +69 -0
  119. package/tools/templates/modules/chatbot/backend/node-express/backend/src/routes/chatbot.ts.ejs +37 -0
  120. package/tools/templates/modules/chatbot/backend/node-express/backend/src/services/chatbotService.ts +124 -0
  121. package/tools/templates/modules/chatbot/backend/python-fastapi/backend/app/main.py.ejs +69 -0
  122. package/tools/templates/modules/chatbot/backend/python-fastapi/backend/app/routes/chatbot.py +38 -0
  123. package/tools/templates/modules/chatbot/backend/python-fastapi/backend/app/services/chatbot_service.py +123 -0
  124. package/tools/templates/modules/chatbot/backend/python-fastapi/backend/requirements.txt +1 -0
  125. package/tools/templates/modules/chatbot/frontend/react/frontend/src/App.jsx.ejs +74 -0
  126. package/tools/templates/modules/chatbot/frontend/react/frontend/src/components/Chatbot.css +198 -0
  127. package/tools/templates/modules/chatbot/frontend/react/frontend/src/components/Chatbot.jsx +113 -0
  128. package/tools/templates/modules/contact/backend/java-spring/backend/src/main/java/com/app/controller/ContactController.java +29 -0
  129. package/tools/templates/modules/contact/backend/java-spring/backend/src/main/java/com/app/entity/ContactMessage.java +66 -0
  130. package/tools/templates/modules/contact/backend/java-spring/backend/src/main/java/com/app/repository/ContactMessageRepository.java +8 -0
  131. package/tools/templates/modules/contact/backend/java-spring/backend/src/main/resources/db/migration/V2__contact.sql +7 -0
  132. package/tools/templates/modules/contact/frontend/svelte/frontend/src/routes/contact/+page.svelte +80 -0
  133. package/tools/templates/modules/payments/backend/java-spring/backend/src/main/java/com/app/controller/PaymentController.java +69 -0
  134. package/tools/templates/modules/payments/backend/node-express/backend/src/routes/payments.ts +30 -0
  135. package/tools/templates/modules/payments/backend/node-express/backend/src/routes/webhook.ts +36 -0
  136. package/tools/templates/modules/payments/frontend/svelte/frontend/src/lib/payments.ts +28 -0
@@ -0,0 +1,93 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { getToken, isAuthenticated } from '$lib/auth';
4
+ import { goto } from '$app/navigation';
5
+
6
+ let stats: any = null;
7
+ let loading = true;
8
+ let error = '';
9
+
10
+ onMount(async () => {
11
+ const auth = await new Promise<boolean>((resolve) => {
12
+ const unsubscribe = isAuthenticated.subscribe(value => {
13
+ resolve(value);
14
+ unsubscribe();
15
+ });
16
+ });
17
+
18
+ if (!auth) {
19
+ goto('/login');
20
+ return;
21
+ }
22
+
23
+ try {
24
+ const API_BASE = import.meta.env.PUBLIC_API_BASE || 'http://localhost:8080';
25
+ const token = await getToken();
26
+
27
+ const response = await fetch(`${API_BASE}/api/admin/stats`, {
28
+ headers: {
29
+ 'Authorization': `Bearer ${token}`
30
+ }
31
+ });
32
+
33
+ if (response.ok) {
34
+ stats = await response.json();
35
+ } else {
36
+ error = 'Failed to load stats';
37
+ }
38
+ } catch (err) {
39
+ error = 'Error loading stats';
40
+ } finally {
41
+ loading = false;
42
+ }
43
+ });
44
+ </script>
45
+
46
+ <h1>Admin Dashboard</h1>
47
+
48
+ {#if loading}
49
+ <p>Loading...</p>
50
+ {:else if error}
51
+ <p style="color: red;">{error}</p>
52
+ {:else if stats}
53
+ <div class="stats">
54
+ <div class="stat-card">
55
+ <h2>Users</h2>
56
+ <p class="stat-value">{stats.users}</p>
57
+ </div>
58
+
59
+ <% if (modules.contact) { -%>
60
+ <div class="stat-card">
61
+ <h2>Contact Messages</h2>
62
+ <p class="stat-value">{stats.contacts}</p>
63
+ </div>
64
+ <% } -%>
65
+ </div>
66
+ {/if}
67
+
68
+ <style>
69
+ .stats {
70
+ display: grid;
71
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
72
+ gap: 1rem;
73
+ margin-top: 2rem;
74
+ }
75
+
76
+ .stat-card {
77
+ padding: 1.5rem;
78
+ background: #f5f5f5;
79
+ border-radius: 8px;
80
+ }
81
+
82
+ .stat-card h2 {
83
+ margin: 0 0 0.5rem 0;
84
+ font-size: 1rem;
85
+ color: #666;
86
+ }
87
+
88
+ .stat-value {
89
+ font-size: 2.5rem;
90
+ font-weight: bold;
91
+ margin: 0;
92
+ }
93
+ </style>
@@ -0,0 +1,42 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { createRemoteJWKSet, jwtVerify } from 'jose';
3
+
4
+ const JWKS = createRemoteJWKSet(new URL(`https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`));
5
+ const ISSUER = `https://${process.env.AUTH0_DOMAIN}/`;
6
+ const AUDIENCE = process.env.AUTH0_AUDIENCE;
7
+
8
+ export async function authMiddleware(req: Request, res: Response, next: NextFunction) {
9
+ const authHeader = req.headers.authorization;
10
+
11
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
12
+ return res.status(401).json({ error: 'Unauthorized' });
13
+ }
14
+
15
+ const token = authHeader.substring(7);
16
+
17
+ try {
18
+ const { payload } = await jwtVerify(token, JWKS, {
19
+ issuer: ISSUER,
20
+ audience: AUDIENCE
21
+ });
22
+
23
+ (req as any).user = payload;
24
+ next();
25
+ } catch (err) {
26
+ console.error('JWT verification failed:', err);
27
+ return res.status(401).json({ error: 'Invalid token' });
28
+ }
29
+ }
30
+
31
+ export function requireScope(scope: string) {
32
+ return (req: Request, res: Response, next: NextFunction) => {
33
+ const user = (req as any).user;
34
+ const scopes = user?.scope?.split(' ') || [];
35
+
36
+ if (!scopes.includes(scope)) {
37
+ return res.status(403).json({ error: 'Forbidden' });
38
+ }
39
+
40
+ next();
41
+ };
42
+ }
@@ -0,0 +1,3 @@
1
+ import { initAuth } from '$lib/auth';
2
+
3
+ initAuth();
@@ -0,0 +1,104 @@
1
+ import { Auth0Client } from '@auth0/auth0-spa-js';
2
+ import { writable } from 'svelte/store';
3
+
4
+ const domain = import.meta.env.PUBLIC_AUTH0_DOMAIN || '';
5
+ const clientId = import.meta.env.PUBLIC_AUTH0_CLIENT_ID || '';
6
+ const audience = import.meta.env.PUBLIC_AUTH0_AUDIENCE || '';
7
+
8
+ let auth0Client: Auth0Client;
9
+
10
+ export const isAuthenticated = writable(false);
11
+ export const user = writable(null);
12
+ export const token = writable('');
13
+
14
+ async function createClient() {
15
+ auth0Client = new Auth0Client({
16
+ domain,
17
+ clientId,
18
+ authorizationParams: {
19
+ audience,
20
+ redirect_uri: window.location.origin + '/callback'
21
+ },
22
+ cacheLocation: 'localstorage'
23
+ });
24
+
25
+ return auth0Client;
26
+ }
27
+
28
+ export async function initAuth() {
29
+ if (!auth0Client) {
30
+ await createClient();
31
+ }
32
+
33
+ try {
34
+ const isAuth = await auth0Client.isAuthenticated();
35
+ isAuthenticated.set(isAuth);
36
+
37
+ if (isAuth) {
38
+ const userInfo = await auth0Client.getUser();
39
+ user.set(userInfo);
40
+
41
+ const accessToken = await auth0Client.getTokenSilently();
42
+ token.set(accessToken);
43
+ }
44
+ } catch (err) {
45
+ console.error('Auth init error:', err);
46
+ }
47
+ }
48
+
49
+ export async function login() {
50
+ if (!auth0Client) {
51
+ await createClient();
52
+ }
53
+ await auth0Client.loginWithRedirect();
54
+ }
55
+
56
+ export async function handleCallback() {
57
+ if (!auth0Client) {
58
+ await createClient();
59
+ }
60
+
61
+ try {
62
+ await auth0Client.handleRedirectCallback();
63
+ const userInfo = await auth0Client.getUser();
64
+ const accessToken = await auth0Client.getTokenSilently();
65
+
66
+ isAuthenticated.set(true);
67
+ user.set(userInfo);
68
+ token.set(accessToken);
69
+
70
+ return true;
71
+ } catch (err) {
72
+ console.error('Callback error:', err);
73
+ return false;
74
+ }
75
+ }
76
+
77
+ export async function logout() {
78
+ if (!auth0Client) {
79
+ await createClient();
80
+ }
81
+
82
+ await auth0Client.logout({
83
+ logoutParams: {
84
+ returnTo: window.location.origin
85
+ }
86
+ });
87
+
88
+ isAuthenticated.set(false);
89
+ user.set(null);
90
+ token.set('');
91
+ }
92
+
93
+ export async function getToken() {
94
+ if (!auth0Client) {
95
+ await createClient();
96
+ }
97
+
98
+ try {
99
+ return await auth0Client.getTokenSilently();
100
+ } catch (err) {
101
+ console.error('Get token error:', err);
102
+ return '';
103
+ }
104
+ }
@@ -0,0 +1,18 @@
1
+ <script>
2
+ import { onMount } from 'svelte';
3
+ import { goto } from '$app/navigation';
4
+ import { handleCallback } from '$lib/auth';
5
+
6
+ onMount(async () => {
7
+ const success = await handleCallback();
8
+ if (success) {
9
+ goto('/');
10
+ } else {
11
+ goto('/login');
12
+ }
13
+ });
14
+ </script>
15
+
16
+ <div>
17
+ <p>Processing login...</p>
18
+ </div>
@@ -0,0 +1,12 @@
1
+ <script>
2
+ import { onMount } from 'svelte';
3
+ import { login } from '$lib/auth';
4
+
5
+ onMount(() => {
6
+ login();
7
+ });
8
+ </script>
9
+
10
+ <div>
11
+ <p>Redirecting to login...</p>
12
+ </div>
@@ -0,0 +1,69 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import dotenv from 'dotenv';
4
+ <% if (modules.auth) { -%>
5
+ import authRoutes from './routes/auth';
6
+ <% } -%>
7
+ <% if (modules.payments) { -%>
8
+ import stripeRoutes from './routes/stripe';
9
+ <% } -%>
10
+ <% if (modules.contact) { -%>
11
+ import contactRoutes from './routes/contact';
12
+ <% } -%>
13
+ <% if (modules.admin) { -%>
14
+ import adminRoutes from './routes/admin';
15
+ <% } -%>
16
+ import chatbotRoutes from './routes/chatbot';
17
+
18
+ dotenv.config();
19
+
20
+ const app = express();
21
+ const PORT = process.env.PORT || 8080;
22
+
23
+ // CORS configuration - allow only the configured frontend URL
24
+ const allowedOrigins = [
25
+ process.env.FRONTEND_URL
26
+ ].filter(Boolean);
27
+
28
+ app.use(cors({
29
+ origin: (origin, callback) => {
30
+ if (!origin) return callback(null, true);
31
+ if (allowedOrigins.indexOf(origin) !== -1) {
32
+ callback(null, true);
33
+ } else {
34
+ callback(new Error('Not allowed by CORS'));
35
+ }
36
+ },
37
+ credentials: true
38
+ }));
39
+
40
+ app.use(express.json());
41
+
42
+ // Example route
43
+ app.get('/api/example', (req, res) => {
44
+ res.json({
45
+ message: 'Hello from node-express backend!',
46
+ timestamp: new Date().toISOString()
47
+ });
48
+ });
49
+
50
+ // Module routes
51
+ <% if (modules.auth) { -%>
52
+ app.use('/api/auth', authRoutes);
53
+ <% } -%>
54
+ <% if (modules.payments) { -%>
55
+ app.use('/stripe', stripeRoutes);
56
+ <% } -%>
57
+ <% if (modules.contact) { -%>
58
+ app.use('/api/contact', contactRoutes);
59
+ <% } -%>
60
+ <% if (modules.admin) { -%>
61
+ app.use('/api/admin', adminRoutes);
62
+ <% } -%>
63
+ app.use('/api/chatbot', chatbotRoutes);
64
+
65
+ app.listen(PORT, () => {
66
+ console.log(`✅ Server running on http://localhost:${PORT}`);
67
+ console.log(`📡 API endpoint: http://localhost:${PORT}/api/example`);
68
+ console.log(`💬 Chatbot endpoint: http://localhost:${PORT}/api/chatbot`);
69
+ });
@@ -0,0 +1,37 @@
1
+ import { Router, Request, Response } from 'express';
2
+ import { ChatbotService } from '../services/chatbotService';
3
+
4
+ const router = Router();
5
+ const chatbotService = new ChatbotService();
6
+
7
+ interface ChatMessage {
8
+ role: 'user' | 'assistant';
9
+ content: string;
10
+ }
11
+
12
+ interface ChatRequest {
13
+ message: string;
14
+ history?: ChatMessage[];
15
+ }
16
+
17
+ router.post('/', async (req: Request, res: Response) => {
18
+ try {
19
+ const { message, history = [] }: ChatRequest = req.body;
20
+
21
+ if (!message || typeof message !== 'string') {
22
+ return res.status(400).json({ error: 'Message is required' });
23
+ }
24
+
25
+ const response = await chatbotService.generateResponse(message, history);
26
+
27
+ res.json({ message: response });
28
+ } catch (error) {
29
+ console.error('Chatbot error:', error);
30
+ res.status(500).json({
31
+ error: 'Failed to generate response',
32
+ message: 'Sorry, I encountered an error. Please try again.'
33
+ });
34
+ }
35
+ });
36
+
37
+ export default router;
@@ -0,0 +1,124 @@
1
+ interface ChatMessage {
2
+ role: 'user' | 'assistant';
3
+ content: string;
4
+ }
5
+
6
+ export class ChatbotService {
7
+ private openaiApiKey: string | undefined;
8
+ private anthropicApiKey: string | undefined;
9
+
10
+ constructor() {
11
+ this.openaiApiKey = process.env.OPENAI_API_KEY;
12
+ this.anthropicApiKey = process.env.ANTHROPIC_API_KEY;
13
+ }
14
+
15
+ async generateResponse(message: string, history: ChatMessage[]): Promise<string> {
16
+ // Try LLM providers in order of preference
17
+ if (this.anthropicApiKey) {
18
+ return this.generateAnthropicResponse(message, history);
19
+ }
20
+
21
+ if (this.openaiApiKey) {
22
+ return this.generateOpenAIResponse(message, history);
23
+ }
24
+
25
+ // Fallback to basic rule-based responses
26
+ return this.generateBasicResponse(message);
27
+ }
28
+
29
+ private async generateOpenAIResponse(message: string, history: ChatMessage[]): Promise<string> {
30
+ try {
31
+ const response = await fetch('https://api.openai.com/v1/chat/completions', {
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ 'Authorization': `Bearer ${this.openaiApiKey}`
36
+ },
37
+ body: JSON.stringify({
38
+ model: 'gpt-4o-mini',
39
+ messages: [
40
+ { role: 'system', content: 'You are a helpful assistant for this web application. Be concise and friendly.' },
41
+ ...history.map(msg => ({ role: msg.role, content: msg.content })),
42
+ { role: 'user', content: message }
43
+ ],
44
+ max_tokens: 500,
45
+ temperature: 0.7
46
+ })
47
+ });
48
+
49
+ if (!response.ok) {
50
+ throw new Error(`OpenAI API error: ${response.statusText}`);
51
+ }
52
+
53
+ const data = await response.json();
54
+ return data.choices[0]?.message?.content || this.generateBasicResponse(message);
55
+ } catch (error) {
56
+ console.error('OpenAI error:', error);
57
+ return this.generateBasicResponse(message);
58
+ }
59
+ }
60
+
61
+ private async generateAnthropicResponse(message: string, history: ChatMessage[]): Promise<string> {
62
+ try {
63
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
64
+ method: 'POST',
65
+ headers: {
66
+ 'Content-Type': 'application/json',
67
+ 'x-api-key': this.anthropicApiKey!,
68
+ 'anthropic-version': '2023-06-01'
69
+ },
70
+ body: JSON.stringify({
71
+ model: 'claude-3-5-haiku-20241022',
72
+ max_tokens: 500,
73
+ system: 'You are a helpful assistant for this web application. Be concise and friendly.',
74
+ messages: [
75
+ ...history.map(msg => ({ role: msg.role, content: msg.content })),
76
+ { role: 'user', content: message }
77
+ ]
78
+ })
79
+ });
80
+
81
+ if (!response.ok) {
82
+ throw new Error(`Anthropic API error: ${response.statusText}`);
83
+ }
84
+
85
+ const data = await response.json();
86
+ return data.content[0]?.text || this.generateBasicResponse(message);
87
+ } catch (error) {
88
+ console.error('Anthropic error:', error);
89
+ return this.generateBasicResponse(message);
90
+ }
91
+ }
92
+
93
+ private generateBasicResponse(message: string): string {
94
+ const lowerMessage = message.toLowerCase();
95
+
96
+ // Greeting patterns
97
+ if (/(hello|hi|hey|greetings)/i.test(lowerMessage)) {
98
+ return "Hello! I'm here to help. You can ask me questions about this application or configure me with an AI API key for more advanced responses.";
99
+ }
100
+
101
+ // Help patterns
102
+ if (/(help|what can you do|how do you work)/i.test(lowerMessage)) {
103
+ return "I can answer questions and assist you with this application. Currently, I'm running in basic mode. To unlock AI-powered responses, add OPENAI_API_KEY or ANTHROPIC_API_KEY to your backend .env file.";
104
+ }
105
+
106
+ // Feature questions
107
+ if (/(feature|what|how)/i.test(lowerMessage)) {
108
+ return "This is a full-stack web application with authentication, database integration, and more. Feel free to explore the codebase or ask me specific questions!";
109
+ }
110
+
111
+ // API/Configuration questions
112
+ if (/(api|configure|setup|install)/i.test(lowerMessage)) {
113
+ return "To configure AI responses:\n\n1. Get an API key from OpenAI or Anthropic\n2. Add it to backend/.env:\n OPENAI_API_KEY=sk-...\n or\n ANTHROPIC_API_KEY=sk-ant-...\n3. Restart the backend\n\nThen I'll be powered by AI!";
114
+ }
115
+
116
+ // Thanks/Goodbye
117
+ if (/(thank|thanks|bye|goodbye)/i.test(lowerMessage)) {
118
+ return "You're welcome! Let me know if you need anything else.";
119
+ }
120
+
121
+ // Default fallback
122
+ return "I received your message! I'm currently in basic mode with limited responses. For more intelligent conversations, configure an AI API key (OpenAI or Anthropic) in your backend environment variables.";
123
+ }
124
+ }
@@ -0,0 +1,69 @@
1
+ from fastapi import FastAPI
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from datetime import datetime
4
+ import os
5
+ <% if (modules.auth) { -%>
6
+ from app.routes import auth
7
+ <% } -%>
8
+ <% if (modules.payments) { -%>
9
+ from app.routes import stripe
10
+ <% } -%>
11
+ <% if (modules.contact) { -%>
12
+ from app.routes import contact
13
+ <% } -%>
14
+ <% if (modules.admin) { -%>
15
+ from app.routes import admin
16
+ <% } -%>
17
+ from app.routes import chatbot
18
+
19
+ app = FastAPI(
20
+ title="<%= AppName %> API",
21
+ description="API for <%= appName %>",
22
+ version="1.0.0"
23
+ )
24
+
25
+ # CORS configuration
26
+ allowed_origins = [
27
+ "http://localhost:5173", # Vite default
28
+ "http://localhost:3000", # Next.js default
29
+ os.getenv("FRONTEND_URL", "")
30
+ ]
31
+ allowed_origins = [origin for origin in allowed_origins if origin]
32
+
33
+ app.add_middleware(
34
+ CORSMiddleware,
35
+ allow_origins=allowed_origins if os.getenv("NODE_ENV") != "development" else ["*"],
36
+ allow_credentials=True,
37
+ allow_methods=["*"],
38
+ allow_headers=["*"],
39
+ )
40
+
41
+ @app.get("/api/example")
42
+ async def example():
43
+ """Example API endpoint"""
44
+ return {
45
+ "message": "Hello from python-fastapi backend!",
46
+ "timestamp": datetime.utcnow().isoformat()
47
+ }
48
+
49
+ # Module routes
50
+ <% if (modules.auth) { -%>
51
+ app.include_router(auth.router, prefix="/api/auth", tags=["auth"])
52
+ <% } -%>
53
+ <% if (modules.payments) { -%>
54
+ app.include_router(stripe.router, prefix="/stripe", tags=["stripe"])
55
+ <% } -%>
56
+ <% if (modules.contact) { -%>
57
+ app.include_router(contact.router, prefix="/api/contact", tags=["contact"])
58
+ <% } -%>
59
+ <% if (modules.admin) { -%>
60
+ app.include_router(admin.router, prefix="/api/admin", tags=["admin"])
61
+ <% } -%>
62
+ app.include_router(chatbot.router, prefix="/api/chatbot", tags=["chatbot"])
63
+
64
+ @app.on_event("startup")
65
+ async def startup_event():
66
+ print("✅ Server running on http://localhost:8080")
67
+ print("📡 API endpoint: http://localhost:8080/api/example")
68
+ print("💬 Chatbot endpoint: http://localhost:8080/api/chatbot")
69
+ print("📚 API docs: http://localhost:8080/docs")
@@ -0,0 +1,38 @@
1
+ from fastapi import APIRouter, HTTPException
2
+ from pydantic import BaseModel
3
+ from typing import List, Optional
4
+ from app.services.chatbot_service import ChatbotService
5
+
6
+ router = APIRouter()
7
+ chatbot_service = ChatbotService()
8
+
9
+ class ChatMessage(BaseModel):
10
+ role: str
11
+ content: str
12
+
13
+ class ChatRequest(BaseModel):
14
+ message: str
15
+ history: Optional[List[ChatMessage]] = []
16
+
17
+ @router.post("")
18
+ async def chat(request: ChatRequest):
19
+ """
20
+ Chat endpoint that works with or without AI API keys.
21
+ Falls back to basic responses if no API key is configured.
22
+ """
23
+ try:
24
+ if not request.message or not isinstance(request.message, str):
25
+ raise HTTPException(status_code=400, detail="Message is required")
26
+
27
+ response = await chatbot_service.generate_response(
28
+ request.message,
29
+ request.history
30
+ )
31
+
32
+ return {"message": response}
33
+ except Exception as e:
34
+ print(f"Chatbot error: {e}")
35
+ raise HTTPException(
36
+ status_code=500,
37
+ detail="Sorry, I encountered an error. Please try again."
38
+ )