• Both are pluggable seams (dev-plan-4.md §1.2 items 1-2), same shape as
    T20's sendVerificationEmail: a fixed env var gates a real vendor call
    that isn't implemented yet (none has been picked), and the no-vendor
    path is what actually runs today.
    
    describeGarmentImage() — mode 2 has no admin around to hand-write
    garments.appearanceDescriptor the way admin_seed garments do, so it
    needs *something* non-empty to fill that field; today that's a fixed
    placeholder string, swapped for a real vision-model call once a
    vendor's picked.
    
    moderateGarmentImage() — the requirements doc flags image-content
    moderation as vendor-TBD but never actually lists it as its own task;
    this fills that gap. Every call writes a moderation_log row (new
    dressup-store.js export, M.9 schema) regardless of which branch runs,
    so the audit trail exists before a real vendor does. Default-permissive
    when unconfigured — T2's upload endpoint already enforces the real
    trust-boundary checks (magic-byte type check, 8MB cap, EXIF-stripped
    re-encode); this only covers the *content* check, which nothing here
    can perform without a vendor.
    
    Neither is unit-tested standalone (server.js doesn't export internal
    functions, matching sendVerificationEmail's existing precedent) — T27
    integration-tests both through the actual upload endpoint.
    
    Also adds pricing.js to the tmpdir file-copy list in every test that
    spawns server.js in isolation — server.js now requires it.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_011DUiYs6UjxDMkfvnjtsKFo
    yibo authored
     
    Browse File »
  • POST /api/auth/register, POST /api/auth/login, POST /api/auth/logout,
    and a debug GET /api/auth/me (T19's own acceptance criteria calls for
    it explicitly). Passwords hashed with node:crypto's scryptSync (salt
    stored alongside the hash, verified with timingSafeEqual) — still zero
    new dependencies.
    
    Sessions live in a separate user_session cookie from admin_session
    (T6) — the two are unrelated systems, confirmed by a test that a user
    session can't unlock /api/admin/*. Login and "email not registered"
    return the identical 401 body so the endpoint can't be used to
    enumerate registered emails.
    
    Every test file that spawns server.js in an isolated tmpdir now also
    copies db.js alongside it (dance-channel/dressup-generate/dressup-crud/
    admin-pages-flow/uploads-route .test.js) — server.js requires db.js
    unconditionally now, so those child processes were crashing silently
    without it.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_011Deddn3sM3oQnmvqWSMb4y
    yibo authored
     
    Browse File »
  • generateDanceVideo() takes a queued generationJobs record through
    prompt build -> forward character/garment/dance-ref uploads to H3
    Console's /v1/assets -> POST /v1/videos -> poll -> push a traced clip
    into library.json (characterId/garmentId/refDanceVideoId). Mirrors
    generateForPrompt's submit/poll/persist shape but keyed off the new
    generationJobs store so a restart resumes any in-flight job by its
    h3JobId, same as resumeGenerations() does for the live queue.
    
    POST /api/admin/generate accepts characterId/garmentId (+ optional
    refDanceVideoId, else a random active dance-ref is picked) and skips
    straight to the cached clip if that character x garment combo was
    already generated (M.7 dedupe). GET /api/admin/generate/:id polls job
    status.
    
    Tests spin up a mock H3 Console over HTTP so the full submit/poll/land
    path runs offline. Also fixes two existing test fixtures
    (dressup-crud, admin-pages-flow) that spawn server.js without copying
    prompt-template.js — worked until T8 added the first require() of it
    into server.js, at which point the spawned server silently failed to
    boot.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    yibo authored
     
    Browse File »
  • mountCrud() is a small generic GET-list/POST-create/PATCH/DELETE
    router shared across the three entities (same shape, see MVCP
    M.3/M.4/M.5) instead of copy-pasting the boilerplate three times.
    Each entity fills in its own admin_seed-appropriate defaults on
    create. characters additionally enforces the single-default-character
    invariant (2 节) across create/update.
    
    HTML admin pages for these come next.
    yibo authored
     
    Browse File »