adds zod API validation

This commit is contained in:
Christian Beutel
2024-12-27 01:56:35 +01:00
parent 1f81779a64
commit 75c94a3ce4
80 changed files with 847 additions and 559 deletions

View File

@@ -0,0 +1,23 @@
import { z } from "zod";
const RecordOptionsSchema = z.object({
expand: z.string().optional(),
requestKey: z.string().optional()
})
const RecordListOptionsSchema = RecordOptionsSchema.extend({
page: z.number({ coerce: true }).int().positive().optional(),
perPage: z.number({ coerce: true }).int().optional(),
sort: z.string().optional(),
filter: z.string().optional(),
expand: z.string().optional(),
requestKey: z.string().optional()
})
const RecordIdSchema = z.object({
id: z.string().length(15)
})
export { RecordOptionsSchema, RecordListOptionsSchema, RecordIdSchema }

View File

@@ -0,0 +1,17 @@
import { z, ZodType } from "zod";
import type { Comment } from "../comment";
const CommentCreateSchema = z.object({
text: z.string(),
rating: z.number(),
author: z.string().length(15),
trail: z.string().length(15),
}) satisfies ZodType<Comment>
const CommentUpdateSchema = z.object({
text: z.string().optional(),
rating: z.number().optional(),
}) satisfies ZodType<Partial<Comment>>
export { CommentCreateSchema, CommentUpdateSchema }

View File

@@ -0,0 +1,11 @@
import { z, ZodType } from "zod";
import type { Follow } from "../follow";
const FollowCreateSchema = z.object({
follower: z.string().length(15),
followee: z.string().length(15),
}) satisfies ZodType<Follow>
export { FollowCreateSchema };

View File

@@ -0,0 +1,23 @@
import { z, ZodType } from "zod";
import type { List } from "../list";
const ListCreateSchema = z.object({
name: z.string(),
public: z.boolean(),
description: z.string().optional(),
trails: z.array(z.string().length(15)),
avatar: z.string().optional(),
author: z.string()
}) satisfies ZodType<List>
const ListUpdateSchema = z.object({
name: z.string().optional(),
public: z.boolean().optional(),
description: z.string().optional(),
trails: z.array(z.string().length(15)).optional(),
"trails-": z.string().optional(),
"trails+": z.string().optional(),
avatar: z.string().optional(),
}) satisfies ZodType<Partial<List>>
export { ListCreateSchema, ListUpdateSchema };

View File

@@ -0,0 +1,16 @@
import { z, ZodType } from "zod";
import type { ListShare } from "../list_share";
const ListShareCreateSchema = z.object({
user: z.string().length(15),
list: z.string().length(15),
permission: z.enum(["view", "edit"])
}) satisfies ZodType<ListShare>
const ListShareUpdateSchema = z.object({
permission: z.enum(["view", "edit"])
}) satisfies ZodType<Partial<ListShare>>
export { ListShareCreateSchema, ListShareUpdateSchema };

View File

@@ -0,0 +1,8 @@
import { z, type ZodType } from "zod"
import { type Notification } from "../notification";
const NotificationUpdateSchema = z.object({
seen: z.literal(true)
}) satisfies ZodType<Partial<Notification>>
export { NotificationUpdateSchema }

View File

@@ -0,0 +1,28 @@
import { z, ZodType } from "zod";
import { NotificationType } from "../notification";
import type { Settings } from "../settings";
const SettingsCreateSchema = z.object({
unit: z.enum(["metric", "imperial"]).optional(),
language: z.enum(["en", "de", "fr", "hu", "it", "nl", "pl", "pt", "zh"]).optional(),
mapFocus: z.enum(["trails", "location"]).optional(),
location: z.object({
name: z.string(),
lat: z.number(),
lon: z.number()
}).optional(),
category: z.string().optional(),
tilesets: z.array(z.object({ name: z.string(), url: z.string().url() })).optional(),
terrain: z.object({ terrain: z.string().url(), hillshading: z.string().url() }).optional(),
user: z.string().optional(),
privacy: z.object({
account: z.enum(["public", "private"]),
trails: z.enum(["public", "private"]),
lists: z.enum(["public", "private"])
}).optional(),
notifications: z.record(z.enum(Object.values(NotificationType) as [string, ...string[]]), z.object({ web: z.boolean(), email: z.boolean() })).optional()
}) satisfies ZodType<Settings>
ZodType<Partial<Comment>>
export { SettingsCreateSchema };

View File

@@ -0,0 +1,31 @@
import { z, ZodType } from "zod";
import type { SummitLog } from "../summit_log";
const SummitLogCreateSchema = z.object({
id: z.string().length(15).optional(),
date: z.string().date(),
text: z.string().optional(),
gpx: z.string().optional(),
distance: z.number().nonnegative().optional(),
elevation_gain: z.number().nonnegative().optional(),
elevation_loss: z.number().nonnegative().optional(),
duration: z.number().nonnegative().optional(),
author: z.string().length(15),
photos: z.array(z.string())
}) satisfies ZodType<SummitLog>
const SummitLogUpdateSchema = z.object({
date: z.string().date().optional(),
text: z.string().optional(),
gpx: z.string().optional(),
distance: z.number().nonnegative().optional(),
elevation_gain: z.number().nonnegative().optional(),
elevation_loss: z.number().nonnegative().optional(),
duration: z.number().nonnegative().optional(),
photos: z.array(z.string()).optional(),
"photos-": z.string().optional(),
"photos+": z.string().optional(),
}) satisfies ZodType<Partial<SummitLog>>
export { SummitLogCreateSchema, SummitLogUpdateSchema };

View File

@@ -0,0 +1,49 @@
import { z, ZodType } from "zod";
import type { Trail } from "../trail";
const TrailCreateSchema = z.object({
name: z.string(),
description: z.string().optional(),
location: z.string().optional(),
date: z.string().date(),
public: z.boolean(),
difficulty: z.enum(["easy", "moderate", "difficult"]).optional(),
lat: z.number().optional(),
lon: z.number().optional(),
distance: z.number({coerce: true}).nonnegative().optional(),
elevation_gain: z.number({coerce: true}).nonnegative().optional(),
elevation_loss: z.number({coerce: true}).nonnegative().optional(),
duration: z.number({coerce: true}).nonnegative().optional(),
photos: z.array(z.string()),
thumbnail: z.number().int().nonnegative().optional(),
waypoints: z.array(z.string()),
summit_logs: z.array(z.string()),
category: z.string().optional(),
gpx: z.string().optional(),
author: z.string().length(15),
}) satisfies ZodType<Trail>
const TrailUpdateSchema = z.object({
name: z.string(),
description: z.string().optional(),
location: z.string().optional(),
date: z.string().date(),
public: z.boolean().optional(),
difficulty: z.enum(["easy", "moderate", "difficult"]).optional(),
lat: z.number().optional(),
lon: z.number().optional(),
distance: z.number({coerce: true}).nonnegative().optional(),
elevation_gain: z.number({coerce: true}).nonnegative().optional(),
elevation_loss: z.number({coerce: true}).nonnegative().optional(),
duration: z.number({coerce: true}).nonnegative().optional(),
photos: z.array(z.string()).optional(),
thumbnail: z.number().int().nonnegative().optional(),
waypoints: z.array(z.string()).optional(),
summit_logs: z.array(z.string()).optional(),
category: z.string().optional(),
gpx: z.string().optional(),
}) satisfies ZodType<Partial<Trail>>
export { TrailCreateSchema, TrailUpdateSchema };

View File

@@ -0,0 +1,16 @@
import { z, ZodType } from "zod";
import type { TrailShare } from "../trail_share";
const TrailShareCreateSchema = z.object({
user: z.string().length(15),
trail: z.string().length(15),
permission: z.enum(["view", "edit"])
}) satisfies ZodType<TrailShare>
const TrailShareUpdateSchema = z.object({
permission: z.enum(["view", "edit"])
}) satisfies ZodType<Partial<TrailShare>>
export { TrailShareCreateSchema, TrailShareUpdateSchema };

View File

@@ -0,0 +1,29 @@
import { z, ZodType } from "zod";
import type { User } from "../user";
const UserCreateSchema = (z.object({
username: z.string({ message: "required" }).min(3, "must-be-at-least-n-characters-long").regex(/^[\w][\w\.]*$/, "invalid-username"),
email: z.string({ message: "required" }).email("not-a-valid-email-address"),
password: z.string().min(8, "must-be-at-least-n-characters-long"),
passwordConfirm: z.string().optional()
}) satisfies ZodType<User>).refine((data) => data.password === data.passwordConfirm, {
message: "passwords-must-match",
path: ["passwordConfirm"],
})
const UserUpdateSchema = (z.object({
username: z.string({ message: "required" }).min(3, "must-be-at-least-n-characters-long").regex(/^[\w][\w\.]*$/, "invalid-username").optional(),
email: z.string({ message: "required" }).email("not-a-valid-email-address").optional(),
password: z.string().min(8, "must-be-at-least-n-characters-long").optional(),
oldPassword: z.string().min(8, "must-be-at-least-n-characters-long").optional(),
passwordConfirm: z.string().min(8, "must-be-at-least-n-characters-long").optional(),
bio: z.string().optional()
}) satisfies ZodType<Partial<User>>).refine((data) => data.password === data.passwordConfirm, {
message: "passwords-must-match",
path: ["passwordConfirm"],
});
export { UserCreateSchema, UserUpdateSchema };

View File

@@ -0,0 +1,28 @@
import { z, ZodType } from "zod";
import type { Waypoint } from "../waypoint";
const WaypointCreateSchema = z.object({
id: z.string().length(15).optional(),
name: z.string().optional(),
description: z.string().optional(),
text: z.string().optional(),
lat: z.number(),
lon: z.number(),
icon: z.string().optional(),
author: z.string().length(15),
photos: z.array(z.string()),
}) satisfies ZodType<Waypoint>
const WaypointUpdateSchema = z.object({
name: z.string().optional(),
description: z.string().optional(),
text: z.string().optional(),
lat: z.number().optional(),
lon: z.number().optional(),
icon: z.string().optional(),
photos: z.array(z.string()),
"photos-": z.string().optional(),
"photos+": z.string().optional(),
}) satisfies ZodType<Partial<Waypoint>>
export { WaypointCreateSchema, WaypointUpdateSchema };

View File

@@ -1,4 +1,4 @@
import type { User } from "$lib/stores/user_store";
import type { UserAnonymous } from "./user";
export class Comment {
id?: string;
@@ -8,7 +8,7 @@ export class Comment {
trail: string;
created?: string;
expand?: {
author: User
author: UserAnonymous
}
constructor(text: string, rating: number, author: string, trail: string) {

View File

@@ -6,16 +6,16 @@ class SummitLog {
date: string;
text?: string;
gpx?: string;
_gpx: File | null;
_gpx?: File | null;
photos: string[];
_photos: File[];
_photos?: File[];
distance?: number
elevation_gain?: number
elevation_loss?: number
duration?: number
author?: string;
expand: {
expand?: {
gpx_data?: string;
trails_via_summit_logs?: Trail[];
author?: UserAnonymous

View File

@@ -18,14 +18,14 @@ class Trail {
difficulty?: "easy" | "moderate" | "difficult"
lat?: number;
lon?: number;
thumbnail: number;
thumbnail?: number;
photos: string[];
gpx?: string;
created?: string;
category?: string;
waypoints: string[];
summit_logs: string[];
expand: {
expand?: {
category?: Category;
waypoints: Waypoint[]
summit_logs: SummitLog[]

View File

@@ -7,7 +7,6 @@ export type User = {
password: string,
avatar?: string;
bio?: string;
language?: string;
created?: string;
}

View File

@@ -11,7 +11,7 @@ class Waypoint {
icon?: string;
marker?: M.Marker;
photos: string[];
_photos: File[];
_photos?: File[];
author?: string;
constructor(lat: number, lon: number, params?: {