Social assets: no auto table create, manual SQL only
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -74,7 +74,11 @@
|
|||||||
## QRMaster Social Asset Hosting (no-deploy image URLs)
|
## QRMaster Social Asset Hosting (no-deploy image URLs)
|
||||||
- `POST https://qrmaster.net/api/social-assets` (header `x-admin-key: <TIKTOK_ADMIN_KEY>`) with JSON `{ "files": [{ "filename", "mimeType", "dataBase64" }] }` stores images in PostgreSQL and returns public `https://qrmaster.net/api/social-assets/<id>` URLs on the verified domain.
|
- `POST https://qrmaster.net/api/social-assets` (header `x-admin-key: <TIKTOK_ADMIN_KEY>`) with JSON `{ "files": [{ "filename", "mimeType", "dataBase64" }] }` stores images in PostgreSQL and returns public `https://qrmaster.net/api/social-assets/<id>` URLs on the verified domain.
|
||||||
- `GET /api/social-assets/<id>` serves the file publicly (immutable cache); `GET /api/social-assets` (admin) lists the last 100 assets; `DELETE /api/social-assets/<id>` (admin) removes one.
|
- `GET /api/social-assets/<id>` serves the file publicly (immutable cache); `GET /api/social-assets` (admin) lists the last 100 assets; `DELETE /api/social-assets/<id>` (admin) removes one.
|
||||||
- Allowed types: jpeg/png/webp/mp4, max 10 MB per file. Table `SocialAsset` is auto-created on first upload (`CREATE TABLE IF NOT EXISTS`, in line with the no-migrations policy).
|
- Allowed types: jpeg/png/webp/mp4, max 10 MB per file.
|
||||||
|
- The `SocialAsset` table must be created manually on the server (one-time, no auto-create in the route). Run on the server:
|
||||||
|
```bash
|
||||||
|
docker-compose exec db psql -U postgres -d qrmaster -c 'CREATE TABLE IF NOT EXISTS "SocialAsset" ("id" TEXT PRIMARY KEY, "filename" TEXT NOT NULL, "mimeType" TEXT NOT NULL, "data" BYTEA NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP);'
|
||||||
|
```
|
||||||
- Typical carousel flow: upload slides here → pass the returned URLs as `photos` to `POST /api/tiktok/upload` → check via `/api/tiktok/upload/status?publish_id=...`. No app deploy needed per carousel.
|
- Typical carousel flow: upload slides here → pass the returned URLs as `photos` to `POST /api/tiktok/upload` → check via `/api/tiktok/upload/status?publish_id=...`. No app deploy needed per carousel.
|
||||||
|
|
||||||
## Refresh Behavior
|
## Refresh Behavior
|
||||||
|
|||||||
@@ -22,19 +22,9 @@ const ALLOWED_MIME_TYPES = new Set([
|
|||||||
'video/mp4',
|
'video/mp4',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Schema changes in this repo are applied as raw SQL against the running
|
// The "SocialAsset" table is NOT created by this route. Apply the schema
|
||||||
// database (no Prisma migrations); this keeps the route self-bootstrapping.
|
// manually on the server (npm run docker:db), see
|
||||||
async function ensureTable() {
|
// docs/automations/social-accounts-and-jobs.md.
|
||||||
await db.$executeRawUnsafe(`
|
|
||||||
CREATE TABLE IF NOT EXISTS "SocialAsset" (
|
|
||||||
"id" TEXT PRIMARY KEY,
|
|
||||||
"filename" TEXT NOT NULL,
|
|
||||||
"mimeType" TEXT NOT NULL,
|
|
||||||
"data" BYTEA NOT NULL,
|
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
if (!isAdminRequest(request)) {
|
if (!isAdminRequest(request)) {
|
||||||
@@ -82,8 +72,6 @@ export async function POST(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ensureTable();
|
|
||||||
|
|
||||||
const baseUrl = process.env.NEXTAUTH_URL || 'https://qrmaster.net';
|
const baseUrl = process.env.NEXTAUTH_URL || 'https://qrmaster.net';
|
||||||
const assets = [];
|
const assets = [];
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@@ -114,7 +102,6 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ensureTable();
|
|
||||||
const assets = await db.socialAsset.findMany({
|
const assets = await db.socialAsset.findMany({
|
||||||
select: { id: true, filename: true, mimeType: true, createdAt: true },
|
select: { id: true, filename: true, mimeType: true, createdAt: true },
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
|
|||||||
Reference in New Issue
Block a user