Skip to content

Netlify launch prep: scheduled cron function + deploy checklist - #13

Merged
Bonobo791 merged 12 commits into
mainfrom
launch-netlify
Jul 30, 2026
Merged

Netlify launch prep: scheduled cron function + deploy checklist#13
Bonobo791 merged 12 commits into
mainfrom
launch-netlify

Conversation

@Bonobo791

Copy link
Copy Markdown
Owner

What

Gets the repo launch-ready for Netlify (no Netlify-side setup in this PR):

  • netlify/functions/cron.mjs — Netlify Scheduled Function on */15 * * * * calling GET $URL/api/cron?secret=$CRON_SECRET (URL is Netlify's own site-URL variable). This matches the trigger design in EXECUTION_PLAN ("a Netlify Scheduled Function ... hitting the deployed URL"); each invocation processes one channel, so 15 minutes is the per-channel cadence. Missing env or a non-OK endpoint response throws, so failures are loud in the Netlify function logs.
  • netlify/functions/cron.test.mjs — 5 vitest cases written test-first: schedule, endpoint URL construction, missing CRON_SECRET/URL loud failures, non-OK endpoint propagation.
  • DEPLOY.md — one-time launch checklist: Turso migrate, Netlify env vars table, Google OAuth production redirect URI + consent screen, cron verification, post-launch dry-run → live sequence.
  • .env.example — production notes (APP_URL = deployed URL, DRY_RUN semantics).
  • AGENTS.md — documents netlify/functions/ and DEPLOY.md in Project Structure.

Verification

  • npm run test 77/77 (72 existing + 5 new), npm run check 0 errors, npm run build green.
  • Build settings were already in netlify.toml (build command, publish build, Node 24); adapter-netlify already configured in svelte.config.js.

@cla-bot cla-bot Bot added the cla-signed label Jul 30, 2026
@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Incremental review completed e864ff2 Jul 30, 2026 · 22:22 22:26
✅ Reviewed your PR 2ae419d Jul 30, 2026 · 17:35 17:37

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Bonobo791, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 12 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3583594b-e396-4881-b503-2991440ee504

📥 Commits

Reviewing files that changed from the base of the PR and between 4c27337 and 4155c91.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • DEPLOY.md
  • netlify/functions/cron.mjs
  • netlify/functions/cron.test.mjs
  • package.json
  • src/routes/api/cron/+server.ts
  • src/routes/api/cron/cron.test.ts
  • tsconfig.json
📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added an automated production task that runs every 15 minutes to process scheduled moderation activity.
    • Secured task triggering via an HTTP Authorization header, while retaining manual triggering through a secret query parameter.
  • Documentation
    • Added Netlify deployment instructions and updated setup guidance for required environment variables, OAuth redirect configuration, and preview vs live behavior.
  • Bug Fixes
    • Hardened cron/trigger validation with safer secret comparison, request time budgeting, and bounded error/log output.
  • Tests
    • Added/expanded automated tests covering scheduled and manual trigger success and failure cases.

Walkthrough

Adds a Netlify function that invokes /api/cron every 15 minutes with bearer authentication, updates the API route to validate configured secrets and enforce a run deadline, adds coverage for both paths, and documents deployment, OAuth, environment variables, and dry-run operation.

Changes

Cron authentication and scheduling

Layer / File(s) Summary
API cron authentication
src/routes/api/cron/+server.ts, src/routes/api/cron/cron.test.ts
The cron API requires CRON_SECRET, accepts bearer or query-string credentials, uses constant-time comparison, passes a run deadline to runChannel, and tests valid, invalid, and missing-secret requests.
Netlify scheduled trigger
netlify/functions/cron.mjs, netlify/functions/cron.test.mjs
Adds a 15-minute scheduled function that calls /api/cron with a bearer token, applies a 25-second timeout, handles failures, limits error output, and tests the behavior.
Deployment and operation guidance
.env.example, AGENTS.md, DEPLOY.md
Documents Netlify environment variables, OAuth redirects, scheduled and manual cron invocation, database deployment, and dry-run verification.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • Bonobo791/Moderaty#3: Adds the runChannel(channelId, { deadline }) API used by the cron route's deadline handling.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly captures the main change: a Netlify cron function plus deployment docs.
Description check ✅ Passed The description is on-topic and matches the launch-prep changes, including cron, tests, and deploy guidance.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch launch-netlify

Comment @coderabbitai help to get the list of available commands.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Jul 30, 2026
@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

User description

What

Gets the repo launch-ready for Netlify (no Netlify-side setup in this PR):

  • netlify/functions/cron.mjs — Netlify Scheduled Function on */15 * * * * calling GET $URL/api/cron?secret=$CRON_SECRET (URL is Netlify's own site-URL variable). This matches the trigger design in EXECUTION_PLAN ("a Netlify Scheduled Function ... hitting the deployed URL"); each invocation processes one channel, so 15 minutes is the per-channel cadence. Missing env or a non-OK endpoint response throws, so failures are loud in the Netlify function logs.
  • netlify/functions/cron.test.mjs — 5 vitest cases written test-first: schedule, endpoint URL construction, missing CRON_SECRET/URL loud failures, non-OK endpoint propagation.
  • DEPLOY.md — one-time launch checklist: Turso migrate, Netlify env vars table, Google OAuth production redirect URI + consent screen, cron verification, post-launch dry-run → live sequence.
  • .env.example — production notes (APP_URL = deployed URL, DRY_RUN semantics).
  • AGENTS.md — documents netlify/functions/ and DEPLOY.md in Project Structure.

Verification

  • npm run test 77/77 (72 existing + 5 new), npm run check 0 errors, npm run build green.
  • Build settings were already in netlify.toml (build command, publish build, Node 24); adapter-netlify already configured in svelte.config.js.

CodeAnt-AI Description

Add scheduled Netlify moderation runs and a clear production deployment path

What Changed

  • Netlify triggers one moderation run every 15 minutes through the deployed app
  • Missing configuration or a failed moderation request is reported as a failed function invocation
  • Added a deployment checklist covering the database, environment variables, Google OAuth, dry-run verification, and live moderation rollout
  • Documented production environment behavior, including the deployed URL and dry-run mode
  • Added automated checks for scheduling, request construction, missing settings, and failed requests

Impact

✅ Scheduled moderation runs
✅ Visible cron failures in Netlify logs
✅ Safer production launch and dry-run verification

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR adds Netlify deployment infrastructure with a scheduled cron function and deployment documentation. The implementation is well-tested (5 new test cases, all passing) and follows the project structure conventions.

Critical Issues

Two critical issues must be addressed before merge:

  1. Crash Risk: The cron function lacks a timeout on the fetch call, which can cause silent failures when the function hits Netlify's execution time limit
  2. Security: CRON_SECRET is passed in URL query parameters, exposing it to logs and monitoring systems (CWE-598)

Overall Assessment

The deployment checklist (DEPLOY.md) is comprehensive and the test coverage is solid. Once the timeout and authentication issues are resolved, this will be ready for merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Comment thread netlify/functions/cron.mjs Outdated
Comment thread netlify/functions/cron.mjs Outdated
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Netlify launch prep: scheduled cron trigger + deploy checklist

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Add Netlify scheduled function to invoke /api/cron every 15 minutes with a shared secret.
• Add Vitest coverage for schedule, URL construction, missing env, and non-OK propagation.
• Document Netlify/Turso/Google OAuth launch checklist and production env variable guidance.
Diagram

graph TD
  sched{{"Netlify Scheduler"}} --> fn["Scheduled Function (cron.mjs)"] --> api["SvelteKit endpoint (/api/cron)"]
  api --> db[("Turso DB")]
  api --> pipe["Moderation pipeline (runChannel)"] --> yt{{"YouTube Data API"}}
  pipe --> ai{{"OpenAI API"}}
  subgraph Legend
    direction LR
    _db[("Database")] ~~~ _svc["App/Function"] ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Invoke pipeline directly from the Netlify Scheduled Function
  • ➕ Avoids an extra HTTP hop and duplicated error formatting
  • ➕ Can run without requiring the public site URL to be set correctly
  • ➖ Introduces a second execution entrypoint that must mirror auth/lease semantics
  • ➖ Harder to keep behavior consistent with the public /api/cron endpoint
2. Use an external scheduler (e.g., GitHub Actions/Upstash) to hit /api/cron
  • ➕ Works across hosting providers; decouples scheduling from Netlify
  • ➕ Can provide richer retry/alerting options
  • ➖ Adds another operational dependency and secrets surface area
  • ➖ Drifts from Netlify-native scheduled function design documented in the execution plan

Recommendation: The chosen approach (Netlify Scheduled Function calling the deployed /api/cron endpoint) is a good fit: it keeps the cron logic centralized in the SvelteKit endpoint (including lease semantics and failure shaping) while using Netlify-native scheduling. The added tests ensure loud failures on missing env and non-OK endpoint responses, which is appropriate for launch readiness.

Files changed (5) +198 / -0

Tests (1) +74 / -0
cron.test.mjsTest scheduled cron function behavior and failure modes +74/-0

Test scheduled cron function behavior and failure modes

• Adds Vitest coverage for the 15-minute schedule, correct endpoint URL construction, missing env var failures, and propagation of endpoint non-OK responses.

netlify/functions/cron.test.mjs

Documentation (2) +80 / -0
AGENTS.mdDocument Netlify scheduled cron trigger and deployment checklist location +5/-0

Document Netlify scheduled cron trigger and deployment checklist location

• Updates project structure docs to describe the scheduled cron function, its cadence, and how it calls the deployed /api/cron endpoint. Links to DEPLOY.md for one-time setup steps.

AGENTS.md

DEPLOY.mdAdd Netlify launch checklist (Turso, env vars, Google OAuth, cron verification) +75/-0

Add Netlify launch checklist (Turso, env vars, Google OAuth, cron verification)

• Introduces a one-time deployment guide covering Turso migrations, required Netlify environment variables, Google OAuth production configuration, and cron/manual trigger verification steps.

DEPLOY.md

Other (2) +44 / -0
.env.exampleClarify Netlify env var setup and DRY_RUN production semantics +5/-0

Clarify Netlify env var setup and DRY_RUN production semantics

• Adds notes directing operators to configure variables in Netlify UI. Clarifies that APP_URL must match the deployed URL for Google OAuth redirects and explains DRY_RUN behavior.

.env.example

cron.mjsAdd Netlify Scheduled Function to call /api/cron every 15 minutes +39/-0

Add Netlify Scheduled Function to call /api/cron every 15 minutes

• Implements a scheduled function that builds the deployed endpoint URL from Netlify’s URL env var, attaches CRON_SECRET, and fetches /api/cron. Throws on missing env or non-OK responses so failures are visible in Netlify function logs.

netlify/functions/cron.mjs

@codacy-production

codacy-production Bot commented Jul 30, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 27 complexity · 0 duplication

Metric Results
Complexity 27 (≤ 100 complexity)
Duplication 0 (≤ 1 duplication)

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Latest suggestions up to commit 2ae419d
CategorySuggestion                                                                                                                                    Severity
Security
Putting the cron credential in the request URL can leak it through URL logging

Embedding CRON_SECRET in the URL query string exposes the credential to HTTP access
logs, proxy telemetry, tracing systems, and any other component that records request
URLs. Because this is a server-to-server trigger, authenticate with a secret-bearing
header and update the endpoint to read that header instead of placing the credential
in the URL.

netlify/functions/cron.mjs [32]

Why it matters? 🤔
  • ❌ Cron credential may enter URL-based telemetry.
  • ❌ Leaked credentials authorize moderation runs.
  • ⚠️ Manual cron instructions repeat the query-string exposure.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** netlify/functions/cron.mjs
**Line:** 32:32
**Comment:**
	*Security: Embedding `CRON_SECRET` in the URL query string exposes the credential to HTTP access logs, proxy telemetry, tracing systems, and any other component that records request URLs. Because this is a server-to-server trigger, authenticate with a secret-bearing header and update the endpoint to read that header instead of placing the credential in the URL.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Critical
Possible bug
Unbounded endpoint requests can outlive the scheduled function and be terminated without controlled failure handling

The request has no timeout or abort deadline. The /api/cron handler calls runChannel
without a deadline, and its downstream API requests can take multiple retries; if
the site invocation hangs or exceeds Netlify's scheduled-function execution limit,
this function is terminated by the platform instead of failing explicitly and may
leave the scheduled run without a useful result. Add an abort deadline appropriate
for the Netlify execution limit and propagate it to the endpoint or otherwise
enforce a bounded request.

netlify/functions/cron.mjs [33-34]

Why it matters? 🤔
  • ❌ Slow cron runs can be terminated by Netlify.
  • ⚠️ Scheduled moderation failures lack controlled timeout reporting.
  • ⚠️ A terminated run may delay channel processing until lease expiry.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** netlify/functions/cron.mjs
**Line:** 33:34
**Comment:**
	*Possible Bug: The request has no timeout or abort deadline. The `/api/cron` handler calls `runChannel` without a deadline, and its downstream API requests can take multiple retries; if the site invocation hangs or exceeds Netlify's scheduled-function execution limit, this function is terminated by the platform instead of failing explicitly and may leave the scheduled run without a useful result. Add an abort deadline appropriate for the Netlify execution limit and propagate it to the endpoint or otherwise enforce a bounded request.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

The PR successfully establishes the Netlify deployment framework, including the scheduled function logic and a comprehensive checklist. Codacy analysis indicates the code is up to standards with no quality issues or complexity regressions.

However, a critical implementation gap was identified: the function relies on the URL environment variable, which is not available in the Netlify Functions runtime. This will cause the cron job to fail in production. To resolve this, the code and test suite must be updated to use APP_URL, which is already documented in the deployment checklist. No other major blockers were found.

About this PR

  • The deployment requires a switch from URL to APP_URL across the function logic and tests. Netlify does not provide the URL variable to functions at runtime, so using the manually configured APP_URL is necessary for the cron job to reach the internal API.

Test suggestions

  • Cron function configuration uses the correct cron schedule string
  • Cron function correctly constructs the URL using process.env.URL and appends the CRON_SECRET
  • Cron function throws an error if the URL environment variable is missing
  • Cron function throws an error if the CRON_SECRET environment variable is missing
  • Cron function throws an error containing the status code when the API endpoint returns a non-OK response

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread netlify/functions/cron.mjs Outdated
Comment thread netlify/functions/cron.test.mjs Outdated
@qodo-code-review

qodo-code-review Bot commented Jul 30, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 48 rules

Grey Divider


Remediation recommended

1. Secret in cron URL ✓ Resolved 🐞 Bug ⛨ Security
Description
netlify/functions/cron.mjs sends CRON_SECRET as a query parameter, which increases the chance of
secret exposure via URL capture (e.g., request logs/telemetry) compared to header-based auth. The
cron endpoint also validates the secret only from the query string, so the scheduled trigger path
inherits that higher-exposure pattern.
Code

netlify/functions/cron.mjs[R31-33]

+	const endpoint = new URL('/api/cron', base);
+	endpoint.searchParams.set('secret', secret);
+	const res = await fetch(endpoint);
Relevance

●● Moderate

No direct precedent for “secret in URL query param”; team has mixed security-hardening acceptance in
past reviews.

PR-#4

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The scheduled function explicitly adds the secret to the URL query string, and the cron endpoint’s
authentication reads only that query param, making the secret-in-URL pattern part of the new
scheduled execution path.

netlify/functions/cron.mjs[27-33]
src/routes/api/cron/+server.ts[35-37]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`netlify/functions/cron.mjs` authenticates to `/api/cron` by placing `CRON_SECRET` in the request URL query string. Secrets in URLs are easier to accidentally retain/propagate (logs, APM spans, copy/paste, etc.) than secrets carried in headers.

## Issue Context
- The scheduled function builds `https://<site>/api/cron?secret=<CRON_SECRET>`.
- The SvelteKit endpoint currently checks `url.searchParams.get('secret')`.

## Fix Focus Areas
- netlify/functions/cron.mjs[27-36]
- src/routes/api/cron/+server.ts[35-37]

## Suggested fix
1. Change the scheduled function to send the secret via an `Authorization: Bearer <secret>` header (or a dedicated `X-Cron-Secret` header). Consider switching to `POST` to further avoid credential-in-URL patterns.
2. Update `/api/cron` to read the secret from the chosen header (and optionally continue supporting the query param for manual curl triggers, but prefer header for automation).
3. Update DEPLOY.md manual trigger instructions accordingly (or provide both examples).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Cron logs full response ✓ Resolved 🐞 Bug ⛨ Security
Description
netlify/functions/cron.mjs logs the entire /api/cron response body on success and embeds the full
body into thrown errors, which can leak internal identifiers/error details into Netlify logs and
increase log noise. In failure cases, /api/cron returns serialized error messages from the
moderation pipeline, which can include specific comment IDs.
Code

netlify/functions/cron.mjs[R34-36]

+	const body = await res.text();
+	if (!res.ok) throw new Error(`cron endpoint failed: ${res.status} ${body}`);
+	console.log(`cron endpoint ok: ${body}`);
Relevance

●● Moderate

Team previously addressed token/log leakage partially, but no specific precedent on logging full
response bodies.

PR-#4

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The scheduled function logs/throws with the full response body. The cron endpoint returns
exception-derived messages in JSON on 500s, and the pipeline constructs error strings that include
comment IDs, which would then be included in the logged body.

netlify/functions/cron.mjs[33-36]
src/routes/api/cron/+server.ts[59-65]
src/lib/server/pipeline.ts[240-248]
src/lib/server/pipeline.ts[488-492]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The scheduled function logs and throws with the *entire* response body from `/api/cron`. This unnecessarily persists internal details to logs and makes failures noisier than needed.

## Issue Context
- `/api/cron` returns JSON including `results` and, on failure, an error string derived from caught exceptions.
- The moderation pipeline can include comment IDs in error messages.

## Fix Focus Areas
- netlify/functions/cron.mjs[33-36]
- src/routes/api/cron/+server.ts[59-65]
- src/lib/server/pipeline.ts[240-248]
- src/lib/server/pipeline.ts[488-492]

## Suggested fix
1. On success: parse JSON and log only a small summary (e.g., `ok`, `dryRun`, channel id processed, counts) rather than the full body.
2. On failure: throw an error that includes status code plus a *truncated* body (or just the top-level `error` field), to avoid logging large/internal payloads.
3. Optionally add a stable, redacted server-side error code in `/api/cron` responses, so the scheduler can log that code without needing the full serialized message.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. process.env.URL undocumented in .env.example ⊘ Outdated 📘 Rule violation ⚙ Maintainability
Description
netlify/functions/cron.mjs now requires the non-sensitive URL env var, but .env.example does
not list it, making deployments easier to misconfigure. This violates the requirement to document
non-sensitive env vars consumed by the code.
Code

netlify/functions/cron.mjs[R27-28]

+	const base = process.env.URL;
+	if (!base) throw new Error('URL environment variable is required (Netlify sets it in production)');
Relevance

●● Moderate

No prior review evidence about adding env vars to .env.example; team does accept env validation
changes.

PR-#3
PR-#4

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The cron function reads process.env.URL as a required value, while .env.example enumerates
expected variables but does not include URL, violating the checklist requirement to document
non-sensitive env vars.

Rule 2401165: Document non-sensitive environment variables in .env.example
netlify/functions/cron.mjs[27-30]
.env.example[24-32]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new non-sensitive environment variable (`URL`) is read by the Netlify scheduled function, but it is not documented in `.env.example`.

## Issue Context
`netlify/functions/cron.mjs` reads `process.env.URL` to determine the deployed site base URL. `.env.example` currently documents other required variables (including `CRON_SECRET`) but not `URL`.

## Fix Focus Areas
- netlify/functions/cron.mjs[27-30]
- .env.example[24-32]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Spaces used in DEPLOY.md 📘 Rule violation ⚙ Maintainability
Description
DEPLOY.md contains indented lines starting with spaces (e.g., the environment-variable table),
violating the tabs-only indentation requirement. This can create inconsistent formatting and
lint/style conflicts across editors and tooling.
Code

DEPLOY.md[R39-48]

+  | Variable | Notes |
+  | --- | --- |
+  | `TURSO_DATABASE_URL` | `libsql://...` from step 1 |
+  | `TURSO_AUTH_TOKEN` | from step 1 |
+  | `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` | Google Cloud OAuth client (Web) |
+  | `OPENAI_API_KEY` | for AI scoring |
+  | `ENCRYPTION_KEY` | `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"` |
+  | `CRON_SECRET` | any long random string; also used to trigger cron manually |
+  | `APP_URL` | the deployed site URL, e.g. `https://moderaty.netlify.app` |
+  | `DRY_RUN` | start with `true`; flip to `false` after verifying a dry run |
Relevance

● Weak

Tabs-only indentation suggestions were previously rejected (PRODUCT.md, src/app.css). Likely not
enforced for docs/tables.

PR-#7
PR-#11

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The checklist requires tabs (not spaces) for indentation. DEPLOY.md lines for the variables table
begin with spaces before the | character, demonstrating space-indented lines in the change.

Rule 2401137: Use tabs for indentation, never spaces
DEPLOY.md[39-48]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `DEPLOY.md` file includes indentation using spaces (leading whitespace) instead of tabs.

## Issue Context
The compliance rule requires tabs for indentation in changed source files; `DEPLOY.md` includes a block (the env var table) whose lines begin with spaces.

## Fix Focus Areas
- DEPLOY.md[39-48]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment thread netlify/functions/cron.mjs Outdated
Comment thread netlify/functions/cron.mjs Outdated
Comment thread netlify/functions/cron.mjs Outdated
@Bonobo791

Copy link
Copy Markdown
Owner Author

Review findings addressed in e864ff2, all test-first:

  • Fetch timeout (Amazon Q): the trigger request now uses AbortSignal.timeout(25_000) — it aborts with a loud error instead of hanging into the platform limit.
  • Secret in URL (Amazon Q, Qodo Add server-side moderation pipeline #3): the scheduled function now sends Authorization: Bearer <CRON_SECRET> and the URL carries no query string. The endpoint accepts the header first and falls back to the plan-documented ?secret= form, so EXECUTION_PLAN §trigger and docs/e2e-verification.md manual-curl steps keep working unchanged — header-only would have conflicted with the repo plan. The endpoint also now fails loudly with 500 when CRON_SECRET is unset instead of comparing against undefined.
  • URLAPP_URL (Codacy; also resolves Qodo Scaffold Drizzle/libSQL and adapter-node setup #1, .env.example coverage): the function reads APP_URL, which is already required and documented in DEPLOY.md / .env.example, instead of relying on Netlify's URL being present in the function runtime.
  • Full response body in logs (Qodo Phase D: Auth and cron routes #4): bodies written to logs/errors are truncated to 500 chars — bounded noise and no long pipeline error dumps, while keeping enough to diagnose.

New tests: 5 endpoint cases (src/routes/api/cron/cron.test.ts — 401s, unconfigured-secret 500, query + bearer acceptance) and updated function cases (bearer-only URL, AbortSignal, APP_URL, truncation bound).

npm run test 84/84, npm run check 0 errors, npm run build green.

@codeant-ai codeant-ai Bot added size:L This PR changes 100-499 lines, ignoring generated files and removed size:L This PR changes 100-499 lines, ignoring generated files labels Jul 30, 2026
@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

User description

What

Gets the repo launch-ready for Netlify (no Netlify-side setup in this PR):

  • netlify/functions/cron.mjs — Netlify Scheduled Function on */15 * * * * calling GET $URL/api/cron?secret=$CRON_SECRET (URL is Netlify's own site-URL variable). This matches the trigger design in EXECUTION_PLAN ("a Netlify Scheduled Function ... hitting the deployed URL"); each invocation processes one channel, so 15 minutes is the per-channel cadence. Missing env or a non-OK endpoint response throws, so failures are loud in the Netlify function logs.
  • netlify/functions/cron.test.mjs — 5 vitest cases written test-first: schedule, endpoint URL construction, missing CRON_SECRET/URL loud failures, non-OK endpoint propagation.
  • DEPLOY.md — one-time launch checklist: Turso migrate, Netlify env vars table, Google OAuth production redirect URI + consent screen, cron verification, post-launch dry-run → live sequence.
  • .env.example — production notes (APP_URL = deployed URL, DRY_RUN semantics).
  • AGENTS.md — documents netlify/functions/ and DEPLOY.md in Project Structure.

Verification

  • npm run test 77/77 (72 existing + 5 new), npm run check 0 errors, npm run build green.
  • Build settings were already in netlify.toml (build command, publish build, Node 24); adapter-netlify already configured in svelte.config.js.

CodeAnt-AI Description

Secure and prepare scheduled moderation runs for Netlify

What Changed

  • Netlify now runs one moderation check every 15 minutes using APP_URL, with the secret sent securely in an Authorization header instead of the URL
  • Scheduled requests stop after 25 seconds, report failed endpoint responses, and limit logged error details
  • Manual cron requests still support the existing query-parameter secret
  • Missing deployment settings now produce clear configuration errors
  • Added a Netlify deployment checklist covering database setup, environment variables, Google OAuth, dry-run verification, and activation
  • Added coverage for authentication, scheduling, timeouts, configuration failures, and bounded error output

Impact

✅ Scheduled moderation every 15 minutes
✅ Secrets kept out of cron URLs
✅ Fewer hanging or silent cron failures

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: 4155c918
Scan Time: 2026-07-30 23:16:39 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED Rating S: No issues
Antipatterns ✅ PASSED No antipatterns

View Full Results

@codeant-ai

codeant-ai Bot commented Jul 30, 2026

Copy link
Copy Markdown

PR Code Suggestions ✨

Latest suggestions up to commit e864ff2
CategorySuggestion                                                                                                                                    Severity
Api mismatch
Aborting the client request does not stop the server-side moderation run

The 25-second abort only terminates the Netlify function's outbound request; the
SvelteKit endpoint does not receive this signal and runChannel is invoked without a
deadline. When a pipeline run exceeds 25 seconds, the scheduled invocation fails
while the server continues processing, potentially performing moderation writes and
retaining the channel lease after the caller has gone away. Propagate a deadline or
cancellation signal to the endpoint and pipeline, or use a timeout consistent with
the server-side run bound.

netlify/functions/cron.mjs [36]

Why it matters? 🤔
  • ⚠️ Scheduled cron logs failures while runs continue.
  • ⚠️ Long runs can overlap subsequent scheduling attempts.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** netlify/functions/cron.mjs
**Line:** 36:36
**Comment:**
	*Api Mismatch: The 25-second abort only terminates the Netlify function's outbound request; the SvelteKit endpoint does not receive this signal and `runChannel` is invoked without a deadline. When a pipeline run exceeds 25 seconds, the scheduled invocation fails while the server continues processing, potentially performing moderation writes and retaining the channel lease after the caller has gone away. Propagate a deadline or cancellation signal to the endpoint and pipeline, or use a timeout consistent with the server-side run bound.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
Major

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@DEPLOY.md`:
- Around line 66-68: Update the manual cron example in DEPLOY.md to use the
Authorization bearer-header form with CRON_SECRET instead of placing the secret
in the ?secret= query parameter. Retain the query-parameter invocation only as a
clearly labeled fallback note.

In `@netlify/functions/cron.mjs`:
- Around line 34-37: Update the fetch timeout in the cron handler around
AbortSignal.timeout to remain safely below the deployed Netlify function limit,
leaving enough margin for cleanup and response handling; alternatively, document
and enforce a deployment plan whose configured limit exceeds the current
timeout. Ensure cron execution remains within the serverless hard limit.

In `@netlify/functions/cron.test.mjs`:
- Around line 86-92: Add coverage for the successful logging path in the test
around handler(): stub fetch with a 200 response containing an oversized body,
spy on console.log, invoke the handler, and assert the logged response argument
remains below the expected bound. Preserve the existing 500-response case and
its failure-message length assertion.
- Around line 59-64: Update the timeout test around handler to stub fetch with a
promise that waits for the request signal, advance the fake timers by the
configured 25-second timeout, and assert that handler rejects once the signal
aborts. Retain the AbortSignal setup but verify actual expiration rather than
only checking its presence.

In `@src/routes/api/cron/cron.test.ts`:
- Around line 22-30: Replace the synthetic CRON_SECRET value in the mocks
fixture at src/routes/api/cron/cron.test.ts lines 22-30 and
netlify/functions/cron.test.mjs lines 22-30 with an approved fixture, or add the
required documented maintainer approval record at both sites; do not commit the
unapproved test-secret value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 81deb988-c486-46c6-939a-eb2fd1326eb3

📥 Commits

Reviewing files that changed from the base of the PR and between 83de549 and e864ff2.

📒 Files selected for processing (7)
  • .env.example
  • AGENTS.md
  • DEPLOY.md
  • netlify/functions/cron.mjs
  • netlify/functions/cron.test.mjs
  • src/routes/api/cron/+server.ts
  • src/routes/api/cron/cron.test.ts

Comment thread DEPLOY.md Outdated
Comment thread netlify/functions/cron.mjs Outdated
Comment thread netlify/functions/cron.test.mjs Outdated
Comment thread netlify/functions/cron.test.mjs Outdated
Comment thread src/routes/api/cron/cron.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/api/cron/`+server.ts:
- Around line 48-50: Update the authorization selection in the cron GET handler
around secretMatches so query-string authentication is used only when the
Authorization header is absent; any present non-Bearer or malformed header must
fail with 401, even when a valid query secret exists. Add a regression test
covering a Basic authorization header with a valid query secret and assert that
GET rejects with status 401.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 972bb55f-c69e-4595-b740-f6f18277aab5

📥 Commits

Reviewing files that changed from the base of the PR and between e864ff2 and 7feffd1.

📒 Files selected for processing (2)
  • src/routes/api/cron/+server.ts
  • src/routes/api/cron/cron.test.ts

Comment thread src/routes/api/cron/+server.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/api/cron/`+server.ts:
- Line 72: Move calculation of the run deadline to the beginning of the cron
handler, before channel selection and lease-claiming database work, then reuse
that captured deadline in the runChannel call. Update the relevant test to
simulate pre-run delay and verify the deadline budget starts when the handler
begins rather than after the prelude.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a2485fa2-625d-4e49-aeb9-52c190ab9062

📥 Commits

Reviewing files that changed from the base of the PR and between 7feffd1 and 4c27337.

📒 Files selected for processing (2)
  • src/routes/api/cron/+server.ts
  • src/routes/api/cron/cron.test.ts

Comment thread src/routes/api/cron/+server.ts Outdated
…s logs, header-form manual trigger, document 26s function timeout and synthetic test credential
@Bonobo791

Copy link
Copy Markdown
Owner Author

Second review round addressed (4c27337, fe78ef8):

  • Deadline propagation (api-mismatch review comment): validated as correct in mechanism — the endpoint called runChannel with no deadline, so the trigger's 25s abort left the server running unbounded. The endpoint now passes a 20s server-side deadline (RUN_BUDGET_MS), inside the trigger's 25s abort window; on expiry the pipeline stops cleanly with partial: true and the finally still releases the lease. (Note: the lease was never truly stranded — the 10-minute lease expiry and dispatched-action reconciliation already covered hard kills.)
  • Timeout proof (CodeRabbit): the timeout test now uses fake timers with a fetch that waits on the abort signal and asserts rejection — it fails if the timeout is removed. Implementation refactored from AbortSignal.timeout() to an explicit AbortController + setTimeout (fake-timer controllable, same runtime behavior).
  • Success-path log bound (CodeRabbit): added a 200 + large-body test asserting the logged body is truncated; existing 500-path bound kept.
  • Manual trigger example (CodeRabbit): DEPLOY.md now shows the bearer-header curl as the primary form, with ?secret= kept as a documented fallback (still supported, per the execution plan's trigger contract).
  • Platform limit (CodeRabbit): DEPLOY.md now calls out raising the Netlify function timeout to 26s so the 20s/25s budgets can actually fire.
  • Synthetic credential (CodeRabbit): test-secret is synthetic; maintainer approved the documented exception in conversation on 2026-07-30, and both test files now carry that note per AGENTS.md.

npm run test 87/87, npm run check 0 errors, npm run build green.

@Bonobo791

Copy link
Copy Markdown
Owner Author

Third review round addressed in 3d963d7 (test-first):

  • Fail closed on malformed Authorization (CodeRabbit, Minor): a non-Bearer Authorization header no longer falls through to query auth — the query param is only consulted when no header is present at all. Regression test (Basic scheme + valid query secret → 401) added first, watched red.
  • Deadline captured at handler entry (CodeRabbit, Major): deadline = Date.now() + RUN_BUDGET_MS now starts before channel selection and lease claiming, so the DB prelude consumes the same 20s budget and the trigger's 25s abort can't fire first even under a slow prelude.
  • Toolchain repair: commit 5716204 had downgraded @sveltejs/kit to ^0.0.30 (pre-1.0), adapter-netlify to ^0.0.13, and drizzle-kit to ^0.18.1, which broke module resolution for the whole app. Restored the SvelteKit 2 toolchain versions; the tsconfig trailing-comma change from 5a9e319 is kept.

All review comments on this PR are now addressed or have posted dispositions. npm run test 87/87, npm run check 0 errors, npm run build green.

…audit moderate)

drizzle-kit 0.31.10 (latest) pulls esbuild 0.18.20 via the deprecated
@esbuild-kit/esm-loader -> core-utils chain. An npm override pins that
nested esbuild to ^0.25.12, clearing the 4 moderate advisories
(GHSA-67mh-4wv8-2f99). drizzle-kit config loading verified via
`drizzle-kit check`. Remaining 3 low cookie advisories are upstream in
@sveltejs/kit 2.70.2 (latest) with no fix released yet.
@Bonobo791

Copy link
Copy Markdown
Owner Author

Fixed the esbuild vulnerability properly. The earlier downgrade of @sveltejs/kit wasn't the right lever — esbuild is a transitive dep, so direct-dep versions don't affect it.

What was wrong: drizzle-kit@0.31.10 (the latest release) pulls esbuild@0.18.20 through the deprecated @esbuild-kit/esm-loader@esbuild-kit/core-utils chain (GHSA-67mh-4wv8-2f99, moderate — dev-server CORS issue, dev tooling only, not shipped to production).

Fix (commit 4155c91): an npm overrides entry in package.json pins just that nested esbuild to ^0.25.12. Everything else untouched — vite stays on 0.28.1.

Verified: npm ls esbuild shows no 0.18.20 anywhere; npm audit moderate count 4 → 0; drizzle-kit check loads the TS config fine through the overridden chain (the actual risk point); 87/87 tests, check 0 errors, build green.

Residual: 3 low-severity cookie advisories remain — they're upstream in @sveltejs/kit@2.70.2, which is the latest release, so there's no fix to take yet. npm's suggested --force 'fix' would downgrade kit to 0.0.30 (nonsense). These should clear on the next kit release.

@sonarqubecloud

Copy link
Copy Markdown

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 47 complexity · 0 duplication

Metric Results
Complexity 47 (≤ 100 complexity)
Duplication 0 (≤ 1 duplication)

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@Bonobo791
Bonobo791 merged commit 53d12e9 into main Jul 30, 2026
10 of 11 checks passed
@Bonobo791
Bonobo791 deleted the launch-netlify branch July 30, 2026 23:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant