Mount in server/server.ts:

import stockRoute from "./api/stock/library";
// Serve /uploads publicly
app.use("/uploads", express.static("uploads"));
app.use("/api/stock", stockRoute);


(We already have authWithKey + roles from earlier.)

Frontend
1) Admin Dashboard tiles

They’ll appear automatically if you added the Admin Dashboard earlier. We’ll add two pages:

Admin › Stock Uploader (tabs: Photos / Mockups)

Public Stock Photos and Mockups library pages

Admin uploader: src/pages/admin/StockUploader.tsx
import React, { useState } from "react";
import { getAdminKey } from "../../admin/useAdminSession";

export default function StockUploader() {
  const ADMIN_KEY = getAdminKey();
  const [tab, setTab] = useState<"photos"|"mockups">("photos");
  const [files, setFiles] = useState<FileList | null>(null);
  const [tags, setTags] = useState("");
  const [category, setCategory] = useState("");

  async function upload() {
    if (!files || !files.length) { alert("Pick files"); return; }
    const fd = new FormData();
    Array.from(files).forEach(f => fd.append("files", f));
    fd.append("tags", tags);
    fd.append("category", category);

    const res = await fetch(`/api/stock/${tab}`, {
      method: "POST",
      headers: { "x-admin-key": ADMIN_KEY },
      body: fd
    });
    if (!res.ok) { alert("Upload failed"); return; }
    const j = await res.json();
    alert(`Uploaded ${j.added} ${tab}`);
    setFiles(null); (document.getElementById("filepick") as HTMLInputElement).value = "";
  }

  return (
    <div className="p-6 space-y-6">
      <div className="flex items-center justify-between">
        <h1 className="text-2xl font-bold">Admin · Stock Uploader</h1>
        <div className="flex gap-2">
          <button className={`px-3 py-1.5 rounded-lg border ${tab==="photos"?"bg-white/10":""}`} onClick={()=>setTab("photos")}>Photos</button>
          <button className={`px-3 py-1.5 rounded-lg border ${tab==="mockups"?"bg-white/10":""}`} onClick={()=>setTab("mockups")}>Mockups</button>
          <button className="px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white" onClick={upload}>Upload</button>
        </div>
      </div>

      <div className="grid lg:grid-cols-[360px,1fr] gap-6">
        <aside className="rounded-xl border border-white/10 bg-slate-900/60 p-4 space-y-3">
          <div>
            <div className="text-sm text-slate-400 mb-1">Files</div>
            <input id="filepick" type="file" accept="image/*" multiple onChange={(e)=>setFiles(e.target.files)} />
            <div className="text-xs text-slate-400 mt-1">Tip: upload JPG/PNG; max ~25MB each.</div>
          </div>
          <label className="text-xs block">Tags (comma)</label>
          <input value={tags} onChange={(e)=>setTags(e.target.value)} className="mt-1 w-full rounded bg-slate-950/60 border border-white/10 p-2 text-sm"/>
          <label className="text-xs block">Category</label>
          <input value={category} onChange={(e)=>setCategory(e.target.value)} className="mt-1 w-full rounded bg-slate-950/60 border border-white/10 p-2 text-sm"/>
        </aside>

        <section className="rounded-xl border border-white/10 bg-slate-900/60 p-4">
          <div className="text-sm text-slate-400">These items will appear on the public <b>{tab==="photos"?"Stock Photos":"Mockups"}</b> page instantly after upload.</div>
        </section>
      </div>
    </div>
  );
}