feat(deploy): production deployment setup and fixes

- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma
  binaryTargets for openssl 3.0
- nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal
  cron, http->https redirect
- prisma: add origin_goods.delisted migration, sync missing schema
  (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz
- api: CORS wildcard reflection, helmet CORP cross-origin, price
  backfill in persistProductDetail, categoryIcon ancestor fallback,
  mediaByColor per-color gallery in public goods detail
- admin: /admin base path (vite + router)
- import-data.mjs: udt_name casting, serial sequence advance fix
This commit is contained in:
yeuimu
2026-08-26 14:23:09 +08:00
parent be0b90e68f
commit 6c61a4e871
982 changed files with 74156 additions and 179393 deletions
@@ -1,54 +1,54 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const countryIcons: Record<string, string> = {
: '/assets/product-center/countries/us.png',
: '/assets/product-center/countries/jp.png',
西: '/assets/product-center/countries/mx.png',
西: '/assets/product-center/countries/br.png',
: '/assets/product-center/countries/middle-east.png',
: '/assets/product-center/countries/pl.png',
西: '/assets/product-center/countries/es.png',
: '/assets/product-center/countries/de.png',
: '/assets/product-center/countries/it.png',
: '/assets/product-center/countries/gb.png',
: '/assets/product-center/countries/ca.png',
: '/assets/product-center/countries/au.png',
: '/assets/product-center/countries/kr.png',
};
const categoryIcons: Record<string, string> = {
: '/assets/product-center/categories/men.svg',
: '/assets/product-center/categories/women.svg',
: '/assets/product-center/categories/children.svg',
: '/assets/product-center/categories/home.svg',
};
async function updateExistingIcons(
entries: Record<string, string>,
update: (name: string, icon: string) => Promise<{ count: number }>,
): Promise<number> {
let updated = 0;
for (const [name, icon] of Object.entries(entries)) {
const result = await update(name, icon);
updated += result.count;
}
return updated;
}
async function main(): Promise<void> {
const countries = await updateExistingIcons(countryIcons, (countryName, countryIcon) =>
prisma.country.updateMany({ where: { countryName }, data: { countryIcon } }),
);
const categories = await updateExistingIcons(categoryIcons, (categoryName, categoryIcon) =>
prisma.category.updateMany({
where: { categoryName, parentCategoryId: null },
data: { categoryIcon },
}),
);
console.log(`Configured ${countries} countries and ${categories} root categories.`);
}
main()
.finally(async () => prisma.$disconnect());
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const countryIcons: Record<string, string> = {
: '/assets/product-center/countries/us.png',
: '/assets/product-center/countries/jp.png',
西: '/assets/product-center/countries/mx.png',
西: '/assets/product-center/countries/br.png',
: '/assets/product-center/countries/middle-east.png',
: '/assets/product-center/countries/pl.png',
西: '/assets/product-center/countries/es.png',
: '/assets/product-center/countries/de.png',
: '/assets/product-center/countries/it.png',
: '/assets/product-center/countries/gb.png',
: '/assets/product-center/countries/ca.png',
: '/assets/product-center/countries/au.png',
: '/assets/product-center/countries/kr.png',
};
const categoryIcons: Record<string, string> = {
: '/assets/product-center/categories/men.svg',
: '/assets/product-center/categories/women.svg',
: '/assets/product-center/categories/children.svg',
: '/assets/product-center/categories/home.svg',
};
async function updateExistingIcons(
entries: Record<string, string>,
update: (name: string, icon: string) => Promise<{ count: number }>,
): Promise<number> {
let updated = 0;
for (const [name, icon] of Object.entries(entries)) {
const result = await update(name, icon);
updated += result.count;
}
return updated;
}
async function main(): Promise<void> {
const countries = await updateExistingIcons(countryIcons, (countryName, countryIcon) =>
prisma.country.updateMany({ where: { countryName }, data: { countryIcon } }),
);
const categories = await updateExistingIcons(categoryIcons, (categoryName, categoryIcon) =>
prisma.category.updateMany({
where: { categoryName, parentCategoryId: null },
data: { categoryIcon },
}),
);
console.log(`Configured ${countries} countries and ${categories} root categories.`);
}
main()
.finally(async () => prisma.$disconnect());