Skip to content

Commit da9f842

Browse files
committed
Add upload validation and sync profile tools
1 parent 30da29f commit da9f842

17 files changed

Lines changed: 1593 additions & 22 deletions

backend/cmd/server/agent_api_handlers.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func mountAgentRoutes(r chi.Router, app *app) {
4040
r.Get("/save/latest", app.handleSaveLatest)
4141
r.Get("/saves", app.handleAgentSavesList)
4242
r.Post("/saves", app.handleSaves)
43+
r.Post("/saves/preview", app.handleSavesPreview)
4344
r.Delete("/saves", app.handleDeleteManySaves)
4445
r.Post("/saves/rescan", app.handleAgentSaveRescan)
4546
r.Get("/saves/download-many", app.handleDownloadManySaves)
@@ -65,6 +66,9 @@ func mountAgentRoutes(r chi.Router, app *app) {
6566
r.Get("/helpers/auto-enroll", app.handleAuthAppPasswordsAutoStatus)
6667
r.Post("/helpers/auto-enroll", app.handleAuthAppPasswordsAutoEnable)
6768

69+
r.Get("/validation", app.handleValidationStatus)
70+
r.Post("/validation/rescan", app.handleValidationRescan)
71+
6872
r.Get("/modules", app.handleModulesList)
6973
r.Post("/modules/sync", app.handleModulesSync)
7074
r.Post("/modules/upload", app.handleModulesUpload)
@@ -112,10 +116,13 @@ func (a *app) handleAgentAPIIndex(w http.ResponseWriter, r *http.Request) {
112116
"helperHeartbeat": basePath + "/helpers/heartbeat",
113117
"logs": basePath + "/logs",
114118
"saves": basePath + "/saves",
119+
"savesPreview": basePath + "/saves/preview",
115120
"saveLatest": basePath + "/save/latest",
116121
"roms": basePath + "/roms",
117122
"romLookup": basePath + "/roms/lookup",
118123
"conflicts": basePath + "/conflicts",
124+
"validation": basePath + "/validation",
125+
"validationRescan": basePath + "/validation/rescan",
119126
"autoEnroll": basePath + "/helpers/auto-enroll",
120127
"modules": basePath + "/modules",
121128
"modulesSync": basePath + "/modules/sync",

backend/cmd/server/save_handlers.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ func (a *app) handleSaves(w http.ResponseWriter, r *http.Request) {
268268
SlotName: strings.TrimSpace(formValue("slotName")),
269269
SystemSlug: declaredSystem,
270270
GameSlug: canonicalSegment(gameInfo.Name, "unknown-game"),
271-
TrustedHelperSystem: helperCtx.IsHelper && strings.TrimSpace(formValue("system")) != "",
271+
TrustedHelperSystem: strings.TrimSpace(formValue("system")) != "",
272272
}
273273
if helperCtx.IsHelper && isProjectionCapableSystem(declaredSystem) && declaredSystem != "psx" && declaredSystem != "ps2" {
274274
if runtimeProfile == "" {
@@ -277,6 +277,7 @@ func (a *app) handleSaves(w http.ResponseWriter, r *http.Request) {
277277
}
278278
input, err = normalizeProjectionUpload(input, runtimeProfile)
279279
if err != nil {
280+
a.quarantineRejectedUpload(filename, "", payload, rejectedPreviewItem(filename, "", payload, declaredSystem, input.Format, runtimeProfile, err.Error()), deviceName)
280281
writeJSON(w, http.StatusUnprocessableEntity, apiError{Error: "Unprocessable Entity", Message: err.Error(), StatusCode: http.StatusUnprocessableEntity})
281282
return
282283
}
@@ -291,6 +292,7 @@ func (a *app) handleSaves(w http.ResponseWriter, r *http.Request) {
291292
if rejectReason != "" {
292293
logMessage = logMessage + ": " + rejectReason
293294
}
295+
a.quarantineRejectedUpload(filename, "", payload, previewItemFromNormalized(filename, "", payload, runtimeProfile, preview), deviceName)
294296
a.appendSyncLog(syncLogInput{
295297
DeviceName: deviceName,
296298
Action: "upload",
@@ -486,6 +488,10 @@ func (a *app) handleSaves(w http.ResponseWriter, r *http.Request) {
486488
SystemSlug: preview.Input.SystemSlug,
487489
})
488490
if errors.Is(err, errUnsupportedSaveFormat) {
491+
item := previewItemFromNormalized(filename, "", payload, runtimeProfile, preview)
492+
item.Accepted = false
493+
item.Reason = firstNonEmpty(unsupportedSaveRejectReason(err), err.Error())
494+
a.quarantineRejectedUpload(filename, "", payload, item, deviceName)
489495
writeJSON(w, http.StatusUnprocessableEntity, apiError{
490496
Error: "Unprocessable Entity",
491497
Message: errUnsupportedSaveFormat.Error(),

backend/cmd/server/save_upload_expansion.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func (a *app) handleExpandedMultipartSaveUploads(w http.ResponseWriter, helperCt
147147
SlotName: firstNonEmpty(strings.TrimSpace(formValue("slotName")), upload.SourcePath),
148148
SystemSlug: upload.SystemSlug,
149149
GameSlug: canonicalSegment(upload.Game.Name, "unknown-game"),
150-
TrustedHelperSystem: helperCtx.IsHelper && strings.TrimSpace(formValue("system")) != "",
150+
TrustedHelperSystem: strings.TrimSpace(formValue("system")) != "",
151151
}
152152
preview := a.normalizeSaveInputDetailed(input)
153153
if preview.Rejected || !isSupportedSystemSlug(preview.Input.SystemSlug) {
@@ -156,6 +156,7 @@ func (a *app) handleExpandedMultipartSaveUploads(w http.ResponseWriter, helperCt
156156
if rejectReason != "" {
157157
logMessage += ": " + rejectReason
158158
}
159+
a.quarantineRejectedUpload(upload.Filename, upload.SourcePath, upload.Payload, previewItemFromNormalized(upload.Filename, upload.SourcePath, upload.Payload, "", preview), deviceName)
159160
a.appendSyncLog(syncLogInput{DeviceName: deviceName, Action: "upload", Game: syncLogGameLabelFromFilename(upload.Filename), ErrorMessage: logMessage, SystemSlug: preview.Input.SystemSlug})
160161
errorCount++
161162
results = append(results, map[string]any{"filename": upload.Filename, "sourcePath": upload.SourcePath, "success": false, "error": errUnsupportedSaveFormat.Error(), "reason": rejectReason})
@@ -195,6 +196,10 @@ func (a *app) handleExpandedMultipartSaveUploads(w http.ResponseWriter, helperCt
195196
if reason := unsupportedSaveRejectReason(err); reason != "" {
196197
result["reason"] = reason
197198
}
199+
item := previewItemFromNormalized(upload.Filename, upload.SourcePath, upload.Payload, "", preview)
200+
item.Accepted = false
201+
item.Reason = firstNonEmpty(unsupportedSaveRejectReason(err), err.Error())
202+
a.quarantineRejectedUpload(upload.Filename, upload.SourcePath, upload.Payload, item, deviceName)
198203
}
199204
results = append(results, result)
200205
continue

0 commit comments

Comments
 (0)