Move some of the "add spot" checks from client-side to server-side to avoid duplication and enforce them in the proper place. #95

This commit is contained in:
Ian Renton
2026-06-20 10:30:24 +01:00
parent e08a183d1b
commit 8d09484425
3 changed files with 46 additions and 63 deletions

View File

@@ -180,9 +180,40 @@ class APISpotHandler(tornado.web.RequestHandler):
self.set_header("Content-Type", "application/json") self.set_header("Content-Type", "application/json")
return return
# Validate upstream submission requirements
if submit_upstream and upstream_provider_name:
if not spot.sig:
self.set_status(422)
self.write(json.dumps("Error - a SIG must be selected to submit upstream.",
default=serialize_everything))
self.set_header("Cache-Control", "no-store")
self.set_header("Content-Type", "application/json")
return
if not spot.sig_refs and upstream_provider_name != "Tiles":
self.set_status(422)
self.write(json.dumps("Error - a SIG reference is required to submit upstream.",
default=serialize_everything))
self.set_header("Cache-Control", "no-store")
self.set_header("Content-Type", "application/json")
return
if not spot.dx_grid and upstream_provider_name == "Tiles":
self.set_status(422)
self.write(json.dumps("Error - a grid reference is required to submit upstream to Tiles on the Air.",
default=serialize_everything))
self.set_header("Cache-Control", "no-store")
self.set_header("Content-Type", "application/json")
return
if not spot.mode and upstream_provider_name == "Tiles":
self.set_status(422)
self.write(json.dumps("Error - a mode is required to submit upstream to Tiles on the Air.",
default=serialize_everything))
self.set_header("Cache-Control", "no-store")
self.set_header("Content-Type", "application/json")
return
# Submit upstream if requested # Submit upstream if requested
upstream_warning = None upstream_warning = None
if submit_upstream and upstream_provider_name and spot.sig: if submit_upstream and upstream_provider_name:
provider = self._find_provider(upstream_provider_name, spot.sig) provider = self._find_provider(upstream_provider_name, spot.sig)
if provider: if provider:
try: try:

View File

@@ -24,14 +24,14 @@
</div> </div>
</div> </div>
<div class="card-body"> <div class="card-body">
<form class="row g-3"> <form class="row g-3" onsubmit="return addSpot();">
<div class="col-auto"> <div class="col-auto">
<label for="dx-call" class="form-label">DX Call *</label> <label for="dx-call" class="form-label">DX Call *</label>
<input type="text" class="form-control input-narrow" id="dx-call" placeholder="N0CALL"> <input type="text" class="form-control input-narrow" id="dx-call" placeholder="N0CALL" required>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<label for="freq" class="form-label">Frequency (kHz) *</label> <label for="freq" class="form-label">Frequency (kHz) *</label>
<input type="text" class="form-control input-narrow" id="freq" placeholder="e.g. 14100"> <input type="text" class="form-control input-narrow" id="freq" placeholder="e.g. 14100" required>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<label for="mode" class="form-label">Mode</label> <label for="mode" class="form-label">Mode</label>
@@ -60,10 +60,10 @@
<div class="col-auto"> <div class="col-auto">
<label for="de-call" class="form-label">Your Call *</label> <label for="de-call" class="form-label">Your Call *</label>
<input type="text" class="form-control storeable-text input-narrow" id="de-call" <input type="text" class="form-control storeable-text input-narrow" id="de-call"
placeholder="N0CALL"> placeholder="N0CALL" required>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<button type="button" class="btn btn-primary mt-2em" onclick="addSpot();">Spot</button> <button type="submit" class="btn btn-primary mt-2em">Spot</button>
</div> </div>
</form> </form>

View File

@@ -205,40 +205,14 @@ function addSpot() {
// Prepare the spot object for the server // Prepare the spot object for the server
const spot = {}; const spot = {};
if (dx !== "") {
spot["dx_call"] = dx; spot["dx_call"] = dx;
} else {
// todo maybe for neatness just make all these error/rejections server side rather than having logic in two places
showAddSpotError("A DX callsign is required in order to spot.");
return;
}
if (freqStr !== "") {
spot["freq"] = parseFloat(freqStr) * 1000; spot["freq"] = parseFloat(freqStr) * 1000;
} else { if (mode !== "") spot["mode"] = mode;
showAddSpotError("A frequency is required in order to spot."); if (sig !== "") spot["sig"] = sig;
return; if (sigRef !== "") spot["sig_refs"] = [{id: sigRef}];
} if (dxGrid !== "") spot["dx_grid"] = dxGrid;
if (mode !== "") { if (comment !== "") spot["comment"] = comment;
spot["mode"] = mode;
}
if (sig !== "") {
spot["sig"] = sig;
}
if (sigRef !== "") {
spot["sig_refs"] = [{id: sigRef}];
}
if (dxGrid !== "") {
spot["dx_grid"] = dxGrid;
}
if (comment !== "") {
spot["comment"] = comment;
}
if (de !== "") {
spot["de_call"] = de; spot["de_call"] = de;
} else {
showAddSpotError("A spotter callsign is required in order to spot.");
return;
}
spot["time"] = moment.utc().valueOf() / 1000.0; spot["time"] = moment.utc().valueOf() / 1000.0;
// Prepare "handling" structure to tell the server what to do with this spot // Prepare "handling" structure to tell the server what to do with this spot
@@ -246,35 +220,13 @@ function addSpot() {
// Add CAPTCHA token if reCAPTCHA is loaded // Add CAPTCHA token if reCAPTCHA is loaded
if (window._recaptchaWidgetId !== undefined) { if (window._recaptchaWidgetId !== undefined) {
const token = grecaptcha.getResponse(window._recaptchaWidgetId); handling["captcha_token"] = grecaptcha.getResponse(window._recaptchaWidgetId);
if (!token) {
showAddSpotError("Please complete the CAPTCHA to submit upstream.");
return;
}
handling["captcha_token"] = token;
} }
// Upstream submission // Upstream submission
const submitUpstream = $("#submit-upstream").is(":checked"); const submitUpstream = $("#submit-upstream").is(":checked");
const upstreamProviderName = getSelectedUpstreamProvider(); const upstreamProviderName = getSelectedUpstreamProvider();
if (submitUpstream && upstreamProviderName) { if (submitUpstream && upstreamProviderName) {
if (!sig) {
showAddSpotError("A SIG must be selected to submit upstream.");
return;
}
if (!sigRef && upstreamProviderName !== "Tiles") {
showAddSpotError("A SIG reference is required to submit upstream.");
return;
}
if (!dxGrid && upstreamProviderName === "Tiles") {
showAddSpotError("A grid reference is required to submit upstream to Tiles on the Air.");
return;
}
if (!mode && upstreamProviderName === "Tiles") {
showAddSpotError("A mode is required to submit upstream to Tiles on the Air.");
return;
}
handling["submit_upstream"] = true; handling["submit_upstream"] = true;
handling["upstream_provider"] = upstreamProviderName; handling["upstream_provider"] = upstreamProviderName;
handling["upstream_credentials"] = loadCredentials(upstreamProviderName); handling["upstream_credentials"] = loadCredentials(upstreamProviderName);