mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-08 20:25:19 +02:00
feat(web): add public chat and thread API types and services
This commit is contained in:
parent
aeb0deb21e
commit
9d7259aab9
5 changed files with 170 additions and 2 deletions
19
surfsense_web/contracts/types/chat-threads.types.ts
Normal file
19
surfsense_web/contracts/types/chat-threads.types.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Toggle public share
|
||||
*/
|
||||
export const togglePublicShareRequest = z.object({
|
||||
thread_id: z.number(),
|
||||
enabled: z.boolean(),
|
||||
});
|
||||
|
||||
export const togglePublicShareResponse = z.object({
|
||||
enabled: z.boolean(),
|
||||
public_url: z.string().nullable(),
|
||||
share_token: z.string().nullable(),
|
||||
});
|
||||
|
||||
// Type exports
|
||||
export type TogglePublicShareRequest = z.infer<typeof togglePublicShareRequest>;
|
||||
export type TogglePublicShareResponse = z.infer<typeof togglePublicShareResponse>;
|
||||
61
surfsense_web/contracts/types/public-chat.types.ts
Normal file
61
surfsense_web/contracts/types/public-chat.types.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Author info for public chat
|
||||
*/
|
||||
export const publicAuthor = z.object({
|
||||
display_name: z.string().nullable(),
|
||||
avatar_url: z.string().nullable(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Message in a public chat
|
||||
*/
|
||||
export const publicChatMessage = z.object({
|
||||
role: z.string(),
|
||||
content: z.unknown(),
|
||||
author: publicAuthor.nullable(),
|
||||
created_at: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Thread info for public chat
|
||||
*/
|
||||
export const publicChatThread = z.object({
|
||||
title: z.string(),
|
||||
created_at: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Get public chat
|
||||
*/
|
||||
export const getPublicChatRequest = z.object({
|
||||
share_token: z.string(),
|
||||
});
|
||||
|
||||
export const getPublicChatResponse = z.object({
|
||||
thread: publicChatThread,
|
||||
messages: z.array(publicChatMessage),
|
||||
});
|
||||
|
||||
/**
|
||||
* Clone public chat
|
||||
*/
|
||||
export const clonePublicChatRequest = z.object({
|
||||
share_token: z.string(),
|
||||
});
|
||||
|
||||
export const clonePublicChatResponse = z.object({
|
||||
status: z.string(),
|
||||
task_id: z.string(),
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
// Type exports
|
||||
export type PublicAuthor = z.infer<typeof publicAuthor>;
|
||||
export type PublicChatMessage = z.infer<typeof publicChatMessage>;
|
||||
export type PublicChatThread = z.infer<typeof publicChatThread>;
|
||||
export type GetPublicChatRequest = z.infer<typeof getPublicChatRequest>;
|
||||
export type GetPublicChatResponse = z.infer<typeof getPublicChatResponse>;
|
||||
export type ClonePublicChatRequest = z.infer<typeof clonePublicChatRequest>;
|
||||
export type ClonePublicChatResponse = z.infer<typeof clonePublicChatResponse>;
|
||||
|
|
@ -23,7 +23,10 @@ export type RequestOptions = {
|
|||
class BaseApiService {
|
||||
baseUrl: string;
|
||||
|
||||
noAuthEndpoints: string[] = ["/auth/jwt/login", "/auth/register", "/auth/refresh"]; // Add more endpoints as needed
|
||||
noAuthEndpoints: string[] = ["/auth/jwt/login", "/auth/register", "/auth/refresh"];
|
||||
|
||||
// Prefixes that don't require auth (checked with startsWith)
|
||||
noAuthPrefixes: string[] = ["/api/v1/public/"];
|
||||
|
||||
// Use a getter to always read fresh token from localStorage
|
||||
// This ensures the token is always up-to-date after login/logout
|
||||
|
|
@ -84,7 +87,10 @@ class BaseApiService {
|
|||
}
|
||||
|
||||
// Validate the bearer token
|
||||
if (!this.bearerToken && !this.noAuthEndpoints.includes(url)) {
|
||||
const isNoAuthEndpoint =
|
||||
this.noAuthEndpoints.includes(url) ||
|
||||
this.noAuthPrefixes.some((prefix) => url.startsWith(prefix));
|
||||
if (!this.bearerToken && !isNoAuthEndpoint) {
|
||||
throw new AuthenticationError("You are not authenticated. Please login again.");
|
||||
}
|
||||
|
||||
|
|
|
|||
33
surfsense_web/lib/apis/chat-threads-api.service.ts
Normal file
33
surfsense_web/lib/apis/chat-threads-api.service.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import {
|
||||
type TogglePublicShareRequest,
|
||||
type TogglePublicShareResponse,
|
||||
togglePublicShareRequest,
|
||||
togglePublicShareResponse,
|
||||
} from "@/contracts/types/chat-threads.types";
|
||||
import { ValidationError } from "../error";
|
||||
import { baseApiService } from "./base-api.service";
|
||||
|
||||
class ChatThreadsApiService {
|
||||
/**
|
||||
* Toggle public sharing for a thread.
|
||||
* Requires authentication.
|
||||
*/
|
||||
togglePublicShare = async (
|
||||
request: TogglePublicShareRequest
|
||||
): Promise<TogglePublicShareResponse> => {
|
||||
const parsed = togglePublicShareRequest.safeParse(request);
|
||||
|
||||
if (!parsed.success) {
|
||||
const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", ");
|
||||
throw new ValidationError(`Invalid request: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return baseApiService.patch(
|
||||
`/api/v1/threads/${parsed.data.thread_id}/public-share`,
|
||||
togglePublicShareResponse,
|
||||
{ body: { enabled: parsed.data.enabled } }
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const chatThreadsApiService = new ChatThreadsApiService();
|
||||
49
surfsense_web/lib/apis/public-chat-api.service.ts
Normal file
49
surfsense_web/lib/apis/public-chat-api.service.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import {
|
||||
type ClonePublicChatRequest,
|
||||
type ClonePublicChatResponse,
|
||||
clonePublicChatRequest,
|
||||
clonePublicChatResponse,
|
||||
type GetPublicChatRequest,
|
||||
type GetPublicChatResponse,
|
||||
getPublicChatRequest,
|
||||
getPublicChatResponse,
|
||||
} from "@/contracts/types/public-chat.types";
|
||||
import { ValidationError } from "../error";
|
||||
import { baseApiService } from "./base-api.service";
|
||||
|
||||
class PublicChatApiService {
|
||||
/**
|
||||
* Get a public chat by share token.
|
||||
* No authentication required.
|
||||
*/
|
||||
getPublicChat = async (request: GetPublicChatRequest): Promise<GetPublicChatResponse> => {
|
||||
const parsed = getPublicChatRequest.safeParse(request);
|
||||
|
||||
if (!parsed.success) {
|
||||
const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", ");
|
||||
throw new ValidationError(`Invalid request: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return baseApiService.get(`/api/v1/public/${parsed.data.share_token}`, getPublicChatResponse);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clone a public chat to the user's account.
|
||||
* Requires authentication.
|
||||
*/
|
||||
clonePublicChat = async (request: ClonePublicChatRequest): Promise<ClonePublicChatResponse> => {
|
||||
const parsed = clonePublicChatRequest.safeParse(request);
|
||||
|
||||
if (!parsed.success) {
|
||||
const errorMessage = parsed.error.issues.map((issue) => issue.message).join(", ");
|
||||
throw new ValidationError(`Invalid request: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return baseApiService.post(
|
||||
`/api/v1/public/${parsed.data.share_token}/clone`,
|
||||
clonePublicChatResponse
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const publicChatApiService = new PublicChatApiService();
|
||||
Loading…
Add table
Add a link
Reference in a new issue