Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/apis/extension-store-apis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createApp } from "@aklinker1/zeta";
import { z } from "zod";
import { contextPlugin } from "../plugins/context-plugin";
import { NotFoundHttpError } from "@aklinker1/zeta";
import { HttpStatus } from "@aklinker1/zeta";
import { OpenApiTag } from "../enums";
import { ExtensionStoreNameSchema } from "../models";

export const extensionStoreApis = createApp({
tags: [OpenApiTag.ExtensionStores],
})
.use(contextPlugin)
.get(
"/api/rest/:storeName/:id/screenshots/:index",
{
operationId: "redirectToScreenshot",
description:
"Redirect to a screenshot's URL from the Chrome Web Store listing",
params: z.object({
storeName: ExtensionStoreNameSchema,
id: z.string(),
index: z.coerce.number().int().min(0),
}),
},
async ({ params, stores, set }) => {
const screenshotUrl = await stores[params.storeName].getScreenshotUrl(
params.id,
params.index,
);
if (!screenshotUrl)
throw new NotFoundHttpError("Extension or screenshot not found");

set.status = HttpStatus.Found;
set.headers["Location"] = screenshotUrl;
},
);
7 changes: 4 additions & 3 deletions src/routes/grpahql-routes.ts → src/apis/graphql-apis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { version } from "../../package.json";
import { createGraphql } from "../graphql";
import { z } from "zod";
import dedent from "dedent";
import { OpenApiTag } from "../enums";

const PLAYGROUND_HTML = (PLAYGROUND_HTML_TEMPLATE as any as string).replace(
"{{VERSION}}",
Expand All @@ -12,12 +13,13 @@ const PLAYGROUND_HTML = (PLAYGROUND_HTML_TEMPLATE as any as string).replace(

const graphql = createGraphql();

export const graphqlRoutes = createApp()
export const graphqlApis = createApp({
tags: [OpenApiTag.Graphql],
})
.post(
"/api",
{
summary: "Send Query",
tags: ["GraphQL"],
description:
"Send a query to the GraphQL API. You can play around with queries on the [GraphiQL playground](/playground).",
body: z
Expand Down Expand Up @@ -73,7 +75,6 @@ export const graphqlRoutes = createApp()
"/playground",
{
operationId: "playground",
tags: ["GraphQL"],
description: dedent`
Open the GraphiQL playground. This is where you can interact and test out
the GraphQL API. It also contains the GraphQL documentation explorer.
Expand Down
31 changes: 31 additions & 0 deletions src/apis/system-apis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createApp } from "@aklinker1/zeta";
import z from "zod";
import { version } from "../version";

export const systemApis = createApp({
tags: ["System"],
})
.get(
"/",
{
operationId: "apiDocsRedirect",
summary: "API Docs Redirect",
description: "Redirect to the API reference when visiting the root URL.",
},
({ set }) => {
set.status = 302;
set.headers.Location = "/scalar";
},
)
.get(
"/api/health",
{
operationId: "healthCheck",
description: "Used to make sure the API is up and running.",
responses: z.object({
status: z.literal("ok"),
version: z.string(),
}),
},
() => ({ status: "ok" as const, version }),
);
28 changes: 22 additions & 6 deletions src/dependencies.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
import { createIocContainer } from "@aklinker1/zero-ioc";
import { createChromeService } from "./utils/chrome/chrome-service";
import { createFirefoxService } from "./utils/firefox/firefox-service";
import { createEdgeService } from "./utils/edge/edge-service";
import { createChromeWebStore } from "./services/chrome-web-store";
import { createFirefoxAddonStore } from "./services/firefox-addon-store";
import { createEdgeAddonStore } from "./services/edge-addon-store";
import type { ExtensionStores } from "./services/extension-stores";
import { ExtensionStoreName } from "./enums";

export const dependencies = createIocContainer()
.register("chrome", createChromeService)
.register("firefox", createFirefoxService)
.register("edge", createEdgeService);
.register("chromeWebStore", createChromeWebStore)
.register("firefoxAddonStore", createFirefoxAddonStore)
.register("edgeAddonStore", createEdgeAddonStore)
.register(
"stores",
(deps) =>
({
[ExtensionStoreName.ChromeWebStore]: deps.chromeWebStore,
[ExtensionStoreName.FirefoxAddonStore]: deps.firefoxAddonStore,
[ExtensionStoreName.EdgeAddonStore]: deps.edgeAddonStore,

// Deprecated, but staying around for a while.
[ExtensionStoreName.ChromeExtensions]: deps.chromeWebStore,
[ExtensionStoreName.FirefoxExtensions]: deps.firefoxAddonStore,
[ExtensionStoreName.EdgeExtensions]: deps.edgeAddonStore,
}) satisfies ExtensionStores,
);
18 changes: 18 additions & 0 deletions src/enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export enum ExtensionStoreName {
ChromeWebStore = "chrome-web-store",
FirefoxAddonStore = "firefox-addon-store",
EdgeAddonStore = "edge-addon-store",

/** @deprecated Use {@link ChromeWebStore} instead. */
ChromeExtensions = "chrome-extensions",
/** @deprecated Use {@link FirefoxAddonStore} instead. */
FirefoxExtensions = "firefox-extensions",
/** @deprecated Use {@link EdgeAddonStore} instead. */
EdgeExtensions = "edge-extensions",
}

export enum OpenApiTag {
System = "System",
ExtensionStores = "Extension Stores",
Graphql = "GraphQL",
}
12 changes: 6 additions & 6 deletions src/graphql/resolvers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export const rootResolver: Gql.RootResolver = {
chromeExtension: ({ id }, ctx) => ctx.chrome.getExtension(id),
chromeExtensions: ({ ids }, ctx) => ctx.chrome.getExtensions(ids),
firefoxAddon: ({ id }, ctx) => ctx.firefox.getAddon(id),
firefoxAddons: ({ ids }, ctx) => ctx.firefox.getAddons(ids),
edgeAddon: ({ id }, ctx) => ctx.edge.getAddon(id),
edgeAddons: ({ ids }, ctx) => ctx.edge.getAddons(ids),
chromeExtension: ({ id }, ctx) => ctx.chromeWebStore.getExtension(id),
chromeExtensions: ({ ids }, ctx) => ctx.chromeWebStore.getExtensions(ids),
firefoxAddon: ({ id }, ctx) => ctx.firefoxAddonStore.getExtension(id),
firefoxAddons: ({ ids }, ctx) => ctx.firefoxAddonStore.getExtensions(ids),
edgeAddon: ({ id }, ctx) => ctx.edgeAddonStore.getExtension(id),
edgeAddons: ({ ids }, ctx) => ctx.edgeAddonStore.getExtensions(ids),
};
6 changes: 6 additions & 0 deletions src/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import z from "zod";
import { ExtensionStoreName } from "./enums";

export const ExtensionStoreNameSchema = z
.enum(ExtensionStoreName)
.meta({ ref: "ExtensionStoreName" });
80 changes: 0 additions & 80 deletions src/routes/rest-routes.ts

This file was deleted.

46 changes: 11 additions & 35 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import consola from "consola";
import { createApp } from "@aklinker1/zeta";
import { corsPlugin } from "./plugins/cors-plugin";
import { graphqlRoutes } from "./routes/grpahql-routes";
import { restRoutes } from "./routes/rest-routes";
import { graphqlApis } from "./apis/graphql-apis";
import { extensionStoreApis } from "./apis/extension-store-apis";
import { zodSchemaAdapter } from "@aklinker1/zeta/adapters/zod-schema-adapter";
import { version } from "../package.json";
import { z } from "zod";
import { version } from "./version";
import dedent from "dedent";
import { systemApis } from "./apis/system-apis";
import { OpenApiTag } from "./enums";

const app = createApp({
schemaAdapter: zodSchemaAdapter,
Expand Down Expand Up @@ -35,47 +36,22 @@ const app = createApp({
},
tags: [
{
name: "GraphQL",
name: OpenApiTag.Graphql,
description: dedent`
To play around with the GraphQL API, checkout the
[GraphiQL Playground](/playground).
`,
},
{ name: "Chrome Extensions" },
{ name: "Firefox Addons" },
{ name: "System" },
{ name: OpenApiTag.ExtensionStores },
{ name: OpenApiTag.System },
],
},
})
.onGlobalError(({ error }) => void consola.error(error))
.use(corsPlugin)
.use(restRoutes)
.use(graphqlRoutes)
.get(
"/",
{
summary: "API Docs Redirect",
tags: ["System"],
description: "Redirect to the API reference when visiting the root URL.",
},
({ set }) => {
set.status = 302;
set.headers.Location = "/scalar";
},
)
.get(
"/api/health",
{
summary: "Health Check",
tags: ["System"],
description: "Used to make sure the API is up and running.",
responses: z.object({
status: z.literal("ok"),
version: z.string(),
}),
},
() => ({ status: "ok" as const, version }),
);
.use(systemApis)
.use(extensionStoreApis)
.use(graphqlApis);

export default app;
export type App = typeof app;
Loading
Loading