polyforge-cli 0.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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -0
  3. package/dist/commands/create.js +46 -0
  4. package/dist/commands/doctor.js +136 -0
  5. package/dist/commands/list.js +32 -0
  6. package/dist/core/git.js +25 -0
  7. package/dist/core/installer.js +64 -0
  8. package/dist/core/prompts.js +147 -0
  9. package/dist/core/renderer.js +175 -0
  10. package/dist/core/validator.js +86 -0
  11. package/dist/index.js +47 -0
  12. package/dist/templates/backend/go-gin/apps/api/Makefile +10 -0
  13. package/dist/templates/backend/go-gin/apps/api/cmd/server/main.go +29 -0
  14. package/dist/templates/backend/go-gin/apps/api/go.mod +5 -0
  15. package/dist/templates/backend/go-gin/apps/api/internal/config/config.go +22 -0
  16. package/dist/templates/backend/go-gin/apps/api/internal/handler/health.go +27 -0
  17. package/dist/templates/backend/go-gin/apps/api/internal/handler/helpers.go +8 -0
  18. package/dist/templates/backend/go-gin/apps/api/internal/handler/ping.go +15 -0
  19. package/dist/templates/backend/go-gin/apps/api/internal/middleware/logger.go +18 -0
  20. package/dist/templates/backend/go-gin/apps/api/internal/middleware/recovery.go +23 -0
  21. package/dist/templates/backend/go-gin/apps/api/internal/middleware/trace.go +28 -0
  22. package/dist/templates/backend/go-gin/apps/api/internal/repository/ping.go +11 -0
  23. package/dist/templates/backend/go-gin/apps/api/internal/service/ping.go +17 -0
  24. package/dist/templates/backend/go-gin/apps/api/pkg/response/response.go +35 -0
  25. package/dist/templates/backend/go-gin/apps/api/pkg/response/response_test.go +21 -0
  26. package/dist/templates/backend/springboot/apps/api/pom.xml +44 -0
  27. package/dist/templates/backend/springboot/apps/api/src/main/java/com/scaffold/api/Application.java +11 -0
  28. package/dist/templates/backend/springboot/apps/api/src/main/java/com/scaffold/api/common/ApiResponse.java +19 -0
  29. package/dist/templates/backend/springboot/apps/api/src/main/java/com/scaffold/api/common/GlobalExceptionHandler.java +22 -0
  30. package/dist/templates/backend/springboot/apps/api/src/main/java/com/scaffold/api/common/TraceIdHolder.java +19 -0
  31. package/dist/templates/backend/springboot/apps/api/src/main/java/com/scaffold/api/config/TraceIdFilter.java +38 -0
  32. package/dist/templates/backend/springboot/apps/api/src/main/java/com/scaffold/api/controller/HealthController.java +17 -0
  33. package/dist/templates/backend/springboot/apps/api/src/main/java/com/scaffold/api/controller/PingController.java +23 -0
  34. package/dist/templates/backend/springboot/apps/api/src/main/java/com/scaffold/api/repository/PingRepository.java +10 -0
  35. package/dist/templates/backend/springboot/apps/api/src/main/java/com/scaffold/api/service/PingService.java +17 -0
  36. package/dist/templates/backend/springboot/apps/api/src/main/resources/application-dev.yml +2 -0
  37. package/dist/templates/backend/springboot/apps/api/src/main/resources/application-prod.yml +2 -0
  38. package/dist/templates/backend/springboot/apps/api/src/main/resources/application-test.yml +2 -0
  39. package/dist/templates/backend/springboot/apps/api/src/main/resources/application.yml +8 -0
  40. package/dist/templates/backend/springboot/apps/api/src/test/java/com/scaffold/api/ApplicationTests.java +12 -0
  41. package/dist/templates/base/.env.example +5 -0
  42. package/dist/templates/base/docs/ARCHITECTURE.md +9 -0
  43. package/dist/templates/base/infra/scripts/build.sh +39 -0
  44. package/dist/templates/base/infra/scripts/check.sh +58 -0
  45. package/dist/templates/base/infra/scripts/dev.sh +32 -0
  46. package/dist/templates/base/infra/scripts/test.sh +41 -0
  47. package/dist/templates/base/package.json +11 -0
  48. package/dist/templates/data/mongodb/infra/data/mongodb/README.md +4 -0
  49. package/dist/templates/data/mysql/infra/data/mysql/README.md +4 -0
  50. package/dist/templates/data/mysql/infra/data/mysql/schema.sql +5 -0
  51. package/dist/templates/data/postgresql/infra/data/postgresql/README.md +4 -0
  52. package/dist/templates/data/postgresql/infra/data/postgresql/schema.sql +5 -0
  53. package/dist/templates/data/redis/infra/data/redis/README.md +4 -0
  54. package/dist/templates/data/sqlite/infra/data/sqlite/README.md +4 -0
  55. package/dist/templates/frontend/react/apps/web/.env.example +4 -0
  56. package/dist/templates/frontend/react/apps/web/index.html +12 -0
  57. package/dist/templates/frontend/react/apps/web/package.json +23 -0
  58. package/dist/templates/frontend/react/apps/web/src/App.test.tsx +7 -0
  59. package/dist/templates/frontend/react/apps/web/src/App.tsx +59 -0
  60. package/dist/templates/frontend/react/apps/web/src/api/client.ts +20 -0
  61. package/dist/templates/frontend/react/apps/web/src/api/request.ts +104 -0
  62. package/dist/templates/frontend/react/apps/web/src/api/services/api.ts +15 -0
  63. package/dist/templates/frontend/react/apps/web/src/api/services/bff.ts +11 -0
  64. package/dist/templates/frontend/react/apps/web/src/main.tsx +9 -0
  65. package/dist/templates/frontend/react/apps/web/tsconfig.json +12 -0
  66. package/dist/templates/frontend/react/apps/web/vite.config.ts +22 -0
  67. package/dist/templates/frontend/vue/apps/web/.env.example +4 -0
  68. package/dist/templates/frontend/vue/apps/web/index.html +12 -0
  69. package/dist/templates/frontend/vue/apps/web/package.json +20 -0
  70. package/dist/templates/frontend/vue/apps/web/src/App.test.ts +7 -0
  71. package/dist/templates/frontend/vue/apps/web/src/App.vue +59 -0
  72. package/dist/templates/frontend/vue/apps/web/src/api/client.ts +20 -0
  73. package/dist/templates/frontend/vue/apps/web/src/api/request.ts +104 -0
  74. package/dist/templates/frontend/vue/apps/web/src/api/services/api.ts +15 -0
  75. package/dist/templates/frontend/vue/apps/web/src/api/services/bff.ts +11 -0
  76. package/dist/templates/frontend/vue/apps/web/src/env.d.ts +3 -0
  77. package/dist/templates/frontend/vue/apps/web/src/main.ts +4 -0
  78. package/dist/templates/frontend/vue/apps/web/tsconfig.json +11 -0
  79. package/dist/templates/frontend/vue/apps/web/vite.config.ts +22 -0
  80. package/dist/templates/modules/auth-center/apps/auth-center/.env.example +3 -0
  81. package/dist/templates/modules/auth-center/apps/auth-center/README.md +5 -0
  82. package/dist/templates/modules/auth-center/apps/auth-center/package.json +14 -0
  83. package/dist/templates/modules/auth-center/apps/auth-center/server.js +21 -0
  84. package/dist/templates/modules/cache-redis/infra/cache/README.md +5 -0
  85. package/dist/templates/modules/cache-redis/infra/cache/policies.md +5 -0
  86. package/dist/templates/modules/gateway-bff/apps/gateway-bff/README.md +5 -0
  87. package/dist/templates/modules/gateway-bff/apps/gateway-bff/package.json +13 -0
  88. package/dist/templates/modules/gateway-bff/apps/gateway-bff/server.js +17 -0
  89. package/dist/templates/modules/grpc-service/apps/grpc-service/README.md +5 -0
  90. package/dist/templates/modules/grpc-service/contracts/proto/greeter.proto +17 -0
  91. package/dist/templates/modules/grpc-service/infra/scripts/gen-proto.sh +9 -0
  92. package/dist/templates/modules/mq/infra/mq/README.md +5 -0
  93. package/dist/templates/modules/mq/infra/mq/kafka-topics.md +5 -0
  94. package/dist/templates/modules/mq/infra/mq/nats-subjects.md +4 -0
  95. package/dist/templates/modules/mq/infra/mq/rabbitmq-exchanges.md +5 -0
  96. package/dist/templates/modules/observability/infra/observability/README.md +5 -0
  97. package/dist/templates/modules/observability/infra/observability/grafana/README.md +3 -0
  98. package/dist/templates/modules/observability/infra/observability/otel-collector.yaml +15 -0
  99. package/dist/templates/modules/observability/infra/observability/prometheus/prometheus.yml +7 -0
  100. package/dist/templates/modules/python-ai/apps/python-ai/README.md +5 -0
  101. package/dist/templates/modules/python-ai/apps/python-ai/app/main.py +27 -0
  102. package/dist/templates/modules/python-ai/apps/python-ai/requirements.txt +2 -0
  103. package/dist/templates/modules/python-ai/apps/python-ai/scripts/batch_infer.py +6 -0
  104. package/dist/templates/modules/python-worker/apps/worker-python/README.md +5 -0
  105. package/dist/templates/modules/python-worker/apps/worker-python/requirements.txt +1 -0
  106. package/dist/templates/modules/python-worker/apps/worker-python/tasks/sample_task.py +6 -0
  107. package/dist/templates/modules/python-worker/apps/worker-python/tests/test_worker.py +13 -0
  108. package/dist/templates/modules/python-worker/apps/worker-python/worker.py +10 -0
  109. package/dist/templates/modules/worker-go/apps/worker-go/README.md +5 -0
  110. package/dist/templates/modules/worker-go/apps/worker-go/cmd/worker/main.go +16 -0
  111. package/dist/templates/modules/worker-go/apps/worker-go/go.mod +3 -0
  112. package/dist/templates/modules/worker-go/apps/worker-go/internal/tasks/heartbeat.go +7 -0
  113. package/dist/types/config.js +18 -0
  114. package/package.json +52 -0
@@ -0,0 +1,22 @@
1
+ package com.scaffold.api.common;
2
+
3
+ import org.springframework.http.HttpStatus;
4
+ import org.springframework.http.ResponseEntity;
5
+ import org.springframework.web.bind.MethodArgumentNotValidException;
6
+ import org.springframework.web.bind.annotation.ControllerAdvice;
7
+ import org.springframework.web.bind.annotation.ExceptionHandler;
8
+
9
+ @ControllerAdvice
10
+ public class GlobalExceptionHandler {
11
+
12
+ @ExceptionHandler(MethodArgumentNotValidException.class)
13
+ public ResponseEntity<ApiResponse<Void>> handleValidation(MethodArgumentNotValidException ex) {
14
+ return ResponseEntity.badRequest().body(ApiResponse.error(10001, "validation error", TraceIdHolder.get()));
15
+ }
16
+
17
+ @ExceptionHandler(Exception.class)
18
+ public ResponseEntity<ApiResponse<Void>> handleException(Exception ex) {
19
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
20
+ .body(ApiResponse.error(10000, "internal server error", TraceIdHolder.get()));
21
+ }
22
+ }
@@ -0,0 +1,19 @@
1
+ package com.scaffold.api.common;
2
+
3
+ public final class TraceIdHolder {
4
+ private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
5
+
6
+ private TraceIdHolder() {}
7
+
8
+ public static void set(String traceId) {
9
+ HOLDER.set(traceId);
10
+ }
11
+
12
+ public static String get() {
13
+ return HOLDER.get();
14
+ }
15
+
16
+ public static void clear() {
17
+ HOLDER.remove();
18
+ }
19
+ }
@@ -0,0 +1,38 @@
1
+ package com.scaffold.api.config;
2
+
3
+ import com.scaffold.api.common.TraceIdHolder;
4
+ import jakarta.servlet.FilterChain;
5
+ import jakarta.servlet.ServletException;
6
+ import jakarta.servlet.http.HttpServletRequest;
7
+ import jakarta.servlet.http.HttpServletResponse;
8
+ import org.slf4j.MDC;
9
+ import org.springframework.stereotype.Component;
10
+ import org.springframework.web.filter.OncePerRequestFilter;
11
+
12
+ import java.io.IOException;
13
+ import java.util.UUID;
14
+
15
+ @Component
16
+ public class TraceIdFilter extends OncePerRequestFilter {
17
+ private static final String TRACE_HEADER = "X-Trace-Id";
18
+
19
+ @Override
20
+ protected void doFilterInternal(HttpServletRequest request,
21
+ HttpServletResponse response,
22
+ FilterChain filterChain) throws ServletException, IOException {
23
+ String traceId = request.getHeader(TRACE_HEADER);
24
+ if (traceId == null || traceId.isBlank()) {
25
+ traceId = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
26
+ }
27
+ TraceIdHolder.set(traceId);
28
+ MDC.put("traceId", traceId);
29
+ response.setHeader(TRACE_HEADER, traceId);
30
+
31
+ try {
32
+ filterChain.doFilter(request, response);
33
+ } finally {
34
+ TraceIdHolder.clear();
35
+ MDC.remove("traceId");
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,17 @@
1
+ package com.scaffold.api.controller;
2
+
3
+ import com.scaffold.api.common.ApiResponse;
4
+ import com.scaffold.api.common.TraceIdHolder;
5
+ import org.springframework.web.bind.annotation.GetMapping;
6
+ import org.springframework.web.bind.annotation.RestController;
7
+
8
+ import java.util.Map;
9
+
10
+ @RestController
11
+ public class HealthController {
12
+
13
+ @GetMapping("/health")
14
+ public ApiResponse<Map<String, String>> health() {
15
+ return ApiResponse.success(Map.of("status", "ok"), TraceIdHolder.get());
16
+ }
17
+ }
@@ -0,0 +1,23 @@
1
+ package com.scaffold.api.controller;
2
+
3
+ import com.scaffold.api.common.ApiResponse;
4
+ import com.scaffold.api.common.TraceIdHolder;
5
+ import com.scaffold.api.service.PingService;
6
+ import org.springframework.web.bind.annotation.GetMapping;
7
+ import org.springframework.web.bind.annotation.RestController;
8
+
9
+ import java.util.Map;
10
+
11
+ @RestController
12
+ public class PingController {
13
+ private final PingService pingService;
14
+
15
+ public PingController(PingService pingService) {
16
+ this.pingService = pingService;
17
+ }
18
+
19
+ @GetMapping("/api/v1/ping")
20
+ public ApiResponse<Map<String, String>> ping() {
21
+ return ApiResponse.success(Map.of("message", pingService.ping()), TraceIdHolder.get());
22
+ }
23
+ }
@@ -0,0 +1,10 @@
1
+ package com.scaffold.api.repository;
2
+
3
+ import org.springframework.stereotype.Repository;
4
+
5
+ @Repository
6
+ public class PingRepository {
7
+ public String message() {
8
+ return "pong";
9
+ }
10
+ }
@@ -0,0 +1,17 @@
1
+ package com.scaffold.api.service;
2
+
3
+ import com.scaffold.api.repository.PingRepository;
4
+ import org.springframework.stereotype.Service;
5
+
6
+ @Service
7
+ public class PingService {
8
+ private final PingRepository pingRepository;
9
+
10
+ public PingService(PingRepository pingRepository) {
11
+ this.pingRepository = pingRepository;
12
+ }
13
+
14
+ public String ping() {
15
+ return pingRepository.message();
16
+ }
17
+ }
@@ -0,0 +1,8 @@
1
+ spring:
2
+ application:
3
+ name: {{PROJECT_NAME}}-api
4
+ server:
5
+ port: ${APP_PORT:8080}
6
+ logging:
7
+ pattern:
8
+ level: "%5p [traceId:%X{traceId}]"
@@ -0,0 +1,12 @@
1
+ package com.scaffold.api;
2
+
3
+ import org.junit.jupiter.api.Test;
4
+ import org.springframework.boot.test.context.SpringBootTest;
5
+
6
+ @SpringBootTest
7
+ class ApplicationTests {
8
+
9
+ @Test
10
+ void contextLoads() {
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ APP_NAME={{PROJECT_NAME}}
2
+ APP_ENV=dev
3
+ APP_PORT=8080
4
+ LOG_LEVEL=info
5
+ TRACE_HEADER=X-Trace-Id
@@ -0,0 +1,9 @@
1
+ # Architecture Notes
2
+
3
+ - Unified response payload: code/message/data/traceId/timestamp
4
+ - Health endpoint: `/health`
5
+ - Trace propagation header: `X-Trace-Id`
6
+ - Backend main stack: `{{BACKEND_MAIN}}`
7
+ - Frontend: `{{FRONTEND}}`
8
+ - Data modules: `{{DATA_MODULES}}`
9
+ - Extra modules: `{{EXTRA_MODULES}}`
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ echo "[build] building project"
5
+ if [[ -d apps/web && -f apps/web/package.json ]]; then
6
+ if command -v npm >/dev/null 2>&1; then
7
+ (cd apps/web && npm run build || true)
8
+ fi
9
+ fi
10
+
11
+ if [[ -f apps/api/go.mod ]]; then
12
+ if command -v go >/dev/null 2>&1; then
13
+ (cd apps/api && go mod tidy && go build ./...)
14
+ fi
15
+ elif [[ -f apps/api/pom.xml ]]; then
16
+ if command -v mvn >/dev/null 2>&1; then
17
+ (cd apps/api && mvn -q -DskipTests package)
18
+ fi
19
+ fi
20
+
21
+ if [[ -f apps/worker-go/go.mod ]]; then
22
+ if command -v go >/dev/null 2>&1; then
23
+ (cd apps/worker-go && go mod tidy && go build ./...)
24
+ fi
25
+ fi
26
+
27
+ if [[ -f apps/gateway-bff/package.json ]]; then
28
+ if command -v npm >/dev/null 2>&1; then
29
+ (cd apps/gateway-bff && npm run build || true)
30
+ fi
31
+ fi
32
+
33
+ if [[ -f apps/auth-center/package.json ]]; then
34
+ if command -v npm >/dev/null 2>&1; then
35
+ (cd apps/auth-center && npm run build || true)
36
+ fi
37
+ fi
38
+
39
+ echo "[build] done"
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ echo "[check] workspace sanity"
5
+ if [[ ! -f package.json ]]; then
6
+ echo "[check] package.json missing at repo root" >&2
7
+ exit 1
8
+ fi
9
+
10
+ if [[ -d apps/api ]]; then
11
+ if [[ -f apps/api/go.mod ]]; then
12
+ if command -v go >/dev/null 2>&1; then
13
+ (cd apps/api && go mod tidy && go test ./...)
14
+ else
15
+ echo "[check] go missing, skip go test"
16
+ fi
17
+ elif [[ -f apps/api/pom.xml ]]; then
18
+ if command -v mvn >/dev/null 2>&1; then
19
+ (cd apps/api && mvn -q -DskipTests verify)
20
+ else
21
+ echo "[check] maven missing, skip spring verify"
22
+ fi
23
+ fi
24
+ fi
25
+
26
+ if [[ -f apps/worker-go/go.mod ]]; then
27
+ if command -v go >/dev/null 2>&1; then
28
+ (cd apps/worker-go && go mod tidy && go test ./...)
29
+ else
30
+ echo "[check] go missing, skip worker-go tests"
31
+ fi
32
+ fi
33
+
34
+ if [[ -f apps/gateway-bff/server.js ]]; then
35
+ if command -v node >/dev/null 2>&1; then
36
+ node --check apps/gateway-bff/server.js
37
+ else
38
+ echo "[check] node missing, skip gateway-bff syntax check"
39
+ fi
40
+ fi
41
+
42
+ if [[ -f apps/auth-center/server.js ]]; then
43
+ if command -v node >/dev/null 2>&1; then
44
+ node --check apps/auth-center/server.js
45
+ else
46
+ echo "[check] node missing, skip auth-center syntax check"
47
+ fi
48
+ fi
49
+
50
+ if [[ -f apps/python-ai/app/main.py ]]; then
51
+ if command -v python3 >/dev/null 2>&1; then
52
+ python3 -m py_compile apps/python-ai/app/main.py
53
+ else
54
+ echo "[check] python3 missing, skip python-ai compile check"
55
+ fi
56
+ fi
57
+
58
+ echo "[check] done"
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ echo "[dev] starting minimal development flow"
5
+
6
+ if [[ -d apps/web ]]; then
7
+ echo "[dev] frontend available at apps/web"
8
+ fi
9
+
10
+ if [[ -f apps/api/go.mod ]]; then
11
+ if command -v go >/dev/null 2>&1; then
12
+ echo "[dev] running go api"
13
+ (cd apps/api && go mod tidy && go run ./cmd/server)
14
+ else
15
+ echo "[dev] go missing, skip api startup"
16
+ fi
17
+ elif [[ -f apps/api/pom.xml ]]; then
18
+ if command -v mvn >/dev/null 2>&1; then
19
+ echo "[dev] running spring api"
20
+ (cd apps/api && mvn spring-boot:run)
21
+ else
22
+ echo "[dev] maven missing, skip api startup"
23
+ fi
24
+ fi
25
+
26
+ if [[ -f apps/gateway-bff/server.js ]]; then
27
+ echo "[dev] gateway-bff available at apps/gateway-bff"
28
+ fi
29
+
30
+ if [[ -f apps/python-ai/app/main.py ]]; then
31
+ echo "[dev] python-ai available at apps/python-ai"
32
+ fi
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ echo "[test] running tests"
5
+ if [[ -d apps/web && -f apps/web/package.json ]]; then
6
+ if command -v npm >/dev/null 2>&1; then
7
+ (cd apps/web && npm run test -- --run || true)
8
+ fi
9
+ fi
10
+
11
+ if [[ -f apps/api/go.mod ]]; then
12
+ if command -v go >/dev/null 2>&1; then
13
+ (cd apps/api && go mod tidy && go test ./...)
14
+ fi
15
+ elif [[ -f apps/api/pom.xml ]]; then
16
+ if command -v mvn >/dev/null 2>&1; then
17
+ (cd apps/api && mvn -q test)
18
+ fi
19
+ fi
20
+
21
+ if [[ -d apps/worker-python ]]; then
22
+ if command -v python3 >/dev/null 2>&1; then
23
+ (cd apps/worker-python && python3 -m unittest -q)
24
+ else
25
+ echo "[test] python missing, skip worker tests"
26
+ fi
27
+ fi
28
+
29
+ if [[ -f apps/worker-go/go.mod ]]; then
30
+ if command -v go >/dev/null 2>&1; then
31
+ (cd apps/worker-go && go mod tidy && go test ./...)
32
+ fi
33
+ fi
34
+
35
+ if [[ -f apps/python-ai/app/main.py ]]; then
36
+ if command -v python3 >/dev/null 2>&1; then
37
+ python3 -m py_compile apps/python-ai/app/main.py
38
+ fi
39
+ fi
40
+
41
+ echo "[test] done"
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "scripts": {
6
+ "dev": "bash infra/scripts/dev.sh",
7
+ "build": "bash infra/scripts/build.sh",
8
+ "test": "bash infra/scripts/test.sh",
9
+ "check": "bash infra/scripts/check.sh"
10
+ }
11
+ }
@@ -0,0 +1,4 @@
1
+ # MongoDB Module
2
+
3
+ - URI placeholder: `mongodb://localhost:27017/app`
4
+ - Suitable for document-oriented workloads
@@ -0,0 +1,4 @@
1
+ # MySQL Module
2
+
3
+ - DSN placeholder: `mysql://root:root@localhost:3306/app`
4
+ - Suggested for relational production workloads
@@ -0,0 +1,5 @@
1
+ CREATE TABLE IF NOT EXISTS sample_users (
2
+ id BIGINT PRIMARY KEY,
3
+ name VARCHAR(128) NOT NULL,
4
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
5
+ );
@@ -0,0 +1,4 @@
1
+ # PostgreSQL Module
2
+
3
+ - DSN placeholder: `postgres://postgres:postgres@localhost:5432/app`
4
+ - Add pool settings in backend config for production
@@ -0,0 +1,5 @@
1
+ CREATE TABLE IF NOT EXISTS sample_users (
2
+ id BIGSERIAL PRIMARY KEY,
3
+ name TEXT NOT NULL,
4
+ created_at TIMESTAMPTZ DEFAULT NOW()
5
+ );
@@ -0,0 +1,4 @@
1
+ # Redis Module
2
+
3
+ - Use for cache/session/rate-limit placeholders
4
+ - Key naming sample: `app:{domain}:{id}`
@@ -0,0 +1,4 @@
1
+ # SQLite Module
2
+
3
+ - Default file: `./data/app.db`
4
+ - Suitable for local dev / PoC
@@ -0,0 +1,4 @@
1
+ VITE_API_BASE=/api
2
+ VITE_BFF_BASE=/bff
3
+ VITE_DEV_API_TARGET=http://localhost:8080
4
+ VITE_DEV_BFF_TARGET=http://localhost:3001
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{PROJECT_NAME}} web</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}-web",
3
+ "private": true,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "test": "vitest"
10
+ },
11
+ "dependencies": {
12
+ "react": "^18.3.1",
13
+ "react-dom": "^18.3.1"
14
+ },
15
+ "devDependencies": {
16
+ "@types/react": "^18.3.8",
17
+ "@types/react-dom": "^18.3.0",
18
+ "@vitejs/plugin-react": "^4.4.0",
19
+ "typescript": "^5.9.2",
20
+ "vite": "^5.4.8",
21
+ "vitest": "^2.1.3"
22
+ }
23
+ }
@@ -0,0 +1,7 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ describe("app", () => {
4
+ it("basic assertion", () => {
5
+ expect(true).toBe(true);
6
+ });
7
+ });
@@ -0,0 +1,59 @@
1
+ import { useState } from "react";
2
+ import {
3
+ availableServices,
4
+ getApiBase,
5
+ getBffBase,
6
+ hasBffEnabled,
7
+ pingApi,
8
+ pingBff,
9
+ type ApiPayload,
10
+ } from "./api/client";
11
+
12
+ const HAS_BFF = hasBffEnabled();
13
+
14
+ export function App() {
15
+ const [apiResult, setApiResult] = useState<ApiPayload | null>(null);
16
+ const [apiError, setApiError] = useState<string>("");
17
+ const [bffResult, setBffResult] = useState<ApiPayload | null>(null);
18
+ const [bffError, setBffError] = useState<string>("");
19
+
20
+ const callApi = async () => {
21
+ setApiError("");
22
+ try {
23
+ setApiResult(await pingApi());
24
+ } catch (error) {
25
+ setApiError(error instanceof Error ? error.message : String(error));
26
+ }
27
+ };
28
+
29
+ const callBff = async () => {
30
+ if (!HAS_BFF) return;
31
+ setBffError("");
32
+ try {
33
+ setBffResult(await pingBff());
34
+ } catch (error) {
35
+ setBffError(error instanceof Error ? error.message : String(error));
36
+ }
37
+ };
38
+
39
+ return (
40
+ <main style={{ fontFamily: "sans-serif", padding: 24, maxWidth: 900 }}>
41
+ <h1>{{PROJECT_NAME}} - React</h1>
42
+ <p>API base: <code>{getApiBase()}</code> | BFF base: <code>{getBffBase()}</code></p>
43
+ <p>Available services: <code>{availableServices.join(", ")}</code></p>
44
+
45
+ <section style={{ marginBottom: 20 }}>
46
+ <button onClick={callApi}>Call API /api/v1/ping</button>
47
+ {apiError ? <p style={{ color: "crimson" }}>{apiError}</p> : null}
48
+ {apiResult ? <pre>{JSON.stringify(apiResult, null, 2)}</pre> : null}
49
+ </section>
50
+
51
+ <section>
52
+ <button onClick={callBff} disabled={!HAS_BFF}>Call BFF /bff/ping</button>
53
+ {!HAS_BFF ? <p>BFF module not selected. Enable <code>gateway-bff</code> to use this call.</p> : null}
54
+ {bffError ? <p style={{ color: "crimson" }}>{bffError}</p> : null}
55
+ {bffResult ? <pre>{JSON.stringify(bffResult, null, 2)}</pre> : null}
56
+ </section>
57
+ </main>
58
+ );
59
+ }
@@ -0,0 +1,20 @@
1
+ import { type ApiPayload } from "./request";
2
+ import { getApiBase, healthApi, pingApi } from "./services/api";
3
+ import { getBffBase, pingBff as pingBffService } from "./services/bff";
4
+
5
+ const HAS_BFF = "{{HAS_GATEWAY_BFF}}" === "true";
6
+
7
+ export { type ApiPayload, getApiBase, healthApi, pingApi, getBffBase };
8
+
9
+ export const availableServices = HAS_BFF ? ["api", "bff"] as const : ["api"] as const;
10
+
11
+ export async function pingBff(): Promise<ApiPayload<{ message: string }>> {
12
+ if (!HAS_BFF) {
13
+ throw new Error("gateway-bff module not enabled");
14
+ }
15
+ return pingBffService();
16
+ }
17
+
18
+ export function hasBffEnabled(): boolean {
19
+ return HAS_BFF;
20
+ }