mirror of
https://github.com/linsa-io/linsa.git
synced 2026-01-11 22:40:32 +01:00
Fix API route patterns for TanStack Start
- Use createFileRoute with server.handlers pattern - Export Route instead of APIRoute
This commit is contained in:
@@ -44,6 +44,7 @@ import { Route as ApiChatThreadsRouteImport } from './routes/api/chat-threads'
|
||||
import { Route as ApiChatMessagesRouteImport } from './routes/api/chat-messages'
|
||||
import { Route as ApiCanvasRouteImport } from './routes/api/canvas'
|
||||
import { Route as ApiBrowserSessionsRouteImport } from './routes/api/browser-sessions'
|
||||
import { Route as ApiBookmarksRouteImport } from './routes/api/bookmarks'
|
||||
import { Route as ApiArchivesRouteImport } from './routes/api/archives'
|
||||
import { Route as ApiApiKeysRouteImport } from './routes/api/api-keys'
|
||||
import { Route as DemoStartServerFuncsRouteImport } from './routes/demo/start.server-funcs'
|
||||
@@ -59,6 +60,7 @@ import { Route as ApiStreamsUsernameRouteImport } from './routes/api/streams.$us
|
||||
import { Route as ApiStreamSettingsRouteImport } from './routes/api/stream.settings'
|
||||
import { Route as ApiStreamReplaysReplayIdRouteImport } from './routes/api/stream-replays.$replayId'
|
||||
import { Route as ApiSpotifyNowPlayingRouteImport } from './routes/api/spotify.now-playing'
|
||||
import { Route as ApiJazzCloudflareConfigRouteImport } from './routes/api/jazz.cloudflare-config'
|
||||
import { Route as ApiFlowgladSplatRouteImport } from './routes/api/flowglad/$'
|
||||
import { Route as ApiCreatorTiersRouteImport } from './routes/api/creator/tiers'
|
||||
import { Route as ApiCreatorSubscribeRouteImport } from './routes/api/creator/subscribe'
|
||||
@@ -256,6 +258,11 @@ const ApiBrowserSessionsRoute = ApiBrowserSessionsRouteImport.update({
|
||||
path: '/api/browser-sessions',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const ApiBookmarksRoute = ApiBookmarksRouteImport.update({
|
||||
id: '/api/bookmarks',
|
||||
path: '/api/bookmarks',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const ApiArchivesRoute = ApiArchivesRouteImport.update({
|
||||
id: '/api/archives',
|
||||
path: '/api/archives',
|
||||
@@ -332,6 +339,11 @@ const ApiSpotifyNowPlayingRoute = ApiSpotifyNowPlayingRouteImport.update({
|
||||
path: '/api/spotify/now-playing',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const ApiJazzCloudflareConfigRoute = ApiJazzCloudflareConfigRouteImport.update({
|
||||
id: '/api/jazz/cloudflare-config',
|
||||
path: '/api/jazz/cloudflare-config',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const ApiFlowgladSplatRoute = ApiFlowgladSplatRouteImport.update({
|
||||
id: '/api/flowglad/$',
|
||||
path: '/api/flowglad/$',
|
||||
@@ -462,6 +474,7 @@ export interface FileRoutesByFullPath {
|
||||
'/users': typeof UsersRoute
|
||||
'/api/api-keys': typeof ApiApiKeysRoute
|
||||
'/api/archives': typeof ApiArchivesRouteWithChildren
|
||||
'/api/bookmarks': typeof ApiBookmarksRoute
|
||||
'/api/browser-sessions': typeof ApiBrowserSessionsRouteWithChildren
|
||||
'/api/canvas': typeof ApiCanvasRouteWithChildren
|
||||
'/api/chat-messages': typeof ApiChatMessagesRoute
|
||||
@@ -493,6 +506,7 @@ export interface FileRoutesByFullPath {
|
||||
'/api/creator/subscribe': typeof ApiCreatorSubscribeRoute
|
||||
'/api/creator/tiers': typeof ApiCreatorTiersRoute
|
||||
'/api/flowglad/$': typeof ApiFlowgladSplatRoute
|
||||
'/api/jazz/cloudflare-config': typeof ApiJazzCloudflareConfigRoute
|
||||
'/api/spotify/now-playing': typeof ApiSpotifyNowPlayingRoute
|
||||
'/api/stream-replays/$replayId': typeof ApiStreamReplaysReplayIdRoute
|
||||
'/api/stream/settings': typeof ApiStreamSettingsRoute
|
||||
@@ -534,6 +548,7 @@ export interface FileRoutesByTo {
|
||||
'/users': typeof UsersRoute
|
||||
'/api/api-keys': typeof ApiApiKeysRoute
|
||||
'/api/archives': typeof ApiArchivesRouteWithChildren
|
||||
'/api/bookmarks': typeof ApiBookmarksRoute
|
||||
'/api/browser-sessions': typeof ApiBrowserSessionsRouteWithChildren
|
||||
'/api/canvas': typeof ApiCanvasRouteWithChildren
|
||||
'/api/chat-messages': typeof ApiChatMessagesRoute
|
||||
@@ -565,6 +580,7 @@ export interface FileRoutesByTo {
|
||||
'/api/creator/subscribe': typeof ApiCreatorSubscribeRoute
|
||||
'/api/creator/tiers': typeof ApiCreatorTiersRoute
|
||||
'/api/flowglad/$': typeof ApiFlowgladSplatRoute
|
||||
'/api/jazz/cloudflare-config': typeof ApiJazzCloudflareConfigRoute
|
||||
'/api/spotify/now-playing': typeof ApiSpotifyNowPlayingRoute
|
||||
'/api/stream-replays/$replayId': typeof ApiStreamReplaysReplayIdRoute
|
||||
'/api/stream/settings': typeof ApiStreamSettingsRoute
|
||||
@@ -608,6 +624,7 @@ export interface FileRoutesById {
|
||||
'/users': typeof UsersRoute
|
||||
'/api/api-keys': typeof ApiApiKeysRoute
|
||||
'/api/archives': typeof ApiArchivesRouteWithChildren
|
||||
'/api/bookmarks': typeof ApiBookmarksRoute
|
||||
'/api/browser-sessions': typeof ApiBrowserSessionsRouteWithChildren
|
||||
'/api/canvas': typeof ApiCanvasRouteWithChildren
|
||||
'/api/chat-messages': typeof ApiChatMessagesRoute
|
||||
@@ -639,6 +656,7 @@ export interface FileRoutesById {
|
||||
'/api/creator/subscribe': typeof ApiCreatorSubscribeRoute
|
||||
'/api/creator/tiers': typeof ApiCreatorTiersRoute
|
||||
'/api/flowglad/$': typeof ApiFlowgladSplatRoute
|
||||
'/api/jazz/cloudflare-config': typeof ApiJazzCloudflareConfigRoute
|
||||
'/api/spotify/now-playing': typeof ApiSpotifyNowPlayingRoute
|
||||
'/api/stream-replays/$replayId': typeof ApiStreamReplaysReplayIdRoute
|
||||
'/api/stream/settings': typeof ApiStreamSettingsRoute
|
||||
@@ -683,6 +701,7 @@ export interface FileRouteTypes {
|
||||
| '/users'
|
||||
| '/api/api-keys'
|
||||
| '/api/archives'
|
||||
| '/api/bookmarks'
|
||||
| '/api/browser-sessions'
|
||||
| '/api/canvas'
|
||||
| '/api/chat-messages'
|
||||
@@ -714,6 +733,7 @@ export interface FileRouteTypes {
|
||||
| '/api/creator/subscribe'
|
||||
| '/api/creator/tiers'
|
||||
| '/api/flowglad/$'
|
||||
| '/api/jazz/cloudflare-config'
|
||||
| '/api/spotify/now-playing'
|
||||
| '/api/stream-replays/$replayId'
|
||||
| '/api/stream/settings'
|
||||
@@ -755,6 +775,7 @@ export interface FileRouteTypes {
|
||||
| '/users'
|
||||
| '/api/api-keys'
|
||||
| '/api/archives'
|
||||
| '/api/bookmarks'
|
||||
| '/api/browser-sessions'
|
||||
| '/api/canvas'
|
||||
| '/api/chat-messages'
|
||||
@@ -786,6 +807,7 @@ export interface FileRouteTypes {
|
||||
| '/api/creator/subscribe'
|
||||
| '/api/creator/tiers'
|
||||
| '/api/flowglad/$'
|
||||
| '/api/jazz/cloudflare-config'
|
||||
| '/api/spotify/now-playing'
|
||||
| '/api/stream-replays/$replayId'
|
||||
| '/api/stream/settings'
|
||||
@@ -828,6 +850,7 @@ export interface FileRouteTypes {
|
||||
| '/users'
|
||||
| '/api/api-keys'
|
||||
| '/api/archives'
|
||||
| '/api/bookmarks'
|
||||
| '/api/browser-sessions'
|
||||
| '/api/canvas'
|
||||
| '/api/chat-messages'
|
||||
@@ -859,6 +882,7 @@ export interface FileRouteTypes {
|
||||
| '/api/creator/subscribe'
|
||||
| '/api/creator/tiers'
|
||||
| '/api/flowglad/$'
|
||||
| '/api/jazz/cloudflare-config'
|
||||
| '/api/spotify/now-playing'
|
||||
| '/api/stream-replays/$replayId'
|
||||
| '/api/stream/settings'
|
||||
@@ -902,6 +926,7 @@ export interface RootRouteChildren {
|
||||
UsersRoute: typeof UsersRoute
|
||||
ApiApiKeysRoute: typeof ApiApiKeysRoute
|
||||
ApiArchivesRoute: typeof ApiArchivesRouteWithChildren
|
||||
ApiBookmarksRoute: typeof ApiBookmarksRoute
|
||||
ApiBrowserSessionsRoute: typeof ApiBrowserSessionsRouteWithChildren
|
||||
ApiCanvasRoute: typeof ApiCanvasRouteWithChildren
|
||||
ApiChatMessagesRoute: typeof ApiChatMessagesRoute
|
||||
@@ -926,6 +951,7 @@ export interface RootRouteChildren {
|
||||
ApiCreatorSubscribeRoute: typeof ApiCreatorSubscribeRoute
|
||||
ApiCreatorTiersRoute: typeof ApiCreatorTiersRoute
|
||||
ApiFlowgladSplatRoute: typeof ApiFlowgladSplatRoute
|
||||
ApiJazzCloudflareConfigRoute: typeof ApiJazzCloudflareConfigRoute
|
||||
ApiSpotifyNowPlayingRoute: typeof ApiSpotifyNowPlayingRoute
|
||||
ApiStreamsUsernameRoute: typeof ApiStreamsUsernameRouteWithChildren
|
||||
ApiStripeBillingRoute: typeof ApiStripeBillingRoute
|
||||
@@ -1189,6 +1215,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof ApiBrowserSessionsRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/api/bookmarks': {
|
||||
id: '/api/bookmarks'
|
||||
path: '/api/bookmarks'
|
||||
fullPath: '/api/bookmarks'
|
||||
preLoaderRoute: typeof ApiBookmarksRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/api/archives': {
|
||||
id: '/api/archives'
|
||||
path: '/api/archives'
|
||||
@@ -1294,6 +1327,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof ApiSpotifyNowPlayingRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/api/jazz/cloudflare-config': {
|
||||
id: '/api/jazz/cloudflare-config'
|
||||
path: '/api/jazz/cloudflare-config'
|
||||
fullPath: '/api/jazz/cloudflare-config'
|
||||
preLoaderRoute: typeof ApiJazzCloudflareConfigRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/api/flowglad/$': {
|
||||
id: '/api/flowglad/$'
|
||||
path: '/api/flowglad/$'
|
||||
@@ -1611,6 +1651,7 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
UsersRoute: UsersRoute,
|
||||
ApiApiKeysRoute: ApiApiKeysRoute,
|
||||
ApiArchivesRoute: ApiArchivesRouteWithChildren,
|
||||
ApiBookmarksRoute: ApiBookmarksRoute,
|
||||
ApiBrowserSessionsRoute: ApiBrowserSessionsRouteWithChildren,
|
||||
ApiCanvasRoute: ApiCanvasRouteWithChildren,
|
||||
ApiChatMessagesRoute: ApiChatMessagesRoute,
|
||||
@@ -1635,6 +1676,7 @@ const rootRouteChildren: RootRouteChildren = {
|
||||
ApiCreatorSubscribeRoute: ApiCreatorSubscribeRoute,
|
||||
ApiCreatorTiersRoute: ApiCreatorTiersRoute,
|
||||
ApiFlowgladSplatRoute: ApiFlowgladSplatRoute,
|
||||
ApiJazzCloudflareConfigRoute: ApiJazzCloudflareConfigRoute,
|
||||
ApiSpotifyNowPlayingRoute: ApiSpotifyNowPlayingRoute,
|
||||
ApiStreamsUsernameRoute: ApiStreamsUsernameRouteWithChildren,
|
||||
ApiStripeBillingRoute: ApiStripeBillingRoute,
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { createAPIFileRoute } from "@tanstack/react-start/api"
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { getDb } from "@/db/connection"
|
||||
import { api_keys, bookmarks, users } from "@/db/schema"
|
||||
|
||||
const json = (data: unknown, status = 200) =>
|
||||
new Response(JSON.stringify(data), {
|
||||
status,
|
||||
headers: { "content-type": "application/json" },
|
||||
})
|
||||
|
||||
// Hash function for API key verification
|
||||
async function hashApiKey(key: string): Promise<string> {
|
||||
const encoder = new TextEncoder()
|
||||
@@ -44,81 +50,91 @@ async function getUserFromApiKey(apiKey: string) {
|
||||
return user || null
|
||||
}
|
||||
|
||||
export const APIRoute = createAPIFileRoute("/api/bookmarks")({
|
||||
// POST - Add a bookmark
|
||||
POST: async ({ request }) => {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { url, title, description, tags, api_key } = body
|
||||
export const Route = createFileRoute("/api/bookmarks")({
|
||||
server: {
|
||||
handlers: {
|
||||
// POST - Add a bookmark
|
||||
POST: async ({ request }) => {
|
||||
try {
|
||||
const body = (await request.json()) as {
|
||||
url?: string
|
||||
title?: string
|
||||
description?: string
|
||||
tags?: string
|
||||
api_key?: string
|
||||
}
|
||||
const { url, title, description, tags, api_key } = body
|
||||
|
||||
if (!url) {
|
||||
return Response.json({ error: "URL is required" }, { status: 400 })
|
||||
}
|
||||
if (!url) {
|
||||
return json({ error: "URL is required" }, 400)
|
||||
}
|
||||
|
||||
if (!api_key) {
|
||||
return Response.json({ error: "API key is required" }, { status: 401 })
|
||||
}
|
||||
if (!api_key) {
|
||||
return json({ error: "API key is required" }, 401)
|
||||
}
|
||||
|
||||
const user = await getUserFromApiKey(api_key)
|
||||
if (!user) {
|
||||
return Response.json({ error: "Invalid API key" }, { status: 401 })
|
||||
}
|
||||
const user = await getUserFromApiKey(api_key)
|
||||
if (!user) {
|
||||
return json({ error: "Invalid API key" }, 401)
|
||||
}
|
||||
|
||||
const db = getDb(process.env.DATABASE_URL!)
|
||||
const db = getDb(process.env.DATABASE_URL!)
|
||||
|
||||
// Insert bookmark
|
||||
const [bookmark] = await db
|
||||
.insert(bookmarks)
|
||||
.values({
|
||||
user_id: user.id,
|
||||
url,
|
||||
title: title || null,
|
||||
description: description || null,
|
||||
tags: tags || null,
|
||||
})
|
||||
.returning()
|
||||
// Insert bookmark
|
||||
const [bookmark] = await db
|
||||
.insert(bookmarks)
|
||||
.values({
|
||||
user_id: user.id,
|
||||
url,
|
||||
title: title || null,
|
||||
description: description || null,
|
||||
tags: tags || null,
|
||||
})
|
||||
.returning()
|
||||
|
||||
return Response.json({
|
||||
success: true,
|
||||
bookmark: {
|
||||
id: bookmark.id,
|
||||
url: bookmark.url,
|
||||
title: bookmark.title,
|
||||
created_at: bookmark.created_at,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Error adding bookmark:", error)
|
||||
return Response.json({ error: "Failed to add bookmark" }, { status: 500 })
|
||||
}
|
||||
},
|
||||
return json({
|
||||
success: true,
|
||||
bookmark: {
|
||||
id: bookmark.id,
|
||||
url: bookmark.url,
|
||||
title: bookmark.title,
|
||||
created_at: bookmark.created_at,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Error adding bookmark:", error)
|
||||
return json({ error: "Failed to add bookmark" }, 500)
|
||||
}
|
||||
},
|
||||
|
||||
// GET - List bookmarks (requires API key in header)
|
||||
GET: async ({ request }) => {
|
||||
try {
|
||||
const apiKey = request.headers.get("x-api-key")
|
||||
// GET - List bookmarks (requires API key in header)
|
||||
GET: async ({ request }) => {
|
||||
try {
|
||||
const apiKey = request.headers.get("x-api-key")
|
||||
|
||||
if (!apiKey) {
|
||||
return Response.json({ error: "API key is required" }, { status: 401 })
|
||||
}
|
||||
if (!apiKey) {
|
||||
return json({ error: "API key is required" }, 401)
|
||||
}
|
||||
|
||||
const user = await getUserFromApiKey(apiKey)
|
||||
if (!user) {
|
||||
return Response.json({ error: "Invalid API key" }, { status: 401 })
|
||||
}
|
||||
const user = await getUserFromApiKey(apiKey)
|
||||
if (!user) {
|
||||
return json({ error: "Invalid API key" }, 401)
|
||||
}
|
||||
|
||||
const db = getDb(process.env.DATABASE_URL!)
|
||||
const db = getDb(process.env.DATABASE_URL!)
|
||||
|
||||
const userBookmarks = await db
|
||||
.select()
|
||||
.from(bookmarks)
|
||||
.where(eq(bookmarks.user_id, user.id))
|
||||
.orderBy(bookmarks.created_at)
|
||||
const userBookmarks = await db
|
||||
.select()
|
||||
.from(bookmarks)
|
||||
.where(eq(bookmarks.user_id, user.id))
|
||||
.orderBy(bookmarks.created_at)
|
||||
|
||||
return Response.json({ bookmarks: userBookmarks })
|
||||
} catch (error) {
|
||||
console.error("Error fetching bookmarks:", error)
|
||||
return Response.json({ error: "Failed to fetch bookmarks" }, { status: 500 })
|
||||
}
|
||||
return json({ bookmarks: userBookmarks })
|
||||
} catch (error) {
|
||||
console.error("Error fetching bookmarks:", error)
|
||||
return json({ error: "Failed to fetch bookmarks" }, 500)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,42 +1,49 @@
|
||||
import { json } from "@tanstack/react-start"
|
||||
import type { APIContext } from "@tanstack/react-router"
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
|
||||
/**
|
||||
* Get or set Cloudflare stream configuration from Jazz
|
||||
*
|
||||
* GET: Returns current Cloudflare Live Input UID
|
||||
* PUT: Updates Cloudflare Live Input UID
|
||||
*/
|
||||
export async function GET({ request, context }: APIContext) {
|
||||
try {
|
||||
// For now, return the hardcoded value
|
||||
// TODO: Read from Jazz when worker is set up
|
||||
return json({
|
||||
liveInputUid: "bb7858eafc85de6c92963f3817477b5d",
|
||||
customerCode: "xctsztqzu046isdc",
|
||||
name: "linsa-nikiv",
|
||||
updatedAt: Date.now(),
|
||||
})
|
||||
} catch (error) {
|
||||
return json({ error: "Failed to fetch config" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
const json = (data: unknown, status = 200) =>
|
||||
new Response(JSON.stringify(data), {
|
||||
status,
|
||||
headers: { "content-type": "application/json" },
|
||||
})
|
||||
|
||||
export async function PUT({ request, context }: APIContext) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { liveInputUid, customerCode, name } = body
|
||||
export const Route = createFileRoute("/api/jazz/cloudflare-config")({
|
||||
server: {
|
||||
handlers: {
|
||||
// GET: Returns current Cloudflare Live Input UID
|
||||
GET: async () => {
|
||||
try {
|
||||
return json({
|
||||
liveInputUid: "bb7858eafc85de6c92963f3817477b5d",
|
||||
customerCode: "xctsztqzu046isdc",
|
||||
name: "linsa-nikiv",
|
||||
updatedAt: Date.now(),
|
||||
})
|
||||
} catch (error) {
|
||||
return json({ error: "Failed to fetch config" }, 500)
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: Write to Jazz when worker is set up
|
||||
// For now, just return success
|
||||
return json({
|
||||
success: true,
|
||||
liveInputUid,
|
||||
customerCode,
|
||||
name,
|
||||
updatedAt: Date.now(),
|
||||
})
|
||||
} catch (error) {
|
||||
return json({ error: "Failed to update config" }, { status: 500 })
|
||||
}
|
||||
}
|
||||
// PUT: Updates Cloudflare Live Input UID
|
||||
PUT: async ({ request }) => {
|
||||
try {
|
||||
const body = (await request.json()) as {
|
||||
liveInputUid?: string
|
||||
customerCode?: string
|
||||
name?: string
|
||||
}
|
||||
const { liveInputUid, customerCode, name } = body
|
||||
|
||||
return json({
|
||||
success: true,
|
||||
liveInputUid,
|
||||
customerCode,
|
||||
name,
|
||||
updatedAt: Date.now(),
|
||||
})
|
||||
} catch (error) {
|
||||
return json({ error: "Failed to update config" }, 500)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user