UPDATE: monster bar
Some checks failed
Gitea Auto Deploy / Deploy-Container (push) Failing after 1m36s

This commit is contained in:
2025-07-25 09:20:39 +07:00
parent 604cf1ceec
commit 487c29def1
133 changed files with 841207 additions and 16695 deletions

218
src/lib/api/api.ts Normal file
View File

@@ -0,0 +1,218 @@
import { AffixDetail, ASDetail, CharacterDetail, ConfigMaze, FreeSRJson, LightConeDetail, MocDetail, PFDetail, PSResponse, RelicDetail } from "@/types";
import axios from 'axios';
import { pSResponseSchema } from "@/zod";
export async function getConfigMazeApi(): Promise<ConfigMaze> {
try {
const res = await axios.get<ConfigMaze>(
`/api/config-maze`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data as ConfigMaze;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return {
Avatar: {},
MOC: {},
AS: {},
PF: {},
};
}
}
export async function getMainAffixApi(): Promise<Record<string, Record<string, AffixDetail>>> {
try {
const res = await axios.get<Record<string, Record<string, AffixDetail>>>(
`/api/main-affixes`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data as Record<string, Record<string, AffixDetail>>;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return {};
}
}
export async function getSubAffixApi(): Promise<Record<string, Record<string, AffixDetail>>> {
try {
const res = await axios.get<Record<string, Record<string, AffixDetail>>>(
`/api/sub-affixes`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data as Record<string, Record<string, AffixDetail>>;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return {};
}
}
export async function fetchCharacterByIdNative(id: string, locale: string): Promise<CharacterDetail | null> {
try {
const res = await axios.get<CharacterDetail>(`/api/${locale}/characters/${id}`);
return res.data;
} catch (error) {
console.error('Failed to fetch character:', error);
return null;
}
}
export async function fetchCharactersByIdsNative(ids: string[], locale: string): Promise<Record<string, CharacterDetail>> {
try {
const res = await axios.post<Record<string, CharacterDetail>>(`/api/${locale}/characters`, { charIds: ids });
return res.data;
} catch (error) {
console.error('Failed to fetch characters:', error);
return {};
}
}
export async function fetchLightconeByIdNative(id: string, locale: string): Promise<LightConeDetail | null> {
try {
const res = await axios.get<LightConeDetail>(`/api/${locale}/lightcones/${id}`);
return res.data;
} catch (error) {
console.error('Failed to fetch lightcone:', error);
return null;
}
}
export async function fetchLightconesByIdsNative(ids: string[], locale: string): Promise<Record<string, LightConeDetail>> {
try {
const res = await axios.post<Record<string, LightConeDetail>>(`/api/${locale}/lightcones`, { lightconeIds: ids });
return res.data;
} catch (error) {
console.error('Failed to fetch lightcones:', error);
return {};
}
}
export async function fetchRelicByIdNative(id: string, locale: string): Promise<RelicDetail | null> {
try {
const res = await axios.get<RelicDetail>(`/api/${locale}/relics/${id}`);
return res.data;
} catch (error) {
console.error('Failed to fetch relic:', error);
return null;
}
}
export async function fetchRelicsByIdsNative(ids: string[], locale: string): Promise<Record<string, RelicDetail>> {
try {
const res = await axios.post<Record<string, RelicDetail>>(`/api/${locale}/relics`, { relicIds: ids });
return res.data;
} catch (error) {
console.error('Failed to fetch relics:', error);
return {};
}
}
export async function fetchASByIdsNative(ids: string[], locale: string): Promise<Record<string, ASDetail> | null> {
try {
const res = await axios.post<Record<string, ASDetail>>(`/api/${locale}/as`, { asIds: ids });
return res.data;
} catch (error) {
console.error('Failed to fetch AS:', error);
return null;
}
}
export async function fetchASByIdNative(ids: string, locale: string): Promise<ASDetail | null> {
try {
const res = await axios.get<ASDetail>(`/api/${locale}/as/${ids}`);
return res.data;
} catch (error) {
console.error('Failed to fetch AS:', error);
return null;
}
}
export async function fetchPFByIdsNative(ids: string[], locale: string): Promise<Record<string, PFDetail> | null> {
try {
const res = await axios.post<Record<string, PFDetail>>(`/api/${locale}/pf`, { pfIds: ids });
return res.data;
} catch (error) {
console.error('Failed to fetch PF:', error);
return null;
}
}
export async function fetchPFByIdNative(ids: string, locale: string): Promise<PFDetail | null> {
try {
const res = await axios.get<PFDetail>(`/api/${locale}/pf/${ids}`);
return res.data;
} catch (error) {
console.error('Failed to fetch PF:', error);
return null;
}
}
export async function fetchMOCByIdsNative(ids: string[], locale: string): Promise<Record<string, MocDetail[]> | null> {
try {
const res = await axios.post<Record<string, MocDetail[]>>(`/api/${locale}/moc`, { mocIds: ids });
return res.data;
} catch (error) {
console.error('Failed to fetch MOC:', error);
return null;
}
}
export async function fetchMOCByIdNative(ids: string, locale: string): Promise<MocDetail[] | null> {
try {
const res = await axios.get<MocDetail[]>(`/api/${locale}/moc/${ids}`);
return res.data;
} catch (error) {
console.error('Failed to fetch MOC:', error);
return null;
}
}
export async function SendDataToServer(username: string, password: string, serverUrl: string, data: FreeSRJson | null): Promise<PSResponse | string> {
try {
const response = await axios.post(`${serverUrl}`, { username, password, data })
const parsed = pSResponseSchema.safeParse(response.data)
if (!parsed.success) {
return "Invalid response schema";
}
return parsed.data;
} catch (error: any) {
return error?.message || "Unknown error";
}
}
export async function SendDataThroughProxy({data}: {data: any}) {
try {
const response = await axios.post(`/api/proxy`, { ...data })
return response.data;
} catch (error: any) {
return error;
}
}

View File

@@ -1,102 +1,6 @@
import { CharacterBasic, CharacterBasicRaw } from "@/types/characterBasic";
import { AffixDetail, CharacterDetail, ConfigMaze, EnkaResponse, FreeSRJson, LightConeBasic, LightConeBasicRaw, LightConeDetail, PSResponse, RelicBasic, RelicBasicEffect, RelicBasicRaw, RelicDetail } from "@/types";
import axios from 'axios';
import { convertAvatar, convertLightcone, convertRelicSet } from "@/helper";
import { psResponseSchema } from "@/zod";
export async function getConfigMazeApi(): Promise<ConfigMaze> {
try {
const res = await axios.get<ConfigMaze>(
`/api/config-maze`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data as ConfigMaze;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return {
Avatar: {},
MOC: {},
AS: {},
PF: {},
};
}
}
export async function getMainAffixApi(): Promise<Record<string, Record<string, AffixDetail>>> {
try {
const res = await axios.get<Record<string, Record<string, AffixDetail>>>(
`/api/main-affixes`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data as Record<string, Record<string, AffixDetail>>;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return {};
}
}
export async function getSubAffixApi(): Promise<Record<string, Record<string, AffixDetail>>> {
try {
const res = await axios.get<Record<string, Record<string, AffixDetail>>>(
`/api/sub-affixes`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data as Record<string, Record<string, AffixDetail>>;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return {};
}
}
export async function getCharacterInfoApi(avatarId: number, locale: string): Promise<CharacterDetail | null> {
try {
const res = await axios.get<CharacterDetail>(
`https://api.hakush.in/hsr/data/${locale}/character/${avatarId}.json`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data as CharacterDetail;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return null;
}
}
import { convertAvatar, convertEvent, convertLightcone, convertMonster, convertRelicSet } from "@/helper";
import { ASDetail, CharacterBasic, CharacterBasicRaw, CharacterDetail, EventBasic, EventBasicRaw, LightConeBasic, LightConeBasicRaw, LightConeDetail, MocDetail, MonsterBasic, MonsterBasicRaw, MonsterDetail, PFDetail, RelicBasic, RelicBasicRaw, RelicDetail } from "@/types";
import axios from "axios";
export async function getLightconeInfoApi(lightconeId: number, locale: string): Promise<LightConeDetail | null> {
try {
@@ -142,63 +46,91 @@ export async function getRelicInfoApi(relicId: number, locale: string): Promise<
}
}
export async function fetchCharacterByIdNative(id: string, locale: string): Promise<CharacterDetail | null> {
export async function getCharacterInfoApi(avatarId: number, locale: string): Promise<CharacterDetail | null> {
try {
const res = await axios.get<CharacterDetail>(`/api/${locale}/characters/${id}`);
return res.data;
} catch (error) {
console.error('Failed to fetch character:', error);
const res = await axios.get<CharacterDetail>(
`https://api.hakush.in/hsr/data/${locale}/character/${avatarId}.json`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data as CharacterDetail;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return null;
}
}
export async function fetchCharactersByIdsNative(ids: string[], locale: string): Promise<Record<string, CharacterDetail>> {
export async function getMOCEventInfoApi(eventId: number, locale: string): Promise<MocDetail[] | null> {
try {
const res = await axios.post<Record<string, CharacterDetail>>(`/api/${locale}/characters`, { charIds: ids });
return res.data;
} catch (error) {
console.error('Failed to fetch characters:', error);
return {};
}
}
const res = await axios.get<MocDetail[]>(
`https://api.hakush.in/hsr/data/${locale}/maze/${eventId}.json`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
export async function fetchLightconeByIdNative(id: string, locale: string): Promise<LightConeDetail | null> {
try {
const res = await axios.get<LightConeDetail>(`/api/${locale}/lightcones/${id}`);
return res.data;
} catch (error) {
console.error('Failed to fetch lightcone:', error);
return res.data as MocDetail[];
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return null;
}
}
export async function fetchLightconesByIdsNative(ids: string[], locale: string): Promise<Record<string, LightConeDetail>> {
export async function getASEventInfoApi(eventId: number, locale: string): Promise<ASDetail | null> {
try {
const res = await axios.post<Record<string, LightConeDetail>>(`/api/${locale}/lightcones`, { lightconeIds: ids });
return res.data;
} catch (error) {
console.error('Failed to fetch lightcones:', error);
return {};
}
}
const res = await axios.get<ASDetail>(
`https://api.hakush.in/hsr/data/${locale}/boss/${eventId}.json`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
export async function fetchRelicByIdNative(id: string, locale: string): Promise<RelicDetail | null> {
try {
const res = await axios.get<RelicDetail>(`/api/${locale}/relics/${id}`);
return res.data;
} catch (error) {
console.error('Failed to fetch relic:', error);
return res.data as ASDetail;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return null;
}
}
export async function fetchRelicsByIdsNative(ids: string[], locale: string): Promise<Record<string, RelicDetail>> {
export async function getPFEventInfoApi(eventId: number, locale: string): Promise<PFDetail | null> {
try {
const res = await axios.post<Record<string, RelicDetail>>(`/api/${locale}/relics`, { relicIds: ids });
return res.data;
} catch (error) {
console.error('Failed to fetch relics:', error);
return {};
const res = await axios.get<PFDetail>(
`https://api.hakush.in/hsr/data/${locale}/story/${eventId}.json`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data as PFDetail;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return null;
}
}
@@ -274,24 +206,124 @@ export async function getRelicSetListApi(): Promise<RelicBasic[]> {
}
}
export async function SendDataToServer(username: string, password: string, serverUrl: string, data: FreeSRJson | null): Promise<PSResponse | string> {
export async function getMOCEventListApi(): Promise<EventBasic[]> {
try {
const response = await axios.post(`${serverUrl}`, { username, password, data })
const parsed = psResponseSchema.safeParse(response.data)
if (!parsed.success) {
return "Invalid response schema";
const res = await axios.get<Record<string, EventBasicRaw>>(
'https://api.hakush.in/hsr/data/maze.json',
{
headers: {
'Content-Type': 'application/json',
},
}
);
const data = new Map(Object.entries(res.data));
return Array.from(data.entries()).map(([id, it]) => convertEvent(id, it));
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return parsed.data;
} catch (error: any) {
return error?.message || "Unknown error";
return [];
}
}
export async function SendDataThroughProxy({data}: {data: any}) {
export async function getASEventListApi(): Promise<EventBasic[]> {
try {
const response = await axios.post(`/api/proxy`, { ...data })
return response.data;
} catch (error: any) {
return error;
const res = await axios.get<Record<string, EventBasicRaw>>(
'https://api.hakush.in/hsr/data/maze_boss.json',
{
headers: {
'Content-Type': 'application/json',
},
}
);
const data = new Map(Object.entries(res.data));
return Array.from(data.entries()).map(([id, it]) => convertEvent(id, it));
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return [];
}
}
}
export async function getPFEventListApi(): Promise<EventBasic[]> {
try {
const res = await axios.get<Record<string, EventBasicRaw>>(
'https://api.hakush.in/hsr/data/maze_extra.json',
{
headers: {
'Content-Type': 'application/json',
},
}
);
const data = new Map(Object.entries(res.data));
return Array.from(data.entries()).map(([id, it]) => convertEvent(id, it));
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return [];
}
}
export async function getMonsterListApi(): Promise<{list: MonsterBasic[], map: Record<string, MonsterBasic>}> {
try {
const res = await axios.get<Record<string, MonsterBasicRaw>>(
'https://api.hakush.in/hsr/data/monster.json',
{
headers: {
'Content-Type': 'application/json',
},
}
);
const dataArr = Array.from(Object.entries(res.data)).map(([id, it]) => convertMonster(id, it));
const dataMap = Object.fromEntries(Object.entries(res.data).map(([id, it]) => [id, convertMonster(id, it)]));
return {
list: dataArr,
map: dataMap
};
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return {list: [], map: {}};
}
}
export async function getMonsterDetailApi(): Promise<Record<string, MonsterDetail> | null> {
try {
const res = await axios.get<Record<string, MonsterDetail>>(
`https://api.hakush.in/hsr/data/monstervalue.json`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
return res.data;
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.log(`Error: ${error.response?.status} - ${error.message}`);
} else {
console.log(`Unexpected error: ${String(error)}`);
}
return null;
}
}

2
src/lib/api/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from "./api";
export * from "./hakushi";

View File

@@ -0,0 +1,51 @@
import fs from 'fs';
import path from 'path';
import { ASDetail } from '@/types';
import { getASEventInfoApi } from '../api';
const DATA_DIR = path.join(process.cwd(), 'data');
const asFileCache: Record<string, Record<string, ASDetail>> = {};
export let asMap: Record<string, ASDetail> = {};
function getJsonFilePath(locale: string): string {
return path.join(DATA_DIR, `as.${locale}.json`);
}
function loadFromFileIfExists(locale: string): Record<string, ASDetail> | null {
if (asFileCache[locale]) return asFileCache[locale];
const filePath = getJsonFilePath(locale);
if (fs.existsSync(filePath)) {
const data = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as Record<string, ASDetail>;
asFileCache[locale] = data;
return data;
}
return null;
}
export async function loadAS(charIds: string[], locale: string): Promise<Record<string, ASDetail>> {
const fileData = loadFromFileIfExists(locale);
const fileIds = fileData ? Object.keys(fileData) : [];
if (fileData && charIds.every(id => fileIds.includes(id))) {
asMap = fileData;
return asMap;
}
const result: Record<string, ASDetail> = {};
await Promise.all(
charIds.map(async id => {
const info = await getASEventInfoApi(Number(id), locale);
if (info) result[id] = info;
})
);
fs.mkdirSync(DATA_DIR, { recursive: true });
const filePath = getJsonFilePath(locale);
fs.writeFileSync(filePath, JSON.stringify(result, null, 2), 'utf-8');
asFileCache[locale] = result;
asMap = result;
return result;
}

View File

@@ -1,7 +1,7 @@
import fs from 'fs';
import path from 'path';
import { getCharacterInfoApi } from './api';
import { CharacterDetail } from '@/types/characterDetail';
import { getCharacterInfoApi } from '../api';
const DATA_DIR = path.join(process.cwd(), 'data');
const characterFileCache: Record<string, Record<string, CharacterDetail>> = {};

8
src/lib/loader/index.ts Normal file
View File

@@ -0,0 +1,8 @@
export * from "./characterLoader";
export * from "./lighconeLoader";
export * from "./affixLoader";
export * from "./configMazeLoader";
export * from "./relicLoader";
export * from "./asLoader";
export * from "./pfLoader";
export * from "./mocLoader";

View File

@@ -1,6 +1,6 @@
import fs from 'fs';
import path from 'path';
import { getLightconeInfoApi } from './api';
import { getLightconeInfoApi } from '../api';
import { LightConeDetail } from '@/types';
const DATA_DIR = path.join(process.cwd(), 'data');

View File

@@ -0,0 +1,51 @@
import fs from 'fs';
import path from 'path';
import { MocDetail } from '@/types';
import { getMOCEventInfoApi } from '../api';
const DATA_DIR = path.join(process.cwd(), 'data');
const mocFileCache: Record<string, Record<string, MocDetail[]>> = {};
export let mocMap: Record<string, MocDetail[]> = {};
function getJsonFilePath(locale: string): string {
return path.join(DATA_DIR, `moc.${locale}.json`);
}
function loadFromFileIfExists(locale: string): Record<string, MocDetail[]> | null {
if (mocFileCache[locale]) return mocFileCache[locale];
const filePath = getJsonFilePath(locale);
if (fs.existsSync(filePath)) {
const data = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as Record<string, MocDetail[]>;
mocFileCache[locale] = data;
return data;
}
return null;
}
export async function loadMOC(charIds: string[], locale: string): Promise<Record<string, MocDetail[]>> {
const fileData = loadFromFileIfExists(locale);
const fileIds = fileData ? Object.keys(fileData) : [];
if (fileData && charIds.every(id => fileIds.includes(id))) {
mocMap = fileData;
return mocMap;
}
const result: Record<string, MocDetail[]> = {};
await Promise.all(
charIds.map(async id => {
const info = await getMOCEventInfoApi(Number(id), locale);
if (info) result[id] = info;
})
);
fs.mkdirSync(DATA_DIR, { recursive: true });
const filePath = getJsonFilePath(locale);
fs.writeFileSync(filePath, JSON.stringify(result, null, 2), 'utf-8');
mocFileCache[locale] = result;
mocMap = result;
return result;
}

View File

@@ -0,0 +1,51 @@
import fs from 'fs';
import path from 'path';
import { PFDetail } from '@/types';
import { getPFEventInfoApi } from '../api';
const DATA_DIR = path.join(process.cwd(), 'data');
const pfFileCache: Record<string, Record<string, PFDetail>> = {};
export let pfMap: Record<string, PFDetail> = {};
function getJsonFilePath(locale: string): string {
return path.join(DATA_DIR, `pf.${locale}.json`);
}
function loadFromFileIfExists(locale: string): Record<string, PFDetail> | null {
if (pfFileCache[locale]) return pfFileCache[locale];
const filePath = getJsonFilePath(locale);
if (fs.existsSync(filePath)) {
const data = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as Record<string, PFDetail>;
pfFileCache[locale] = data;
return data;
}
return null;
}
export async function loadPF(charIds: string[], locale: string): Promise<Record<string, PFDetail>> {
const fileData = loadFromFileIfExists(locale);
const fileIds = fileData ? Object.keys(fileData) : [];
if (fileData && charIds.every(id => fileIds.includes(id))) {
pfMap = fileData;
return pfMap;
}
const result: Record<string, PFDetail> = {};
await Promise.all(
charIds.map(async id => {
const info = await getPFEventInfoApi(Number(id), locale);
if (info) result[id] = info;
})
);
fs.mkdirSync(DATA_DIR, { recursive: true });
const filePath = getJsonFilePath(locale);
fs.writeFileSync(filePath, JSON.stringify(result, null, 2), 'utf-8');
pfFileCache[locale] = result;
pfMap = result;
return result;
}

View File

@@ -1,7 +1,7 @@
import fs from 'fs';
import path from 'path';
import { getRelicInfoApi } from './api';
import { RelicDetail } from '@/types';
import { getRelicInfoApi } from '../api';
const DATA_DIR = path.join(process.cwd(), 'data');
const relicFileCache: Record<string, Record<string, RelicDetail>> = {};