feat: implement daily character guessing game with local storage and hint system
- Added character selection and history management using local storage. - Implemented hint system that unlocks based on the number of guesses. - Enhanced UI with animations for hint unlocks and special win conditions. - Created a server endpoint to record wins in the database.
This commit is contained in:
145
scripts/daily-characters.json
Normal file
145
scripts/daily-characters.json
Normal file
@@ -0,0 +1,145 @@
|
||||
[
|
||||
"aladdin_aladdin",
|
||||
"alvida_alvida",
|
||||
"aramaki_aramaki",
|
||||
"arlong_arlong",
|
||||
"ashura_doji_ashura_doji",
|
||||
"baby_5_baby_5",
|
||||
"baggy_baggy",
|
||||
"bartholomew_kuma_bartholomew_kuma",
|
||||
"bartolomeo_bartolomeo",
|
||||
"basil_hawkins_basil_hawkins",
|
||||
"batman_batman",
|
||||
"bellamy_bellamy",
|
||||
"belo_betty_belo_betty",
|
||||
"ben_beckman_ben_beckman",
|
||||
"bentham_bentham",
|
||||
"bepo_bepo",
|
||||
"black_maria_black_maria",
|
||||
"boa_hancock_boa_hancock",
|
||||
"boa_marigold_boa_marigold",
|
||||
"boa_sandersonia_boa_sandersonia",
|
||||
"borsalino_borsalino",
|
||||
"brogy_brogy",
|
||||
"brook_brook",
|
||||
"camie_camie",
|
||||
"capone_bege_capone_bege",
|
||||
"caribou_caribou",
|
||||
"carrot_carrot",
|
||||
"catarina_devon_catarina_devon",
|
||||
"cavendish_cavendish",
|
||||
"cesar_clown_cesar_clown",
|
||||
"chinjao_chinjao",
|
||||
"coby_coby",
|
||||
"corazon_corazon",
|
||||
"crocodile_crocodile",
|
||||
"crocus_crocus",
|
||||
"curly_dadan_curly_dadan",
|
||||
"dalton_dalton",
|
||||
"daz_bones_daz_bones",
|
||||
"denjiro_denjiro",
|
||||
"diamante_diamante",
|
||||
"doc_q_doc_q",
|
||||
"don_quichotte_doflamingo_don_quichotte_doflamingo",
|
||||
"don_quichotte_rossinante_don_quichotte_rossinante",
|
||||
"dorry_dorry",
|
||||
"dracule_mihawk_dracule_mihawk",
|
||||
"duval_duval",
|
||||
"edward_newgate_edward_newgate",
|
||||
"edward_weevil_edward_weevil",
|
||||
"emporio_ivankov_emporio_ivankov",
|
||||
"enel_enel",
|
||||
"eustass_kid_eustass_kid",
|
||||
"fisher_tiger_fisher_tiger",
|
||||
"foxy_foxy",
|
||||
"franky_franky",
|
||||
"fujitora_fujitora",
|
||||
"gan_forr_gan_forr",
|
||||
"gecko_moria_gecko_moria",
|
||||
"gin_gin",
|
||||
"gol_d_roger_gol_d_roger",
|
||||
"haguar_d_sauro_haguar_d_sauro",
|
||||
"hajrudin_hajrudin",
|
||||
"hannyabal_hannyabal",
|
||||
"hatchan_hatchan",
|
||||
"hina_hina",
|
||||
"hody_jones_hody_jones",
|
||||
"hyogoro_hyogoro",
|
||||
"iceburg_iceburg",
|
||||
"imu_imu",
|
||||
"inazuma_inazuma",
|
||||
"inuarashi_inuarashi",
|
||||
"issho_issho",
|
||||
"izo_izo",
|
||||
"jabra_jabra",
|
||||
"jack_jack",
|
||||
"jesus_burgess_jesus_burgess",
|
||||
"jewelry_bonney_jewelry_bonney",
|
||||
"jinbei_jinbei",
|
||||
"joy_boy_joy_boy",
|
||||
"kaidou_kaidou",
|
||||
"kaku_kaku",
|
||||
"kalgara_kalgara",
|
||||
"kalifa_kalifa",
|
||||
"karasu_karasu",
|
||||
"karoo_karoo",
|
||||
"kawamatsu_kawamatsu",
|
||||
"kaya_kaya",
|
||||
"killer_killer",
|
||||
"kinemon_kinemon",
|
||||
"koala_koala",
|
||||
"koby_koby",
|
||||
"kong_kong",
|
||||
"kozuki_hiyori_kozuki_hiyori",
|
||||
"kozuki_momonosuke_kozuki_momonosuke",
|
||||
"kozuki_oden_kozuki_oden",
|
||||
"krieg_krieg",
|
||||
"kureha_kureha",
|
||||
"kuro_kuro",
|
||||
"kurozumi_orochi_kurozumi_orochi",
|
||||
"kuzan_kuzan",
|
||||
"kyros_kyros",
|
||||
"laboon_laboon",
|
||||
"laffitte_laffitte",
|
||||
"lao_g_lao_g",
|
||||
"leo_leo",
|
||||
"lindbergh_lindbergh",
|
||||
"loki_loki",
|
||||
"lucky_roux_lucky_roux",
|
||||
"magellan_magellan",
|
||||
"makino_makino",
|
||||
"marco_marco",
|
||||
"marshall_d_teach_marshall_d_teach",
|
||||
"monkey_d_dragon_monkey_d_dragon",
|
||||
"monkey_d_garp_monkey_d_garp",
|
||||
"monkey_d_luffy_monkey_d_luffy",
|
||||
"montblanc_norland_montblanc_norland",
|
||||
"morgans_morgans",
|
||||
"morley_morley",
|
||||
"mr_3_mr_3",
|
||||
"nami_nami",
|
||||
"nefertari_cobra_nefertari_cobra",
|
||||
"nefertari_vivi_nefertari_vivi",
|
||||
"nekomamushi_nekomamushi",
|
||||
"neptune_neptune",
|
||||
"nico_robin_nico_robin",
|
||||
"oars_oars",
|
||||
"orlumbus_orlumbus",
|
||||
"otohime_otohime",
|
||||
"page_one_page_one",
|
||||
"pandaman_pandaman",
|
||||
"pekoms_pekoms",
|
||||
"pell_pell",
|
||||
"perona_perona",
|
||||
"pica_pica",
|
||||
"portgas_d_ace_portgas_d_ace",
|
||||
"queen_queen",
|
||||
"raizo_raizo",
|
||||
"rebecca_rebecca",
|
||||
"rob_lucci_rob_lucci",
|
||||
"rocks_d_xebec_rocks_d_xebec",
|
||||
"roronoa_zoro_roronoa_zoro",
|
||||
"sabo_sabo",
|
||||
"vegapunk_vegapunk",
|
||||
"yamato_yamato"
|
||||
]
|
||||
406
scripts/import-json.ts
Normal file
406
scripts/import-json.ts
Normal file
@@ -0,0 +1,406 @@
|
||||
import { createClient } from '@libsql/client';
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { sql, eq } from 'drizzle-orm';
|
||||
import fs from 'fs';
|
||||
import { arc, character, devilFruit, characterScrapeValidation, type DevilFruitType } from '../src/lib/server/db/schema';
|
||||
|
||||
type ArcRecord = {
|
||||
id: string;
|
||||
name: string;
|
||||
startChapter: number;
|
||||
endChapter?: number | null;
|
||||
url?: string | null;
|
||||
};
|
||||
|
||||
type DevilFruitRecord = {
|
||||
id: string;
|
||||
name: string;
|
||||
type?: DevilFruitType | string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
|
||||
type CharacterRecord = {
|
||||
id: string;
|
||||
name: string;
|
||||
gender?: string | null;
|
||||
age?: number | null;
|
||||
affiliations?: string[] | string | null;
|
||||
devilFruitId?: string | null;
|
||||
hakiObservation?: boolean;
|
||||
hakiArmament?: boolean;
|
||||
hakiConqueror?: boolean;
|
||||
bounty?: number | null;
|
||||
height?: number | null;
|
||||
origin?: string | null;
|
||||
firstAppearance?: number;
|
||||
pictureUrl?: string | null;
|
||||
epithets?: string[] | string | null;
|
||||
status?: string | null;
|
||||
arcId?: string | null;
|
||||
url?: string | null;
|
||||
};
|
||||
|
||||
const DATABASE_URL = process.env.DATABASE_URL || 'file:local.db';
|
||||
|
||||
const client = createClient({ url: DATABASE_URL });
|
||||
const db = drizzle(client);
|
||||
|
||||
function readJsonFile<T>(path: string): T[] | null {
|
||||
if (!fs.existsSync(path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(path, 'utf-8');
|
||||
return JSON.parse(content) as T[];
|
||||
}
|
||||
|
||||
function toNullable<T>(value: T | undefined | null | ''): T | null {
|
||||
return value === undefined || value === null || value === '' ? null : value;
|
||||
}
|
||||
|
||||
function toJsonArray(value: string[] | string | null | undefined): string[] | null {
|
||||
if (Array.isArray(value)) {
|
||||
return value.length > 0 ? value : null;
|
||||
}
|
||||
|
||||
if (typeof value === 'string' && value.trim() !== '') {
|
||||
if (value.trim().startsWith('[')) {
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return Array.isArray(parsed) ? parsed : [value];
|
||||
} catch {
|
||||
return [value];
|
||||
}
|
||||
}
|
||||
|
||||
const splitValues = value
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
return splitValues.length > 0 ? splitValues : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function toDevilFruitType(value: DevilFruitType | string | null | undefined): DevilFruitType | null {
|
||||
if (!value) return null;
|
||||
if (value === 'Paramecia' || value === 'Zoan' || value === 'Logia' || value === 'Unknown') {
|
||||
return value;
|
||||
}
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
function toNumber(value: string | number | null | undefined): number | null {
|
||||
if (value === null || value === undefined || value === '') return null;
|
||||
const num = typeof value === 'string' ? parseFloat(value) : value;
|
||||
return isNaN(num) ? null : num;
|
||||
}
|
||||
|
||||
function getErrorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
function logSqlOnError(statement: { sql: string; params: unknown[] } | null): void {
|
||||
if (!statement) return;
|
||||
console.error(` SQL: ${statement.sql}`);
|
||||
console.error(` Params: ${JSON.stringify(statement.params)}`);
|
||||
}
|
||||
|
||||
function transformCharacterData(item: CharacterRecord) {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
gender: toNullable(item.gender),
|
||||
age: toNullable(item.age),
|
||||
affiliations: toJsonArray(item.affiliations),
|
||||
devilFruitId: toNullable(item.devilFruitId),
|
||||
hakiObservation: !!item.hakiObservation,
|
||||
hakiArmament: !!item.hakiArmament,
|
||||
hakiConqueror: !!item.hakiConqueror,
|
||||
bounty: item.bounty ?? 0,
|
||||
height: toNumber(item.height as any),
|
||||
origin: toNullable(item.origin),
|
||||
firstAppearance: item.firstAppearance ?? 0,
|
||||
pictureUrl: toNullable(item.pictureUrl),
|
||||
epithets: toJsonArray(item.epithets),
|
||||
status: toNullable(item.status),
|
||||
arcId: toNullable(item.arcId),
|
||||
url: toNullable(item.url)
|
||||
};
|
||||
}
|
||||
|
||||
function hasChanged(jsonData: any, dbData: any): boolean {
|
||||
if (!dbData) return true;
|
||||
|
||||
// Print any differences for debugging
|
||||
for (const key in jsonData) {
|
||||
const jsonValue = jsonData[key];
|
||||
const dbValue = dbData[key];
|
||||
const jsonString = typeof jsonValue === 'object' ? JSON.stringify(jsonValue) : String(jsonValue);
|
||||
const dbString = typeof dbValue === 'object' ? JSON.stringify(dbValue) : String(dbValue);
|
||||
if (jsonString !== dbString) {
|
||||
console.log(`\nField "${key}" changed for character ID ${jsonData.id}:`);
|
||||
console.log(` JSON: ${jsonString}`);
|
||||
console.log(` DB: ${dbString}`);
|
||||
} }
|
||||
|
||||
// Compare each field
|
||||
return (
|
||||
jsonData.name != dbData.name ||
|
||||
jsonData.gender != dbData.gender ||
|
||||
jsonData.age != dbData.age ||
|
||||
JSON.stringify(jsonData.affiliations) != JSON.stringify(dbData.affiliations) ||
|
||||
jsonData.devilFruitId != dbData.devilFruitId ||
|
||||
jsonData.hakiObservation != dbData.hakiObservation ||
|
||||
jsonData.hakiArmament != dbData.hakiArmament ||
|
||||
jsonData.hakiConqueror != dbData.hakiConqueror ||
|
||||
jsonData.bounty != dbData.bounty ||
|
||||
jsonData.height != dbData.height ||
|
||||
jsonData.origin != dbData.origin ||
|
||||
jsonData.firstAppearance != dbData.firstAppearance ||
|
||||
jsonData.pictureUrl != dbData.pictureUrl ||
|
||||
JSON.stringify(jsonData.epithets) != JSON.stringify(dbData.epithets) ||
|
||||
jsonData.status != dbData.status ||
|
||||
jsonData.arcId != dbData.arcId ||
|
||||
jsonData.url != dbData.url
|
||||
);
|
||||
}
|
||||
|
||||
async function isCharacterTableEmpty(): Promise<boolean> {
|
||||
const result = await db.select({ count: sql<number>`COUNT(*)` }).from(character);
|
||||
return result[0]?.count === 0;
|
||||
}
|
||||
|
||||
async function importFromJson(): Promise<void> {
|
||||
let totalSuccess = 0;
|
||||
let totalErrors = 0;
|
||||
|
||||
try {
|
||||
const arcs = readJsonFile<ArcRecord>('./scraped-data/arcs.json');
|
||||
if (arcs) {
|
||||
console.log('\n=== Importing Arcs ===\n');
|
||||
console.log(`Found ${arcs.length} arcs\n`);
|
||||
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (let i = 0; i < arcs.length; i++) {
|
||||
const item = arcs[i];
|
||||
let lastSql: { sql: string; params: unknown[] } | null = null;
|
||||
try {
|
||||
const query = db
|
||||
.insert(arc)
|
||||
.values({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
startChapter: item.startChapter,
|
||||
endChapter: toNullable(item.endChapter),
|
||||
url: toNullable(item.url)
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: arc.id,
|
||||
set: {
|
||||
name: item.name,
|
||||
startChapter: item.startChapter,
|
||||
endChapter: toNullable(item.endChapter),
|
||||
url: toNullable(item.url)
|
||||
}
|
||||
});
|
||||
|
||||
lastSql = query.toSQL();
|
||||
await query;
|
||||
|
||||
successCount++;
|
||||
process.stdout.write(`\rExecuted: ${successCount}/${arcs.length}`);
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
console.error(`\n✗ Error at arc ${i + 1}:`);
|
||||
console.error(` ID: ${item.id ?? 'N/A'}`);
|
||||
console.error(` Message: ${getErrorMessage(error)}`);
|
||||
logSqlOnError(lastSql);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n\n✓ Arcs imported!`);
|
||||
console.log(` Success: ${successCount}`);
|
||||
console.log(` Errors: ${errorCount}`);
|
||||
|
||||
totalSuccess += successCount;
|
||||
totalErrors += errorCount;
|
||||
} else {
|
||||
console.log('\n⚠️ No arcs.json found, skipping...\n');
|
||||
}
|
||||
|
||||
const fruits = readJsonFile<DevilFruitRecord>('./scraped-data/devil-fruits.json');
|
||||
if (fruits) {
|
||||
console.log('\n=== Importing Devil Fruits ===\n');
|
||||
console.log(`Found ${fruits.length} devil fruits\n`);
|
||||
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (let i = 0; i < fruits.length; i++) {
|
||||
const item = fruits[i];
|
||||
let lastSql: { sql: string; params: unknown[] } | null = null;
|
||||
try {
|
||||
const query = db
|
||||
.insert(devilFruit)
|
||||
.values({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
type: toDevilFruitType(item.type),
|
||||
url: toNullable(item.url)
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: devilFruit.id,
|
||||
set: {
|
||||
name: item.name,
|
||||
type: toDevilFruitType(item.type),
|
||||
url: toNullable(item.url)
|
||||
}
|
||||
});
|
||||
|
||||
lastSql = query.toSQL();
|
||||
await query;
|
||||
|
||||
successCount++;
|
||||
process.stdout.write(`\rExecuted: ${successCount}/${fruits.length}`);
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
console.error(`\n✗ Error at devil fruit ${i + 1}:`);
|
||||
console.error(` ID: ${item.id ?? 'N/A'}`);
|
||||
console.error(` Message: ${getErrorMessage(error)}`);
|
||||
logSqlOnError(lastSql);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n\n✓ Devil Fruits imported!`);
|
||||
console.log(` Success: ${successCount}`);
|
||||
console.log(` Errors: ${errorCount}`);
|
||||
|
||||
totalSuccess += successCount;
|
||||
totalErrors += errorCount;
|
||||
} else {
|
||||
console.log('\n⚠️ No devil-fruits.json found, skipping...\n');
|
||||
}
|
||||
|
||||
const characters = readJsonFile<CharacterRecord>('./scraped-data/characters.json');
|
||||
if (characters) {
|
||||
console.log('\n=== Importing Characters ===\n');
|
||||
console.log(`Found ${characters.length} characters\n`);
|
||||
|
||||
const isEmpty = await isCharacterTableEmpty();
|
||||
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
if (isEmpty) {
|
||||
// Populate empty character table
|
||||
console.log('Characters table is empty, populating...\n');
|
||||
|
||||
for (let i = 0; i < characters.length; i++) {
|
||||
const item = characters[i];
|
||||
let lastSql: { sql: string; params: unknown[] } | null = null;
|
||||
try {
|
||||
const data = transformCharacterData(item);
|
||||
const query = db
|
||||
.insert(character)
|
||||
.values(data)
|
||||
.onConflictDoUpdate({
|
||||
target: character.id,
|
||||
set: data
|
||||
});
|
||||
|
||||
lastSql = query.toSQL();
|
||||
await query;
|
||||
|
||||
successCount++;
|
||||
process.stdout.write(`\rExecuted: ${successCount}/${characters.length}`);
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
console.error(`\n✗ Error at character ${i + 1}:`);
|
||||
console.error(` ID: ${item.id ?? 'N/A'}`);
|
||||
console.error(` Message: ${getErrorMessage(error)}`);
|
||||
logSqlOnError(lastSql);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check for changes and update scrapeValidation table
|
||||
console.log('Characters table not empty, checking for changes...\n');
|
||||
|
||||
for (let i = 0; i < characters.length; i++) {
|
||||
const item = characters[i];
|
||||
let lastSql: { sql: string; params: unknown[] } | null = null;
|
||||
try {
|
||||
const selectQuery = db
|
||||
.select()
|
||||
.from(character)
|
||||
.where(eq(character.id, item.id));
|
||||
|
||||
lastSql = selectQuery.toSQL();
|
||||
const [dbCharacter] = await selectQuery;
|
||||
|
||||
const jsonData = transformCharacterData(item);
|
||||
const changed = hasChanged(jsonData, dbCharacter);
|
||||
|
||||
if (changed) {
|
||||
// Update scrapeValidation table with changes
|
||||
const upsertQuery = db
|
||||
.insert(characterScrapeValidation)
|
||||
.values(jsonData)
|
||||
.onConflictDoUpdate({
|
||||
target: characterScrapeValidation.id,
|
||||
set: jsonData
|
||||
});
|
||||
|
||||
lastSql = upsertQuery.toSQL();
|
||||
await upsertQuery;
|
||||
} else {
|
||||
// No changes, delete from scrapeValidation if it exists
|
||||
const deleteQuery = db
|
||||
.delete(characterScrapeValidation)
|
||||
.where(eq(characterScrapeValidation.id, item.id));
|
||||
|
||||
lastSql = deleteQuery.toSQL();
|
||||
await deleteQuery;
|
||||
}
|
||||
|
||||
successCount++;
|
||||
process.stdout.write(`\rProcessed: ${successCount}/${characters.length}`);
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
console.error(`\n✗ Error at character ${i + 1}:`);
|
||||
console.error(` ID: ${item.id ?? 'N/A'}`);
|
||||
console.error(` Message: ${getErrorMessage(error)}`);
|
||||
logSqlOnError(lastSql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n\n✓ Characters imported!`);
|
||||
console.log(` Success: ${successCount}`);
|
||||
console.log(` Errors: ${errorCount}`);
|
||||
|
||||
totalSuccess += successCount;
|
||||
totalErrors += errorCount;
|
||||
} else {
|
||||
console.log('\n⚠️ No characters.json found, skipping...\n');
|
||||
}
|
||||
|
||||
console.log(`\n=== Total Import Summary ===`);
|
||||
console.log(` Total Success: ${totalSuccess}`);
|
||||
console.log(` Total Errors: ${totalErrors}\n`);
|
||||
} catch (error) {
|
||||
console.error('✗ Import failed:', getErrorMessage(error));
|
||||
process.exit(1);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
importFromJson().catch((error) => {
|
||||
console.error(getErrorMessage(error));
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,104 +0,0 @@
|
||||
import { createClient } from '@libsql/client';
|
||||
import fs from 'fs';
|
||||
|
||||
// Load environment variables
|
||||
const DATABASE_URL = process.env.DATABASE_URL || 'file:local.db';
|
||||
|
||||
const client = createClient({
|
||||
url: DATABASE_URL
|
||||
});
|
||||
|
||||
async function importSQL() {
|
||||
try {
|
||||
let totalSuccess = 0;
|
||||
let totalErrors = 0;
|
||||
|
||||
// Step 1: Import Devil Fruits
|
||||
if (fs.existsSync('./scraped-data/devil-fruits.sql')) {
|
||||
console.log('\n=== Importing Devil Fruits ===\n');
|
||||
const devilFruitsSql = fs.readFileSync('./scraped-data/devil-fruits.sql', 'utf-8');
|
||||
const dfStatements = devilFruitsSql.split(';\n\n').filter(s => s.trim());
|
||||
|
||||
console.log(`Found ${dfStatements.length} devil fruit statements\n`);
|
||||
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (let i = 0; i < dfStatements.length; i++) {
|
||||
const statement = dfStatements[i];
|
||||
if (statement.trim()) {
|
||||
try {
|
||||
await client.execute(statement.trim() + ';');
|
||||
successCount++;
|
||||
process.stdout.write(`\rExecuted: ${successCount}/${dfStatements.length}`);
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
const valuesMatch = statement.match(/VALUES\s*\(([^)]+)\)/);
|
||||
const values = valuesMatch ? valuesMatch[1] : 'N/A';
|
||||
console.error(`\n✗ Error at statement ${i + 1}:`);
|
||||
console.error(` Values: ${values}`);
|
||||
console.error(` Message: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n\n✓ Devil Fruits imported!`);
|
||||
console.log(` Success: ${successCount}`);
|
||||
console.log(` Errors: ${errorCount}`);
|
||||
|
||||
totalSuccess += successCount;
|
||||
totalErrors += errorCount;
|
||||
} else {
|
||||
console.log('\n⚠️ No devil-fruits.sql found, skipping...\n');
|
||||
}
|
||||
|
||||
// Step 2: Import Characters
|
||||
if (fs.existsSync('./scraped-data/characters.sql')) {
|
||||
console.log('\n=== Importing Characters ===\n');
|
||||
const charactersSql = fs.readFileSync('./scraped-data/characters.sql', 'utf-8');
|
||||
const charStatements = charactersSql.split(';\n\n').filter(s => s.trim());
|
||||
|
||||
console.log(`Found ${charStatements.length} character statements\n`);
|
||||
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (let i = 0; i < charStatements.length; i++) {
|
||||
const statement = charStatements[i];
|
||||
if (statement.trim()) {
|
||||
try {
|
||||
await client.execute(statement.trim() + ';');
|
||||
successCount++;
|
||||
process.stdout.write(`\rExecuted: ${successCount}/${charStatements.length}`);
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
const valuesMatch = statement.match(/VALUES\s*\(([^)]+)\)/);
|
||||
const values = valuesMatch ? valuesMatch[1] : 'N/A';
|
||||
console.error(`\n✗ Error at statement ${i + 1}:`);
|
||||
console.error(` Values: ${values}`);
|
||||
console.error(` Message: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n\n✓ Characters imported!`);
|
||||
console.log(` Success: ${successCount}`);
|
||||
console.log(` Errors: ${errorCount}`);
|
||||
|
||||
totalSuccess += successCount;
|
||||
totalErrors += errorCount;
|
||||
} else {
|
||||
console.log('\n⚠️ No characters.sql found, skipping...\n');
|
||||
}
|
||||
|
||||
console.log(`\n=== Total Import Summary ===`);
|
||||
console.log(` Total Success: ${totalSuccess}`);
|
||||
console.log(` Total Errors: ${totalErrors}\n`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('✗ Import failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
importSQL().catch(console.error);
|
||||
64
scripts/init-column-config.ts
Normal file
64
scripts/init-column-config.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Initialize config table with column visibility settings for characterHistory table
|
||||
*/
|
||||
|
||||
import { createClient } from '@libsql/client';
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { config } from '../src/lib/server/db/schema';
|
||||
import { like } from 'drizzle-orm';
|
||||
|
||||
// Load environment variables
|
||||
const DATABASE_URL = process.env.DATABASE_URL || 'file:local.db';
|
||||
|
||||
const client = createClient({ url: DATABASE_URL });
|
||||
const db = drizzle(client);
|
||||
|
||||
// Define the columns in characterHistory table
|
||||
const columns = [
|
||||
'gender',
|
||||
'affiliations',
|
||||
'haki',
|
||||
'bounty',
|
||||
'height',
|
||||
'origin',
|
||||
'devilFruitType',
|
||||
'arc',
|
||||
'status'
|
||||
] as const;
|
||||
|
||||
async function initColumnConfig(): Promise<void> {
|
||||
try {
|
||||
console.log('Initializing column visibility config...\n');
|
||||
|
||||
for (const column of columns) {
|
||||
const key = `characterHistory.column.${column}.visible`;
|
||||
const value = 'true';
|
||||
|
||||
await db.insert(config)
|
||||
.values({ key, value })
|
||||
.onConflictDoNothing();
|
||||
|
||||
console.log(`✓ Added config key: ${key} = ${value}`);
|
||||
}
|
||||
|
||||
console.log('\n✓ Successfully initialized column visibility config');
|
||||
|
||||
// Display all config entries
|
||||
const allConfig = await db.select()
|
||||
.from(config)
|
||||
.where(like(config.key, 'characterHistory.column.%.visible'));
|
||||
|
||||
console.log('\nCurrent column visibility config:');
|
||||
allConfig.forEach(row => {
|
||||
console.log(` ${row.key}: ${row.value}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error initializing config:', error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
initColumnConfig();
|
||||
File diff suppressed because it is too large
Load Diff
84
scripts/set-daily-mode.ts
Normal file
84
scripts/set-daily-mode.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { createClient } from '@libsql/client';
|
||||
import { drizzle } from 'drizzle-orm/libsql';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import fs from 'fs';
|
||||
import { character } from '../src/lib/server/db/schema';
|
||||
|
||||
const DATABASE_URL = process.env.DATABASE_URL || 'file:local.db';
|
||||
|
||||
const client = createClient({ url: DATABASE_URL });
|
||||
const db = drizzle(client);
|
||||
|
||||
function readJsonFile(path: string): string[] | null {
|
||||
if (!fs.existsSync(path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(path, 'utf-8');
|
||||
return JSON.parse(content) as string[];
|
||||
}
|
||||
|
||||
function getErrorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
async function setDailyCharacters(): Promise<void> {
|
||||
try {
|
||||
const dailyCharacterIds = readJsonFile('./scripts/daily-characters.json');
|
||||
|
||||
if (!dailyCharacterIds || dailyCharacterIds.length === 0) {
|
||||
console.error('❌ No daily characters found in daily-characters.json');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`\n=== Setting Daily Mode Characters ===\n`);
|
||||
console.log(`Found ${dailyCharacterIds.length} characters to set as daily\n`);
|
||||
|
||||
// Step 1: Disable isInDailyMode for all characters
|
||||
console.log('Step 1: Disabling isInDailyMode for all characters...');
|
||||
await db.update(character).set({ isInDailyMode: false });
|
||||
console.log('✓ All characters disabled from daily mode\n');
|
||||
|
||||
// Step 2: Enable isInDailyMode for characters in the list
|
||||
console.log('Step 2: Enabling isInDailyMode for daily characters...\n');
|
||||
|
||||
let successCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (let i = 0; i < dailyCharacterIds.length; i++) {
|
||||
const charId = dailyCharacterIds[i];
|
||||
try {
|
||||
const result = await db
|
||||
.update(character)
|
||||
.set({ isInDailyMode: true })
|
||||
.where(eq(character.id, charId));
|
||||
|
||||
successCount++;
|
||||
process.stdout.write(`\rUpdated: ${successCount}/${dailyCharacterIds.length}`);
|
||||
} catch (error) {
|
||||
errorCount++;
|
||||
console.error(`\n✗ Error updating character ${i + 1}:`);
|
||||
console.error(` ID: ${charId}`);
|
||||
console.error(` Message: ${getErrorMessage(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n\n✓ Daily characters updated!`);
|
||||
console.log(` Success: ${successCount}`);
|
||||
console.log(` Errors: ${errorCount}`);
|
||||
|
||||
if (errorCount === 0) {
|
||||
console.log(`\n✅ Successfully set ${successCount} characters as daily mode characters\n`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('✗ Operation failed:', getErrorMessage(error));
|
||||
process.exit(1);
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
setDailyCharacters().catch((error) => {
|
||||
console.error(getErrorMessage(error));
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user