docs: README + casdoor-auth guide for the v1.3 changes
- README: Traefik edge (app at http://localhost), single postgres (casdoor DB inside), prod overlay usage, partial receipts, admin panel, dev-mode 3-step flow, layout for infra/ and 02-casdoor-db.sql - docs/casdoor-auth.md: new section «Stack changes in this release» (DB merge, Traefik, RBAC, partial receipt, calendar, upload, project filter), updated addresses and redirect-URI notes
This commit is contained in:
@@ -5,20 +5,26 @@ folder — no external files or sibling folders required.
|
||||
|
||||
| Service | Container | Tech | Exposed port |
|
||||
|---|---|---|---|
|
||||
| **postgres** | `procurement-postgres` | PostgreSQL 18 | `5433` |
|
||||
| **postgres** | `procurement-postgres` | PostgreSQL 18 (one instance: `procurement` + `casdoor` DBs) | `5433` |
|
||||
| **minio** | `procurement-minio` | MinIO object storage (S3-compatible) | `9000` (API), `9001` (console) |
|
||||
| **casdoor** | `procurement-casdoor` | Casdoor (OIDC identity provider) | `8080` |
|
||||
| **api** | `procurement-api` | FastAPI + SQLAlchemy (asyncpg) | `8000` |
|
||||
| **frontend** | `procurement-frontend` | React 19 + Vite, served by nginx | `3000` |
|
||||
| **frontend** | `procurement-frontend` | React 19 + Vite, served by nginx (internal) | via Traefik |
|
||||
| **traefik** | `procurement-traefik` | Traefik v3 — public reverse proxy (edge) | `80` (web), `443` (websecure) |
|
||||
|
||||
```
|
||||
Browser ──► frontend :3000 (nginx SPA)
|
||||
Browser ──► Traefik :80 (edge)
|
||||
│
|
||||
└── /api/* proxy ──► api :8000 (FastAPI, /api/v1)
|
||||
│
|
||||
├── postgres :5432 (procurement DB)
|
||||
├── minio :9000 (invoice/proforma files, bucket `procurement-files`)
|
||||
└── casdoor :8000 (OIDC — login for the inventory panel)
|
||||
├── Host(localhost) ──► frontend (nginx SPA :80)
|
||||
│ │
|
||||
│ └── /api/* proxy ──► api :8000 (/api/v1)
|
||||
│
|
||||
├── Host(api.localhost) ──► api :8000
|
||||
├── Host(auth.localhost) ──► casdoor :8000 (OIDC login)
|
||||
└── Host(s3/storage.localhost) ──► minio (API / console)
|
||||
│
|
||||
├── postgres :5432 (procurement + casdoor DBs)
|
||||
└── minio :9000 (invoice/proforma files)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -28,11 +34,15 @@ Browser ──► frontend :3000 (nginx SPA)
|
||||
```text
|
||||
v1.3/
|
||||
├── docker-compose.yml ← all paths are relative to this folder
|
||||
├── docker-compose.prod.yml ← production overlay (real domains + HTTPS via Let's Encrypt)
|
||||
├── start.ps1 ← PowerShell helper (build + start + status)
|
||||
├── mc.cmd ← MinIO Client helper (inspect the bucket)
|
||||
├── README.md
|
||||
├── sql/
|
||||
│ └── migration.sql ← DB schema v2.6 (auto-runs on first boot)
|
||||
│ ├── migration.sql ← DB schema v2.6 (auto-runs on first boot)
|
||||
│ └── 02-casdoor-db.sql ← creates the `casdoor` role+DB on the same instance
|
||||
├── infra/
|
||||
│ └── traefik-dynamic.yml ← Traefik shared middlewares (HTTP→HTTPS redirect)
|
||||
├── docs/ ← ERDs + migration plan + auth guide
|
||||
│ ├── erd.txt (v2.4 — old procurement schema)
|
||||
│ ├── new erd.txt (v2.5 — material → supplier → category)
|
||||
@@ -83,13 +93,18 @@ no browser redirect to Casdoor.
|
||||
«کالاهای در راه» (receiving included) and «سازماندهی» (users, PMs, warehouses).
|
||||
The «انبارها» and «تغییر موجودی» sections are PM-only.
|
||||
|
||||
**Developer Mode** (top-left button on the login page) allows test login as
|
||||
مدیر تدارکات / ادمین / مدیر پروژه without Casdoor. It is controlled by the
|
||||
`VITE_ENABLE_DEV_LOGIN` env var (in Docker: the `DEV_LOGIN_ENABLED` compose
|
||||
build arg, default `true` — set it to `false` for production builds).
|
||||
**Developer Mode** (top-left button on the login page) is a 3-step flow for
|
||||
the admin (super user): ① superuser auth gate (admin credentials only) →
|
||||
② role switcher (ادمین / مدیر تدارکات / مدیر پروژه + project) → ③ session
|
||||
activation by role (admin → **پنل مدیریت ادمین** at `/admin`, مدیر تدارکات →
|
||||
procurement office, مدیر پروژه → their project's inventory panel). It is
|
||||
controlled by the `VITE_ENABLE_DEV_LOGIN` env var (in Docker: the
|
||||
`DEV_LOGIN_ENABLED` compose build arg, default `true` — set it to `false`
|
||||
for production builds).
|
||||
|
||||
| What | Where | Credentials |
|
||||
|---|---|---|
|
||||
| App | http://localhost (via Traefik) | — |
|
||||
| Casdoor console | http://localhost:8080 | `admin` / `123` |
|
||||
| Seeded app users (org `inventory`) | `razaei` (admin), `mousavi` (مدیر تدارکات), `mohaghegh` (مدیر پروژه), `tester` (مدیر پروژه، پروژه «تست») | password `123456` |
|
||||
|
||||
@@ -132,16 +147,16 @@ All should show `Up` (postgres also `(healthy)`).
|
||||
|
||||
| What | Where |
|
||||
|---|---|
|
||||
| Frontend UI | http://localhost:3000 |
|
||||
| Frontend UI | http://localhost |
|
||||
| API docs (Swagger) | http://localhost:8000/docs |
|
||||
| API health | http://localhost:8000/health → `{"status":"ok","service":"procurement-api"}` |
|
||||
| MinIO console | http://localhost:9001 (user/pass `minioadmin`/`minioadmin`) |
|
||||
| Database | `localhost:5433`, db `procurement`, user/pass `postgres`/`postgres` |
|
||||
| Database | `localhost:5433`, db `procurement`, user/pass `postgres`/`postgres` (Casdoor uses the same instance, db `casdoor`) |
|
||||
|
||||
Quick end-to-end check (frontend proxy → API → DB):
|
||||
Quick end-to-end check (Traefik → frontend proxy → API → DB):
|
||||
|
||||
```powershell
|
||||
curl http://localhost:3000/api/v1/quotes?expand=supplier,project,line_items,termsheets,events,decision,receipt
|
||||
curl http://localhost/api/v1/quotes?expand=supplier,project,line_items,termsheets,events,decision,receipt
|
||||
```
|
||||
|
||||
---
|
||||
@@ -200,7 +215,22 @@ docker compose down -v # ⚠️ full reset — wipes DB + MinIO vol
|
||||
The procurement CRUD endpoints (quotes, suppliers, materials, …) are still
|
||||
open — dev placeholder, do not expose to the public internet.
|
||||
- Roles live in the app's `users` table (Casdoor is identity-only): `pm`,
|
||||
`procurement_manager`, `admin`. See `docs/casdoor-auth.md`.
|
||||
`procurement_manager`, `admin`. Role changes are **admin-only** (RBAC);
|
||||
the procurement manager can only assign projects to project managers.
|
||||
The admin has a dedicated panel at `/admin` (user sync from Casdoor +
|
||||
grant/revoke «مدیر تدارکات»).
|
||||
- **Partial goods receipt** («رسید جزیی») is supported in «کالاهای در راه»:
|
||||
at least one item with a valid quantity is enough to submit; other items
|
||||
stay in transit. The procurement ledger shows «رسیده X از Y» / «در راه Z»
|
||||
badges and a «رسید جزیی» badge on the quote.
|
||||
- **One PostgreSQL instance** serves both the app (`procurement`) and Casdoor
|
||||
(`casdoor`) — the separate casdoor-postgres container is gone (data was
|
||||
dumped/restored; see `docs/casdoor-auth.md`).
|
||||
- **Traefik v3** is the public edge (ports 80/443); the frontend nginx only
|
||||
serves the SPA and proxies `/api` internally. Production (real domains,
|
||||
HTTPS via Let's Encrypt, HTTP→HTTPS redirect):
|
||||
`docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build`
|
||||
(see the prod file header for the required env vars).
|
||||
- Schema runs automatically ONLY on the first boot of an empty `postgres_data` volume
|
||||
(see `docker-entrypoint-initdb.d`); the API also runs `CREATE TABLE IF NOT EXISTS`
|
||||
for the units-dashboard tables and `quote_attachments` at startup.
|
||||
|
||||
+43
-4
@@ -10,12 +10,13 @@ scenarios, test results, and how to manage the system yourself.
|
||||
|
||||
| What | Address | Credentials |
|
||||
|---|---|---|
|
||||
| App (frontend) | http://localhost:3000 | — |
|
||||
| App (frontend) | http://localhost (Traefik edge; production: your domain) | — |
|
||||
| API (backend) | http://localhost:8000 (Swagger: `/docs`) | — |
|
||||
| Casdoor console | http://localhost:8080 | **admin / 123** (built-in org) |
|
||||
| Casdoor OIDC discovery | http://localhost:8080/.well-known/openid-configuration | — |
|
||||
| Casdoor public JWKS | http://localhost:8080/.well-known/jwks | — |
|
||||
| In-network Casdoor (from the api container) | http://casdoor:8000 | — |
|
||||
| Traefik host routes (local) | `localhost` → app · `api.localhost` → api · `auth.localhost` → casdoor · `s3.localhost` / `storage.localhost` → MinIO | needs those hosts to resolve (or use the host ports) |
|
||||
|
||||
### Seeded application users (org `inventory`)
|
||||
|
||||
@@ -143,7 +144,7 @@ After that, log in as the admin and use the Organization panel
|
||||
| 1 | Casdoor unavailable | `POST /auth/login` (token exchange) or JWKS fetch on protected endpoints | Login page: «سرویس احراز هویت (Casdoor) در دسترس نیست»; API: 503 | `Casdoor token endpoint unreachable (…): …` or `JWKS fetch failed (…): …` | `docker compose ps` — casdoor must be `Up`; from the api container: `python -c "import urllib.request; print(urllib.request.urlopen('http://casdoor:8000/').status)"` |
|
||||
| 2 | Backend unavailable | nginx `/api/` proxy | App shows «اتصال به سرور برقرار نشد» (frontend toast) | none (api is down) | `docker compose ps api`; `docker logs procurement-api`; curl `http://localhost:8000/health` |
|
||||
| 3 | Invalid client (wrong client_id/secret) | Casdoor token endpoint | 503 «سرویس احراز هویت به درستی پیکربندی نشده است» | `Casdoor rejected password grant: error=invalid_client description=…` | Compare `PROCUREMENT_CASDOOR_CLIENT_ID/SECRET` in docker-compose with the application in the Casdoor console (Applications → inventory-app) |
|
||||
| 4 | Invalid redirect URI | Casdoor authorization endpoint (browser flows only) | Casdoor's own error page in the browser | — (backend not involved) | The app no longer uses redirects. In the Casdoor console, the app's Redirect URIs must contain `http://localhost:3000/login` |
|
||||
| 4 | Invalid redirect URI | Casdoor authorization endpoint (browser flows only) | Casdoor's own error page in the browser | — (backend not involved) | The app no longer uses redirects. If a browser OIDC flow is re-enabled, the app's Redirect URIs in the Casdoor console must contain the current app URL (`http://localhost/login` with Traefik) |
|
||||
| 5 | Invalid / corrupted JWT | Backend JWT verification | 401 «توکن نامعتبر است» / «امضای توکن نامعتبر است» | `JWT unreadable: …` or `JWT kid '…' not found in JWKS` | Decode the token at jwt.io; check `kid` against `http://localhost:8080/.well-known/jwks` |
|
||||
| 6 | Expired JWT | Backend JWT verification | 401 «توکن نامعتبر یا منقضی شده است» | `JWT verification failed: … Signature has expired` | Token `exp` claim; lifetime = Application → `expireInHours` (24h) |
|
||||
| 7 | Missing JWT | Protected endpoint dependency | 401 «احراز هویت لازم است…» | normal access log, 401 | Send `Authorization: Bearer <token>`; in the app this means the session was cleared — log in again |
|
||||
@@ -179,7 +180,7 @@ After that, log in as the admin and use the Organization panel
|
||||
| 7 | Application exists | ✅ `inventory-app` |
|
||||
| 8 | Client exists | ✅ `inventory-app-client` (grants incl. `password`) |
|
||||
| 9 | Test users exist | ✅ razaei, mousavi, mohaghegh, tester (password 123456) |
|
||||
| 10 | Redirect URI matches | ✅ `http://localhost:3000/login` registered (unused by the new flow, kept for console flows) |
|
||||
| 10 | Redirect URI matches | ✅ N/A for the in-page flow (redirects unused). The seeded app holds the legacy `http://localhost:3000/login`; the seeder now defaults to `http://localhost/login` |
|
||||
| 11 | OIDC works | ✅ discovery doc + token endpoint + JWT issuance verified |
|
||||
| 12 | PKCE works | ➖ **N/A** — the PKCE/redirect flow was replaced by the in-page password-grant login (per the redesign spec); nothing to verify |
|
||||
| 13 | Login works | ✅ form login in the browser (tester → `/inventory`) |
|
||||
@@ -196,7 +197,45 @@ After that, log in as the admin and use the Organization panel
|
||||
|
||||
---
|
||||
|
||||
## 8. Managing Casdoor yourself
|
||||
## 8. Stack changes in this release
|
||||
|
||||
- **One PostgreSQL instance.** The separate casdoor-postgres container is
|
||||
gone: the app DB (`procurement`) and Casdoor's DB (`casdoor`) now live on
|
||||
the same `postgres:18` service. The `casdoor` role + database are created
|
||||
by `sql/02-casdoor-db.sql` on a fresh volume; the migration was done via
|
||||
`pg_dump`/`pg_restore` (pre-migration backups are kept on the host).
|
||||
- **Traefik v3 is the public edge.** Ports 80/443 (web/websecure); host rules
|
||||
for app / api / auth / s3 / storage (env-overridable, localhost defaults).
|
||||
The frontend nginx now only serves the SPA and proxies `/api` internally.
|
||||
Production (real domains, Let's Encrypt, HTTP→HTTPS redirect) is a one
|
||||
command with `docker-compose.prod.yml` — see its header for the env vars.
|
||||
Note: Traefik must be **v3.6+** (≤3.3 fails against Docker Engine 29.x),
|
||||
and the docker socket is mounted read-write (Docker Desktop rejects `:ro`).
|
||||
- **RBAC hardening (Organization / Admin).** Only the `admin` can change
|
||||
roles (`PATCH /users/{id}`); the procurement manager can only assign
|
||||
projects to project managers. The user-creation form is removed — users
|
||||
come from Casdoor via `POST /users/sync` (identity mirroring, roles
|
||||
untouched). The admin has a dedicated panel at `/admin` (sync + grant/
|
||||
revoke «مدیر تدارکات»).
|
||||
- **Developer Mode is now a 3-step admin flow** on the login page: admin
|
||||
credentials → role switcher (ادمین / مدیر تدارکات / مدیر پروژه) → role-based
|
||||
routing (admin → `/admin`, manager → office, PM → inventory).
|
||||
- **Partial goods receipt** in «کالاهای در راه»: submitting any item with a
|
||||
valid quantity is enough; the rest stays in transit. The ledger shows
|
||||
«رسیده X از Y» / «در راه Z» and a «رسید جزیی» badge on the quote. The
|
||||
arrival-confirmation 500 (a `db.begin()` on an already-begun session) was
|
||||
fixed, and the receipt response now returns the real derived state.
|
||||
- **Daily control calendar** now renders day-of-month (was rendering the
|
||||
Jalali year), navigates by real Jalali months, and uses pure Jalali dates
|
||||
(«امروز ۸ شهریور ۱۴۰۵»), with a blue accent on today and a ring on the
|
||||
selected day.
|
||||
- **Uploads** (invoice/proforma) no longer send `Content-Type: application/json`
|
||||
on multipart requests — the 400/422 «دادهی ارسالشده نامعتبر است» is fixed.
|
||||
- **Project filter** in the admin inventory views is fixed: empty/undefined
|
||||
query params are dropped (previously `project_id=undefined` → 400 → empty
|
||||
dropdowns).
|
||||
|
||||
## 9. Managing Casdoor yourself
|
||||
|
||||
- **Console:** http://localhost:8080 — log in as **admin / 123**. From there
|
||||
you can manage Organizations, Users, Applications, Providers, Certificates.
|
||||
|
||||
Reference in New Issue
Block a user