mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 05:05:38 -04:00
fix: remove @sofa/core and @sofa/api deps from public-api to fix Vercel runtime compatibility
- Drop `@sofa/core` and `@sofa/api` from `apps/public-api` — importing workspace packages with DB/Node dependencies broke the Vercel edge runtime - `fetchUserData` on Trakt and Simkl importers now returns raw `unknown` API responses; parsing is deferred to the self-hosted server - Add `source` + `rawPayload` fields to `ParsePayloadInput`; `parsePayload` procedure now dispatches to `parseTraktPayload`/`parseSimklPayload` and returns full warnings + diagnostics - Export `ProviderEnum` from `apps/public-api/src/importers/index.ts` instead of defining it inline in `app.ts`
This commit is contained in:
@@ -2,15 +2,17 @@ import { zValidator } from "@hono/zod-validator";
|
||||
import { checkRateLimit } from "@vercel/firewall";
|
||||
import { Hono } from "hono";
|
||||
import { cors } from "hono/cors";
|
||||
import { logger } from "hono/logger";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getImporter, getImporterConfig } from "./importers";
|
||||
import { getImporter, getImporterConfig, ProviderEnum } from "./importers";
|
||||
|
||||
const GITHUB_RELEASES_URL = "https://api.github.com/repos/jakejarvis/sofa/releases/latest";
|
||||
const VERSION_PREFIX_RE = /^v/;
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
app.use("*", logger());
|
||||
app.use("*", cors());
|
||||
|
||||
// ─── Version Check ──────────────────────────────────────────
|
||||
@@ -101,8 +103,6 @@ app.post(
|
||||
// The self-hosted server also prevents concurrent imports per user
|
||||
// via the importJobs table.
|
||||
|
||||
const ProviderEnum = z.enum(["trakt", "simkl"]);
|
||||
|
||||
app.post(
|
||||
"/v1/import/:provider/device-code",
|
||||
zValidator(
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import * as z from "zod";
|
||||
|
||||
import { simkl } from "./simkl";
|
||||
import { trakt } from "./trakt";
|
||||
import type { ImportProvider } from "./types";
|
||||
|
||||
const importers: Record<string, ImportProvider> = {
|
||||
export const ProviderEnum = z.enum(["trakt", "simkl"]);
|
||||
|
||||
const importers: Record<z.infer<typeof ProviderEnum>, ImportProvider> = {
|
||||
trakt,
|
||||
simkl,
|
||||
};
|
||||
|
||||
export function getImporter(name: string): ImportProvider | undefined {
|
||||
export function getImporter(name: z.infer<typeof ProviderEnum>): ImportProvider | undefined {
|
||||
return importers[name];
|
||||
}
|
||||
|
||||
export function getImporterConfig(name: string): {
|
||||
export function getImporterConfig(name: z.infer<typeof ProviderEnum>): {
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
} {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { parseSimklPayload } from "@sofa/core/imports/parsers";
|
||||
|
||||
import type { DeviceCodeResponse, ImportProvider, NormalizedImport, PollResult } from "./types";
|
||||
import type { DeviceCodeResponse, ImportProvider, PollResult } from "./types";
|
||||
|
||||
const API_BASE = "https://api.simkl.com";
|
||||
const AUTH_BASE = "https://simkl.com";
|
||||
@@ -108,7 +106,7 @@ export const simkl: ImportProvider = {
|
||||
return { status: "pending" };
|
||||
},
|
||||
|
||||
async fetchUserData(accessToken, clientId): Promise<NormalizedImport> {
|
||||
async fetchUserData(accessToken, clientId): Promise<unknown> {
|
||||
const headers = simklHeaders(clientId, accessToken);
|
||||
|
||||
// Fetch movies, shows, and anime in parallel
|
||||
@@ -131,14 +129,11 @@ export const simkl: ImportProvider = {
|
||||
animeRes.ok ? (animeRes.json() as Promise<SimklApiItem[]>) : ([] as SimklApiItem[]),
|
||||
]);
|
||||
|
||||
// Flatten API's nested movie/show objects into the flat format
|
||||
// the core parser expects, then delegate normalization
|
||||
const result = parseSimklPayload({
|
||||
// Return flattened API response — parsing happens on the self-hosted server
|
||||
return {
|
||||
movies: flattenSimklItems(moviesData, "movie"),
|
||||
shows: flattenSimklItems(showsData, "show"),
|
||||
anime: flattenSimklItems(animeData, "show"),
|
||||
});
|
||||
|
||||
return result.data;
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { parseTraktPayload } from "@sofa/core/imports/parsers";
|
||||
|
||||
import type { DeviceCodeResponse, ImportProvider, NormalizedImport, PollResult } from "./types";
|
||||
import type { DeviceCodeResponse, ImportProvider, PollResult } from "./types";
|
||||
|
||||
const API_BASE = "https://api.trakt.tv";
|
||||
|
||||
@@ -55,7 +53,7 @@ export const trakt: ImportProvider = {
|
||||
return { status: "expired" };
|
||||
},
|
||||
|
||||
async fetchUserData(accessToken, clientId): Promise<NormalizedImport> {
|
||||
async fetchUserData(accessToken, clientId): Promise<unknown> {
|
||||
const headers = traktHeaders(clientId, accessToken);
|
||||
|
||||
// Fetch all data in parallel
|
||||
@@ -82,15 +80,11 @@ export const trakt: ImportProvider = {
|
||||
ratingsRes.ok ? ratingsRes.json() : [],
|
||||
]);
|
||||
|
||||
// Restructure API response into the format parseTraktPayload expects.
|
||||
// The Trakt API returns the same item shapes as the JSON export format.
|
||||
type TraktPayload = Parameters<typeof parseTraktPayload>[0];
|
||||
const result = parseTraktPayload({
|
||||
// Return raw aggregated API response — parsing happens on the self-hosted server
|
||||
return {
|
||||
history: { movies: moviesData, shows: showsData },
|
||||
watchlist: watchlistData,
|
||||
ratings: ratingsData,
|
||||
} as TraktPayload);
|
||||
|
||||
return result.data;
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import type { NormalizedImport } from "@sofa/api/schemas";
|
||||
|
||||
export type { NormalizedImport };
|
||||
|
||||
export interface DeviceCodeResponse {
|
||||
device_code: string;
|
||||
user_code: string;
|
||||
@@ -19,5 +15,5 @@ export type PollResult =
|
||||
export interface ImportProvider {
|
||||
getDeviceCode(clientId: string, clientSecret: string): Promise<DeviceCodeResponse>;
|
||||
pollForToken(clientId: string, clientSecret: string, deviceCode: string): Promise<PollResult>;
|
||||
fetchUserData(accessToken: string, clientId: string): Promise<NormalizedImport>;
|
||||
fetchUserData(accessToken: string, clientId: string): Promise<unknown>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user