diff --git a/apps/golinks-v2/src/api/meta.ts b/apps/golinks-v2/src/api/meta.ts new file mode 100644 index 0000000..3c92130 --- /dev/null +++ b/apps/golinks-v2/src/api/meta.ts @@ -0,0 +1,57 @@ +import { OpenAPIRoute, Num, Bool, Str, contentJson } from "chanfana"; +import { Context, Env } from "hono"; +import { generateSlug } from "lib/utils"; +import { EnvBindings } from "types"; +import { z } from "zod"; + +export class CommitHash extends OpenAPIRoute { + schema = { + description: "Get the latest commit hash", + responses: { + "200": { + description: "Returns a commit hash", + content: { + "application/json": { + schema: z.object({ + success: z.boolean().default(true), + result: z.string(), + }), + }, + }, + }, + }, + }; + async handle(c: Context) { + return c.json( + { + success: true, + return: c.env.GIT_DEPLOY_COMMIT, + }, + 200, + ); + } +} + +export class PingPong extends OpenAPIRoute { + schema = { + description: "Ping the API service if it's up.", + responses: { + "200": { + description: "Returns a `success: true`", + content: { + "application/json": { + schema: z.object({ + success: z.boolean(), + }), + }, + }, + }, + }, + }; + async handle(c) { + return c.json({ + success: true, + result: `Pong!`, + }); + } +} diff --git a/apps/golinks-v2/src/index.tsx b/apps/golinks-v2/src/index.tsx index 6cc0ef4..7af8db7 100644 --- a/apps/golinks-v2/src/index.tsx +++ b/apps/golinks-v2/src/index.tsx @@ -7,11 +7,15 @@ import { getDiscordInvite, getLink } from "lib/db"; import { adminApiKey, contact, getWorkersDashboardUrl, homepage, servers, errorMessages, tags } from "lib/constants"; import { DiscordInviteLinkCreate, DiscordInviteLinkList } from "api/discord"; import { adminApiKeyAuth } from "lib/auth"; -import { DeprecatedGoLinkPage } from "pages/deprecated-link" +import { DeprecatedGoLinkPage } from "pages/deprecated-link"; +import { CommitHash, PingPong } from "api/meta"; +import { prettyJSON } from "hono/pretty-json"; +import { handleOldUrls } from "lib/utils"; // Start a Hono app const app = new Hono<{ Bindings: EnvBindings }>(); -app.use("/openapi.json", cors()); +app.use(prettyJSON()); +app.use("/openapi.*", cors()); app.use( "/api/*", cors({ @@ -26,6 +30,7 @@ app.use( }), ); app.use("/api/*", adminApiKeyAuth); +app.use("/*", async (c, next) => await handleOldUrls(c, next)); // Setup OpenAPI registry const openapi = fromHono(app, { @@ -41,11 +46,11 @@ const openapi = fromHono(app, { }, }, servers, - tags, - externalDocs: { - description: "Learn more about golinks and this service", - url: homepage - } + tags, + externalDocs: { + description: "Learn more about golinks and this service", + url: homepage, + }, }, }); @@ -56,6 +61,9 @@ openapi.get("/api/links", GoLinkList); openapi.post("/api/links", GoLinkCreate); openapi.get("/api/discord-invites", DiscordInviteLinkList); openapi.post("/api/discord-invites", DiscordInviteLinkCreate); +openapi.get("/api/ping", PingPong); + +openapi.get("/api/commit", CommitHash); app.get("/", (c) => { return c.redirect(homepage); @@ -72,9 +80,9 @@ app.get("/workers/dashboard", (c) => { return c.redirect(getWorkersDashboardUrl(c.env.DEPLOY_ENV)); }); app.get("/workers", (c) => { - const {origin} = new URL(c.req.url) - return c.redirect(`${origin}/workers/dashboard`) -}) + const { origin } = new URL(c.req.url); + return c.redirect(`${origin}/workers/dashboard`); +}); app.get("/:link", async (c) => { try { @@ -114,23 +122,18 @@ app.get("/discord/:inviteCode", async (c) => { /* Old /edit/* stuff */ app.get("/edit", (c) => { - return c.redirect("/workers/edit") -}) -app.get("/edit/links", (c) => { - if (!c.req.query("force_redirect")) { - return c.redirect("/landing/deprecated?golink=edit/links&reason=golinks+KV+backend+no+longer+in+use,+now+points+to+feedback+link", 301) - } - return c.redirect("/feedback/suggest-new-golink") -}) + return c.redirect("/workers/edit"); +}); + app.get("/landing/deprecated", (c) => { - const params = c.req.query() + const params = c.req.query(); - if (!c.req.param()) { - return c.newResponse("This is unexpected request for this route", 400) - } + if (!c.req.param()) { + return c.newResponse("This is unexpected request for this route", 400); + } - return c.html() -}) + return c.html(); +}); // Export the Hono app export default app; diff --git a/apps/golinks-v2/src/lib/constants.ts b/apps/golinks-v2/src/lib/constants.ts index b6f338a..9b0df9a 100644 --- a/apps/golinks-v2/src/lib/constants.ts +++ b/apps/golinks-v2/src/lib/constants.ts @@ -43,23 +43,23 @@ export const servers = [ ]; export const tags = [ - { - name: "golinks", - description: "Personal golinks and link shortener", - externalDocs: { - description: "Learn more about golinks", - url: homepage - } - }, - { - name: "discord-invites", - description: "Custom Discord invite links at `go/discord/`", - externalDocs: { - description: "Add your Discord invite code", - url: "https://go.andreijiroh.xyz/feedback/add-discord-invite" - } - } -] + { + name: "golinks", + description: "Personal golinks and link shortener", + externalDocs: { + description: "Learn more about golinks", + url: homepage, + }, + }, + { + name: "discord-invites", + description: "Custom Discord invite links at `go/discord/`", + externalDocs: { + description: "Add your Discord invite code", + url: "https://go.andreijiroh.xyz/feedback/add-discord-invite", + }, + }, +]; export const errorMessages = { discordServerNotFound: `\ diff --git a/apps/golinks-v2/src/lib/old-links.json b/apps/golinks-v2/src/lib/old-links.json new file mode 100644 index 0000000..39a6c68 --- /dev/null +++ b/apps/golinks-v2/src/lib/old-links.json @@ -0,0 +1,22 @@ +{ + "/sourcehut/plaintext": { + "reason": "Updating golink to go/plaintext-emails", + "url": "https://useplaintext.email" + }, + "/substack/s/fromthebshq": { + "reason": "Deprecated substack redirect", + "url": "https://fromthebshq.substack.com" + }, + "/yugipedia/discord": { + "reason": "Updating golink to go/discord/yugipedia", + "url": "/discord/yugipedia" + }, + "/edit/links": { + "reason": "golinks KV backend no longer in use, now points to feedback link", + "url": "/feedback/suggest-new-golink" + }, + "/mikedmoy": { + "reason": "You might meant go/gildedguy? (see website reboot blog post from 2016-02-14 for context)", + "url": "https://gildedguy.com/website-reborn" + } +} diff --git a/apps/golinks-v2/src/lib/utils.ts b/apps/golinks-v2/src/lib/utils.ts index 9016f35..b2d9ddf 100644 --- a/apps/golinks-v2/src/lib/utils.ts +++ b/apps/golinks-v2/src/lib/utils.ts @@ -1,3 +1,5 @@ +import { Context, Next } from "hono"; + export const PAGE_SIZE = 10; export function getOffset(page: number): number { @@ -13,3 +15,17 @@ export function generateSlug(length: number) { } return result; } + +import data from "./old-links.json"; +export async function handleOldUrls(c: Context, next: Next) { + const force_redirect = c.req.query("force_redirect"); + console.log(`[old-link-redirects] data for ${c.req.path}: ${JSON.stringify(data[c.req.path])}`); + if (data[c.req.path] && !force_redirect) { + return c.redirect( + `/landing/deprecated?golink=${c.req.path.replace(/^\/|\/$/g, "")}&reason=${encodeURIComponent(data[c.req.path].reason)}`, + ); + } else if (data[c.req.path] && force_redirect) { + return c.redirect(data[c.req.path].url); + } + await next(); +} diff --git a/apps/golinks-v2/src/pages/deprecated-link.tsx b/apps/golinks-v2/src/pages/deprecated-link.tsx index 8e89b88..9c7e5ac 100644 --- a/apps/golinks-v2/src/pages/deprecated-link.tsx +++ b/apps/golinks-v2/src/pages/deprecated-link.tsx @@ -1,35 +1,35 @@ -import type { FC } from "hono/jsx" +import type { FC } from "hono/jsx"; -const Layout: FC = (props,) => { - return ( - - - Deprecated golink landing page - - {props.children} - - ) -} +const Layout: FC = (props) => { + return ( + + + Deprecated golink landing page + + {props.children} + + ); +}; export const DeprecatedGoLinkPage: FC<{ - url: string, - golink: string, - reason: string | "We're changing things up" -}> = (props: { - golink: string, - reason: string | null -})=> { - const forceRedirect = `/${props.golink}?force_redirect=1` - return( - -

Deprecated golink: go/{props.golink}

- This golink is being flagged as deprecated due to the following reason: -
    -
  • {props.reason || "No reason provided yet" }
  • -
-

If the golink is being renamed as the reason, please update your URLs. If you still want to go there, please click here.

-
-

The lack of CSS is intentional to keep things simple. It will be added soon.

-
- ) -} + url: string; + golink: string; + reason: string | "We're changing things up"; +}> = (props: { golink: string; reason: string | null }) => { + const forceRedirect = `/${props.golink}?force_redirect=1`; + return ( + +

Deprecated golink: go/{props.golink}

+ This golink is being flagged as deprecated due to the following reason: +
    +
  • {props.reason || "No reason provided yet"}
  • +
+

+ If the golink is being renamed as the reason, please update your URLs. If you still want to go there, please{" "} + click here. +

+
+

The lack of CSS is intentional to keep things simple. It will be added soon.

+
+ ); +};