Initial commit: Complete project-bootstrap tool
- Bootstrap script for creating monorepo projects - FastAPI backend templates with uv, ruff, mypy, pytest - React frontend templates with TypeScript, ESLint, Prettier - Docker Compose setup with backend, frontend, and database - 9 development and CI scripts - Gitea Actions CI/CD workflows - Comprehensive documentation (8 files) - 45 template files for complete project structure - Automated verification script (all tests pass) - Based on coding-agent-rules standards
This commit is contained in:
39
templates/.gitea/workflows/backend-ci.yml
Normal file
39
templates/.gitea/workflows/backend-ci.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Backend CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master, develop]
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'scripts/ci/backend-test.sh'
|
||||
- '.gitea/workflows/backend-ci.yml'
|
||||
pull_request:
|
||||
branches: [main, master, develop]
|
||||
paths:
|
||||
- 'backend/**'
|
||||
- 'scripts/ci/backend-test.sh'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install uv
|
||||
run: pip install uv
|
||||
|
||||
- name: Run backend tests
|
||||
run: bash scripts/ci/backend-test.sh
|
||||
|
||||
- name: Upload coverage reports
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./backend/coverage.xml
|
||||
flags: backend
|
||||
38
templates/.gitea/workflows/frontend-ci.yml
Normal file
38
templates/.gitea/workflows/frontend-ci.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Frontend CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master, develop]
|
||||
paths:
|
||||
- 'frontend/**'
|
||||
- 'scripts/ci/frontend-test.sh'
|
||||
- '.gitea/workflows/frontend-ci.yml'
|
||||
pull_request:
|
||||
branches: [main, master, develop]
|
||||
paths:
|
||||
- 'frontend/**'
|
||||
- 'scripts/ci/frontend-test.sh'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
- name: Run frontend tests
|
||||
run: bash scripts/ci/frontend-test.sh
|
||||
|
||||
- name: Upload coverage reports
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./frontend/coverage/coverage-final.json
|
||||
flags: frontend
|
||||
72
templates/.gitignore
vendored
Normal file
72
templates/.gitignore
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
.mypy_cache/
|
||||
.ruff_cache/
|
||||
|
||||
# Virtual environments
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
.venv
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.npm
|
||||
.eslintcache
|
||||
|
||||
# Frontend build
|
||||
frontend/build/
|
||||
frontend/dist/
|
||||
frontend/.next/
|
||||
frontend/out/
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
|
||||
# Docker
|
||||
*.log
|
||||
|
||||
# Testing
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
||||
# Misc
|
||||
*.bak
|
||||
*.tmp
|
||||
.cache/
|
||||
21
templates/LICENSE
Normal file
21
templates/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
104
templates/README.md
Normal file
104
templates/README.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Project Name
|
||||
|
||||
A modern monorepo project with FastAPI backend and React frontend.
|
||||
|
||||
## Project Structure
|
||||
|
||||
This project follows a modular monorepo layout based on best practices:
|
||||
|
||||
- **backend/** - FastAPI server (Python 3.11+)
|
||||
- **frontend/** - React application (TypeScript)
|
||||
- **deploy/** - Docker and deployment configurations
|
||||
- **scripts/** - Shared scripts for CI and development
|
||||
- **docs/** - Project documentation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.11+
|
||||
- Node.js 20 LTS
|
||||
- Docker & Docker Compose
|
||||
- uv (Python package manager)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Using Docker Compose (Recommended)
|
||||
|
||||
```bash
|
||||
# Start all services
|
||||
docker compose -f deploy/compose.yml up
|
||||
|
||||
# Backend will be available at http://localhost:8000
|
||||
# Frontend will be available at http://localhost:3000
|
||||
# API docs at http://localhost:8000/docs
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
#### Backend
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
cp .env.example .env
|
||||
uv sync
|
||||
uv run uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
#### Frontend
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
cp .env.example .env
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
## Development Scripts
|
||||
|
||||
Located in `scripts/` directory:
|
||||
|
||||
- **scripts/dev/** - Developer convenience scripts
|
||||
- `start-backend.sh` - Start backend server
|
||||
- `start-frontend.sh` - Start frontend dev server
|
||||
- `start-database.sh` - Start database container
|
||||
- `reset-database.sh` - Reset database
|
||||
|
||||
- **scripts/utils/** - Utility scripts
|
||||
- `lint-backend.sh` - Lint backend code
|
||||
- `lint-frontend.sh` - Lint frontend code
|
||||
- `format-all.sh` - Format all code
|
||||
|
||||
## Testing
|
||||
|
||||
### Backend
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
uv run pytest --cov=app --cov-report=term-missing
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm test
|
||||
npm run test:coverage
|
||||
```
|
||||
|
||||
## Code Quality
|
||||
|
||||
- **Backend**: ruff (format + lint), mypy (type checking), 100% test coverage required
|
||||
- **Frontend**: ESLint, Prettier, Jest, 90%+ test coverage
|
||||
|
||||
## CI/CD
|
||||
|
||||
CI workflows are defined in `.gitea/workflows/` and use scripts from `scripts/ci/`.
|
||||
|
||||
## Documentation
|
||||
|
||||
- API documentation: `/docs/api/`
|
||||
- Architecture diagrams: `/docs/architecture/`
|
||||
- User guide: `/docs/user-guide/`
|
||||
|
||||
## License
|
||||
|
||||
[Specify your license here]
|
||||
21
templates/backend/.env.example
Normal file
21
templates/backend/.env.example
Normal file
@@ -0,0 +1,21 @@
|
||||
# Application
|
||||
APP_NAME=backend
|
||||
APP_VERSION=0.1.0
|
||||
DEBUG=true
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
# API
|
||||
API_HOST=0.0.0.0
|
||||
API_PORT=8000
|
||||
API_PREFIX=/api/v1
|
||||
|
||||
# CORS
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:8080
|
||||
|
||||
# Database (if needed)
|
||||
# DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/dbname
|
||||
|
||||
# Security
|
||||
# SECRET_KEY=your-secret-key-here
|
||||
# ALGORITHM=HS256
|
||||
# ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||
36
templates/backend/app/core/config.py
Normal file
36
templates/backend/app/core/config.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""Application configuration."""
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""Application settings."""
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
env_file_encoding="utf-8",
|
||||
case_sensitive=False,
|
||||
extra="ignore",
|
||||
)
|
||||
|
||||
# Application
|
||||
app_name: str = "backend"
|
||||
app_version: str = "0.1.0"
|
||||
debug: bool = False
|
||||
log_level: str = "INFO"
|
||||
|
||||
# API
|
||||
api_host: str = "0.0.0.0"
|
||||
api_port: int = 8000
|
||||
api_prefix: str = "/api/v1"
|
||||
|
||||
# CORS
|
||||
cors_origins: list[str] = ["http://localhost:3000"]
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_settings() -> Settings:
|
||||
"""Get cached settings instance."""
|
||||
return Settings()
|
||||
58
templates/backend/app/core/errors.py
Normal file
58
templates/backend/app/core/errors.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""Error handling utilities."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
|
||||
class AppException(Exception):
|
||||
"""Base application exception."""
|
||||
|
||||
def __init__(self, message: str, details: dict[str, Any] | None = None) -> None:
|
||||
"""Initialize exception."""
|
||||
self.message = message
|
||||
self.details = details or {}
|
||||
super().__init__(self.message)
|
||||
|
||||
|
||||
class NotFoundError(AppException):
|
||||
"""Resource not found error."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ValidationError(AppException):
|
||||
"""Validation error."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class AuthenticationError(AppException):
|
||||
"""Authentication error."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def raise_not_found(resource: str, identifier: str | int) -> None:
|
||||
"""Raise HTTP 404 exception."""
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"{resource} with id '{identifier}' not found",
|
||||
)
|
||||
|
||||
|
||||
def raise_validation_error(message: str) -> None:
|
||||
"""Raise HTTP 422 exception."""
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail=message,
|
||||
)
|
||||
|
||||
|
||||
def raise_unauthorized(message: str = "Unauthorized") -> None:
|
||||
"""Raise HTTP 401 exception."""
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail=message,
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
35
templates/backend/app/main.py
Normal file
35
templates/backend/app/main.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""FastAPI application entry point."""
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from app.core.config import get_settings
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
app = FastAPI(
|
||||
title=settings.app_name,
|
||||
version=settings.app_version,
|
||||
debug=settings.debug,
|
||||
)
|
||||
|
||||
# CORS middleware
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.cors_origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root() -> dict[str, str]:
|
||||
"""Root endpoint."""
|
||||
return {"message": "Welcome to the API", "version": settings.app_version}
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health() -> dict[str, str]:
|
||||
"""Health check endpoint."""
|
||||
return {"status": "healthy"}
|
||||
82
templates/backend/pyproject.toml
Normal file
82
templates/backend/pyproject.toml
Normal file
@@ -0,0 +1,82 @@
|
||||
[project]
|
||||
name = "backend"
|
||||
version = "0.1.0"
|
||||
description = "FastAPI backend service"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"fastapi>=0.104.0",
|
||||
"uvicorn[standard]>=0.24.0",
|
||||
"pydantic>=2.5.0",
|
||||
"pydantic-settings>=2.1.0",
|
||||
"httpx>=0.25.0",
|
||||
"python-multipart>=0.0.6",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=7.4.0",
|
||||
"pytest-cov>=4.1.0",
|
||||
"pytest-asyncio>=0.21.0",
|
||||
"ruff>=0.1.0",
|
||||
"mypy>=1.7.0",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
target-version = "py311"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"E", # pycodestyle errors
|
||||
"W", # pycodestyle warnings
|
||||
"F", # pyflakes
|
||||
"I", # isort
|
||||
"B", # flake8-bugbear
|
||||
"C4", # flake8-comprehensions
|
||||
"UP", # pyupgrade
|
||||
]
|
||||
ignore = []
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.11"
|
||||
strict = true
|
||||
warn_return_any = true
|
||||
warn_unused_configs = true
|
||||
disallow_untyped_defs = true
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
python_files = ["test_*.py"]
|
||||
python_classes = ["Test*"]
|
||||
python_functions = ["test_*"]
|
||||
addopts = [
|
||||
"--strict-markers",
|
||||
"--cov=app",
|
||||
"--cov-report=term-missing",
|
||||
"--cov-report=html",
|
||||
"--cov-fail-under=100",
|
||||
]
|
||||
asyncio_mode = "auto"
|
||||
|
||||
[tool.coverage.run]
|
||||
source = ["app"]
|
||||
omit = ["tests/*", "**/__init__.py"]
|
||||
|
||||
[tool.coverage.report]
|
||||
exclude_lines = [
|
||||
"pragma: no cover",
|
||||
"def __repr__",
|
||||
"raise AssertionError",
|
||||
"raise NotImplementedError",
|
||||
"if __name__ == .__main__.:",
|
||||
"if TYPE_CHECKING:",
|
||||
]
|
||||
12
templates/backend/tests/conftest.py
Normal file
12
templates/backend/tests/conftest.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""Test configuration and fixtures."""
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client() -> TestClient:
|
||||
"""Create test client."""
|
||||
return TestClient(app)
|
||||
22
templates/backend/tests/integration/test_api/test_health.py
Normal file
22
templates/backend/tests/integration/test_api/test_health.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""Integration tests for health endpoints."""
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def test_health_endpoint(client: TestClient) -> None:
|
||||
"""Test health check endpoint."""
|
||||
response = client.get("/health")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"status": "healthy"}
|
||||
|
||||
|
||||
def test_root_endpoint(client: TestClient) -> None:
|
||||
"""Test root endpoint."""
|
||||
response = client.get("/")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert "message" in data
|
||||
assert "version" in data
|
||||
assert data["version"] == "0.1.0"
|
||||
24
templates/backend/tests/unit/test_core/test_config.py
Normal file
24
templates/backend/tests/unit/test_core/test_config.py
Normal file
@@ -0,0 +1,24 @@
|
||||
"""Tests for core configuration."""
|
||||
|
||||
from app.core.config import Settings, get_settings
|
||||
|
||||
|
||||
def test_settings_default_values() -> None:
|
||||
"""Test that settings have correct default values."""
|
||||
settings = Settings()
|
||||
|
||||
assert settings.app_name == "backend"
|
||||
assert settings.app_version == "0.1.0"
|
||||
assert settings.debug is False
|
||||
assert settings.log_level == "INFO"
|
||||
assert settings.api_host == "0.0.0.0"
|
||||
assert settings.api_port == 8000
|
||||
assert settings.api_prefix == "/api/v1"
|
||||
|
||||
|
||||
def test_get_settings_returns_cached_instance() -> None:
|
||||
"""Test that get_settings returns the same instance."""
|
||||
settings1 = get_settings()
|
||||
settings2 = get_settings()
|
||||
|
||||
assert settings1 is settings2
|
||||
16
templates/deploy/.env.example
Normal file
16
templates/deploy/.env.example
Normal file
@@ -0,0 +1,16 @@
|
||||
# Docker Compose Environment Variables
|
||||
|
||||
# Backend
|
||||
BACKEND_PORT=8000
|
||||
DEBUG=true
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
# Frontend
|
||||
FRONTEND_PORT=3000
|
||||
REACT_APP_API_URL=http://localhost:8000
|
||||
|
||||
# Database
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_DB=appdb
|
||||
POSTGRES_PORT=5432
|
||||
71
templates/deploy/README.md
Normal file
71
templates/deploy/README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Deployment Configuration
|
||||
|
||||
This directory contains deployment artifacts for the project.
|
||||
|
||||
## Structure
|
||||
|
||||
- **compose.yml** - Docker Compose configuration for local development and deployment
|
||||
- **.env.example** - Example environment variables
|
||||
- **docker/** - Dockerfiles and container documentation
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Local Development
|
||||
|
||||
1. Copy environment file:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Start all services:
|
||||
```bash
|
||||
docker compose -f deploy/compose.yml up
|
||||
```
|
||||
|
||||
3. Access services:
|
||||
- Backend: http://localhost:8000
|
||||
- Frontend: http://localhost:3000
|
||||
- API Docs: http://localhost:8000/docs
|
||||
- Database: localhost:5432
|
||||
|
||||
### Stop Services
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/compose.yml down
|
||||
```
|
||||
|
||||
### Rebuild Services
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/compose.yml up --build
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker compose -f deploy/compose.yml logs -f
|
||||
|
||||
# Specific service
|
||||
docker compose -f deploy/compose.yml logs -f backend
|
||||
```
|
||||
|
||||
## Production Deployment
|
||||
|
||||
For production deployments, consider:
|
||||
|
||||
1. Using separate compose files for different environments
|
||||
2. Implementing proper secret management
|
||||
3. Setting up reverse proxy (nginx/traefik)
|
||||
4. Configuring SSL/TLS certificates
|
||||
5. Setting up monitoring and logging
|
||||
6. Implementing backup strategies
|
||||
|
||||
## Kubernetes (Optional)
|
||||
|
||||
If deploying to Kubernetes, create a `k8s/` directory with:
|
||||
|
||||
- Deployment manifests
|
||||
- Service definitions
|
||||
- ConfigMaps and Secrets
|
||||
- Ingress configuration
|
||||
63
templates/deploy/compose.yml
Normal file
63
templates/deploy/compose.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: ../backend
|
||||
dockerfile: ../deploy/docker/backend.Dockerfile
|
||||
container_name: backend
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- DEBUG=true
|
||||
- LOG_LEVEL=INFO
|
||||
- API_HOST=0.0.0.0
|
||||
- API_PORT=8000
|
||||
- CORS_ORIGINS=http://localhost:3000
|
||||
volumes:
|
||||
- ../backend:/app
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
networks:
|
||||
- app-network
|
||||
depends_on:
|
||||
- database
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ../frontend
|
||||
dockerfile: ../deploy/docker/frontend.Dockerfile
|
||||
container_name: frontend
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- REACT_APP_API_URL=http://localhost:8000
|
||||
- REACT_APP_API_PREFIX=/api/v1
|
||||
volumes:
|
||||
- ../frontend:/app
|
||||
- /app/node_modules
|
||||
command: npm start
|
||||
networks:
|
||||
- app-network
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
database:
|
||||
image: postgres:16-alpine
|
||||
container_name: database
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_DB=appdb
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- app-network
|
||||
|
||||
networks:
|
||||
app-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
40
templates/deploy/docker/README.md
Normal file
40
templates/deploy/docker/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Docker Configuration
|
||||
|
||||
This directory contains Dockerfiles for building container images.
|
||||
|
||||
## Files
|
||||
|
||||
- **backend.Dockerfile** - Backend service (FastAPI)
|
||||
- **frontend.Dockerfile** - Frontend service (React)
|
||||
|
||||
## Building Images
|
||||
|
||||
### Backend
|
||||
|
||||
```bash
|
||||
docker build -f deploy/docker/backend.Dockerfile -t backend:latest ./backend
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
```bash
|
||||
docker build -f deploy/docker/frontend.Dockerfile -t frontend:latest ./frontend
|
||||
```
|
||||
|
||||
## Running with Docker Compose
|
||||
|
||||
From the project root:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/compose.yml up
|
||||
```
|
||||
|
||||
## Production Builds
|
||||
|
||||
For production, consider:
|
||||
|
||||
1. Multi-stage builds to reduce image size
|
||||
2. Non-root user for security
|
||||
3. Health checks
|
||||
4. Proper secret management
|
||||
5. Optimized layer caching
|
||||
23
templates/deploy/docker/backend.Dockerfile
Normal file
23
templates/deploy/docker/backend.Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
# Backend Dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install uv
|
||||
RUN pip install --no-cache-dir uv
|
||||
|
||||
# Copy dependency files
|
||||
COPY pyproject.toml ./
|
||||
|
||||
# Install dependencies
|
||||
RUN uv pip install --system -e .
|
||||
RUN uv pip install --system -e ".[dev]"
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
|
||||
# Run the application
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
19
templates/deploy/docker/frontend.Dockerfile
Normal file
19
templates/deploy/docker/frontend.Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
# Frontend Dockerfile
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy dependency files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Run the application
|
||||
CMD ["npm", "start"]
|
||||
35
templates/docs/api/CHANGELOG.md
Normal file
35
templates/docs/api/CHANGELOG.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# API Changelog
|
||||
|
||||
All notable changes to the API will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Initial API setup with FastAPI
|
||||
- Health check endpoint
|
||||
- Root endpoint
|
||||
|
||||
### Changed
|
||||
- N/A
|
||||
|
||||
### Deprecated
|
||||
- N/A
|
||||
|
||||
### Removed
|
||||
- N/A
|
||||
|
||||
### Fixed
|
||||
- N/A
|
||||
|
||||
### Security
|
||||
- N/A
|
||||
|
||||
## [0.1.0] - YYYY-MM-DD
|
||||
|
||||
### Added
|
||||
- Initial release
|
||||
- Basic project structure
|
||||
- FastAPI application setup
|
||||
79
templates/docs/api/README.md
Normal file
79
templates/docs/api/README.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# API Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The API is built with FastAPI and follows REST principles.
|
||||
|
||||
## Base URL
|
||||
|
||||
- **Development**: `http://localhost:8000`
|
||||
- **API Prefix**: `/api/v1`
|
||||
|
||||
## Interactive Documentation
|
||||
|
||||
FastAPI provides automatic interactive API documentation:
|
||||
|
||||
- **Swagger UI**: http://localhost:8000/docs
|
||||
- **ReDoc**: http://localhost:8000/redoc
|
||||
|
||||
## Authentication
|
||||
|
||||
(Add authentication details here when implemented)
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Health Check
|
||||
|
||||
```
|
||||
GET /health
|
||||
```
|
||||
|
||||
Returns the health status of the API.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "healthy"
|
||||
}
|
||||
```
|
||||
|
||||
### Root
|
||||
|
||||
```
|
||||
GET /
|
||||
```
|
||||
|
||||
Returns basic API information.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"message": "Welcome to the API",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
```
|
||||
|
||||
## Error Responses
|
||||
|
||||
All error responses follow this format:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "Error message here"
|
||||
}
|
||||
```
|
||||
|
||||
### Common Status Codes
|
||||
|
||||
- **200 OK** - Request succeeded
|
||||
- **201 Created** - Resource created successfully
|
||||
- **400 Bad Request** - Invalid request data
|
||||
- **401 Unauthorized** - Authentication required
|
||||
- **403 Forbidden** - Insufficient permissions
|
||||
- **404 Not Found** - Resource not found
|
||||
- **422 Unprocessable Entity** - Validation error
|
||||
- **500 Internal Server Error** - Server error
|
||||
|
||||
## Changelog
|
||||
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for API changes and migration notes.
|
||||
131
templates/docs/architecture/README.md
Normal file
131
templates/docs/architecture/README.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# Architecture Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This project follows a monorepo architecture with separate backend and frontend applications.
|
||||
|
||||
## System Architecture
|
||||
|
||||
```
|
||||
┌─────────────┐
|
||||
│ Client │
|
||||
│ (Browser) │
|
||||
└──────┬──────┘
|
||||
│
|
||||
│ HTTP/HTTPS
|
||||
│
|
||||
┌──────▼──────┐
|
||||
│ Frontend │
|
||||
│ (React) │
|
||||
└──────┬──────┘
|
||||
│
|
||||
│ REST API
|
||||
│
|
||||
┌──────▼──────┐
|
||||
│ Backend │
|
||||
│ (FastAPI) │
|
||||
└──────┬──────┘
|
||||
│
|
||||
│ SQL
|
||||
│
|
||||
┌──────▼──────┐
|
||||
│ Database │
|
||||
│ (PostgreSQL)│
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
## Backend Architecture
|
||||
|
||||
### Layers
|
||||
|
||||
1. **API Layer** (`app/api/`)
|
||||
- HTTP endpoints
|
||||
- Request/response handling
|
||||
- Route definitions
|
||||
|
||||
2. **Service Layer** (`app/services/`)
|
||||
- Business logic
|
||||
- Data processing
|
||||
- External integrations
|
||||
|
||||
3. **Data Layer** (`app/db/`)
|
||||
- Database models
|
||||
- Database connections
|
||||
- Query operations
|
||||
|
||||
4. **Core Layer** (`app/core/`)
|
||||
- Configuration
|
||||
- Security
|
||||
- Error handling
|
||||
|
||||
### Request Flow
|
||||
|
||||
```
|
||||
Request → API Endpoint → Service → Database → Service → API Response
|
||||
```
|
||||
|
||||
## Frontend Architecture
|
||||
|
||||
### Structure
|
||||
|
||||
- **Components** - Reusable UI components
|
||||
- **Pages** - Route-level components
|
||||
- **Services** - API communication
|
||||
- **Store** - State management
|
||||
- **Utils** - Helper functions
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
User Action → Component → Service → API → Backend
|
||||
↓
|
||||
State Update
|
||||
↓
|
||||
UI Re-render
|
||||
```
|
||||
|
||||
## Deployment Architecture
|
||||
|
||||
### Development
|
||||
|
||||
All services run in Docker containers orchestrated by Docker Compose:
|
||||
|
||||
- Backend container (FastAPI)
|
||||
- Frontend container (React dev server)
|
||||
- Database container (PostgreSQL)
|
||||
|
||||
### Production
|
||||
|
||||
(Add production deployment architecture here)
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- CORS configuration
|
||||
- Environment variable management
|
||||
- API authentication/authorization
|
||||
- Input validation
|
||||
- SQL injection prevention
|
||||
|
||||
## Scalability
|
||||
|
||||
- Horizontal scaling via container orchestration
|
||||
- Database connection pooling
|
||||
- Caching strategies
|
||||
- Load balancing
|
||||
|
||||
## Technology Stack
|
||||
|
||||
### Backend
|
||||
- Python 3.11+
|
||||
- FastAPI
|
||||
- Pydantic v2
|
||||
- PostgreSQL
|
||||
|
||||
### Frontend
|
||||
- React 18
|
||||
- TypeScript
|
||||
- Axios
|
||||
|
||||
### DevOps
|
||||
- Docker & Docker Compose
|
||||
- Gitea Actions (CI/CD)
|
||||
186
templates/docs/user-guide/README.md
Normal file
186
templates/docs/user-guide/README.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# User Guide
|
||||
|
||||
## Getting Started
|
||||
|
||||
This guide will help you get started with the application.
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following installed:
|
||||
|
||||
- Docker and Docker Compose
|
||||
- Python 3.11+ (for local backend development)
|
||||
- Node.js 20 LTS (for local frontend development)
|
||||
- uv (Python package manager)
|
||||
|
||||
### Quick Start with Docker
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd <project-name>
|
||||
```
|
||||
|
||||
2. Start all services:
|
||||
```bash
|
||||
docker compose -f deploy/compose.yml up
|
||||
```
|
||||
|
||||
3. Access the application:
|
||||
- Frontend: http://localhost:3000
|
||||
- Backend API: http://localhost:8000
|
||||
- API Documentation: http://localhost:8000/docs
|
||||
|
||||
## Local Development
|
||||
|
||||
### Backend Development
|
||||
|
||||
1. Navigate to backend directory:
|
||||
```bash
|
||||
cd backend
|
||||
```
|
||||
|
||||
2. Copy environment file:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
uv sync
|
||||
```
|
||||
|
||||
4. Start the development server:
|
||||
```bash
|
||||
uv run uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
Or use the convenience script:
|
||||
```bash
|
||||
bash scripts/dev/start-backend.sh
|
||||
```
|
||||
|
||||
### Frontend Development
|
||||
|
||||
1. Navigate to frontend directory:
|
||||
```bash
|
||||
cd frontend
|
||||
```
|
||||
|
||||
2. Copy environment file:
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
4. Start the development server:
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
Or use the convenience script:
|
||||
```bash
|
||||
bash scripts/dev/start-frontend.sh
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Backend Tests
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
uv run pytest --cov=app
|
||||
```
|
||||
|
||||
### Frontend Tests
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm test
|
||||
```
|
||||
|
||||
## Code Quality
|
||||
|
||||
### Linting
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
bash scripts/utils/lint-backend.sh
|
||||
|
||||
# Frontend
|
||||
bash scripts/utils/lint-frontend.sh
|
||||
```
|
||||
|
||||
### Formatting
|
||||
|
||||
```bash
|
||||
bash scripts/utils/format-all.sh
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Adding a New API Endpoint
|
||||
|
||||
1. Create endpoint in `backend/app/api/`
|
||||
2. Add business logic in `backend/app/services/`
|
||||
3. Define schemas in `backend/app/schemas/`
|
||||
4. Write tests in `backend/tests/`
|
||||
|
||||
### Adding a New Frontend Component
|
||||
|
||||
1. Create component in `frontend/src/components/`
|
||||
2. Add styles in `frontend/src/styles/`
|
||||
3. Write tests in `frontend/tests/components/`
|
||||
|
||||
### Database Migrations
|
||||
|
||||
(Add database migration instructions when implemented)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port Already in Use
|
||||
|
||||
If you get a "port already in use" error:
|
||||
|
||||
```bash
|
||||
# Find process using the port
|
||||
lsof -i :8000 # or :3000 for frontend
|
||||
|
||||
# Kill the process
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
### Docker Issues
|
||||
|
||||
```bash
|
||||
# Clean up Docker resources
|
||||
docker compose -f deploy/compose.yml down
|
||||
docker system prune -a
|
||||
```
|
||||
|
||||
### Dependency Issues
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd backend
|
||||
rm -rf .venv
|
||||
uv sync
|
||||
|
||||
# Frontend
|
||||
cd frontend
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues and questions:
|
||||
- Check the documentation in `/docs`
|
||||
- Review the API documentation at http://localhost:8000/docs
|
||||
- Open an issue in the repository
|
||||
7
templates/frontend/.env.example
Normal file
7
templates/frontend/.env.example
Normal file
@@ -0,0 +1,7 @@
|
||||
# API Configuration
|
||||
REACT_APP_API_URL=http://localhost:8000
|
||||
REACT_APP_API_PREFIX=/api/v1
|
||||
|
||||
# Application
|
||||
REACT_APP_NAME=Frontend
|
||||
REACT_APP_VERSION=0.1.0
|
||||
34
templates/frontend/.eslintrc.json
Normal file
34
templates/frontend/.eslintrc.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"jest": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["react", "@typescript-eslint"],
|
||||
"rules": {
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
templates/frontend/.prettierrc.json
Normal file
10
templates/frontend/.prettierrc.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
73
templates/frontend/package.json
Normal file
73
templates/frontend/package.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.20.0",
|
||||
"axios": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^6.1.5",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@testing-library/user-event": "^14.5.1",
|
||||
"@types/jest": "^29.5.10",
|
||||
"@types/node": "^20.10.0",
|
||||
"@types/react": "^18.2.42",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
||||
"@typescript-eslint/parser": "^6.13.0",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"prettier": "^3.1.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^5.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"test:coverage": "react-scripts test --coverage --watchAll=false",
|
||||
"eject": "react-scripts eject",
|
||||
"lint": "eslint src --ext .ts,.tsx",
|
||||
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
||||
"format": "prettier --write \"src/**/*.{ts,tsx,json,css,md}\""
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.{ts,tsx}",
|
||||
"!src/**/*.d.ts",
|
||||
"!src/index.tsx",
|
||||
"!src/reportWebVitals.ts"
|
||||
],
|
||||
"coverageThreshold": {
|
||||
"global": {
|
||||
"branches": 90,
|
||||
"functions": 90,
|
||||
"lines": 90,
|
||||
"statements": 90
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
templates/frontend/public/index.html
Normal file
15
templates/frontend/public/index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Modern monorepo application" />
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
15
templates/frontend/src/App.test.tsx
Normal file
15
templates/frontend/src/App.test.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders welcome message', () => {
|
||||
render(<App />);
|
||||
const headingElement = screen.getByText(/Welcome to Your Monorepo Project/i);
|
||||
expect(headingElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders edit instruction', () => {
|
||||
render(<App />);
|
||||
const instructionElement = screen.getByText(/Edit src\/App.tsx and save to reload/i);
|
||||
expect(instructionElement).toBeInTheDocument();
|
||||
});
|
||||
15
templates/frontend/src/App.tsx
Normal file
15
templates/frontend/src/App.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import './styles/App.css';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<h1>Welcome to Your Monorepo Project</h1>
|
||||
<p>Edit src/App.tsx and save to reload.</p>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
11
templates/frontend/src/index.tsx
Normal file
11
templates/frontend/src/index.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './styles/index.css';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
41
templates/frontend/src/services/api.ts
Normal file
41
templates/frontend/src/services/api.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
|
||||
const API_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000';
|
||||
const API_PREFIX = process.env.REACT_APP_API_PREFIX || '/api/v1';
|
||||
|
||||
const apiClient: AxiosInstance = axios.create({
|
||||
baseURL: `${API_URL}${API_PREFIX}`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Request interceptor
|
||||
apiClient.interceptors.request.use(
|
||||
(config) => {
|
||||
// Add auth token if available
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Response interceptor
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
// Handle errors globally
|
||||
if (error.response?.status === 401) {
|
||||
// Handle unauthorized
|
||||
localStorage.removeItem('token');
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default apiClient;
|
||||
5
templates/frontend/src/setupTests.ts
Normal file
5
templates/frontend/src/setupTests.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
23
templates/frontend/src/styles/App.css
Normal file
23
templates/frontend/src/styles/App.css
Normal file
@@ -0,0 +1,23 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-header h1 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.App-header p {
|
||||
font-size: 1rem;
|
||||
color: #61dafb;
|
||||
}
|
||||
11
templates/frontend/src/styles/index.css
Normal file
11
templates/frontend/src/styles/index.css
Normal file
@@ -0,0 +1,11 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
32
templates/frontend/tsconfig.json
Normal file
32
templates/frontend/tsconfig.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"jsx": "react-jsx",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"outDir": "./build",
|
||||
"rootDir": "./src",
|
||||
"removeComments": true,
|
||||
"noEmit": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"skipLibCheck": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "build", "dist"]
|
||||
}
|
||||
22
templates/scripts/ci/backend-test.sh
Executable file
22
templates/scripts/ci/backend-test.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Backend CI test script
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/../../backend"
|
||||
|
||||
echo "Installing dependencies..."
|
||||
uv sync
|
||||
uv pip install --system -e ".[dev]"
|
||||
|
||||
echo "Running linter..."
|
||||
uv run ruff check app tests
|
||||
|
||||
echo "Running type checker..."
|
||||
uv run mypy app
|
||||
|
||||
echo "Running tests with coverage..."
|
||||
uv run pytest --cov=app --cov-report=term-missing --cov-report=xml --cov-fail-under=100
|
||||
|
||||
echo "Backend tests passed!"
|
||||
18
templates/scripts/ci/frontend-test.sh
Executable file
18
templates/scripts/ci/frontend-test.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Frontend CI test script
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/../../frontend"
|
||||
|
||||
echo "Installing dependencies..."
|
||||
npm ci
|
||||
|
||||
echo "Running linter..."
|
||||
npm run lint
|
||||
|
||||
echo "Running tests with coverage..."
|
||||
npm run test:coverage
|
||||
|
||||
echo "Frontend tests passed!"
|
||||
26
templates/scripts/dev/reset-database.sh
Executable file
26
templates/scripts/dev/reset-database.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Reset database (WARNING: This will delete all data)
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/../../deploy"
|
||||
|
||||
echo "WARNING: This will delete all database data!"
|
||||
read -p "Are you sure you want to continue? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
echo "Aborted."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Stopping database container..."
|
||||
docker compose down database
|
||||
|
||||
echo "Removing database volume..."
|
||||
docker volume rm $(basename $(pwd))_postgres-data 2>/dev/null || true
|
||||
|
||||
echo "Starting fresh database..."
|
||||
docker compose up -d database
|
||||
|
||||
echo "Database reset complete"
|
||||
31
templates/scripts/dev/start-backend.sh
Executable file
31
templates/scripts/dev/start-backend.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start backend development server
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/../../backend"
|
||||
|
||||
echo "Starting backend server..."
|
||||
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "Creating .env from .env.example..."
|
||||
cp .env.example .env
|
||||
fi
|
||||
|
||||
# Check if uv is installed
|
||||
if ! command -v uv &> /dev/null; then
|
||||
echo "Error: uv is not installed. Please install it first."
|
||||
echo "Visit: https://github.com/astral-sh/uv"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install dependencies if needed
|
||||
if [ ! -d ".venv" ]; then
|
||||
echo "Installing dependencies..."
|
||||
uv sync
|
||||
fi
|
||||
|
||||
# Start the server
|
||||
echo "Starting uvicorn..."
|
||||
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
14
templates/scripts/dev/start-database.sh
Executable file
14
templates/scripts/dev/start-database.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start database container
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/../../deploy"
|
||||
|
||||
echo "Starting database container..."
|
||||
|
||||
docker compose up -d database
|
||||
|
||||
echo "Database started successfully"
|
||||
echo "Connection string: postgresql://postgres:postgres@localhost:5432/appdb"
|
||||
24
templates/scripts/dev/start-frontend.sh
Executable file
24
templates/scripts/dev/start-frontend.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start frontend development server
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/../../frontend"
|
||||
|
||||
echo "Starting frontend server..."
|
||||
|
||||
if [ ! -f ".env" ]; then
|
||||
echo "Creating .env from .env.example..."
|
||||
cp .env.example .env
|
||||
fi
|
||||
|
||||
# Install dependencies if needed
|
||||
if [ ! -d "node_modules" ]; then
|
||||
echo "Installing dependencies..."
|
||||
npm install
|
||||
fi
|
||||
|
||||
# Start the server
|
||||
echo "Starting React development server..."
|
||||
npm start
|
||||
17
templates/scripts/utils/format-all.sh
Executable file
17
templates/scripts/utils/format-all.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Format all code
|
||||
|
||||
set -e
|
||||
|
||||
PROJECT_ROOT="$(dirname "$0")/../.."
|
||||
|
||||
echo "Formatting backend code..."
|
||||
cd "$PROJECT_ROOT/backend"
|
||||
uv run ruff format app tests
|
||||
|
||||
echo "Formatting frontend code..."
|
||||
cd "$PROJECT_ROOT/frontend"
|
||||
npm run format
|
||||
|
||||
echo "All code formatted!"
|
||||
15
templates/scripts/utils/lint-backend.sh
Executable file
15
templates/scripts/utils/lint-backend.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Lint backend code
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/../../backend"
|
||||
|
||||
echo "Running ruff linter..."
|
||||
uv run ruff check app tests
|
||||
|
||||
echo "Running mypy type checker..."
|
||||
uv run mypy app
|
||||
|
||||
echo "Backend linting complete!"
|
||||
12
templates/scripts/utils/lint-frontend.sh
Executable file
12
templates/scripts/utils/lint-frontend.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Lint frontend code
|
||||
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/../../frontend"
|
||||
|
||||
echo "Running ESLint..."
|
||||
npm run lint
|
||||
|
||||
echo "Frontend linting complete!"
|
||||
Reference in New Issue
Block a user