Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2d12884
deps(deps): update dependency hono to v4.12.27 [security] (#345)
renovate[bot] Jul 27, 2026
53aade2
fix(deps): update dependency @hono/node-server to v2 [security] (#390)
renovate[bot] Jul 28, 2026
86768e9
chore(deps): update dependency vite to v7.3.5 [security] (#344)
renovate[bot] Jul 28, 2026
6e3b185
Merge branch 'main' into chore/update-dependencies
malte-b Jul 28, 2026
a5ae2ca
feat: overwrite renovate settings
malte-b Jul 28, 2026
d8c7a5c
fix: vulnerabilities
malte-b Jul 29, 2026
b3443f9
fix: pre-existing linting issue
malte-b Jul 29, 2026
59b9d98
fix: update some deprecated deps
malte-b Jul 29, 2026
0aa5773
chore: update to vite 8 for performance gains
malte-b Jul 29, 2026
ebc1ffd
fix: remove remaining deprecation warnings
malte-b Jul 29, 2026
efce178
chore: small vite optimizations
malte-b Jul 29, 2026
0e3b944
fix: prettier
malte-b Jul 29, 2026
70d8e2a
fix: update supabase cli version in asdf
malte-b Jul 29, 2026
005af46
fix: review comments
malte-b Jul 29, 2026
aee6f4e
fix: add allow-scripts
malte-b Jul 29, 2026
a000a68
fix: uses supabase anon key in new action version
malte-b Jul 29, 2026
f2cf0dd
fix: GH actions errors
malte-b Jul 30, 2026
42654b3
fix: prettier
malte-b Jul 30, 2026
8af076c
fix: switch route for health check and add more logs
malte-b Jul 30, 2026
a1943ac
fix: revert back to actual table
malte-b Jul 30, 2026
d034397
chore: remove unused env var
malte-b Jul 30, 2026
5fd4cc6
fix: quickfix for permisions, would need new migration in the future
malte-b Jul 30, 2026
b65c353
fix: pin to supabase 2.71 instead bc later versions treat auth differ…
malte-b Jul 31, 2026
9d0dc2d
fix: suggestions
malte-b Jul 31, 2026
1fc112f
fix: count seconds not iterations
malte-b Jul 31, 2026
7bbb181
fix: replace Inbucket with Mailpit according to updated Supabase version
malte-b Jul 31, 2026
5a44be2
chore: upgrade turbo
malte-b Jul 31, 2026
e32537e
chore: update playwright and tsx
malte-b Jul 31, 2026
9169ad9
chore: add cleanup functions
malte-b Jul 31, 2026
754e015
fix: weird race condition
malte-b Jul 31, 2026
71cb9ac
Update apps/frontend/tests/fixtures/test-with-registered-user.ts
malte-b Jul 31, 2026
1ddedaa
Merge branch 'staging' into chore/update-dependencies
malte-b Jul 31, 2026
eb67f5e
fix: prettier
malte-b Jul 31, 2026
1f4a0cb
chore: update npm version
malte-b Jul 31, 2026
58248ce
feat: add cutoff time for test
malte-b Aug 4, 2026
6eaf5a4
feat: remove cleanup at start of tests
malte-b Aug 4, 2026
1f636b3
Merge branch 'staging' into chore/update-dependencies
malte-b Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,29 @@ runs:
- name: Start supabase ⚡️
if: inputs.start-supabase == 'true'
shell: bash
run: cd apps/backend && supabase start --exclude studio,realtime,imgproxy,edge-runtime,logflare,supavisor,postgres-meta &
working-directory: apps/backend
run: |
# Run supabase start in the background with nohup so it survives the
# GitHub Actions step closing its stdout/stderr. Redirect output to a log
# file so we can inspect it if the wait action times out.
nohup supabase start --exclude studio,realtime,imgproxy,edge-runtime,supavisor,postgres-meta > /tmp/supabase-start.log 2>&1 &
echo $! > /tmp/supabase-start.pid

# Fail fast if the process died immediately (e.g. config error). If it is already
# gone we cannot tell success from failure by existence alone, so reap it with `wait`
# to read the real exit status: a clean exit just means the stack was already up.
sleep 2
SUPABASE_PID="$(cat /tmp/supabase-start.pid)"
if ! kill -0 "$SUPABASE_PID" 2>/dev/null; then
if wait "$SUPABASE_PID"; then
echo "supabase start already finished successfully:"
cat /tmp/supabase-start.log
else
echo "supabase start exited immediately with status $?:"
cat /tmp/supabase-start.log
exit 1
fi
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
malte-b marked this conversation as resolved.

- name: Setup Node.js using .nvmrc ⚙️
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0, pinned to commit sha to avoid sha-hulud like attacks
Expand Down
97 changes: 89 additions & 8 deletions .github/actions/wait-for-supabase/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Wait for Supabase to be ready
description: Polls until the Supabase REST API and PostgreSQL are both accepting connections
description: Polls until the Supabase REST API and PostgreSQL are both accepting connections, then reloads GoTrue's custom email templates

runs:
using: composite
Expand All @@ -8,18 +8,99 @@ runs:
shell: bash
run: |
timeout=600
counter=0
deadline=$((SECONDS + timeout))

ANON_KEY="${SUPABASE_ANON_KEY:-}"
if [ -z "$ANON_KEY" ]; then
ANON_KEY=$(grep -h '^SUPABASE_ANON_KEY=' apps/backend/.env "$RUNNER_TEMP/backend.env" 2>/dev/null | cut -d'=' -f2- | tr -d '"' | tr -d "'" | head -n1 || true)
fi

HEADER_ARG=()
if [ -n "$ANON_KEY" ]; then
HEADER_ARG=(-H "apikey: $ANON_KEY")
fi

# Sometimes the postgrest api and postgres are up, but individual postgrest endpoints are not ready yet.
# Therefore we check if a table (e.g. `application_admins`) is available in the postgrest api.
# Therefore we check that a real table (e.g. `application_admins`) is queryable, not just the
# PostgREST root/OpenAPI endpoint — the root answers even when table privileges are missing,
# which lets a broken stack look healthy here and only fail several steps later.

until curl -sf http://localhost:54321/rest/v1/application_admins && pg_isready -U postgres -h localhost -p 54322; do
if [ $counter -gt $timeout ]; then
echo "Timed out waiting for Supabase"
until \
HTTP_STATUS=$(curl -s --connect-timeout 5 --max-time 10 -o /tmp/supabase-health.body -w "%{http_code}" "${HEADER_ARG[@]}" http://localhost:54321/rest/v1/application_admins) && \
[ "$HTTP_STATUS" = "200" ] && \
pg_isready -U postgres -h localhost -p 54322 >/tmp/pg_isready.out 2>&1; do
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if [ "$SECONDS" -ge "$deadline" ]; then
echo "Timed out waiting for Supabase after ${SECONDS}s (limit ${timeout}s)"
echo "=== PostgREST health check (http://localhost:54321/rest/v1/application_admins) ==="
echo "HTTP status: ${HTTP_STATUS:-<failed>}"
if [ -f /tmp/supabase-health.body ]; then
echo "Response body (first 500 chars):"
head -c 500 /tmp/supabase-health.body || true
echo
fi
echo "=== PostgreSQL readiness (pg_isready -U postgres -h localhost -p 54322) ==="
cat /tmp/pg_isready.out 2>/dev/null || echo "pg_isready output unavailable"
if command -v docker >/dev/null 2>&1; then
echo "=== Docker status (running) ==="
docker ps
echo "=== Docker status (all) ==="
docker ps -a
fi
if [ -f /tmp/supabase-start.log ]; then
echo "=== Supabase start log ==="
cat /tmp/supabase-start.log
echo "=== End Supabase start log ==="
fi
exit 1
fi
echo "Waiting for Supabase to be ready..."
echo "Waiting for Supabase to be ready... (${SECONDS}s/${timeout}s, POSTGREST status: ${HTTP_STATUS:-<failed>})"
sleep 1
counter=$((counter + 1))
done
echo "Supabase is ready!"

# GoTrue reads the custom email templates configured under [auth.email.template.*] over HTTP
# from a static file server that Kong exposes on port 8088. It fetches them once, when its
# template cache worker boots, and never retries on failure. Because `supabase start` brings
# auth and kong up in parallel, auth regularly wins the race, gets ECONNREFUSED on :8088, and
# then silently serves GoTrue's built-in English templates for the rest of its lifetime. Those
# defaults carry no `{{ .Token }}` on a line of its own, so every test that reads a security
# code out of a mail fails with a confusing "could not read a security code" error.
#
# The readiness loop above already proves Kong is routing (it answers on :54321), so restarting
# auth here makes the second template fetch succeed deterministically. Costs a few seconds.
- name: Reload GoTrue email templates 📧
shell: bash
run: |
AUTH_CONTAINER="$(docker ps --filter "name=supabase_auth_" --format '{{.Names}}' | head -n1)"
if [ -z "$AUTH_CONTAINER" ]; then
echo "No supabase_auth_* container found — cannot reload email templates."
docker ps -a
exit 1
fi

echo "Restarting $AUTH_CONTAINER so it re-fetches the custom email templates from Kong..."
docker restart "$AUTH_CONTAINER" >/dev/null
RESTARTED_AT="$(docker inspect -f '{{.State.StartedAt}}' "$AUTH_CONTAINER")"

timeout=60
deadline=$((SECONDS + timeout))
until curl -s --connect-timeout 5 --max-time 10 -o /dev/null -f http://localhost:54321/auth/v1/health; do
if [ "$SECONDS" -ge "$deadline" ]; then
echo "Timed out waiting for GoTrue to come back after ${SECONDS}s (limit ${timeout}s)"
docker logs --since "$RESTARTED_AT" "$AUTH_CONTAINER" 2>&1 | tail -50
exit 1
fi
echo "Waiting for GoTrue to be ready again... (${SECONDS}s/${timeout}s)"
sleep 1
done

# Assert the reload actually worked. Without this the run would carry on with default
# templates and fail much later, in a test whose error says nothing about email templates.
if docker logs --since "$RESTARTED_AT" "$AUTH_CONTAINER" 2>&1 \
| grep -q "templatemailer_template_body_http_error"; then
echo "GoTrue still could not fetch its custom email templates from Kong:"
docker logs --since "$RESTARTED_AT" "$AUTH_CONTAINER" 2>&1 \
| grep "templatemailer_template_body_http_error"
exit 1
fi
echo "GoTrue reloaded its custom email templates!"
Comment thread
malte-b marked this conversation as resolved.
21 changes: 19 additions & 2 deletions .github/renovate.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>technologiestiftung/renovate-config"]
}
"extends": ["github>technologiestiftung/renovate-config"],
"baseBranches": ["staging"],

"vulnerabilityAlerts": {
"enabled": true,
"minimumReleaseAge": null,
"schedule": ["at any time"],
"labels": ["security"],
"automerge": true
},

"packageRules": [
{
"matchUpdateTypes": ["patch", "minor"],
"automerge": true
}
]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
supabase-cli 2.40.7
supabase-cli 2.71.0
terraform 1.15.7
4 changes: 2 additions & 2 deletions README_DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ This starts local Supabase services:

- **API URL**: http://localhost:54321
- **Studio (Database UI)**: http://localhost:54323
- **Inbucket (Email Testing)**: http://localhost:54324
- **Mailpit (Email Testing)**: http://localhost:54324

⚠️ **Save the output** - you'll need the `anon key`, `service_role key`, and `JWT secret`.

Expand Down Expand Up @@ -410,7 +410,7 @@ supabase status # Check if running
```

**Email Confirmation in Development**
View test emails at: http://localhost:54324 (Inbucket)
View test emails at: http://localhost:54324 (Mailpit)

**TypeScript Errors After Schema Changes**

Expand Down
24 changes: 12 additions & 12 deletions apps/admin-panel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,33 +33,33 @@
"clsx": "2.1.1",
"date-fns": "4.1.0",
"lucide-react": "0.525.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-router-dom": "6.30.3",
"recharts": "2.15.4",
"react": "19.2.8",
"react-dom": "19.2.8",
"react-router": "8.3.0",
"recharts": "3.1.2",
"slugify": "1.6.6",
"tailwind-merge": "3.3.1",
"tailwindcss-animate": "1.0.7",
"tw-animate-css": "1.4.0",
"zustand": "4.5.5"
"zustand": "5.0.14"
},
"devDependencies": {
"@axe-core/playwright": "4.10.2",
"@playwright/test": "1.55.1",
"@playwright/test": "1.62.1",
"@repo/typescript-config": "file:../../libs/typescript-config",
"@types/dotenv": "6.1.1",
"@types/node": "22.14.0",
"@types/react": "18.3.10",
"@types/react-dom": "18.3.0",
"@vitejs/plugin-react": "5.0.3",
"@types/react": "19.2.3",
"@types/react-dom": "19.2.3",
"@vitejs/plugin-react": "6.0.4",
"autoprefixer": "10.4.20",
"dotenv": "16.4.7",
"globals": "15.10.0",
"postcss": "8.5.18",
"shadcn": "^4.0.5",
"shadcn": "4.16.0",
"tailwindcss": "3.4.13",
"typescript": "5.8.3",
"vite": "7.3.2",
"vitest": "3.2.6"
"vite": "8.1.5",
"vitest": "4.1.10"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from "react";
import { useLocation, Link } from "react-router-dom";
import { useLocation, Link } from "react-router";
import {
Sidebar,
SidebarContent,
Expand Down
2 changes: 1 addition & 1 deletion apps/admin-panel/src/hooks/use-session-redirect.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect } from "react";
import { useAuthStore } from "../store/use-auth-store.ts";
import { useLocation, useNavigate } from "react-router-dom";
import { useLocation, useNavigate } from "react-router";
import type { Session } from "@supabase/supabase-js";
import { useAuthErrorStore } from "@/store/use-auth-error-store.ts";
import { useUserStore } from "@/store/use-user-store.ts";
Expand Down
2 changes: 1 addition & 1 deletion apps/admin-panel/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import ReactDOM from "react-dom/client";

import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { createBrowserRouter, RouterProvider } from "react-router";
import "./index.css";
import { IndexPage } from "./routes";
import { BaseKnowledgePage } from "./routes/base-knowledge/index";
Expand Down
6 changes: 6 additions & 0 deletions apps/admin-panel/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,10 @@ export default defineConfig(({ mode }) => ({
},
},
server: mode === "development" ? { port: 5174 } : undefined,
build: {
// Vite 8 defaults to Lightning CSS for CSS minification. The project uses
// Tailwind CSS 3.4.13 with its PostCSS-based pipeline and tailwind.config.js,
// so keep esbuild minification to avoid downstream build issues.
cssMinify: "esbuild",
},
Comment thread
malte-b marked this conversation as resolved.
}));
4 changes: 2 additions & 2 deletions apps/backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ It allows for using the `mistral-small` LLM via Mistral API.
- After running `supabase start`, several local services will be available:
- **API**: http://localhost:54321
- **Supabase Studio**: http://localhost:54323
- **Inbucket (Email Testing Server)**: http://localhost:54324
- When registering a new user in development, confirmation and password reset emails are sent to Inbucket instead of a real email address. Open [http://localhost:54324](http://localhost:54324) in your browser to view and access these emails, including registration confirmation links.
- **Mailpit (Email Testing Server)**: http://localhost:54324
- When registering a new user in development, confirmation and password reset emails are sent to Mailpit instead of a real email address. Open [http://localhost:54324](http://localhost:54324) in your browser to view and access these emails, including registration confirmation links. Mailpit also exposes a JSON API on the same port (`/api/v1/messages`), which the e2e tests use to read the confirmation code.
- Manually enable _Database Publications_: Go to http://localhost:54323/project/default/database/publications -> on _Source_, enable the following tables:
- `document_folders`
- `documents`
Expand Down
17 changes: 9 additions & 8 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@
"@ai-sdk/mcp": "2.0.15",
"@ai-sdk/mistral": "4.0.13",
"@dqbd/tiktoken": "1.0.17",
"@hono/node-server": "1.19.13",
"@hono/node-server": "2.0.12",
"@langfuse/client": "4.2.0",
"@langfuse/core": "4.2.0",
"@langfuse/otel": "4.2.0",
"@langfuse/tracing": "4.2.0",
"@mistralai/mistralai": "1.15.1",
"@opentelemetry/auto-instrumentations-node": "0.75.0",
"@opentelemetry/sdk-node": "0.218.0",
"@opentelemetry/sdk-trace-node": "2.1.0",
"@opentelemetry/instrumentation-http": "0.221.0",
"@opentelemetry/instrumentation-undici": "0.31.0",
"@opentelemetry/sdk-node": "0.221.0",
"@opentelemetry/sdk-trace-node": "2.10.0",
"@repo/db-schema": "file:../../libs/db-schema",
"@sentry/node": "10.38.0",
"@sentry/node": "10.68.0",
"@supabase/supabase-js": "2.81.1",
"ai": "7.0.32",
"dotenv": "16.4.7",
Expand All @@ -63,10 +64,10 @@
"date-fns": "4.1.0",
"globals": "15.15.0",
"nodemon": "3.1.4",
"supertest": "7.1.0",
"tsx": "4.19.3",
"supertest": "7.1.3",
"tsx": "4.23.1",
"typescript": "5.8.3",
"vitest": "3.2.6"
"vitest": "4.1.10"
},
"overrides": {
"zod": "4.4.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ const { uploadMock, ocrProcessMock, deleteMock } = vi.hoisted(() => ({
}));

vi.mock("@mistralai/mistralai", () => ({
Mistral: vi.fn().mockImplementation(() => ({
files: {
upload: uploadMock,
delete: deleteMock,
},
ocr: {
process: ocrProcessMock,
},
})),
// Vitest 4 requires constructor mocks to use `function` or `class` so `new Mistral()` works.
Mistral: vi.fn().mockImplementation(function () {
return {
files: {
upload: uploadMock,
delete: deleteMock,
},
ocr: {
process: ocrProcessMock,
},
};
}),
}));

vi.mock("../monitoring/capture-error", () => ({
Expand Down
5 changes: 3 additions & 2 deletions apps/backend/src/monitoring/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
import { LangfuseSpanProcessor, ShouldExportSpan } from "@langfuse/otel";
import * as Sentry from "@sentry/node";
import { SentryPropagator, SentrySampler } from "@sentry/opentelemetry";
Expand All @@ -26,7 +27,7 @@ const sdk = new NodeSDK({
sampler: sentryClient ? new SentrySampler(sentryClient) : undefined,
contextManager: new Sentry.SentryContextManager(),
textMapPropagator: new SentryPropagator(),
instrumentations: [getNodeAutoInstrumentations()],
instrumentations: [new HttpInstrumentation(), new UndiciInstrumentation()],
});

sdk.start();
Expand Down
4 changes: 3 additions & 1 deletion apps/backend/supabase/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ policy = "oneshot"
inspector_port = 8083

[analytics]
enabled = true
# Disabled so the logflare/vector containers are not started. We never read local analytics in CI or
# local dev, and the vector container has a history of coming up unhealthy on newer CLI versions.
enabled = false
port = 54327
# Configure one of the supported backends: `postgres`, `bigquery`.
backend = "postgres"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ DROP FUNCTION IF EXISTS public.handle_document_deletion ();

DROP FUNCTION IF EXISTS public.delete_document_and_update_count (BIGINT);

-- Bypass the storage.protect_delete() trigger that blocks direct DELETEs
-- from storage.objects. This cleanup removes orphaned objects before the
-- pure-SQL deletion path is removed entirely.
SET
LOCAL storage.allow_delete_query = 'true';

-- Delete all currently orphaned storage objects
DELETE FROM storage.objects
WHERE
Expand All @@ -26,3 +32,5 @@ WHERE
WHERE
source_type = 'public_document'
);

RESET storage.allow_delete_query;
Loading