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.
- package/LICENSE +21 -0
- package/README.md +364 -0
- package/bin/create-stack.js +277 -0
- package/package.json +60 -0
- package/tools/templates/backend/go-echo/backend/.env.example +10 -0
- package/tools/templates/backend/go-echo/backend/cmd/server/main.go.ejs +57 -0
- package/tools/templates/backend/go-echo/backend/go.mod.ejs +10 -0
- package/tools/templates/backend/go-echo/backend/internal/handlers/example.go +15 -0
- package/tools/templates/backend/go-echo/backend/internal/handlers/health.go +13 -0
- package/tools/templates/backend/java-spring/backend/.env.example +10 -0
- package/tools/templates/backend/java-spring/backend/pom.xml.ejs +64 -0
- package/tools/templates/backend/java-spring/backend/src/main/java/com/app/Application.java.ejs +11 -0
- package/tools/templates/backend/java-spring/backend/src/main/java/com/app/config/SecurityConfig.java.ejs +64 -0
- package/tools/templates/backend/java-spring/backend/src/main/java/com/app/controller/ExampleController.java +19 -0
- package/tools/templates/backend/java-spring/backend/src/main/java/com/app/controller/HealthController.java +15 -0
- package/tools/templates/backend/java-spring/backend/src/main/resources/application.yml.ejs +20 -0
- package/tools/templates/backend/node-express/backend/.env.example +10 -0
- package/tools/templates/backend/node-express/backend/.eslintrc.json +21 -0
- package/tools/templates/backend/node-express/backend/.prettierrc +10 -0
- package/tools/templates/backend/node-express/backend/Dockerfile +52 -0
- package/tools/templates/backend/node-express/backend/README.md +68 -0
- package/tools/templates/backend/node-express/backend/package.json.ejs +37 -0
- package/tools/templates/backend/node-express/backend/src/index.ts.ejs +75 -0
- package/tools/templates/backend/node-express/backend/src/routes/health.ts +54 -0
- package/tools/templates/backend/node-express/backend/tsconfig.json +17 -0
- package/tools/templates/backend/python-fastapi/backend/.env.example +18 -0
- package/tools/templates/backend/python-fastapi/backend/README.md +73 -0
- package/tools/templates/backend/python-fastapi/backend/app/__init__.py +1 -0
- package/tools/templates/backend/python-fastapi/backend/app/main.py.ejs +68 -0
- package/tools/templates/backend/python-fastapi/backend/requirements.txt.ejs +22 -0
- package/tools/templates/base/.dockerignore +16 -0
- package/tools/templates/base/.env.example +31 -0
- package/tools/templates/base/.github/workflows/ci.yml.ejs +124 -0
- package/tools/templates/base/.github/workflows/deploy-separate.yml.example +144 -0
- package/tools/templates/base/.vscode/extensions.json +17 -0
- package/tools/templates/base/.vscode/settings.json +49 -0
- package/tools/templates/base/Makefile +98 -0
- package/tools/templates/base/README.md.ejs +118 -0
- package/tools/templates/base/docker-compose.yml.ejs +49 -0
- package/tools/templates/base/package.json.ejs +30 -0
- package/tools/templates/base/scripts/split-repos.sh +189 -0
- package/tools/templates/db/postgres/backend/java-spring/backend/pom.xml.ejs +81 -0
- package/tools/templates/db/postgres/backend/java-spring/backend/src/main/resources/application.yml.ejs +39 -0
- package/tools/templates/db/postgres/backend/java-spring/backend/src/main/resources/db/migration/V1__init.sql +17 -0
- package/tools/templates/db/postgres/backend/node-express/backend/.env.example +10 -0
- package/tools/templates/db/postgres/backend/node-express/backend/package.json.ejs +32 -0
- package/tools/templates/db/postgres/backend/node-express/backend/prisma/schema.prisma.ejs +39 -0
- package/tools/templates/frontend/angular/frontend/.env.example +9 -0
- package/tools/templates/frontend/angular/frontend/angular.json +66 -0
- package/tools/templates/frontend/angular/frontend/package.json.ejs +31 -0
- package/tools/templates/frontend/angular/frontend/src/app/app.component.ts +30 -0
- package/tools/templates/frontend/angular/frontend/src/app/app.routes.ts +18 -0
- package/tools/templates/frontend/angular/frontend/src/app/components/home.component.ts +24 -0
- package/tools/templates/frontend/angular/frontend/src/app/services/api.service.ts +48 -0
- package/tools/templates/frontend/angular/frontend/src/favicon.ico +1 -0
- package/tools/templates/frontend/angular/frontend/src/index.html +13 -0
- package/tools/templates/frontend/angular/frontend/src/main.ts +10 -0
- package/tools/templates/frontend/angular/frontend/src/styles.css +31 -0
- package/tools/templates/frontend/angular/frontend/tsconfig.app.json +9 -0
- package/tools/templates/frontend/angular/frontend/tsconfig.json +27 -0
- package/tools/templates/frontend/nextjs/frontend/.env.example +9 -0
- package/tools/templates/frontend/nextjs/frontend/next.config.js +37 -0
- package/tools/templates/frontend/nextjs/frontend/package.json.ejs +25 -0
- package/tools/templates/frontend/nextjs/frontend/src/app/globals.css +31 -0
- package/tools/templates/frontend/nextjs/frontend/src/app/layout.tsx +36 -0
- package/tools/templates/frontend/nextjs/frontend/src/app/page.tsx +19 -0
- package/tools/templates/frontend/nextjs/frontend/src/lib/api.ts +45 -0
- package/tools/templates/frontend/nextjs/frontend/tsconfig.json +27 -0
- package/tools/templates/frontend/react/frontend/.env.example +9 -0
- package/tools/templates/frontend/react/frontend/.eslintrc.json +32 -0
- package/tools/templates/frontend/react/frontend/.prettierrc +10 -0
- package/tools/templates/frontend/react/frontend/Dockerfile +37 -0
- package/tools/templates/frontend/react/frontend/README.md +54 -0
- package/tools/templates/frontend/react/frontend/index.html +13 -0
- package/tools/templates/frontend/react/frontend/nginx.conf +35 -0
- package/tools/templates/frontend/react/frontend/package.json.ejs +41 -0
- package/tools/templates/frontend/react/frontend/public/vite.svg +4 -0
- package/tools/templates/frontend/react/frontend/src/App.css +65 -0
- package/tools/templates/frontend/react/frontend/src/App.jsx +41 -0
- package/tools/templates/frontend/react/frontend/src/assets/react.svg +7 -0
- package/tools/templates/frontend/react/frontend/src/components/ErrorBoundary.jsx +62 -0
- package/tools/templates/frontend/react/frontend/src/components/Home.jsx +58 -0
- package/tools/templates/frontend/react/frontend/src/components/__tests__/Home.test.jsx +74 -0
- package/tools/templates/frontend/react/frontend/src/index.css +31 -0
- package/tools/templates/frontend/react/frontend/src/lib/api.js +42 -0
- package/tools/templates/frontend/react/frontend/src/lib/env.js +58 -0
- package/tools/templates/frontend/react/frontend/src/main.jsx +16 -0
- package/tools/templates/frontend/react/frontend/src/setupTests.js +8 -0
- package/tools/templates/frontend/react/frontend/vite.config.js +30 -0
- package/tools/templates/frontend/react/frontend/vitest.config.js +20 -0
- package/tools/templates/frontend/svelte/frontend/.env.example +9 -0
- package/tools/templates/frontend/svelte/frontend/package.json.ejs +21 -0
- package/tools/templates/frontend/svelte/frontend/src/app.html +12 -0
- package/tools/templates/frontend/svelte/frontend/src/lib/api.ts +45 -0
- package/tools/templates/frontend/svelte/frontend/src/routes/+layout.svelte +56 -0
- package/tools/templates/frontend/svelte/frontend/src/routes/+page.svelte +20 -0
- package/tools/templates/frontend/svelte/frontend/static/favicon.png +1 -0
- package/tools/templates/frontend/svelte/frontend/svelte.config.js +10 -0
- package/tools/templates/frontend/svelte/frontend/vite.config.js +9 -0
- package/tools/templates/frontend/vue/frontend/.env.example +9 -0
- package/tools/templates/frontend/vue/frontend/index.html +13 -0
- package/tools/templates/frontend/vue/frontend/package.json.ejs +20 -0
- package/tools/templates/frontend/vue/frontend/src/App.vue +60 -0
- package/tools/templates/frontend/vue/frontend/src/lib/api.js +42 -0
- package/tools/templates/frontend/vue/frontend/src/main.js +33 -0
- package/tools/templates/frontend/vue/frontend/src/views/ApiTest.vue +39 -0
- package/tools/templates/frontend/vue/frontend/src/views/Home.vue +30 -0
- package/tools/templates/frontend/vue/frontend/vite.config.js +9 -0
- package/tools/templates/modules/admin/backend/java-spring/backend/src/main/java/com/app/controller/AdminController.java +41 -0
- package/tools/templates/modules/admin/backend/java-spring/backend/src/main/java/com/app/entity/User.java +55 -0
- package/tools/templates/modules/admin/backend/java-spring/backend/src/main/java/com/app/repository/UserRepository.java +8 -0
- package/tools/templates/modules/admin/frontend/svelte/frontend/src/routes/dashboard/+page.svelte +93 -0
- package/tools/templates/modules/auth/backend/node-express/backend/src/middleware/auth.ts +42 -0
- package/tools/templates/modules/auth/frontend/svelte/frontend/src/hooks.client.ts +3 -0
- package/tools/templates/modules/auth/frontend/svelte/frontend/src/lib/auth.ts +104 -0
- package/tools/templates/modules/auth/frontend/svelte/frontend/src/routes/callback/+page.svelte +18 -0
- package/tools/templates/modules/auth/frontend/svelte/frontend/src/routes/login/+page.svelte +12 -0
- package/tools/templates/modules/chatbot/backend/node-express/backend/src/index.ts.ejs +69 -0
- package/tools/templates/modules/chatbot/backend/node-express/backend/src/routes/chatbot.ts.ejs +37 -0
- package/tools/templates/modules/chatbot/backend/node-express/backend/src/services/chatbotService.ts +124 -0
- package/tools/templates/modules/chatbot/backend/python-fastapi/backend/app/main.py.ejs +69 -0
- package/tools/templates/modules/chatbot/backend/python-fastapi/backend/app/routes/chatbot.py +38 -0
- package/tools/templates/modules/chatbot/backend/python-fastapi/backend/app/services/chatbot_service.py +123 -0
- package/tools/templates/modules/chatbot/backend/python-fastapi/backend/requirements.txt +1 -0
- package/tools/templates/modules/chatbot/frontend/react/frontend/src/App.jsx.ejs +74 -0
- package/tools/templates/modules/chatbot/frontend/react/frontend/src/components/Chatbot.css +198 -0
- package/tools/templates/modules/chatbot/frontend/react/frontend/src/components/Chatbot.jsx +113 -0
- package/tools/templates/modules/contact/backend/java-spring/backend/src/main/java/com/app/controller/ContactController.java +29 -0
- package/tools/templates/modules/contact/backend/java-spring/backend/src/main/java/com/app/entity/ContactMessage.java +66 -0
- package/tools/templates/modules/contact/backend/java-spring/backend/src/main/java/com/app/repository/ContactMessageRepository.java +8 -0
- package/tools/templates/modules/contact/backend/java-spring/backend/src/main/resources/db/migration/V2__contact.sql +7 -0
- package/tools/templates/modules/contact/frontend/svelte/frontend/src/routes/contact/+page.svelte +80 -0
- package/tools/templates/modules/payments/backend/java-spring/backend/src/main/java/com/app/controller/PaymentController.java +69 -0
- package/tools/templates/modules/payments/backend/node-express/backend/src/routes/payments.ts +30 -0
- package/tools/templates/modules/payments/backend/node-express/backend/src/routes/webhook.ts +36 -0
- package/tools/templates/modules/payments/frontend/svelte/frontend/src/lib/payments.ts +28 -0
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "leedstack",
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "One-command scaffolder for full-stack web applications with 60+ stack combinations (React/Vue/Next.js/Svelte/Angular + Python/Node/Java/Go + Postgres/MySQL/MongoDB)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"leedstack": "./bin/create-stack.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"tools"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=20.0.0"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"scaffolder",
|
|
18
|
+
"scaffold",
|
|
19
|
+
"generator",
|
|
20
|
+
"full-stack",
|
|
21
|
+
"cli",
|
|
22
|
+
"template",
|
|
23
|
+
"boilerplate",
|
|
24
|
+
"react",
|
|
25
|
+
"vue",
|
|
26
|
+
"vuejs",
|
|
27
|
+
"nextjs",
|
|
28
|
+
"next.js",
|
|
29
|
+
"svelte",
|
|
30
|
+
"angular",
|
|
31
|
+
"python",
|
|
32
|
+
"fastapi",
|
|
33
|
+
"spring-boot",
|
|
34
|
+
"express",
|
|
35
|
+
"nodejs",
|
|
36
|
+
"go",
|
|
37
|
+
"postgres",
|
|
38
|
+
"postgresql",
|
|
39
|
+
"mysql",
|
|
40
|
+
"mongodb",
|
|
41
|
+
"auth0",
|
|
42
|
+
"stripe",
|
|
43
|
+
"typescript"
|
|
44
|
+
],
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/CTLeed/leedstack.git"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/CTLeed/leedstack/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/CTLeed/leedstack#readme",
|
|
53
|
+
"author": "Colby Leed",
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"commander": "^12.0.0",
|
|
57
|
+
"fs-extra": "^11.2.0",
|
|
58
|
+
"ejs": "^3.1.9"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
APP_PORT=8080
|
|
2
|
+
FRONTEND_URL=http://localhost:<%= frontendPort %>
|
|
3
|
+
<% if (modules.auth) { -%>
|
|
4
|
+
AUTH0_DOMAIN=your-tenant.us.auth0.com
|
|
5
|
+
AUTH0_AUDIENCE=https://api.<%= appSlug %>
|
|
6
|
+
<% } -%>
|
|
7
|
+
<% if (modules.payments) { -%>
|
|
8
|
+
STRIPE_SECRET_KEY=sk_test_xxx
|
|
9
|
+
STRIPE_WEBHOOK_SECRET=whsec_xxx
|
|
10
|
+
<% } -%>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"<%= appSlug %>-backend/internal/handlers"
|
|
5
|
+
"os"
|
|
6
|
+
|
|
7
|
+
"github.com/joho/godotenv"
|
|
8
|
+
"github.com/labstack/echo/v4"
|
|
9
|
+
"github.com/labstack/echo/v4/middleware"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func main() {
|
|
13
|
+
godotenv.Load()
|
|
14
|
+
|
|
15
|
+
e := echo.New()
|
|
16
|
+
|
|
17
|
+
// Middleware
|
|
18
|
+
e.Use(middleware.Logger())
|
|
19
|
+
e.Use(middleware.Recover())
|
|
20
|
+
// CORS configuration - allow only the configured frontend URL
|
|
21
|
+
allowedOrigins := []string{}
|
|
22
|
+
if frontendURL := os.Getenv("FRONTEND_URL"); frontendURL != "" {
|
|
23
|
+
allowedOrigins = append(allowedOrigins, frontendURL)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
|
|
27
|
+
AllowOrigins: allowedOrigins,
|
|
28
|
+
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
|
29
|
+
AllowHeaders: []string{"*"},
|
|
30
|
+
AllowCredentials: true,
|
|
31
|
+
}))
|
|
32
|
+
|
|
33
|
+
// Health endpoint
|
|
34
|
+
e.GET("/health", handlers.Health)
|
|
35
|
+
e.GET("/actuator/health", handlers.Health)
|
|
36
|
+
|
|
37
|
+
// Example API route
|
|
38
|
+
e.GET("/api/example", handlers.Example)
|
|
39
|
+
|
|
40
|
+
// Protected API routes
|
|
41
|
+
<% if (modules.auth) { -%>
|
|
42
|
+
// TODO: Add auth middleware
|
|
43
|
+
<% } -%>
|
|
44
|
+
api := e.Group("/api")
|
|
45
|
+
_ = api // Will be used for API routes
|
|
46
|
+
|
|
47
|
+
port := os.Getenv("APP_PORT")
|
|
48
|
+
if port == "" {
|
|
49
|
+
port = "8080"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
e.Logger.Printf("✅ Server running on http://localhost:%s", port)
|
|
53
|
+
e.Logger.Printf("📍 API available at http://localhost:%s/api", port)
|
|
54
|
+
e.Logger.Printf("🏥 Health check at http://localhost:%s/health", port)
|
|
55
|
+
|
|
56
|
+
e.Logger.Fatal(e.Start(":" + port))
|
|
57
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module <%= appSlug %>-backend
|
|
2
|
+
|
|
3
|
+
go 1.22
|
|
4
|
+
|
|
5
|
+
require (
|
|
6
|
+
github.com/labstack/echo/v4 v4.12.0
|
|
7
|
+
github.com/joho/godotenv v1.5.1<% if (modules.auth) { %>
|
|
8
|
+
github.com/lestrrat-go/jwx/v2 v2.1.3<% } %><% if (modules.payments) { %>
|
|
9
|
+
github.com/stripe/stripe-go/v76 v76.25.0<% } %>
|
|
10
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package handlers
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"net/http"
|
|
5
|
+
"time"
|
|
6
|
+
|
|
7
|
+
"github.com/labstack/echo/v4"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
func Example(c echo.Context) error {
|
|
11
|
+
return c.JSON(http.StatusOK, map[string]interface{}{
|
|
12
|
+
"message": "Hello from go-echo backend!",
|
|
13
|
+
"timestamp": time.Now().Format(time.RFC3339),
|
|
14
|
+
})
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
APP_PORT=8080
|
|
2
|
+
FRONTEND_URL=http://localhost:<%= frontendPort %>
|
|
3
|
+
<% if (modules.auth) { -%>
|
|
4
|
+
AUTH0_DOMAIN=your-tenant.us.auth0.com
|
|
5
|
+
AUTH0_AUDIENCE=https://api.<%= appSlug %>
|
|
6
|
+
<% } -%>
|
|
7
|
+
<% if (modules.payments) { -%>
|
|
8
|
+
STRIPE_SECRET_KEY=sk_test_xxx
|
|
9
|
+
STRIPE_WEBHOOK_SECRET=whsec_xxx
|
|
10
|
+
<% } -%>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
3
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
4
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
5
|
+
<modelVersion>4.0.0</modelVersion>
|
|
6
|
+
|
|
7
|
+
<parent>
|
|
8
|
+
<groupId>org.springframework.boot</groupId>
|
|
9
|
+
<artifactId>spring-boot-starter-parent</artifactId>
|
|
10
|
+
<version>3.3.4</version>
|
|
11
|
+
<relativePath/>
|
|
12
|
+
</parent>
|
|
13
|
+
|
|
14
|
+
<groupId>com.app</groupId>
|
|
15
|
+
<artifactId><%= appSlug %>-backend</artifactId>
|
|
16
|
+
<version>0.0.1-SNAPSHOT</version>
|
|
17
|
+
<name><%= appName %> Backend</name>
|
|
18
|
+
|
|
19
|
+
<properties>
|
|
20
|
+
<java.version>21</java.version>
|
|
21
|
+
</properties>
|
|
22
|
+
|
|
23
|
+
<dependencies>
|
|
24
|
+
<dependency>
|
|
25
|
+
<groupId>org.springframework.boot</groupId>
|
|
26
|
+
<artifactId>spring-boot-starter-web</artifactId>
|
|
27
|
+
</dependency>
|
|
28
|
+
<% if (modules.auth) { -%>
|
|
29
|
+
<dependency>
|
|
30
|
+
<groupId>org.springframework.boot</groupId>
|
|
31
|
+
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
|
32
|
+
</dependency>
|
|
33
|
+
<dependency>
|
|
34
|
+
<groupId>org.springframework.boot</groupId>
|
|
35
|
+
<artifactId>spring-boot-starter-security</artifactId>
|
|
36
|
+
</dependency>
|
|
37
|
+
<% } -%>
|
|
38
|
+
<% if (modules.payments) { -%>
|
|
39
|
+
<dependency>
|
|
40
|
+
<groupId>com.stripe</groupId>
|
|
41
|
+
<artifactId>stripe-java</artifactId>
|
|
42
|
+
<version>26.0.0</version>
|
|
43
|
+
</dependency>
|
|
44
|
+
<% } -%>
|
|
45
|
+
<dependency>
|
|
46
|
+
<groupId>org.springframework.boot</groupId>
|
|
47
|
+
<artifactId>spring-boot-starter-actuator</artifactId>
|
|
48
|
+
</dependency>
|
|
49
|
+
<dependency>
|
|
50
|
+
<groupId>org.springframework.boot</groupId>
|
|
51
|
+
<artifactId>spring-boot-starter-test</artifactId>
|
|
52
|
+
<scope>test</scope>
|
|
53
|
+
</dependency>
|
|
54
|
+
</dependencies>
|
|
55
|
+
|
|
56
|
+
<build>
|
|
57
|
+
<plugins>
|
|
58
|
+
<plugin>
|
|
59
|
+
<groupId>org.springframework.boot</groupId>
|
|
60
|
+
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
61
|
+
</plugin>
|
|
62
|
+
</plugins>
|
|
63
|
+
</build>
|
|
64
|
+
</project>
|
package/tools/templates/backend/java-spring/backend/src/main/java/com/app/Application.java.ejs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
package com.app;
|
|
2
|
+
|
|
3
|
+
import org.springframework.boot.SpringApplication;
|
|
4
|
+
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
5
|
+
|
|
6
|
+
@SpringBootApplication
|
|
7
|
+
public class Application {
|
|
8
|
+
public static void main(String[] args) {
|
|
9
|
+
SpringApplication.run(Application.class, args);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
package com.app.config;
|
|
2
|
+
|
|
3
|
+
import org.springframework.context.annotation.Bean;
|
|
4
|
+
import org.springframework.context.annotation.Configuration;
|
|
5
|
+
<% if (modules.auth) { -%>
|
|
6
|
+
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
|
7
|
+
<% } -%>
|
|
8
|
+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
9
|
+
import org.springframework.security.web.SecurityFilterChain;
|
|
10
|
+
import org.springframework.web.cors.*;
|
|
11
|
+
|
|
12
|
+
import java.util.List;
|
|
13
|
+
|
|
14
|
+
@Configuration
|
|
15
|
+
<% if (modules.auth && modules.admin) { -%>
|
|
16
|
+
@EnableMethodSecurity
|
|
17
|
+
<% } -%>
|
|
18
|
+
public class SecurityConfig {
|
|
19
|
+
|
|
20
|
+
@Bean
|
|
21
|
+
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
|
22
|
+
http.csrf(csrf -> csrf.disable());
|
|
23
|
+
http.cors(cors -> cors.configurationSource(corsConfigurationSource()));
|
|
24
|
+
|
|
25
|
+
http.authorizeHttpRequests(auth -> auth
|
|
26
|
+
.requestMatchers("/actuator/health"<% if (modules.payments) { %>, "/stripe/webhook"<% } %>).permitAll()
|
|
27
|
+
<% if (modules.admin) { -%>
|
|
28
|
+
.requestMatchers("/api/admin/**").hasAuthority("SCOPE_admin")
|
|
29
|
+
<% } -%>
|
|
30
|
+
<% if (modules.auth) { -%>
|
|
31
|
+
.anyRequest().authenticated()
|
|
32
|
+
<% } else { -%>
|
|
33
|
+
.anyRequest().permitAll()
|
|
34
|
+
<% } -%>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
<% if (modules.auth) { -%>
|
|
38
|
+
http.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> {}));
|
|
39
|
+
<% } -%>
|
|
40
|
+
|
|
41
|
+
return http.build();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Bean
|
|
45
|
+
public CorsConfigurationSource corsConfigurationSource() {
|
|
46
|
+
CorsConfiguration configuration = new CorsConfiguration();
|
|
47
|
+
|
|
48
|
+
// Allow only the configured frontend URL
|
|
49
|
+
String frontendUrl = System.getenv("FRONTEND_URL");
|
|
50
|
+
if (frontendUrl != null && !frontendUrl.isEmpty()) {
|
|
51
|
+
configuration.setAllowedOrigins(List.of(frontendUrl));
|
|
52
|
+
} else {
|
|
53
|
+
configuration.setAllowedOrigins(List.of());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
|
57
|
+
configuration.setAllowedHeaders(Arrays.asList("*"));
|
|
58
|
+
configuration.setAllowCredentials(true);
|
|
59
|
+
|
|
60
|
+
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
|
61
|
+
source.registerCorsConfiguration("/**", configuration);
|
|
62
|
+
return source;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package com.app.controller;
|
|
2
|
+
|
|
3
|
+
import org.springframework.web.bind.annotation.GetMapping;
|
|
4
|
+
import org.springframework.web.bind.annotation.RestController;
|
|
5
|
+
|
|
6
|
+
import java.time.Instant;
|
|
7
|
+
import java.util.Map;
|
|
8
|
+
|
|
9
|
+
@RestController
|
|
10
|
+
public class ExampleController {
|
|
11
|
+
|
|
12
|
+
@GetMapping("/api/example")
|
|
13
|
+
public Map<String, Object> example() {
|
|
14
|
+
return Map.of(
|
|
15
|
+
"message", "Hello from java-spring backend!",
|
|
16
|
+
"timestamp", Instant.now().toString()
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package com.app.controller;
|
|
2
|
+
|
|
3
|
+
import org.springframework.web.bind.annotation.GetMapping;
|
|
4
|
+
import org.springframework.web.bind.annotation.RestController;
|
|
5
|
+
|
|
6
|
+
import java.util.Map;
|
|
7
|
+
|
|
8
|
+
@RestController
|
|
9
|
+
public class HealthController {
|
|
10
|
+
|
|
11
|
+
@GetMapping("/health")
|
|
12
|
+
public Map<String, String> health() {
|
|
13
|
+
return Map.of("status", "UP");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
server:
|
|
2
|
+
port: ${APP_PORT:8080}
|
|
3
|
+
|
|
4
|
+
spring:
|
|
5
|
+
application:
|
|
6
|
+
name: <%= appSlug %>-backend
|
|
7
|
+
<% if (modules.auth) { -%>
|
|
8
|
+
security:
|
|
9
|
+
oauth2:
|
|
10
|
+
resourceserver:
|
|
11
|
+
jwt:
|
|
12
|
+
issuer-uri: https://${AUTH0_DOMAIN}/
|
|
13
|
+
audiences: ${AUTH0_AUDIENCE}
|
|
14
|
+
<% } -%>
|
|
15
|
+
|
|
16
|
+
management:
|
|
17
|
+
endpoints:
|
|
18
|
+
web:
|
|
19
|
+
exposure:
|
|
20
|
+
include: health
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
APP_PORT=8080
|
|
2
|
+
FRONTEND_URL=http://localhost:<%= frontendPort %>
|
|
3
|
+
<% if (modules.auth) { -%>
|
|
4
|
+
AUTH0_DOMAIN=your-tenant.us.auth0.com
|
|
5
|
+
AUTH0_AUDIENCE=https://api.<%= appSlug %>
|
|
6
|
+
<% } -%>
|
|
7
|
+
<% if (modules.payments) { -%>
|
|
8
|
+
STRIPE_SECRET_KEY=sk_test_xxx
|
|
9
|
+
STRIPE_WEBHOOK_SECRET=whsec_xxx
|
|
10
|
+
<% } -%>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"parser": "@typescript-eslint/parser",
|
|
3
|
+
"extends": [
|
|
4
|
+
"eslint:recommended",
|
|
5
|
+
"plugin:@typescript-eslint/recommended"
|
|
6
|
+
],
|
|
7
|
+
"parserOptions": {
|
|
8
|
+
"ecmaVersion": "latest",
|
|
9
|
+
"sourceType": "module"
|
|
10
|
+
},
|
|
11
|
+
"rules": {
|
|
12
|
+
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
|
|
13
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
14
|
+
"no-console": "off",
|
|
15
|
+
"@typescript-eslint/explicit-module-boundary-types": "off"
|
|
16
|
+
},
|
|
17
|
+
"env": {
|
|
18
|
+
"node": true,
|
|
19
|
+
"es2021": true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Multi-stage build for production
|
|
2
|
+
|
|
3
|
+
# Stage 1: Build
|
|
4
|
+
FROM node:20-alpine AS builder
|
|
5
|
+
|
|
6
|
+
WORKDIR /app
|
|
7
|
+
|
|
8
|
+
# Copy package files
|
|
9
|
+
COPY package*.json ./
|
|
10
|
+
|
|
11
|
+
# Install dependencies
|
|
12
|
+
RUN npm ci
|
|
13
|
+
|
|
14
|
+
# Copy source code
|
|
15
|
+
COPY . .
|
|
16
|
+
|
|
17
|
+
# Build TypeScript
|
|
18
|
+
RUN npm run build
|
|
19
|
+
|
|
20
|
+
# Stage 2: Production
|
|
21
|
+
FROM node:20-alpine
|
|
22
|
+
|
|
23
|
+
WORKDIR /app
|
|
24
|
+
|
|
25
|
+
# Copy package files
|
|
26
|
+
COPY package*.json ./
|
|
27
|
+
|
|
28
|
+
# Install production dependencies only
|
|
29
|
+
RUN npm ci --only=production
|
|
30
|
+
|
|
31
|
+
# Copy built application from builder
|
|
32
|
+
COPY --from=builder /app/dist ./dist
|
|
33
|
+
|
|
34
|
+
# Create non-root user
|
|
35
|
+
RUN addgroup -g 1001 -S nodejs && \
|
|
36
|
+
adduser -S nodejs -u 1001
|
|
37
|
+
|
|
38
|
+
# Change ownership
|
|
39
|
+
RUN chown -R nodejs:nodejs /app
|
|
40
|
+
|
|
41
|
+
# Switch to non-root user
|
|
42
|
+
USER nodejs
|
|
43
|
+
|
|
44
|
+
# Expose port
|
|
45
|
+
EXPOSE 8080
|
|
46
|
+
|
|
47
|
+
# Health check
|
|
48
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
|
49
|
+
CMD node -e "require('http').get('http://localhost:8080/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
|
50
|
+
|
|
51
|
+
# Start application
|
|
52
|
+
CMD ["node", "dist/index.js"]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# <%= AppName %> - Backend
|
|
2
|
+
|
|
3
|
+
Node.js + Express + TypeScript backend.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies
|
|
9
|
+
npm install
|
|
10
|
+
|
|
11
|
+
# Set up environment
|
|
12
|
+
cp .env.example .env
|
|
13
|
+
|
|
14
|
+
# Start development server
|
|
15
|
+
npm run dev
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Server runs on http://localhost:8080
|
|
19
|
+
|
|
20
|
+
## Available Scripts
|
|
21
|
+
|
|
22
|
+
- `npm run dev` - Start development server with hot reload
|
|
23
|
+
- `npm run build` - Compile TypeScript to JavaScript
|
|
24
|
+
- `npm start` - Run production build
|
|
25
|
+
|
|
26
|
+
## Environment Variables
|
|
27
|
+
|
|
28
|
+
See `.env.example` for required variables:
|
|
29
|
+
|
|
30
|
+
- `APP_PORT` - Server port (default: 8080)
|
|
31
|
+
<% if (db === 'postgres' || db === 'mysql') { -%>
|
|
32
|
+
- `DATABASE_URL` - Database connection string
|
|
33
|
+
<% } else if (db === 'mongodb') { -%>
|
|
34
|
+
- `DATABASE_URL` - MongoDB connection string
|
|
35
|
+
<% } -%>
|
|
36
|
+
<% if (modules.auth) { -%>
|
|
37
|
+
- `AUTH0_DOMAIN` - Auth0 domain
|
|
38
|
+
- `AUTH0_AUDIENCE` - Auth0 API audience
|
|
39
|
+
<% } -%>
|
|
40
|
+
|
|
41
|
+
## API Routes
|
|
42
|
+
|
|
43
|
+
- `GET /health` - Health check endpoint
|
|
44
|
+
- `GET /api/example` - Example API route
|
|
45
|
+
<% if (modules.contact) { -%>
|
|
46
|
+
- `POST /api/contact` - Submit contact form
|
|
47
|
+
<% } -%>
|
|
48
|
+
<% if (modules.admin) { -%>
|
|
49
|
+
- `GET /api/admin/stats` - Admin statistics (requires auth)
|
|
50
|
+
<% } -%>
|
|
51
|
+
<% if (modules.payments) { -%>
|
|
52
|
+
- `POST /api/payments/create-checkout-session` - Create Stripe checkout
|
|
53
|
+
- `POST /stripe/webhook` - Stripe webhook handler
|
|
54
|
+
<% } -%>
|
|
55
|
+
|
|
56
|
+
## Tech Stack
|
|
57
|
+
|
|
58
|
+
- **Node.js ≥20** - Runtime
|
|
59
|
+
- **Express 5** - Web framework
|
|
60
|
+
- **TypeScript** - Type safety
|
|
61
|
+
<% if (db === 'postgres' || db === 'mysql') { -%>
|
|
62
|
+
- **Prisma** - Database ORM
|
|
63
|
+
<% } else if (db === 'mongodb') { -%>
|
|
64
|
+
- **Mongoose** - MongoDB ODM
|
|
65
|
+
<% } -%>
|
|
66
|
+
<% if (modules.auth) { -%>
|
|
67
|
+
- **jose** - JWT validation
|
|
68
|
+
<% } -%>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= appSlug %>-backend",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=20.0.0"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "tsx watch src/index.ts",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"lint": "eslint . --ext .ts --report-unused-disable-directives --max-warnings 0",
|
|
14
|
+
"lint:fix": "eslint . --ext .ts --fix",
|
|
15
|
+
"format": "prettier --write \"src/**/*.{ts,json}\"",
|
|
16
|
+
"format:check": "prettier --check \"src/**/*.{ts,json}\"",
|
|
17
|
+
"type-check": "tsc --noEmit"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"express": "^5.0.0",
|
|
21
|
+
"cors": "^2.8.5",
|
|
22
|
+
"dotenv": "^16.4.5"<% if (modules.auth) { %>,
|
|
23
|
+
"jose": "^5.9.6"<% } %><% if (modules.payments) { %>,
|
|
24
|
+
"stripe": "^16.0.0"<% } %>
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/express": "^5.0.0",
|
|
28
|
+
"@types/cors": "^2.8.17",
|
|
29
|
+
"@types/node": "^22.0.0",
|
|
30
|
+
"tsx": "^4.19.2",
|
|
31
|
+
"typescript": "^5.6.3",
|
|
32
|
+
"@typescript-eslint/parser": "^6.19.0",
|
|
33
|
+
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
|
34
|
+
"eslint": "^8.57.0",
|
|
35
|
+
"prettier": "^3.2.4"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
<% if (modules.auth) { -%>
|
|
5
|
+
import { authMiddleware } from './middleware/auth.js';
|
|
6
|
+
<% } -%>
|
|
7
|
+
|
|
8
|
+
dotenv.config();
|
|
9
|
+
|
|
10
|
+
const app = express();
|
|
11
|
+
const PORT = process.env.APP_PORT || 8080;
|
|
12
|
+
|
|
13
|
+
// CORS configuration - allow only the configured frontend URL
|
|
14
|
+
const allowedOrigins = [
|
|
15
|
+
process.env.FRONTEND_URL
|
|
16
|
+
].filter(Boolean);
|
|
17
|
+
|
|
18
|
+
app.use(cors({
|
|
19
|
+
origin: (origin, callback) => {
|
|
20
|
+
// Allow requests with no origin (like mobile apps or curl)
|
|
21
|
+
if (!origin) return callback(null, true);
|
|
22
|
+
if (allowedOrigins.indexOf(origin) !== -1) {
|
|
23
|
+
callback(null, true);
|
|
24
|
+
} else {
|
|
25
|
+
callback(new Error('Not allowed by CORS'));
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
credentials: true
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
app.use(express.json());
|
|
32
|
+
|
|
33
|
+
// Health endpoint
|
|
34
|
+
app.get('/health', (req, res) => {
|
|
35
|
+
res.json({ status: 'UP' });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
app.get('/actuator/health', (req, res) => {
|
|
39
|
+
res.json({ status: 'UP' });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Example API route
|
|
43
|
+
app.get('/api/example', (req, res) => {
|
|
44
|
+
res.json({
|
|
45
|
+
message: 'Hello from <%= backend %> backend!',
|
|
46
|
+
timestamp: new Date().toISOString()
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Apply auth middleware to protected routes
|
|
51
|
+
<% if (modules.auth) { -%>
|
|
52
|
+
app.use('/api', authMiddleware);
|
|
53
|
+
<% } -%>
|
|
54
|
+
|
|
55
|
+
const server = app.listen(PORT, () => {
|
|
56
|
+
console.log(`✅ Server running on http://localhost:${PORT}`);
|
|
57
|
+
console.log(`📍 API available at http://localhost:${PORT}/api`);
|
|
58
|
+
console.log(`🏥 Health check at http://localhost:${PORT}/health`);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Graceful shutdown
|
|
62
|
+
process.on('SIGTERM', () => {
|
|
63
|
+
console.log('SIGTERM signal received: closing HTTP server');
|
|
64
|
+
server.close(() => {
|
|
65
|
+
console.log('HTTP server closed');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
process.on('SIGINT', () => {
|
|
70
|
+
console.log('SIGINT signal received: closing HTTP server');
|
|
71
|
+
server.close(() => {
|
|
72
|
+
console.log('HTTP server closed');
|
|
73
|
+
process.exit(0);
|
|
74
|
+
});
|
|
75
|
+
});
|