Server Scripts #
This directory contains utility scripts for managing the Coop server.
get-invite-token.ts #
Retrieves the signup invite link for a user that was invited from the UI.
Usage #
From the server directory, run:
npm run get-invite -- --email "user@example.com"
Parameters #
--email: Email address of the invited user (required)
Output #
The script will display:
- Invite details (email, role, org ID, created date)
- Signup URL - The full URL to complete signup
Example Output #
✅ Invite Token Found!
════════════════════════════════════════════════════════════
Invite Details:
════════════════════════════════════════════════════════════
Email: user@example.com
Role: MODERATOR
Organization: abc123xyz
Created At: 2025-12-10T20:15:30.000Z
════════════════════════════════════════════════════════════
🔗 Signup URL:
════════════════════════════════════════════════════════════
http://localhost:3000/signup/a1b2c3d4e5f6...
Copy this URL and paste it in your browser to sign up.
════════════════════════════════════════════════════════════
Notes #
- Invite tokens are stored in the
public.invite_user_tokenstable. - Emails are normally sent to new users, but require SENDGRID token for this.
- The script shows the most recent invite for the given email.
create-org-and-user.ts #
Creates a new organization with an admin user and generates an API key.
Usage #
From the server directory, run:
npm run create-org -- \
--name "My Organization" \
--email "admin@example.com" \
--website "https://example.com" \
--firstName "John" \
--lastName "Doe" \
--password "testpassword123"
Parameters #
All parameters are required:
--name: Organization name (must be unique)--email: Admin user email (must be unique)--website: Organization website URL (must be a valid URL)--firstName: Admin user's first name--lastName: Admin user's last name--password: Admin user's password (minimum 8 characters recommended)
Output #
The script will output:
- Organization ID
- Organization details (name, email, website)
- Admin user ID and details
- API Key - This is only shown once! Copy and store it securely.
Example Output #
✅ Organization and admin user created successfully!
════════════════════════════════════════════════════════════
Organization Details:
════════════════════════════════════════════════════════════
Organization ID: abc123xyz
Organization Name: My Organization
Organization Email: admin@example.com
Website URL: https://example.com
════════════════════════════════════════════════════════════
Admin User Details:
════════════════════════════════════════════════════════════
User ID: user456def
Name: John Doe
Email: admin@example.com
Role: ADMIN
════════════════════════════════════════════════════════════
🔑 API KEY (STORE THIS SECURELY!)
════════════════════════════════════════════════════════════
New API key generated successfully! Please copy and store it securely.
API Key: ABC123-XYZ789-DEF456-GHI012
⚠️ This API key will not be shown again. Save it now!
════════════════════════════════════════════════════════════
What Gets Created #
The script performs the following actions:
- Creates an Organization with the provided details
- Generates an API Key for the organization
- Creates Signing Keys for JWT token verification
- Initializes Organization Settings:
- Default user type for moderation
- Default user interface settings
- Default organization settings
- Default manual review tool settings
- Creates an Admin User with:
- Password login method enabled
- Admin role permissions
- Approved status
Notes #
- The API key is only displayed once. Make sure to copy and store it securely.
- The organization name and email must be unique in the database.
- The password should be strong and at least 8 characters long.
- All database connections are properly closed after the script completes.
Troubleshooting #
If the script fails:
- Check that your database connection is configured correctly in
.env - Verify that the organization name and email don't already exist
- Ensure the website URL is valid (must start with
http://orhttps://) - Check the console output for specific error messages
recover-mrt-queue.ts #
Re-enqueues items into a Manual Review Tool queue after Redis loss.
Pending MRT job payloads only live in Redis (BullMQ). When a queue is
obliterated — intentionally via the "Delete All Jobs" button, or
unintentionally via a Redis cluster reset / data loss — the items disappear
from the moderator's queue. The list of WHICH items were enqueued is
preserved in Postgres (manual_review_tool.job_creations); rebuilt
reportHistory is sourced from the data warehouse table
REPORTING_SERVICE.REPORTS; item bodies are re-fetched via
ItemInvestigationService.getItemByIdentifier, which cascades through
Scylla (item_submission_by_thread), the org's Partial Items endpoint,
and the data warehouse (6-month lookback) — using whichever source still
has the item.
Usage #
Get the --queueId from the MRT queues dashboard (the "ID" column has a
copy-to-clipboard button next to each id). The mode is auto-detected from
the queue's role in ncmec_org_settings, so the same command works for
both default and NCMEC queues:
# Dry run -- prints what would be re-enqueued, makes no changes
npm run recover-mrt-queue -- \
--orgId "<orgId>" \
--queueId "<queueId>"
# Actually re-enqueue
npm run recover-mrt-queue -- \
--orgId "<orgId>" \
--queueId "<queueId>" \
--apply
Parameters #
--orgId(required): Organization id whose queue is being recovered.--queueId(required): MRT queue id to recover into. Must already exist.--mode default|ncmec(optional): job kind to re-enqueue. Defaults to auto-detect:ncmecif--queueIdmatches the org'sncmec_org_settings.default_ncmec_queue_id, elsedefault. Pass explicitly only to override (rare; only useful for orgs whose routing rules send DEFAULT jobs into the NCMEC queue, or vice versa). The configuration banner prints both the chosen mode and its source.--since "<ISO timestamp>"(default: 30 days ago): only considerjob_creationsrows after this timestamp.--limit <N>(default10000, max100000): cap on number of items.--apply(default off): actually call enqueue. Without this flag, the script is a dry-run and prints the items it would re-enqueue.--no-report-history(default off): skip rebuildingreportHistoryfrom the data warehouse (useful if the warehouse is unavailable).
Safety #
- Dry-run by default.
--applyis required to make any changes. - Items that already have a decision in
manual_review_tool.manual_review_decisionsare filtered out. - BullMQ dedupes by
(itemTypeId, itemId)per queue, so re-running the script on a partially-recovered queue is safe. - Per-item enqueue errors are logged and counted but do not abort the run.
- The script validates that
--orgIdand--queueIdlook like opaque ids before issuing any DB queries.
What it does NOT recover #
- The original BullMQ
JobIdvalues are not preserved — recovered jobs receive new ids derived from the item identifier. - Item field data is whatever the cascade above can find. If the item has been hard-deleted from Scylla, the org has no Partial Items endpoint, and the warehouse 6-month lookback misses, the item is logged as skipped and counted in the final summary.
- Report history is best-effort: only inbound
submitReportrows that made it intoREPORTING_SERVICE.REPORTSare restored. Rule-driven enqueues (ENQUEUE_TO_MRT) never had a report history to begin with.