Skip to main content

stacks-server

Use when working with the Stacks development or production server - server configuration, server middleware, or server startup. Covers @stacksjs/server and storage/framework/server/.

Informations de source

Dépôt
stacksjs/stacks
Dernière activité de la source
17 octobre 2026 à 16:13
Langue détectée de SKILL.md
anglais
Étoiles
633
Forks
20

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
stacks-server
description
Use when working with the Stacks development or production server - server configuration, server middleware, or server startup. Covers @stacksjs/server and storage/framework/server/.
license
MIT
compatibility
Bun >= 1.3.0, TypeScript
allowed-tools
Read Edit Write Bash Grep Glob
# Stacks Server ## Key Paths - Core package: `storage/framework/core/server/src/` (published as `@stacksjs/server`) - Server runtime: `storage/framework/server/` (the actual Bun HTTP server + Docker build) - Server types: `storage/framework/core/types/src/server.ts` - Ports types: `storage/framework/core/types/src/ports.ts` - Package (core): `storage/framework/core/server/package.json` - Package (runtime): `storage/framework/server/package.json` ## Source Files ``` core/server/src/ ├── index.ts # Re-exports: config, controllers/base, imports, maintenance ├── config.ts # config() factory - maps ServerOptions.type to host/port ├── config-production.ts # Minimal env-based config for compiled production binaries ├── imports.ts # Auto-import system: scan models/jobs/controllers, register bun plugin ├── maintenance.ts # Laravel-like maintenance mode (down/up/bypass) └── controllers/ └── base.ts # Base Controller class with json/success/error helpers server/ ├── src/ │ ├── index.ts # Bun.serve() HTTP entry and one-shot queue worker │ └── utils.ts # Docker build helpers (cleanCopy, buildDockerImage, useCustomOrDefaultServerConfig) ├── build.ts # Full build pipeline: bundle server + app, post-process, optionally build Docker ├── dev # Shell script for local/remote Docker dev (mounts volumes) ├── Dockerfile # Multi-stage Bun Docker image (oven/bun:1.3.10) ├── package.json # stacks-server package ├── tsconfig.json # Extends core tsconfig ├── tsconfig.docker.json # Docker-specific tsconfig with path aliases ├── .dockerignore # Excludes Dockerfile, .git, node_modules, etc. └── .gitignore # Excludes app/, config/, dist/, storage/ (build artifacts) ``` ## Server Entry Point (server/src/index.ts) The HTTP runtime delegates fetch requests to serverResponse from `@stacksjs/router/runtime`. It has no WebSocket upgrade handlers. Realtime clients connect to the separate authenticated broadcast server described by `stacks-realtime`. PORT defaults to 3000; APP_ENV production/prod disables development mode. Production enables reusePort for replacement instances on supported platforms. SIGTERM stops accepting connections and drains in-flight requests, with a SHUTDOWN_GRACE_MS deadline (default fifteen seconds). Supervisors still own restart and deploy health-gating policy. With QUEUE_WORKER set, this entry instead loads the sanitized JOB file and runs its handle with retry. JOB_RETRIES, JOB_BACKOFF_FACTOR, JOB_INITIAL_DELAY and JOB_JITTER configure that one-shot mode. It is distinct from the durable `buddy queue:work` processor. ## Server Config (core/server/src/config.ts) ```typescript function config(options: ServerOptions): { host: string, port: number, open: boolean } ``` Maps a `ServerOptions.type` to a host/port pair using the `ports` config object. Supported types: | Type | Port Source | |---------------|---------------------| | `frontend` | `ports.frontend` | | `backend` | `ports.backend` | | `api` | `ports.api` | | `admin` | `ports.admin` | | `library` | `ports.library` | | `desktop` | `ports.desktop` | | `docs` | `ports.docs` | | `email` | `ports.email` | | `inspect` | `ports.inspect` | | `system-tray` | `ports.systemTray` | | `database` | `ports.database` | All types resolve to `host: 'localhost'`. If no type is provided or it doesn't match, falls back to `host: options.host || 'stacks.localhost'` and `port: options.port || 3000`. ### ServerOptions Type ```typescript interface ServerOptions { type?: 'frontend' | 'backend' | 'api' | 'library' | 'desktop' | 'docs' | 'email' | 'admin' | 'system-tray' | 'database' host?: string port?: number open?: boolean } ``` ### Ports Interface ```typescript interface Ports { frontend: number backend: number // proxies api admin: number library: number desktop: number email: number docs: number inspect: number api: number // the bun server systemTray: number database: number // i.e. DynamoDB local } ``` ## Production Config (core/server/src/config-production.ts) Minimal config for compiled binaries -- no runtime file loading, all env-based: ```typescript const config = { app: { name: process.env.APP_NAME || 'Stacks', env: process.env.APP_ENV || 'production', debug: process.env.APP_DEBUG === 'true' || false, url: process.env.APP_URL || 'https://stacksjs.com', }, server: { port: Number(process.env.PORT) || 3000, host: '0.0.0.0', }, logging: { level: process.env.LOG_LEVEL || 'info', }, } ``` Used by `start.ts` (the production binary entry point) with `SKIP_CONFIG_LOADING=true` to bypass dynamic config imports. ## Production Start (core/server/src/start.ts) The compiled-binary entry marks binary mode and disables external runtime config loading. It reads appRouteRegistry at runtime, loads manual routes first, then tries `@stacksjs/orm/routes` with a checkout-relative fallback. Missing ORM routes warn; unresolved registered route middleware aborts startup rather than serving an unguarded route. Bun receives `hostname`, not a host option. Unhandled rejections reach the shared report hook; uncaught exceptions report, flush logging and exit so a supervisor can restart. This entry's startup contract differs from the Docker HTTP entry and the STX production views server; inspect the selected build/deploy entrypoint before copying environment assumptions. ## Auto-Imports System (core/server/src/imports.ts) ### initiateImports() ```typescript function initiateImports(): void ``` Registers a Bun bundler plugin (`bun-plugin-auto-imports`) that makes models, jobs, controllers, and resource functions available globally without explicit imports. Scan order (user overrides framework overrides defaults): - **Models**: `app/Models/` > `storage/framework/defaults/app/Models/` > `storage/framework/defaults/app/Models/` - **Jobs**: `app/Jobs/` - **Controllers**: `app/Controllers/` > `storage/framework/defaults/app/Controllers/` - **Functions**: `resources/functions/` Outputs: - `.d.ts` file: `storage/framework/types/server-auto-imports.d.ts` - Compatibility manifest: storage/framework/server-auto-imports.json; lint remains pickier - Runtime index files: `storage/framework/auto-imports/` (see below) ### generateAutoImportFiles() ```typescript async function generateAutoImportFiles(): Promise<void> ``` Package-based apps address framework defaults through `@stacksjs/defaults` exports, preserving application overrides. Generated imports never depend on the physical npm or Pantry installation path. An app created with `--with-core` keeps source-relative defaults imports; detection uses the framework source layout, independently of the application's package name. Generates runtime-importable index files under `storage/framework/auto-imports/`: - `functions.ts` -- re-exports from `resources/functions/` - `models.ts` -- re-exports default exports from model definition files as named exports - `jobs.ts` -- re-exports default exports from job definition files - `controllers.ts` -- re-exports default exports from controller definition files - `index.ts` -- combined re-export of all above - `globals.ts` -- script that assigns all exports to `globalThis` ### injectGlobalAutoImports() ```typescript async function injectGlobalAutoImports(): Promise<void> ``` Dynamically imports `storage/framework/auto-imports/index.ts` and assigns all exports to `globalThis`. Call this early in application startup to make models available globally (e.g., `Post.where('title', 'test')` without imports). ### scanDefineModelExports(dir: string): ExportInfo[] Internal helper that scans a directory for `.ts` files (excluding `.d.ts`, `index.ts`, `README*`) and returns file names as export info. Used for models, jobs, and controllers that use `export default defineModel(...)` pattern. ## Base Controller (core/server/src/controllers/base.ts) ```typescript class Controller { protected json(data: unknown, status?: ResponseStatus): Response // default status: 200 protected success(data: unknown): Response // alias for json(data, 200) protected created(data: unknown): Response // json(data, 201) protected noContent(): any // response.noContent() protected error(message: string, status?: ResponseStatus): Response // json({ error }, 500) protected notFound(message?: string): Response // error(msg, 404) protected unauthorized(message?: string): Response // error(msg, 401) protected forbidden(message?: string): Response // error(msg, 403) protected validate(request: Request, rules: Record<string, unknown>): Promise<void> } ``` Usage: ```typescript import { Controller } from '@stacksjs/server' class UserController extends Controller { async index() { const users = await User.all() return this.success(users) } async store(request: Request) { await this.validate(request, { name: 'required', email: 'required|email' }) const user = await User.create(request.body) return this.created(user) } async show(id: number) { const user = await User.find(id) if (!user) return this.notFound('User not found') return this.success(user) } } ``` ## Maintenance Mode (core/server/src/maintenance.ts) Laravel-like maintenance mode that writes/removes a `storage/framework/down` file. ### Core Functions ```typescript async function down(options?: Partial<MaintenancePayload>): Promise<void> async function up(): Promise<void> async function isDownForMaintenance(): Promise<boolean> async function maintenancePayload(): Promise<MaintenancePayload | null> ``` ### MaintenancePayload ```typescript interface MaintenancePayload { time: number // When maintenance was activated (Date.now()) message?: string // Display message (default: 'We are currently performing maintenance...') retry?: number // Retry-After header value in seconds secret?: string // Bypass token allowed?: string[] // Allowed IP addresses status?: number // HTTP status (default: 503) template?: string // Custom template path redirect?: string // Redirect URL instead of showing maintenance page } ``` ### Bypass Helpers ```typescript function isAllowedIp(ip: string, allowed?: string[]): boolean // Localhost IPs ('127.0.0.1', '::1', 'localhost') are always allowed function hasValidBypassCookie(cookies: Record<string, string>, secret: string): boolean // Checks for cookie: stacks_maintenance_bypass=<secret> function isSecretPath(path: string, secret: string): boolean // Matches /<secret> or /<secret>/... function bypassCookieValue(secret: string): string // Returns: 'stacks_maintenance_bypass=<secret>; Path=/; HttpOnly; SameSite=Lax' ``` ### Response Helpers ```typescript function maintenanceHtml(payload: MaintenancePayload): string // Returns a styled HTML page with gradient background function maintenanceResponse(payload: MaintenancePayload): Response // Returns Response with correct status, Retry-After header, or 302 redirect ``` ### Usage ```typescript import { down, up, isDownForMaintenance } from '@stacksjs/server' // Put app in maintenance mode with bypass secret await down({ secret: 'my-bypass-token', retry: 300 }) // Check if in maintenance if (await isDownForMaintenance()) { const payload = await maintenancePayload() // ... } // Bring app back up await up() ``` ## Sitemap and robots.txt (core/actions/src/seo.ts) The views server answers `/sitemap.xml` and `/robots.txt` for every app, generated from `resources/views` at request time. `buddy dev` (`core/actions/src/dev/views.ts`) and `buddy serve` (`core/buddy/src/production-server.ts`) call the same `createSiteSeoHandler()` at the same point in `onRequest` (after redirects and rewrites), so dev and production answer identically. No route, no build step, no app code. Do not add `route.get('/sitemap.xml')` to `routes/`: the views server does not forward root GETs to the API. - **Listed**: every static route under `resources/views`, with `lastmod` from the file, plus the blog's published posts (`content/blog`, or a CMS-built `dist/blog/sitemap.xml`) merged into the same file. `/blog/sitemap.xml` is still served too. - **Not listed**: dynamic routes (`[slug].stx`), error and holding pages (`404`, `errors/*`, `coming-soon`), `components/`, `layouts/`, `partials/`, `emails/`, `_`-prefixed files, pages behind `definePageMeta({ middleware: ['auth'] })`, paths in `seo.sitemap.exclude`, paths robots.txt disallows, and any page that says **noindex**. The framework's default views (`/login`, ...) are never listed. - **noindex**, the one signal, read from the page source: `<meta name="robots" content="noindex">`, `robots: 'noindex'` in a head object, or `const noindex = true` in the server script (the flag a shared head partial turns into the tag - the scan cannot follow an `@include`, so spell it that way). `definePageMeta({ sitemap: false })` also keeps a page out. - **robots.txt**: `User-agent: *`, `Allow: /`, `Disallow: /api/` plus `seo.robots.disallow`, extra groups from `seo.robots.rules`, and `Sitemap: <origin>/sitemap.xml`. Never disallow a page just to hide it: a crawler that cannot fetch it never sees its noindex.
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub