mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-02 12:22:40 +02:00
add zod schemas & inferences
This commit is contained in:
parent
0c41e487d8
commit
77d49ca11c
10 changed files with 143 additions and 92 deletions
35
surfsense_web/contracts/types/auth.types.ts
Normal file
35
surfsense_web/contracts/types/auth.types.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const loginRequest = z.object({
|
||||||
|
email: z.string().email(),
|
||||||
|
password: z.string().min(1),
|
||||||
|
grant_type: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const loginResponse = z.object({
|
||||||
|
access_token: z.string(),
|
||||||
|
token_type: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const registerRequest = z.object({
|
||||||
|
email: z.string().email(),
|
||||||
|
password: z.string().min(1),
|
||||||
|
is_active: z.boolean().optional(),
|
||||||
|
is_superuser: z.boolean().optional(),
|
||||||
|
is_verified: z.boolean().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const registerResponse = z.object({
|
||||||
|
id: z.number(),
|
||||||
|
email: z.string().email(),
|
||||||
|
is_active: z.boolean(),
|
||||||
|
is_superuser: z.boolean(),
|
||||||
|
is_verified: z.boolean(),
|
||||||
|
pages_limit: z.number(),
|
||||||
|
pages_used: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type LoginRequest = z.infer<typeof loginRequest>;
|
||||||
|
export type LoginResponse = z.infer<typeof loginResponse>;
|
||||||
|
export type RegisterRequest = z.infer<typeof registerRequest>;
|
||||||
|
export type RegisterResponse = z.infer<typeof registerResponse>;
|
||||||
48
surfsense_web/contracts/types/chat.types.ts
Normal file
48
surfsense_web/contracts/types/chat.types.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
import { type Message } from "@ai-sdk/react";
|
||||||
|
import { paginationQueryParams } from ".";
|
||||||
|
|
||||||
|
export const chatTypeEnum = z.enum(["QNA"]);
|
||||||
|
|
||||||
|
export const chatSummary = z.object({
|
||||||
|
created_at: z.string(),
|
||||||
|
id: z.number(),
|
||||||
|
type: chatTypeEnum,
|
||||||
|
title: z.string(),
|
||||||
|
search_space_id: z.number(),
|
||||||
|
state_version: z.number(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const chatDetails = chatSummary.extend({
|
||||||
|
initial_connectors: z.array(z.string()),
|
||||||
|
messages: z.array(z.any()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getChatDetailsRequest = chatSummary.pick({ id: true });
|
||||||
|
|
||||||
|
export const getChatsBySearchSpaceRequest = chatSummary.pick({
|
||||||
|
search_space_id: true,
|
||||||
|
}).merge(paginationQueryParams);
|
||||||
|
|
||||||
|
export const deleteChatResponse = z.object({
|
||||||
|
message: z.literal("Chat deleted successfully"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteChatRequest = chatSummary.pick({ id: true });
|
||||||
|
|
||||||
|
export const createChatRequest = chatDetails
|
||||||
|
.omit({ created_at: true, id: true, state_version: true });
|
||||||
|
|
||||||
|
export const updateChatRequest = chatDetails
|
||||||
|
.omit({ created_at: true, state_version: true });
|
||||||
|
|
||||||
|
export type ChatSummary = z.infer<typeof chatSummary>;
|
||||||
|
export type ChatDetails = z.infer<typeof chatDetails> & { messages: Message[] };
|
||||||
|
export type GetChatDetailsRequest = z.infer<typeof getChatDetailsRequest>;
|
||||||
|
export type GetChatsBySearchSpaceRequest = z.infer<
|
||||||
|
typeof getChatsBySearchSpaceRequest
|
||||||
|
>;
|
||||||
|
export type DeleteChatResponse = z.infer<typeof deleteChatResponse>;
|
||||||
|
export type DeleteChatRequest = z.infer<typeof deleteChatRequest>;
|
||||||
|
export type CreateChatRequest = z.infer<typeof createChatRequest>;
|
||||||
|
export type UpdateChatRequest = z.infer<typeof updateChatRequest>;
|
||||||
8
surfsense_web/contracts/types/index.ts
Normal file
8
surfsense_web/contracts/types/index.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const paginationQueryParams = z.object({
|
||||||
|
limit: z.number().optional(),
|
||||||
|
skip: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type PaginationQueryParams = z.infer<typeof paginationQueryParams>;
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
import { LoginRequest, LoginResponse, RegisterRequest, RegisterResponse } from "./contracts";
|
|
||||||
|
|
||||||
export class AuthApiService {
|
|
||||||
login = async (request: LoginRequest) : Promise<LoginResponse> => {
|
|
||||||
const requestBody = new URLSearchParams();
|
|
||||||
requestBody.append("username", request.email);
|
|
||||||
requestBody.append("password", request.password);
|
|
||||||
requestBody.append("grant_type", "password");
|
|
||||||
|
|
||||||
const response = await fetch(
|
|
||||||
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/auth/jwt/login`,
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
|
||||||
},
|
|
||||||
body: requestBody.toString(),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(data.detail || `HTTP ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
register = async (request: RegisterRequest) : Promise<RegisterResponse> => {
|
|
||||||
const response = await fetch(
|
|
||||||
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/auth/register`,
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(request),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(data.detail || `HTTP ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
/**
|
|
||||||
* LOGIN
|
|
||||||
*/
|
|
||||||
export type LoginRequest = {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
grant_type?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LoginResponse = {
|
|
||||||
access_token: string;
|
|
||||||
token_type: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* REGISTER
|
|
||||||
*/
|
|
||||||
export type RegisterRequest = {
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
is_active: boolean;
|
|
||||||
is_superuser: boolean;
|
|
||||||
is_verified: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RegisterResponse = {
|
|
||||||
id: number;
|
|
||||||
email: string;
|
|
||||||
is_active: boolean;
|
|
||||||
is_superuser: boolean;
|
|
||||||
is_verified: boolean;
|
|
||||||
pages_limit: number;
|
|
||||||
pages_used: number;
|
|
||||||
};
|
|
||||||
|
|
@ -33,7 +33,7 @@ export class BaseApiService {
|
||||||
body?: any,
|
body?: any,
|
||||||
responseSchema?: z.ZodSchema<T>,
|
responseSchema?: z.ZodSchema<T>,
|
||||||
options?: RequestOptions
|
options?: RequestOptions
|
||||||
) {
|
) : Promise<T> {
|
||||||
const defaultOptions: RequestOptions = {
|
const defaultOptions: RequestOptions = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
@ -55,7 +55,10 @@ export class BaseApiService {
|
||||||
|
|
||||||
// Serialize body
|
// Serialize body
|
||||||
if (body) {
|
if (body) {
|
||||||
if (mergedOptions.headers?.["Content-Type"].toLocaleLowerCase() === "application/json") {
|
if (
|
||||||
|
mergedOptions.headers?.["Content-Type"].toLocaleLowerCase() ===
|
||||||
|
"application/json"
|
||||||
|
) {
|
||||||
requestBody = JSON.stringify(body);
|
requestBody = JSON.stringify(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,7 +128,7 @@ export class BaseApiService {
|
||||||
async get<T>(
|
async get<T>(
|
||||||
url: string,
|
url: string,
|
||||||
responseSchema?: z.ZodSchema<T>,
|
responseSchema?: z.ZodSchema<T>,
|
||||||
options?: RequestOptions
|
options?: Omit<RequestOptions, "method">
|
||||||
) {
|
) {
|
||||||
return this.request(url, undefined, responseSchema, {
|
return this.request(url, undefined, responseSchema, {
|
||||||
...options,
|
...options,
|
||||||
|
|
@ -137,7 +140,7 @@ export class BaseApiService {
|
||||||
url: string,
|
url: string,
|
||||||
body?: any,
|
body?: any,
|
||||||
responseSchema?: z.ZodSchema<T>,
|
responseSchema?: z.ZodSchema<T>,
|
||||||
options?: RequestOptions
|
options?: Omit<RequestOptions, "method">
|
||||||
) {
|
) {
|
||||||
return this.request(url, body, responseSchema, {
|
return this.request(url, body, responseSchema, {
|
||||||
...options,
|
...options,
|
||||||
|
|
@ -149,7 +152,7 @@ export class BaseApiService {
|
||||||
url: string,
|
url: string,
|
||||||
body?: any,
|
body?: any,
|
||||||
responseSchema?: z.ZodSchema<T>,
|
responseSchema?: z.ZodSchema<T>,
|
||||||
options?: RequestOptions
|
options?: Omit<RequestOptions, "method">
|
||||||
) {
|
) {
|
||||||
return this.request(url, body, responseSchema, {
|
return this.request(url, body, responseSchema, {
|
||||||
...options,
|
...options,
|
||||||
|
|
@ -161,7 +164,7 @@ export class BaseApiService {
|
||||||
url: string,
|
url: string,
|
||||||
body?: any,
|
body?: any,
|
||||||
responseSchema?: z.ZodSchema<T>,
|
responseSchema?: z.ZodSchema<T>,
|
||||||
options?: RequestOptions
|
options?: Omit<RequestOptions, "method">
|
||||||
) {
|
) {
|
||||||
return this.request(url, body, responseSchema, {
|
return this.request(url, body, responseSchema, {
|
||||||
...options,
|
...options,
|
||||||
|
|
@ -169,3 +172,8 @@ export class BaseApiService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const baseApiService = new BaseApiService(
|
||||||
|
typeof window !== "undefined" ? localStorage.getItem("surfsense_bearer_token") || "" : "",
|
||||||
|
process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || ""
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
// Will contain a ChatApiService class that will be used to make API calls
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
// Will contains contracts for all chat related APIs
|
|
||||||
30
surfsense_web/lib/error.ts
Normal file
30
surfsense_web/lib/error.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
export class AppError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = this.constructor.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NetworkError extends AppError {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ValidationError extends AppError {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuthenticationError extends AppError {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuthorizationError extends AppError {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
|
import { Message } from "@ai-sdk/react";
|
||||||
import { type ClassValue, clsx } from "clsx";
|
import { type ClassValue, clsx } from "clsx";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function getChatTitleFromMessages(messages: Message[]) {
|
||||||
|
const userMessages = messages.filter((msg) => msg.role === "user");
|
||||||
|
if (userMessages.length === 0) return "Untitled Chat";
|
||||||
|
return userMessages[0].content;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue