Add Backend Validation

Dieser Commit ist enthalten in:
Chaoscaot 2023-08-16 13:36:53 +02:00
Ursprung b14ef2981e
Commit 4a501ea870
Signiert von: Chaoscaot
GPG-Schlüssel-ID: BDF8FADD7D5EDB7A
8 geänderte Dateien mit 104 neuen und 70 gelöschten Zeilen

Datei anzeigen

@ -14,6 +14,10 @@
"@tsconfig/svelte": "^4.0.1", "@tsconfig/svelte": "^4.0.1",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"flowbite-svelte-icons": "^0.2.5", "flowbite-svelte-icons": "^0.2.5",
"flowbite": "^1.7.0",
"flowbite-svelte": "^0.39.2",
"svelte-awesome": "^3.2.0",
"tailwind-merge": "^1.13.2",
"postcss": "^8.4.24", "postcss": "^8.4.24",
"sass": "^1.62.0", "sass": "^1.62.0",
"svelte": "^4.0.1", "svelte": "^4.0.1",
@ -25,14 +29,9 @@
"vite": "^4.3.9" "vite": "^4.3.9"
}, },
"dependencies": { "dependencies": {
"@popperjs/core": "^2.11.8",
"color": "^4.2.3", "color": "^4.2.3",
"flowbite": "^1.7.0",
"flowbite-svelte": "^0.39.2",
"install": "^0.13.0",
"moment": "^2.29.4", "moment": "^2.29.4",
"svelte-awesome": "^3.2.0",
"svelte-spa-router": "^3.3.0", "svelte-spa-router": "^3.3.0",
"tailwind-merge": "^1.13.2" "zod": "^3.21.4"
} }
} }

Datei anzeigen

@ -28,6 +28,9 @@ dependencies:
tailwind-merge: tailwind-merge:
specifier: ^1.13.2 specifier: ^1.13.2
version: 1.13.2 version: 1.13.2
zod:
specifier: ^3.21.4
version: 3.21.4
devDependencies: devDependencies:
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
@ -1459,6 +1462,10 @@ packages:
engines: {node: '>= 14'} engines: {node: '>= 14'}
dev: true dev: true
/zod@3.21.4:
resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
dev: false
settings: settings:
autoInstallPeers: true autoInstallPeers: true
excludeLinksFromLockfile: false excludeLinksFromLockfile: false

Datei anzeigen

@ -1,6 +1,8 @@
import type {ExtendedEvent, ShortEvent, SWEvent} from "../types/event.js"; import type {ExtendedEvent, ShortEvent, SWEvent} from "../types/event.js";
import {fetchWithToken} from "./repo.js"; import {fetchWithToken} from "./repo.js";
import type {Moment} from "moment"; import type {Moment} from "moment";
import {ExtendedEventSchema, ShortEventSchema, SWEventSchema} from "../types/event.js";
import {z} from "zod";
export interface CreateEvent { export interface CreateEvent {
name: string name: string
@ -26,7 +28,7 @@ export class EventRepo {
const res = await fetchWithToken(this.token, "/events"); const res = await fetchWithToken(this.token, "/events");
if (res.ok) { if (res.ok) {
return await res.json() as ShortEvent[]; return z.array(ShortEventSchema).parse(await res.json());
} else { } else {
throw new Error("Could not fetch events: " + res.statusText); throw new Error("Could not fetch events: " + res.statusText);
} }
@ -36,7 +38,7 @@ export class EventRepo {
const res = await fetchWithToken(this.token, `/events/${id}`); const res = await fetchWithToken(this.token, `/events/${id}`);
if (res.ok) { if (res.ok) {
return await res.json() as ExtendedEvent; return ExtendedEventSchema.parse(await res.json());
} else { } else {
throw new Error("Could not fetch event: " + res.statusText); throw new Error("Could not fetch event: " + res.statusText);
} }
@ -53,7 +55,7 @@ export class EventRepo {
}); });
if (res.ok) { if (res.ok) {
return await res.json() as SWEvent; return SWEventSchema.parse(await res.json());
} else { } else {
throw new Error("Could not create event: " + res.statusText); throw new Error("Could not create event: " + res.statusText);
} }
@ -78,7 +80,7 @@ export class EventRepo {
}); });
if (res.ok) { if (res.ok) {
return await res.json() as SWEvent; return SWEventSchema.parse(await res.json());
} else { } else {
throw new Error("Could not update event: " + res.statusText); throw new Error("Could not update event: " + res.statusText);
} }

Datei anzeigen

@ -1,6 +1,8 @@
import type {EventFight} from "../types/event.js"; import type {EventFight} from "../types/event.js";
import {fetchWithToken} from "./repo.js"; import {fetchWithToken} from "./repo.js";
import type {Moment} from "moment"; import type {Moment} from "moment";
import {z} from "zod";
import {EventFightSchema} from "../types/event.js";
export interface CreateFight { export interface CreateFight {
spielmodus: string spielmodus: string
@ -29,7 +31,7 @@ export class FightRepo {
const res = await fetchWithToken(this.token, `/events/${eventId}/fights`); const res = await fetchWithToken(this.token, `/events/${eventId}/fights`);
if (res.ok) { if (res.ok) {
return await res.json() as EventFight[]; return z.array(EventFightSchema).parse(await res.json());
} else { } else {
throw new Error("Could not fetch fights: " + res.statusText); throw new Error("Could not fetch fights: " + res.statusText);
} }
@ -51,7 +53,7 @@ export class FightRepo {
}) })
if (res.ok) { if (res.ok) {
return await res.json() as EventFight; return EventFightSchema.parse(await res.json());
} else { } else {
throw new Error("Could not create fight: " + res.statusText); throw new Error("Could not create fight: " + res.statusText);
} }
@ -72,7 +74,7 @@ export class FightRepo {
}) })
if (res.ok) { if (res.ok) {
return await res.json() as EventFight; return EventFightSchema.parse(await res.json());
} else { } else {
throw new Error("Could not update fight: " + res.statusText); throw new Error("Could not update fight: " + res.statusText);
} }

Datei anzeigen

@ -3,20 +3,23 @@ import {cached, cachedFamily} from "./cached.js";
import type {Team} from "../types/team.js"; import type {Team} from "../types/team.js";
import {get, writable} from "svelte/store"; import {get, writable} from "svelte/store";
import {tokenStore} from "../repo/repo.js"; import {tokenStore} from "../repo/repo.js";
import {PlayerSchema} from "../types/data.js";
import {z} from "zod";
import {TeamSchema} from "../types/team.js";
export const schemTypes = cached<SchematicType[]>([], () => { export const schemTypes = cached<SchematicType[]>([], () => {
return fetch("https://steamwar.de/eventplanner-api/data/schematicTypes", {headers: {"X-SW-Auth": get(tokenStore)}}) return fetch("https://steamwar.de/eventplanner-api/data/schematicTypes", {headers: {"X-SW-Auth": get(tokenStore)}})
.then(res => res.json()) .then(res => res.json())
}) })
export const players = cached<Player[]>([], () => { export const players = cached<Player[]>([], async () => {
return fetch("https://steamwar.de/eventplanner-api/data/users", {headers: {"X-SW-Auth": get(tokenStore)}}) const res = await fetch("https://steamwar.de/eventplanner-api/data/users", {headers: {"X-SW-Auth": get(tokenStore)}});
.then(res => res.json()) return z.array(PlayerSchema).parse(await res.json());
}) })
export const gamemodes = cached<string[]>([], () => { export const gamemodes = cached<string[]>([], async () => {
return fetch("https://steamwar.de/eventplanner-api/data/gamemodes", {headers: {"X-SW-Auth": get(tokenStore)}}) const res = await fetch("https://steamwar.de/eventplanner-api/data/gamemodes", {headers: {"X-SW-Auth": get(tokenStore)}});
.then(res => res.json()) return z.array(z.string()).parse(await res.json());
}) })
export const maps = cachedFamily<string, string[]>([], async (gamemode) => { export const maps = cachedFamily<string, string[]>([], async (gamemode) => {
@ -30,14 +33,14 @@ export const maps = cachedFamily<string, string[]>([], async (gamemode) => {
} }
}) })
export const groups = cached<string[]>([], () => { export const groups = cached<string[]>([], async () => {
return fetch("https://steamwar.de/eventplanner-api/data/groups", {headers: {"X-SW-Auth": get(tokenStore)}}) const res = await fetch("https://steamwar.de/eventplanner-api/data/groups", {headers: {"X-SW-Auth": get(tokenStore)}});
.then(res => res.json()) return z.array(z.string()).parse(await res.json());
}) })
export const teams = cached<Team[]>([], () => { export const teams = cached<Team[]>([], async () => {
return fetch("https://steamwar.de/eventplanner-api/team", {headers: {"X-SW-Auth": get(tokenStore)}}) const res = await fetch("https://steamwar.de/eventplanner-api/team", {headers: {"X-SW-Auth": get(tokenStore)}});
.then(res => res.json()) return z.array(TeamSchema).parse(await res.json());
}) })
export const isWide = writable(window.innerWidth >= 640); export const isWide = writable(window.innerWidth >= 640);

Datei anzeigen

@ -1,10 +1,16 @@
export interface SchematicType { import {z} from "zod";
name: string;
db: string;
}
export interface Player { export const SchematicTypeSchema = z.object({
id: number; name: z.string(),
name: string; db: z.string(),
uuid: string; })
}
export type SchematicType = z.infer<typeof SchematicTypeSchema>;
export const PlayerSchema = z.object({
id: z.number(),
name: z.string(),
uuid: z.string().uuid(),
})
export type Player = z.infer<typeof PlayerSchema>;

Datei anzeigen

@ -1,35 +1,46 @@
import type {Team} from "./team.js"; import type {Team} from "./team.js";
import type {Player} from "./data.js"; import type {Player} from "./data.js";
import {z} from "zod";
import {TeamSchema} from "./team.js";
import {PlayerSchema} from "./data.js";
export interface ShortEvent { export const ShortEventSchema = z.object({
id: number; id: z.number(),
name: string; name: z.string(),
start: number; start: z.number(),
end: number; end: z.number(),
} })
export interface SWEvent extends ShortEvent { export type ShortEvent = z.infer<typeof ShortEventSchema>;
deadline: number;
maxTeamMembers: number;
schemType: string | null;
publicSchemsOnly: boolean;
spectateSystem: boolean;
}
export interface EventFight { export const SWEventSchema = ShortEventSchema.extend({
id: number; deadline: z.number(),
spielmodus: string; maxTeamMembers: z.number(),
map: string; schemType: z.string().nullable(),
blueTeam: Team; publicSchemsOnly: z.boolean(),
redTeam: Team; spectateSystem: z.boolean(),
kampfleiter: Player | null; })
start: number;
ergebnis: number;
group: string | null;
}
export interface ExtendedEvent { export type SWEvent = z.infer<typeof SWEventSchema>;
event: SWEvent;
teams: Team[]; export const EventFightSchema = z.object({
fights: EventFight[]; id: z.number(),
} spielmodus: z.string(),
map: z.string(),
blueTeam: TeamSchema,
redTeam: TeamSchema,
kampfleiter: PlayerSchema.nullable(),
start: z.number(),
ergebnis: z.number(),
group: z.string().nullable(),
})
export type EventFight = z.infer<typeof EventFightSchema>;
export const ExtendedEventSchema = z.object({
event: SWEventSchema,
teams: z.array(TeamSchema),
fights: z.array(EventFightSchema),
})
export type ExtendedEvent = z.infer<typeof ExtendedEventSchema>;

Datei anzeigen

@ -1,8 +1,12 @@
import Color from "color"; import {z} from "zod";
export const TeamSchema = z.object({
id: z.number(),
name: z.string(),
kuerzel: z.string().min(1).max(4),
color: z.string().max(1),
})
export type Team = z.infer<typeof TeamSchema>;
export interface Team {
id: number;
name: string;
kuerzel: string;
color: string;
}