Skip to content
You're viewing documentation for v1.2. It may be out of date.Go to latest
jalco-drop
Esc
↑↓navigate↵open⌘Jpreview
On this page

Deploy on Neon

Run jalco-drop as a Neon Function with a Neon bucket and an hourly cleanup trigger, from the template or from this repo.

Neon Functions run jalco-drop as one long-running function next to a Neon Object Storage bucket. A schedule trigger deletes expired drops every hour. Everything is declared in neon.ts, so one neon deploy creates it all.

You need the Neon CLI (npm install -g neon@latest, then neon login) and a project in a region with Functions and Object Storage: AWS us-east-1, us-east-2, eu-central-1 or ap-southeast-1.

Two ways in

From the template From this repo
Start with neon functions new jalco-drop in any Neon project npx jalco-drop init my-drop --target neon
Your code A short handler you own: settings, a /health route and a custom file format The whole app, ready to change
Upgrade Bump jalco-drop in package.json npx jalco-drop upgrade
Pick it when You want a drop site and won’t touch the code You want to customize formats, pages or styles

From the template

Scaffold it

neon link
neon functions new jalco-drop

This writes functions/jalcodrop/index.ts, adds the jalcodrop function to neon.ts, and installs jalco-drop from npm. index.ts is yours to edit: it calls createDrop() with your settings, answers /health with a storage check, and adds a format that tints errors and warnings in .log files.

Add the bucket and the cleanup trigger

Add these to neon.ts, next to functions:

buckets: {
  drops: {},
},
triggers: {
  cleanup: {
    type: "schedule",
    function: "jalcodrop",
    cron: "0 * * * *",
    functionPath: "/__scheduled",
  },
},

Set the admin token

echo "ADMIN_TOKEN=$(openssl rand -base64 32)" >> .env.local

Save it in a password manager: it signs in to /admin and the upload API.

Deploy

neon config plan --env .env.local
neon deploy --env .env.local

The plan should list the drops bucket, the jalcodrop function and the cleanup trigger. The deploy prints the function’s URL; neon functions get jalcodrop shows it again.

From this repo

Create the project

npx jalco-drop init my-drop --target neon

Setup writes .env with a fresh admin token. Add --domain drop.example.com to serve from your own hostname.

Deploy

cd my-drop
neon link
npm run deploy:neon

npm run deploy:neon builds dist/neon (the function plus its static files) and runs neon deploy --env .env. The repo’s neon.ts already declares the bucket and trigger; the function’s slug is drop.

The handler you own

import { createDrop, rawHtml, type TextFormat } from "jalco-drop/neon";

const logs: TextFormat = {
  id: "logs",
  test: (name) => name.endsWith(".log"),
  render: (text) => ({ mono: true, body: rawHtml(`<pre>${escape(text)}</pre>`) }),
};

const drop = createDrop({
  adminToken: process.env.ADMIN_TOKEN!,
  site: { owner: process.env.OWNER_NAME },
  formats: [logs],
});

export default {
  async fetch(request: Request) {
    if (new URL(request.url).pathname === "/health") return Response.json(await drop.health());
    return drop.fetch(request);
  },
};

createDrop() takes adminToken, bucket, site (name, owner, accent, codeTheme, homeUrl), rateLimit and formats, and returns fetch and health(). Formats render HTML through rawHtml(), so escape anything that comes from the file. The formats guide explains how formats are chosen.

Settings

Variable
ADMIN_TOKEN required Signs in to /admin and the API, and signs session and unlock cookies
OWNER_NAME, SITE_NAME, ACCENT_COLOR, CODE_THEME, HOME_URL optional Starting settings. You can change them later in /admin without redeploying
DROPS_BUCKET optional The bucket drops are stored in. Defaults to drops
RATE_LIMIT optional off disables the limit of 60 requests a minute per IP

Neon injects the bucket’s AWS_* credentials itself. Values are read when you run neon deploy, so redeploy after changing one.

Your own domain

Add customDomains to the function in neon.ts, deploy, then follow Neon’s custom domains guide for the DNS record. With the repo’s neon.ts, set DROP_DOMAIN in .env instead.

jalcodrop: {
  // ...
  customDomains: ["drop.example.com"],
},

How cleanup works

The cleanup trigger POSTs to /__scheduled at the top of every hour, and jalco-drop deletes drops whose expiry has passed. The route only runs when the request carries Neon’s X-Neon-Trigger-Invocation-Id header, which Neon strips from outside requests, so anyone else gets a 401. Expired drops stop being served the moment they expire, before cleanup runs.

Check that it ran:

neon triggers list
neon logs query --source function --service-name neon-function/jalcodrop

Limits

  • 10 MB uploads. Neon rejects any request body over 10 MiB before it reaches jalco-drop, so that is the largest single drop. Use Cloudflare (100 MB) or Docker for bigger files.
  • No rendered link previews. Neon has no headless browser, so shared links show a summary card instead of the rendered image.
  • Rate limiting is per instance. The 60-a-minute limit is kept in memory, so it resets when the function restarts.

Was this page helpful?