Skip to content

Instantly share code, notes, and snippets.

@yuhangch
Created June 26, 2025 09:03
Show Gist options
  • Save yuhangch/c4b432e4e8fa0753a407238e8f73738e to your computer and use it in GitHub Desktop.
Save yuhangch/c4b432e4e8fa0753a407238e8f73738e to your computer and use it in GitHub Desktop.
import { Context, Env, Hono } from 'hono';
import { cors } from 'hono/cors';
import { getReviewsHandler, postReviewsHandler } from './review';
import {
getMomentHandler,
getMomentsCountHandler,
getMomentsHandler,
postMomentsHandler,
updateMomentsHandler
} from './moment';
import { tmdbApi } from './tmdb';
const app = new Hono();
const token = 'your-token';
async function AuthMiddleware(ctx: any, next: any) {
const { req } = ctx;
const auth = req.headers.get('authorization');
if (auth && auth.startsWith('Bearer')) {
const bearer = bearerAuth({ token });
return bearer(ctx, next);
} else if (auth && auth.startsWith('Basic')) {
const basic = basicAuth({
username: 'hello',
password: 'world'
});
return basic(ctx, next);
} else {
ctx.status = 401;
return ctx.text('Unauthorized');
}
}
app.use('/blog-api/*', cors(
//@ts-ignore
{ allowOrigin: '*' })
);
app.use('/blog-api/*', AuthMiddleware);
app.get('/blog-api/moments/info', getMomentsCountHandler);
app.get('/blog-api/moments/:id', getMomentHandler);
app.get('/blog-api/moments', getMomentsHandler);
app.get('/blog-api/reviews', getReviewsHandler);
app.post('/blog-api/reviews', postReviewsHandler);
app.post('/blog-api/moments', postMomentsHandler);
app.post('/blog-api/moments/:id', updateMomentsHandler);
app.use('/blog-api/tmdb/*', tmdbApi);
export default {
fetch: app.fetch
};
DROP TABLE IF EXISTS moments;
CREATE TABLE IF NOT EXISTS moments (
id integer PRIMARY KEY AUTOINCREMENT,
body text NOT NULL,
tags text WITH NULL,
star integer NOT NULL default 0,
created_at text NOT NULL,
deleted_at text WITH NULL
);
CREATE INDEX idx_moments_created_at ON moments (created_at);
import { Context, Env } from 'hono';
const pageSize = 10;
export const postMomentsHandler = async (c: Context<Env, '/blog-api/moments'>) => {
// body text NOT NULL,
// tags text WITH NULL,
const { body, tags } = await c.req.json();
if (!body) return c.text('Missing body for new post');
if (!tags) return c.text('Missing tags value for new post');
const DB = c.env!.DB as D1Database;
const { success,meta } = await DB.prepare(`
insert into moments (body, tags,created_at) values (?, ?, ?)
`).bind(body, tags, new Date().toISOString()).run();
const lastRowId = meta.last_row_id;
if (success) {
c.status(201);
return c.text(lastRowId.toString());
} else {
c.status(500);
return c.text('Something went wrong');
}
};
export const updateMomentsHandler = async (c: Context<Env, '/blog-api/moments/:id'>) => {
// body text NOT NULL,
// tags text WITH NULL,
const { id } = c.req.param();
if (!id) return c.text('Missing id for new post');
const numberId = parseInt(id);
const { body, tags } = await c.req.json();
if (!body) return c.text('Missing body for new post');
if (!tags) return c.text('Missing tags value for new post');
const DB = c.env!.DB as D1Database;
const { success,meta } = await DB.prepare(`
update moments set body = ?, tags = ? where id = ?
`).bind(body, tags, numberId).run();
if (success) {
c.status(200);
return c.text(numberId.toString());
} else {
c.status(500);
return c.text('Something went wrong');
}
};
export const getMomentsCountHandler = async (c: Context<Env, '/blog-api/moments/info'>) => {
const DB = c.env!.DB as D1Database;
const result = await DB.prepare(`
select count(*) as count from moments
where deleted_at is null
`).first<any>();
return c.json({ totalCount: result.count, maxPageIndex: Math.ceil(result.count / pageSize) });
};
export const getMomentHandler = async (c: Context<Env, '/blog-api/moments/:id'>) => {
const DB = c.env!.DB as D1Database;
const { id } = c.req.param();
const result = await DB.prepare(`
select moments.*,title,title_en,imdb_id from moments
left join reviews on moments.id = reviews.moments_id
where id = ${id}
`).first<any>();
if (result.deleted_at) {
result.body = '404';
}
return c.json(result);
};
export const getMomentsHandler = async (c: Context<Env, '/blog-api/moments'>) => {
let { page } = c.req.query();
if (!page) page = '1';
const offset = (parseInt(page) - 1) * pageSize;
const DB = c.env!.DB as D1Database;
const { results: moments } = await DB.prepare(`
select moments.*,title,title_en,imdb_id from moments
left join reviews on moments.id = reviews.moments_id
where deleted_at is null
order by created_at desc
limit 10 offset ${offset}
`).all();
const count = await DB.prepare(`
select count(*) as count from moments
where deleted_at is null
`).first<any>();
const maxPageIndex = Math.ceil(count.count / pageSize);
return c.json({
moments,
next: parseInt(page) < maxPageIndex ? parseInt(page) + 1 : null,
prev: parseInt(page) > 1 ? parseInt(page) - 1 : null
});
};
CREATE TABLE reviews
(
imdb_id TEXT PRIMARY KEY,
title TEXT,
title_en TEXT,
media_type TEXT,
imdb_rating TEXT,
rating TEXT,
release_date TEXT,
rated_date TEXT,
moments_id INTEGER,
FOREIGN KEY (moments_id) REFERENCES moments(id)
);
import { Context, Env } from 'hono';
const pageSize = 50;
export const getReviewsHandler = async (c: Context<Env, '/blog-api/reviews'>) => {
let { page,media_type } = c.req.query();
if (!page) page = '1';
const offset = (parseInt(page) - 1) * pageSize;
const DB = c.env!.DB as D1Database;
const typeFilter = media_type ? `where media_type = '${media_type}'` : '';
const { results: reviews } = await DB.prepare(`
select * from reviews
${typeFilter}
order by rated_date desc
limit ${pageSize} offset ${offset}
`).all();
const count = await DB.prepare(`
select count(*) as count from reviews
`).first<any>();
const maxPageIndex = Math.ceil(count.count / pageSize);
return c.json({
reviews,
next: parseInt(page) < maxPageIndex ? parseInt(page) + 1 : null,
prev: parseInt(page) > 1 ? parseInt(page) - 1 : null
});
};
export const postReviewsHandler = async (c: Context<Env, '/blog-api/reviews'>) => {
//imdb_id title title_en media_type imdb_rating rating release_date rated_date moments_id
const { imdb_id, title, title_en, media_type, imdb_rating, rating, release_date, rated_date, moments_id } = await c.req.json();
const DB = c.env!.DB as D1Database;
const { success } = await DB.prepare(`
insert into reviews (imdb_id, title, title_en, media_type, imdb_rating, rating, release_date, rated_date, moments_id) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
`).bind(imdb_id, title, title_en, media_type, imdb_rating, rating, release_date, rated_date, moments_id).run();
if (success) {
c.status(201);
return c.text('Created');
} else {
c.status(500);
return c.text('Something went wrong');
}
};
import { Context, Env } from 'hono';
const tmdbApiPath = 'https://api.themoviedb.org/';
const tmdbSecret = 'tmdb-secret';
export const tmdbApi = async (c: Context<Env, '/blog-api/tmdb/*'>) => {
// use request handler get processed request
const url = c.req.url.split("/blog-api/tmdb/")[1];
if (url){
let dest = tmdbApiPath + url;
// return processed response
const response = await fetch(dest, {
headers: {
'accept': 'application/json',
'Authorization': `Bearer ${tmdbSecret}`
}
})
const result = await response.json();
return c.json(result);
}
};
name = "blog-api"
main = "src/index.ts"
compatibility_flags = [ "nodejs_compat" ]
compatibility_date = "2023-07-04"
[[d1_databases]]
binding = "DB"
database_name = "blog"
database_id = "xxxx-xxxx-xxxx"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment