Skip to content

Commit

Permalink
Merge pull request #12 from tscircuit/registry-compat
Browse files Browse the repository at this point in the history
Registry Compatibility, Login
  • Loading branch information
seveibar authored Oct 5, 2024
2 parents 2145152 + 0ddec3a commit b4a922f
Show file tree
Hide file tree
Showing 46 changed files with 760 additions and 248 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/bun-formatcheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Created using @tscircuit/plop (npm install -g @tscircuit/plop)
name: Format Check

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
format-check:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest

- name: Install dependencies
run: bun install

- name: Run format check
run: bun run lint
26 changes: 26 additions & 0 deletions .github/workflows/bun-typecheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Created using @tscircuit/plop (npm install -g @tscircuit/plop)
name: Type Check

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
type-check:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup bun
uses: oven-sh/setup-bun@v1
with:
bun-version: latest

- name: Install dependencies
run: bun i

- name: Run type check
run: bunx tsc --noEmit
Binary file modified bun.lockb
Binary file not shown.
62 changes: 54 additions & 8 deletions fake-snippets-api/lib/db/db-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import { createStore, type StoreApi } from "zustand/vanilla"
import { immer } from "zustand/middleware/immer"
import { hoist, type HoistedStoreApi } from "zustand-hoist"

import {
databaseSchema,
Snippet,
type DatabaseSchema,
type Thing,
} from "./schema.ts"
import { databaseSchema, Snippet, Session, LoginPage, Account, type DatabaseSchema } from "./schema.ts"
import { combine } from "zustand/middleware"

export const createDatabase = () => {
Expand Down Expand Up @@ -46,7 +41,7 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
},
updateSnippet: (
snippet_id: string,
content: string,
code: string,
updated_at: string,
options?: {
is_board?: boolean
Expand All @@ -66,7 +61,7 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
const updatedSnippets = [...state.snippets]
updatedSnippets[snippetIndex] = {
...updatedSnippets[snippetIndex],
content: content,
code: code,
updated_at: updated_at,
...(options?.is_board !== undefined && { is_board: options.is_board }),
...(options?.is_package !== undefined && {
Expand All @@ -87,4 +82,55 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
const state = get()
return state.snippets.find((snippet) => snippet.snippet_id === snippet_id)
},
addSession: (session: Omit<Session, "session_id">): Session => {
const newSession = { session_id: `session_${Date.now()}`, ...session }
set((state) => ({
sessions: [...state.sessions, newSession],
}))
return newSession
},
getSessions: ({ account_id, is_cli_session }: { account_id: string, is_cli_session?: boolean }): Session[] => {
const state = get()
return state.sessions.filter(
(session) =>
session.account_id === account_id &&
(is_cli_session === undefined || session.is_cli_session === is_cli_session)
)
},
createLoginPage: (): LoginPage => {
const newLoginPage: LoginPage = {
login_page_id: `login_page_${Date.now()}`,
login_page_auth_token: `token_${Date.now()}`,
was_login_successful: false,
has_been_used_to_create_session: false,
created_at: new Date().toISOString(),
expires_at: new Date(Date.now() + 30 * 60 * 1000).toISOString(), // 30 minutes expiration
}
set((state) => ({
loginPages: [...state.loginPages, newLoginPage],
}))
return newLoginPage
},
getLoginPage: (login_page_id: string): LoginPage | undefined => {
const state = get()
return state.loginPages.find((lp) => lp.login_page_id === login_page_id)
},
updateLoginPage: (login_page_id: string, updates: Partial<LoginPage>): void => {
set((state) => ({
loginPages: state.loginPages.map((lp) =>
lp.login_page_id === login_page_id ? { ...lp, ...updates } : lp
),
}))
},
getAccount: (account_id: string): Account | undefined => {
const state = get()
return state.accounts.find((account) => account.account_id === account_id)
},
createSession: (session: Omit<Session, "session_id">): Session => {
const newSession = { session_id: `session_${Date.now()}`, ...session }
set((state) => ({
sessions: [...state.sessions, newSession],
}))
return newSession
},
}))
64 changes: 38 additions & 26 deletions fake-snippets-api/lib/db/schema.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,47 @@
import { z } from "zod"

export const snippetSchema = z
.object({
snippet_id: z.string(),
full_snippet_name: z.string(),
snippet_name: z.string(),
owner_name: z.string(),
content: z.string(),
created_at: z.string(),
updated_at: z.string(),
is_board: z.boolean().default(true),
is_package: z.boolean().default(false),
is_model: z.boolean().default(false),
is_footprint: z.boolean().default(false),
})
.transform((snippet) => ({
...snippet,
type: snippet.is_board
? ("board" as const)
: snippet.is_package
? ("package" as const)
: snippet.is_model
? ("model" as const)
: snippet.is_footprint
? ("footprint" as const)
: ("board" as const),
}))
export const snippetSchema = z.object({
snippet_id: z.string(),
name: z.string(),
unscoped_name: z.string(),
owner_name: z.string(),
code: z.string(),
created_at: z.string(),
updated_at: z.string(),
snippet_type: z.enum(["board", "package", "model", "footprint"]),
description: z.string().optional(),
})
export type Snippet = z.infer<typeof snippetSchema>

export const sessionSchema = z.object({
session_id: z.string(),
account_id: z.string(),
expires_at: z.string(),
is_cli_session: z.boolean(),
})
export type Session = z.infer<typeof sessionSchema>

export const loginPageSchema = z.object({
login_page_id: z.string(),
login_page_auth_token: z.string(),
was_login_successful: z.boolean(),
has_been_used_to_create_session: z.boolean(),
created_at: z.string(),
expires_at: z.string(),
})
export type LoginPage = z.infer<typeof loginPageSchema>

export const accountSchema = z.object({
account_id: z.string(),
github_username: z.string(),
})
export type Account = z.infer<typeof accountSchema>

export const databaseSchema = z.object({
idCounter: z.number().default(0),
snippets: z.array(snippetSchema).default([]),
sessions: z.array(sessionSchema).default([]),
loginPages: z.array(loginPageSchema).default([]),
accounts: z.array(accountSchema).default([]),
})
export type DatabaseSchema = z.infer<typeof databaseSchema>
26 changes: 26 additions & 0 deletions fake-snippets-api/lib/middleware/with-ctx-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Middleware } from "winterspec/middleware"

export type CtxErrorFn = (
status: number,
error_payload: {
error_code: string
message: string
},
) => Response

export const withCtxError: Middleware<
{},
{
error: CtxErrorFn
}
> = async (req, ctx, next) => {
ctx.error = (status, error_payload) => {
return new Response(JSON.stringify({ error: error_payload }), {
status,
headers: {
"Content-Type": "application/json",
},
})
}
return next(req, ctx)
}
41 changes: 41 additions & 0 deletions fake-snippets-api/lib/middleware/with-session-auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { Middleware } from "winterspec/middleware"
import { CtxErrorFn } from "./with-ctx-error"

export const withSessionAuth: Middleware<
{
error: CtxErrorFn
},
{
auth: {
type: "session"
account_id: string
personal_org_id: string
github_username: string
session_id: string
}
},
{}
> = async (req, ctx, next) => {
if (req.method === "OPTIONS") return next(req, ctx)

const token = req.headers.get("authorization")?.split("Bearer ")?.[1]

console.log("token", token)

if (!token) {
return ctx.error(401, {
error_code: "no_token",
message: "No token provided",
})
}

ctx.auth = {
type: "session",
account_id: "account-1234",
personal_org_id: "org-1234",
github_username: "testuser",
session_id: "session-1234",
}

return next(req, ctx)
}
8 changes: 6 additions & 2 deletions fake-snippets-api/lib/middleware/with-winter-spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { createWithWinterSpec } from "winterspec"
import { withDb } from "./with-db"
import { createWithDefaultExceptionHandling } from "winterspec/middleware"
import { withCtxError } from "./with-ctx-error"
import { withSessionAuth } from "./with-session-auth"

export const withRouteSpec = createWithWinterSpec({
apiName: "tscircuit Snippets API",
productionServerUrl: "https://snippets.tscircuit.com/api",
beforeAuthMiddleware: [],
authMiddleware: {},
beforeAuthMiddleware: [withCtxError],
authMiddleware: {
session: withSessionAuth,
},
afterAuthMiddleware: [
withDb,
createWithDefaultExceptionHandling({
Expand Down
2 changes: 1 addition & 1 deletion fake-snippets-api/routes/api/aistream/[...anyroute].ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default async (req: Request) => {
})

// Return a streaming response
return new Response(stream, {
return new Response(stream as any, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
import { z } from "zod"
import ms from "ms"
import { SignJWT } from "jose"

export default withRouteSpec({
methods: ["POST"],
auth: "none",
jsonBody: z
.object({
account_id: z.string(),
})
.or(
z.object({
github_username: z.string(),
}),
),
jsonResponse: z.object({
session: z.object({
session_id: z.string(),
account_id: z.string(),
expires_at: z.string(),
is_cli_session: z.boolean(),
token: z.string(),
}),
}),
})(async (req, ctx) => {
let account
if ("account_id" in req.jsonBody) {
account = ctx.db.getAccount(req.jsonBody.account_id)
} else {
account = ctx.db.getAccount(req.jsonBody.github_username)
}

if (!account) {
return ctx.error(404, {
error_code: "account_not_found",
message: "Account not found",
})
}

const new_session = ctx.db.createSession({
expires_at: new Date(Date.now() + ms("60 day")).toISOString(),
account_id: account.account_id,
is_cli_session: false,
})

const token = await new SignJWT({
account_id: account.account_id,
session_id: new_session.session_id,
github_username: account.github_username,
})
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime(new Date(Date.now() + ms("60 day")).toISOString())
.sign(new TextEncoder().encode(process.env.JWT_SECRET || ""))

return ctx.json({
session: {
...new_session,
token,
},
})
})
Loading

0 comments on commit b4a922f

Please sign in to comment.