Skip to content

Commit

Permalink
feat: add cdn and cdn client option
Browse files Browse the repository at this point in the history
  • Loading branch information
cstrnt committed Jan 20, 2024
1 parent 7d052cf commit 81d4499
Show file tree
Hide file tree
Showing 29 changed files with 630 additions and 108 deletions.
7 changes: 7 additions & 0 deletions apps/angular-example/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# angular-example

## 0.0.16

### Patch Changes

- @tryabby/angular@2.0.6
- @tryabby/devtools@5.0.0

## 0.0.15

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion apps/angular-example/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "angular-example",
"version": "0.0.15",
"version": "0.0.16",
"private": true,
"scripts": {
"ng": "ng",
Expand Down
10 changes: 10 additions & 0 deletions apps/cdn/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules
dist
.wrangler
.dev.vars

# Change them to your taste:
wrangler.toml
package-lock.json
yarn.lock
pnpm-lock.yaml
8 changes: 8 additions & 0 deletions apps/cdn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
```
npm install
npm run dev
```

```
npm run deploy
```
16 changes: 16 additions & 0 deletions apps/cdn/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@tryabby/cdn",
"private": true,
"scripts": {
"dev": "wrangler dev src/index.ts",
"deploy": "wrangler deploy --minify src/index.ts"
},
"dependencies": {
"hono": "^3.12.6"
},
"devDependencies": {
"@tryabby/core": "workspace:*",
"@cloudflare/workers-types": "^4.20231218.0",
"wrangler": "^3.22.0"
}
}
66 changes: 66 additions & 0 deletions apps/cdn/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Hono } from "hono";
import { ZoneCache } from "./lib/cache";
import { ABBY_WINDOW_KEY, AbbyDataResponse } from "@tryabby/core";

import { cors } from "hono/cors";
import { timing } from "hono/timing";
import { logger } from "hono/logger";
import { ConfigService } from "./lib/config";

const cache = new ZoneCache<{
config: AbbyDataResponse;
}>({
cloudflareApiKey: "",
domain: "cache.tryabby.com",
fresh: 60 * 1000,
stale: 60 * 1000,
zoneId: "",
});

const configCache = new ConfigService(cache);

const app = new Hono()
.use(
"*",
cors({
origin: "*",
maxAge: 60 * 60 * 24 * 30,
})
)
.use("*", timing())
.use("*", logger())
.get("/:projectId/:environment", async (c) => {
const environment = c.req.param("environment");
const projectId = c.req.param("projectId");

const [data, , reason] = await configCache.retrieveConfig({
c,
environment,
projectId,
});

c.header("x-abby-cache", reason);
return c.json(data);
})
.get("/:projectId/:environment/script.js", async (c) => {
const environment = c.req.param("environment");
const projectId = c.req.param("projectId");

const [data, , reason] = await configCache.retrieveConfig({
c,
environment,
projectId,
});

c.header("x-abby-cache", reason);

const script = `window.${ABBY_WINDOW_KEY} = ${JSON.stringify(data)};`;

return c.text(script, {
headers: {
"Content-Type": "application/javascript",
},
});
});

export default app;
85 changes: 85 additions & 0 deletions apps/cdn/src/lib/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import type { Context } from "hono";

export type CacheConfig = {
/**
* How long an entry should be fresh in milliseconds
*/
fresh: number;

/**
* How long an entry should be stale in milliseconds
*
* Stale entries are still valid but should be refreshed in the background
*/
stale: number;
};

export type Entry<TValue> = {
value: TValue;
};

export type ZoneCacheConfig = CacheConfig & {
domain: string;
zoneId: string;
/**
* This token must have at least
*/
cloudflareApiKey: string;
};

export class ZoneCache<TNamespaces extends Record<string, unknown>> {
private readonly config: ZoneCacheConfig;

constructor(config: ZoneCacheConfig) {
this.config = config;
}

private createCacheKey<TName extends keyof TNamespaces>(
namespace: TName,
key: string,
cacheBuster = "v1"
): URL {
return new URL(
`https://${this.config.domain}/cache/${cacheBuster}/${String(namespace)}/${key}`
);
}

public async get<TName extends keyof TNamespaces>(
c: Context,
namespace: TName,
key: string
): Promise<[TNamespaces[TName] | undefined, "stale" | "hit" | "miss" | "error"]> {
try {
const res = await caches.default.match(new Request(this.createCacheKey(namespace, key)));
if (!res) {
return [undefined, "miss"];
}
const entry = (await res.json()) as Entry<TNamespaces[TName]>;

return [entry.value, "hit"];
} catch (e) {
console.error("zone cache error:", e);
return [undefined, "error"];
}
}

public async set<TName extends keyof TNamespaces>(
_c: Context,
namespace: TName,
key: string,
value: TNamespaces[TName] | null
): Promise<void> {
const entry: Entry<TNamespaces[TName] | null> = {
value: value,
};
const req = new Request(this.createCacheKey(namespace, key));
const res = new Response(JSON.stringify(entry), {
headers: {
"Content-Type": "application/json",
"Cache-Control": `public, s-maxage=60`,
},
});

await caches.default.put(req, res);
}
}
50 changes: 50 additions & 0 deletions apps/cdn/src/lib/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { AbbyDataResponse, HttpService } from "@tryabby/core";

import type { ZoneCache } from "./cache";
import { Context } from "hono";
import { endTime, startTime } from "hono/timing";

export class ConfigService {
constructor(
private readonly cache: ZoneCache<{
config: AbbyDataResponse;
}>
) {}

async retrieveConfig({
environment,
projectId,
c,
}: {
projectId: string;
environment: string;
c: Context;
}) {
const cacheKey = [projectId, environment].join(",");

startTime(c, "cacheRead");
const [cachedData, reason] = await this.cache.get(c, "config", cacheKey);

endTime(c, "cacheRead");

if (cachedData) {
return [cachedData, true, reason] as const;
}

startTime(c, "remoteRead");

const data = await HttpService.getProjectData({
projectId,
environment,
});

if (!data) {
throw new Error("Failed to fetch data");
}

endTime(c, "remoteRead");
c.executionCtx.waitUntil(this.cache.set(c, "config", cacheKey, data));

return [data, false, reason] as const;
}
}
17 changes: 17 additions & 0 deletions apps/cdn/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"lib": [
"esnext"
],
"types": [
"@cloudflare/workers-types"
],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
},
}
9 changes: 9 additions & 0 deletions apps/web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# web

## 0.2.34

### Patch Changes

- Updated dependencies
- @tryabby/core@5.1.4
- @tryabby/devtools@5.0.0
- @tryabby/next@5.0.6

## 0.2.33

### Patch Changes
Expand Down
2 changes: 2 additions & 0 deletions apps/web/abby.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export default defineConfig(
projectId: process.env.NEXT_PUBLIC_ABBY_PROJECT_ID!,
currentEnvironment: process.env.VERCEL_ENV ?? process.env.NODE_ENV,
apiUrl: process.env.NEXT_PUBLIC_ABBY_API_URL,
__experimentalCdnUrl: process.env.NEXT_PUBLIC_ABBY_CDN_URL,
debug: process.env.NEXT_PUBLIC_ABBY_DEBUG === "true",
},
{
environments: ["development", "production"],
Expand Down
4 changes: 2 additions & 2 deletions apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "web",
"version": "0.2.33",
"version": "0.2.34",
"private": true,
"scripts": {
"build": "next build",
Expand Down Expand Up @@ -82,7 +82,7 @@
"csstype": "^3.1.2",
"dayjs": "^1.11.7",
"framer-motion": "^10.12.7",
"hono": "^3.11.8",
"hono": "^3.12.6",
"immer": "^9.0.21",
"ioredis": "^5.3.1",
"isbot": "^3.6.8",
Expand Down
29 changes: 11 additions & 18 deletions apps/web/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
import { makeConfigRoute } from "api/routes/v1_config";
import { makeProjectDataRoute } from "api/routes/v1_project_data";
import { projectDataRoute } from "api/routes/v1_project_data";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { logger } from "hono/logger";
import { makeHealthRoute } from "./routes/health";
import { makeEventRoute } from "./routes/v1_event";
import { makeLegacyProjectDataRoute } from "./routes/legacy_project_data";

export function bootstrapApi() {
const app = new Hono().basePath("/api");

export const app = new Hono()
.basePath("/api")
// base middleware
app.use("*", logger());
app.use("*", cors({ origin: "*", maxAge: 86400 }));

app.route("/health", makeHealthRoute());

.use("*", logger())
.use("*", cors({ origin: "*", maxAge: 86400 }))
.route("/health", makeHealthRoute())
// legacy routes
app.route("/data", makeEventRoute());
app.route("/dashboard", makeLegacyProjectDataRoute());

.route("/data", makeEventRoute())
.route("/dashboard", makeLegacyProjectDataRoute())
// v1 routes
app.route("/v1/config", makeConfigRoute());
app.route("/v1/data", makeProjectDataRoute());
app.route("/v1/track", makeEventRoute());

return app;
}
.route("/v1/config", makeConfigRoute())
.route("/v1/data", projectDataRoute)
.route("/v1/track", makeEventRoute());
7 changes: 7 additions & 0 deletions packages/angular/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @tryabby/angular

## 2.0.6

### Patch Changes

- Updated dependencies
- @tryabby/core@5.1.4

## 2.0.5

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/angular/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tryabby/angular",
"version": "2.0.5",
"version": "2.0.6",
"scripts": {
"ng": "ng",
"start": "ng serve",
Expand Down
6 changes: 6 additions & 0 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @tryabby/core

## 5.1.4

### Patch Changes

- add experimentalCDN flag

## 5.1.3

### Patch Changes
Expand Down
Loading

1 comment on commit 81d4499

@vercel
Copy link

@vercel vercel bot commented on 81d4499 Jan 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.