Download official INI files (#807)
This commit is contained in:
parent
7303c0320b
commit
8a8976a45a
|
@ -3,6 +3,7 @@
|
|||
export enum Collections {
|
||||
Profiles = 'profiles',
|
||||
Tunes = 'tunes',
|
||||
IniFiles = 'iniFiles',
|
||||
}
|
||||
|
||||
export type ProfilesRecord = {
|
||||
|
@ -37,3 +38,9 @@ export type TunesRecord = {
|
|||
logFiles?: string[];
|
||||
toothLogFiles?: string[];
|
||||
}
|
||||
|
||||
export type IniFilesRecord = {
|
||||
signature: string;
|
||||
file: string;
|
||||
ecosystem: string;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
formatError,
|
||||
} from '../pocketbase';
|
||||
import {
|
||||
IniFilesRecordFull,
|
||||
TunesRecordFull,
|
||||
TunesRecordPartial,
|
||||
} from '../types/dbData';
|
||||
|
@ -55,6 +56,21 @@ const useDb = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const getIni = async (signature: string) => {
|
||||
try {
|
||||
const tune = await client.records.getList(Collections.IniFiles, 1, 1, {
|
||||
filter: `signature = "${signature}"`,
|
||||
});
|
||||
|
||||
return Promise.resolve(tune.totalItems > 0 ? tune.items[0] as IniFilesRecordFull : null);
|
||||
} catch (error) {
|
||||
Sentry.captureException(error);
|
||||
databaseGenericError(new Error(formatError(error)));
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
const searchTunes = async (search?: string) => {
|
||||
// TODO: add pagination
|
||||
const batchSide = 100;
|
||||
|
@ -84,6 +100,7 @@ const useDb = () => {
|
|||
updateTune: (tuneId: string, data: TunesRecordPartial): Promise<void> => updateTune(tuneId, data),
|
||||
createTune: (data: TunesRecord): Promise<TunesRecordFull> => createTune(data),
|
||||
getTune: (tuneId: string): Promise<TunesRecordFull | null> => getTune(tuneId),
|
||||
getIni: (tuneId: string): Promise<IniFilesRecordFull | null> => getIni(tuneId),
|
||||
searchTunes: (search?: string): Promise<TunesRecordFull[]> => searchTunes(search),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,63 +1,44 @@
|
|||
import { notification } from 'antd';
|
||||
import Pako from 'pako';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import { fetchEnv } from '../utils/env';
|
||||
import { API_URL } from '../pocketbase';
|
||||
import { Collections } from '../@types/pocketbase-types';
|
||||
import useDb from './useDb';
|
||||
|
||||
const PUBLIC_PATH = 'public';
|
||||
const INI_PATH = `${PUBLIC_PATH}/ini`;
|
||||
export const CDN_URL = fetchEnv('VITE_CDN_URL');
|
||||
|
||||
const fetchFromServer = async (path: string): Promise<ArrayBuffer> => {
|
||||
const response = await fetch(`${CDN_URL}/${path}`);
|
||||
return Promise.resolve(response.arrayBuffer());
|
||||
};
|
||||
|
||||
const fetchFileFromServer = async (recordId: string, filename: string, inflate = true): Promise<ArrayBuffer> => {
|
||||
const response = await fetch(`${API_URL}/api/files/${Collections.Tunes}/${recordId}/${filename}`);
|
||||
|
||||
if (inflate) {
|
||||
return Pako.inflate(new Uint8Array(await response.arrayBuffer()));
|
||||
}
|
||||
|
||||
return response.arrayBuffer();
|
||||
};
|
||||
|
||||
const useServerStorage = () => {
|
||||
const getINIFile = async (signature: string) => {
|
||||
const { version, baseVersion } = /.+?(?<version>(?<baseVersion>\d+)(-\w+)*)/.exec(signature)?.groups || { version: null, baseVersion: null };
|
||||
const { getIni } = useDb();
|
||||
|
||||
try {
|
||||
return Pako.inflate(new Uint8Array(await fetchFromServer(`${INI_PATH}/${version}.ini.gz`)));
|
||||
} catch (error) {
|
||||
// TODO: use built in pocketbase function
|
||||
const buildFileUrl = (collection: Collections, recordId: string, filename: string) => `${API_URL}/api/files/${collection}/${recordId}/${filename}`;
|
||||
|
||||
const fetchTuneFile = async (recordId: string, filename: string): Promise<ArrayBuffer> => {
|
||||
const response = await fetch(buildFileUrl(Collections.Tunes, recordId, filename));
|
||||
|
||||
return Pako.inflate(new Uint8Array(await response.arrayBuffer()));
|
||||
};
|
||||
|
||||
const fetchINIFile = async (signature: string): Promise<ArrayBuffer> => {
|
||||
// const { version, baseVersion } = /.+?(?<version>(?<baseVersion>\d+)(-\w+)*)/.exec(signature)?.groups || { version: null, baseVersion: null };
|
||||
const ini = await getIni(signature);
|
||||
|
||||
if (!ini) {
|
||||
const msg = `Signature: "${signature}" not supported!`;
|
||||
const error = new Error(msg);
|
||||
Sentry.captureException(error);
|
||||
console.error(error);
|
||||
|
||||
notification.warning({
|
||||
message: 'INI not found',
|
||||
description: `INI version: "${version}" not found. Trying base version: "${baseVersion}"!`,
|
||||
});
|
||||
|
||||
try {
|
||||
return fetchFromServer(`${INI_PATH}/${baseVersion}.ini.gz`);
|
||||
} catch (err) {
|
||||
Sentry.captureException(err);
|
||||
console.error(err);
|
||||
|
||||
notification.error({
|
||||
message: 'INI not found',
|
||||
description: `INI version: "${baseVersion}" not found. Try uploading custom INI file!`,
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
const response = await fetch(buildFileUrl(Collections.IniFiles, ini.id, ini.file));
|
||||
|
||||
return Pako.inflate(new Uint8Array(await response.arrayBuffer()));
|
||||
};
|
||||
|
||||
return {
|
||||
getINIFile: (signature: string): Promise<ArrayBuffer> => getINIFile(signature),
|
||||
fetchFileFromServer: (recordId: string, filename: string): Promise<ArrayBuffer> => fetchFileFromServer(recordId, filename),
|
||||
fetchTuneFile: (recordId: string, filename: string): Promise<ArrayBuffer> => fetchTuneFile(recordId, filename),
|
||||
fetchINIFile: (signature: string): Promise<ArrayBuffer> => fetchINIFile(signature),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -132,10 +132,10 @@ const UploadPage = () => {
|
|||
const shareSupported = 'share' in navigator;
|
||||
const { currentUser, refreshUser } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const { fetchFileFromServer } = useServerStorage();
|
||||
const { fetchTuneFile } = useServerStorage();
|
||||
const { createTune, updateTune, getTune } = useDb();
|
||||
|
||||
const fetchFile = async (tuneId: string, fileName: string) => bufferToFile(await fetchFileFromServer(tuneId, fileName), fileName);
|
||||
const fetchFile = async (tuneId: string, fileName: string) => bufferToFile(await fetchTuneFile(tuneId, fileName), fileName);
|
||||
|
||||
const noop = () => { };
|
||||
|
||||
|
|
|
@ -119,6 +119,12 @@ const databaseGenericError = (err: Error) => notification.error({
|
|||
...baseOptions,
|
||||
});
|
||||
|
||||
const iniLoadingError = (err: Error) => notification.error({
|
||||
message: 'INI not found',
|
||||
description: err.message,
|
||||
...baseOptions,
|
||||
});
|
||||
|
||||
const copiedToClipboard = () => notification.success({
|
||||
message: 'Copied to clipboard',
|
||||
...baseOptions,
|
||||
|
@ -145,4 +151,5 @@ export {
|
|||
passwordUpdateFailed,
|
||||
databaseGenericError,
|
||||
copiedToClipboard,
|
||||
iniLoadingError,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Record } from 'pocketbase';
|
||||
import {
|
||||
IniFilesRecord,
|
||||
ProfilesRecord,
|
||||
TunesRecord,
|
||||
} from '../@types/pocketbase-types';
|
||||
|
@ -13,3 +14,5 @@ export type TunesRecordPartial = Partial<TunesRecord>;
|
|||
export interface TunesRecordFull extends TunesRecord, Record { }
|
||||
|
||||
export interface ProfilesRecordFull extends ProfilesRecord, Record { }
|
||||
|
||||
export interface IniFilesRecordFull extends IniFilesRecord, Record { }
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
import TuneParser from './tune/TuneParser';
|
||||
import useServerStorage, { CDN_URL } from '../hooks/useServerStorage';
|
||||
import { TunesRecordFull } from '../types/dbData';
|
||||
import { iniLoadingError } from '../pages/auth/notifications';
|
||||
|
||||
// TODO: refactor this!!
|
||||
export const loadTune = async (tuneData: TunesRecordFull | null) => {
|
||||
|
@ -21,10 +22,10 @@ export const loadTune = async (tuneData: TunesRecordFull | null) => {
|
|||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const { getINIFile, fetchFileFromServer } = useServerStorage();
|
||||
const { fetchINIFile, fetchTuneFile } = useServerStorage();
|
||||
|
||||
const started = new Date();
|
||||
const tuneRaw = await fetchFileFromServer(tuneData.id, tuneData.tuneFile);
|
||||
const tuneRaw = await fetchTuneFile(tuneData.id, tuneData.tuneFile);
|
||||
|
||||
const tuneParser = new TuneParser().parse(tuneRaw);
|
||||
|
||||
|
@ -36,26 +37,30 @@ export const loadTune = async (tuneData: TunesRecordFull | null) => {
|
|||
}
|
||||
|
||||
const tune = tuneParser.getTune();
|
||||
const iniRaw = tuneData.customIniFile ? fetchFileFromServer(tuneData.id, tuneData.customIniFile) : getINIFile(tuneData.signature);
|
||||
const config = new INI(await iniRaw).parse().getResults();
|
||||
try {
|
||||
const iniRaw = tuneData.customIniFile ? fetchTuneFile(tuneData.id, tuneData.customIniFile) : fetchINIFile(tuneData.signature);
|
||||
const config = new INI(await iniRaw).parse().getResults();
|
||||
|
||||
// override / merge standard dialogs, constants and help
|
||||
config.dialogs = {
|
||||
...config.dialogs,
|
||||
...stdDialogs,
|
||||
};
|
||||
config.help = {
|
||||
...config.help,
|
||||
...help,
|
||||
};
|
||||
config.constants.pages[0].data.divider = divider;
|
||||
// override / merge standard dialogs, constants and help
|
||||
config.dialogs = {
|
||||
...config.dialogs,
|
||||
...stdDialogs,
|
||||
};
|
||||
config.help = {
|
||||
...config.help,
|
||||
...help,
|
||||
};
|
||||
config.constants.pages[0].data.divider = divider;
|
||||
|
||||
const loadingTimeInfo = `Tune loaded in ${(new Date().getTime() - started.getTime())}ms`;
|
||||
console.info(loadingTimeInfo);
|
||||
const loadingTimeInfo = `Tune loaded in ${(new Date().getTime() - started.getTime())}ms`;
|
||||
console.info(loadingTimeInfo);
|
||||
|
||||
store.dispatch({ type: 'config/load', payload: config });
|
||||
store.dispatch({ type: 'tune/load', payload: tune });
|
||||
store.dispatch({ type: 'status', payload: loadingTimeInfo });
|
||||
store.dispatch({ type: 'config/load', payload: config });
|
||||
store.dispatch({ type: 'tune/load', payload: tune });
|
||||
store.dispatch({ type: 'status', payload: loadingTimeInfo });
|
||||
} catch (error) {
|
||||
iniLoadingError((error as Error));
|
||||
}
|
||||
};
|
||||
|
||||
export const loadLogs = (onProgress?: onProgressType, signal?: AbortSignal) =>
|
||||
|
|
Loading…
Reference in New Issue