mirror of
https://github.com/jordienr/zenblog.git
synced 2026-09-26 17:20:26 -04:00
wip
This commit is contained in:
@@ -3,38 +3,11 @@ import { createClient } from "@/lib/server/supabase";
|
||||
import { logger } from "hono/logger";
|
||||
import { prettyJSON } from "hono/pretty-json";
|
||||
import { Hono } from "hono";
|
||||
import bcrypt from "bcrypt";
|
||||
import { categories, postBySlug, posts, tags } from "./public-api.constants";
|
||||
import { PublicApiResponse } from "./public-api.types";
|
||||
import { Post } from "@zenblog/types";
|
||||
import { throwError } from "./public-api.errors";
|
||||
|
||||
async function verifyAPIKey(header: string, blogId: string) {
|
||||
const supabase = createClient();
|
||||
const unhashedKey = header.split(" ")[1];
|
||||
|
||||
if (!unhashedKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from("blogs")
|
||||
.select("access_token")
|
||||
.eq("id", blogId)
|
||||
.single();
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data?.access_token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isValid = await bcrypt.compare(unhashedKey, data?.access_token);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
const app = new Hono()
|
||||
.basePath("/api/public")
|
||||
.use("*", logger())
|
||||
@@ -45,22 +18,11 @@ app.get(posts.path, async (c) => {
|
||||
const offset = parseInt(c.req.query("offset") || "0");
|
||||
const limit = parseInt(c.req.query("limit") || "30");
|
||||
const supabase = createClient();
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (!blogId) {
|
||||
return throwError(c, "MISSING_BLOG_ID");
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return throwError(c, "MISSING_API_KEY");
|
||||
}
|
||||
|
||||
const isValid = await verifyAPIKey(authHeader, blogId);
|
||||
|
||||
if (!isValid) {
|
||||
return throwError(c, "INVALID_API_KEY");
|
||||
}
|
||||
|
||||
const { data: posts, error } = await supabase
|
||||
.from("posts_v7")
|
||||
.select("title, slug, published_at, excerpt, cover_image, tags, category")
|
||||
@@ -89,22 +51,11 @@ app.get(postBySlug.path, async (c) => {
|
||||
const blogId = c.req.param("blogId");
|
||||
const slug = c.req.param("slug");
|
||||
const supabase = createClient();
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (!blogId || !slug) {
|
||||
return throwError(c, "MISSING_BLOG_ID_OR_SLUG");
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return throwError(c, "MISSING_API_KEY");
|
||||
}
|
||||
|
||||
const isValid = await verifyAPIKey(authHeader, blogId);
|
||||
|
||||
if (!isValid) {
|
||||
return throwError(c, "INVALID_API_KEY");
|
||||
}
|
||||
|
||||
const { data: post, error } = await supabase
|
||||
.from("posts_v7")
|
||||
.select(
|
||||
@@ -124,22 +75,11 @@ app.get(postBySlug.path, async (c) => {
|
||||
app.get(categories.path, async (c) => {
|
||||
const blogId = c.req.param("blogId");
|
||||
const supabase = createClient();
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (!blogId) {
|
||||
return throwError(c, "MISSING_BLOG_ID");
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return throwError(c, "MISSING_API_KEY");
|
||||
}
|
||||
|
||||
const isValid = await verifyAPIKey(authHeader, blogId);
|
||||
|
||||
if (!isValid) {
|
||||
return throwError(c, "INVALID_API_KEY");
|
||||
}
|
||||
|
||||
const { data: categories, error } = await supabase
|
||||
.from("categories")
|
||||
.select("name, slug")
|
||||
@@ -155,22 +95,11 @@ app.get(categories.path, async (c) => {
|
||||
app.get(tags.path, async (c) => {
|
||||
const blogId = c.req.param("blogId");
|
||||
const supabase = createClient();
|
||||
const authHeader = c.req.header("Authorization");
|
||||
|
||||
if (!blogId) {
|
||||
return throwError(c, "MISSING_BLOG_ID");
|
||||
}
|
||||
|
||||
if (!authHeader) {
|
||||
return throwError(c, "MISSING_API_KEY");
|
||||
}
|
||||
|
||||
const isValid = await verifyAPIKey(authHeader, blogId);
|
||||
|
||||
if (!isValid) {
|
||||
return throwError(c, "INVALID_API_KEY");
|
||||
}
|
||||
|
||||
const { data: tags, error } = await supabase
|
||||
.from("blog_tags")
|
||||
.select("name, slug")
|
||||
|
||||
Vendored
+1
-2
@@ -1,11 +1,10 @@
|
||||
import { Category, Post, PostWithContent, Tag } from "@zenblog/types";
|
||||
type CreateClientOpts = {
|
||||
accessToken: string;
|
||||
blogId: string;
|
||||
_url?: string;
|
||||
_debug?: boolean;
|
||||
};
|
||||
export declare function createZenblogClient({ accessToken, blogId, _url, _debug, }: CreateClientOpts): {
|
||||
export declare function createZenblogClient({ blogId, _url, _debug, }: CreateClientOpts): {
|
||||
posts: {
|
||||
list: ({ limit, offset, cache }?: {
|
||||
cache?: RequestInit["cache"];
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAsCtE,KAAK,gBAAgB,GAAG;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AACF,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,MAAM,EACN,IAAI,EACJ,MAAM,GACP,EAAE,gBAAgB;;;oBAqBP,WAAW,CAAC,OAAO,CAAC;;;;kBAYP,IAAI,EAAE;;;kBAYL,MAAM;;oBAxBpB,WAAW,CAAC,OAAO,CAAC;;;0BA0BvB,QAAQ;YAAE,IAAI,EAAE,eAAe,CAAA;SAAE,CAAC;;;;kBAUI,QAAQ,EAAE;;;;;kBASV,GAAG,EAAE;;;EASnD"}
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AA0CtE,KAAK,gBAAgB,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AACF,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,IAAI,EACJ,MAAM,GACP,EAAE,gBAAgB;;;oBAiBP,WAAW,CAAC,OAAO,CAAC;;;;kBAYP,IAAI,EAAE;;;kBAYL,MAAM;;oBAxBpB,WAAW,CAAC,OAAO,CAAC;;;0BA0BvB,QAAQ;YAAE,IAAI,EAAE,eAAe,CAAA;SAAE,CAAC;;;;kBAUI,QAAQ,EAAE;;;;;kBASV,GAAG,EAAE;;;EASnD"}
|
||||
Vendored
+9
-8
@@ -13,14 +13,19 @@ function createFetcher(config, log) {
|
||||
const reqOpts = {
|
||||
...opts,
|
||||
headers: {
|
||||
Authorization: config.accessToken,
|
||||
"Content-Type": "application/json",
|
||||
...opts.headers,
|
||||
},
|
||||
};
|
||||
log("fetch ", URL, reqOpts.method);
|
||||
const res = await fetch(URL, reqOpts);
|
||||
const json = await res.json();
|
||||
let json;
|
||||
try {
|
||||
json = await res.json();
|
||||
}
|
||||
catch (e) {
|
||||
(0, lib_1.throwError)("Failed to parse JSON response from API", e);
|
||||
}
|
||||
if (!res.ok) {
|
||||
(0, lib_1.throwError)("Error fetching data from API", res);
|
||||
}
|
||||
@@ -32,17 +37,13 @@ function createFetcher(config, log) {
|
||||
}
|
||||
};
|
||||
}
|
||||
function createZenblogClient({ accessToken, blogId, _url, _debug, }) {
|
||||
function createZenblogClient({ blogId, _url, _debug, }) {
|
||||
if (typeof window !== "undefined") {
|
||||
(0, lib_1.throwError)("Zenblog is not supported in the browser. Make sure you don't leak your access token.");
|
||||
}
|
||||
if (!accessToken) {
|
||||
(0, lib_1.throwError)("accessToken is required");
|
||||
}
|
||||
const logger = (0, lib_1.createLogger)(_debug || false);
|
||||
const fetcher = createFetcher({
|
||||
url: _url || "https://zenblog.com/api/v1/public",
|
||||
accessToken,
|
||||
url: _url || "https://zenblog.com/api/public",
|
||||
blogId,
|
||||
}, logger);
|
||||
return {
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zenblog",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"description": "The typescript client for zenblog",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -6,7 +6,7 @@ function toQueryString(obj: Record<string, any>) {
|
||||
}
|
||||
|
||||
function createFetcher(
|
||||
config: { url: string; accessToken: string; blogId: string },
|
||||
config: { url: string; blogId: string },
|
||||
log: (...args: any[]) => void
|
||||
) {
|
||||
return async function _fetch(path: string, opts: RequestInit) {
|
||||
@@ -15,7 +15,6 @@ function createFetcher(
|
||||
const reqOpts = {
|
||||
...opts,
|
||||
headers: {
|
||||
Authorization: config.accessToken,
|
||||
"Content-Type": "application/json",
|
||||
...opts.headers,
|
||||
},
|
||||
@@ -23,7 +22,12 @@ function createFetcher(
|
||||
|
||||
log("fetch ", URL, reqOpts.method);
|
||||
const res = await fetch(URL, reqOpts);
|
||||
const json = await res.json();
|
||||
let json;
|
||||
try {
|
||||
json = await res.json();
|
||||
} catch (e) {
|
||||
throwError("Failed to parse JSON response from API", e);
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
throwError("Error fetching data from API", res);
|
||||
@@ -38,13 +42,11 @@ function createFetcher(
|
||||
}
|
||||
|
||||
type CreateClientOpts = {
|
||||
accessToken: string;
|
||||
blogId: string;
|
||||
_url?: string;
|
||||
_debug?: boolean;
|
||||
};
|
||||
export function createZenblogClient({
|
||||
accessToken,
|
||||
blogId,
|
||||
_url,
|
||||
_debug,
|
||||
@@ -54,15 +56,11 @@ export function createZenblogClient({
|
||||
"Zenblog is not supported in the browser. Make sure you don't leak your access token."
|
||||
);
|
||||
}
|
||||
if (!accessToken) {
|
||||
throwError("accessToken is required");
|
||||
}
|
||||
|
||||
const logger = createLogger(_debug || false);
|
||||
const fetcher = createFetcher(
|
||||
{
|
||||
url: _url || "https://zenblog.com/api/v1/public",
|
||||
accessToken,
|
||||
url: _url || "https://zenblog.com/api/public",
|
||||
blogId,
|
||||
},
|
||||
logger
|
||||
|
||||
Reference in New Issue
Block a user