Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
a0126d9
feat(profile): add job-aware resume optimization advice
claude Oct 27, 2026
65bd7cf
feat(utils): add Vercel handler adapter and serverless env defaults
claude Oct 27, 2026
561818c
fix(profile,jobs,applications,outreach): export a Vercel request handler
claude Oct 27, 2026
92c2a2d
fix(matching,referrals,learning,analytics): export a Vercel request h…
claude Oct 27, 2026
5ff6816
ci(db): add Supabase stand-in and test database setup script
claude Oct 27, 2026
fcd6252
ci: run DB-backed integration tests against a Postgres service
claude Oct 27, 2026
1ced4a7
ci: fail when the committed api/ bundles are stale
claude Oct 27, 2026
7d1e3f1
ci: add Dependabot and a non-blocking dependency audit
claude Oct 27, 2026
c149c9d
docs(progress): record that CI now runs the DB-backed suites (pending…
claude Oct 27, 2026
6916121
fix(auth): use shared Vercel handler and default PORT on Vercel
claude Oct 27, 2026
bacbb38
chore(api): regenerate committed bundles for serverless entrypoints
claude Oct 27, 2026
59b081c
Merge stream E: CI runs DB-backed tests, bundle freshness, dependabot
claude Oct 27, 2026
fbbf394
feat(db): lock down the public schema against the Supabase Data API
claude Oct 27, 2026
a460741
ci: apply rls.sql when preparing test databases
claude Oct 27, 2026
b05b588
test(db): prove rls.sql locks anon/authenticated out of public
claude Oct 27, 2026
f1f0e06
feat(types): add admin-service API contracts
claude Oct 27, 2026
05e1c4e
feat(admin): add CSV reader/writer and roster row validation
claude Oct 27, 2026
db27b3f
feat(admin): add admin-service for university roster management
claude Oct 27, 2026
cc87def
feat(db): add durable event outbox, consumer registry and delivery ta…
claude Oct 27, 2026
d46b91e
feat(messaging): postgres broker driver backed by a durable outbox
claude Oct 27, 2026
25574b6
feat(services): pass the database handle to createBroker in all services
claude Oct 27, 2026
2f765ef
feat(services): authenticated /internal/drain for the event consumers
claude Oct 27, 2026
2305aff
test(messaging): cross-service events end to end over the postgres dr…
claude Oct 27, 2026
5230387
Merge stream G: admin-service backend for university pilot
claude Oct 27, 2026
2b8dc4a
chore: register admin-service in bundle, vercel.json, dev scripts
claude Oct 27, 2026
f7bbb65
Merge stream F: durable Postgres event pipeline
claude Oct 27, 2026
567b0fa
feat: wire the durable event pipeline for serverless
claude Oct 27, 2026
e55e8a2
fix(deps): bump next to 16.4.1 in web and admin
claude Oct 27, 2026
1a4d14d
fix(deps): refresh source-map-js to 1.2.2 in the lockfile
claude Oct 27, 2026
9aac8af
fix(deps): bump zod to 4.6.6 across all workspace packages
claude Oct 27, 2026
af45af7
Merge stream N: patch vulnerable dependencies (next, sharp, zod, sour…
claude Oct 27, 2026
59b1e65
chore(deps): align admin-service on zod 4.6.6, repair lockfile, rebundle
claude Oct 27, 2026
38a0ec4
feat(applications): return a job summary to the application owner
claude Oct 27, 2026
abec2b3
feat(applications): let the owner record an assisted apply on the emp…
claude Oct 27, 2026
d998145
feat(security): platform-admin allowlist, signup-metadata org escalat…
claude Oct 27, 2026
aed0406
Merge stream B: security launch blockers (platform admin, signup esca…
claude Oct 27, 2026
48b5d10
fix(applications): reject mode "auto" until a submission worker exists
claude Oct 27, 2026
2a946fc
feat(observability): add registerRateLimitedRoutes so rate limits att…
claude Oct 27, 2026
05f91f1
fix(services): make rate limiting actually enforce in every service
claude Oct 27, 2026
f34f197
docs: record that rate limits now enforce per instance; shared store …
claude Oct 27, 2026
2b3f93f
test: align rls and cross-service suites with the platform-admin / no…
claude Oct 27, 2026
2ab18ba
Merge stream K: applications job context, assisted-apply marker, gate…
claude Oct 27, 2026
6790725
Merge stream I: rate limiting actually enforces in all services
claude Oct 27, 2026
2a0738a
feat(messaging): batch publish for the postgres broker
claude Oct 27, 2026
671c3bc
fix(matching): idempotent, batched job.scored fan-out
claude Oct 27, 2026
ea0c5c4
fix(referrals): derive referral.detected ids per contact and application
claude Oct 27, 2026
66e5263
fix(learning): one aggregate query and derived ids for learning.updated
claude Oct 27, 2026
a2a9310
test(analytics): pin idempotency under concurrent redelivery
claude Oct 27, 2026
ac6b092
test: redeliver the event consumers end to end over the postgres driver
claude Oct 27, 2026
9487907
chore(build): rebundle backend functions after merging security, appl…
claude Oct 27, 2026
043a7fe
feat(web): truthful marketing, email-confirm signup, password reset, …
claude Oct 27, 2026
2035bae
Merge stream C: truthful web app and sound auth journey
claude Oct 27, 2026
3d75ecf
Merge stream H: consumers safe under at-least-once delivery
claude Oct 27, 2026
91cb4c8
chore(build): rebundle backend functions after merging idempotent con…
claude Oct 27, 2026
a50755f
feat(storage): Supabase Storage driver for durable resume files
claude Oct 27, 2026
ae2910e
fix(referrals): only platform admins may write to the shared contacts…
claude Oct 27, 2026
809e492
ci: run apps/*/test in the root vitest suite; drain workflow supports…
claude Oct 27, 2026
9470ac6
test: run apps/*/test in the root vitest config
claude Oct 27, 2026
b843be1
test: run apps/*/test in the root vitest config
claude Oct 27, 2026
281dd67
test: run apps/*/test under the root vitest config
claude Oct 27, 2026
376d773
docs(ops): launch checklist for owner actions and a post-deploy smoke…
claude Oct 27, 2026
db78ed7
test: run apps/*/test in the root vitest config
claude Oct 27, 2026
077337f
feat(web): pure helpers and service clients for the job-to-apply journey
claude Oct 27, 2026
338e220
feat(types): account export and deletion contracts
claude Oct 27, 2026
ac4fe19
feat(db): add job_sources table for scheduled job-board ingestion
claude Oct 27, 2026
2574944
feat(outreach): stop claiming delivery; add owner-scoped mark-sent
claude Oct 27, 2026
951f0dc
fix(api): serialize timestamps as strict ISO 8601 in profile, applica…
claude Oct 27, 2026
4a0ca68
test(outreach): pin the claim itself under simultaneous callers
claude Oct 27, 2026
76b5546
feat(auth): account data export and deletion endpoints
claude Oct 27, 2026
a13a0f5
feat(web): job detail page with resume check and assisted apply
claude Oct 27, 2026
7951ea3
test(messaging): run the cross-service suite on its own scratch datab…
claude Oct 27, 2026
ac14182
docs(ops): record that twelve Vercel projects build every push
claude Oct 27, 2026
41d7c50
feat(web): messages are drafts you send yourself
claude Oct 27, 2026
d8cdac4
refactor(jobs): extract per-posting dedupe/insert/publish into ingest…
claude Oct 27, 2026
643d4d5
feat(jobs): Greenhouse, Lever and Ashby mappers and a guarded fetch l…
claude Oct 27, 2026
8821d43
fix(auth): allow DELETE through CORS, keep events from outweighing th…
claude Oct 27, 2026
f877281
test(auth): account export and deletion
claude Oct 27, 2026
cd70b8d
docs(api): document the outreach endpoints and that Atlas does not de…
claude Oct 27, 2026
fcf5c3f
feat(web): 'Your data' page to download or delete the account
claude Oct 27, 2026
92e13cd
Merge stream Q: job detail, resume check and assisted-apply UI
claude Oct 27, 2026
081996a
docs(security): data export and deletion; env example for the privacy…
claude Oct 27, 2026
10f1eb0
test(outreach): type the mark-sent helper's inject options
claude Oct 27, 2026
78b1876
feat(applications): filter the own application list by job_id
claude Oct 27, 2026
884a2e1
Merge stream R: outreach honesty (no fake sends) and drafts-only mess…
claude Oct 27, 2026
f615cd3
feat(jobs): scheduled job-board ingestion and platform-admin source m…
claude Oct 27, 2026
6ed7efb
feat(admin): typed admin-service client, error mapping and tested CSV…
claude Oct 27, 2026
7e360ef
feat(admin): sign-in guard, organization chooser, and platform-wide d…
claude Oct 27, 2026
a617bb5
feat(admin): student roster page with search, filters, cursor paging …
claude Oct 27, 2026
4035aec
feat(admin): roster CSV import with pre-flight checks, per-row errors…
claude Oct 27, 2026
e876902
feat(admin): student funnel dashboard and an honest audit-log page
claude Oct 27, 2026
e7c8166
chore: ISO timestamps for outreach; rebundle after merging apply flow…
claude Oct 27, 2026
c659d5f
feat(deploy): hourly ingest-jobs workflow, jobs rewrite, seed example…
claude Oct 27, 2026
f65608c
Merge stream T: university staff console in apps/admin
claude Oct 27, 2026
51b7593
docs(security): say what to verify on a staging Supabase project afte…
claude Oct 27, 2026
c231c1b
Merge stream U: account data export and deletion
claude Oct 27, 2026
5ce339b
chore(auth): normalise response timestamps to ISO after merging accou…
claude Oct 27, 2026
170304b
chore(web): drop the Connectors placeholder page from the dashboard
claude Oct 27, 2026
4b6383c
fix(deploy): route /v1/companies to the referrals function
claude Oct 27, 2026
bb888aa
chore(build): rebundle backend functions after merging account export…
claude Oct 27, 2026
ab3e46c
fix(jobs): stop recommending deactivated postings; pin ingestPosting …
claude Oct 27, 2026
4dcd057
Merge stream P: scheduled job-board ingestion (Greenhouse, Lever, Ashby)
claude Oct 27, 2026
8c072d0
feat(jobs): name the employer on job list, detail and recommendations
claude Oct 27, 2026
06ff072
docs: refresh progress report and launch checklist; smoke-check the i…
claude Oct 27, 2026
02e25b9
Merge main into the launch-prep branch
claude Oct 27, 2026
43c5972
docs: record the first live smoke result; word the ingest-route warni…
claude Oct 27, 2026
27c8465
docs: record the owner's apply-model decision in the launch checklist
claude Oct 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
61 changes: 58 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,37 @@ REDIS_URL=redis://localhost:6379
ATLAS_TEST_DATABASE_URL=postgres://atlas:atlas@localhost:5432/atlas_test

# --- Messaging --------------------------------------------------------------
# driver: memory | rabbitmq
# driver: memory | rabbitmq | postgres
# memory - in-process, for tests and a single local process (default)
# postgres - durable outbox in the shared database; the only driver that
# delivers events across Vercel functions. Consumers (matching,
# referrals, learning, analytics) only run when their
# /internal/drain route is hit, so CRON_SECRET must be set there.
MESSAGING_DRIVER=memory
RABBITMQ_URL=amqp://atlas:atlas@localhost:5672
# Bearer secret for POST|GET /internal/drain on the four consumer services and
# for POST|GET /internal/jobs/ingest-sources on the jobs service (a scheduler
# sends it as `Authorization: Bearer $CRON_SECRET`). At least 16 characters,
# e.g. `openssl rand -hex 32`. Unset, those routes answer 503.
# CRON_SECRET=
# Optional, jobs service: an email address or URL where the operator of a job
# board can reach you, appended to the User-Agent the job-board reader sends
# (printable ASCII only). The reader is read-only and uses no API keys.
# JOB_SOURCE_CONTACT=

# --- Storage ----------------------------------------------------------------
# driver: local | s3
# driver: local | supabase (s3 is reserved, not implemented)
# local = files on disk (development only; ephemeral on Vercel).
# supabase = a PRIVATE Supabase Storage bucket (production): create the bucket in the
# dashboard, then set the two values below on the services that touch resume files: the
# profile service (uploads) and the auth service (deletes a user's files when they delete
# their account). On Vercel, where every service shares one project's environment, that is
# automatic.
STORAGE_DRIVER=local
STORAGE_LOCAL_ROOT=./.atlas-storage
SUPABASE_STORAGE_BUCKET=resumes
# Server-side secret key (sb_secret_... or legacy service_role). Bypasses RLS: never commit it.
SUPABASE_SECRET_KEY=
S3_BUCKET=
S3_REGION=us-east-1
S3_ENDPOINT=
Expand All @@ -44,6 +67,22 @@ SUPABASE_ACCESS_TOKEN=
# GitHub: https://github.com/settings/developers
# Each app's authorized redirect URI is https://<project-ref>.supabase.co/auth/v1/callback.

# Platform administrators: comma-separated auth.users ids allowed to touch GLOBAL
# data (jobs-service POST /v1/jobs/ingest, referrals-service POST /v1/contacts,
# learning-service GET /v1/learning/metrics).
# Deliberately separate from org admin/owner roles, which only carry authority
# inside one organization. Empty or unset = nobody (fail closed). Read by
# jobs-service, referrals-service and learning-service only.
PLATFORM_ADMIN_USER_IDS=

# --- Account privacy (auth service) -------------------------------------------
# Per-client-address budgets for GET /v1/auth/me/export and DELETE /v1/auth/me. They replace
# (do not add to) the general RATE_LIMIT_* budget on those two routes. Counters live in each
# function instance's memory. Raise them if many people share one address (a campus network).
# PRIVACY_EXPORT_RATE_LIMIT_MAX=10
# PRIVACY_DELETE_RATE_LIMIT_MAX=3
# PRIVACY_RATE_LIMIT_WINDOW=1 hour

# --- Service ports ----------------------------------------------------------
AUTH_SERVICE_PORT=4001
PROFILE_SERVICE_PORT=4002
Expand All @@ -55,10 +94,26 @@ OUTREACH_SERVICE_PORT=4007
LEARNING_SERVICE_PORT=4008
ANALYTICS_SERVICE_PORT=4009
CONNECTORS_SERVICE_PORT=4010
ADMIN_SERVICE_PORT=4011

# connectors-service secret store: local (dev-only, plaintext on disk) | kms (not implemented yet)
SECRET_STORE_DRIVER=local
SECRET_STORE_LOCAL_ROOT=./.atlas-secrets

# outreach-service transport: local (logs only, safe default) | smtp (not implemented yet)
# outreach-service transport: local (logs only, dev/test) | smtp (not implemented yet)
OUTREACH_TRANSPORT_DRIVER=local

# outreach-service: let Atlas itself "send" (POST /v1/messages/:id/send) and schedule messages.
# Keep false. No transport delivers anything yet (local only logs, smtp is not implemented), so
# true would mark messages "sent" and tell users they went out when nothing was delivered. When
# false, send and schedule are rejected with 503 before anything is written; users copy or open
# the draft in their own mail app and record it with POST /v1/messages/:id/mark-sent.
# Must be exactly a boolean word (true/false/1/0/yes/no/on/off): a typo fails at boot. Even when
# true, nothing dispatches scheduled messages (no worker), and NODE_ENV=production refuses true
# together with OUTREACH_TRANSPORT_DRIVER=local. Set true only for local dev or tests.
OUTREACH_DELIVERY_ENABLED=false

# applications-service: accept mode "auto" on POST /v1/applications. Keep false until a submission
# worker consumes queued applications — otherwise auto applications sit in "queued" forever.
# When false, mode "auto" is rejected with 400 VALIDATION_ERROR; "manual" and "review" are unaffected.
APPLICATIONS_AUTO_MODE_ENABLED=false
49 changes: 49 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Automated dependency update PRs. Kept deliberately quiet: one weekly pass,
# minor/patch bumps batched into a single PR per group, and a low cap on open
# PRs so a backlog cannot bury review of real work.
#
# Major-version bumps are NOT grouped: each opens its own PR so its breaking
# changes get reviewed on their own (and so one risky major cannot hold up a
# batch of safe patches).
#
# Every Dependabot PR runs the normal CI gate (typecheck, lint, tests including
# the DB-backed suites, bundle freshness). A dependency bump that changes a
# service's bundled dependencies will fail the "api/ bundles are up to date"
# check until `node scripts/bundle-backend.mjs` is re-run and committed.

version: 2

updates:
# pnpm workspace: Dependabot reads pnpm-lock.yaml and pnpm-workspace.yaml at
# the root and updates every package in the workspace from this one entry.
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
time: '06:00'
timezone: Etc/UTC
open-pull-requests-limit: 5
commit-message:
prefix: 'chore(deps)'
groups:
production-dependencies:
dependency-type: production
update-types: [minor, patch]
development-dependencies:
dependency-type: development
update-types: [minor, patch]

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
time: '06:00'
timezone: Etc/UTC
open-pull-requests-limit: 3
commit-message:
prefix: 'chore(ci)'
groups:
github-actions:
patterns: ['*']
93 changes: 89 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,46 @@ concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

# Nothing here writes to the repo or to PRs; checkout is the only token use.
permissions:
contents: read

jobs:
backend:
name: Backend (typecheck, lint, test)
runs-on: ubuntu-latest
timeout-minutes: 20

env:
# Setting this is what turns the DB-backed integration suites on: they
# self-skip (and still report green) when it is unset. The credentials
# are for the throwaway service container below, which only exists for
# the duration of this job and is not reachable from outside it, so
# they are not secrets.
ATLAS_TEST_DATABASE_URL: postgres://atlas:atlas@localhost:5432/atlas_test

services:
# Same image as docker-compose.yml (pgvector bundled; pg_trgm and
# pgcrypto ship with Postgres contrib). The user is the container's
# superuser, which migrate.ts needs for CREATE EXTENSION.
postgres:
image: pgvector/pgvector:pg17
env:
POSTGRES_USER: atlas
POSTGRES_PASSWORD: atlas
POSTGRES_DB: atlas_test
ports:
- 5432:5432
# -h 127.0.0.1 forces a TCP check. During first start the image runs a
# temporary server that listens only on a unix socket; a socket check
# would report healthy too early and the next step would get
# "connection refused" while the real server restarts.
options: >-
--health-cmd "pg_isready -h 127.0.0.1 -U atlas -d atlas_test"
--health-interval 5s
--health-timeout 5s
--health-retries 20

steps:
- uses: actions/checkout@v4

Expand All @@ -41,12 +77,39 @@ jobs:
- name: Lint (whole repo)
run: pnpm lint

# DB-backed integration suites self-skip without ATLAS_TEST_DATABASE_URL
# (see .env.example) — no Postgres service is wired up in this job yet,
# so those suites report as skipped here, not passing.
- name: Test (backend, DB-backed suites skipped)
# Supabase is not available here, so this applies a minimal stand-in
# (roles + auth.users; infra/ci/supabase-stub.sql), then the real
# migrations, then packages/db/supabase/auth-hooks.sql. The same script
# works locally: see its header.
- name: Prepare test database (Supabase stand-in, migrations, auth hooks)
env:
DATABASE_URL: ${{ env.ATLAS_TEST_DATABASE_URL }}
run: bash scripts/ci/setup-test-db.sh

- name: Test (backend, including DB-backed integration suites)
run: pnpm test

# api/*/index.js are committed esbuild bundles (see the header of
# scripts/bundle-backend.mjs): Vercel deploys what is in git, not what a
# build step would produce. Rebuild here and fail if the result differs,
# so a source or packages/* change cannot ship with stale bundles.
- name: Check committed api/ bundles are up to date
run: |
node scripts/bundle-backend.mjs
# Mark brand-new bundles (untracked) as intent-to-add so git diff
# sees them too, not just modified ones.
git add --intent-to-add --all -- api
if ! git diff --exit-code --stat -- api; then
echo "::error title=Stale api/ bundles::The committed api/*/index.js bundles do not match the current sources."
echo ""
echo "The committed Vercel bundles are out of date with the code in this change."
echo "Fix: run node scripts/bundle-backend.mjs locally, then commit the updated api/ files."
echo ""
echo "First 200 lines of the difference:"
git --no-pager diff -- api | head -n 200
exit 1
fi

frontend:
name: Frontend (typecheck, build)
runs-on: ubuntu-latest
Expand Down Expand Up @@ -83,3 +146,25 @@ jobs:
NEXT_PUBLIC_SUPABASE_URL: https://qkqllkfgxsmhjintkqfm.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: sb_publishable_DGb1kAEeNl_w2Tbk9gT2rA_M75FcXhv
run: pnpm --filter ${{ matrix.app }} run build

# Advisory only. Failing the build on every high-severity advisory in a
# transitive dependency would block unrelated changes (and launch) on
# upstream fixes we cannot ship ourselves, so this reports without gating.
# Read the log, and treat a finding in a direct dependency as a to-do.
# Reads only the lockfile, so no install is needed and it runs in parallel.
audit:
name: Dependency audit (non-blocking)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 22

- name: Audit production dependencies (high and above)
continue-on-error: true
run: pnpm audit --prod --audit-level=high
69 changes: 69 additions & 0 deletions .github/workflows/drain-events.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Drain events

# Cross-service events live in a Postgres outbox (MESSAGING_DRIVER=postgres) and
# are only processed when a consumer service's drain endpoint is called — a
# serverless function has no long-lived worker to poll for them. Vercel Cron on a
# Hobby plan runs at most once a day, so this scheduled workflow is the trigger:
# every 5 minutes it calls POST /internal/<service>/drain on the four consumers
# (matching, referrals, learning, analytics) with the shared CRON_SECRET.
#
# Inert until configured. Set, in GitHub -> Settings -> Secrets and variables:
# variable ATLAS_API_BASE_URL e.g. https://atlas-backend.example.vercel.app (no trailing slash)
# -- when every service is behind ONE Vercel project (root vercel.json)
# variable ATLAS_<SERVICE>_URL ATLAS_MATCHING_URL, ATLAS_REFERRALS_URL, ATLAS_LEARNING_URL,
# ATLAS_ANALYTICS_URL -- when each service is its OWN Vercel project;
# overrides ATLAS_API_BASE_URL for that service
# secret CRON_SECRET the same value set as CRON_SECRET on the Vercel project(s)
# Every service answers /internal/<service>/drain itself, so both layouts use the same path.
#
# Caveats: GitHub only runs `schedule` on the default branch, may delay runs under
# load, and disables schedules after 60 days without repository activity. If the
# project moves to Vercel Pro, replace this with `crons` entries in vercel.json.

on:
schedule:
- cron: '*/5 * * * *'
workflow_dispatch:

# A slow drain must not pile up behind the next tick.
concurrency:
group: drain-events
cancel-in-progress: false

permissions: {}

jobs:
drain:
name: Drain ${{ matrix.service }}
runs-on: ubuntu-latest
timeout-minutes: 3
strategy:
fail-fast: false
matrix:
include:
- service: matching
url: ${{ vars.ATLAS_MATCHING_URL }}
- service: referrals
url: ${{ vars.ATLAS_REFERRALS_URL }}
- service: learning
url: ${{ vars.ATLAS_LEARNING_URL }}
- service: analytics
url: ${{ vars.ATLAS_ANALYTICS_URL }}
steps:
- name: Call the drain endpoint
env:
ATLAS_API_BASE_URL: ${{ matrix.url != '' && matrix.url || vars.ATLAS_API_BASE_URL }}
CRON_SECRET: ${{ secrets.CRON_SECRET }}
SERVICE: ${{ matrix.service }}
run: |
if [ -z "${ATLAS_API_BASE_URL}" ] || [ -z "${CRON_SECRET}" ]; then
echo "::notice title=Drain skipped::Set ATLAS_API_BASE_URL (or ATLAS_<SERVICE>_URL) and the CRON_SECRET secret to enable event draining."
exit 0
fi
# --fail-with-body makes a 401/503/5xx fail the job (visible in Actions)
# while still printing the JSON counts the endpoint returns.
curl --fail-with-body --silent --show-error --max-time 60 \
--request POST \
--header "Authorization: Bearer ${CRON_SECRET}" \
"${ATLAS_API_BASE_URL%/}/internal/${SERVICE}/drain"
echo
Loading
Loading