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:
2025-10-15 21:34:08 -04:00
commit 8dd4f0ca63
56 changed files with 3979 additions and 0 deletions

View 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

View 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"
}
}
}

View File

@@ -0,0 +1,10 @@
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"arrowParens": "always",
"endOfLine": "lf"
}

View 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
}
}
}
}

View 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>

View 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();
});

View 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;

View 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>
);

View 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;

View 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';

View 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;
}

View 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;
}

View 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"]
}