Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:46:40 +08:00
commit 66f1bf4fb0
33 changed files with 6059 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
# React Setup Skill (Vite + Django Backend)
Spin up a fresh React app (Vite) that talks to a Django backend over HTTPS with correct CORS/CSRF handling, Axios defaults, and a basic router/layout scaffold.
## What This Skill Does
- Initializes a new Vite React project in `frontend/`.
- Configures `vite.config.js` for HTTPS dev using mkcert certs from `certs/`, port 5173, and proxying `/api` to `https://localhost:8000`.
- Adds CSRF service and Axios interceptors that inject `X-CSRFToken` for mutating requests.
- Sets up React Router layout (`Root`), navbar/footer, home page, and backend health check component that verifies CSRF + `/api/health/`.
- Provides a minimal API config file to centralize endpoints.
- Validates the build with `npm --prefix ./frontend run build`.
## Prerequisites
- Django backend running over HTTPS (see `django-setup` skill) with endpoints `/api/csrf/` and `/api/health/`.
- mkcert certificates stored in project `certs/` (key/cert filenames discovered in skill steps).
- Node.js 18+ and npm available.
## Setup Summary (see SKILL.md for exact commands)
1) **Create project**: `npm create vite@latest frontend -- --template react --yes`; then `npm --prefix ./frontend install react-router-dom axios`.
2) **Configure `vite.config.js`**: import `fs`/`path`; load mkcert key/cert from `certs/`; set `server.https`, `server.port=5173`, and proxy `/api``https://localhost:8000` with `secure:false`.
3) **API config** `src/config/api.js`: export `API_BASE_URL` (empty to use proxy) and `ENDPOINTS` with `CSRF: '/api/csrf/'`.
4) **CSRF service** `src/services/csrf.js`: fetch `/api/csrf/` once, read `csrftoken` cookie, cache/token helpers.
5) **Axios instance** `src/services/api.js`: `withCredentials:true`, JSON headers; request interceptor adds `X-CSRFToken` via `getCsrfToken()` for POST/PUT/PATCH/DELETE.
6) **UI scaffold**: navbar/footer components; `Root` layout with `Outlet`; `Home` page using `BackendStatus` component that pings CSRF then `/api/health/`.
7) **Routing**: `src/router/AppRoutes.jsx` exports `createAppRouter()` config; `src/router/index.jsx` wraps `RouterProvider`; `App.jsx` renders `<Router />`.
8) **Verify**: `npm --prefix ./frontend run build` to ensure config/imports succeed.
## Outputs/Artifacts
- Vite React app under `frontend/` with HTTPS dev server ready to hit Django.
- `vite.config.js` with mkcert HTTPS + API proxy.
- Axios + CSRF utilities wired for Django session auth.
- Minimal router/layout/pages demonstrating backend connectivity.
## Notes & Gotchas
- Make sure `certs/` exists and filenames match what mkcert generated; update `vite.config.js` accordingly.
- Backend must expose `/api/csrf/` to set the `csrftoken` cookie; otherwise Axios interceptor will fail.
- If you change backend host/port, update the proxy target in `vite.config.js` and any hardcoded `FRONTEND_URL` on the Django side.

268
skills/react-setup/SKILL.md Normal file
View File

@@ -0,0 +1,268 @@
---
name: react-setup
description: Use when initializing a new React frontend with Vite to connect to a Django backend over HTTPS. Sets up routing, CSRF protection, Axios config, and validates the build. Not for existing React projects.
allowed-tools: Bash, Write, Edit, Read, Glob, Grep, TodoWrite, Task
---
## Overview
Sets up a production-ready React + Vite frontend configured for Django backend integration with:
- HTTPS local development (using mkcert certificates)
- CSRF token handling for Django session auth
- Axios interceptors for automatic CSRF injection
- React Router with layout structure
- Production build validation
**When to use:** New React projects that need Django backend connectivity
**When NOT to use:** Existing React projects, non-Django backends, or projects not using session auth
## Prerequisites
- Django backend configured with HTTPS
- mkcert certificates in `/certs/` directory (use `mkcert-https-setup` skill)
- Node.js 18+ installed
---
Copy this checklist and track your progress:
- [ ] 1. Initialize React + Vite Project
- [ ] 2. Configure vite.config.js:
- [ ] 3. Create API Configuration
- [ ] 4. Create CSRF Service
- [ ] 5. Create Axios Configuration
- [ ] 6. Create Navbar and Footer Components
- [ ] 7. Create `src/layouts/Root.jsx`:
- [ ] 8. Create Page Components
- [ ] 9. Create `src/router/AppRoutes.jsx`
- [ ] 10. Create Router Wrapper
- [ ] 11. Update App.jsx
- [ ] 12. Verify Setup
---
1. Initialize React + Vite Project
- `npm create vite@latest frontend -- --template react --yes`
- `npm --prefix ./frontend install react-router-dom axios`
---
2. Configure vite.config.js:
1. List cert files in `certs/` to find actual filenames
2. Read existing `frontend/vite.config.js` (created by Vite)
3. Add imports: `fs`, `path`
4. Add server config:
- server.https: Load key/cert from `certs/` using found filenames
- server.port: 5173
- server.proxy: `/api``https://localhost:8000` (changeOrigin: true, secure: false)
---
3. Create API Configuration
src/config/api.js:
```javascript
export const API_BASE_URL = ''; // Empty string uses Vite proxy
export const ENDPOINTS = {
CSRF: '/api/csrf/',
// Add your API endpoints here as you build features
// Example: PRODUCTS: '/api/products/',
};
```
---
4. Create CSRF Service
src/services/csrf.js:
```javascript
import { ENDPOINTS } from '../config/api';
let csrfToken = null;
/**
* Fetches and caches the CSRF token from Django backend
* The token is stored in a cookie by Django after the first request
*/
export async function getCsrfToken() {
// Return cached token if available
if (csrfToken) {
return csrfToken;
}
try {
const response = await fetch(ENDPOINTS.CSRF, {
credentials: 'include',
headers: {
'Accept': 'application/json',
}
});
if (!response.ok) {
throw new Error(`Failed to fetch CSRF token: ${response.status}`);
}
// Django sets the token in a cookie after this request
const cookies = document.cookie.split(';');
const csrfCookie = cookies.find(cookie => cookie.trim().startsWith('csrftoken='));
if (!csrfCookie) {
throw new Error('CSRF token not found in cookies');
}
csrfToken = csrfCookie.split('=')[1].trim();
return csrfToken;
} catch (error) {
console.error('CSRF token fetch error:', error);
throw error;
}
}
/**
* Clears the cached CSRF token (useful for logout or token refresh)
*/
export function clearCsrfToken() {
csrfToken = null;
}
```
---
5. Create Axios Configuration
src/services/api.js:
```javascript
import axios from 'axios';
import { API_BASE_URL } from '../config/api';
import { getCsrfToken } from './csrf'; // ADD THIS
const axiosInstance = axios.create({
baseURL: API_BASE_URL,
withCredentials: true,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
// REQUEST INTERCEPTOR
axiosInstance.interceptors.request.use(
async (config) => {
if (['post', 'put', 'patch', 'delete'].includes(config.method.toLowerCase())) {
const csrfToken = await getCsrfToken();
config.headers['X-CSRFToken'] = csrfToken;
}
return config;
}
);
// Response interceptor
axiosInstance.interceptors.response.use(
response => response,
error => {
console.error('API request failed:', error);
return Promise.reject(error);
}
);
export default axiosInstance;
```
---
6. Create Navbar and Footer Components
Create `src/components/Navbar.jsx`:
```javascript
export default function Navbar() {
return (
<nav>
<h1>My App</h1>
</nav>
);
}
```
Create `src/components/Footer.jsx`:
```javascript
export default function Footer() {
return (
<footer>
<p>&copy; {new Date().getFullYear()} My App. All rights reserved.</p>
</footer>
);
}
```
---
7. Create `src/layouts/Root.jsx`:
```javascript
import { Outlet } from 'react-router-dom';
import Navbar from '../components/Navbar';
import Footer from '../components/Footer';
export default function Root() {
return (
<div>
<Navbar />
<main>
<Outlet />
</main>
<Footer />
</div>
);
}
```
---
8. Create Page Components
Create `src/components/BackendStatus.jsx`:
1. Import axios from `../services/api`, useState, useEffect
2. Initialize state: `status = "checking..."`, `error = null`
3. useEffect hook (runs on mount):
- First: `axios.get('/api/csrf/')` to initialize cookies
- Second: `axios.get('/api/health/')` using same axios instance
- Success path: update status with backend message
- Error path: set error state, display "Backend unreachable"
4. Component is presentational only (no routing/layout logic)
Create `src/pages/Home.jsx`:
1. Render `<BackendStatus />`
2. Add heading + text explaining the backend connectivity check
---
9. Create `src/router/AppRoutes.jsx`:
1. Export `createAppRouter()` function
2. Returns route config array: Root layout at "/" with Home page as nested "/" child route
3. Imports: `Root` from `../layouts/Root`, `Home` from `../pages/Home`
---
10. Create `src/router/index.jsx`:
1. Export default `AppRouter` component
2. Create browser router from `createAppRouter()` config
3. Return `<RouterProvider router={router} />`
4. Imports: `createBrowserRouter`, `RouterProvider` from react-router-dom, `createAppRouter` from `./AppRoutes`
---
11. Update `src/App.jsx`:
- Remove: All useState, demo imports (reactLogo, viteLogo, App.css), counter UI
- Add imports: `Router` from `./router`
- Render: `<Router />`
---
12. Verify Setup
Run `npm --prefix ./frontend run build` to validate imports, dependencies, and syntax
**Common issues:**
- Missing certs: Check `/certs/` directory exists with .pem files
- Module not found: Verify all imports use correct paths
- CSRF errors: Ensure Django backend is running at `https://localhost:8000`