diff options
| author | Dhravya <[email protected]> | 2024-02-23 16:04:49 -0700 |
|---|---|---|
| committer | Dhravya <[email protected]> | 2024-02-23 16:04:49 -0700 |
| commit | aa1b57bbcd99da4c6d68c145f7c07ea7bd35c8dc (patch) | |
| tree | f5ab247b154a21bbebbe56cca0d7f3397219b7c3 /apps/anycontext-front/src | |
| parent | hide bun lockfile (diff) | |
| download | supermemory-aa1b57bbcd99da4c6d68c145f7c07ea7bd35c8dc.tar.xz supermemory-aa1b57bbcd99da4c6d68c145f7c07ea7bd35c8dc.zip | |
chaos
Diffstat (limited to 'apps/anycontext-front/src')
| -rw-r--r-- | apps/anycontext-front/src/app/MessagePoster.tsx | 19 | ||||
| -rw-r--r-- | apps/anycontext-front/src/app/api/[...nextauth]/route.ts | 2 | ||||
| -rw-r--r-- | apps/anycontext-front/src/app/api/hello/route.ts | 22 | ||||
| -rw-r--r-- | apps/anycontext-front/src/app/api/store/route.ts | 22 | ||||
| -rw-r--r-- | apps/anycontext-front/src/app/favicon.ico | bin | 0 -> 25931 bytes | |||
| -rw-r--r-- | apps/anycontext-front/src/app/globals.css | 33 | ||||
| -rw-r--r-- | apps/anycontext-front/src/app/layout.tsx | 22 | ||||
| -rw-r--r-- | apps/anycontext-front/src/app/not-found.tsx | 58 | ||||
| -rw-r--r-- | apps/anycontext-front/src/app/page.tsx | 11 | ||||
| -rw-r--r-- | apps/anycontext-front/src/env.js | 67 | ||||
| -rw-r--r-- | apps/anycontext-front/src/server/auth.ts | 37 | ||||
| -rw-r--r-- | apps/anycontext-front/src/server/db/index.ts | 8 | ||||
| -rw-r--r-- | apps/anycontext-front/src/server/db/schema.ts | 111 |
13 files changed, 412 insertions, 0 deletions
diff --git a/apps/anycontext-front/src/app/MessagePoster.tsx b/apps/anycontext-front/src/app/MessagePoster.tsx new file mode 100644 index 00000000..ad7d450d --- /dev/null +++ b/apps/anycontext-front/src/app/MessagePoster.tsx @@ -0,0 +1,19 @@ +'use client'; + +import { useEffect } from 'react'; + +function MessagePoster({ jwt }: { jwt: string }) { + useEffect(() => { + if (typeof window === 'undefined') return; + + window.postMessage({ jwt }, '*'); + }, [jwt]); + + return ( + <button onClick={() => window.postMessage({ jwt }, '*')}> + Validate Extension + </button> + ); +} + +export default MessagePoster; diff --git a/apps/anycontext-front/src/app/api/[...nextauth]/route.ts b/apps/anycontext-front/src/app/api/[...nextauth]/route.ts new file mode 100644 index 00000000..db7d1fb8 --- /dev/null +++ b/apps/anycontext-front/src/app/api/[...nextauth]/route.ts @@ -0,0 +1,2 @@ +export { GET, POST } from "@/server/auth"; +export const runtime = "edge"; diff --git a/apps/anycontext-front/src/app/api/hello/route.ts b/apps/anycontext-front/src/app/api/hello/route.ts new file mode 100644 index 00000000..705b3cb8 --- /dev/null +++ b/apps/anycontext-front/src/app/api/hello/route.ts @@ -0,0 +1,22 @@ +import type { NextRequest } from 'next/server' +import { getRequestContext } from '@cloudflare/next-on-pages' + +export const runtime = 'edge' + +export async function GET(request: NextRequest) { + let responseText = 'Hello World' + + // In the edge runtime you can use Bindings that are available in your application + // (for more details see: + // - https://developers.cloudflare.com/pages/framework-guides/deploy-a-nextjs-site/#use-bindings-in-your-nextjs-application + // - https://developers.cloudflare.com/pages/functions/bindings/ + // ) + // + // KV Example: + // const myKv = getRequestContext().env.MY_KV + // await myKv.put('suffix', ' from a KV store!') + // const suffix = await myKv.get('suffix') + // responseText += suffix + + return new Response(responseText) +} diff --git a/apps/anycontext-front/src/app/api/store/route.ts b/apps/anycontext-front/src/app/api/store/route.ts new file mode 100644 index 00000000..0d1c38ff --- /dev/null +++ b/apps/anycontext-front/src/app/api/store/route.ts @@ -0,0 +1,22 @@ +import { db } from "@/server/db"; +import { eq } from "drizzle-orm"; +import { sessions, users } from "@/server/db/schema"; +import { type NextRequest, NextResponse } from "next/server"; + +export const runtime = "edge"; + +export async function GET(req: NextRequest) { + try { + const token = req.cookies.get("next-auth.session-token")?.value ?? req.headers.get("Authorization")?.replace("Bearer ", ""); + + const session = await db.select().from(sessions).where(eq(sessions.sessionToken, token!)) + .leftJoin(users, eq(sessions.userId, users.id)) + + if (!session || session.length === 0) { + return NextResponse.json({ message: "Invalid Key, session not found." }, { status: 404 }); + } + return NextResponse.json({ message: "OK", data: session[0] }, { status: 200 }); + } catch (error) { + return NextResponse.json({ message: "Error", error }, { status: 500 }); + } +}
\ No newline at end of file diff --git a/apps/anycontext-front/src/app/favicon.ico b/apps/anycontext-front/src/app/favicon.ico Binary files differnew file mode 100644 index 00000000..718d6fea --- /dev/null +++ b/apps/anycontext-front/src/app/favicon.ico diff --git a/apps/anycontext-front/src/app/globals.css b/apps/anycontext-front/src/app/globals.css new file mode 100644 index 00000000..875c01e8 --- /dev/null +++ b/apps/anycontext-front/src/app/globals.css @@ -0,0 +1,33 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +@layer utilities { + .text-balance { + text-wrap: balance; + } +} diff --git a/apps/anycontext-front/src/app/layout.tsx b/apps/anycontext-front/src/app/layout.tsx new file mode 100644 index 00000000..3314e478 --- /dev/null +++ b/apps/anycontext-front/src/app/layout.tsx @@ -0,0 +1,22 @@ +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import "./globals.css"; + +const inter = Inter({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + <html lang="en"> + <body className={inter.className}>{children}</body> + </html> + ); +} diff --git a/apps/anycontext-front/src/app/not-found.tsx b/apps/anycontext-front/src/app/not-found.tsx new file mode 100644 index 00000000..3409889a --- /dev/null +++ b/apps/anycontext-front/src/app/not-found.tsx @@ -0,0 +1,58 @@ +export const runtime = "edge"; + +export default function NotFound() { + return ( + <> + <title>404: This page could not be found.</title> + <div style={styles.error}> + <div> + <style + dangerouslySetInnerHTML={{ + __html: `body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}`, + }} + /> + <h1 className="next-error-h1" style={styles.h1}> + 404 + </h1> + <div style={styles.desc}> + <h2 style={styles.h2}>This page could not be found.</h2> + </div> + </div> + </div> + </> + ); +} + +const styles = { + error: { + fontFamily: + 'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"', + height: "100vh", + textAlign: "center", + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + }, + + desc: { + display: "inline-block", + }, + + h1: { + display: "inline-block", + margin: "0 20px 0 0", + padding: "0 23px 0 0", + fontSize: 24, + fontWeight: 500, + verticalAlign: "top", + lineHeight: "49px", + }, + + h2: { + fontSize: 14, + fontWeight: 400, + lineHeight: "49px", + margin: 0, + }, +} as const; diff --git a/apps/anycontext-front/src/app/page.tsx b/apps/anycontext-front/src/app/page.tsx new file mode 100644 index 00000000..11e75371 --- /dev/null +++ b/apps/anycontext-front/src/app/page.tsx @@ -0,0 +1,11 @@ +import Image from "next/image"; +import MessagePoster from "./MessagePoster"; +import { cookies } from "next/headers"; + +export default function Home() { + return ( + <main> + <MessagePoster jwt={cookies().get('next-auth.session-token')?.value!} /> + </main> + ); +} diff --git a/apps/anycontext-front/src/env.js b/apps/anycontext-front/src/env.js new file mode 100644 index 00000000..2ed9456a --- /dev/null +++ b/apps/anycontext-front/src/env.js @@ -0,0 +1,67 @@ +import { createEnv } from "@t3-oss/env-nextjs"; +import { z } from "zod"; + +export const env = process.env + +// export const env = createEnv({ +// /** +// * Specify your server-side environment variables schema here. This way you can ensure the app +// * isn't built with invalid env vars. +// */ +// server: { +// DATABASE_URL: z +// .string() +// .refine( +// (str) => !str.includes("YOUR_MYSQL_URL_HERE"), +// "You forgot to change the default URL" +// ), +// NODE_ENV: z +// .enum(["development", "test", "production"]) +// .default("development"), +// NEXTAUTH_SECRET: +// process.env.NODE_ENV === "production" +// ? z.string() +// : z.string().optional(), +// NEXTAUTH_URL: z.preprocess( +// // This makes Vercel deployments not fail if you don't set NEXTAUTH_URL +// // Since NextAuth.js automatically uses the VERCEL_URL if present. +// (str) => process.env.VERCEL_URL ?? str, +// // VERCEL_URL doesn't include `https` so it cant be validated as a URL +// process.env.VERCEL ? z.string() : z.string().url() +// ), +// GOOGLE_CLIENT_ID: z.string(), +// GOOGLE_CLIENT_SECRET: z.string() +// }, + +// /** +// * Specify your client-side environment variables schema here. This way you can ensure the app +// * isn't built with invalid env vars. To expose them to the client, prefix them with +// * `NEXT_PUBLIC_`. +// */ +// client: { +// // NEXT_PUBLIC_CLIENTVAR: z.string(), +// }, + +// /** +// * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. +// * middlewares) or client-side so we need to destruct manually. +// */ +// runtimeEnv: { +// DATABASE_URL: process.env.DATABASE_URL, +// NODE_ENV: process.env.NODE_ENV, +// NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, +// NEXTAUTH_URL: process.env.NEXTAUTH_URL, +// GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, +// GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET, +// }, +// /** +// * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially +// * useful for Docker builds. +// */ +// skipValidation: !!process.env.SKIP_ENV_VALIDATION, +// /** +// * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and +// * `SOME_VAR=''` will throw an error. +// */ +// emptyStringAsUndefined: true, +// }); diff --git a/apps/anycontext-front/src/server/auth.ts b/apps/anycontext-front/src/server/auth.ts new file mode 100644 index 00000000..3b8d749e --- /dev/null +++ b/apps/anycontext-front/src/server/auth.ts @@ -0,0 +1,37 @@ +import { env } from "@/env"; +import { DrizzleAdapter } from "@auth/drizzle-adapter"; +import NextAuth, { DefaultSession } from "next-auth"; +import { Adapter } from "next-auth/adapters"; +import Google from "next-auth/providers/google"; +import { db } from "./db"; +import { createTable } from "./db/schema"; + +export const { + handlers: { GET, POST }, + auth, +} = NextAuth({ + secret: env.NEXTAUTH_SECRET, + callbacks: { + session: ({session, token}) => ({ + ...session, + user: { + ...session.user, + id: token.id as string, + token + }, + }) + }, + adapter: DrizzleAdapter(db, createTable) as Adapter, + providers: [ + Google({ + clientId: env.GOOGLE_CLIENT_ID, + clientSecret: env.GOOGLE_CLIENT_SECRET, + authorization: { + params: { + prompt: "consent", + response_type: "code", + }, + }, + }), + ], +}); diff --git a/apps/anycontext-front/src/server/db/index.ts b/apps/anycontext-front/src/server/db/index.ts new file mode 100644 index 00000000..b4f4d4ce --- /dev/null +++ b/apps/anycontext-front/src/server/db/index.ts @@ -0,0 +1,8 @@ +import { drizzle } from 'drizzle-orm/d1'; + +import * as schema from "./schema"; + +export const db = drizzle( + process.env.DATABASE as unknown as D1Database, + { schema } +); diff --git a/apps/anycontext-front/src/server/db/schema.ts b/apps/anycontext-front/src/server/db/schema.ts new file mode 100644 index 00000000..7de02f15 --- /dev/null +++ b/apps/anycontext-front/src/server/db/schema.ts @@ -0,0 +1,111 @@ +import { relations, sql } from "drizzle-orm"; +import { + index, + int, + primaryKey, + sqliteTableCreator, + text, +} from "drizzle-orm/sqlite-core"; +import { type AdapterAccount } from "next-auth/adapters"; + +/** + * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same + * database instance for multiple projects. + * + * @see https://orm.drizzle.team/docs/goodies#multi-project-schema + */ +export const createTable = sqliteTableCreator((name) => `anycontext_${name}`); + +export const posts = createTable( + "post", + { + id: int("id", { mode: "number" }).primaryKey({ autoIncrement: true }), + name: text("name", { length: 256 }), + createdById: text("createdById", { length: 255 }) + .notNull() + .references(() => users.id), + createdAt: int("created_at", { mode: "timestamp" }) + .default(sql`CURRENT_TIMESTAMP`) + .notNull(), + updatedAt: int("updatedAt", { mode: "timestamp" }), + }, + (example) => ({ + createdByIdIdx: index("createdById_idx").on(example.createdById), + nameIndex: index("name_idx").on(example.name), + }) +); + +export const users = createTable("user", { + id: text("id", { length: 255 }).notNull().primaryKey(), + name: text("name", { length: 255 }), + email: text("email", { length: 255 }).notNull(), + emailVerified: int("emailVerified", { + mode: "timestamp", + }).default(sql`CURRENT_TIMESTAMP`), + image: text("image", { length: 255 }), +}); + +export const usersRelations = relations(users, ({ many }) => ({ + accounts: many(accounts), +})); + +export const accounts = createTable( + "account", + { + userId: text("userId", { length: 255 }) + .notNull() + .references(() => users.id), + type: text("type", { length: 255 }) + .$type<AdapterAccount["type"]>() + .notNull(), + provider: text("provider", { length: 255 }).notNull(), + providerAccountId: text("providerAccountId", { length: 255 }).notNull(), + refresh_token: text("refresh_token"), + access_token: text("access_token"), + expires_at: int("expires_at"), + token_type: text("token_type", { length: 255 }), + scope: text("scope", { length: 255 }), + id_token: text("id_token"), + session_state: text("session_state", { length: 255 }), + }, + (account) => ({ + compoundKey: primaryKey({ + columns: [account.provider, account.providerAccountId], + }), + userIdIdx: index("account_userId_idx").on(account.userId), + }) +); + +export const accountsRelations = relations(accounts, ({ one }) => ({ + user: one(users, { fields: [accounts.userId], references: [users.id] }), +})); + +export const sessions = createTable( + "session", + { + sessionToken: text("sessionToken", { length: 255 }).notNull().primaryKey(), + userId: text("userId", { length: 255 }) + .notNull() + .references(() => users.id), + expires: int("expires", { mode: "timestamp" }).notNull(), + }, + (session) => ({ + userIdIdx: index("session_userId_idx").on(session.userId), + }) +); + +export const sessionsRelations = relations(sessions, ({ one }) => ({ + user: one(users, { fields: [sessions.userId], references: [users.id] }), +})); + +export const verificationTokens = createTable( + "verificationToken", + { + identifier: text("identifier", { length: 255 }).notNull(), + token: text("token", { length: 255 }).notNull(), + expires: int("expires", { mode: "timestamp" }).notNull(), + }, + (vt) => ({ + compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }), + }) +); |