Compare commits
10 Commits
edfaabeb0d
..
master
| 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
|
// suspends during the initial render. This may not be needed if we
|
||||||
// have a suspense boundary BELOW the creation of the query client
|
// have a suspense boundary BELOW the creation of the query client
|
||||||
if (!browserQueryClient) browserQueryClient = makeQueryClient();
|
if (!browserQueryClient) browserQueryClient = makeQueryClient();
|
||||||
|
// This code is for all users
|
||||||
|
window.__TANSTACK_QUERY_CLIENT__ = browserQueryClient;
|
||||||
return browserQueryClient;
|
return browserQueryClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This code is only for TypeScript
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
__TANSTACK_QUERY_CLIENT__: import("@tanstack/query-core").QueryClient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function SignInWithGoogle() {
|
export function SignInWithGoogle() {
|
||||||
const pageContext = usePageContext();
|
const pageContext = usePageContext();
|
||||||
/** This is populated using the +onCreatePageContext.server.ts hook */
|
/** This is populated using the +onCreatePageContext.server.ts hook */
|
||||||
@@ -232,7 +241,9 @@ function NavLinkChat() {
|
|||||||
const { urlPathname } = pageContext;
|
const { urlPathname } = pageContext;
|
||||||
const trpc = useTRPC();
|
const trpc = useTRPC();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
// const
|
const { data: conversations } = useQuery(
|
||||||
|
trpc.chat.conversations.fetchAll.queryOptions()
|
||||||
|
);
|
||||||
const startConversation = useMutation(
|
const startConversation = useMutation(
|
||||||
trpc.chat.conversations.start.mutationOptions({
|
trpc.chat.conversations.start.mutationOptions({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -286,9 +297,6 @@ function NavLinkChat() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: conversations } = useQuery(
|
|
||||||
trpc.chat.conversations.fetchAll.queryOptions()
|
|
||||||
);
|
|
||||||
// const selectedConversationId = useStore(
|
// const selectedConversationId = useStore(
|
||||||
// (state) => state.selectedConversationId
|
// (state) => state.selectedConversationId
|
||||||
// );
|
// );
|
||||||
@@ -356,7 +364,7 @@ function NavLinkChat() {
|
|||||||
>
|
>
|
||||||
{conversations?.map((conversation) => (
|
{conversations?.map((conversation) => (
|
||||||
<NavLink
|
<NavLink
|
||||||
key={conversation.id}
|
key={conversation.id + conversation.title}
|
||||||
href={`/chat/${conversation.id}`}
|
href={`/chat/${conversation.id}`}
|
||||||
label={conversation.title}
|
label={conversation.title}
|
||||||
className="hover-container"
|
className="hover-container"
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
"@mantine/hooks": "^8.1.1",
|
"@mantine/hooks": "^8.1.1",
|
||||||
"@neondatabase/serverless": "^1.0.1",
|
"@neondatabase/serverless": "^1.0.1",
|
||||||
"@openrouter/ai-sdk-provider": "^1.1.2",
|
"@openrouter/ai-sdk-provider": "^1.1.2",
|
||||||
"@sinclair/typebox": "^0.34.37",
|
|
||||||
"@tabler/icons-react": "^3.34.1",
|
"@tabler/icons-react": "^3.34.1",
|
||||||
"@tanstack/react-query": "^5.85.3",
|
"@tanstack/react-query": "^5.85.3",
|
||||||
"@trpc/client": "^11.4.4",
|
"@trpc/client": "^11.4.4",
|
||||||
|
|||||||
@@ -316,14 +316,18 @@ export default function ChatPage() {
|
|||||||
newConversations: null,
|
newConversations: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const newConversations: Array<Conversation> = [
|
|
||||||
...previousConversations,
|
/** MUST make a deep, immutable copy in order to trigger re-render of
|
||||||
{
|
* oberserving components. */
|
||||||
...conversation,
|
const newConversations: Array<Conversation> = JSON.parse(
|
||||||
title,
|
JSON.stringify(previousConversations)
|
||||||
} as Conversation,
|
);
|
||||||
];
|
const conversationToUpdate = newConversations.find((c) => c.id === id);
|
||||||
queryClient.setQueryData(
|
if (!conversationToUpdate) {
|
||||||
|
return { previousConversations, newConversations };
|
||||||
|
}
|
||||||
|
conversationToUpdate.title = title;
|
||||||
|
queryClient.setQueryData<Array<Conversation>>(
|
||||||
trpc.chat.conversations.fetchAll.queryKey(),
|
trpc.chat.conversations.fetchAll.queryKey(),
|
||||||
newConversations
|
newConversations
|
||||||
);
|
);
|
||||||
@@ -505,6 +509,12 @@ export default function ChatPage() {
|
|||||||
// onChange={(e) => {
|
// onChange={(e) => {
|
||||||
// setConversationTitle(e.target.value);
|
// setConversationTitle(e.target.value);
|
||||||
// }}
|
// }}
|
||||||
|
onKeyUp={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
e.currentTarget.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
updateConversationTitle.mutateAsync({
|
updateConversationTitle.mutateAsync({
|
||||||
id: conversationId,
|
id: conversationId,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { PageContextServer } from "vike/types";
|
import type { PageContextServer } from "vike/types";
|
||||||
import { createCaller } from "../../../server/trpc/chat.js";
|
import { createCaller } from "../../../server/trpc/chat.js";
|
||||||
import { getDbClient } from "../../../database/postgres.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";
|
import { env } from "../../../server/env.js";
|
||||||
|
|
||||||
export type Data = Awaited<ReturnType<typeof data>>;
|
export type Data = Awaited<ReturnType<typeof data>>;
|
||||||
@@ -9,9 +9,10 @@ export type Data = Awaited<ReturnType<typeof data>>;
|
|||||||
export const data = async (pageContext: PageContextServer) => {
|
export const data = async (pageContext: PageContextServer) => {
|
||||||
const { id } = pageContext.routeParams;
|
const { id } = pageContext.routeParams;
|
||||||
const caller = createCaller({
|
const caller = createCaller({
|
||||||
openrouter: getOpenrouter(
|
openrouter: createOpenRouter({
|
||||||
(pageContext.env?.OPENROUTER_API_KEY || env.OPENROUTER_API_KEY) as string
|
apiKey: (pageContext.env?.OPENROUTER_API_KEY ||
|
||||||
),
|
env.OPENROUTER_API_KEY) as string,
|
||||||
|
}),
|
||||||
jwt: pageContext.session?.jwt,
|
jwt: pageContext.session?.jwt,
|
||||||
dbClient: getDbClient(
|
dbClient: getDbClient(
|
||||||
(pageContext.env?.POSTGRES_CONNECTION_STRING ||
|
(pageContext.env?.POSTGRES_CONNECTION_STRING ||
|
||||||
|
|||||||
Generated
-8
@@ -32,9 +32,6 @@ importers:
|
|||||||
'@openrouter/ai-sdk-provider':
|
'@openrouter/ai-sdk-provider':
|
||||||
specifier: ^1.1.2
|
specifier: ^1.1.2
|
||||||
version: 1.1.2(ai@5.0.9(zod@4.0.17))(zod@4.0.17)
|
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':
|
'@tabler/icons-react':
|
||||||
specifier: ^3.34.1
|
specifier: ^3.34.1
|
||||||
version: 3.34.1(react@19.1.0)
|
version: 3.34.1(react@19.1.0)
|
||||||
@@ -1155,9 +1152,6 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@sinclair/typebox@0.34.37':
|
|
||||||
resolution: {integrity: sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==}
|
|
||||||
|
|
||||||
'@standard-schema/spec@1.0.0':
|
'@standard-schema/spec@1.0.0':
|
||||||
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
|
resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==}
|
||||||
|
|
||||||
@@ -3936,8 +3930,6 @@ snapshots:
|
|||||||
'@rollup/rollup-win32-x64-msvc@4.44.1':
|
'@rollup/rollup-win32-x64-msvc@4.44.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@sinclair/typebox@0.34.37': {}
|
|
||||||
|
|
||||||
'@standard-schema/spec@1.0.0': {}
|
'@standard-schema/spec@1.0.0': {}
|
||||||
|
|
||||||
'@tabler/icons-react@3.34.1(react@19.1.0)':
|
'@tabler/icons-react@3.34.1(react@19.1.0)':
|
||||||
|
|||||||
+120
-116
@@ -8,17 +8,13 @@ import {
|
|||||||
import GoogleProvider from "@auth/core/providers/google";
|
import GoogleProvider from "@auth/core/providers/google";
|
||||||
import type { Session } from "@auth/core/types";
|
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.)
|
// 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 {
|
import {
|
||||||
Get,
|
type Get,
|
||||||
UniversalHandler,
|
type UniversalHandler,
|
||||||
UniversalMiddleware,
|
type UniversalMiddleware,
|
||||||
|
env as getEnv,
|
||||||
} from "@universal-middleware/core";
|
} from "@universal-middleware/core";
|
||||||
import { env } from "./env.js";
|
|
||||||
import { getDbClient } from "../database/index.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) {
|
if (!globalThis.crypto) {
|
||||||
/**
|
/**
|
||||||
@@ -33,111 +29,114 @@ if (!globalThis.crypto) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const authjsConfig = {
|
const authjsConfig = (env: Record<string, string>) =>
|
||||||
basePath: "/api/auth",
|
({
|
||||||
trustHost: Boolean(
|
basePath: "/api/auth",
|
||||||
env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production"
|
// trustHost: Boolean(
|
||||||
),
|
// env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production"
|
||||||
// TODO: Replace secret {@see https://authjs.dev/reference/core#secret}
|
// ),
|
||||||
secret: "buginoo",
|
trustHost: true,
|
||||||
providers: [
|
// TODO: Replace secret {@see https://authjs.dev/reference/core#secret}
|
||||||
// TODO: Choose and implement providers
|
secret: env.AUTHJS_SECRET,
|
||||||
// CredentialsProvider({
|
providers: [
|
||||||
// name: "Credentials",
|
// TODO: Choose and implement providers
|
||||||
// credentials: {
|
// CredentialsProvider({
|
||||||
// username: { label: "Username", type: "text", placeholder: "jsmith" },
|
// name: "Credentials",
|
||||||
// password: { label: "Password", type: "password" },
|
// credentials: {
|
||||||
// },
|
// username: { label: "Username", type: "text", placeholder: "jsmith" },
|
||||||
// async authorize() {
|
// password: { label: "Password", type: "password" },
|
||||||
// // Add logic here to look up the user from the credentials supplied
|
// },
|
||||||
// const user = {
|
// async authorize() {
|
||||||
// id: "019900bb-61b3-7333-b760-b27784dfe33b",
|
// // Add logic here to look up the user from the credentials supplied
|
||||||
// name: "J Smith",
|
// const user = {
|
||||||
// email: "jsmith@example.com",
|
// id: "019900bb-61b3-7333-b760-b27784dfe33b",
|
||||||
// };
|
// name: "J Smith",
|
||||||
|
// email: "jsmith@example.com",
|
||||||
|
// };
|
||||||
|
|
||||||
// // Any object returned will be saved in `user` property of the JWT
|
// // Any object returned will be saved in `user` property of the JWT
|
||||||
// // If you return null then an error will be displayed advising the user to check their details.
|
// // If you return null then an error will be displayed advising the user to check their details.
|
||||||
// // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter
|
// // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter
|
||||||
// return user ?? null;
|
// return user ?? null;
|
||||||
// },
|
// },
|
||||||
// }),
|
// }),
|
||||||
GoogleProvider({
|
GoogleProvider({
|
||||||
clientId:
|
clientId: env.GOOGLE_CLIENT_ID,
|
||||||
"697711350664-t6237s5n3ttjd1npp1qif1aupptkr0va.apps.googleusercontent.com",
|
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
||||||
clientSecret: "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",
|
}),
|
||||||
}),
|
],
|
||||||
],
|
callbacks: {
|
||||||
callbacks: {
|
async signIn({ user, account, profile }) {
|
||||||
async signIn({ user, account, profile }) {
|
if (typeof user?.email !== "string") return false;
|
||||||
if (typeof user?.email !== "string") return false;
|
//@ts-ignore
|
||||||
const dbClient = await getDbClient(POSTGRES_CONNECTION_STRING);
|
const dbClient = await getDbClient(env.POSTGRES_CONNECTION_STRING);
|
||||||
let userFromDb = await dbClient
|
let userFromDb = await dbClient
|
||||||
.selectFrom("users")
|
.selectFrom("users")
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where("email", "=", user.email)
|
.where("email", "=", user.email)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
if (!userFromDb) {
|
if (!userFromDb) {
|
||||||
userFromDb = (
|
userFromDb = (
|
||||||
await dbClient
|
await dbClient
|
||||||
.insertInto("users")
|
.insertInto("users")
|
||||||
.values({
|
.values({
|
||||||
email: user.email,
|
email: user.email,
|
||||||
username: user.email,
|
username: user.email,
|
||||||
password: null,
|
password: null,
|
||||||
createdAt: null,
|
createdAt: null,
|
||||||
lastLogin: null,
|
lastLogin: null,
|
||||||
})
|
})
|
||||||
.returningAll()
|
.returningAll()
|
||||||
.execute()
|
.execute()
|
||||||
)[0];
|
)[0];
|
||||||
}
|
}
|
||||||
console.log("signIn", user, account, profile);
|
console.log("signIn", user, account, profile);
|
||||||
return true;
|
return true;
|
||||||
|
},
|
||||||
|
jwt: async ({ token }) => {
|
||||||
|
if (typeof token?.email !== "string") return token;
|
||||||
|
//@ts-ignore
|
||||||
|
const dbClient = await getDbClient(env.POSTGRES_CONNECTION_STRING);
|
||||||
|
let userFromDb = await dbClient
|
||||||
|
.selectFrom("users")
|
||||||
|
.selectAll()
|
||||||
|
.where("email", "=", token.email || "")
|
||||||
|
.executeTakeFirst();
|
||||||
|
/** TODO: the following should never happen, because the account in
|
||||||
|
* created in the `isgnIn` step; but I don't know what error to throw here
|
||||||
|
* if for some reason there is no such account.*/
|
||||||
|
if (!userFromDb) {
|
||||||
|
userFromDb = (
|
||||||
|
await dbClient
|
||||||
|
.insertInto("users")
|
||||||
|
.values({
|
||||||
|
email: token.email,
|
||||||
|
username: token.email,
|
||||||
|
password: null,
|
||||||
|
createdAt: null,
|
||||||
|
lastLogin: null,
|
||||||
|
})
|
||||||
|
.returningAll()
|
||||||
|
.execute()
|
||||||
|
)[0];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...token,
|
||||||
|
id: userFromDb?.id || "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
session: ({ token, session }) => {
|
||||||
|
return {
|
||||||
|
...session,
|
||||||
|
user: {
|
||||||
|
...session.user,
|
||||||
|
id: token.id as string,
|
||||||
|
},
|
||||||
|
jwt: token,
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
jwt: async ({ token }) => {
|
} satisfies Omit<AuthConfig, "raw">);
|
||||||
if (typeof token?.email !== "string") return token;
|
|
||||||
const dbClient = await getDbClient(POSTGRES_CONNECTION_STRING);
|
|
||||||
let userFromDb = await dbClient
|
|
||||||
.selectFrom("users")
|
|
||||||
.selectAll()
|
|
||||||
.where("email", "=", token.email || "")
|
|
||||||
.executeTakeFirst();
|
|
||||||
/** TODO: the following should never happen, because the account in
|
|
||||||
* created in the `isgnIn` step; but I don't know what error to throw here
|
|
||||||
* if for some reason there is no such account.*/
|
|
||||||
if (!userFromDb) {
|
|
||||||
userFromDb = (
|
|
||||||
await dbClient
|
|
||||||
.insertInto("users")
|
|
||||||
.values({
|
|
||||||
email: token.email,
|
|
||||||
username: token.email,
|
|
||||||
password: null,
|
|
||||||
createdAt: null,
|
|
||||||
lastLogin: null,
|
|
||||||
})
|
|
||||||
.returningAll()
|
|
||||||
.execute()
|
|
||||||
)[0];
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...token,
|
|
||||||
id: userFromDb?.id || "",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
session: ({ token, session }) => {
|
|
||||||
return {
|
|
||||||
...session,
|
|
||||||
user: {
|
|
||||||
...session.user,
|
|
||||||
id: token.id as string,
|
|
||||||
},
|
|
||||||
jwt: token,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies Omit<AuthConfig, "raw">;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve Auth.js session from Request
|
* 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}
|
* @link {@see https://authjs.dev/getting-started/session-management/get-session}
|
||||||
**/
|
**/
|
||||||
export const authjsSessionMiddleware: Get<[], UniversalMiddleware> =
|
export const authjsSessionMiddleware: Get<[], UniversalMiddleware> =
|
||||||
() => async (request, context) => {
|
() => async (request, context, runtime) => {
|
||||||
|
const env = getEnv(runtime);
|
||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
...context,
|
...context,
|
||||||
session: await getSession(request, authjsConfig),
|
session: await getSession(
|
||||||
|
request,
|
||||||
|
authjsConfig(env as Record<string, string>)
|
||||||
|
),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.debug("authjsSessionMiddleware:", error);
|
console.debug("authjsSessionMiddleware:", error);
|
||||||
@@ -194,6 +197,7 @@ export const authjsSessionMiddleware: Get<[], UniversalMiddleware> =
|
|||||||
* Auth.js route
|
* Auth.js route
|
||||||
* @link {@see https://authjs.dev/getting-started/installation}
|
* @link {@see https://authjs.dev/getting-started/installation}
|
||||||
**/
|
**/
|
||||||
export const authjsHandler = (() => async (request) => {
|
export const authjsHandler = (() => async (request, context, runtime) => {
|
||||||
return Auth(request, authjsConfig);
|
const env = getEnv(runtime);
|
||||||
|
return Auth(request, authjsConfig(env as Record<string, string>));
|
||||||
}) satisfies Get<[], UniversalHandler>;
|
}) 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 = "mistralai/mistral-nemo";
|
||||||
// export const MODEL_NAME = "z-ai/glm-4.5-air";
|
// export const MODEL_NAME = "z-ai/glm-4.5-air";
|
||||||
// export const MODEL_NAME = "openai/gpt-5-mini";
|
// export const MODEL_NAME = "openai/gpt-5-mini";
|
||||||
|
|||||||
+17
-5
@@ -7,7 +7,7 @@ import {
|
|||||||
} from "@universal-middleware/core";
|
} from "@universal-middleware/core";
|
||||||
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
||||||
import { getDbClient } from "../database/postgres";
|
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 { env as processEnv } from "./env.js";
|
||||||
import { getToken } from "@auth/core/jwt";
|
import { getToken } from "@auth/core/jwt";
|
||||||
|
|
||||||
@@ -22,10 +22,22 @@ export const trpcHandler = ((endpoint) => (request, context, runtime) => {
|
|||||||
(env.POSTGRES_CONNECTION_STRING ||
|
(env.POSTGRES_CONNECTION_STRING ||
|
||||||
processEnv.POSTGRES_CONNECTION_STRING) as string
|
processEnv.POSTGRES_CONNECTION_STRING) as string
|
||||||
);
|
);
|
||||||
const openrouter = getOpenrouter(
|
const openrouter = createOpenRouter({
|
||||||
(env.OPENROUTER_API_KEY || processEnv.OPENROUTER_API_KEY) as string
|
apiKey: (env.OPENROUTER_API_KEY ||
|
||||||
);
|
processEnv.OPENROUTER_API_KEY) as string,
|
||||||
const jwt = await getToken({ req: request, secret: "buginoo" });
|
});
|
||||||
|
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 {
|
return {
|
||||||
...context,
|
...context,
|
||||||
...runtime,
|
...runtime,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { router, createCallerFactory, authProcedure } from "./server.js";
|
import { router, createCallerFactory, authProcedure } from "./server.js";
|
||||||
import type { DraftMessage } from "../../types.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 { generateObject, generateText, jsonSchema } from "ai";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { z } from "zod";
|
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 { initTRPC, TRPCError } from "@trpc/server";
|
||||||
import type { getDbClient } from "../../database/postgres";
|
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";
|
import type { JWT } from "@auth/core/jwt";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,7 +11,7 @@ const t = initTRPC
|
|||||||
.context<
|
.context<
|
||||||
object & {
|
object & {
|
||||||
dbClient: ReturnType<typeof getDbClient>;
|
dbClient: ReturnType<typeof getDbClient>;
|
||||||
openrouter: ReturnType<typeof getOpenrouter>;
|
openrouter: ReturnType<typeof createOpenRouter>;
|
||||||
jwt?: JWT | null;
|
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;
|
export const createCallerFactory = t.createCallerFactory;
|
||||||
|
|||||||
+12
-1
@@ -5,4 +5,15 @@ compatibility_flags = [ "nodejs_compat" ]
|
|||||||
|
|
||||||
[vars]
|
[vars]
|
||||||
MILVUS_ADDRESS = "in03-639fdba4bcde098.serverless.gcp-us-west1.cloud.zilliz.com"
|
MILVUS_ADDRESS = "in03-639fdba4bcde098.serverless.gcp-us-west1.cloud.zilliz.com"
|
||||||
MILVUS_USERNAME = "db_639fdba4bcde098"
|
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