implemented save file compression in browser
This commit is contained in:
@@ -2,14 +2,12 @@
|
||||
// already uses Discord, so email/password login is not worth building. The
|
||||
// /api/auth/login and /api/auth/register routes below are never implemented —
|
||||
// delete them along with this service.
|
||||
//TODO: controllable rate limits
|
||||
|
||||
class AuthService {
|
||||
private API_BASE = '/api/auth'
|
||||
|
||||
// Hashes the password before sending it to the backend
|
||||
private hashPassword(password: string) {
|
||||
// TODO: password hashing
|
||||
return password
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { PROXY_PATHS } from '@/app/api/proxy/paths'
|
||||
import type { SaveUploadConfig } from '@/config/AppConfig'
|
||||
import { GameEnum } from '@/config/games'
|
||||
import { packSaveForUpload, StageCallback } from '@/lib/saveArchive/client'
|
||||
import { OriginalFileType } from '@/lib/saveArchive/messages'
|
||||
|
||||
export interface UploadSaveResponse {
|
||||
id: string
|
||||
@@ -9,6 +13,10 @@ export interface SaveStatusResponse {
|
||||
game: string
|
||||
status: string
|
||||
uploadDateTime: string
|
||||
// Shape the save had when it was uploaded, needed to rebuild a file the
|
||||
// game can load again.
|
||||
originalFileType: OriginalFileType
|
||||
originalFileName: string
|
||||
}
|
||||
|
||||
// Tries to read the response body as string, returning an empty string if that fails.
|
||||
@@ -23,16 +31,34 @@ async function getBodyTextOrEmpty(res: Response): Promise<string> {
|
||||
export class SaveParserBackendService {
|
||||
private API_BASE = PROXY_PATHS.saveParserBackend
|
||||
|
||||
//TODO: save size limit 30 mb
|
||||
// Uploads a save file for the given game and returns its assigned id.
|
||||
async uploadSave(game: string, file: File): Promise<UploadSaveResponse> {
|
||||
const url = `${this.API_BASE}/uploadSave?game=${encodeURIComponent(game)}`
|
||||
/**
|
||||
* Re-compresses a save file and uploads it, returning its assigned id.
|
||||
*
|
||||
* Saves are not uploaded as the game wrote them. Plain text and archived
|
||||
* saves are both packed into a tar inside a zstd frame, which is a lot
|
||||
* smaller than the game's own zip and cheap for the backend to unpack.
|
||||
* The shape the file originally had travels along, so it can be rebuilt
|
||||
* when someone downloads the save later.
|
||||
*/
|
||||
async uploadSave(
|
||||
game: GameEnum,
|
||||
file: File,
|
||||
config: SaveUploadConfig,
|
||||
onStage: StageCallback
|
||||
): Promise<UploadSaveResponse> {
|
||||
const save = await packSaveForUpload(game, file, config, onStage)
|
||||
onStage('uploading')
|
||||
|
||||
const url =
|
||||
`${this.API_BASE}/uploadSave?game=${encodeURIComponent(game)}` +
|
||||
`&originalFileType=${encodeURIComponent(save.originalFileType)}` +
|
||||
`&originalFileName=${encodeURIComponent(save.originalFileName)}`
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Type': 'application/zstd',
|
||||
},
|
||||
body: file,
|
||||
body: save.data,
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -61,8 +87,8 @@ export class SaveParserBackendService {
|
||||
}
|
||||
|
||||
// Fetches the fully parsed data for a save once its parsing is done.
|
||||
async getParsedSave(id: string): Promise<Record<string, unknown>> {
|
||||
const url = `${this.API_BASE}/getParsedSave?id=${encodeURIComponent(id)}`
|
||||
async getSaveData(id: string): Promise<Record<string, unknown>> {
|
||||
const url = `${this.API_BASE}/getSaveData?id=${encodeURIComponent(id)}`
|
||||
const response = await fetch(url)
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -74,6 +100,24 @@ export class SaveParserBackendService {
|
||||
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the stored save archive, exactly as it was uploaded. Turning it
|
||||
* back into a file the game can load is done in the browser.
|
||||
*/
|
||||
async getSaveFile(id: string): Promise<Blob> {
|
||||
const url = `${this.API_BASE}/getSaveFile?id=${encodeURIComponent(id)}`
|
||||
const response = await fetch(url)
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to get save file: ${response.statusText} ` +
|
||||
(await getBodyTextOrEmpty(response))
|
||||
)
|
||||
}
|
||||
|
||||
return await response.blob()
|
||||
}
|
||||
}
|
||||
|
||||
export const saveParserBackendService = new SaveParserBackendService()
|
||||
|
||||
Reference in New Issue
Block a user