diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 347614dd..dde8cca3 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -6,7 +6,7 @@ ## Overview -**Botsmann** is a modern AI bot platform featuring intelligent assistants for legal, medical, research, and language learning. Built with Next.js 16 and React 19. +**Botsmann** is a modern AI bot platform featuring intelligent assistants for legal, medical, research, and language learning. Built with Next.js 16 and React 19, self-hosted on the Hetzner box "bitbaum" behind Caddy (botsmann.orangecat.ch). --- @@ -19,7 +19,7 @@ | Language | TypeScript | | Styling | Tailwind CSS | | Testing | Jest | -| Deployment | Vercel | +| Deployment | Self-hosted (Hetzner + Caddy) | --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..de955826 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: CI + +# Botsmann is deployed self-hosted on the Hetzner box "bitbaum" behind Caddy. +# Deployment runs from the local `.husky/pre-push` hook (build -> rsync -> systemd +# restart) when main is pushed. This workflow only validates build/lint/tests. + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build-and-test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + - name: Install dependencies and build + run: npm run build:ci + env: + NEXT_PUBLIC_DEPLOY_TIME: ${{ github.event.head_commit.timestamp }} + + - name: Run format check + run: npm run format:check --if-present + + - name: Run linting + run: npm run lint --if-present + + - name: Run tests + run: npm run test --if-present diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 21a7ffe9..00000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Deploy to Vercel - -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - cache: 'npm' - - - name: Install dependencies and build - run: npm run build:ci - env: - NEXT_PUBLIC_DEPLOY_TIME: ${{ github.event.head_commit.timestamp }} - - - name: Run format check - run: npm run format:check --if-present - - - name: Run linting - run: npm run lint --if-present - - - name: Run tests - run: npm run test --if-present - - - name: Install Vercel CLI - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - run: npm install -g vercel@latest - - - name: Deploy to Vercel (Production) - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - run: vercel deploy --prod --archive=tgz --yes --token=${{ secrets.VERCEL_TOKEN }} - env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} - - - name: Deploy to Vercel (Preview) - if: github.event_name == 'pull_request' - run: vercel deploy --archive=tgz --yes --token=${{ secrets.VERCEL_TOKEN }} - env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} diff --git a/.gitignore b/.gitignore index 930bd50e..545928a2 100644 --- a/.gitignore +++ b/.gitignore @@ -26,9 +26,6 @@ Thumbs.db logs/ *.log -# Vercel -.vercel - # Runtime state / cache (bot runtime artifacts) .call_count .circuit_breaker_history diff --git a/README.md b/README.md index 339f88ec..b97adcb2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Domain-specialized AI professionals, not another generic chatbot wrapper. [](LICENSE) [](https://www.typescriptlang.org/) [](https://nextjs.org/) -[](https://botsmann.vercel.app) +[](https://botsmann.orangecat.ch) --- diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index e355acba..9cd70d0f 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -25,8 +25,7 @@ import { searchUserDocuments, } from '@/lib/chat'; -// Extend function timeout for model loading (Vercel) -// Note: Free tier max is 10s, Pro tier allows up to 60s +// Extend function timeout for model loading (Next.js route segment config) export const maxDuration = 30; /** diff --git a/app/api/custom-bots/[id]/chat/route.ts b/app/api/custom-bots/[id]/chat/route.ts index ff567beb..2509a0bd 100644 --- a/app/api/custom-bots/[id]/chat/route.ts +++ b/app/api/custom-bots/[id]/chat/route.ts @@ -34,7 +34,7 @@ import { parseResponseWithSuggestions, } from '@/lib/chat'; -// Extend function timeout for model loading (Vercel) +// Extend function timeout for model loading (Next.js route segment config) export const maxDuration = 30; const ChatRequestSchema = z.object({ diff --git a/app/api/demo/document-chat/route.ts b/app/api/demo/document-chat/route.ts index 9eb6e5ac..7e264739 100644 --- a/app/api/demo/document-chat/route.ts +++ b/app/api/demo/document-chat/route.ts @@ -15,7 +15,7 @@ import { getClientIp } from '@/lib/request'; import { sanitizeUserMessage, sanitizePromptContent } from '@/lib/prompt-sanitizer'; import { logger } from '@/lib/logger'; -// Extend function timeout for model loading (Vercel) +// Extend function timeout for model loading (Next.js route segment config) export const maxDuration = 30; // Maximum document content size (characters) to prevent abuse diff --git a/app/api/documents/process/route.ts b/app/api/documents/process/route.ts index 3a5a6bb1..b785ec8f 100644 --- a/app/api/documents/process/route.ts +++ b/app/api/documents/process/route.ts @@ -28,7 +28,7 @@ import { DOMAIN_ERRORS } from '@/lib/constants'; import { detectDomains } from '@/lib/context/domain-detector'; import { logger } from '@/lib/logger'; -// Extend function timeout for embedding generation (Vercel) +// Extend function timeout for embedding generation (Next.js route segment config) export const maxDuration = 60; // Allow longer for document processing /** diff --git a/app/api/documents/search/route.ts b/app/api/documents/search/route.ts index 2b747144..fad79a3e 100644 --- a/app/api/documents/search/route.ts +++ b/app/api/documents/search/route.ts @@ -14,7 +14,7 @@ import { jsonSuccess, jsonError, jsonUnauthorized, handleError, HTTP_STATUS } fr import { DOMAIN_ERRORS } from '@/lib/constants'; import { logger } from '@/lib/logger'; -// Extend function timeout for embedding generation (Vercel) +// Extend function timeout for embedding generation (Next.js route segment config) export const maxDuration = 30; /** diff --git a/app/api/professional-chat/route.ts b/app/api/professional-chat/route.ts index e0873515..9c953fcf 100644 --- a/app/api/professional-chat/route.ts +++ b/app/api/professional-chat/route.ts @@ -26,7 +26,7 @@ import { import { getUserLLMSettings, searchUserDocuments } from '@/lib/chat'; import { getRelevantContext, extractAndSaveContext } from '@/lib/context'; -// Extend function timeout for model loading (Vercel) +// Extend function timeout for model loading (Next.js route segment config) export const maxDuration = 30; const MAX_CONTEXT_CHARS = 4000; // Leave room for system prompt + response diff --git a/app/api/quick-chat/route.ts b/app/api/quick-chat/route.ts index 350601b2..493f4a9b 100644 --- a/app/api/quick-chat/route.ts +++ b/app/api/quick-chat/route.ts @@ -20,7 +20,7 @@ import { wrapUserContext, } from '@/lib/prompt-sanitizer'; -// Extend function timeout for model loading (Vercel) +// Extend function timeout for model loading (Next.js route segment config) export const maxDuration = 30; export async function POST(request: NextRequest) { diff --git a/app/api/warmup/route.ts b/app/api/warmup/route.ts index fb287733..84c0b75b 100644 --- a/app/api/warmup/route.ts +++ b/app/api/warmup/route.ts @@ -3,7 +3,7 @@ * * GET /api/warmup - Pre-loads the embedding model to avoid cold start delays * - * Called periodically by Vercel cron to keep the function warm + * Called periodically by a cron/uptime check to keep the embedding model warm */ import { NextResponse } from 'next/server'; diff --git a/app/demo/page.tsx b/app/demo/page.tsx index 88993671..a7079029 100644 --- a/app/demo/page.tsx +++ b/app/demo/page.tsx @@ -256,7 +256,7 @@ const DemoPage: FC = () => { • Backend: Next.js API routes