Skip to content

Supawitk/NodeOps

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NodeOps

A self-hostable operations platform for data-center field teams. One app for what's happening today (duty, tasks, incidents), what we know (KB wiki + file search + per-site location tree), and who's around (staff, teams, on-call rotations).

Built so a single duty operator can answer "what's burning, who's on it, where's the runbook" without bouncing between four tools.

Python FastAPI SQLAlchemy Pydantic PostgreSQL pgvector MinIO React TypeScript Vite Swift Docker


🚀 5-minute quick start

git clone https://github.com/Supawitk/NodeOps.git
cd NodeOps/backend
cp .env.example .env
docker compose up -d --build

Wait ~30 seconds for the containers to come up, then open:

URL What How to run
http://localhost:3000 Operator console — main desktop app included in docker compose
http://localhost:3100 User-view — engineer's daily workspace cd user-view && npm install && npm run dev
http://localhost:4321 PWA — installable mobile shell cd pwa && npm install && npm run dev
http://localhost:8000/docs FastAPI Swagger UI included
http://localhost:9001 MinIO console included

🔑 Default login: admin / 1234. Change SEED_ADMIN_PASSWORD and set AUTH_TOKEN_SECRET in .env before exposing this beyond localhost. Generate a secret with:

python -c "import secrets; print(secrets.token_hex(32))"

📱 iOS app: open mobile/megabase.xcodeproj in Xcode, set API base URL in the More tab to your laptop's LAN IP, or to the frpc URL below.

🌐 Want to reach it from your phone / iOS app / a demo viewer?

localhost only works from the machine running Docker. The easiest way to get a real URL is frpc — an open-source reverse-tunnel that runs on any $5/month VPS:

# frpc.toml on your laptop — point it at your VPS running frps
serverAddr = "your.vps.public.ip"
serverPort = 7000
auth.token = "shared-secret-between-frpc-and-frps"

[[proxies]]
name = "nodeops-api"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8000
remotePort = 18000

[[proxies]]
name = "nodeops-web"
type = "tcp"
localIP = "127.0.0.1"
localPort = 3000
remotePort = 13000
frpc -c frpc.toml

Your stack is now reachable at http://your.vps.public.ip:18000 (API) and :13000 (web). Point the PWA / iOS app's API base URL setting at the API one.


🎯 What's in the box

Three frontends + one native iOS app, one Python API, one Postgres, one S3-compatible store. All wired together by docker compose.

┌────────── Operator Console (:3000) ──────────┐
│  Vite + React 19 + TypeScript                │   ← Desktop SPA for duty operators
│  React Flow + dagre (KB canvas)              │
│  Markdown editor, PDF.js viewer              │
└────┬─────────────────────────────────────────┘
     │
┌────┴── User-view (:3100) ────────────────────┐
│  Lighter SPA — engineer's daily workspace    │   ← Today / MyTasks / Inbox / Reports
└────┬─────────────────────────────────────────┘
     │
┌────┴── PWA (:4321) ──────────────────────────┐
│  Installable mobile shell wrapping user-view │   ← Home-screen app for field engineers
│  Service worker, offline queue, glass tabbar │
└────┬─────────────────────────────────────────┘
     │
┌────┴── iOS app (mobile/) ────────────────────┐
│  SwiftUI native app — same backend           │   ← Optional: build with Xcode
└────┬─────────────────────────────────────────┘
     │ /api (WebSocket + REST)
┌────▼──────────────────────────────────────────┐
│  FastAPI (Python 3.12)                        │
│  SQLAlchemy + raw SQL                         │
│  /v1/* — auto OpenAPI at /docs                │
│  WebSocket /v1/ws — realtime broadcasts       │
└──┬─────────────────────────────────────┬──────┘
   │                                     │
┌──▼─────────────┐                ┌─────▼────────┐
│  Postgres 16   │                │  MinIO (S3)  │
│  + pgvector    │                │  attachments │
└────────────────┘                └──────────────┘

📋 What you can do

Area What lives there
Overview KPIs (active issues, open tasks, overdue, staff online), today's duty, my tasks/reports, sites + status, mini-month calendar.
Calendar / Duty Day / week / month / year views. Recurring schedules and one-off tasks share the same surface.
Operations Sites (project cards, project-detail timelines), tasks (filterable list + Kanban ribbon), reporting (issue + note feed, severity, SLA timer, audit log).
Knowledge base Tree-style wiki (folders / Markdown pages / file uploads with PDF + Office preview), full-text search, and a React Flow canvas view that mirrors the org tree (clients → sites → projects → teams → people) with live operational overlay.
Inbox / Chat Realtime site-, team-, and room-scoped chat over WebSocket. Per-channel unread watermarks roll up to the sidebar Inbox badge. Tasks-to-me and issues share the same surface.
Realtime incidents Field engineer can halt an active task → files a sev-tagged report linked to that task. Sev1/sev2 auto-blocks the task and pushes an incident.new toast to every operator console in <1s. Ack / resolve mirrors instantly across web + PWA + iOS.
Presence Staff status (online / offline / on-leave) flips automatically on login + check-in / check-out. Same flag the operator console and the PWA both consume.
Management Roster, teams, on-call shifts, clients / sites / projects CRUD, user accounts, optional Microsoft account linking.

🧱 Tech stack — what & why

Frontend (backend/web/, user-view/, pwa/)

Tech Role
React UI runtime. SPA, no SSR — keeps the dev loop tight.
TypeScript Strict mode across components, stores, and generated API types.
Vite Dev server + build. /api proxy to FastAPI in dev; built static assets in prod.
React Router Client-side routing, deep links into every workspace.
React Flow @xyflow/react KB canvas — interactive graph of clients/sites/projects/teams with live overlay.
dagre Auto-layout for the KB canvas.
pdfjs-dist Inline PDF previews — vendored under web/public/pdfjs/.

Backend (backend/api/)

Tech Role
Python Runtime.
FastAPI HTTP framework; auto-generates /openapi.json for the web typegen. WebSocket routes for realtime.
Pydantic Request/response models, settings.
SQLAlchemy DB engine + pool. Routers use raw SQL via text() for clarity.
Docling Lazy in-process document extraction (PDF / Office → text + images + KB index).
Gotenberg LibreOffice headless wrapper for office-doc → PDF preview rendering.

Data layer

Tech Role
PostgreSQL Single source of truth — users, sites, projects, tasks, reports, KB, chat.
pgvector Embeddings live next to relational data — KB retrieval is one SQL hop.
MinIO S3-compatible object storage for attachment bytes. On-prem; files never leave your network.
Docker One command brings up the whole stack.

Mobile

Tech Role
Swift Native iOS app in mobile/ — same backend, same auth, same realtime. Open mobile/megabase.xcodeproj in Xcode.
PWA The /pwa SPA installs to a home screen with offline queue, network-first service worker, and a glass tabbar. Works on iOS Safari and Chrome Android.

📂 What's in the repo

.
├── backend/                Backend stack — docker compose lives here.
│   ├── api/                FastAPI service (Python 3.12) — REST + WebSocket.
│   ├── web/                Operator console (Vite + React 19), :3000.
│   ├── infra/              Postgres init scripts (pgvector + base schema).
│   └── docker-compose.yml  One command brings everything up.
├── user-view/              Engineer workspace SPA (:3100). Separate codebase
│                            so day-of-work UI evolves without dragging the
│                            operator console along.
├── pwa/                    Installable mobile shell (:4321) wrapping user-view.
└── mobile/                 SwiftUI native iOS app — open in Xcode.

🤖 LLM-ready — wire up a RAG chatbot in an afternoon

The data layer is deliberately built so dropping in a retrieval-augmented chatbot is mostly UI work — the hard parts are already done:

What's already in place Where
pgvector extension installed in Postgres on first boot — embeddings live next to relational data, retrieval is one SQL hop. backend/infra/postgres/init.sql, backend/api/pyproject.toml
Docling extraction pipeline — every uploaded PDF / DOCX / XLSX / PPTX is parsed into plain text and stored in kb_nodes.body, then auto-indexed. No "convert this PDF to text" plumbing required. backend/api/app/services/kb_extract.py
Structured KB tree — the wiki is a real tree (clients → sites → projects → teams → pages), not a flat document dump. Retrieval can scope by branch (e.g. "only this site"), filter by node type, or follow parent / child relations for context expansion. backend/api/app/routers/kb/
Full-text search already running — Postgres tsvector + GIN indexes over titles + extracted bodies. Useful as a cheap first-pass retriever, or as a fallback when vector recall is low. backend/api/app/routers/_schema.py (search the file for to_tsvector)
Per-page anchors — each KB page can be deep-linked to a specific heading, so retrieved chunks come with a click-through location, not just "we found this somewhere in the wiki." KB editor pin feature

What's missing (the fun part)

Three small files. The Ollama env vars are already stubbed in .env.example:

OLLAMA_MODEL=qwen2.5:7b
EMBEDDING_MODEL=nomic-embed-text

What you add:

  1. backend/api/app/services/embed.py — wrap httpx POST to Ollama's /api/embeddings. Add an embedding vector(768) column to kb_nodes via a migration, embed on insert / update, store inline.
  2. backend/api/app/services/rag.py — given a query: embed it, run ORDER BY embedding <=> $1 LIMIT 8 against kb_nodes, optionally rerank with full-text search, return the top chunks with their tree paths.
  3. backend/api/app/routers/chat_ai.pyPOST /v1/chat/ai: takes a question, calls rag.retrieve(), stuffs the chunks into the system prompt, streams the LLM response back via SSE.

Frontend: a sidebar drawer on the operator console, the chat tab in the user-view inbox, and an "Ask AI" button on KB pages. All three can hit the same endpoint.

Because the KB is already a clean tree of human-curated content (not a scraped pile), retrieval quality should be unusually good out of the box — most RAG projects start by building this structure; NodeOps starts with it already there.


🙏 Inspiration & prior art

Project What it shaped here
llmwiki The "wiki + LLM retrieval over the same store" model — keep human-edited knowledge and embeddings in one DB, retrieve at chat time. Drove the pgvector choice.
Docling In-process PDF / DOCX / XLSX / PPTX → text + layout extraction. Lets the KB ingest office docs without a separate Tika-style sidecar service.
Gotenberg HTTP wrapper around LibreOffice headless — converts office docs to PDF for the inline KB preview, so the operator sees the slide as the author drew it, not just the extracted text.

About

Self-hostable operations platform for data-center field teams — FastAPI + Postgres + 3 React frontends + SwiftUI iOS app, realtime WebSocket, on-operation incident system, KB wiki with file search, and on-prem object storage.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors