- Removed infinite query options and related functions from Trading212, User Registration, and User API files. - Updated API client base URLs in privateClient.ts and publicClient.ts to use environment variable for backend URL. - Refactored Downloader component to directly call API functions for video info retrieval instead of using a hook.
223 lines
8.3 KiB
Markdown
223 lines
8.3 KiB
Markdown
# Copilot Instructions for Vontor CZ
|
|
|
|
## Overview
|
|
This monorepo contains a Django backend and a Vite/React frontend, orchestrated via Docker Compose. The project is designed for a Czech e-marketplace, with custom payment integrations and real-time features.
|
|
|
|
## Architecture
|
|
- **backend/**: Django project (`vontor_cz`), custom `account` app, and third-party payment integrations (`thirdparty/`).
|
|
- Uses Django REST Framework, Channels (ASGI), Celery, and S3/static/media via `django-storages`.
|
|
- Custom user model: `account.CustomUser`.
|
|
- API docs: DRF Spectacular (`/api/schema/`).
|
|
- **frontend/**: Vite + React + TypeScript app.
|
|
- Organized by `src/api/`, `components/`, `features/`, `layouts/`, `pages/`, `routes/`.
|
|
- Uses React Router layouts and nested routes (see `src/layouts/`, `src/routes/`).
|
|
- Uses Tailwind CSS for styling (configured via `src/index.css` with `@import "tailwindcss";`). Prefer utility classes over custom CSS.
|
|
|
|
## Developer Workflows
|
|
- **Backend**
|
|
- Local dev: `python manage.py runserver` (or use Docker Compose)
|
|
- Migrations: `python manage.py makemigrations && python manage.py migrate`
|
|
- Celery: `celery -A vontor_cz worker -l info`
|
|
- Channels: Daphne/ASGI (see Docker Compose command)
|
|
- Env config: `.env` files in `backend/` (see `.gitignore` for secrets)
|
|
- **Frontend**
|
|
- Install: `npm install`
|
|
- Dev server: `npm run dev`
|
|
- Build: `npm run build`
|
|
- Preview: `npm run preview`
|
|
- Static assets: `src/assets/` (import in JS/CSS), `public/` (referenced in HTML)
|
|
|
|
## Conventions & Patterns
|
|
- **Backend**
|
|
- Use environment variables for secrets and config (see `settings.py`).
|
|
- Static/media files: S3 in production, local in dev (see `settings.py`).
|
|
- API versioning and docs: DRF Spectacular config in `settings.py`.
|
|
- Custom permissions, filters, and serializers in each app.
|
|
- **Frontend**
|
|
- Use React Router layouts for shared UI (see `src/layouts/`, `LAYOUTS.md`).
|
|
- API calls and JWT handling in `src/api/`.
|
|
- Route definitions and guards in `src/routes/` (`ROUTES.md`).
|
|
- Use TypeScript strict mode (see `tsconfig.*.json`).
|
|
- Linting: ESLint config in `eslint.config.js`.
|
|
- Styling: Tailwind CSS is present. Prefer utility classes; keep minimal component-scoped CSS. Global/base styles live in `src/index.css`. Avoid inline styles and CSS-in-JS unless necessary.
|
|
|
|
### Frontend API Client (required)
|
|
All frontend API calls must use the shared client at frontend/src/api/Client.ts.
|
|
|
|
- Client.public: no cookies, no Authorization header (for public Django endpoints).
|
|
- Client.auth: sends cookies and includes Bearer token; auto-refreshes on 401 (retries up to 2x).
|
|
- Centralized error handling: subscribe via Client.onError to show toasts/snackbars.
|
|
- Tokens are stored in cookies by Client.setTokens and cleared by Client.clearTokens.
|
|
|
|
Example usage (TypeScript)
|
|
```ts
|
|
import Client from "@/api/Client";
|
|
|
|
// Public request (no credentials)
|
|
async function listPublicItems() {
|
|
const res = await Client.public.get("/api/public/items/");
|
|
return res.data;
|
|
}
|
|
|
|
// Login (obtain tokens and persist to cookies)
|
|
async function login(username: string, password: string) {
|
|
// Default SimpleJWT endpoint (adjust if your backend differs)
|
|
const res = await Client.public.post("/api/token/", { username, password });
|
|
const { access, refresh } = res.data;
|
|
Client.setTokens(access, refresh);
|
|
}
|
|
|
|
// Authenticated requests (auto Bearer + refresh on 401)
|
|
async function fetchProfile() {
|
|
const res = await Client.auth.get("/api/users/me/");
|
|
return res.data;
|
|
}
|
|
|
|
function logout() {
|
|
Client.clearTokens();
|
|
window.location.assign("/login");
|
|
}
|
|
|
|
// Global error toasts
|
|
import { useEffect } from "react";
|
|
function useApiErrors(showToast: (msg: string) => void) {
|
|
useEffect(() => {
|
|
const unsubscribe = Client.onError((e) => {
|
|
const { message, status } = e.detail;
|
|
showToast(status ? `${status}: ${message}` : message);
|
|
});
|
|
return unsubscribe;
|
|
}, [showToast]);
|
|
}
|
|
```
|
|
|
|
Vite env used by the client:
|
|
- VITE_API_BASE_URL (default: http://localhost:8000)
|
|
- VITE_API_REFRESH_URL (default: /api/token/refresh/)
|
|
- VITE_LOGIN_PATH (default: /login)
|
|
|
|
Notes
|
|
- Public client never sends cookies or Authorization.
|
|
- Ensure Django CORS settings allow your frontend origin. See backend/vontor_cz/settings.py.
|
|
- Use React Router layouts and guards as documented in frontend/src/routes/ROUTES.md and frontend/src/layouts/LAYOUTS.md.
|
|
|
|
## Integration Points
|
|
- **Payments**: `thirdparty/` contains custom integrations for Stripe, GoPay, Trading212.
|
|
- **Real-time**: Django Channels (ASGI, Redis) for websockets.
|
|
- **Task queue**: Celery + Redis for async/background jobs.
|
|
- **API**: REST endpoints, JWT auth, API key support.
|
|
|
|
### OpenAPI Client Generation (Orval)
|
|
This project uses **Orval** to auto-generate TypeScript API clients from the Django OpenAPI schema.
|
|
|
|
#### Configuration
|
|
- **Orval config**: `frontend/src/orval.config.ts`
|
|
- **Schema URL**: `/api/schema/` (DRF Spectacular endpoint)
|
|
- **Fetch script**: `frontend/scripts/fetch-openapi.js`
|
|
- **Commands**:
|
|
- `npm run api:update` — fetches schema + generates client
|
|
- Runs: `node scripts/fetch-openapi.js && npx orval`
|
|
|
|
#### Generated Output
|
|
- **Location**: `frontend/src/api/generated/`
|
|
- **Files**: TypeScript interfaces, Axios-based API hooks
|
|
- Uses custom mutators: `publicMutator` and `privateMutator`
|
|
|
|
#### Custom Mutators
|
|
Two Axios clients handle public/private API requests:
|
|
|
|
**Public Client** (`frontend/src/api/publicClient.ts`):
|
|
```ts
|
|
import axios, { type AxiosRequestConfig } from "axios";
|
|
|
|
const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:8000";
|
|
|
|
export const publicApi = axios.create({
|
|
baseURL: backendUrl + "/api/",
|
|
withCredentials: false, // no cookies for public endpoints
|
|
});
|
|
|
|
export const publicMutator = async <T>(config: AxiosRequestConfig): Promise<T> => {
|
|
const response = await publicApi.request<T>(config);
|
|
return response.data;
|
|
};
|
|
```
|
|
|
|
**Private Client** (`frontend/src/api/privateClient.ts`):
|
|
```ts
|
|
import axios, { type AxiosRequestConfig } from "axios";
|
|
|
|
const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:8000";
|
|
|
|
export const privateApi = axios.create({
|
|
baseURL: backendUrl + "/api/",
|
|
withCredentials: true, // sends HttpOnly cookies (access/refresh tokens)
|
|
});
|
|
|
|
// Auto-refresh on 401
|
|
privateApi.interceptors.response.use(
|
|
(res) => res,
|
|
async (error) => {
|
|
const original = error.config;
|
|
if (error.response?.status === 401 && !original._retry) {
|
|
original._retry = true;
|
|
try {
|
|
await privateApi.post("/auth/refresh/");
|
|
return privateApi(original);
|
|
} catch {
|
|
// optional: logout
|
|
}
|
|
}
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
export const privateMutator = async <T>(config: AxiosRequestConfig): Promise<T> => {
|
|
const response = await privateApi.request<T>(config);
|
|
return response.data;
|
|
};
|
|
```
|
|
|
|
#### Environment Variables (Vite)
|
|
- **IMPORTANT**: Use `import.meta.env.VITE_*` instead of `process.env` in browser code
|
|
- **NEVER** import `dotenv/config` in frontend files (causes "process is not defined" error)
|
|
- **Available vars**:
|
|
- `VITE_BACKEND_URL` (default: `http://localhost:8000`)
|
|
- `VITE_API_BASE_URL` (if using Client.ts wrapper)
|
|
- `VITE_API_REFRESH_URL` (default: `/api/token/refresh/`)
|
|
- `VITE_LOGIN_PATH` (default: `/login`)
|
|
|
|
#### Usage Example
|
|
```ts
|
|
import { useGetOrders } from "@/api/generated/orders";
|
|
|
|
function OrdersList() {
|
|
const { data, isLoading, error } = useGetOrders();
|
|
|
|
if (isLoading) return <div>Loading...</div>;
|
|
if (error) return <div>Error: {error.message}</div>;
|
|
|
|
return (
|
|
<ul>
|
|
{data?.map(order => <li key={order.id}>{order.status}</li>)}
|
|
</ul>
|
|
);
|
|
}
|
|
```
|
|
|
|
#### Helpers
|
|
- **Choices helper**: `frontend/src/api/get_choices.ts`
|
|
- Function: `getChoices(requests, lang)`
|
|
- Returns: `{ "Model.field": [{ value, label }] }`
|
|
|
|
## References
|
|
- [frontend/REACT.md](../frontend/REACT.md): Frontend structure, workflows, and conventions.
|
|
- [frontend/src/layouts/LAYOUTS.md](../frontend/src/layouts/LAYOUTS.md): Layout/component patterns.
|
|
- [frontend/src/routes/ROUTES.md](../frontend/src/routes/ROUTES.md): Routing conventions.
|
|
- [backend/vontor_cz/settings.py](../backend/vontor_cz/settings.py): All backend config, env, and integration details.
|
|
- [docker-compose.yml](../docker-compose.yml): Service orchestration and dev workflow.
|
|
|
|
---
|
|
|
|
**When in doubt, check the referenced markdown files and `settings.py` for project-specific logic and patterns.**
|