From 27156e225e0224a019e3872824d3fa300f85f1d6 Mon Sep 17 00:00:00 2001 From: Sam Denty Date: Thu, 26 Sep 2024 17:44:17 +0100 Subject: [PATCH] feat: remove authentication --- .gitignore | 7 + .vscode/launch.json | 16 -- README.md | 9 +- app/components/chat/BaseChat.tsx | 3 - app/components/chat/Messages.client.tsx | 9 +- app/lib/.server/auth.ts | 41 ---- app/lib/.server/sessions.ts | 240 ------------------------ app/lib/analytics.ts | 38 ---- app/lib/auth.ts | 4 - app/lib/constants.ts | 2 - app/lib/stores/files.ts | 2 +- app/lib/webcontainer/index.ts | 2 - app/routes/_index.tsx | 7 +- app/routes/api.chat.ts | 3 +- app/routes/api.enhancer.ts | 3 +- app/routes/chat.$id.tsx | 3 +- app/routes/login.tsx | 201 -------------------- app/routes/logout.tsx | 10 - package.json | 12 +- pnpm-lock.yaml | 10 +- worker-configuration.d.ts | 2 - 21 files changed, 27 insertions(+), 597 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 app/lib/.server/auth.ts delete mode 100644 app/lib/.server/sessions.ts delete mode 100644 app/lib/analytics.ts delete mode 100644 app/lib/auth.ts delete mode 100644 app/lib/constants.ts delete mode 100644 app/routes/login.tsx delete mode 100644 app/routes/logout.tsx diff --git a/.gitignore b/.gitignore index c3ed3e66..965ef504 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,10 @@ dist-ssr *.njsproj *.sln *.sw? + +/.cache +/build +.env* +*.vars +.wrangler +_worker.bundle diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index feaf4181..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Debug Current Test File", - "autoAttachChildProcesses": true, - "skipFiles": ["/**", "**/node_modules/**"], - "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs", - "args": ["run", "${relativeFile}"], - "smartStep": true, - "console": "integratedTerminal" - } - ] -} diff --git a/README.md b/README.md index 424b5352..26365781 100644 --- a/README.md +++ b/README.md @@ -29,17 +29,10 @@ pnpm install ANTHROPIC_API_KEY=XXX ``` -Optionally, you an set the debug level or disable authentication: +Optionally, you an set the debug level: ``` VITE_LOG_LEVEL=debug -VITE_DISABLE_AUTH=1 -``` - -If you want to run authentication against a local StackBlitz instance, add: - -``` -VITE_CLIENT_ORIGIN=https://local.stackblitz.com:3000 ``` **Important**: Never commit your `.env.local` file to version control. It's already included in .gitignore. diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 887593f2..b3820e16 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -9,7 +9,6 @@ import { Messages } from './Messages.client'; import { SendButton } from './SendButton.client'; import styles from './BaseChat.module.scss'; -import { useLoaderData } from '@remix-run/react'; interface BaseChatProps { textareaRef?: React.RefObject | undefined; @@ -59,7 +58,6 @@ export const BaseChat = React.forwardRef( ref, ) => { const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; - const { avatar } = useLoaderData<{ avatar?: string }>(); return (
( className="flex flex-col w-full flex-1 max-w-chat px-4 pb-6 mx-auto z-1" messages={messages} isStreaming={isStreaming} - avatar={avatar} /> ) : null; }} diff --git a/app/components/chat/Messages.client.tsx b/app/components/chat/Messages.client.tsx index 918edb4f..2f35f49d 100644 --- a/app/components/chat/Messages.client.tsx +++ b/app/components/chat/Messages.client.tsx @@ -9,11 +9,10 @@ interface MessagesProps { className?: string; isStreaming?: boolean; messages?: Message[]; - avatar?: string; } export const Messages = React.forwardRef((props: MessagesProps, ref) => { - const { id, isStreaming = false, messages = [], avatar } = props; + const { id, isStreaming = false, messages = [] } = props; return (
@@ -36,11 +35,7 @@ export const Messages = React.forwardRef((props: > {isUserMessage && (
- {avatar ? ( - - ) : ( -
- )} +
)}
diff --git a/app/lib/.server/auth.ts b/app/lib/.server/auth.ts deleted file mode 100644 index 1ab24cdc..00000000 --- a/app/lib/.server/auth.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { json, redirect, type LoaderFunctionArgs, type TypedResponse } from '@remix-run/cloudflare'; -import { isAuthenticated, type Session } from './sessions'; - -type RequestArgs = Pick; - -export async function loadWithAuth( - args: T, - handler: (args: T, session: Session) => Promise, -) { - return handleWithAuth(args, handler, (response) => redirect('/login', response)); -} - -export async function actionWithAuth( - args: T, - handler: (args: T, session: Session) => Promise, -) { - return await handleWithAuth(args, handler, (response) => json({}, { status: 401, ...response })); -} - -async function handleWithAuth( - args: T, - handler: (args: T, session: Session) => Promise, - fallback: (partial: ResponseInit) => R, -) { - const { request, context } = args; - const { session, response } = await isAuthenticated(request, context.cloudflare.env); - - if (session == null && !import.meta.env.VITE_DISABLE_AUTH) { - return fallback(response); - } - - const handlerResponse = await handler(args, session || {}); - - if (response) { - for (const [key, value] of Object.entries(response.headers)) { - handlerResponse.headers.append(key, value); - } - } - - return handlerResponse; -} diff --git a/app/lib/.server/sessions.ts b/app/lib/.server/sessions.ts deleted file mode 100644 index 29866195..00000000 --- a/app/lib/.server/sessions.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { createCookieSessionStorage, redirect, type Session as RemixSession } from '@remix-run/cloudflare'; -import { decodeJwt } from 'jose'; -import { CLIENT_ID, CLIENT_ORIGIN } from '~/lib/constants'; -import { request as doRequest } from '~/lib/fetch'; -import { logger } from '~/utils/logger'; -import type { Identity } from '~/lib/analytics'; -import { decrypt, encrypt } from '~/lib/crypto'; - -const DEV_SESSION_SECRET = import.meta.env.DEV ? 'LZQMrERo3Ewn/AbpSYJ9aw==' : undefined; -const DEV_PAYLOAD_SECRET = import.meta.env.DEV ? '2zAyrhjcdFeXk0YEDzilMXbdrGAiR+8ACIUgFNfjLaI=' : undefined; - -const TOKEN_KEY = 't'; -const EXPIRES_KEY = 'e'; -const USER_ID_KEY = 'u'; -const SEGMENT_KEY = 's'; -const AVATAR_KEY = 'a'; -const ENCRYPTED_KEY = 'd'; - -interface PrivateSession { - [TOKEN_KEY]: string; - [EXPIRES_KEY]: number; - [USER_ID_KEY]?: string; - [SEGMENT_KEY]?: string; -} - -interface PublicSession { - [ENCRYPTED_KEY]: string; - [AVATAR_KEY]?: string; -} - -export interface Session { - userId?: string; - segmentWriteKey?: string; - avatar?: string; -} - -export async function isAuthenticated(request: Request, env: Env) { - const { session, sessionStorage } = await getSession(request, env); - - const sessionData: PrivateSession | null = await decryptSessionData(env, session.get(ENCRYPTED_KEY)); - - const header = async (cookie: Promise) => ({ headers: { 'Set-Cookie': await cookie } }); - const destroy = () => header(sessionStorage.destroySession(session)); - - if (sessionData?.[TOKEN_KEY] == null) { - return { session: null, response: await destroy() }; - } - - const expiresAt = sessionData[EXPIRES_KEY] ?? 0; - - if (Date.now() < expiresAt) { - return { session: getSessionData(session, sessionData) }; - } - - logger.debug('Renewing token'); - - let data: Awaited> | null = null; - - try { - data = await refreshToken(sessionData[TOKEN_KEY]); - } catch (error) { - // we can ignore the error here because it's handled below - logger.error(error); - } - - if (data != null) { - const expiresAt = cookieExpiration(data.expires_in, data.created_at); - - const newSessionData = { ...sessionData, [EXPIRES_KEY]: expiresAt }; - const encryptedData = await encryptSessionData(env, newSessionData); - - session.set(ENCRYPTED_KEY, encryptedData); - - return { - session: getSessionData(session, newSessionData), - response: await header(sessionStorage.commitSession(session)), - }; - } else { - return { session: null, response: await destroy() }; - } -} - -export async function createUserSession( - request: Request, - env: Env, - tokens: { refresh: string; expires_in: number; created_at: number }, - identity?: Identity, -): Promise { - const { session, sessionStorage } = await getSession(request, env); - - const expiresAt = cookieExpiration(tokens.expires_in, tokens.created_at); - - const sessionData: PrivateSession = { - [TOKEN_KEY]: tokens.refresh, - [EXPIRES_KEY]: expiresAt, - [USER_ID_KEY]: identity?.userId ?? undefined, - [SEGMENT_KEY]: identity?.segmentWriteKey ?? undefined, - }; - - const encryptedData = await encryptSessionData(env, sessionData); - session.set(ENCRYPTED_KEY, encryptedData); - session.set(AVATAR_KEY, identity?.avatar); - - return { - headers: { - 'Set-Cookie': await sessionStorage.commitSession(session, { - maxAge: 3600 * 24 * 30, // 1 month - }), - }, - }; -} - -function getSessionStorage(cloudflareEnv: Env) { - return createCookieSessionStorage({ - cookie: { - name: '__session', - httpOnly: true, - path: '/', - secrets: [DEV_SESSION_SECRET || cloudflareEnv.SESSION_SECRET], - secure: import.meta.env.PROD, - }, - }); -} - -export async function logout(request: Request, env: Env) { - const { session, sessionStorage } = await getSession(request, env); - - const sessionData = await decryptSessionData(env, session.get(ENCRYPTED_KEY)); - - if (sessionData) { - revokeToken(sessionData[TOKEN_KEY]); - } - - return redirect('/login', { - headers: { - 'Set-Cookie': await sessionStorage.destroySession(session), - }, - }); -} - -export function validateAccessToken(access: string) { - const jwtPayload = decodeJwt(access); - - return jwtPayload.bolt === true; -} - -function getSessionData(session: RemixSession, data: PrivateSession): Session { - return { - userId: data?.[USER_ID_KEY], - segmentWriteKey: data?.[SEGMENT_KEY], - avatar: session.get(AVATAR_KEY), - }; -} - -async function getSession(request: Request, env: Env) { - const sessionStorage = getSessionStorage(env); - const cookie = request.headers.get('Cookie'); - - return { session: await sessionStorage.getSession(cookie), sessionStorage }; -} - -async function refreshToken(refresh: string): Promise<{ expires_in: number; created_at: number }> { - const response = await doRequest(`${CLIENT_ORIGIN}/oauth/token`, { - method: 'POST', - body: urlParams({ grant_type: 'refresh_token', client_id: CLIENT_ID, refresh_token: refresh }), - headers: { - 'content-type': 'application/x-www-form-urlencoded', - }, - }); - - const body = await response.json(); - - if (!response.ok) { - throw new Error(`Unable to refresh token\n${response.status} ${JSON.stringify(body)}`); - } - - const { access_token: access } = body; - - if (!validateAccessToken(access)) { - throw new Error('User is no longer authorized for Bolt'); - } - - return body; -} - -function cookieExpiration(expireIn: number, createdAt: number) { - return (expireIn + createdAt - 10 * 60) * 1000; -} - -async function revokeToken(refresh?: string) { - if (refresh == null) { - return; - } - - try { - const response = await doRequest(`${CLIENT_ORIGIN}/oauth/revoke`, { - method: 'POST', - body: urlParams({ - token: refresh, - token_type_hint: 'refresh_token', - client_id: CLIENT_ID, - }), - headers: { - 'content-type': 'application/x-www-form-urlencoded', - }, - }); - - if (!response.ok) { - throw new Error(`Unable to revoke token: ${response.status}`); - } - } catch (error) { - logger.debug(error); - return; - } -} - -function urlParams(data: Record) { - const encoded = new URLSearchParams(); - - for (const [key, value] of Object.entries(data)) { - encoded.append(key, value); - } - - return encoded; -} - -async function decryptSessionData(env: Env, encryptedData?: string) { - const decryptedData = encryptedData ? await decrypt(payloadSecret(env), encryptedData) : undefined; - const sessionData: PrivateSession | null = JSON.parse(decryptedData ?? 'null'); - - return sessionData; -} - -async function encryptSessionData(env: Env, sessionData: PrivateSession) { - return await encrypt(payloadSecret(env), JSON.stringify(sessionData)); -} - -function payloadSecret(env: Env) { - return DEV_PAYLOAD_SECRET || env.PAYLOAD_SECRET; -} diff --git a/app/lib/analytics.ts b/app/lib/analytics.ts deleted file mode 100644 index 7be24d32..00000000 --- a/app/lib/analytics.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { CLIENT_ORIGIN } from '~/lib/constants'; -import { request as doRequest } from '~/lib/fetch'; - -export interface Identity { - userId?: string | null; - guestId?: string | null; - segmentWriteKey?: string | null; - avatar?: string; -} - -const MESSAGE_PREFIX = 'Bolt'; - -export enum AnalyticsTrackEvent { - MessageSent = `${MESSAGE_PREFIX} Message Sent`, - MessageComplete = `${MESSAGE_PREFIX} Message Complete`, - ChatCreated = `${MESSAGE_PREFIX} Chat Created`, -} - -export async function identifyUser(access: string): Promise { - const response = await doRequest(`${CLIENT_ORIGIN}/api/identify`, { - method: 'GET', - headers: { authorization: `Bearer ${access}` }, - }); - - const body = await response.json(); - - if (!response.ok) { - return undefined; - } - - // convert numerical identity values to strings - const stringified = Object.entries(body).map(([key, value]) => [ - key, - typeof value === 'number' ? value.toString() : value, - ]); - - return Object.fromEntries(stringified) as Identity; -} diff --git a/app/lib/auth.ts b/app/lib/auth.ts deleted file mode 100644 index 6d43611f..00000000 --- a/app/lib/auth.ts +++ /dev/null @@ -1,4 +0,0 @@ -export function forgetAuth() { - // FIXME: use dedicated method - localStorage.removeItem('__wc_api_tokens__'); -} diff --git a/app/lib/constants.ts b/app/lib/constants.ts deleted file mode 100644 index 80120af4..00000000 --- a/app/lib/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const CLIENT_ID = 'bolt'; -export const CLIENT_ORIGIN = import.meta.env.VITE_CLIENT_ORIGIN ?? 'https://stackblitz.com'; diff --git a/app/lib/stores/files.ts b/app/lib/stores/files.ts index 879156cc..663ae581 100644 --- a/app/lib/stores/files.ts +++ b/app/lib/stores/files.ts @@ -116,7 +116,7 @@ export class FilesStore { async #init() { const webcontainer = await this.#webcontainer; - webcontainer.watchPaths( + webcontainer.internal.watchPaths( { include: [`${WORK_DIR}/**`], exclude: ['**/node_modules', '.git'], includeContent: true }, bufferWatchEvents(100, this.#processEventBuffer.bind(this)), ); diff --git a/app/lib/webcontainer/index.ts b/app/lib/webcontainer/index.ts index 1830061b..38bb27f5 100644 --- a/app/lib/webcontainer/index.ts +++ b/app/lib/webcontainer/index.ts @@ -1,6 +1,5 @@ import { WebContainer } from '@webcontainer/api'; import { WORK_DIR_NAME } from '~/utils/constants'; -import { forgetAuth } from '~/lib/auth'; interface WebContainerContext { loaded: boolean; @@ -23,7 +22,6 @@ if (!import.meta.env.SSR) { import.meta.hot?.data.webcontainer ?? Promise.resolve() .then(() => { - forgetAuth(); return WebContainer.boot({ workdirName: WORK_DIR_NAME }); }) .then((webcontainer) => { diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index dbffde7a..86d73409 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,17 +1,14 @@ -import { json, type LoaderFunctionArgs, type MetaFunction } from '@remix-run/cloudflare'; +import { json, type MetaFunction } from '@remix-run/cloudflare'; import { ClientOnly } from 'remix-utils/client-only'; import { BaseChat } from '~/components/chat/BaseChat'; import { Chat } from '~/components/chat/Chat.client'; import { Header } from '~/components/header/Header'; -import { loadWithAuth } from '~/lib/.server/auth'; export const meta: MetaFunction = () => { return [{ title: 'Bolt' }, { name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' }]; }; -export async function loader(args: LoaderFunctionArgs) { - return loadWithAuth(args, async (_args, session) => json({ avatar: session.avatar })); -} +export const loader = () => json({}); export default function Index() { return ( diff --git a/app/routes/api.chat.ts b/app/routes/api.chat.ts index 3b614c27..b685ac85 100644 --- a/app/routes/api.chat.ts +++ b/app/routes/api.chat.ts @@ -1,12 +1,11 @@ import { type ActionFunctionArgs } from '@remix-run/cloudflare'; -import { actionWithAuth } from '~/lib/.server/auth'; import { MAX_RESPONSE_SEGMENTS, MAX_TOKENS } from '~/lib/.server/llm/constants'; import { CONTINUE_PROMPT } from '~/lib/.server/llm/prompts'; import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text'; import SwitchableStream from '~/lib/.server/llm/switchable-stream'; export async function action(args: ActionFunctionArgs) { - return actionWithAuth(args, chatAction); + return chatAction(args); } async function chatAction({ context, request }: ActionFunctionArgs) { diff --git a/app/routes/api.enhancer.ts b/app/routes/api.enhancer.ts index a3ee8b71..5c8175ca 100644 --- a/app/routes/api.enhancer.ts +++ b/app/routes/api.enhancer.ts @@ -1,6 +1,5 @@ import { type ActionFunctionArgs } from '@remix-run/cloudflare'; import { StreamingTextResponse, parseStreamPart } from 'ai'; -import { actionWithAuth } from '~/lib/.server/auth'; import { streamText } from '~/lib/.server/llm/stream-text'; import { stripIndents } from '~/utils/stripIndent'; @@ -8,7 +7,7 @@ const encoder = new TextEncoder(); const decoder = new TextDecoder(); export async function action(args: ActionFunctionArgs) { - return actionWithAuth(args, enhancerAction); + return enhancerAction(args); } async function enhancerAction({ context, request }: ActionFunctionArgs) { diff --git a/app/routes/chat.$id.tsx b/app/routes/chat.$id.tsx index a1315c93..689ab7a2 100644 --- a/app/routes/chat.$id.tsx +++ b/app/routes/chat.$id.tsx @@ -1,9 +1,8 @@ import { json, type LoaderFunctionArgs } from '@remix-run/cloudflare'; import { default as IndexRoute } from './_index'; -import { loadWithAuth } from '~/lib/.server/auth'; export async function loader(args: LoaderFunctionArgs) { - return loadWithAuth(args, async (_args, session) => json({ id: args.params.id, avatar: session.avatar })); + return json({ id: args.params.id }); } export default IndexRoute; diff --git a/app/routes/login.tsx b/app/routes/login.tsx deleted file mode 100644 index c7153373..00000000 --- a/app/routes/login.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import { - json, - redirect, - redirectDocument, - type ActionFunctionArgs, - type LoaderFunctionArgs, -} from '@remix-run/cloudflare'; -import { useFetcher, useLoaderData } from '@remix-run/react'; -import { useEffect, useState } from 'react'; -import { LoadingDots } from '~/components/ui/LoadingDots'; -import { createUserSession, isAuthenticated, validateAccessToken } from '~/lib/.server/sessions'; -import { identifyUser } from '~/lib/analytics'; -import { CLIENT_ID, CLIENT_ORIGIN } from '~/lib/constants'; -import { request as doRequest } from '~/lib/fetch'; -import { auth, type AuthAPI } from '~/lib/webcontainer/auth.client'; -import { logger } from '~/utils/logger'; - -export async function loader({ request, context }: LoaderFunctionArgs) { - const { session, response } = await isAuthenticated(request, context.cloudflare.env); - - if (session != null) { - return redirect('/', response); - } - - const url = new URL(request.url); - - return json( - { - redirected: url.searchParams.has('code') || url.searchParams.has('error'), - }, - response, - ); -} - -export async function action({ request, context }: ActionFunctionArgs) { - const formData = await request.formData(); - - const payload = { - access: String(formData.get('access')), - refresh: String(formData.get('refresh')), - }; - - let response: Awaited> | undefined; - - try { - response = await doRequest(`${CLIENT_ORIGIN}/oauth/token/info`, { - headers: { authorization: `Bearer ${payload.access}` }, - }); - - if (!response.ok) { - throw await response.json(); - } - } catch (error) { - logger.warn('Authentication failed'); - logger.warn(error); - - return json({ error: 'invalid-token' as const }, { status: 401 }); - } - - const boltEnabled = validateAccessToken(payload.access); - - if (!boltEnabled) { - return json({ error: 'bolt-access' as const }, { status: 401 }); - } - - const identity = await identifyUser(payload.access); - - const tokenInfo: { expires_in: number; created_at: number } = await response.json(); - - const init = await createUserSession(request, context.cloudflare.env, { ...payload, ...tokenInfo }, identity); - - return redirectDocument('/', init); -} - -type LoginState = - | { - kind: 'error'; - error: string; - description: string; - } - | { kind: 'pending' }; - -const ERRORS = { - 'bolt-access': 'You do not have access to Bolt.', - 'invalid-token': 'Authentication failed.', -}; - -export default function Login() { - const { redirected } = useLoaderData(); - - useEffect(() => { - if (!import.meta.hot?.data.wcAuth) { - auth.init({ clientId: CLIENT_ID, scope: 'public', editorOrigin: CLIENT_ORIGIN }); - } - - if (import.meta.hot) { - import.meta.hot.data.wcAuth = true; - } - }, []); - - return ( -
- {redirected ? ( - - ) : ( -
-
-

Login

-
- -

- By using Bolt, you agree to the collection of usage data for analytics. -

-
- )} -
- ); -} - -function LoginForm() { - const [login, setLogin] = useState(null); - - const fetcher = useFetcher(); - - useEffect(() => { - auth.logout({ ignoreRevokeError: true }); - }, []); - - useEffect(() => { - if (fetcher.data?.error) { - auth.logout({ ignoreRevokeError: true }); - - setLogin({ - kind: 'error' as const, - ...{ error: fetcher.data.error, description: ERRORS[fetcher.data.error] }, - }); - } - }, [fetcher.data]); - - async function attemptLogin() { - startAuthFlow(); - - function startAuthFlow() { - auth.startAuthFlow({ popup: true }); - - Promise.race([authEvent(auth, 'auth-failed'), auth.loggedIn()]).then((error) => { - if (error) { - setLogin({ kind: 'error', ...error }); - } else { - onTokens(); - } - }); - } - - async function onTokens() { - const tokens = auth.tokens()!; - - fetcher.submit(tokens, { - method: 'POST', - }); - - setLogin({ kind: 'pending' }); - } - } - - return ( - <> - - {login?.kind === 'error' && ( -
-

- {login.error} -

-

{login.description}

-
- )} - - ); -} - -interface AuthError { - error: string; - description: string; -} - -function authEvent(auth: AuthAPI, event: 'logged-out'): Promise; -function authEvent(auth: AuthAPI, event: 'auth-failed'): Promise; -function authEvent(auth: AuthAPI, event: 'logged-out' | 'auth-failed') { - return new Promise((resolve) => { - const unsubscribe = auth.on(event as any, (arg: any) => { - unsubscribe(); - resolve(arg); - }); - }); -} diff --git a/app/routes/logout.tsx b/app/routes/logout.tsx deleted file mode 100644 index b1de638b..00000000 --- a/app/routes/logout.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { LoaderFunctionArgs } from '@remix-run/cloudflare'; -import { logout } from '~/lib/.server/sessions'; - -export async function loader({ request, context }: LoaderFunctionArgs) { - return logout(request, context.cloudflare.env); -} - -export default function Logout() { - return ''; -} diff --git a/package.json b/package.json index ccc0775c..10fd94a6 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "@remix-run/react": "^2.10.2", "@uiw/codemirror-theme-vscode": "^4.23.0", "@unocss/reset": "^0.61.0", - "@webcontainer/api": "^1.3.0-internal.2", + "@webcontainer/api": "1.3.0-internal.10", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", @@ -76,13 +76,16 @@ "unist-util-visit": "^5.0.0" }, "devDependencies": { + "@blitz/eslint-plugin": "0.1.0", "@cloudflare/workers-types": "^4.20240620.0", "@remix-run/dev": "^2.10.0", "@types/diff": "^5.2.1", "@types/react": "^18.2.20", "@types/react-dom": "^18.2.7", "fast-glob": "^3.3.2", + "is-ci": "^3.0.1", "node-fetch": "^3.3.2", + "prettier": "^3.3.2", "typescript": "^5.5.2", "unified": "^11.0.5", "unocss": "^0.61.3", @@ -90,12 +93,9 @@ "vite-plugin-node-polyfills": "^0.22.0", "vite-plugin-optimize-css-modules": "^1.1.0", "vite-tsconfig-paths": "^4.3.2", + "vitest": "^2.0.1", "wrangler": "^3.63.2", - "zod": "^3.23.8", - "@blitz/eslint-plugin": "0.1.0", - "is-ci": "^3.0.1", - "prettier": "^3.3.2", - "vitest": "^2.0.1" + "zod": "^3.23.8" }, "resolutions": { "@typescript-eslint/utils": "^8.0.0-alpha.30" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ef296af..2c593c0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,8 +93,8 @@ importers: specifier: ^0.61.0 version: 0.61.3 '@webcontainer/api': - specifier: ^1.3.0-internal.2 - version: 1.3.0-internal.2 + specifier: 1.3.0-internal.10 + version: 1.3.0-internal.10 '@xterm/addon-fit': specifier: ^0.10.0 version: 0.10.0(@xterm/xterm@5.5.0) @@ -1956,8 +1956,8 @@ packages: '@web3-storage/multipart-parser@1.0.0': resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==} - '@webcontainer/api@1.3.0-internal.2': - resolution: {integrity: sha512-lLSlSehbuYc9E7ecK+tMRX4BbWETNX1OgRlS+NerQh3X3sHNbxLD86eScEMAiA5VBnUeSnLtLe7eC/ftM8fR3Q==} + '@webcontainer/api@1.3.0-internal.10': + resolution: {integrity: sha512-iuqjuDX2uADiJMYZok7+tJqVCJYZ+tU2NwVtxlvakRWSSmIFBGrJ38pD0C5igaOnBV8C9kGDjCE6B03SvLtN4Q==} '@xterm/addon-fit@0.10.0': resolution: {integrity: sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==} @@ -7127,7 +7127,7 @@ snapshots: '@web3-storage/multipart-parser@1.0.0': {} - '@webcontainer/api@1.3.0-internal.2': {} + '@webcontainer/api@1.3.0-internal.10': {} '@xterm/addon-fit@0.10.0(@xterm/xterm@5.5.0)': dependencies: diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts index 3a1564d9..606a4e52 100644 --- a/worker-configuration.d.ts +++ b/worker-configuration.d.ts @@ -1,5 +1,3 @@ interface Env { ANTHROPIC_API_KEY: string; - SESSION_SECRET: string; - PAYLOAD_SECRET: string; }