Add Backend Validation
Dieser Commit ist enthalten in:
Ursprung
b14ef2981e
Commit
4a501ea870
11
package.json
11
package.json
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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>;
|
||||||
|
@ -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>;
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren