Download official INI files (#807)

This commit is contained in:
Piotr Rogowski 2022-10-18 18:28:44 +02:00 committed by GitHub
parent 7303c0320b
commit 8a8976a45a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 84 additions and 64 deletions

View File

@ -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;
}

View File

@ -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),
};
};

View File

@ -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),
};
};

View File

@ -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 = () => { };

View File

@ -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,
};

View File

@ -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 { }

View File

@ -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) =>