Javis OS
Javis OS는 기업가를 돕는 에이전시 AI로, 업무를 지원하고 함께 성장합니다
자체 호스팅 현장 서비스 관리 플랫폼, 파견·견적·청구·결제·급여 관리, 데이터는 직접 보유
Self-hosted field-service management platform — dispatch, estimates, invoicing, payments, payroll. Own you data don't let others control it. CRM ERP FSM
비즈니스Python · JavaScriptGitHub @freePerro온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A single-tenant, self-hosted field-service management platform for
service business — scheduling and dispatch, jobs and work orders,
estimates and proposals, invoicing, inventory, payroll, and customer records.
FastAPI backend, Vue 3 single-page frontend.
The platform spans the full field-service workflow, organized as feature
modules under gdx_dispatch/modules/ and HTTP routes
under gdx_dispatch/routers/:README 전문 보기 ↗A single-tenant, self-hosted field-service management platform for service business — scheduling and dispatch, jobs and work orders, estimates and proposals, invoicing, inventory, payroll, and customer records. FastAPI backend, Vue 3 single-page frontend.
The platform spans the full field-service workflow, organized as feature
modules under gdx_dispatch/modules/ and HTTP routes
under gdx_dispatch/routers/:
gdx_dispatch/plugin_host/, ADR-013)Access is permission-driven with role canonicalization — see
docs/ROLE_AND_NAV_NAMING_CONVENTIONS.md.
grant_helpers.py uses pg_input_is_valid, which does not exist on 15 — a fresh install on 15 fails at that migration; every compose file pins postgres:16-alpine), schema managed by Alembicmodules/error_sink) + OpenTelemetry (optional)The application package is gdx_dispatch/; the inner gdx_dispatch/gdx_dispatch/
directory holds the Python source.
gdx_dispatch/ repo root
├── gdx_dispatch/ application package
│ ├── app.py FastAPI application factory
│ ├── main.py ASGI entrypoint (uvicorn gdx_dispatch.main:app)
│ ├── celery_app.py Celery app (workers + beat)
│ ├── routers/ HTTP API routes
│ ├── api/ public API router
│ ├── modules/ feature modules (quickbooks, payroll, inventory, …)
│ ├── models/ SQLAlchemy ORM models
│ ├── services/ domain services (pricing engine, …)
│ ├── core/ shared services, settings, auth helpers
│ ├── integrations/ third-party integration glue
│ ├── plugin_host/ sandboxed plugin runtime (separate container)
│ ├── plugin_api/ stable API surface exposed to plugins
│ ├── tasks/ Celery task definitions
│ ├── migrations/ Alembic migrations (+ squashed baseline)
│ ├── frontend/ Vue 3 + PrimeVue SPA (Vite)
│ ├── docs/ developer, admin, and runbook guides
│ ├── docker/ Docker Compose stacks + Dockerfiles
│ ├── tests/ pytest suite
│ ├── requirements.txt Python dependencies (ranges)
│ └── requirements.lock every package pinned; what CI and the images install
├── docs/ ops & runbook docs
└── pyproject.toml
The Vue frontend lives in gdx_dispatch/frontend/,
with its entrypoint at gdx_dispatch/frontend/src/main.js
and root component at gdx_dispatch/frontend/src/App.vue.
Requires Docker and Docker Compose. Local development runs the same compose
layout every install runs (gdx_dispatch/docker/docker-compose.yml) plus the
dev overlay (docker-compose.dev.yml), which builds the images from this
checkout. dev-up.sh wraps both, stamps the git SHA into the build, and creates
the dev overlay's two external volumes (so docker compose down -v can never
delete the dev database) on first run:
cp .env.template .env
# Secrets are minted on first boot; .env only needs what you want to override.
gdx_dispatch/docker/dev-up.sh --build # add --reload for hot reload
This brings up PostgreSQL, Redis, the API (whose container also runs the Celery
workers and beat), and the plugin host as compose project docker. The API is exposed on
http://localhost:8001 with a health check at /health.
On first start the API container automatically (and idempotently) runs database migrations, creates the application tables, and seeds a single default tenant plus an initial admin user so you can log in right away:
Email: GDX_ADMIN_EMAIL (default [email protected])
Password: GDX_ADMIN_PASSWORD if you set it, otherwise a random password is
generated and printed to the container logs:
docker logs docker-app-1 2>&1 | grep -A4 "initial admin account"
The admin is created with must_change_password set — change it after your
first login.
The easiest way to run GDX Dispatch is the compose file published with every
release. It is gdx_dispatch/docker/docker-compose.yml, the same layout
production runs, attached to the release as docker-compose.customer.yml. It needs no .env, no cloned repo, and no
secret to fill in — a secrets-init container mints strong secrets on first
boot and persists them in a volume, so restarts and updates keep your logins
and encrypted data.
Requirements: a Linux VPS with Docker 24+ and Compose v2 (any Hostinger/DigitalOcean "Docker" image works), ports 80/443 reachable, at least 4 GB of RAM, and — optionally — a domain with an A record pointing at the server.
curl -fsSL https://github.com/freePerro/gdx_dispatch/releases/latest/download/install.sh | sudo sh # release asset name; link-ok
# with a domain (automatic HTTPS via Let's Encrypt) and your login email:
curl -fsSL https://github.com/freePerro/gdx_dispatch/releases/latest/download/install.sh | sudo sh -s -- --domain dispatch.example.com --email [email protected] # release asset name; link-ok
That's the whole install. The script checks Docker, writes /opt/gdx/.env
(asking for the domain and email when run in a terminal), downloads the
compose file of its own release, and starts the stack. On first boot the stack
mints its secrets, runs all database migrations, seeds your admin account, and
obtains a real TLS certificate automatically: for the domain, or without one
for the server's public IP (a 6-day Let's Encrypt IP certificate that Caddy
renews). If that certificate cannot be issued within about two minutes (ports 80/443
blocked, or no public IPv4), the script falls back to plain HTTP and says so;
over plain HTTP a login lasts only 15 minutes, because the browser drops the
app's secure refresh cookie. The script waits until the app is healthy and
prints the address, the admin email and the one-time password. --help lists
its options (--ip, --dir, --install-docker, …).
Without the script, the same install is three commands; the admin password is then in the app log:
mkdir gdx && cd gdx
curl -fsSL https://github.com/freePerro/gdx_dispatch/releases/latest/download/docker-compose.customer.yml -o docker-compose.yml # release asset name; link-ok
echo 'GDX_DOMAIN=dispatch.example.com' > .env # optional — enables automatic HTTPS via Let's Encrypt
# or, with no domain, HTTPS on the server's public IPv4: echo 'GDX_PUBLIC_IP=203.0.113.7' > .env
docker compose -p gdx up -d
docker compose -p gdx logs app | grep -A5 "initial admin"
Optional settings go in the same .env (all documented in the compose file's
header): GDX_TENANT_NAME, GDX_ADMIN_EMAIL, GDX_ADMIN_PASSWORD,
APP_VERSION to pin a release, and integration keys (Stripe, QuickBooks,
Google Maps, SMTP) — every integration is optional and its feature simply
stays off while blank. A backup sidecar dumps the database daily to the
gdx_backups volume (30-day retention). To update, run the latest
install.sh again: it snapshots the database to /opt/gdx/backups/ first,
then pulls the new release and restarts, keeping every volume and setting.
Without the script, download the new release's compose file over
docker-compose.yml as above (an install made before install.sh named it
docker-compose.customer.yml; the script renames it), then:
docker compose -p gdx pull
docker compose -p gdx up -d --remove-orphans
Data lives in named volumes (gdx_db_data, gdx_uploads, gdx_recordings,
gdx_plugins, gdx_redis_data, gdx_backups, gdx_secrets);
docker compose down without -v never touches them.
Full guides on the wiki: Getting Started · Updating · Backup & Restore
The same file runs from a clone of this repository, which is how production
runs. It pulls ghcr.io/freeperro/gdx_dispatch:<APP_VERSION> (latest when
unset); nothing is built. The image tag is the single source of truth for the
running version (reported by /health and the admin update-check).
echo 'APP_VERSION=1.0.0' >> .env # or leave unset to track :latest
docker compose -p gdx --env-file ./.env -f gdx_dispatch/docker/docker-compose.yml up -d
A host that needs something the base does not do (production terminates TLS in
its own nginx and keeps volumes that predate this layout) adds an overlay and
names both files in .env, so every command and update.sh agree on them:
COMPOSE_FILE=gdx_dispatch/docker/docker-compose.yml:gdx_dispatch/docker/docker-compose.prod.yml
To upgrade to a newer release, bump APP_VERSION in .env (or leave it on
latest) and run the updater — it snapshots the database, pulls the new image,
applies migrations on boot (the app container starts its Celery workers after
them), and only starts plugin-host and the backup sidecar once /health is green:
./gdx_dispatch/docker/update.sh
# or pin explicitly: APP_VERSION=1.1.0 ./gdx_dispatch/docker/update.sh
The pre-update database snapshot is written to backups/ — keep it; it's your
rollback if a migration goes wrong (the script prints the exact restore steps if
the new image fails to come up). Admins can check whether a newer release exists
from Settings → update-check (GET /api/admin/update-check), which compares
the running version against the latest GitHub release.
Backend:
python -m venv .venv && source .venv/bin/activate
pip install --no-deps -r gdx_dispatch/requirements.lock # the set CI tests and the images ship
uvicorn gdx_dispatch.main:app --reload --port 8000
Frontend:
cd gdx_dispatch/frontend
npm install
npm run dev # Vite dev server
npm run build # production build (served by the backend in Docker)
All configuration is via environment variables. Copy .env.template to .env
and fill in the values — lines marked [REQUIRED] must be set for the app to
start; the template documents every option. Never commit your .env.
Backend (pytest):
.venv/bin/pytest gdx_dispatch/tests/ -q
Frontend (Vitest):
cd gdx_dispatch/frontend && npx vitest run
Developer and admin guides live under
gdx_dispatch/docs/ — start with
CONTRIBUTING.md,
DEVELOPER_GUIDE.md, and
ADMIN_GUIDE.md. Operations runbooks live
under docs/.
GDX Dispatch is released under the GNU Affero General Public License v3 (AGPLv3), with one additional permission: the Plugin Exception.
What that means in practice:
Versions released before this change were published under the MIT License and remain available under those terms.