vccs
Vue 3용 차트 컴포넌트 모음, Recharts에서 포팅
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Satellite & AI oil spill intelligence for the Caspian Sea
SeaSentry is a full-stack operational platform for detecting, reviewing, and
responding to potential oil spills. Satellite (SAR) imagery is analyzed for
dark-signature slicks, an AI layer estimates area/risk/probable cause, and a
human specialist always makes the final call — confirm, reject, escalate, or
approve a cleanup response. Everything from detection to PDF report lives in
one workspace.README 전문 보기 ↗Satellite & AI oil spill intelligence for the Caspian Sea
SeaSentry is a full-stack operational platform for detecting, reviewing, and responding to potential oil spills. Satellite (SAR) imagery is analyzed for dark-signature slicks, an AI layer estimates area/risk/probable cause, and a human specialist always makes the final call — confirm, reject, escalate, or approve a cleanup response. Everything from detection to PDF report lives in one workspace.
/docs)main,
synced from https://github.com/Kanan-peoiks/Pelagos, is what Render deploys —
see Deployment for why two repos are involved)Status: this is a working demo with a real backend, real database, and real auth — not a static mockup. The one piece still pending is the ML detection model itself (see Roadmap); everything around it (review workflow, response calculations, admin tooling) is fully built and live.
viewer, operator, or
admin (see Roles & permissions).detectionSource: "Manual report") — useful standalone today, and a
stand-in for the ML pipeline until it's wired up./admin, admin role only)| Layer | Technology |
|---|---|
| Frontend framework | Next.js 16 (App Router, Turbopack disabled — see AGENTS.md), React 19, TypeScript |
| Frontend data/UI | Recharts (charts), Leaflet + react-leaflet (map), jsPDF (report generation), Lucide icons |
| Backend framework | FastAPI (Python), Pydantic v2, SQLAlchemy 2.x |
| Auth | python-jose (JWT), passlib/bcrypt (password hashing) |
| Database | PostgreSQL (Neon serverless) in production, SQLite fallback for local dev |
Gmail SMTP via an app password (stdlib smtplib, no third-party API) | |
| Hosting | Vercel (frontend), Render (backend, via a render.yaml Blueprint) |
| Live external data | Open-Meteo (wind/sea state, no key), Copernicus Data Space Ecosystem / Sentinel Hub (real Sentinel-1 SAR tile fetch, backend-side — see app/satellite.py) |
Browser
│
│ same-origin only — the browser never talks to Render directly
▼
Next.js app (Vercel)
├─ Public pages: / , /login , /register , /forgot-password , /reset-password
├─ proxy.ts — cookie-based route guard (Next.js 16's renamed
│ "middleware"; redirects unauthenticated users to
│ /login, redirects authenticated users away from
│ the auth pages)
├─ Protected AppShell pages: dashboard, incidents, vessels, ai-analysis,
│ response, reports, account, admin
└─ app/api/* — Route Handlers that proxy to the backend,
forwarding the httpOnly JWT cookie as a Bearer
token. This is the only thing that talks to
Render — it sidesteps CORS entirely and keeps
the backend URL and the raw JWT out of the
browser.
│
▼
FastAPI backend (Render)
├─ /auth/* — register, login, demo, forgot/
│ reset password, me
├─ /incidents/* — list, get, create, decision
├─ /reports/* — save/list generated PDF snapshots
├─ /feedback/* — submit, list, resolve, reply
├─ /admin/* — user list, role changes, stats
├─ /detect — ML stub (503 today)
└─ /health
│
▼
Postgres (Neon, serverless)
Why two repos are involved: the frontend (Vercel) and backend (Render)
are configured against two different GitHub remotes —
arizazadli20/Pelagos and the fork Kanan-peoiks/Pelagos — for
account/ownership reasons unrelated to the code itself. Render watches the
fork's main, which is kept caught up via GitHub's "Sync fork" button
after each merge to arizazadli20/Pelagos:main. If backend changes aren't
showing up live, check whether the fork has been synced.
Pelagos/
├── app/
│ ├── page.tsx # Public landing page
│ ├── login/ register/ # Auth pages
│ ├── forgot-password/ reset-password/
│ ├── dashboard/ incidents/ vessels/ ai-analysis/ response/ reports/
│ ├── account/ admin/
│ └── api/ # Route Handlers (proxy to the backend)
│ ├── auth/{login,register,logout,demo,forgot-password,reset-password}/
│ ├── incidents/ incidents/[id]/ incidents/[id]/decision/
│ ├── reports/
│ ├── feedback/ feedback/[id]/ feedback/[id]/reply/
│ ├── admin/{stats,users,users/[id]/role}/
│ └── detect/ # Proxies the "check for new imagery" scan to the backend
├── components/
│ ├── AppShell.tsx # Auth gate + header/sidebar + loading state
│ ├── MapPanel.tsx # Leaflet map (markers, slick polygons, source pin)
│ ├── landing/LandingPage.tsx
│ ├── auth/AuthForm.tsx
│ ├── incidents/ # Detail panel, manual-report form, charts
│ └── ui/ # Shared primitives (DetailPanel, StatCard, …)
├── lib/
│ ├── auth.ts # Client auth helpers (role checks, login/register/…)
│ ├── incident-store.tsx # Shared React context — incidents, decisions, live sim
│ ├── spill-physics.ts # Drift/source-back-calculation, sorbent math
│ ├── useSpillSourceEstimate.ts # Hook wrapping weather + spill-physics
│ ├── weather.ts # Open-Meteo wind/sea-state client
│ ├── mock-data.ts # Still-mock domain data (vessels, risk zones, activity)
│ ├── types.ts # Shared domain types
│ ├── server/backend.ts # Server-only helper: backend URL + auth-header forwarding
│ └── nav.ts
├── proxy.ts # Route guard (formerly middleware.ts)
└── backend/
├── app/
│ ├── main.py # FastAPI app, CORS, router registration
│ ├── config.py # Settings (env vars)
│ ├── database.py # Engine/session, sqlite↔postgres URL normalization
│ ├── models.py # SQLAlchemy models
│ ├── schemas.py # Pydantic request/response shapes (camelCase aliases)
│ ├── security.py # JWT + bcrypt
│ ├── deps.py # get_current_user / require_operator / require_admin
│ ├── rate_limit.py # In-memory login rate limiter
│ ├── email_util.py # SMTP sender + bilingual email templates
│ ├── seed.py # Seeds 2 demo incidents
│ └── routers/
│ ├── auth.py incidents.py reports.py feedback.py admin.py detect.py
├── render.yaml # Render Blueprint
├── ML_INTEGRATION.md # Contract + recommendations for the ML teammate
└── requirements.txt
render.yaml — newer versions may
lack prebuilt wheels for some backend dependencies)cd backend
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS/Linux
pip install -r requirements.txt
cp .env.example .env # fill in DATABASE_URL, or leave blank for SQLite
python -m app.seed # optional: adds 2 demo incidents
uvicorn app.main:app --reload
API docs (Swagger UI): http://localhost:8000/docs.
npm install
cp .env.example .env.local
# set BACKEND_URL=http://localhost:8000 in .env.local
npm run dev
Open http://localhost:3000. npm run build / npm start for a production
build; both dev and build use the --webpack flag (Turbopack currently
panics on this project's PostCSS setup).
/ → click Continue as guest to see the dashboard instantly
(viewer-only — no write actions), or Register for a full account./reports to see it in the report history table.ADMIN_EMAILS, visit /admin to manage
users and reply to feedback..env.local)| Variable | Required | Notes |
|---|---|---|
BACKEND_URL | Yes | The FastAPI backend's base URL — http://localhost:8000 locally, the Render URL in production. Never exposed to the browser; only read inside Route Handlers. |
Copernicus/Sentinel Hub credentials now live on the backend instead (see below) — the real SAR tile fetch happens there, not in a frontend Route Handler.
backend/.env)| Variable | Required | Notes |
|---|---|---|
DATABASE_URL | No | Postgres connection string. Omit for a local SQLite file (sqlite:///./seasentry.db). |
JWT_SECRET | Recommended | Signs auth tokens — generate with python -c "import secrets; print(secrets.token_hex(32))". Auto-generated on Render. |
JWT_ALGORITHM | No | Defaults to HS256. |
ACCESS_TOKEN_EXPIRE_MINUTES | No | Defaults to 1440 (24h). |
CORS_ORIGINS | Yes in production | Comma-separated allowed origins. Note: since the frontend proxies through Route Handlers rather than calling the backend from the browser, this mostly matters for direct/manual API access, not the app itself. |
ADMIN_EMAILS | No | Comma-separated emails auto-promoted to admin on register/login — the bootstrap mechanism for the first admin account. |
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASSWORD | For email features | Gmail SMTP + an app password (requires 2-Step Verification on the account). If unset, emails are logged instead of sent — the flow still works end-to-end for local testing. |
FRONTEND_URL | For email features | Used to build the link inside password-reset emails. |
REQUIRE_ADMIN_2FA | No | Gates the built email-code 2FA challenge on admin login. Defaults false so a broken email sender can never lock admins out. |
SLACK_WEBHOOK_URL | For Slack alerts | Incoming Webhook URL — posts an alert when a new HIGH-risk incident is created. Unset = silently skipped. |
COPERNICUS_CLIENT_ID / COPERNICUS_CLIENT_SECRET | For /detect | Copernicus Data Space Ecosystem OAuth client (dataspace.copernicus.eu → Sentinel Hub → OAuth clients) — used by app/satellite.py to fetch real Sentinel-1 SAR tiles. Unset = returns a clear 502 instead of faking a result. |
| Role | Can view | Can create/decide incidents, generate reports | Can manage users |
|---|---|---|---|
viewer (default for new registrations, and always for the demo account) | ✅ | ❌ | ❌ |
operator | ✅ | ✅ | ❌ |
admin | ✅ | ✅ | ✅ |
New self-registrations start as viewer — an existing admin has to promote
them to operator from /admin. This is deliberate: not every registered
account should be able to act on real incidents, only vetted team members.
The one exception is bootstrapping — any email listed in ADMIN_EMAILS
becomes admin automatically the moment it registers or logs in.
| Table | Purpose |
|---|---|
users | Accounts — role, is_demo, bcrypt password_hash. |
incidents | Mirrors the frontend's Incident type field-for-field (camelCase JSON via a Pydantic alias generator). |
reports | A snapshot of each generated PDF report (materials, cost, who/when). |
feedback | Feedback/suggestion/question submissions, resolved flag, admin_reply/replied_at. |
login_events | One row per successful register/login/demo-login — powers the admin panel's usage stats. |
password_reset_tokens | Single-use, 30-minute-expiry reset tokens. |
There's no migration framework (no Alembic) — schema changes are additive
ALTER TABLE ... ADD COLUMN IF NOT EXISTS statements run once by hand
against the shared database. Base.metadata.create_all() in main.py
handles brand-new tables automatically; it does not add columns to existing
tables.
arizazadli20/Pelagos:main.backend/render.yaml, deploying
Kanan-peoiks/Pelagos:main (see the sync note in
Architecture). Free tier — the service spins down after
~15 minutes idle and takes 30–50s to wake on the next request; hit
/health a minute before a live demo to warm it up.To ship a change: push to feature/kenan-api → open a PR into
arizazadli20/Pelagos:main → merge → Sync fork on
Kanan-peoiks/Pelagos so Render picks it up. Vercel needs no equivalent
step; it deploys straight from the first repo.
lib/mock-data.ts) — no live AIS feed is integrated. Flagged as
out-of-scope for the current backend unless a specific need comes up.ImagePlaceholder in IncidentDetailsPanel.tsx) —
the real tile fetch now exists (app/satellite.py, used by POST /detect) but its output isn't rendered as an on-screen image anywhere
yet, only analyzed.POST /detect's confidence score is a classical CV heuristic, not a
trained model — real Sentinel-1 pixels, real threshold/blob analysis
(app/spill_detect.py), but not machine-learned. See
backend/ML_INTEGRATION.md for swapping in a real model.backend/ML_INTEGRATION.md
documents the exact contract, a recommended detection approach (SAR
dark-spot segmentation, fine-tuning a public dataset rather than training
from scratch), an infrastructure recommendation (keep it light enough for
Render's free tier, or split heavy inference into its own service), and a
suggested trigger model (a manual "check for new imagery" button first,
before an automatic scheduler).arizazadli20/Pelagos directly instead of the synced fork, to remove the
manual "Sync fork" step.DATABASE_URL, JWT_SECRET, SMTP_PASSWORD, etc.) live
only in gitignored .env files locally and in Render/Vercel's dashboard
env-var UI in production — never in git, even in render.yaml (secrets
there are marked sync: false, meaning "set this by hand")./detect