release workflow

This commit is contained in:
2026-02-24 18:30:45 -06:00
parent 06e021f8a1
commit db9b722929
15 changed files with 398 additions and 77 deletions
+2 -2
View File
@@ -5,9 +5,9 @@ services:
image: postgres:17 image: postgres:17
restart: unless-stopped restart: unless-stopped
environment: environment:
POSTGRES_USER: ttscm POSTGRES_USER: optima
POSTGRES_PASSWORD: 123web123 POSTGRES_PASSWORD: 123web123
POSTGRES_DB: ttscm POSTGRES_DB: optima
volumes: volumes:
- ./postgres:/var/lib/postgresql/data - ./postgres:/var/lib/postgresql/data
ports: ports:
+177 -45
View File
@@ -1,89 +1,221 @@
# Copilot / AI Agent Instructions for ttscm-api # Copilot / AI Agent Instructions for optima-api
Purpose: make AI coding agents immediately productive in this repository by describing architecture, conventions, workflows, and helpful code pointers. Purpose: make AI coding agents immediately productive in this repository by describing architecture, conventions, workflows, and helpful code pointers.
-- **Big picture**: This is a TypeScript API service (runs on Bun) using the Hono framework. The HTTP surface is implemented in `src/api` where small router files are mounted on a versioned router in `src/api/server.ts` (see the `/v1` mount). Typical request flow is: ---
- `router` (in `src/api/routers/*`) → `controller` (in `src/controllers/*`) → `manager` (in `src/managers/*`) → `module` / `generated/prisma` for persistence and external integrations. ## Big picture
- Keep each layer focused: routers handle routing & middleware, controllers handle request validation and high-level orchestration, managers encapsulate domain/persistence logic, modules provide shared utilities (external API clients, helpers).
- **Runtime / tooling**: The project runs on Bun. Dev command: `npm run dev` (runs `bun --watch src/index.ts`). DB tooling uses Prisma; generated client lives under `generated/prisma` (do NOT edit generated files). Key scripts in `package.json`: This is a TypeScript API service (runs on **Bun**) using the **Hono** framework. The HTTP surface is implemented in `src/api` where small route-handler files are mounted on a versioned router in `src/api/server.ts` (see the `/v1` mount). The typical request flow is:
- `dev` — start the server with Bun in watch mode
```
server.ts → routers/<domain>Router.ts → api/<domain>/*.ts (route handlers) → managers/<domain>.ts → controllers (domain models) / modules / generated/prisma
```
Keep each layer focused:
- **Route handler files** (`src/api/<domain>/*.ts`) — define individual endpoints using the `createRoute()` utility, handle request validation with Zod, call managers, and return `apiResponse.*` results.
- **Routers** (`src/api/routers/*`) — aggregate route handler files from a domain's `index.ts` and re-mount them under a single prefix. Mounted in `src/api/server.ts`.
- **Managers** (`src/managers/*`) — thin domain/persistence layers that wrap `generated/prisma` calls and other I/O. Managers instantiate and return **controller** instances as domain objects.
- **Controllers** (`src/controllers/*`) — **domain model classes** (e.g., `CompanyController`, `CredentialController`, `UserController`) that encapsulate entity state and domain methods. They are NOT request handlers. Managers create and return controller instances.
- **Modules** (`src/modules/*`) — shared utilities, external API clients (ConnectWise, UniFi, Microsoft), credential helpers, permission utilities, and tools.
---
## Runtime / tooling
The project runs on **Bun**. DB tooling uses **Prisma**; the generated client lives under `generated/prisma` (do NOT edit generated files). Key scripts in `package.json`:
- `dev``NODE_ENV=development bun --watch src/index.ts` (start dev server with hot reload)
- `db:gen``prisma generate` - `db:gen``prisma generate`
- `db:push``prisma migrate dev --skip-generate` - `db:push``prisma migrate dev --skip-generate`
- `utils:dev``docker compose -f .docker/docker-compose.yml up --build`
- `utils:gen_private_keys``bun ./utils/genPrivateKeys`
- `utils:create_admin_role``bun ./utils/createAdminRole`
- `utils:assign_user_role``bun ./utils/assignUserRole`
- **Data layer**: Prisma schema is at `prisma/schema.prisma`. The app imports the generated Prisma client from `generated/prisma/client.ts` (or `generated/prisma/browser.ts` in browser contexts). Always run the `db:gen` script after updating `schema.prisma`. ## Data layer
- **Routing & controllers**: Example flow: a request hits a `router` in `src/api/routers/*` → router delegates to a `controller` in `src/controllers/*` → controller calls a `manager` in `src/managers/*` for domain/persistence logic. Important concrete patterns: Prisma schema is at `prisma/schema.prisma`. The app imports the generated Prisma client from `generated/prisma/client.ts` (or `generated/prisma/browser.ts` for browser type contexts). The shared `prisma` instance is exported from `src/constants.ts`. Always run `npm run db:gen` after updating `schema.prisma`.
- `src/api/server.ts` mounts `v1` and uses `v1.route("/auth", require("./routers/authRouter").default)` style requires; preserve this shape when adding routes.
- Router files export a default Hono router object (CommonJS `module.exports`/`export default` mixture is used across the codebase).
- Controllers are single-export modules named like `CompanyController.ts` with named methods per action (e.g., `fetch`, `update`). Prefer small methods that call into `managers/*`.
- Managers are thin domain layers (e.g., `managers/companies.ts`) that wrap `generated/prisma` calls and other I/O. Keep side effects here, keep controllers pure orchestration.
- Use `src/modules/api-utils/apiResponse.ts` for every HTTP response shape (successful/created/error/internalError/zodError).
- Use Zod schemas in controllers for request validation; server-level `app.onError` maps `ZodError` to `apiResponse.zodError`.
**API layout & conventions (how to add a new endpoint)** ## Shared constants (`src/constants.ts`)
- **Add router**: create `src/api/routers/<thing>Router.ts` exporting a Hono router and mount it in `src/api/server.ts` under the `v1` router. This file exports critical shared instances used across the codebase:
- **Add controller**: create `src/controllers/<Thing>Controller.ts` exporting functions for each action. Controllers should validate input with Zod, call managers, and return `apiResponse.*` results.
- **Add manager**: create `src/managers/<things>.ts` for persistence/domain logic. Use the generated Prisma client (`generated/prisma/client.ts`) here; do not import Prisma directly in controllers.
- **Add modules/types**: if needed, add helpers to `src/modules/*` and runtime types to `src/types/*`.
- **Middleware & auth**: use `src/api/middleware/authorization.ts` for protecting routes; follow existing token/session patterns from `src/controllers/SessionController.ts` and `src/Errors/*`.
- **Error handling**: throw repository-specific errors from `src/Errors/*` (include `status`, `name`, `message`, optional `cause`) and let `src/api/server.ts` map them via `apiResponse.error`.
- **Naming**: prefer plural manager filenames (`companies.ts`) and singular controller names (`CompanyController.ts`) — follow existing files.
**Examples & notable files** - `prisma` — the PrismaClient instance (via `@prisma/adapter-pg`)
- `PORT`, session/token durations, private/public keys for JWT signing
- `msalClient` — Microsoft MSAL client for OAuth
- `connectWiseApi` — Axios instance for ConnectWise API
- `unifi``UnifiClient` instance for UniFi controller interaction
---
## Route handler pattern (`createRoute`)
Every route handler file uses the `createRoute()` utility from `src/modules/api-utils/createRoute.ts`. It creates a self-contained Hono sub-app for a single endpoint:
```ts
export default createRoute(
"get", // HTTP method
["/companies"], // path(s)
async (c) => {
/* handler */
}, // request handler
authMiddleware({ permissions: ["company.fetch.many"] }), // middleware (spread)
);
```
Route files live in `src/api/<domain>/*.ts`. Each domain folder has an `index.ts` that re-exports all route modules. Router files (`src/api/routers/<domain>Router.ts`) import from the domain's index and auto-mount all routes:
```ts
import * as companyRoutes from "../companies";
const companyRouter = new Hono();
Object.values(companyRoutes).map((r) => companyRouter.route("/", r));
export default companyRouter;
```
`src/api/server.ts` then mounts each router under `/v1`:
```ts
v1.route("/company", require("./routers/companyRouter").default);
```
## Routing & domain organization
The `server.ts` file mounts these routers under `/v1`:
- `/teapot` — health check
- `/auth` — Microsoft OAuth flow (`src/api/auth/*`)
- `/user` — user routes (`src/api/user/*`) including `@me` sub-routes
- `/company` — company routes (`src/api/companies/*`)
- `/credential` — credential routes (`src/api/credentials/*`)
- `/credential-type` — credential type routes (`src/api/credential-types/*`)
- `/role` — role management (`src/api/roles/*`)
- `/permissions` — permission node queries (`src/api/permissions/*`)
- `/unifi` — UniFi integration (`src/api/unifi/*` with `sites/` and `site/` sub-folders)
---
## API layout & conventions (how to add a new endpoint)
1. **Add route handler files**: Create `src/api/<domain>/<action>.ts` files, each exporting a default `createRoute(...)` call. Validate input with Zod inside the handler, call managers for business logic, and return responses via `apiResponse.*`.
2. **Add domain index**: Create `src/api/<domain>/index.ts` that re-exports all route modules from the folder.
3. **Add router**: Create `src/api/routers/<domain>Router.ts` that imports all routes from the domain's index and mounts them. Mount it in `src/api/server.ts` under `v1`.
4. **Add manager**: Create `src/managers/<domains>.ts` (plural filename) for persistence/domain logic. Use the `prisma` instance from `src/constants.ts`; do not import Prisma directly in route handlers. Managers should instantiate and return controller instances when the domain warrants it.
5. **Add controller** (if needed): Create `src/controllers/<Domain>Controller.ts` (singular filename) as a **class** that encapsulates entity state, domain methods, and a `toJson()` serializer. Controllers are domain model objects — they do NOT handle HTTP requests.
6. **Add modules/types**: If needed, add helpers to `src/modules/*` and runtime types to `src/types/*`.
7. **Middleware & auth**: Use `authMiddleware()` from `src/api/middleware/authorization.ts` as the last argument to `createRoute()`. It accepts `{ permissions?: string[], scopes?: string[], forbiddenAuthTypes?: string[] }`. Follow existing token/session patterns from `src/controllers/SessionController.ts` and `src/Errors/*`.
8. **Error handling**: Throw repository-specific errors from `src/Errors/*` (include `status`, `name`, `message`, optional `cause`) and let `src/api/server.ts` map them via `apiResponse.error`.
9. **Naming conventions**: plural manager filenames (`companies.ts`), singular controller class names (`CompanyController.ts`), descriptive route handler filenames (`fetchAll.ts`, `create.ts`, `update.ts`).
---
## Examples & notable files
- `src/api/server.ts` — mounts `v1`, registers `cors`, central `onError` handler and `notFound` response. - `src/api/server.ts` — mounts `v1`, registers `cors`, central `onError` handler and `notFound` response.
- `src/api/routers/companyRouter.ts` and `src/controllers/CompanyController.ts` — canonical example for adding company endpoints. - `src/api/companies/fetchAll.ts` — canonical example of a route handler file using `createRoute()`, `authMiddleware()`, managers, and `apiResponse`.
- `src/api/user/@me/*` — nested route example (use Hono sub-routers for subpaths). - `src/api/credentials/create.ts` — example of Zod validation inside a route handler.
- `src/modules/cw-utils/*` — external API integrations; keep interfaces stable and return domain objects consumed by managers. - `src/api/routers/companyRouter.ts` — canonical router that auto-mounts all route modules from a domain folder.
- `src/api/user/@me/*` — nested sub-route example (user's own profile endpoints).
- `src/controllers/CompanyController.ts` — example domain model class with methods like `refreshFromCW()`, `fetchCwData()`, and `toJson()`.
- `src/controllers/CredentialController.ts` — example of a richer domain model class with field validation, secure value handling, and sub-credential support.
- `src/managers/companies.ts` — example manager calling Prisma and returning `CompanyController` instances.
- `src/modules/api-utils/createRoute.ts` — the `createRoute()` utility used by every route handler.
- `src/modules/cw-utils/*` — external ConnectWise API integrations; keep interfaces stable and return domain objects consumed by managers.
- `src/modules/unifi-api/UnifiClient.ts` — UniFi controller API client class with methods for sites, WLANs, devices, networks, etc.
- `src/modules/credentials/*` — credential field validation, secure value encryption/decryption, and type definitions.
- `src/constants.ts` — shared instances (`prisma`, API clients, keys, durations).
- **Validation & errors**: Zod is used for input validation; Zod errors are handled centrally in `src/api/server.ts` via `apiResponse.zodError`. Application errors use custom error classes in `src/Errors/*` (e.g., `AuthenticationError.ts`, `GenericError.ts`). When creating errors, follow the shape used in existing errors (include `status`, `name`, `message`, and optional `cause`). ---
- **Response pattern**: Use the `apiResponse` helpers in `src/modules/api-utils/apiResponse.ts` for formatting responses and status codes (successful, created, error, internalError, zodError). ## Validation & errors
- **Auth & external integrations**: Microsoft OAuth flow is under `src/api/auth/*` and `src/modules/fetchMicrosoftUser.ts`. If touching authentication, follow existing redirect/refresh patterns in `src/api/auth` and preserve token-refresh semantics. Zod is used for input validation **inside route handler files** (not controllers). Zod errors are handled centrally in `src/api/server.ts` via `apiResponse.zodError`. Application errors use custom error classes in `src/Errors/*` (e.g., `AuthenticationError.ts`, `GenericError.ts`, `AuthorizationError.ts`, `InsufficientPermission.ts`). When creating errors, follow the shape used in existing errors (include `status`, `name`, `message`, and optional `cause`).
- **ConnectWise integration**: Utilities for ConnectWise interactions live in `src/modules/cw-utils/*` (e.g., `fetchCompanyConfigurations.ts`). These modules often call external APIs and return domain data; preserve the module contracts (input types and returned shapes) when refactoring. ## Response pattern
- **Generated files and CI**: `generated/` is a build artifact. Do not modify. When updating Prisma models, run `npm run db:gen` and commit changes to `generated/prisma` only if that's the established workflow. Use the `apiResponse` helpers in `src/modules/api-utils/apiResponse.ts` for formatting all HTTP responses:
- **Files to inspect for context / examples**: - `apiResponse.successful(message, data?, meta?)` — 200
- `src/api/server.ts` — central error handling, router mounting, CORS and not-found handling. - `apiResponse.created(message, data?)` — 201
- `src/modules/api-utils/apiResponse.ts` — response shaping used across controllers. - `apiResponse.error(err)` — reads `status` from the error
- `src/controllers/CompanyController.ts` — example controller calling managers. - `apiResponse.internalError()` — 500
- `src/modules/cw-utils/fetchCompanyConfigurations.ts` — example external integration utility. - `apiResponse.zodError(err)` — 400
- `generated/prisma/client.ts` — generated Prisma client imports; avoid editing.
- **Coding conventions & patterns specific to this repo**: ## Auth & external integrations
- Prefer the existing layered architecture: routers → controllers → managers → modules.
Microsoft OAuth flow is under `src/api/auth/*` and `src/modules/fetchMicrosoftUser.ts`. If touching authentication, follow existing redirect/refresh patterns in `src/api/auth` and preserve token-refresh semantics.
## ConnectWise integration
Utilities for ConnectWise interactions live in `src/modules/cw-utils/*` (e.g., `configurations/fetchCompanyConfigurations.ts`, `fetchCompany.ts`, `fetchAllCompanies.ts`). These modules call external APIs via the `connectWiseApi` Axios instance from `src/constants.ts` and return domain data; preserve the module contracts (input types and returned shapes) when refactoring.
## UniFi integration
The `UnifiClient` class in `src/modules/unifi-api/UnifiClient.ts` wraps all UniFi controller API interactions (login, sites, WLANs, devices, networks, AP groups, WLAN groups, speed profiles, PPSKs). The shared instance is exported from `src/constants.ts` as `unifi`. UniFi route handlers live in `src/api/unifi/` with `sites/` (multi-site operations) and `site/` (single-site operations) sub-folders.
## Generated files and CI
`generated/` is a build artifact. Do not modify. When updating Prisma models, run `npm run db:gen` and commit changes to `generated/prisma` only if that's the established workflow.
---
## Coding conventions & patterns specific to this repo
- Prefer the existing layered architecture: route handlers → managers → controllers (domain models) / modules.
- Use the `createRoute()` utility for all route handler files.
- Use the `apiResponse` helpers for all HTTP responses. - Use the `apiResponse` helpers for all HTTP responses.
- Throw or propagate repository-specific custom errors (from `src/Errors/*`) rather than returning bare objects. - Throw or propagate repository-specific custom errors (from `src/Errors/*`) rather than returning bare objects.
- Keep TypeScript types in `src/types/*` and use Zod for runtime checks. - Keep TypeScript types in `src/types/*` and use Zod for runtime checks inside route handlers.
- **Avoid `else` statements** — prefer ternary operators, early returns, or other control flow patterns. Only use `else` if there is absolutely no other way. - **Avoid `else` statements** — prefer ternary operators, early returns, or other control flow patterns. Only use `else` if there is absolutely no other way.
- ES module syntax (`export default`, `import`) is used throughout. The `require()` calls in `server.ts` are for lazy loading but all modules use `export default`.
---
## Local dev / quick checks
- **Local dev / quick checks**:
- Start dev server: `npm run dev` - Start dev server: `npm run dev`
- Regenerate Prisma client: `npm run db:gen` - Regenerate Prisma client: `npm run db:gen`
- Apply DB migrations locally: `npm run db:push` - Apply DB migrations locally: `npm run db:push`
- Docker dev utilities: `npm run utils:dev`
- Generate private keys: `npm run utils:gen_private_keys`
- Create admin role: `npm run utils:create_admin_role`
- Assign user role: `npm run utils:assign_user_role`
- **When editing generated or infra files**: if you need to change `generated/prisma/*` (rare), explain why in the PR and show commands used to regenerate. ## When editing generated or infra files
If you need to change `generated/prisma/*` (rare), explain why in the PR and show commands used to regenerate.
---
## Documentation sync (IMPORTANT)
Whenever you add, remove, or modify API routes or permission nodes, you **must** update all three of the following files to keep them in sync:
- **Documentation sync (IMPORTANT)**: Whenever you add, remove, or modify API routes or permission nodes, you **must** update all three of the following files to keep them in sync:
1. `src/types/PermissionNodes.ts` — the single source of truth for all permission node definitions, categories, descriptions, and `usedIn` references. 1. `src/types/PermissionNodes.ts` — the single source of truth for all permission node definitions, categories, descriptions, and `usedIn` references.
2. `PERMISSIONS.md` — human-readable documentation of all permission nodes; must strictly reflect the data in `PermissionNodes.ts`. 2. `PERMISSIONS.md` — human-readable documentation of all permission nodes; must strictly reflect the data in `PermissionNodes.ts`.
3. `API_ROUTES.md` — comprehensive documentation of all API routes, including method, path, auth requirements, permissions, request/response examples. 3. `API_ROUTES.md` — comprehensive documentation of all API routes, including method, path, auth requirements, permissions, request/response examples.
Always verify that new routes have their required permissions listed in `PermissionNodes.ts`, that `PERMISSIONS.md` tables match the TS file exactly, and that `API_ROUTES.md` includes full documentation for every mounted route. Run through all three files at the end of any route or permission change to catch discrepancies. Always verify that new routes have their required permissions listed in `PermissionNodes.ts`, that `PERMISSIONS.md` tables match the TS file exactly, and that `API_ROUTES.md` includes full documentation for every mounted route. Run through all three files at the end of any route or permission change to catch discrepancies.
- **Field-level permission gating (processObjectValuePerms)**: Some routes use `processObjectValuePerms` from `src/modules/permission-utils/processObjectPermissions.ts` to filter response objects on a per-field basis. When this pattern is used, every key of the response object becomes a permission node in the form `<scope>.<field>` (e.g., `unifi.site.wifi.read.passphrase`). Only fields whose corresponding permission the user holds are included in the response. ---
## Field-level permission gating (`processObjectValuePerms`)
Some routes use `processObjectValuePerms` from `src/modules/permission-utils/processObjectPermissions.ts` to filter response objects on a per-field basis. When this pattern is used, every key of the response object becomes a permission node in the form `<scope>.<field>` (e.g., `unifi.site.wifi.read.passphrase`). Only fields whose corresponding permission the user holds are included in the response.
There is also `processObjectPermMap` in the same file, which returns a `Record<key, boolean>` indicating which fields the user has permission for (useful for UI gating).
**When documenting a route that uses field-level gating, you must:** **When documenting a route that uses field-level gating, you must:**
1. Note in `API_ROUTES.md` that the route uses field-level gating, explain the behaviour, and list every `<scope>.<field>` permission node in a collapsible table. 1. Note in `API_ROUTES.md` that the route uses field-level gating, explain the behaviour, and list every `<scope>.<field>` permission node in a collapsible table.
2. Add a `unifi.site.wifi.read`-style parent permission node in `PermissionNodes.ts` with a `fieldLevelPermissions` array listing every `<scope>.<field>` node. 2. Add a `unifi.site.wifi.read`-style parent permission node in `PermissionNodes.ts` with a `fieldLevelPermissions` array listing every `<scope>.<field>` node.
3. Add matching rows/notes to `PERMISSIONS.md` including the full list of field-level nodes. 3. Add matching rows/notes to `PERMISSIONS.md` including the full list of field-level nodes.
**Current routes using field-level gating:** **Current routes using field-level gating:**
- `GET /v1/unifi/site/:id/wifi` — scope `unifi.site.wifi.read`, gates every field on the `WlanConf` object. - `GET /v1/unifi/site/:id/wifi` — scope `unifi.site.wifi.read`, gates every field on the `WlanConf` object.
If anything here is unclear or you'd like more examples (e.g., a walk-through editing a controller + manager + test run), tell me which area to expand and I'll iterate. ---
If anything here is unclear or you'd like more examples (e.g., a walk-through adding a route handler + manager + controller), tell me which area to expand and I'll iterate.
+63
View File
@@ -0,0 +1,63 @@
name: Build and Publish
on:
release:
types: [created]
jobs:
build:
name: Build
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push the Docker image
uses: docker/build-push-action@v6
with:
push: true
tags: |
ghcr.io/project-optima/ttscm-api:latest
ghcr.io/project-optima/ttscm-api:${{ github.event.release.tag_name }}
deploy:
name: Deploy
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Set the Kubernetes context
uses: azure/k8s-set-context@v2
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
- name: Checkout source code
uses: actions/checkout@v4
- name: Lint Kubernetes manifests
uses: azure/k8s-lint@v1
with:
lintType: dryrun
manifests: |
kubernetes/deployment.yaml
namespace: optima
- name: Deploy to the Kubernetes cluster
uses: azure/k8s-deploy@v5
with:
namespace: optima
force: true
skip-tls-verify: true
manifests: |
kubernetes/deployment.yaml
images: |
ghcr.io/project-optima/ttscm-api:${{ github.event.release.tag_name }}
+2 -2
View File
@@ -1,6 +1,6 @@
# TTSCM API Routes Documentation # Optima API Routes Documentation
This document provides a comprehensive overview of all API routes available in the TTSCM API. This document provides a comprehensive overview of all API routes available in the Optima API.
## Base URL ## Base URL
+57
View File
@@ -0,0 +1,57 @@
# ---- Stage 1: Install dependencies ----
FROM oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production
# ---- Stage 2: Build ----
FROM oven/bun:1 AS build
WORKDIR /app
# Copy dependency manifests and install all deps (including dev)
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
# Copy source code and supporting files
COPY src/ src/
COPY generated/ generated/
COPY prisma/ prisma/
COPY public-keys/ public-keys/
COPY prisma.config.ts tsconfig.json ./
# Compile to a standalone executable
RUN bun build src/index.ts \
--compile \
--minify \
--target=bun-linux-x64 \
--outfile=server
# ---- Stage 3: Production image ----
FROM ubuntu:22.04 AS runtime
WORKDIR /app
# Install minimal runtime dependencies (CA certs for HTTPS calls)
RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates && \
rm -rf /var/lib/apt/lists/*
# Copy the compiled binary from the build stage
COPY --from=build /app/server ./server
# Copy Prisma artifacts needed at runtime
COPY --from=build /app/generated/ ./generated/
COPY --from=build /app/prisma/ ./prisma/
COPY --from=build /app/prisma.config.ts ./prisma.config.ts
COPY --from=build /app/public-keys/ ./public-keys/
# Copy production node_modules (Prisma adapter needs native bindings)
COPY --from=deps /app/node_modules/ ./node_modules/
ENV NODE_ENV=production
EXPOSE 3000
CMD ["./server"]
+1 -1
View File
@@ -1,6 +1,6 @@
# Permission Nodes # Permission Nodes
This document lists all known permission nodes in the ttscm-api application, categorized by resource type. This document lists all known permission nodes in the optima-api application, categorized by resource type.
## Permission System Overview ## Permission System Overview
+9
View File
@@ -0,0 +1,9 @@
{
"version": "1",
"name": "optima",
"type": "collection",
"ignore": [
"node_modules",
".git"
]
}
+2 -5
View File
@@ -1,9 +1,6 @@
{ {
"version": "1", "version": "1",
"name": "ttscm", "name": "optima",
"type": "collection", "type": "collection",
"ignore": [ "ignore": ["node_modules", ".git"]
"node_modules",
".git"
]
} }
+26
View File
@@ -0,0 +1,26 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: optima-api
namespace: optima
spec:
selector:
matchLabels:
app: optima-api
replicas: 1
template:
metadata:
labels:
app: optima-api
spec:
containers:
- name: optima-api
image: ghcr.io/project-optima/ttscm-api:latest
imagePullPolicy: Always
envFrom:
- secretRef:
name: optima-api-env
ports:
- containerPort: 3000
imagePullSecrets:
- name: github-container-registry
+36
View File
@@ -0,0 +1,36 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: optima-api-ingress
namespace: optima
annotations:
ingress.kubernetes.io/ssl-redirect: "false"
spec:
tls:
- secretName: osdci-net-cert
rules:
- host: opt-api.osdci.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: optima-api
port:
number: 3000
---
apiVersion: v1
kind: Service
metadata:
name: optima-api
namespace: optima
labels:
app: optima-api
spec:
type: ClusterIP
ports:
- port: 3000
protocol: TCP
selector:
app: optima-api
+1
View File
@@ -1,6 +1,7 @@
{ {
"name": "tts-optima-api", "name": "tts-optima-api",
"homepage": "https://totaltech.net", "homepage": "https://totaltech.net",
"version": "v0.1.0",
"author": { "author": {
"name": "Jackson Roberts", "name": "Jackson Roberts",
"email": "jackson.roberts@totaltech.net", "email": "jackson.roberts@totaltech.net",
+2 -2
View File
@@ -38,7 +38,7 @@ const toBase64 = (str: string) => Buffer.from(str).toString("base64");
const secretYaml = `apiVersion: v1 const secretYaml = `apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
name: ttscm-keys name: optima-keys
type: Opaque type: Opaque
data: data:
accessToken.key: ${toBase64(generatedKeys["accessToken"].private)} accessToken.key: ${toBase64(generatedKeys["accessToken"].private)}
@@ -48,7 +48,7 @@ data:
secureValues.pub: ${toBase64(generatedKeys["secureValues"].public)} secureValues.pub: ${toBase64(generatedKeys["secureValues"].public)}
`; `;
const secretPath = `${outputDir}/ttscm-keys-secret.yaml`; const secretPath = `${outputDir}/optima-keys-secret.yaml`;
await Bun.write(secretPath, secretYaml); await Bun.write(secretPath, secretYaml);
console.log(`\n ✔ ${secretPath}`); console.log(`\n ✔ ${secretPath}`);