From 66597078904208a9faf5684c53dff16db916eeae Mon Sep 17 00:00:00 2001 From: jason Date: Mon, 23 Mar 2026 16:16:45 -0500 Subject: [PATCH] initial release testing --- .dockerignore | 13 + .env.example | 5 + .gitignore | 5 + Dockerfile | 23 + README.md | 120 +++- UNRAID.md | 175 +++++ app/accounting/page.tsx | 92 +++ app/api/health/route.ts | 11 + app/assemblies/page.tsx | 63 ++ app/customers/page.tsx | 43 ++ app/globals.css | 255 +++++++ app/invoices/page.tsx | 51 ++ app/layout.tsx | 47 ++ app/login/page.tsx | 25 + app/page.tsx | 99 +++ app/parts/page.tsx | 65 ++ app/purchase-orders/page.tsx | 92 +++ app/sales-orders/page.tsx | 66 ++ app/vendor-bills/page.tsx | 51 ++ app/vendors/page.tsx | 42 ++ components/logout-button.tsx | 11 + components/sidebar.tsx | 42 ++ docs/adr/0001-monolith-nextjs-sqlite.md | 55 ++ docs/architecture.md | 87 +++ lib/actions.ts | 865 ++++++++++++++++++++++++ lib/auth.ts | 145 ++++ lib/db.ts | 44 ++ lib/format.ts | 13 + lib/repository.ts | 319 +++++++++ lib/schema.ts | 214 ++++++ lib/types.ts | 132 ++++ middleware.ts | 77 +++ next-env.d.ts | 4 + next.config.ts | 7 + package.json | 24 + public/.gitkeep | 1 + tsconfig.json | 28 + 37 files changed, 3374 insertions(+), 37 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 UNRAID.md create mode 100644 app/accounting/page.tsx create mode 100644 app/api/health/route.ts create mode 100644 app/assemblies/page.tsx create mode 100644 app/customers/page.tsx create mode 100644 app/globals.css create mode 100644 app/invoices/page.tsx create mode 100644 app/layout.tsx create mode 100644 app/login/page.tsx create mode 100644 app/page.tsx create mode 100644 app/parts/page.tsx create mode 100644 app/purchase-orders/page.tsx create mode 100644 app/sales-orders/page.tsx create mode 100644 app/vendor-bills/page.tsx create mode 100644 app/vendors/page.tsx create mode 100644 components/logout-button.tsx create mode 100644 components/sidebar.tsx create mode 100644 docs/adr/0001-monolith-nextjs-sqlite.md create mode 100644 docs/architecture.md create mode 100644 lib/actions.ts create mode 100644 lib/auth.ts create mode 100644 lib/db.ts create mode 100644 lib/format.ts create mode 100644 lib/repository.ts create mode 100644 lib/schema.ts create mode 100644 lib/types.ts create mode 100644 middleware.ts create mode 100644 next-env.d.ts create mode 100644 next.config.ts create mode 100644 package.json create mode 100644 public/.gitkeep create mode 100644 tsconfig.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ee09fa5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.git +.next +node_modules +npm-debug.log* +data +docs +skills +hubs +PROJECT-PROFILE-WORKBOOK.md +ROUTING-EXAMPLES.md +SKILLS.md +AGENTS.md +DEPLOYMENT-PROFILE.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5afc745 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +DATABASE_PATH=/data/inven.sqlite +APP_NAME=Inven +AUTH_SECRET=change-me-to-a-long-random-secret +ADMIN_EMAIL=admin@example.com +ADMIN_PASSWORD=change-me-now diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d47f00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.next +node_modules +data +.env +npm-debug.log* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f4b7477 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM node:22-alpine AS deps +WORKDIR /app +COPY package.json ./ +RUN npm install + +FROM node:22-alpine AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN npm run build + +FROM node:22-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +ENV PORT=3000 +ENV HOSTNAME=0.0.0.0 +ENV DATABASE_PATH=/data/inven.sqlite +RUN mkdir -p /data +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static +COPY --from=builder /app/public ./public +EXPOSE 3000 +CMD ["node", "server.js"] diff --git a/README.md b/README.md index 2a16f86..1cb4015 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,93 @@ -# Drop-In Agent Instruction Suite +# Inven -This repository is a portable markdown instruction pack for coding agents. +Inven is a single-container inventory management system for Unraid-style deployments. It manages stocked parts, assemblies built from parts, sales orders and shipping, purchase orders and restocking, customers, vendors, and a lightweight accounting journal on top of SQLite. -Copy these files into another repository to give the agent: -- a root `AGENTS.md` entrypoint, -- a central skill index, -- category hubs for routing, -- specialized skill files for common software, docs, UX, marketing, and ideation tasks. +## Current Scope -## Structure +- Parts and assemblies share one item master +- Assemblies support bill-of-material component definitions +- Inventory is tracked through a transaction ledger +- Sales orders can be created and shipped +- Purchase orders can be created and received +- Sales orders support partial shipments +- Purchase orders support partial receipts +- Invoices are generated from shipped sales orders +- Vendor bills are generated from received purchase orders +- Customer and vendor payments can clear receivables and payables +- Customer and vendor directories support the order flows +- Low-stock and suggested reorder views help drive replenishment +- A chart of accounts, account balances, and manual journals support the first accounting pass +- Built-in authentication protects the app with a bootstrap admin login -- `AGENTS.md` - base instructions and routing rules -- `DEPLOYMENT-PROFILE.md` - agent-readable prefilled deployment defaults -- `INSTALL.md` - copy and customization guide for other repositories -- `PROJECT-PROFILE-WORKBOOK.md` - one-time questionnaire for staging defaults -- `SKILLS.md` - canonical skill index -- `ROUTING-EXAMPLES.md` - representative prompt-to-skill routing examples -- `hubs/` - category-level routing guides -- `skills/` - specialized reusable skill files +## Stack -## Design Goals +- Next.js App Router +- TypeScript +- SQLite via `better-sqlite3` +- Single Docker container with Next.js standalone output -- Plain markdown only -- Cross-agent portability -- Implementation-first defaults -- On-demand skill loading instead of loading everything every session -- Context-efficient routing for large skill libraries -- Prefilled deployment defaults without per-install questioning -- Repo-local instructions take precedence over this bundle +## Quick Start -## Intended Workflow +1. Install Node.js 22 or newer. +2. Copy `.env.example` to `.env`. +3. Set `AUTH_SECRET`, `ADMIN_EMAIL`, and `ADMIN_PASSWORD`. +4. Update `DATABASE_PATH` if needed. +5. Install dependencies: -1. The agent reads `AGENTS.md`. -2. The agent reads `DEPLOYMENT-PROFILE.md` when it is filled in. -3. The agent checks `SKILLS.md`. -4. The agent opens only the relevant hub and skill files for the task. -5. The agent combines multiple skills when the task spans several domains. +```bash +npm install +``` -## Core Categories +6. Start the development server: -- Software development -- Debugging -- Documentation -- UI/UX -- Marketing -- Brainstorming +```bash +npm run dev +``` + +7. Open `http://localhost:3000` and sign in with the bootstrap admin credentials. + +The SQLite schema is created automatically on first run. +If the database has no users yet, the bootstrap admin user is created from `ADMIN_EMAIL` and `ADMIN_PASSWORD`. + +## Docker + +Build and run locally: + +```bash +docker build -t inven . +docker run --rm -p 3000:3000 -v $(pwd)/data:/data inven +``` + +Suggested Unraid mapping: + +- App data mount: `/data` +- Container port: `3000` +- Environment variable: `DATABASE_PATH=/data/inven.sqlite` +- Environment variable: `AUTH_SECRET=` +- Environment variable: `ADMIN_EMAIL=` +- Environment variable: `ADMIN_PASSWORD=` + +## Workflow Notes + +- Add customers and vendors before creating orders. +- Add parts and assemblies in the Parts module. +- Define assembly components in the Assemblies module. +- Use purchase orders to restock and receive inventory. +- Use build transactions to convert component stock into assembly stock. +- Use sales orders and ship them fully or partially to reduce stock and generate invoices plus journal entries. +- Use purchase orders and receive them fully or partially to increase stock and generate vendor bills plus journal entries. +- Use the Invoices page to receive customer payments against open AR. +- Use the Vendor Bills page to pay vendor obligations against open AP. +- Use the accounting page to add accounts, review balances, and post manual journals when needed. +- Use the login page to access the application with the configured admin account. + +## Known Gaps + +- No lot, serial, warehouse, or bin tracking yet +- No lockfile included because dependencies were not installed in this environment + +## Project Docs + +- [Architecture overview](./docs/architecture.md) +- [ADR: monolith stack and data model](./docs/adr/0001-monolith-nextjs-sqlite.md) +- [Unraid installation guide](./UNRAID.md) diff --git a/UNRAID.md b/UNRAID.md new file mode 100644 index 0000000..11baadf --- /dev/null +++ b/UNRAID.md @@ -0,0 +1,175 @@ +# Unraid Installation Guide + +This document is part of the install contract for this repository. + +If a change would alter how the container is built, started, configured, mapped, or authenticated, this file must be updated in the same change. + +## What You Need + +- Unraid server with Docker enabled +- Network access from Unraid to clone the repository +- A persistent appdata location for the SQLite database +- Initial admin credentials and a long random auth secret + +## Required Environment Variables + +- `DATABASE_PATH` + Recommended: `/data/inven.sqlite` +- `AUTH_SECRET` + Use a long random string. This signs login sessions. +- `ADMIN_EMAIL` + Initial bootstrap admin email. +- `ADMIN_PASSWORD` + Initial bootstrap admin password. + +Important: + +- The bootstrap admin is created only when the database has no users yet. +- Changing `ADMIN_EMAIL` or `ADMIN_PASSWORD` after first boot does not replace an existing user automatically. +- Keep `AUTH_SECRET` stable after deployment. Rotating it will invalidate active sessions. + +## CLI Build And Run + +These steps are useful if you want to build on the Unraid host or another Linux machine before using the Unraid GUI. + +### 1. Clone the repository + +```bash +git clone inven +cd inven +``` + +### 2. Build the image + +```bash +docker build -t inven:latest . +``` + +### 3. Create a persistent data directory + +Example: + +```bash +mkdir -p /mnt/user/appdata/inven/data +``` + +### 4. Run the container + +```bash +docker run -d \ + --name inven \ + -p 3000:3000 \ + -v /mnt/user/appdata/inven/data:/data \ + -e DATABASE_PATH=/data/inven.sqlite \ + -e AUTH_SECRET='replace-with-a-long-random-secret' \ + -e ADMIN_EMAIL='admin@example.com' \ + -e ADMIN_PASSWORD='replace-with-a-strong-password' \ + --restart unless-stopped \ + inven:latest +``` + +### 5. Open the app + +- URL: `http://:3000` +- Sign in with the configured bootstrap admin credentials + +## Unraid GUI Setup + +These instructions assume you are creating a custom Docker container in the Unraid web UI. + +### 1. Build or provide an image + +You need an image first. Use one of these paths: + +- Build from the CLI steps above and tag/push it to a registry you can pull from Unraid +- Or build locally on the Unraid host and reference that image name directly + +Example image name: + +- `inven:latest` + +### 2. Add the container in Unraid + +In the Unraid Docker tab: + +1. Click `Add Container` +2. Set `Name` to `inven` +3. Set `Repository` to your image, for example `inven:latest` +4. Set `Network Type` to `bridge` + +### 3. Configure ports + +Add this port mapping: + +- `Container Port`: `3000` +- `Host Port`: `3000` +- `Connection Type`: `TCP` + +If `3000` is already in use on the host, choose a different host port such as `8087`. + +### 4. Configure paths + +Add this path mapping: + +- `Name`: `data` +- `Container Path`: `/data` +- `Host Path`: `/mnt/user/appdata/inven/data` +- `Access Mode`: `Read/Write` + +This path stores the SQLite database and must be persistent. + +### 5. Configure environment variables + +Add these variables: + +- `DATABASE_PATH` + Value: `/data/inven.sqlite` +- `AUTH_SECRET` + Value: a long random secret +- `ADMIN_EMAIL` + Value: your initial admin email +- `ADMIN_PASSWORD` + Value: your initial admin password + +### 6. Apply and start + +1. Click `Apply` +2. Start the container +3. Open `http://:3000` +4. Log in with `ADMIN_EMAIL` and `ADMIN_PASSWORD` + +## Updating The Container + +When app changes do not require install changes: + +1. Pull or rebuild the updated image +2. Recreate the container with the same `/data` mapping +3. Keep `DATABASE_PATH` and `AUTH_SECRET` consistent + +When app changes do require install changes: + +- Update this document +- Update `.env.example` +- Update the README install section if needed + +## Troubleshooting + +### App starts but login does not work + +- Confirm `ADMIN_EMAIL` and `ADMIN_PASSWORD` were present on first boot +- If the database already existed before auth was configured, the bootstrap user may not have been created +- Confirm `AUTH_SECRET` is set and stable + +### Sessions keep getting invalidated + +- Confirm `AUTH_SECRET` is unchanged across restarts or image updates + +### Data disappears after container recreation + +- Confirm `/data` is mapped to a persistent Unraid host path +- Confirm `DATABASE_PATH=/data/inven.sqlite` + +### Container starts but app is unreachable + +- Confirm the host port is mapped correctly +- Confirm no other container is already using the selected host port diff --git a/app/accounting/page.tsx b/app/accounting/page.tsx new file mode 100644 index 0000000..f85e2fe --- /dev/null +++ b/app/accounting/page.tsx @@ -0,0 +1,92 @@ +import { createAccount, createManualJournalEntry } from "@/lib/actions"; +import { formatCurrency, formatDate } from "@/lib/format"; +import { getAccountBalances, getAccounts, getJournalEntries } from "@/lib/repository"; + +export default function AccountingPage() { + const entries = getJournalEntries(); + const balances = getAccountBalances(); + const accounts = getAccounts(); + + return ( +
+
+
+

Chart of Accounts

+

Seeded operational accounts are ready, and you can add your own accounts here.

+
+
+
+
+ + +
+ +
+
+
+

Manual Journal Entry

+

Enter one line per row as `account code,debit,credit`. Debits and credits must balance.

+
+
+