This commit is contained in:
Jordi Enric
2024-10-30 23:18:57 +01:00
parent 5e852ff99c
commit e317dd447e
7 changed files with 21 additions and 94 deletions
@@ -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")
+1 -2
View File
@@ -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"];
+1 -1
View File
@@ -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"}
+9 -8
View File
@@ -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 {
+1 -1
View File
@@ -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 -1
View File
@@ -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",
+8 -10
View File
@@ -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