mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-12 14:16:04 -05:00
Refactor HACKY_resolvePicture away (#2606)
This commit is contained in:
171
scripts/migrate-tournament-logos-to-s3.ts
Normal file
171
scripts/migrate-tournament-logos-to-s3.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import "dotenv/config";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { Readable } from "node:stream";
|
||||
import { db } from "~/db/sql";
|
||||
import { uploadStreamToS3 } from "~/features/img-upload/s3.server";
|
||||
import { databaseTimestampNow } from "~/utils/dates";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { logger } from "~/utils/logger";
|
||||
|
||||
const TOURNAMENT_LOGO_PATH = "public/static-assets/img/tournament-logos";
|
||||
const ADMIN_ID = 1;
|
||||
|
||||
const LOGO_IDENTIFIER_TO_PATTERN: Record<string, string[]> = {
|
||||
sf: ["sendouq"],
|
||||
pp: ["paddling pool"],
|
||||
itz: ["in the zone"],
|
||||
pn: ["picnic"],
|
||||
pg: ["proving grounds"],
|
||||
tc: ["triton"],
|
||||
sos: ["swim or sink"],
|
||||
ftiu: ["from the ink up"],
|
||||
cc: ["coral clash"],
|
||||
lu: ["level up"],
|
||||
a41: ["all 4 one"],
|
||||
fb: ["fry basket"],
|
||||
d: ["the depths"],
|
||||
e: ["eclipse"],
|
||||
hc: ["homecoming"],
|
||||
bio: ["bad ideas"],
|
||||
ai: ["tenoch"],
|
||||
mm: ["megalodon monday"],
|
||||
ho: ["heaven 2 ocean"],
|
||||
kr: ["kraken royale"],
|
||||
mr: ["menu royale"],
|
||||
bc: ["barracuda co"],
|
||||
ci: ["crimson ink"],
|
||||
me: ["mesozoic mayhem"],
|
||||
ros: ["rain or shine"],
|
||||
sj: ["squid junction"],
|
||||
ss: ["silly sausage"],
|
||||
ul: ["united-lan"],
|
||||
sc: ["soul cup"],
|
||||
};
|
||||
|
||||
async function uploadLogoFile(
|
||||
filePath: string,
|
||||
identifier: string,
|
||||
): Promise<string> {
|
||||
logger.info(`Uploading ${identifier}.png to S3...`);
|
||||
|
||||
const fileBuffer = fs.readFileSync(filePath);
|
||||
const stream = Readable.from(fileBuffer);
|
||||
|
||||
const fileName = `tournament-logo-${identifier}.png`;
|
||||
|
||||
const s3Url = await uploadStreamToS3(stream, fileName);
|
||||
|
||||
invariant(s3Url, `Failed to upload ${identifier}.png to S3`);
|
||||
|
||||
logger.info(`Uploaded ${identifier}.png to ${s3Url}`);
|
||||
return fileName;
|
||||
}
|
||||
|
||||
async function createImageRecord(url: string): Promise<number> {
|
||||
const result = await db
|
||||
.insertInto("UnvalidatedUserSubmittedImage")
|
||||
.values({
|
||||
url,
|
||||
validatedAt: databaseTimestampNow(),
|
||||
submitterUserId: ADMIN_ID,
|
||||
})
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return result.id;
|
||||
}
|
||||
|
||||
async function updateCalendarEvents(
|
||||
imageId: number,
|
||||
patterns: string[],
|
||||
): Promise<number> {
|
||||
const events = await db
|
||||
.selectFrom("CalendarEvent")
|
||||
.select(["id", "name"])
|
||||
.where("avatarImgId", "is", null)
|
||||
.execute();
|
||||
|
||||
let updateCount = 0;
|
||||
|
||||
for (const event of events) {
|
||||
const normalizedEventName = event.name.toLowerCase();
|
||||
const matches = patterns.some((pattern) =>
|
||||
normalizedEventName.includes(pattern),
|
||||
);
|
||||
|
||||
if (matches) {
|
||||
await db
|
||||
.updateTable("CalendarEvent")
|
||||
.set({ avatarImgId: imageId })
|
||||
.where("id", "=", event.id)
|
||||
.execute();
|
||||
|
||||
logger.info(`Updated CalendarEvent ${event.id}: "${event.name}"`);
|
||||
updateCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return updateCount;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
logger.info("Starting tournament logo migration to S3...");
|
||||
|
||||
const defaultLogoPath = path.join(TOURNAMENT_LOGO_PATH, "default.png");
|
||||
if (fs.existsSync(defaultLogoPath)) {
|
||||
logger.info("\n=== Uploading default tournament logo ===");
|
||||
const defaultS3Url = await uploadLogoFile(defaultLogoPath, "default");
|
||||
const defaultImageId = await createImageRecord(defaultS3Url);
|
||||
logger.info(
|
||||
`Created UnvalidatedUserSubmittedImage record for default logo with ID ${defaultImageId}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
const logoFiles = fs
|
||||
.readdirSync(TOURNAMENT_LOGO_PATH)
|
||||
.filter((file) => file.endsWith(".png") && file !== "default.png");
|
||||
|
||||
logger.info(`Found ${logoFiles.length} logo files to migrate`);
|
||||
|
||||
let totalUpdated = 0;
|
||||
|
||||
for (const file of logoFiles) {
|
||||
const identifier = file.replace(".png", "");
|
||||
const patterns = LOGO_IDENTIFIER_TO_PATTERN[identifier];
|
||||
|
||||
if (!patterns) {
|
||||
logger.warn(`No pattern mapping found for ${identifier}, skipping...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const filePath = path.join(TOURNAMENT_LOGO_PATH, file);
|
||||
|
||||
const s3Url = await uploadLogoFile(filePath, identifier);
|
||||
|
||||
const imageId = await createImageRecord(s3Url);
|
||||
logger.info(
|
||||
`Created UnvalidatedUserSubmittedImage record with ID ${imageId}`,
|
||||
);
|
||||
|
||||
const updatedCount = await updateCalendarEvents(imageId, patterns);
|
||||
totalUpdated += updatedCount;
|
||||
|
||||
logger.info(
|
||||
`Updated ${updatedCount} CalendarEvent records for ${identifier}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
logger.info("\n=== Migration Complete ===");
|
||||
logger.info(`Total CalendarEvent records updated: ${totalUpdated}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => {
|
||||
logger.info("Script completed successfully");
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error("Script failed:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
48
scripts/seed-art-urls.ts
Normal file
48
scripts/seed-art-urls.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
export const SEED_ART_URLS = [
|
||||
"https://images.unsplash.com/photo-1611627474565-2367887415d1?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NTA2&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1625120742520-3f085b6894ba?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NTI2&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1656695607245-9686ce8e1a91?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NTQ1&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1673011526786-c7bf154d2c6d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NTY5&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1643833994700-059713434c71?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NTc2&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1541425284102-3d2c49dcb2bc?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NTk5&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1526946366170-7a81b443c4e6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NjA4&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1551368003-4d96079d0a99?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NjIz&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1595960684234-49d2a004e753?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NjMw&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1676275062470-4b628cf1ce01?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU1NjM5&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1602099081031-767e09dfdbad?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NDYz&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1547532182-bf296f6be875?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NDY5&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1601549838695-57580707e367?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NDc2&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1595361315899-72a291112b7b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NDgy&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1676275061266-a28f2f3f4552?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NDg4&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/44/C3EWdWzT8imxs0fKeKoC_blackforrest.JPG?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NDk1&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1628425242605-a0039d89e8b2?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NTAx&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1542917118-105d7d34b9ca?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NTEw&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1601549838695-57580707e367?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NTE3&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1660583490803-75c0307c805b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8ZG9nLDF8fHx8fHwxNjg4NTU2NTQ2&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1490042706304-06c664f6fd9a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8ZG9nLDF8fHx8fHwxNjg4NTU2NTU4&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1676998652985-fd74c7b2a8d5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8ZG9nLDF8fHx8fHwxNjg4NTU2NTY0&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1470390356535-d19bbf47bacb?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8ZG9nLDF8fHx8fHwxNjg4NTU2NTcx&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1503256207526-0d5d80fa2f47?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8ZG9nLDF8fHx8fHwxNjg4NTU2Njcy&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1604495589307-973bd87d7fa3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8ZG9nLDF8fHx8fHwxNjg4NTU2Njg2&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1563476651637-3e5c5941d432?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NzEw&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1551567819-eef106c515b6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NzE2&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
"https://images.unsplash.com/photo-1553536590-d28c5d5dee92?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8bmF0dXJlLDF8fHx8fHwxNjg4NTU2NzIx&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080",
|
||||
];
|
||||
|
||||
export function getArtFilename(index: number): string {
|
||||
return `art-${index}.jpg`;
|
||||
}
|
||||
|
||||
export const SEED_TEAM_IMAGES = [
|
||||
{ filename: "alliance-rogue.png", teamId: 1 },
|
||||
{ filename: "default.png", teamId: null },
|
||||
];
|
||||
|
||||
export const SEED_TOURNAMENT_IMAGES = [
|
||||
{ filename: "picnic.png", tournamentId: 1 },
|
||||
{ filename: "in-the-zone.png", tournamentId: 2 },
|
||||
{ filename: "paddling-pool.png", tournamentId: 3 },
|
||||
{ filename: "swim-or-sink.png", tournamentId: 4 },
|
||||
{ filename: "the-depths.png", tournamentId: 5 },
|
||||
{ filename: "luti.png", tournamentId: 6 },
|
||||
];
|
||||
244
scripts/seed-images.ts
Normal file
244
scripts/seed-images.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import "dotenv/config";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { Readable } from "node:stream";
|
||||
import { S3 } from "@aws-sdk/client-s3";
|
||||
import { Upload } from "@aws-sdk/lib-storage";
|
||||
import { logger } from "~/utils/logger";
|
||||
import {
|
||||
getArtFilename,
|
||||
SEED_ART_URLS,
|
||||
SEED_TEAM_IMAGES,
|
||||
SEED_TOURNAMENT_IMAGES,
|
||||
} from "./seed-art-urls";
|
||||
|
||||
async function checkMinioConnection(): Promise<boolean> {
|
||||
try {
|
||||
const {
|
||||
STORAGE_END_POINT,
|
||||
STORAGE_ACCESS_KEY,
|
||||
STORAGE_SECRET,
|
||||
STORAGE_REGION,
|
||||
STORAGE_BUCKET,
|
||||
} = process.env;
|
||||
|
||||
if (
|
||||
!(
|
||||
STORAGE_ACCESS_KEY &&
|
||||
STORAGE_END_POINT &&
|
||||
STORAGE_SECRET &&
|
||||
STORAGE_REGION &&
|
||||
STORAGE_BUCKET
|
||||
)
|
||||
) {
|
||||
logger.warn("Storage configuration not found in environment");
|
||||
return false;
|
||||
}
|
||||
|
||||
const s3 = new S3({
|
||||
endpoint: STORAGE_END_POINT,
|
||||
forcePathStyle: false,
|
||||
credentials: {
|
||||
accessKeyId: STORAGE_ACCESS_KEY,
|
||||
secretAccessKey: STORAGE_SECRET,
|
||||
},
|
||||
region: STORAGE_REGION,
|
||||
});
|
||||
|
||||
await s3.headBucket({ Bucket: STORAGE_BUCKET });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadImage(url: string): Promise<Buffer> {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to download image: ${response.statusText}`);
|
||||
}
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return Buffer.from(arrayBuffer);
|
||||
}
|
||||
|
||||
async function readLocalImage(filename: string): Promise<Buffer> {
|
||||
const imagePath = join(process.cwd(), "app", "db", "seed", "img", filename);
|
||||
return await readFile(imagePath);
|
||||
}
|
||||
|
||||
async function uploadToMinio(
|
||||
imageBuffer: Buffer,
|
||||
filename: string,
|
||||
): Promise<string> {
|
||||
const {
|
||||
STORAGE_END_POINT,
|
||||
STORAGE_ACCESS_KEY,
|
||||
STORAGE_SECRET,
|
||||
STORAGE_REGION,
|
||||
STORAGE_BUCKET,
|
||||
} = process.env;
|
||||
|
||||
const s3 = new S3({
|
||||
endpoint: STORAGE_END_POINT,
|
||||
forcePathStyle: false,
|
||||
credentials: {
|
||||
accessKeyId: STORAGE_ACCESS_KEY!,
|
||||
secretAccessKey: STORAGE_SECRET!,
|
||||
},
|
||||
region: STORAGE_REGION,
|
||||
});
|
||||
|
||||
const stream = Readable.from(imageBuffer);
|
||||
|
||||
const upload = new Upload({
|
||||
client: s3,
|
||||
params: {
|
||||
Bucket: STORAGE_BUCKET!,
|
||||
Key: filename,
|
||||
Body: stream,
|
||||
ACL: "public-read",
|
||||
},
|
||||
});
|
||||
|
||||
await upload.done();
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
async function fileExistsInMinio(filename: string): Promise<boolean> {
|
||||
try {
|
||||
const {
|
||||
STORAGE_END_POINT,
|
||||
STORAGE_ACCESS_KEY,
|
||||
STORAGE_SECRET,
|
||||
STORAGE_REGION,
|
||||
STORAGE_BUCKET,
|
||||
} = process.env;
|
||||
|
||||
const s3 = new S3({
|
||||
endpoint: STORAGE_END_POINT,
|
||||
forcePathStyle: false,
|
||||
credentials: {
|
||||
accessKeyId: STORAGE_ACCESS_KEY!,
|
||||
secretAccessKey: STORAGE_SECRET!,
|
||||
},
|
||||
region: STORAGE_REGION,
|
||||
});
|
||||
|
||||
await s3.headObject({
|
||||
Bucket: STORAGE_BUCKET!,
|
||||
Key: filename,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function seedImages(): Promise<void> {
|
||||
const minioAvailable = await checkMinioConnection();
|
||||
|
||||
if (!minioAvailable) {
|
||||
logger.warn(
|
||||
"⚠️ Minio is not available. Skipping image seeding. Make sure Docker is running if you want to seed images.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(`📥 Processing ${SEED_ART_URLS.length} art images`);
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
let skippedCount = 0;
|
||||
|
||||
for (let i = 0; i < SEED_ART_URLS.length; i++) {
|
||||
const url = SEED_ART_URLS[i];
|
||||
const filename = getArtFilename(i);
|
||||
const smallFilename = filename.replace(/\.(\w+)$/, "-small.$1");
|
||||
|
||||
try {
|
||||
const regularExists = await fileExistsInMinio(filename);
|
||||
const smallExists = await fileExistsInMinio(smallFilename);
|
||||
|
||||
if (regularExists && smallExists) {
|
||||
skippedCount++;
|
||||
logger.info(
|
||||
` ↷ Files ${filename} and ${smallFilename} already exist in Minio`,
|
||||
);
|
||||
} else {
|
||||
const imageBuffer = await downloadImage(url);
|
||||
|
||||
if (!regularExists) {
|
||||
logger.info(` Uploading ${filename} to Minio...`);
|
||||
await uploadToMinio(imageBuffer, filename);
|
||||
}
|
||||
|
||||
if (!smallExists) {
|
||||
logger.info(` Uploading ${smallFilename} to Minio...`);
|
||||
await uploadToMinio(imageBuffer, smallFilename);
|
||||
}
|
||||
|
||||
successCount++;
|
||||
}
|
||||
} catch (err) {
|
||||
failCount++;
|
||||
logger.error(
|
||||
` ✗ Failed to process ${filename}: ${(err as Error).message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`\n✅ Art image seeding complete: ${successCount} uploaded, ${skippedCount} already existed, ${failCount} failed`,
|
||||
);
|
||||
|
||||
logger.info(
|
||||
`\n📥 Processing ${SEED_TEAM_IMAGES.length} team images and ${SEED_TOURNAMENT_IMAGES.length} tournament images`,
|
||||
);
|
||||
|
||||
const localImages = [...SEED_TEAM_IMAGES, ...SEED_TOURNAMENT_IMAGES];
|
||||
|
||||
let localSuccessCount = 0;
|
||||
let localFailCount = 0;
|
||||
let localSkippedCount = 0;
|
||||
|
||||
for (const { filename } of localImages) {
|
||||
const smallFilename = filename.replace(/\.(\w+)$/, "-small.$1");
|
||||
|
||||
try {
|
||||
const regularExists = await fileExistsInMinio(filename);
|
||||
const smallExists = await fileExistsInMinio(smallFilename);
|
||||
|
||||
if (regularExists && smallExists) {
|
||||
localSkippedCount++;
|
||||
logger.info(
|
||||
` ↷ Files ${filename} and ${smallFilename} already exist in Minio`,
|
||||
);
|
||||
} else {
|
||||
const imageBuffer = await readLocalImage(filename);
|
||||
|
||||
if (!regularExists) {
|
||||
logger.info(` Uploading ${filename} to Minio...`);
|
||||
await uploadToMinio(imageBuffer, filename);
|
||||
}
|
||||
|
||||
if (!smallExists) {
|
||||
logger.info(` Uploading ${smallFilename} to Minio...`);
|
||||
await uploadToMinio(imageBuffer, smallFilename);
|
||||
}
|
||||
|
||||
localSuccessCount++;
|
||||
}
|
||||
} catch (err) {
|
||||
localFailCount++;
|
||||
logger.error(
|
||||
` ✗ Failed to process ${filename}: ${(err as Error).message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`\n✅ Local image seeding complete: ${localSuccessCount} uploaded, ${localSkippedCount} already existed, ${localFailCount} failed`,
|
||||
);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import fs from "node:fs";
|
||||
import { seed } from "~/db/seed";
|
||||
import { db } from "~/db/sql";
|
||||
import { logger } from "~/utils/logger";
|
||||
import { seedImages } from "./seed-images";
|
||||
|
||||
async function main() {
|
||||
// Step 1: Create .env if it doesn't exist
|
||||
@@ -33,6 +34,14 @@ async function main() {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Seed images to Minio
|
||||
logger.info("🖼️ Seeding images to Minio...");
|
||||
try {
|
||||
await seedImages();
|
||||
} catch (err) {
|
||||
logger.error("Error seeding images:", (err as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
Reference in New Issue
Block a user