Compare commits
10 Commits
edfaabeb0d
...
88adc15899
| Author | SHA1 | Date | |
|---|---|---|---|
| 88adc15899 | |||
| d9053e1fc0 | |||
| 3ae805220a | |||
| a76ec7ae1d | |||
| 83254fb770 | |||
| 67841355ac | |||
| 542b6e45c3 | |||
| 185a16be6f | |||
| bb4a581154 | |||
| 680d734f85 |
@@ -68,9 +68,18 @@ function getQueryClient() {
|
||||
// suspends during the initial render. This may not be needed if we
|
||||
// have a suspense boundary BELOW the creation of the query client
|
||||
if (!browserQueryClient) browserQueryClient = makeQueryClient();
|
||||
// This code is for all users
|
||||
window.__TANSTACK_QUERY_CLIENT__ = browserQueryClient;
|
||||
return browserQueryClient;
|
||||
}
|
||||
|
||||
// This code is only for TypeScript
|
||||
declare global {
|
||||
interface Window {
|
||||
__TANSTACK_QUERY_CLIENT__: import("@tanstack/query-core").QueryClient;
|
||||
}
|
||||
}
|
||||
|
||||
export function SignInWithGoogle() {
|
||||
const pageContext = usePageContext();
|
||||
/** This is populated using the +onCreatePageContext.server.ts hook */
|
||||
@@ -232,7 +241,9 @@ function NavLinkChat() {
|
||||
const { urlPathname } = pageContext;
|
||||
const trpc = useTRPC();
|
||||
const queryClient = useQueryClient();
|
||||
// const
|
||||
const { data: conversations } = useQuery(
|
||||
trpc.chat.conversations.fetchAll.queryOptions()
|
||||
);
|
||||
const startConversation = useMutation(
|
||||
trpc.chat.conversations.start.mutationOptions({
|
||||
onSuccess: () => {
|
||||
@@ -286,9 +297,6 @@ function NavLinkChat() {
|
||||
})
|
||||
);
|
||||
|
||||
const { data: conversations } = useQuery(
|
||||
trpc.chat.conversations.fetchAll.queryOptions()
|
||||
);
|
||||
// const selectedConversationId = useStore(
|
||||
// (state) => state.selectedConversationId
|
||||
// );
|
||||
@@ -356,7 +364,7 @@ function NavLinkChat() {
|
||||
>
|
||||
{conversations?.map((conversation) => (
|
||||
<NavLink
|
||||
key={conversation.id}
|
||||
key={conversation.id + conversation.title}
|
||||
href={`/chat/${conversation.id}`}
|
||||
label={conversation.title}
|
||||
className="hover-container"
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
"@mantine/hooks": "^8.1.1",
|
||||
"@neondatabase/serverless": "^1.0.1",
|
||||
"@openrouter/ai-sdk-provider": "^1.1.2",
|
||||
"@sinclair/typebox": "^0.34.37",
|
||||
"@tabler/icons-react": "^3.34.1",
|
||||
"@tanstack/react-query": "^5.85.3",
|
||||
"@trpc/client": "^11.4.4",
|
||||
|
||||
@@ -316,14 +316,18 @@ export default function ChatPage() {
|
||||
newConversations: null,
|
||||
};
|
||||
}
|
||||
const newConversations: Array<Conversation> = [
|
||||
...previousConversations,
|
||||
{
|
||||
...conversation,
|
||||
title,
|
||||
} as Conversation,
|
||||
];
|
||||
queryClient.setQueryData(
|
||||
|
||||
/** MUST make a deep, immutable copy in order to trigger re-render of
|
||||
* oberserving components. */
|
||||
const newConversations: Array<Conversation> = JSON.parse(
|
||||
JSON.stringify(previousConversations)
|
||||
);
|
||||
const conversationToUpdate = newConversations.find((c) => c.id === id);
|
||||
if (!conversationToUpdate) {
|
||||
return { previousConversations, newConversations };
|
||||
}
|
||||
conversationToUpdate.title = title;
|
||||
queryClient.setQueryData<Array<Conversation>>(
|
||||
trpc.chat.conversations.fetchAll.queryKey(),
|
||||
newConversations
|
||||
);
|
||||
@@ -505,6 +509,12 @@ export default function ChatPage() {
|
||||
// onChange={(e) => {
|
||||
// setConversationTitle(e.target.value);
|
||||
// }}
|
||||
onKeyUp={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
e.currentTarget.blur();
|
||||
}
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
updateConversationTitle.mutateAsync({
|
||||
id: conversationId,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { PageContextServer } from "vike/types";
|
||||
import { createCaller } from "../../../server/trpc/chat.js";
|
||||
import { getDbClient } from "../../../database/postgres.js";
|
||||
import { getOpenrouter } from "../../../server/provider.js";
|
||||
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
||||
import { env } from "../../../server/env.js";
|
||||
|
||||
export type Data = Awaited<ReturnType<typeof data>>;
|
||||
@@ -9,9 +9,10 @@ export type Data = Awaited<ReturnType<typeof data>>;
|
||||
export const data = async (pageContext: PageContextServer) => {
|
||||
const { id } = pageContext.routeParams;
|
||||
const caller = createCaller({
|
||||
openrouter: getOpenrouter(
|
||||
(pageContext.env?.OPENROUTER_API_KEY || env.OPENROUTER_API_KEY) as string
|
||||
),
|
||||
openrouter: createOpenRouter({
|
||||
apiKey: (pageContext.env?.OPENROUTER_API_KEY ||
|
||||
env.OPENROUTER_API_KEY) as string,
|
||||
}),
|
||||
jwt: pageContext.session?.jwt,
|
||||
dbClient: getDbClient(
|
||||
(pageContext.env?.POSTGRES_CONNECTION_STRING ||
|
||||
|
||||
Generated
-8
@@ -32,9 +32,6 @@ importers:
|
||||
'@openrouter/ai-sdk-provider':
|
||||
specifier: ^1.1.2
|
||||
version: 1.1.2(ai@5.0.9(zod@4.0.17))(zod@4.0.17)
|
||||
'@sinclair/typebox':
|
||||
specifier: ^0.34.37
|
||||
version: 0.34.37
|
||||
'@tabler/icons-react':
|
||||
specifier: ^3.34.1
|
||||
version: 3.34.1(react@19.1.0)
|
||||
@@ -1155,9 +1152,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@sinclair/typebox@0.34.37':
|
||||
resolution: {integrity: sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==}
|
||||
|
||||
'@standard-schema/spec@1.0.0':
|
||||
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
|
||||
|
||||
@@ -3936,8 +3930,6 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.44.1':
|
||||
optional: true
|
||||
|
||||
'@sinclair/typebox@0.34.37': {}
|
||||
|
||||
'@standard-schema/spec@1.0.0': {}
|
||||
|
||||
'@tabler/icons-react@3.34.1(react@19.1.0)':
|
||||
|
||||
+28
-24
@@ -8,17 +8,13 @@ import {
|
||||
import GoogleProvider from "@auth/core/providers/google";
|
||||
import type { Session } from "@auth/core/types";
|
||||
// TODO: stop using universal-middleware and directly integrate server middlewares instead and/or use vike-server https://vike.dev/server. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.)
|
||||
import type {
|
||||
Get,
|
||||
UniversalHandler,
|
||||
UniversalMiddleware,
|
||||
import {
|
||||
type Get,
|
||||
type UniversalHandler,
|
||||
type UniversalMiddleware,
|
||||
env as getEnv,
|
||||
} from "@universal-middleware/core";
|
||||
import { env } from "./env.js";
|
||||
import { getDbClient } from "../database/index.js";
|
||||
import { JWT } from "@auth/core/jwt";
|
||||
|
||||
const POSTGRES_CONNECTION_STRING =
|
||||
"postgres://neondb_owner:npg_sOVmj8vWq2zG@ep-withered-king-adiz9gpi-pooler.c-2.us-east-1.aws.neon.tech:5432/neondb?sslmode=require&channel_binding=true";
|
||||
|
||||
if (!globalThis.crypto) {
|
||||
/**
|
||||
@@ -33,13 +29,15 @@ if (!globalThis.crypto) {
|
||||
});
|
||||
}
|
||||
|
||||
const authjsConfig = {
|
||||
const authjsConfig = (env: Record<string, string>) =>
|
||||
({
|
||||
basePath: "/api/auth",
|
||||
trustHost: Boolean(
|
||||
env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production"
|
||||
),
|
||||
// trustHost: Boolean(
|
||||
// env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production"
|
||||
// ),
|
||||
trustHost: true,
|
||||
// TODO: Replace secret {@see https://authjs.dev/reference/core#secret}
|
||||
secret: "buginoo",
|
||||
secret: env.AUTHJS_SECRET,
|
||||
providers: [
|
||||
// TODO: Choose and implement providers
|
||||
// CredentialsProvider({
|
||||
@@ -63,15 +61,15 @@ const authjsConfig = {
|
||||
// },
|
||||
// }),
|
||||
GoogleProvider({
|
||||
clientId:
|
||||
"697711350664-t6237s5n3ttjd1npp1qif1aupptkr0va.apps.googleusercontent.com",
|
||||
clientSecret: "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",
|
||||
clientId: env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
async signIn({ user, account, profile }) {
|
||||
if (typeof user?.email !== "string") return false;
|
||||
const dbClient = await getDbClient(POSTGRES_CONNECTION_STRING);
|
||||
//@ts-ignore
|
||||
const dbClient = await getDbClient(env.POSTGRES_CONNECTION_STRING);
|
||||
let userFromDb = await dbClient
|
||||
.selectFrom("users")
|
||||
.selectAll()
|
||||
@@ -97,7 +95,8 @@ const authjsConfig = {
|
||||
},
|
||||
jwt: async ({ token }) => {
|
||||
if (typeof token?.email !== "string") return token;
|
||||
const dbClient = await getDbClient(POSTGRES_CONNECTION_STRING);
|
||||
//@ts-ignore
|
||||
const dbClient = await getDbClient(env.POSTGRES_CONNECTION_STRING);
|
||||
let userFromDb = await dbClient
|
||||
.selectFrom("users")
|
||||
.selectAll()
|
||||
@@ -137,7 +136,7 @@ const authjsConfig = {
|
||||
};
|
||||
},
|
||||
},
|
||||
} satisfies Omit<AuthConfig, "raw">;
|
||||
} satisfies Omit<AuthConfig, "raw">);
|
||||
|
||||
/**
|
||||
* Retrieve Auth.js session from Request
|
||||
@@ -175,11 +174,15 @@ export async function getSession(
|
||||
* @link {@see https://authjs.dev/getting-started/session-management/get-session}
|
||||
**/
|
||||
export const authjsSessionMiddleware: Get<[], UniversalMiddleware> =
|
||||
() => async (request, context) => {
|
||||
() => async (request, context, runtime) => {
|
||||
const env = getEnv(runtime);
|
||||
try {
|
||||
return {
|
||||
...context,
|
||||
session: await getSession(request, authjsConfig),
|
||||
session: await getSession(
|
||||
request,
|
||||
authjsConfig(env as Record<string, string>)
|
||||
),
|
||||
};
|
||||
} catch (error) {
|
||||
console.debug("authjsSessionMiddleware:", error);
|
||||
@@ -194,6 +197,7 @@ export const authjsSessionMiddleware: Get<[], UniversalMiddleware> =
|
||||
* Auth.js route
|
||||
* @link {@see https://authjs.dev/getting-started/installation}
|
||||
**/
|
||||
export const authjsHandler = (() => async (request) => {
|
||||
return Auth(request, authjsConfig);
|
||||
export const authjsHandler = (() => async (request, context, runtime) => {
|
||||
const env = getEnv(runtime);
|
||||
return Auth(request, authjsConfig(env as Record<string, string>));
|
||||
}) satisfies Get<[], UniversalHandler>;
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
||||
import { env } from "./env.js";
|
||||
export const openrouter = createOpenRouter({
|
||||
apiKey: env.OPENROUTER_API_KEY,
|
||||
});
|
||||
|
||||
export function getOpenrouter(OPENROUTER_API_KEY: string) {
|
||||
return createOpenRouter({
|
||||
apiKey: OPENROUTER_API_KEY,
|
||||
});
|
||||
}
|
||||
export const MODEL_NAME = "mistralai/mistral-nemo";
|
||||
// export const MODEL_NAME = "z-ai/glm-4.5-air";
|
||||
// export const MODEL_NAME = "openai/gpt-5-mini";
|
||||
|
||||
+17
-5
@@ -7,7 +7,7 @@ import {
|
||||
} from "@universal-middleware/core";
|
||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
||||
import { getDbClient } from "../database/postgres";
|
||||
import { getOpenrouter } from "./provider.js";
|
||||
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
||||
import { env as processEnv } from "./env.js";
|
||||
import { getToken } from "@auth/core/jwt";
|
||||
|
||||
@@ -22,10 +22,22 @@ export const trpcHandler = ((endpoint) => (request, context, runtime) => {
|
||||
(env.POSTGRES_CONNECTION_STRING ||
|
||||
processEnv.POSTGRES_CONNECTION_STRING) as string
|
||||
);
|
||||
const openrouter = getOpenrouter(
|
||||
(env.OPENROUTER_API_KEY || processEnv.OPENROUTER_API_KEY) as string
|
||||
);
|
||||
const jwt = await getToken({ req: request, secret: "buginoo" });
|
||||
const openrouter = createOpenRouter({
|
||||
apiKey: (env.OPENROUTER_API_KEY ||
|
||||
processEnv.OPENROUTER_API_KEY) as string,
|
||||
});
|
||||
const jwt = await getToken({
|
||||
req,
|
||||
secret: (env.AUTHJS_SECRET || processEnv.AUTHJS_SECRET) as string,
|
||||
/** Needed to specify cookie name because for some reason in production
|
||||
* it wasn't reading the correct cookie but in development it was. It
|
||||
* was not straightforward to fix the name of the cookie in
|
||||
* `authOptions`, so I adjusted what cookie to read from here: */
|
||||
cookieName:
|
||||
env.NODE_ENV === "production"
|
||||
? "__Secure-authjs.session-token"
|
||||
: "authjs.session-token",
|
||||
});
|
||||
return {
|
||||
...context,
|
||||
...runtime,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { router, createCallerFactory, authProcedure } from "./server.js";
|
||||
import type { DraftMessage } from "../../types.js";
|
||||
import { MODEL_NAME, openrouter } from "../provider.js";
|
||||
import { MODEL_NAME } from "../provider.js";
|
||||
import { generateObject, generateText, jsonSchema } from "ai";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
|
||||
+2
-22
@@ -1,8 +1,6 @@
|
||||
import type { TSchema } from "@sinclair/typebox";
|
||||
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
||||
import { initTRPC, TRPCError } from "@trpc/server";
|
||||
import type { getDbClient } from "../../database/postgres";
|
||||
import type { getOpenrouter } from "@server/provider.js";
|
||||
import type { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
||||
import type { JWT } from "@auth/core/jwt";
|
||||
|
||||
/**
|
||||
@@ -13,7 +11,7 @@ const t = initTRPC
|
||||
.context<
|
||||
object & {
|
||||
dbClient: ReturnType<typeof getDbClient>;
|
||||
openrouter: ReturnType<typeof getOpenrouter>;
|
||||
openrouter: ReturnType<typeof createOpenRouter>;
|
||||
jwt?: JWT | null;
|
||||
}
|
||||
>()
|
||||
@@ -53,22 +51,4 @@ export const authProcedure = publicProcedure.use(
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Generate a TRPC-compatible validator function given a Typebox schema.
|
||||
* This was copied from [https://github.com/sinclairzx81/typebox/blob/6cfcdc02cc813af2f1be57407c771fc4fadfc34a/example/trpc/readme.md].
|
||||
* @param schema A Typebox schema
|
||||
* @returns A TRPC-compatible validator function
|
||||
*/
|
||||
export function Validator<T extends TSchema>(schema: T) {
|
||||
const check = TypeCompiler.Compile(schema);
|
||||
return (value: unknown) => {
|
||||
if (check.Check(value)) return value;
|
||||
const err = check.Errors(value).First();
|
||||
throw new TRPCError({
|
||||
message: `${err?.message} for ${err?.path}`,
|
||||
code: "BAD_REQUEST",
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const createCallerFactory = t.createCallerFactory;
|
||||
|
||||
@@ -6,3 +6,14 @@ compatibility_flags = [ "nodejs_compat" ]
|
||||
[vars]
|
||||
MILVUS_ADDRESS = "in03-639fdba4bcde098.serverless.gcp-us-west1.cloud.zilliz.com"
|
||||
MILVUS_USERNAME = "db_639fdba4bcde098"
|
||||
|
||||
[env.production.vars]
|
||||
MILVUS_ADDRESS = "in03-639fdba4bcde098.serverless.gcp-us-west1.cloud.zilliz.com"
|
||||
MILVUS_USERNAME = "db_639fdba4bcde098"
|
||||
GOOGLE_CLIENT_ID = "697711350664-t6237s5n3ttjd1npp1qif1aupptkr0va.apps.googleusercontent.com"
|
||||
NODE_ENV = "production"
|
||||
|
||||
[env.preview.vars]
|
||||
MILVUS_ADDRESS = "in03-639fdba4bcde098.serverless.gcp-us-west1.cloud.zilliz.com"
|
||||
MILVUS_USERNAME = "db_639fdba4bcde098"
|
||||
NODE_ENV = "development"
|
||||
Reference in New Issue
Block a user