diff --git a/README.md b/README.md index b19e2cd..a83d516 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ # innercontext -Personal health and skincare data hub. Collects structured data (products, routines, lab results, medications, skin snapshots) and exposes it via a REST API and MCP to an LLM agent. +Personal health and skincare data hub. Collects structured data (products, routines, lab results, medications, skin snapshots) and exposes it via a REST API, MCP, and a web UI to an LLM agent. ## Repository layout ``` -backend/ Python backend — FastAPI REST API + SQLModel models +backend/ Python backend — FastAPI REST API + MCP server + SQLModel models +frontend/ SvelteKit web UI (Svelte 5, TypeScript, Tailwind CSS v4) +docs/ Deployment guides +nginx/ nginx config for production +systemd/ systemd service units ``` -A frontend will be added in the future. - ## Backend quick start **Requirements:** Python 3.12+, PostgreSQL, [uv](https://docs.astral.sh/uv/) @@ -29,6 +31,22 @@ uv run uvicorn main:app --reload API docs available at `http://localhost:8000/docs`. +## Frontend quick start + +**Requirements:** Node.js 22+, [pnpm](https://pnpm.io/) + +```bash +cd frontend + +# Install dependencies +pnpm install + +# Start dev server (proxies API calls to localhost:8000) +pnpm dev +``` + +UI available at `http://localhost:5173`. + ## API overview | Prefix | Resource | @@ -39,9 +57,32 @@ API docs available at `http://localhost:8000/docs`. | `/health/lab-results` | Lab test results | | `/routines` | AM/PM skincare routines and steps | | `/routines/grooming-schedule` | Weekly grooming schedule | -| `/skin-snapshots` | Weekly skin condition snapshots | +| `/skincare` | Weekly skin condition snapshots | | `/health-check` | Liveness probe | +## MCP server + +innercontext exposes 14 tools via [FastMCP](https://github.com/jlowin/fastmcp) at the StreamableHTTP endpoint `http://localhost:8000/mcp/mcp`. + +Tools include: `get_products`, `get_product`, `get_open_inventory`, `get_recent_routines`, `get_latest_skin_snapshot`, `get_skin_history`, `get_medications`, `get_expiring_inventory`, `get_grooming_schedule`, `get_recent_lab_results`, and more. + +Connect an MCP-compatible LLM agent by pointing it at `http:///mcp/mcp`. + +## Frontend routes + +| Route | Description | +|---|---| +| `/` | Dashboard | +| `/products` | Product list | +| `/products/new` | Add product | +| `/products/[id]` | Product detail / edit | +| `/routines` | Routine list | +| `/routines/new` | Create routine | +| `/routines/[id]` | Routine detail | +| `/health/medications` | Medications | +| `/health/lab-results` | Lab results | +| `/skin` | Skin condition snapshots | + ## Development ```bash @@ -53,10 +94,17 @@ uv run ruff check . # Format uv run black . uv run isort . + +# Tests +uv run pytest ``` ## Stack -- **Runtime:** Python 3.12, FastAPI, Uvicorn -- **ORM:** SQLModel 0.0.37 + SQLAlchemy, Pydantic v2 -- **Database:** PostgreSQL (psycopg3) +- **Backend:** Python 3.12, FastAPI, Uvicorn, SQLModel 0.0.37 + SQLAlchemy, Pydantic v2, PostgreSQL (psycopg3) +- **MCP:** FastMCP 3.0.2 (StreamableHTTP transport) +- **Frontend:** SvelteKit 2, Svelte 5 (Runes), TypeScript, Tailwind CSS v4, shadcn-svelte + +## Deployment + +See [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) for a step-by-step guide for a Proxmox LXC setup (Debian 13, nginx, systemd services). diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md new file mode 100644 index 0000000..91bf6d4 --- /dev/null +++ b/docs/DEPLOYMENT.md @@ -0,0 +1,311 @@ +# Deployment guide — Proxmox LXC (home network) + +Target architecture: + +``` +Reverse proxy (existing) innercontext LXC (new, Debian 13) +┌──────────────────────┐ ┌────────────────────────────────────┐ +│ reverse proxy │────────────▶│ nginx :80 │ +│ innercontext.lan → * │ │ /api/* → uvicorn :8000/* │ +└──────────────────────┘ │ /mcp/* → uvicorn :8000/mcp/* │ + │ /* → SvelteKit Node :3000 │ + └────────────────────────────────────┘ + │ │ + FastAPI + MCP SvelteKit Node +``` + +## 1. Prerequisites + +- Proxmox VE host with an existing PostgreSQL LXC and a reverse proxy +- LAN hostname `innercontext.lan` resolvable on the network (via router DNS or `/etc/hosts`) +- The PostgreSQL LXC must accept connections from the innercontext LXC IP + +--- + +## 2. Create the LXC container + +In the Proxmox UI (or via CLI): + +```bash +# CLI example — adjust storage, bridge, IP to your environment +pct create 200 local:vztmpl/debian-13-standard_13.0-1_amd64.tar.zst \ + --hostname innercontext \ + --cores 2 \ + --memory 1024 \ + --swap 512 \ + --rootfs local-lvm:8 \ + --net0 name=eth0,bridge=vmbr0,ip=dhcp \ + --unprivileged 1 \ + --start 1 +``` + +Note the container's IP address after it starts (`pct exec 200 -- ip -4 a`). + +--- + +## 3. Container setup + +```bash +pct enter 200 # or SSH into the container +``` + +### System packages + +```bash +apt update && apt upgrade -y +apt install -y git nginx curl ca-certificates gnupg lsb-release +``` + +### Python 3.12+ + uv + +```bash +apt install -y python3 python3-venv +curl -LsSf https://astral.sh/uv/install.sh | sh +source $HOME/.local/bin/env # or re-login +``` + +### Node.js 22 + pnpm + +```bash +curl -fsSL https://deb.nodesource.com/setup_22.x | bash - +apt install -y nodejs +npm install -g pnpm +``` + +### Application user + +```bash +useradd --system --create-home --shell /bin/bash innercontext +``` + +--- + +## 4. Create the database on the PostgreSQL LXC + +Run on the **PostgreSQL LXC**: + +```bash +psql -U postgres <<'SQL' +CREATE USER innercontext WITH PASSWORD 'change-me'; +CREATE DATABASE innercontext OWNER innercontext; +SQL +``` + +Edit `/etc/postgresql/18/main/pg_hba.conf` and add (replace `` with the innercontext container IP): + +``` +host innercontext innercontext /32 scram-sha-256 +``` + +Then reload: + +```bash +systemctl reload postgresql +``` + +--- + +## 5. Clone the repository + +```bash +mkdir -p /opt/innercontext +git clone https://github.com/your-user/innercontext.git /opt/innercontext +chown -R innercontext:innercontext /opt/innercontext +``` + +--- + +## 6. Backend setup + +```bash +cd /opt/innercontext/backend +``` + +### Install dependencies + +```bash +sudo -u innercontext uv sync +``` + +### Create `.env` + +```bash +cat > /opt/innercontext/backend/.env <<'EOF' +DATABASE_URL=postgresql+psycopg://innercontext:change-me@/innercontext +EOF +chmod 600 /opt/innercontext/backend/.env +chown innercontext:innercontext /opt/innercontext/backend/.env +``` + +### Test + +```bash +sudo -u innercontext bash -c ' + cd /opt/innercontext/backend + source .env + uv run uvicorn main:app --host 127.0.0.1 --port 8000 +' +# Ctrl-C after confirming it starts +``` + +### Install systemd service + +```bash +cp /opt/innercontext/systemd/innercontext.service /etc/systemd/system/ +systemctl daemon-reload +systemctl enable --now innercontext +systemctl status innercontext +``` + +--- + +## 7. Frontend build and setup + +```bash +cd /opt/innercontext/frontend +``` + +### Create `.env.production` + +```bash +cat > /opt/innercontext/frontend/.env.production <<'EOF' +PUBLIC_API_BASE=http://innercontext.lan/api +EOF +chmod 600 /opt/innercontext/frontend/.env.production +chown innercontext:innercontext /opt/innercontext/frontend/.env.production +``` + +### Install dependencies and build + +```bash +sudo -u innercontext bash -c ' + cd /opt/innercontext/frontend + pnpm install + PUBLIC_API_BASE=http://innercontext.lan/api pnpm build +' +``` + +The production build lands in `/opt/innercontext/frontend/build/`. + +### Install systemd service + +```bash +cp /opt/innercontext/systemd/innercontext-node.service /etc/systemd/system/ +systemctl daemon-reload +systemctl enable --now innercontext-node +systemctl status innercontext-node +``` + +--- + +## 8. nginx setup + +```bash +cp /opt/innercontext/nginx/innercontext.conf /etc/nginx/sites-available/innercontext +ln -s /etc/nginx/sites-available/innercontext /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default +nginx -t +systemctl reload nginx +``` + +--- + +## 9. Reverse proxy configuration + +Point your existing reverse proxy at the innercontext LXC's nginx (`:80`). + +Example — Caddy: + +``` +innercontext.lan { + reverse_proxy :80 +} +``` + +Example — nginx upstream: + +```nginx +server { + listen 80; + server_name innercontext.lan; + location / { + proxy_pass http://:80; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } +} +``` + +Reload your reverse proxy after applying the change. + +--- + +## 10. Verification + +```bash +# From any machine on the LAN: +curl http://innercontext.lan/api/health-check # {"status":"ok"} +curl http://innercontext.lan/api/products # [] +curl http://innercontext.lan/ # SvelteKit HTML shell +curl -N http://innercontext.lan/mcp/mcp # MCP StreamableHTTP endpoint +``` + +The web UI should be accessible at `http://innercontext.lan`. + +--- + +## 11. Updating the application + +```bash +cd /opt/innercontext +git pull + +# If backend dependencies changed: +cd backend && sudo -u innercontext uv sync && cd .. + +# Rebuild frontend: +cd frontend && sudo -u innercontext bash -c ' + pnpm install + PUBLIC_API_BASE=http://innercontext.lan/api pnpm build +' + +systemctl restart innercontext innercontext-node +``` + +--- + +## 12. Troubleshooting + +### 502 Bad Gateway on `/api/*` + +```bash +systemctl status innercontext +journalctl -u innercontext -n 50 +# Check .env DATABASE_URL is correct and PG LXC accepts connections +``` + +### 502 Bad Gateway on `/` + +```bash +systemctl status innercontext-node +journalctl -u innercontext-node -n 50 +# Verify /opt/innercontext/frontend/build/index.js exists (pnpm build ran successfully) +``` + +### MCP endpoint not responding + +```bash +# MCP uses SSE — disable buffering is already in nginx config +# Verify the backend started successfully: +curl http://127.0.0.1:8000/health-check +# Check FastAPI logs: +journalctl -u innercontext -n 50 +``` + +### Database connection refused + +```bash +# From innercontext LXC: +psql postgresql+psycopg://innercontext:change-me@/innercontext -c "SELECT 1" +# If it fails, check pg_hba.conf on the PG LXC and verify the IP matches +``` diff --git a/frontend/README.md b/frontend/README.md index 6ee8cad..c48d921 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,42 +1,74 @@ -# sv +# innercontext — frontend -Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). +SvelteKit web UI for innercontext. Provides a browser interface for managing skincare products, routines, health data, and skin condition snapshots. -## Creating a project +## Stack -If you're seeing this, you've probably already done this step. Congrats! +- **Framework:** SvelteKit 2, Svelte 5 (Runes) +- **Language:** TypeScript +- **Styles:** Tailwind CSS v4 +- **Components:** shadcn-svelte (bits-ui) +- **Adapter:** `@sveltejs/adapter-node` (required — uses `+page.server.ts` form actions) -```sh -# create a new project -npx sv create my-app +## Development + +```bash +# Install dependencies +pnpm install + +# Start dev server (API proxied to localhost:8000) +pnpm dev ``` -To recreate this project with the same configuration: +The backend must be running at `http://localhost:8000`. See `../backend/` for setup instructions. -```sh -# recreate this project -pnpm dlx sv create --template minimal --types ts --install pnpm frontend +## Environment variables + +| Variable | Description | Default | +|---|---|---| +| `PUBLIC_API_BASE` | Base URL of the FastAPI backend | `http://localhost:8000` | + +Set `PUBLIC_API_BASE` at **build time** for production: + +```bash +PUBLIC_API_BASE=http://innercontext.lan/api pnpm build ``` -## Developing +## Building for production -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: - -```sh -npm run dev - -# or start the server and open the app in a new browser tab -npm run dev -- --open +```bash +pnpm build +# Output: build/ ``` -## Building +Run the production server: -To create a production version of your app: - -```sh -npm run build +```bash +node build/index.js ``` -You can preview the production build with `npm run preview`. +Or use the provided systemd service: `../systemd/innercontext-node.service`. -> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. +## Routes + +| Route | Description | +|---|---| +| `/` | Dashboard | +| `/products` | Product list | +| `/products/new` | Add product | +| `/products/[id]` | Product detail / edit | +| `/routines` | Routine list | +| `/routines/new` | Create routine | +| `/routines/[id]` | Routine detail | +| `/health/medications` | Medications | +| `/health/lab-results` | Lab results | +| `/skin` | Skin condition snapshots | + +## Key files + +| File | Purpose | +|---|---| +| `src/lib/api.ts` | API client (typed fetch wrappers) | +| `src/lib/types.ts` | Shared TypeScript types | +| `src/app.css` | Tailwind v4 theme + global styles | +| `svelte.config.js` | SvelteKit config (adapter-node) | diff --git a/frontend/package.json b/frontend/package.json index 60af085..371022b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,7 @@ "devDependencies": { "@internationalized/date": "^3.11.0", "@lucide/svelte": "^0.561.0", - "@sveltejs/adapter-auto": "^7.0.0", + "@sveltejs/adapter-node": "^5.0.0", "@sveltejs/kit": "^2.50.2", "@sveltejs/vite-plugin-svelte": "^6.2.4", "@tailwindcss/vite": "^4.2.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 7c5a708..64ad603 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -30,9 +30,9 @@ importers: '@lucide/svelte': specifier: ^0.561.0 version: 0.561.0(svelte@5.53.5) - '@sveltejs/adapter-auto': - specifier: ^7.0.0 - version: 7.0.1(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.5)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1))) + '@sveltejs/adapter-node': + specifier: ^5.0.0 + version: 5.5.4(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.5)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1))) '@sveltejs/kit': specifier: ^2.50.2 version: 2.53.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.5)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)) @@ -255,6 +255,42 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@rollup/plugin-commonjs@29.0.0': + resolution: {integrity: sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@16.0.3': + resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + '@rollup/rollup-android-arm-eabi@4.59.0': resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] @@ -401,10 +437,10 @@ packages: peerDependencies: acorn: ^8.9.0 - '@sveltejs/adapter-auto@7.0.1': - resolution: {integrity: sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ==} + '@sveltejs/adapter-node@5.5.4': + resolution: {integrity: sha512-45X92CXW+2J8ZUzPv3eLlKWEzINKiiGeFWTjyER4ZN4sGgNoaoeSkCY/QYNxHpPXy71QPsctwccBo9jJs0ySPQ==} peerDependencies: - '@sveltejs/kit': ^2.0.0 + '@sveltejs/kit': ^2.4.0 '@sveltejs/kit@2.53.2': resolution: {integrity: sha512-M+MqAvFve12T1HWws/2npP/s3hFtyjw3GB/OXW/8a1jZBk48qnvPJrtgE+VOMc3RnjUMxc4mv/vQ73nvj2uNMg==} @@ -540,6 +576,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -571,6 +610,9 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} @@ -605,6 +647,9 @@ packages: esrap@2.2.3: resolution: {integrity: sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ==} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -619,12 +664,29 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} @@ -746,6 +808,9 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -761,6 +826,11 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + rollup@4.59.0: resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -803,6 +873,10 @@ packages: style-to-object@1.0.14: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + svelte-check@4.4.4: resolution: {integrity: sha512-F1pGqXc710Oi/wTI4d/x7d6lgPwwfx1U6w3Q35n4xsC2e8C/yN2sM1+mWxjlMcpAfWucjlq4vPi+P4FZ8a14sQ==} engines: {node: '>= 18.0.0'} @@ -1037,6 +1111,42 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@rollup/plugin-commonjs@29.0.0(rollup@4.59.0)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.5.0(picomatch@4.0.3) + is-reference: 1.2.1 + magic-string: 0.30.21 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.59.0 + + '@rollup/plugin-json@6.1.0(rollup@4.59.0)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + optionalDependencies: + rollup: 4.59.0 + + '@rollup/plugin-node-resolve@16.0.3(rollup@4.59.0)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.11 + optionalDependencies: + rollup: 4.59.0 + + '@rollup/pluginutils@5.3.0(rollup@4.59.0)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.59.0 + '@rollup/rollup-android-arm-eabi@4.59.0': optional: true @@ -1118,9 +1228,13 @@ snapshots: dependencies: acorn: 8.16.0 - '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.5)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)))': + '@sveltejs/adapter-node@5.5.4(@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.5)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)))': dependencies: + '@rollup/plugin-commonjs': 29.0.0(rollup@4.59.0) + '@rollup/plugin-json': 6.1.0(rollup@4.59.0) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.59.0) '@sveltejs/kit': 2.53.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.5)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)) + rollup: 4.59.0 '@sveltejs/kit@2.53.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.53.5)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1)))(svelte@5.53.5)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1))': dependencies: @@ -1235,6 +1349,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/resolve@1.20.2': {} + '@types/trusted-types@2.0.7': {} acorn@8.16.0: {} @@ -1262,6 +1378,8 @@ snapshots: clsx@2.1.1: {} + commondir@1.0.1: {} + cookie@0.6.0: {} deepmerge@4.3.1: {} @@ -1312,6 +1430,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + estree-walker@2.0.2: {} + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -1319,10 +1439,26 @@ snapshots: fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + graceful-fs@4.2.11: {} + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + inline-style-parser@0.2.7: {} + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-module@1.0.0: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.8 + is-reference@3.0.3: dependencies: '@types/estree': 1.0.8 @@ -1406,6 +1542,8 @@ snapshots: obug@2.1.1: {} + path-parse@1.0.7: {} + picocolors@1.1.1: {} picomatch@4.0.3: {} @@ -1418,6 +1556,12 @@ snapshots: readdirp@4.1.2: {} + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 @@ -1486,6 +1630,8 @@ snapshots: dependencies: inline-style-parser: 0.2.7 + supports-preserve-symlinks-flag@1.0.0: {} + svelte-check@4.4.4(picomatch@4.0.3)(svelte@5.53.5)(typescript@5.9.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 diff --git a/frontend/svelte.config.js b/frontend/svelte.config.js index 10c4eeb..4422a7d 100644 --- a/frontend/svelte.config.js +++ b/frontend/svelte.config.js @@ -1,12 +1,9 @@ -import adapter from '@sveltejs/adapter-auto'; +import adapter from '@sveltejs/adapter-node'; /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { - // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. - // If your environment is not supported, or you settled on a specific environment, switch out the adapter. - // See https://svelte.dev/docs/kit/adapters for more information about adapters. - adapter: adapter() + adapter: adapter({ out: 'build' }) } }; diff --git a/nginx/innercontext.conf b/nginx/innercontext.conf new file mode 100644 index 0000000..1757c52 --- /dev/null +++ b/nginx/innercontext.conf @@ -0,0 +1,34 @@ +server { + listen 80; + server_name _; + + # FastAPI backend — strip /api/ prefix + location /api/ { + proxy_pass http://127.0.0.1:8000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # MCP endpoint — keep /mcp/ prefix; disable buffering for SSE streaming + location /mcp/ { + proxy_pass http://127.0.0.1:8000/mcp/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 3600s; + } + + # SvelteKit Node server + location / { + proxy_pass http://127.0.0.1:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/systemd/innercontext-node.service b/systemd/innercontext-node.service new file mode 100644 index 0000000..6f82e10 --- /dev/null +++ b/systemd/innercontext-node.service @@ -0,0 +1,18 @@ +[Unit] +Description=innercontext SvelteKit Node frontend +After=network.target + +[Service] +Type=simple +User=innercontext +Group=innercontext +WorkingDirectory=/opt/innercontext/frontend +Environment=PORT=3000 +Environment=HOST=127.0.0.1 +EnvironmentFile=/opt/innercontext/frontend/.env.production +ExecStart=/usr/bin/node /opt/innercontext/frontend/build/index.js +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/systemd/innercontext.service b/systemd/innercontext.service new file mode 100644 index 0000000..b3a3b9c --- /dev/null +++ b/systemd/innercontext.service @@ -0,0 +1,16 @@ +[Unit] +Description=innercontext FastAPI backend +After=network.target + +[Service] +Type=simple +User=innercontext +Group=innercontext +WorkingDirectory=/opt/innercontext/backend +EnvironmentFile=/opt/innercontext/backend/.env +ExecStart=/opt/innercontext/backend/.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target