299 lines
7.4 KiB
TypeScript
299 lines
7.4 KiB
TypeScript
import { fetch } from "@tauri-apps/plugin-http";
|
|
|
|
import {
|
|
type InferOutput,
|
|
array,
|
|
literal,
|
|
null_,
|
|
nullable,
|
|
pipe,
|
|
safeParse,
|
|
strictObject,
|
|
string,
|
|
transform,
|
|
union,
|
|
uuid,
|
|
} from "valibot";
|
|
|
|
type BaseRequestParams = Partial<{
|
|
path: string;
|
|
body: RequestInit["body"];
|
|
method: "GET" | "POST" | "DELETE";
|
|
}>;
|
|
|
|
// export const base = "https://haystack.johncosta.tech";
|
|
export const base = "http://localhost:3040";
|
|
|
|
const getBaseRequest = ({ path, body, method }: BaseRequestParams): Request => {
|
|
return new Request(`${base}/${path}`, {
|
|
body,
|
|
method,
|
|
});
|
|
};
|
|
|
|
const getBaseAuthorizedRequest = ({
|
|
path,
|
|
body,
|
|
method,
|
|
}: BaseRequestParams): Request => {
|
|
return new Request(`${base}/${path}`, {
|
|
headers: {
|
|
Authorization: `Bearer ${localStorage.getItem("access")?.toString()}`,
|
|
},
|
|
body,
|
|
method,
|
|
});
|
|
};
|
|
const sendImageResponseValidator = strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
ImageID: pipe(string(), uuid()),
|
|
UserID: pipe(string(), uuid()),
|
|
Status: string(),
|
|
});
|
|
|
|
export const sendImageFile = async (
|
|
imageName: string,
|
|
file: File,
|
|
): Promise<InferOutput<typeof sendImageResponseValidator>> => {
|
|
const request = getBaseAuthorizedRequest({
|
|
path: `images/${imageName}`,
|
|
body: file,
|
|
method: "POST",
|
|
});
|
|
|
|
request.headers.set("Content-Type", "application/oclet-stream");
|
|
|
|
const res = await fetch(request).then((res) => res.json());
|
|
const parsedRes = safeParse(sendImageResponseValidator, res);
|
|
|
|
if (!parsedRes.success) {
|
|
console.log(parsedRes.issues)
|
|
throw new Error(JSON.stringify(parsedRes.issues));
|
|
}
|
|
|
|
return parsedRes.output;
|
|
};
|
|
|
|
export const deleteImage = async (
|
|
imageID: string
|
|
): Promise<void> => {
|
|
const request = getBaseAuthorizedRequest({
|
|
path: `images/${imageID}`,
|
|
method: "DELETE",
|
|
});
|
|
|
|
await fetch(request);
|
|
}
|
|
|
|
export const deleteImageFromStack = async (listID: string, imageID: string): Promise<void> => {
|
|
const request = getBaseAuthorizedRequest({
|
|
path: `stacks/${listID}/${imageID}`,
|
|
method: "DELETE",
|
|
});
|
|
|
|
await fetch(request);
|
|
}
|
|
|
|
export class ImageLimitReached extends Error {
|
|
constructor() {
|
|
super();
|
|
}
|
|
}
|
|
|
|
export const sendImage = async (
|
|
imageName: string,
|
|
base64Image: string,
|
|
): Promise<InferOutput<typeof sendImageResponseValidator>> => {
|
|
const request = getBaseAuthorizedRequest({
|
|
path: `images/${imageName}`,
|
|
body: base64Image,
|
|
method: "POST",
|
|
});
|
|
|
|
request.headers.set("Content-Type", "application/base64");
|
|
|
|
const rawRes = await fetch(request);
|
|
if (!rawRes.ok && rawRes.status == 429) {
|
|
throw new ImageLimitReached()
|
|
}
|
|
|
|
const res = await rawRes.json();
|
|
|
|
const parsedRes = safeParse(sendImageResponseValidator, res);
|
|
if (!parsedRes.success) {
|
|
console.log(parsedRes.issues)
|
|
throw new Error(JSON.stringify(parsedRes.issues));
|
|
}
|
|
|
|
return parsedRes.output;
|
|
};
|
|
|
|
const imageMetaValidator = strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
ImageName: string(),
|
|
Description: string(),
|
|
Image: null_(),
|
|
});
|
|
|
|
const userImageValidator = strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
CreatedAt: pipe(string()),
|
|
ImageID: pipe(string(), uuid()),
|
|
UserID: pipe(string(), uuid()),
|
|
Image: strictObject({
|
|
...imageMetaValidator.entries,
|
|
ImageLists: pipe(nullable(array(
|
|
strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
ImageID: pipe(string(), uuid()),
|
|
ListID: pipe(string(), uuid()),
|
|
}),
|
|
)), transform(l => l ?? [])),
|
|
}),
|
|
});
|
|
|
|
const userProcessingImageValidator = strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
ImageID: pipe(string(), uuid()),
|
|
UserID: pipe(string(), uuid()),
|
|
Image: imageMetaValidator,
|
|
Status: union([
|
|
literal("not-started"),
|
|
literal("in-progress"),
|
|
literal("complete"),
|
|
]),
|
|
});
|
|
|
|
const listValidator = strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
UserID: pipe(string(), uuid()),
|
|
CreatedAt: pipe(string()),
|
|
Name: string(),
|
|
Description: nullable(string()),
|
|
|
|
Images: pipe(
|
|
nullable(
|
|
array(
|
|
strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
ImageID: pipe(string(), uuid()),
|
|
ListID: pipe(string(), uuid()),
|
|
Items: array(
|
|
strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
ImageID: pipe(string(), uuid()),
|
|
SchemaItemID: pipe(string(), uuid()),
|
|
Value: string(),
|
|
}),
|
|
),
|
|
}),
|
|
),
|
|
),
|
|
transform((n) => n ?? []),
|
|
),
|
|
|
|
Schema: strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
ListID: pipe(string(), uuid()),
|
|
SchemaItems: array(
|
|
strictObject({
|
|
ID: pipe(string(), uuid()),
|
|
SchemaID: pipe(string(), uuid()),
|
|
Item: string(),
|
|
Value: nullable(string()),
|
|
Description: string(),
|
|
}),
|
|
),
|
|
}),
|
|
});
|
|
|
|
export type List = InferOutput<typeof listValidator>;
|
|
|
|
const imageRequestValidator = strictObject({
|
|
userImages: array(userImageValidator),
|
|
processingImages: array(userProcessingImageValidator),
|
|
lists: array(listValidator),
|
|
});
|
|
|
|
export type JustTheImageWhatAreTheseNames = InferOutput<
|
|
typeof userImageValidator
|
|
>[];
|
|
|
|
export const getUserImages = async (): Promise<
|
|
InferOutput<typeof imageRequestValidator>
|
|
> => {
|
|
const request = getBaseAuthorizedRequest({ path: "images" });
|
|
|
|
const res = await fetch(request).then((res) => res.json());
|
|
|
|
console.log("Backend response: ", res);
|
|
|
|
const parsedRes = safeParse(imageRequestValidator, res);
|
|
if (!parsedRes.success) {
|
|
console.log(parsedRes.issues)
|
|
throw new Error(JSON.stringify(parsedRes.issues));
|
|
}
|
|
|
|
return parsedRes.output;
|
|
};
|
|
|
|
export const postLogin = async (email: string): Promise<void> => {
|
|
const request = getBaseRequest({
|
|
path: "auth/login",
|
|
body: JSON.stringify({ email }),
|
|
method: "POST",
|
|
});
|
|
|
|
await fetch(request);
|
|
};
|
|
|
|
const codeValidator = strictObject({
|
|
access: string(),
|
|
refresh: string(),
|
|
});
|
|
|
|
export const postCode = async (
|
|
email: string,
|
|
code: string,
|
|
): Promise<InferOutput<typeof codeValidator>> => {
|
|
const request = getBaseRequest({
|
|
path: "auth/code",
|
|
body: JSON.stringify({ email, code }),
|
|
method: "POST",
|
|
});
|
|
|
|
const res = await fetch(request).then((res) => res.json());
|
|
|
|
const parsedRes = safeParse(codeValidator, res);
|
|
if (!parsedRes.success) {
|
|
console.log(parsedRes.issues)
|
|
throw new Error(JSON.stringify(parsedRes.issues));
|
|
}
|
|
|
|
return parsedRes.output;
|
|
};
|
|
|
|
export class ReachedListLimit extends Error {
|
|
constructor() {
|
|
super();
|
|
}
|
|
}
|
|
|
|
export const createList = async (
|
|
title: string,
|
|
description: string,
|
|
): Promise<void> => {
|
|
const request = getBaseAuthorizedRequest({
|
|
path: "stacks",
|
|
method: "POST",
|
|
body: JSON.stringify({ title, description }),
|
|
});
|
|
|
|
request.headers.set("Content-Type", "application/json");
|
|
|
|
const res = await fetch(request);
|
|
if (!res.ok && res.status == 429) {
|
|
throw new ReachedListLimit();
|
|
}
|
|
};
|