mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-01 11:56:25 +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,
|
||||
responseSchema?: z.ZodSchema<T>,
|
||||
options?: RequestOptions
|
||||
) {
|
||||
) : Promise<T> {
|
||||
const defaultOptions: RequestOptions = {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
|
@ -55,7 +55,10 @@ export class BaseApiService {
|
|||
|
||||
// Serialize body
|
||||
if (body) {
|
||||
if (mergedOptions.headers?.["Content-Type"].toLocaleLowerCase() === "application/json") {
|
||||
if (
|
||||
mergedOptions.headers?.["Content-Type"].toLocaleLowerCase() ===
|
||||
"application/json"
|
||||
) {
|
||||
requestBody = JSON.stringify(body);
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +128,7 @@ export class BaseApiService {
|
|||
async get<T>(
|
||||
url: string,
|
||||
responseSchema?: z.ZodSchema<T>,
|
||||
options?: RequestOptions
|
||||
options?: Omit<RequestOptions, "method">
|
||||
) {
|
||||
return this.request(url, undefined, responseSchema, {
|
||||
...options,
|
||||
|
|
@ -137,7 +140,7 @@ export class BaseApiService {
|
|||
url: string,
|
||||
body?: any,
|
||||
responseSchema?: z.ZodSchema<T>,
|
||||
options?: RequestOptions
|
||||
options?: Omit<RequestOptions, "method">
|
||||
) {
|
||||
return this.request(url, body, responseSchema, {
|
||||
...options,
|
||||
|
|
@ -149,7 +152,7 @@ export class BaseApiService {
|
|||
url: string,
|
||||
body?: any,
|
||||
responseSchema?: z.ZodSchema<T>,
|
||||
options?: RequestOptions
|
||||
options?: Omit<RequestOptions, "method">
|
||||
) {
|
||||
return this.request(url, body, responseSchema, {
|
||||
...options,
|
||||
|
|
@ -161,7 +164,7 @@ export class BaseApiService {
|
|||
url: string,
|
||||
body?: any,
|
||||
responseSchema?: z.ZodSchema<T>,
|
||||
options?: RequestOptions
|
||||
options?: Omit<RequestOptions, "method">
|
||||
) {
|
||||
return this.request(url, body, responseSchema, {
|
||||
...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 { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
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