# AGENTS.md ## Build, Lint, and Test Commands ### Backend (Rust) ```bash cd backend && cargo build # Build project cd backend && cargo test # Run all tests cd backend && cargo test my_test # Run test matching pattern cd backend && cargo test -- --exact # Run exact test name cd backend && cargo test -- --test-threads=1 # Run tests sequentially cd backend && cargo test -- --nocapture # Show test output cd backend && cargo test --lib # Library tests only cd backend && cargo fmt # Format code cd backend && cargo fmt --check # Check formatting cd backend && cargo clippy # Lint code ``` ### Frontend (React + TypeScript) ```bash cd frontend && npm install # Install dependencies cd frontend && npm run dev # Start dev server (port 3000) cd frontend && npm run build # Build for production (tsc + vite) cd frontend && npm run lint # Lint with ESLint cd frontend && npx tsc --noEmit # Type check only cd frontend && npm test # Run Jest tests cd frontend && npm run test:watch # Run tests in watch mode ``` ### Docker ```bash docker-compose up -d # Start all services docker-compose logs -f backend # View backend logs docker-compose logs -f frontend # View frontend build/logs docker-compose down # Stop all services docker cp frontend/dist/. sap-sync-frontend:/usr/share/nginx/html/ # Update frontend in container docker exec sap-sync-frontend nginx -s reload # Reload nginx ``` ## Code Style Guidelines ### Rust Backend **Imports** — Alphabetical order, grouped with blank lines: `std` → external → local. Use full paths: `crate::handlers::sync`. Avoid wildcard imports. **Naming** — Types: `PascalCase`. Functions: `snake_case`. Constants: `SCREAMING_SNAKE_CASE`. Modules: `snake_case`. **Error Handling** — Use `thiserror::Error` for typed errors. Use `ApiError` in HTTP handlers. Log with `tracing::{info, error}` macros. Never use `unwrap()`; prefer `?`, `match`, or `unwrap_or_else()`. Convert errors to `String` for `ApiError::Database`. **Types** — `i32` for DB IDs, `f64` for decimals. `String` for owned text, `&str` for borrowed. `Option` for nullable, `Vec` for collections. `Arc` for shared state. **Handlers** — Return `impl IntoResponse`. Use `build_response()` helper for consistent JSON responses. Extract DB connections via `state.pool.get()` with error handling. **Database** — Use `r2d2` connection pooling. Parameterized queries (`$1`, `$2`). Always handle pool errors. ### React Frontend **Imports** — Alphabetical, grouped: external → lib → components → pages. Named imports preferred. **Formatting** — 2-space indent, single quotes, no semicolons, 100-char line limit (Prettier). **Naming** — Components: `PascalCase`. Hooks: `useCamelCase`. Variables: `camelCase`. **TypeScript** — Strict mode enabled. Avoid `any`; use `unknown` or generics. Define interfaces for API responses. Prefer `type` over `interface` for unions. **Error Handling** — Use try/catch for async ops. Log with `logger.error()`. Show user feedback via `toast.success()`/`toast.error()`. Use MUI Dialogs for confirmations, not `window.confirm`. **Components** — Functional components with hooks. `useCallback` for handlers, `useMemo` for expensive calcs. Avoid inline functions in JSX props. ## Project Structure ``` SAP-PLEX-SYNC/ ├── backend/src/ │ ├── lib.rs # Library exports │ ├── main.rs # Entry point (rouille HTTP server) │ ├── handlers_sync.rs # Sync API handlers (axum - not currently used) │ ├── handlers.rs # Other HTTP handlers (legacy) │ ├── models.rs # Data models │ ├── state.rs # AppState (r2d2 pool) │ ├── errors.rs # ApiError enum │ ├── response.rs # Response helpers │ ├── sync.rs # Sync types & structs │ ├── plesk_client.rs # Plesk API client │ ├── sap_client.rs # SAP API client │ ├── config.rs # Configuration │ └── ... ├── frontend/src/ │ ├── pages/ # Page components │ ├── components/ # Reusable components │ ├── lib/api.ts # API client (apiJson) │ ├── lib/hooks.ts # usePolling, formatDate │ ├── lib/logger.ts # Logging utility │ └── contexts/ # React contexts ├── database/ # Migrations and seeds └── docker-compose.yml ``` ## Cursor Rules (.cursorrules) - Follow `cargo fmt` and `cargo clippy` before committing - Use `anyhow::Result` for error propagation, `ApiError` for HTTP - Return `impl IntoResponse` for axum handlers (note: main server uses rouille) - Functional components with hooks; avoid inline JSX functions - Use `useCallback`/`useMemo` for performance - Test user interactions with React Testing Library, not implementation details - Always check for unsafe `unwrap()` calls and replace with proper error handling - Use proper logging instead of `console.log` in frontend - Keep functions small and focused - Use meaningful variable and function names - Add comments for complex logic ## API Response Format Backend returns flat JSON (no `data` wrapper): ```json { "is_running": false, "stats": { "running": 0 }, "jobs": [...] } ``` For server listings, returns direct array: `[{ "id": 1, "name": "test", ... }]` Frontend uses `apiJson()` from `lib/api.ts` for typed API calls. Frontend proxy: `/api` → `http://localhost:3001` (configured in `vite.config.ts`). ## Testing **Backend** — Tests in `#[cfg(test)] mod tests` blocks. Use `#[test]` for sync, `#[tokio::test]` for async. Test both happy paths and error cases. Mock external deps with `mockall`. To run a specific test: `cd backend && cargo test test_function_name -- --exact` To run tests matching a pattern: `cd backend && cargo test pattern_name` To run tests sequentially: `cd backend && cargo test -- --test-threads=1` To see test output: `cd backend && cargo test -- --nocapture` **Frontend** — Jest + React Testing Library. Use `screen.getByRole`/`getByText`. Test user interactions, not implementation. Mock API with `jest.mock()`. To run frontend tests: `cd frontend && npm test` To run tests in watch mode: `cd frontend && npm run test:watch` ## Git Workflow Commits: `type(scope): description` (e.g., `fix(handlers): resolve API mismatch`). Types: `feat`, `fix`, `docs`, `refactor`, `chore`. Branches: `feature/`, `bugfix/`, `hotfix/`. ## Key Reminders 1. Build and lint before committing: `cargo build --lib`, `cargo fmt`, `cargo clippy`, `npm run build` 2. Update Docker container after frontend changes: `docker cp frontend/dist/. sap-sync-frontend:/usr/share/nginx/html/` 3. Test API endpoints with `curl http://localhost/api/...` to verify response format 4. Use `tracing::{info, error}` for backend logging, `logger.error()` for frontend 5. Never use `unwrap()` in production code; use `?` or proper error handling 6. Avoid `any` in TypeScript; use `unknown` or proper types 7. Use MUI Dialogs for confirmations, not `window.confirm` 8. Use `toast.success()`/`toast.error()` for user feedback, not `alert()` 9. Remember that the main HTTP server uses rouille (not axum) in `main.rs` 10. Database column for passwords is named `password_hash`, not `password`