From 09b87d7a5599c3bc9c5f43ae6a06fc8994297dc1 Mon Sep 17 00:00:00 2001 From: Robert Bo Davis Date: Mon, 13 May 2024 08:14:22 -0400 Subject: [PATCH] chore: initial work on clerk migration (not working) --- package-lock.json | 301 ++++++++++++++++-- remix/app/.server/models/user/userModel.ts | 2 +- remix/app/.server/services/auth.ts | 22 +- remix/app/.server/services/chat.ts | 55 ++-- remix/app/.server/services/types.ts | 6 +- remix/app/.server/services/user.ts | 19 +- remix/app/components/Layout.tsx | 13 - remix/app/components/Navbar.tsx | 47 +-- .../app/components/common/ContentWrapper.tsx | 11 +- remix/app/root.loader.server.ts | 22 +- remix/app/root.tsx | 29 +- remix/app/routes/_index.tsx | 4 + .../app/routes/chat.$chatId/action.server.ts | 33 +- .../app/routes/chat.$chatId/loader.server.ts | 8 +- remix/app/routes/chat._index/loader.server.ts | 4 +- remix/app/routes/chat.create/loader.server.ts | 4 +- remix/app/routes/chat/loader.server.ts | 8 +- remix/app/routes/login.tsx | 11 + remix/app/routes/login/action.server.ts | 6 - remix/app/routes/login/loader.server.ts | 3 - remix/app/routes/login/route.tsx | 2 - remix/app/routes/logout/action.server.ts | 7 - remix/app/routes/logout/route.tsx | 1 - remix/package.json | 2 + 24 files changed, 426 insertions(+), 194 deletions(-) delete mode 100644 remix/app/components/Layout.tsx create mode 100644 remix/app/routes/login.tsx delete mode 100644 remix/app/routes/login/action.server.ts delete mode 100644 remix/app/routes/login/loader.server.ts delete mode 100644 remix/app/routes/login/route.tsx delete mode 100644 remix/app/routes/logout/action.server.ts delete mode 100644 remix/app/routes/logout/route.tsx diff --git a/package-lock.json b/package-lock.json index 472c93c..ea0e5b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -780,6 +780,149 @@ "dev": true, "license": "MIT" }, + "node_modules/@clerk/backend": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-1.1.3.tgz", + "integrity": "sha512-PCGolPkTff7aQjAoS+oDhUldviBYM376klclxJO8SK7eyNkxhwfU95T5McEVpEgz5nITKjKk03Zr7a7v82X6Iw==", + "dependencies": { + "@clerk/shared": "2.0.2", + "cookie": "0.5.0", + "snakecase-keys": "5.4.4", + "tslib": "2.4.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/backend/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@clerk/backend/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "node_modules/@clerk/clerk-react": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.0.4.tgz", + "integrity": "sha512-sVpzLgz/2Muj7W2M3j1g4l296ld+XlEKIQ32sUhFeIM4XiOpBaO8bFu1c4YcdKaetreI6f6dB/K9lYJLGZTtFw==", + "dependencies": { + "@clerk/shared": "2.0.2", + "@clerk/types": "4.2.1", + "tslib": "2.4.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@clerk/clerk-react/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "node_modules/@clerk/remix": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@clerk/remix/-/remix-4.0.6.tgz", + "integrity": "sha512-VYOssme3Sq8wg9QhykXWNi4Q6W9rC21jy32RtEuhWcerUI541qP5MZ42ETEpyj7/vR4Rc3qfVMLhu6no1r09Uw==", + "dependencies": { + "@clerk/backend": "1.1.3", + "@clerk/clerk-react": "5.0.4", + "@clerk/shared": "2.0.2", + "cookie": "0.5.0", + "tslib": "2.4.1" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "@remix-run/react": "^2.0.0", + "@remix-run/server-runtime": "^2.0.0", + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@clerk/remix/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@clerk/remix/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "node_modules/@clerk/shared": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-2.0.2.tgz", + "integrity": "sha512-uQXbBJyUV6B4y/cKIgKypVmCdCh7pHeQYbASCKI3RzlsC3FPyopy0hTlAIvXJqjulPYBfRUI1w2FDRJzpEk3sw==", + "hasInstallScript": true, + "dependencies": { + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.1", + "std-env": "^3.7.0", + "swr": "2.2.0" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@clerk/themes": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@clerk/themes/-/themes-2.1.3.tgz", + "integrity": "sha512-JqOMylpoOvuKUxkSN/lbiofH10xC5IF0lRgFvjOgILF2a9PUiodbKnFzfPezQLwxBLyRdwcfdkZbfw4SPaCPBg==", + "dependencies": { + "@clerk/types": "4.2.1", + "tslib": "2.4.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/themes/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "node_modules/@clerk/types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.2.1.tgz", + "integrity": "sha512-tR7Qd1P5Fa4n81ZxKA0tFPJd1OgrL7chU/+moq/LSnK8ZRN/DUzh5K7730sdw5F26CKtT6pzmW+irtgpYLYdDQ==", + "dependencies": { + "csstype": "3.1.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/types/node_modules/csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, "node_modules/@codellm/cli": { "resolved": "cli", "link": true @@ -5140,6 +5283,20 @@ "node": ">=6.0.0" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -6325,14 +6482,6 @@ "semver": "bin/semver.js" } }, - "node_modules/eslint-plugin-import/node_modules/strip-bom": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { "version": "3.15.0", "dev": true, @@ -7418,6 +7567,11 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.11", "dev": true, @@ -8398,6 +8552,14 @@ "@sideway/pinpoint": "^2.0.0" } }, + "node_modules/js-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", + "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==", + "engines": { + "node": ">=12" + } + }, "node_modules/js-tiktoken": { "version": "1.0.10", "license": "MIT", @@ -8622,14 +8784,6 @@ "node": ">=4" } }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/loader-utils": { "version": "3.2.1", "dev": true, @@ -8753,6 +8907,19 @@ "get-func-name": "^2.0.1" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lower-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/lru-cache": { "version": "5.1.1", "dev": true, @@ -8810,6 +8977,17 @@ "dev": true, "license": "ISC" }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/markdown-extensions": { "version": "1.1.1", "dev": true, @@ -10040,6 +10218,20 @@ "dev": true, "license": "MIT" }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/no-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/node-abi": { "version": "3.56.0", "license": "MIT", @@ -15026,6 +15218,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/snake-case/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/snakecase-keys": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-5.4.4.tgz", + "integrity": "sha512-YTywJG93yxwHLgrYLZjlC75moVEX04LZM4FHfihjHe1FCXm+QaLOFfSf535aXOAd0ArVQMWUAe8ZPm4VtWyXaA==", + "dependencies": { + "map-obj": "^4.1.0", + "snake-case": "^3.0.4", + "type-fest": "^2.5.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/snakecase-keys/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map": { "version": "0.6.1", "license": "BSD-3-Clause", @@ -15126,7 +15356,6 @@ }, "node_modules/std-env": { "version": "3.7.0", - "dev": true, "license": "MIT" }, "node_modules/stream-shift": { @@ -15299,6 +15528,15 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "dev": true, @@ -15413,6 +15651,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.0.tgz", + "integrity": "sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==", + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/tailwindcss": { "version": "3.4.1", "dev": true, @@ -15870,14 +16119,6 @@ "node": ">=6" } }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/tslib": { "version": "1.14.1", "dev": true, @@ -16765,6 +17006,14 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", + "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util": { "version": "0.12.5", "license": "MIT", @@ -17668,6 +17917,8 @@ "remix": { "name": "@codellm/web-remix", "dependencies": { + "@clerk/remix": "^4.0.6", + "@clerk/themes": "^2.1.3", "@codellm/core": "^0.0.1", "@codellm/provider-anthropic": "^0.0.1", "@codellm/provider-langchain": "^0.0.1", diff --git a/remix/app/.server/models/user/userModel.ts b/remix/app/.server/models/user/userModel.ts index 16554a0..80accb5 100644 --- a/remix/app/.server/models/user/userModel.ts +++ b/remix/app/.server/models/user/userModel.ts @@ -1,6 +1,6 @@ import type { ChatInsert, User, UserInsert } from '@remix/.server/db'; import { isError, newError, promiseMayFail } from '@remix/.server/errors'; -import { db, chatSchema, userSchema, messageSchema } from '@remix/.server/db'; +import { db, userSchema } from '@remix/.server/db'; import * as chatModel from '@remix/.server/models/chat/chatModel'; export const ERRORS = { diff --git a/remix/app/.server/services/auth.ts b/remix/app/.server/services/auth.ts index b855c30..339e3b8 100644 --- a/remix/app/.server/services/auth.ts +++ b/remix/app/.server/services/auth.ts @@ -1,5 +1,5 @@ import type { Auth0Profile } from 'remix-auth-auth0'; -import type { ServiceCommonParams } from './types'; +import type { ServiceCommonArgs } from './types'; import { createCookieSessionStorage, redirect } from '@remix-run/node'; import { Authenticator } from 'remix-auth'; @@ -60,7 +60,7 @@ export const { commitSession, destroySession } = sessionStorage; export const isAuthenticated = () => auth.isAuthenticated; -export const getSession = async ({ request }: ServiceCommonParams) => { +export const getSession = async ({ request }: ServiceCommonArgs) => { if (!request?.headers.has('Cookie')) return null; const session = await sessionStorage.getSession( request.headers.get('Cookie'), @@ -68,11 +68,11 @@ export const getSession = async ({ request }: ServiceCommonParams) => { return session; }; -export type LogoutParams = ServiceCommonParams & { +export type LogoutArgs = ServiceCommonArgs & { returnToPath?: string; }; -export const getLogoutURL = ({ request, returnToPath = '/' }: LogoutParams) => { +export const getLogoutURL = ({ request, returnToPath = '/' }: LogoutArgs) => { // Parse the request URL to get the origin and replace the path const url = new URL(request.url); const returnToURL = new URL(returnToPath, url.origin); @@ -84,8 +84,8 @@ export const getLogoutURL = ({ request, returnToPath = '/' }: LogoutParams) => { return logoutURL.toString(); }; -export const getLogoutOptions = async (params: ServiceCommonParams) => { - const session = await getSession(params); +export const getLogoutOptions = async (args: ServiceCommonArgs) => { + const session = await getSession(args); if (!session) return newError({ code: 'auth:noSession' }); return { @@ -95,17 +95,17 @@ export const getLogoutOptions = async (params: ServiceCommonParams) => { }; }; -export const logout = async (params: LogoutParams) => { - const logoutUrl = getLogoutURL(params); - const logoutOptions = await getLogoutOptions(params); +export const logout = async (args: LogoutArgs) => { + const logoutUrl = getLogoutURL(args); + const logoutOptions = await getLogoutOptions(args); if (isError(logoutOptions)) throw redirect('/'); throw redirect(logoutUrl, logoutOptions); }; -export const getAuthProfile = async ({ request }: ServiceCommonParams) => { +export const getAuthProfile = async (args: ServiceCommonArgs) => { if (getConfig('user.userAutoLogin')) return { id: 'mock-user' }; - const session = await getSession({ request }); + const session = await getSession(args); if (!session) return null; return session.data.user; diff --git a/remix/app/.server/services/chat.ts b/remix/app/.server/services/chat.ts index beb6ee8..970e555 100644 --- a/remix/app/.server/services/chat.ts +++ b/remix/app/.server/services/chat.ts @@ -3,7 +3,7 @@ import type { AgentEmitterChannels, AgentHistoryItem, } from '@codellm/core'; -import type { ServiceCommonParams } from './types.js'; +import type { ServiceCommonArgs } from './types.js'; import { EventEmitter } from 'events'; import { remember } from '@epic-web/remember'; @@ -42,14 +42,13 @@ const eventStreamEmitter = remember( export const getEventStreamEmitter = () => eventStreamEmitter; // TODO: we're going to need channels for this eventually -const onAgentEmit = - (chatId: User['id']) => async (params: AgentHistoryItem) => { - const chat = await chatModel.getById(chatId); - if (isError(chat)) return chat; - await chat.addMessage(params); - log('onAgentEmit emitting', 'debug', params); - eventStreamEmitter.emit(`agent:${chatId}`, params); - }; +const onAgentEmit = (chatId: User['id']) => async (args: AgentHistoryItem) => { + const chat = await chatModel.getById(chatId); + if (isError(chat)) return chat; + await chat.addMessage(args); + log('onAgentEmit emitting', 'debug', args); + eventStreamEmitter.emit(`agent:${chatId}`, args); +}; const onEmitListeners = (client: Agent, id: Chat['id']) => channelsToForward.map((channel) => client.onEmit(channel, onAgentEmit(id))); @@ -90,12 +89,12 @@ export const _getOrCreateClient = async (id: Chat['id']) => { return newLock; }; -export type ChatCommonParams = ServiceCommonParams & { +export type ChatCommonArgs = ServiceCommonArgs & { id: Chat['id']; }; -export const createChat = async (params: ServiceCommonParams) => { - const user = await getValidatedUser(params); +export const createChat = async (args: ServiceCommonArgs) => { + const user = await getValidatedUser(args); if (isError(user)) return user; const chat = await user.addChat({ name: 'new chat' }); @@ -108,7 +107,7 @@ export const createChat = async (params: ServiceCommonParams) => { return { client, ...chat }; }; -export const getChat = async ({ id, request }: ChatCommonParams) => { +export const getChat = async ({ id }: ChatCommonArgs) => { const chat = await chatModel.getById(id); if (isError(chat)) return chat; @@ -118,26 +117,26 @@ export const getChat = async ({ id, request }: ChatCommonParams) => { return { client, ...chat }; }; -export const deleteChat = async (params: ChatCommonParams) => { - const chat = await getChat(params); +export const deleteChat = async (args: ChatCommonArgs) => { + const chat = await getChat(args); if (isError(chat)) return chat; - offEmitListeners(chat.client as Agent, params.id); + offEmitListeners(chat.client as Agent, args.id); await chat.destroy(); }; -export const getChats = async (params: ServiceCommonParams) => { - const user = await getValidatedUser(params); +export const getChats = async (args: ServiceCommonArgs) => { + const user = await getValidatedUser(args); if (isError(user)) return user; return user.getChats(); }; -export type UpdateChatParams = ChatCommonParams & { +export type UpdateChatArgs = ChatCommonArgs & { update: ChatUpdate; }; -export const updateChat = async ({ id, update }: UpdateChatParams) => { +export const updateChat = async ({ id, update }: UpdateChatArgs) => { const chat = await chatModel.getById(id); if (isError(chat)) return chat; @@ -146,8 +145,8 @@ export const updateChat = async ({ id, update }: UpdateChatParams) => { return updatedChat; }; -export const getMostRecentChat = async (params: ServiceCommonParams) => { - const user = await getValidatedUser(params); +export const getMostRecentChat = async (args: ServiceCommonArgs) => { + const user = await getValidatedUser(args); if (isError(user)) return user; const chats = await user.getChats(); @@ -156,15 +155,15 @@ export const getMostRecentChat = async (params: ServiceCommonParams) => { return chats[0]; }; -export type SendChatParams = ChatCommonParams & { +export type SendChatArgs = ChatCommonArgs & { message: string; }; -export const sendChat = async (params: SendChatParams) => { - const chat = await getChat(params); +export const sendChat = async (args: SendChatArgs) => { + const chat = await getChat(args); if (isError(chat)) return chat; - await updateChat({ ...params, update: { isLoading: true } }); - await chat.client.chat(params.message); - await updateChat({ ...params, update: { isLoading: false } }); + await updateChat({ ...args, update: { isLoading: true } }); + await chat.client.chat(args.message); + await updateChat({ ...args, update: { isLoading: false } }); }; diff --git a/remix/app/.server/services/types.ts b/remix/app/.server/services/types.ts index 7434ab2..3b17049 100644 --- a/remix/app/.server/services/types.ts +++ b/remix/app/.server/services/types.ts @@ -1,3 +1,3 @@ -export type ServiceCommonParams = { - request: Request; -}; +import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node'; + +export type ServiceCommonArgs = LoaderFunctionArgs | ActionFunctionArgs; diff --git a/remix/app/.server/services/user.ts b/remix/app/.server/services/user.ts index b2fa652..7c3d0e6 100644 --- a/remix/app/.server/services/user.ts +++ b/remix/app/.server/services/user.ts @@ -1,10 +1,9 @@ -import type { ServiceCommonParams } from './types'; +import type { ServiceCommonArgs } from './types'; import type { UserModel } from '@remix/.server/models'; import type { RemixError } from '@remix/.server/errors'; -import { redirect } from '@remix-run/node'; +import { getAuth } from '@clerk/remix/ssr.server'; import { userModel } from '@remix/.server/models'; -import { getAuthProfile, logout } from '@remix/.server/services/auth'; import { isError, newError } from '@remix/.server/errors'; export const ERRORS = { @@ -19,17 +18,19 @@ export const ERRORS = { }, } as const; -export const getUser = async (params: ServiceCommonParams) => { - const authUser = await getAuthProfile(params); - if (!authUser) { +export const getUser = async (args: ServiceCommonArgs) => { + const authUser = await getAuth(args); + console.log('authUser', authUser); + if (!authUser.userId) { return newError({ code: 'userService:noAuthUser', message: 'No authenticated user found', }); } + // @ts-expect-error - not fighting with types for now const user = await userModel.getByAuth0Id(authUser.id); - if (isError(user, 'userModel:notFound')) return logout(params); + // if (isError(user, 'userModel:notFound')) return logout(args); if (isError(user)) throw user; return user; @@ -43,8 +44,8 @@ export const validateUser = (user: UserModel | RemixError) => { return user; }; -export const getValidatedUser = async (params: ServiceCommonParams) => { - const user = await getUser(params); +export const getValidatedUser = async (args: ServiceCommonArgs) => { + const user = await getUser(args); return validateUser(user); }; diff --git a/remix/app/components/Layout.tsx b/remix/app/components/Layout.tsx deleted file mode 100644 index 5e54aa6..0000000 --- a/remix/app/components/Layout.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import AuthProvider from './AuthProvider'; -import Navbar from './Navbar'; - -export const Layout = ({ children }: { children: React.ReactNode }) => { - return ( - -
- -
-
{children}
-
- ); -}; diff --git a/remix/app/components/Navbar.tsx b/remix/app/components/Navbar.tsx index 0152a0f..76f138b 100644 --- a/remix/app/components/Navbar.tsx +++ b/remix/app/components/Navbar.tsx @@ -1,9 +1,11 @@ -import useUser from '@remix/components/hooks/useUser'; import { Form, Link } from '@remix-run/react'; +import { SignedIn, SignedOut, UserButton, useUser } from '@clerk/remix'; + const Navbar = () => { - const { isAuthed, isVerified } = useUser(); + const user = useUser(); + console.log('user', user); return ( ); diff --git a/remix/app/components/common/ContentWrapper.tsx b/remix/app/components/common/ContentWrapper.tsx index 86754c8..6fbb04d 100644 --- a/remix/app/components/common/ContentWrapper.tsx +++ b/remix/app/components/common/ContentWrapper.tsx @@ -1,7 +1,14 @@ -export const ContentWrapper = ({ children }: { children: React.ReactNode }) => { +export const ContentWrapper = ({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) => { + const myClassName = `p-5 border-2 border-primary rounded-lg ${className}`; return (
-
{children}
+
{children}
); }; diff --git a/remix/app/root.loader.server.ts b/remix/app/root.loader.server.ts index c680875..6d557e5 100644 --- a/remix/app/root.loader.server.ts +++ b/remix/app/root.loader.server.ts @@ -1,23 +1,15 @@ import type { LoaderFunctionArgs } from '@remix-run/node'; +import { getAuth, rootAuthLoader } from '@clerk/remix/ssr.server'; + import { getUser, validateUser } from '@remix/.server/services/user'; import { initClient } from '@remix/.server/db/db'; import { isError } from '@remix/.server/errors'; -import { noAuthPayload } from '@remix/components/AuthProvider'; - -export const loader = async ({ request }: LoaderFunctionArgs) => { - await initClient(); - const user = await getUser({ request }); - if (isError(user)) return noAuthPayload; - - const validatedUser = validateUser(user); - const isVerified = !isError(validatedUser); - return { - isAuthed: true, - isVerified, - user, - }; -}; +export const loader = async (args: LoaderFunctionArgs) => + rootAuthLoader(args, (cbArgs) => { + console.log('cbArgs', cbArgs); + return {}; + }); export type RootLoader = typeof loader; diff --git a/remix/app/root.tsx b/remix/app/root.tsx index 251d5e2..591f382 100644 --- a/remix/app/root.tsx +++ b/remix/app/root.tsx @@ -1,14 +1,19 @@ import type { LinksFunction } from '@remix-run/node'; + +import { ClerkApp } from '@clerk/remix'; +import { dark } from '@clerk/themes'; import { Links, + LiveReload, Meta, Outlet, Scripts, ScrollRestoration, } from '@remix-run/react'; + import stylesheet from './styles/app.css?url'; -import { Layout } from '@remix/components/Layout'; +import Navbar from '@remix/components/Navbar'; export { loader } from './root.loader.server'; @@ -16,7 +21,7 @@ export const links: LinksFunction = () => [ { href: stylesheet, rel: 'stylesheet' }, ]; -export default function App() { +export const App = () => { return ( @@ -26,12 +31,22 @@ export default function App() { - +
+ +
+
- - - +
+ + + ); -} +}; + +export default ClerkApp(App, { + appearance: { + baseTheme: dark, + }, +}); diff --git a/remix/app/routes/_index.tsx b/remix/app/routes/_index.tsx index 0ba7810..8efe675 100644 --- a/remix/app/routes/_index.tsx +++ b/remix/app/routes/_index.tsx @@ -1,5 +1,6 @@ import type { MetaFunction } from '@remix-run/node'; +import { useUser } from '@clerk/remix'; import ContentWrapper from '@remix/components/common/ContentWrapper'; export const meta: MetaFunction = () => { @@ -10,6 +11,9 @@ export const meta: MetaFunction = () => { }; export default function Index() { + const user = useUser(); + + console.log('Index user', user); // const isLoading = Boolean(transition.action?.type === 'idle'); return (
diff --git a/remix/app/routes/chat.$chatId/action.server.ts b/remix/app/routes/chat.$chatId/action.server.ts index e477b89..dad5017 100644 --- a/remix/app/routes/chat.$chatId/action.server.ts +++ b/remix/app/routes/chat.$chatId/action.server.ts @@ -10,48 +10,43 @@ import { import { isError } from '@remix/.server/errors'; import { Chat } from '@remix/.server/db'; -export type ChatActionParams = { +export type ChatActionArgs = ActionFunctionArgs & { formData: FormData; id: Chat['id']; - request: Request; }; -export const sendChatAction = async ({ - formData, - id, - request, -}: ChatActionParams) => { - const message = formData.get('userMessage') as string; +export const sendChatAction = async (args: ChatActionArgs) => { + const message = args.formData.get('userMessage') as string; // We don't want to wait because it will block the event stream // triggered revalidation if this action is locked in a loading state - sendChat({ id, message, request }); + sendChat({ ...args, message }); return null; }; -export const deleteChatAction = async (params: ChatActionParams) => { - await deleteChat(params); - const mostRecentChat = await getMostRecentChat(params); +export const deleteChatAction = async (args: ChatActionArgs) => { + await deleteChat(args); + const mostRecentChat = await getMostRecentChat(args); if (isError(mostRecentChat)) throw mostRecentChat; if (mostRecentChat) throw redirect(`/chat/${mostRecentChat.id}`); throw redirect('/chat'); }; -export const renameChat = (params: ChatActionParams) => +export const renameChat = (args: ChatActionArgs) => updateChat({ - ...params, - update: { name: params.formData.get('name') as string }, + ...args, + update: { name: args.formData.get('name') as string }, }); -export const action = async ({ params, request }: ActionFunctionArgs) => { - const { chatId } = params; +export const action = async (args: ActionFunctionArgs) => { + const { chatId } = args.params; if (!chatId) return json({ error: 'Invalid chatId' }, { status: 400 }); - const formData = await request.clone().formData(); + const formData = await args.request.clone().formData(); const intent = formData.get('intent'); - const requestParams = { formData, id: Number(chatId), request }; + const requestParams = { ...args, formData, id: Number(chatId) }; switch (intent) { case 'deleteChat': return deleteChatAction(requestParams); diff --git a/remix/app/routes/chat.$chatId/loader.server.ts b/remix/app/routes/chat.$chatId/loader.server.ts index 35f061f..8cbac78 100644 --- a/remix/app/routes/chat.$chatId/loader.server.ts +++ b/remix/app/routes/chat.$chatId/loader.server.ts @@ -4,13 +4,13 @@ import { redirect } from '@remix-run/node'; import { isError } from '@remix/.server/errors'; import { getChat } from '@remix/.server/services/chat'; -export const loader = async ({ params, request }: LoaderFunctionArgs) => { - if (!params.chatId) throw redirect('/chat'); - const { chatId } = params; +export const loader = async (args: LoaderFunctionArgs) => { + if (!args.params.chatId) throw redirect('/chat'); + const { chatId } = args.params; const currentChat = await getChat({ + ...args, id: Number(chatId), - request, }); if (!currentChat || isError(currentChat)) throw redirect('/'); diff --git a/remix/app/routes/chat._index/loader.server.ts b/remix/app/routes/chat._index/loader.server.ts index d375796..ba27ec1 100644 --- a/remix/app/routes/chat._index/loader.server.ts +++ b/remix/app/routes/chat._index/loader.server.ts @@ -3,8 +3,8 @@ import { redirect } from '@remix-run/node'; import { isError } from '@remix/.server/errors'; import { getMostRecentChat } from '@remix/.server/services/chat'; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const chat = await getMostRecentChat({ request }); +export const loader = async (args: LoaderFunctionArgs) => { + const chat = await getMostRecentChat(args); if (isError(chat)) throw redirect('/'); if (chat) throw redirect(`${chat.id}`); diff --git a/remix/app/routes/chat.create/loader.server.ts b/remix/app/routes/chat.create/loader.server.ts index 0fd7d9b..948cc24 100644 --- a/remix/app/routes/chat.create/loader.server.ts +++ b/remix/app/routes/chat.create/loader.server.ts @@ -3,8 +3,8 @@ import { redirect } from '@remix-run/node'; import { createChat } from '@remix/.server/services/chat'; import { isError } from '@remix/.server/errors'; -export const loader = async ({ request }: LoaderFunctionArgs) => { - const newChat = await createChat({ request }); +export const loader = async (args: LoaderFunctionArgs) => { + const newChat = await createChat(args); if (isError(newChat)) throw redirect('/'); diff --git a/remix/app/routes/chat/loader.server.ts b/remix/app/routes/chat/loader.server.ts index 39f2932..34c8866 100644 --- a/remix/app/routes/chat/loader.server.ts +++ b/remix/app/routes/chat/loader.server.ts @@ -3,12 +3,12 @@ import { redirect } from '@remix-run/node'; import { getChat, getChats } from '@remix/.server/services/chat'; import { isError } from '@remix/.server/errors'; -export const loader = async ({ params, request }: LoaderFunctionArgs) => { - const chats = await getChats({ request }); +export const loader = async (args: LoaderFunctionArgs) => { + const chats = await getChats(args); if (isError(chats)) throw redirect('/'); - if (!params.chatId) return { chats }; + if (!args.params.chatId) return { chats }; - const chat = await getChat({ id: Number(params.chatId), request }); + const chat = await getChat({ id: Number(args.params.chatId), ...args }); if (isError(chat)) throw redirect('/'); return { chat, chats }; }; diff --git a/remix/app/routes/login.tsx b/remix/app/routes/login.tsx new file mode 100644 index 0000000..84c6340 --- /dev/null +++ b/remix/app/routes/login.tsx @@ -0,0 +1,11 @@ +import { SignIn } from '@clerk/remix'; + +import ContentWrapper from '@remix/components/common/ContentWrapper'; + +export default function SignInPage() { + return ( + + + + ); +} diff --git a/remix/app/routes/login/action.server.ts b/remix/app/routes/login/action.server.ts deleted file mode 100644 index 1de09e2..0000000 --- a/remix/app/routes/login/action.server.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { ActionFunctionArgs } from '@remix-run/node'; - -import { auth } from '@remix/.server/services/auth'; - -export const action = async ({ request }: ActionFunctionArgs) => - auth.authenticate('auth0', request); diff --git a/remix/app/routes/login/loader.server.ts b/remix/app/routes/login/loader.server.ts deleted file mode 100644 index 4d5f84a..0000000 --- a/remix/app/routes/login/loader.server.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { redirect } from '@remix-run/node'; - -export const loader = async () => redirect('/'); diff --git a/remix/app/routes/login/route.tsx b/remix/app/routes/login/route.tsx deleted file mode 100644 index 27b263d..0000000 --- a/remix/app/routes/login/route.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export { action } from './action.server'; -export { loader } from './loader.server'; diff --git a/remix/app/routes/logout/action.server.ts b/remix/app/routes/logout/action.server.ts deleted file mode 100644 index 9433e35..0000000 --- a/remix/app/routes/logout/action.server.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { ActionFunctionArgs } from '@remix-run/node'; - -import { logout } from '@remix/.server/services/auth'; - -export const action = async (params: ActionFunctionArgs) => { - return logout(params); -}; diff --git a/remix/app/routes/logout/route.tsx b/remix/app/routes/logout/route.tsx deleted file mode 100644 index 2b68eac..0000000 --- a/remix/app/routes/logout/route.tsx +++ /dev/null @@ -1 +0,0 @@ -export { action } from './action.server'; diff --git a/remix/package.json b/remix/package.json index d67a5f8..c86847f 100644 --- a/remix/package.json +++ b/remix/package.json @@ -13,6 +13,8 @@ "typecheck": "tsc" }, "dependencies": { + "@clerk/remix": "^4.0.6", + "@clerk/themes": "^2.1.3", "@codellm/core": "^0.0.1", "@codellm/provider-anthropic": "^0.0.1", "@codellm/provider-langchain": "^0.0.1",