chore: add repo scaffolding (compose, scripts, gitignore, env example)
This commit is contained in:
+30
@@ -0,0 +1,30 @@
|
|||||||
|
# ---- Node / frontend ----
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# ---- Python / backend ----
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
|
||||||
|
# ---- Env / secrets ----
|
||||||
|
frontend/.env
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# ---- Local session / backups ----
|
||||||
|
.zcode/
|
||||||
|
v1.2 - Copy/
|
||||||
|
inv dash/
|
||||||
|
|
||||||
|
# ---- Screenshots (root) ----
|
||||||
|
*.png
|
||||||
|
*.jpg
|
||||||
|
*.jpeg
|
||||||
|
!frontend/public/icons/*.png
|
||||||
|
|
||||||
|
# ---- OS junk ----
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
# Procurement Stack v1.3 — Dockerized (PostgreSQL + FastAPI + React + MinIO)
|
||||||
|
|
||||||
|
Self-contained ship package: everything needed to run the stack is in this
|
||||||
|
folder — no external files or sibling folders required.
|
||||||
|
|
||||||
|
| Service | Container | Tech | Exposed port |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **postgres** | `procurement-postgres` | PostgreSQL 18 | `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` |
|
||||||
|
|
||||||
|
```
|
||||||
|
Browser ──► frontend :3000 (nginx SPA)
|
||||||
|
│
|
||||||
|
└── /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)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Layout
|
||||||
|
|
||||||
|
```text
|
||||||
|
v1.3/
|
||||||
|
├── docker-compose.yml ← all paths are relative to this folder
|
||||||
|
├── 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)
|
||||||
|
├── docs/ ← ERDs + migration plan
|
||||||
|
│ ├── erd.txt (v2.4 — old procurement schema)
|
||||||
|
│ ├── new erd.txt (v2.5 — material → supplier → category)
|
||||||
|
│ ├── NewERD.txt (v2.6 — integrated procurement + inventory)
|
||||||
|
│ └── material-supplier-category-migration-plan.md
|
||||||
|
├── legacy/
|
||||||
|
│ └── dash-prototype/ ← shipped HTML/JS inventory dash (reference only)
|
||||||
|
├── backend/ ← build context of the `api` service
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ ├── requirements.txt
|
||||||
|
│ ├── .dockerignore
|
||||||
|
│ ├── app/ (source code — procurement + inventory + auth)
|
||||||
|
│ └── tools/ (smoke-test + seed scripts, copied into the image)
|
||||||
|
└── frontend/ ← build context of the `frontend` service
|
||||||
|
├── Dockerfile
|
||||||
|
├── nginx.conf
|
||||||
|
├── .dockerignore
|
||||||
|
├── .env.example (copy to .env if you need a custom API base URL)
|
||||||
|
├── index.html
|
||||||
|
├── package.json
|
||||||
|
├── package-lock.json
|
||||||
|
├── tsconfig*.json
|
||||||
|
├── vite.config.ts
|
||||||
|
├── public/
|
||||||
|
└── src/
|
||||||
|
└── pages/
|
||||||
|
├── Dashboard/ (procurement office)
|
||||||
|
├── Wizard/ (add new offer)
|
||||||
|
├── Suppliers/ (suppliers & topics)
|
||||||
|
├── Projects/ (projects)
|
||||||
|
├── Units/ (unit conversions)
|
||||||
|
└── Inventory/ (warehouse management panel — React port)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Login
|
||||||
|
|
||||||
|
The whole system is behind a login page (`/login`):
|
||||||
|
|
||||||
|
- **مدیر پروژه (PM)** → lands in the inventory panel (their project) — can record
|
||||||
|
stock changes and confirm arrivals («رسید کالاهای در راه»).
|
||||||
|
- **مدیر تدارکات / ادمین** → lands in the procurement office — can manage quotes,
|
||||||
|
suppliers and projects, and open the inventory panel in the read-only («فقط خواندنی»)
|
||||||
|
admin view.
|
||||||
|
|
||||||
|
Until Casdoor is fully seeded, use «ورود آزمایشی (توسعه)» on the login page
|
||||||
|
(role + project). Casdoor (org `inventory`, app `inventory-app`) is the planned
|
||||||
|
production identity source.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites (target machine)
|
||||||
|
|
||||||
|
- **Docker Desktop** (Windows) or Docker Engine + Compose plugin (Linux/macOS)
|
||||||
|
- ~3–4 GB free disk for images (`postgres:18`, `python:3.13-slim`, `node:22-alpine`, `nginx:alpine`, `minio/minio`, `minio/mc`)
|
||||||
|
- Internet access on first build (image + `npm ci` downloads)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Start the whole stack
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd <this-folder>
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
First run downloads base images and builds the frontend (takes a few minutes).
|
||||||
|
Subsequent runs: `docker compose up -d`.
|
||||||
|
|
||||||
|
Wait until all services are up:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
All should show `Up` (postgres also `(healthy)`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verify
|
||||||
|
|
||||||
|
| What | Where |
|
||||||
|
|---|---|
|
||||||
|
| Frontend UI | http://localhost:3000 |
|
||||||
|
| 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` |
|
||||||
|
|
||||||
|
Quick end-to-end check (frontend proxy → API → DB):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
curl http://localhost:3000/api/v1/quotes?expand=supplier,project,line_items,termsheets,events,decision,receipt
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MinIO (object storage for invoice / proforma files)
|
||||||
|
|
||||||
|
| Item | Value |
|
||||||
|
|---|---|
|
||||||
|
| S3 API endpoint | `localhost:9000` (from the API container: `minio:9000`) |
|
||||||
|
| Web console | http://localhost:9001 |
|
||||||
|
| Root user / password | `minioadmin` / `minioadmin` (dev — change for production) |
|
||||||
|
| Bucket | `procurement-files` (created automatically by the `minio-init` container) |
|
||||||
|
| Object key layout | `{quote_id}/{kind}/{attachment_id}` where kind = `invoice` \| `proforma` |
|
||||||
|
|
||||||
|
File **bytes** live in MinIO; the `quote_attachments` table holds the metadata.
|
||||||
|
|
||||||
|
Ad-hoc bucket inspection from the host (see `mc.cmd`):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\mc.cmd ls --recursive
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Database credentials / connection
|
||||||
|
|
||||||
|
```text
|
||||||
|
Host: localhost
|
||||||
|
Port: 5433
|
||||||
|
Database: procurement
|
||||||
|
Username: postgres
|
||||||
|
Password: postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
docker exec -it procurement-postgres psql -U postgres -d procurement
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common operations
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
docker compose logs -f api # see logs
|
||||||
|
docker compose down # stop (keep data)
|
||||||
|
docker compose up -d --build # rebuild after code changes
|
||||||
|
docker compose down -v # ⚠️ full reset — wipes DB + MinIO volumes
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Known behavior
|
||||||
|
|
||||||
|
- The backend has **no authentication** (dev placeholder) — do not expose to the public internet.
|
||||||
|
- 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.
|
||||||
|
- Invoice upload requires an `accepted`/`arrived` quote; proforma has no restriction.
|
||||||
|
- The supplier detail page (`/suppliers/:id`) shows the purchase history with
|
||||||
|
invoice/proforma download links from MinIO.
|
||||||
|
- If builds fail with disk-full errors: `docker system prune -a`, then rebuild.
|
||||||
|
- If the frontend shows «اتصال به سرور برقرار نشد» → the api container is not
|
||||||
|
reachable; check `docker compose ps` and `docker compose logs api`.
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:18
|
||||||
|
container_name: procurement-postgres
|
||||||
|
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: procurement
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
|
||||||
|
ports:
|
||||||
|
- "5433:5432"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql
|
||||||
|
- ./sql/migration.sql:/docker-entrypoint-initdb.d/01-migration.sql:ro
|
||||||
|
# - ./sql/seed.sql:/docker-entrypoint-initdb.d/02-seed.sql:ro
|
||||||
|
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U postgres -d procurement"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
minio:
|
||||||
|
image: minio/minio:latest
|
||||||
|
container_name: procurement-minio
|
||||||
|
|
||||||
|
command: server /data --console-address ":9001"
|
||||||
|
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: minioadmin
|
||||||
|
MINIO_ROOT_PASSWORD: minioadmin
|
||||||
|
|
||||||
|
ports:
|
||||||
|
- "9000:9000" # S3 API
|
||||||
|
- "9001:9001" # Web console
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- minio_data:/data
|
||||||
|
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# MinIO Client (mc): waits for the server, creates the bucket, exits.
|
||||||
|
# Use for ad-hoc ops too: docker compose run --rm minio-init ...
|
||||||
|
minio-init:
|
||||||
|
image: minio/mc:latest
|
||||||
|
container_name: procurement-minio-init
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
minio:
|
||||||
|
condition: service_started
|
||||||
|
|
||||||
|
environment:
|
||||||
|
MINIO_ENDPOINT: minio:9000
|
||||||
|
MINIO_ROOT_USER: minioadmin
|
||||||
|
MINIO_ROOT_PASSWORD: minioadmin
|
||||||
|
MINIO_BUCKET: procurement-files
|
||||||
|
|
||||||
|
entrypoint: >
|
||||||
|
/bin/sh -c "
|
||||||
|
until mc alias set local http://$${MINIO_ENDPOINT} $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD}; do
|
||||||
|
echo 'waiting for minio server...'; sleep 2;
|
||||||
|
done;
|
||||||
|
mc mb --ignore-existing local/$${MINIO_BUCKET};
|
||||||
|
echo bucket $${MINIO_BUCKET} ready;
|
||||||
|
"
|
||||||
|
|
||||||
|
restart: "no"
|
||||||
|
|
||||||
|
# ==========================================================================
|
||||||
|
# v2.6 — Casdoor (OIDC identity provider)
|
||||||
|
# Console: http://localhost:8080 (admin / 123)
|
||||||
|
# Application "inventory-app" (org "inventory", client inventory-app-client)
|
||||||
|
# is seeded by casdoor/seed.py — see tools/seed_casdoor.py in the repo.
|
||||||
|
# ==========================================================================
|
||||||
|
casdoor-postgres:
|
||||||
|
image: postgres:16
|
||||||
|
container_name: procurement-casdoor-postgres
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: casdoor
|
||||||
|
POSTGRES_USER: casdoor
|
||||||
|
POSTGRES_PASSWORD: casdoor
|
||||||
|
volumes:
|
||||||
|
- casdoor_postgres_data:/var/lib/postgresql
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
casdoor:
|
||||||
|
image: casbin/casdoor:latest
|
||||||
|
container_name: procurement-casdoor
|
||||||
|
depends_on:
|
||||||
|
- casdoor-postgres
|
||||||
|
environment:
|
||||||
|
RUNNING_IN_DOCKER: "true"
|
||||||
|
driverName: postgres
|
||||||
|
dataSourceName: "user=casdoor password=casdoor host=casdoor-postgres port=5432 sslmode=disable dbname=casdoor"
|
||||||
|
dbName: casdoor
|
||||||
|
origin: "http://localhost:8080"
|
||||||
|
ports:
|
||||||
|
- "8080:8000"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
api:
|
||||||
|
build:
|
||||||
|
context: ./backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: procurement-api
|
||||||
|
|
||||||
|
environment:
|
||||||
|
PROCUREMENT_DATABASE_URL: postgresql+asyncpg://postgres:postgres@postgres:5432/procurement
|
||||||
|
PROCUREMENT_MINIO_ENDPOINT: minio:9000
|
||||||
|
PROCUREMENT_MINIO_ACCESS_KEY: minioadmin
|
||||||
|
PROCUREMENT_MINIO_SECRET_KEY: minioadmin
|
||||||
|
PROCUREMENT_MINIO_BUCKET: procurement-files
|
||||||
|
PROCUREMENT_MINIO_SECURE: "false"
|
||||||
|
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
minio-init:
|
||||||
|
condition: service_completed_successfully
|
||||||
|
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./frontend
|
||||||
|
container_name: procurement-frontend
|
||||||
|
|
||||||
|
ports:
|
||||||
|
- "3000:80"
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- api
|
||||||
|
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
minio_data:
|
||||||
|
casdoor_postgres_data:
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Base URL of the backend API.
|
||||||
|
# Empty = same-origin proxy (nginx → /api/v1 in Docker).
|
||||||
|
# For local dev against a remote backend, set e.g.:
|
||||||
|
# VITE_API_BASE_URL=http://localhost:8000/api/v1
|
||||||
|
VITE_API_BASE_URL=
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
@echo off
|
||||||
|
rem MinIO Client (mc) helper — wraps the minio/mc container on the compose network.
|
||||||
|
rem Usage from the project root:
|
||||||
|
rem mc.cmd ls --recursive
|
||||||
|
rem mc.cmd ls invoices/
|
||||||
|
rem mc.cmd admin info local
|
||||||
|
rem
|
||||||
|
rem Uses `docker compose run` so it works regardless of the folder name
|
||||||
|
rem (the container joins the compose network and talks to the `minio` service).
|
||||||
|
docker compose run --rm --entrypoint /bin/sh minio-init -c "mc alias set local http://minio:9000 minioadmin minioadmin >/dev/null 2>&1 && mc %*"
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Start the full procurement stack (postgres + api + frontend)
|
||||||
|
# Run this from the my-project folder (or double-click).
|
||||||
|
#
|
||||||
|
# powershell -ExecutionPolicy Bypass -File .\start.ps1
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
Set-Location $here
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "==============================================" -ForegroundColor Cyan
|
||||||
|
Write-Host " Procurement stack — starting Docker..." -ForegroundColor Cyan
|
||||||
|
Write-Host "==============================================" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host "Build/start failed. Check 'docker compose logs'." -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Waiting for services..." -ForegroundColor Yellow
|
||||||
|
Start-Sleep -Seconds 5
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Frontend : http://localhost:3000" -ForegroundColor Green
|
||||||
|
Write-Host "API docs : http://localhost:8000/docs" -ForegroundColor Green
|
||||||
|
Write-Host "DB : localhost:5432 (procurement / postgres / postgres)" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Stop: docker compose down | Full reset: docker compose down -v && up -d --build" -ForegroundColor DarkGray
|
||||||
Reference in New Issue
Block a user