Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add kv binding in actions for referral tracker #163

Open
wants to merge 13 commits into
base: development
Choose a base branch
from
2 changes: 1 addition & 1 deletion .dev.vars.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
SUPABASE_URL=""
SUPABASE_KEY=""
VOYAGEAI_API_KEY=""
VOYAGEAI_API_KEY=""
32 changes: 32 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,43 @@ on:
types:
- completed

permissions:
contents: read

jobs:
deploy-to-cloudflare:
name: Automatic Cloudflare Deploy
runs-on: ubuntu-22.04
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20.10.0

- name: Run setup script
run: |
yarn install
yarn setup-kv
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

- name: Update wrangler.toml Name Field
run: |
branch_name=$(echo '${{ github.event.ref }}' | sed 's#refs/heads/##' | sed 's#[^a-zA-Z0-9]#-#g')
# Extract base name from wrangler.toml
base_name=$(grep '^name = ' wrangler.toml | sed 's/^name = "\(.*\)"$/\1/')
# Concatenate branch name with base name
new_name="${base_name}-${branch_name}"
# Truncate the new name to 63 characters for RFC 1035
new_name=$(echo "$new_name" | cut -c 1-63)
# Update the wrangler.toml file
sed -i "s/^name = .*/name = \"$new_name\"/" wrangler.toml
echo "Updated wrangler.toml name to: $new_name"

- name: Deploy to Cloudflare
if: ${{ github.event.workflow_run.conclusion == 'success' }}
uses: ubiquity/cloudflare-deploy-action@main
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ node_modules
.pnp.loader.mjs
static/dist
.env
.dev.vars
.wrangler/

cypress/screenshots
Expand Down
108 changes: 108 additions & 0 deletions deploy/setup-kv-namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// This file is a fork from: https://github.com/ubiquity-os/ubiquity-os-kernel

/**
* The purpose of the script is to ensure that the KV for the worker is properly set on deployment.
* There is currently a bug that makes the environment reset on each deploy, because of a problem with Wrangler not
* parsing the TOML configuration properly. See https://github.com/cloudflare/workers-sdk/issues/5634
* It seems to only work when the values are set at the root of the TOML, not withing the environments.
* This scripts takes out the Production values for kv_namespaces and rewrites them at the root of the TOML file.
*/

import { execSync } from "child_process";
import * as fs from "fs";
import * as toml from "toml";
// @ts-expect-error No typings exist for this package
import * as tomlify from "tomlify-j0.4";

const tomlFilePath = "./wrangler.toml";
const wranglerToml: WranglerConfiguration = toml.parse(fs.readFileSync(tomlFilePath, "utf-8"));

const NAMESPACE_TITLE = "kv";
const NAMESPACE_TITLE_WITH_PREFIX = `${wranglerToml.name}-${NAMESPACE_TITLE}`;
const BINDING_NAME = "REFERRAL_TRACKING";

interface Namespace {
id: string;
title: string;
}

interface WranglerConfiguration {
name: string;
env: {
production: {
kv_namespaces?: {
id: string;
binding: string;
}[];
};
dev: {
kv_namespaces?: {
id: string;
binding: string;
}[];
};
};
kv_namespaces: {
id: string;
binding: string;
}[];
}

function updateWranglerToml(namespaceId: string) {
// Ensure kv_namespaces array exists
if (!wranglerToml.kv_namespaces) {
wranglerToml.kv_namespaces = [];
}
if (wranglerToml.env.production.kv_namespaces) {
wranglerToml.kv_namespaces = wranglerToml.env.production.kv_namespaces;
delete wranglerToml.env.production.kv_namespaces;
}
if (wranglerToml.env.dev.kv_namespaces) {
delete wranglerToml.env.dev.kv_namespaces;
}

const existingNamespace = wranglerToml.kv_namespaces.find((o) => o.binding === BINDING_NAME);
if (existingNamespace) {
existingNamespace.id = namespaceId;
} else {
wranglerToml.kv_namespaces.push({
binding: BINDING_NAME,
id: namespaceId,
});
}

fs.writeFileSync(tomlFilePath, tomlify.toToml(wranglerToml, { space: 1 }));
}

async function main() {
// Check if the namespace exists or create a new one
let namespaceId: string;
try {
const res = execSync(`wrangler kv namespace create ${NAMESPACE_TITLE}`).toString();
console.log(res);
const newId = res.match(/id = \s*"([^"]+)"/)?.[1];
if (!newId) {
throw new Error(`The new ID could not be found.`);
}
namespaceId = newId;
console.log(`Namespace created with ID: ${namespaceId}`);
} catch (error) {
console.error(error);
const listOutput = JSON.parse(execSync(`wrangler kv namespace list`).toString()) as Namespace[];
const existingNamespace = listOutput.find((o) => o.title === NAMESPACE_TITLE_WITH_PREFIX);
if (!existingNamespace) {
throw new Error(`Error creating namespace: ${error}`);
}
namespaceId = existingNamespace.id;
console.log(`Namespace ${NAMESPACE_TITLE_WITH_PREFIX} already exists with ID: ${namespaceId}`);
}

updateWranglerToml(namespaceId);
}

main()
.then(() => console.log("Successfully bound namespace."))
.catch((e) => {
console.error("Error checking or creating namespace:\n", e);
process.exit(1);
});
10 changes: 5 additions & 5 deletions functions/referral-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async function handleSet(env: Env, request: Request): Promise<Response> {

const gitHubUserId = gitHubUser.id.toString();

const oldRefCode = await env.KVNamespace.get(gitHubUserId);
const oldRefCode = await env.REFERRAL_TRACKING.get(gitHubUserId);

if (oldRefCode) {
return new Response(`Key '${gitHubUserId}' already has a referral code: '${oldRefCode}'`, {
Expand All @@ -70,7 +70,7 @@ async function handleSet(env: Env, request: Request): Promise<Response> {
});
}

await env.KVNamespace.put(gitHubUserId, referralCode);
await env.REFERRAL_TRACKING.put(gitHubUserId, referralCode);

return new Response(`Key '${gitHubUserId}' added with value '${referralCode}'`, {
headers: corsHeaders,
Expand All @@ -79,7 +79,7 @@ async function handleSet(env: Env, request: Request): Promise<Response> {
}

async function handleGet(gitHubUserId: string, env: Env): Promise<Response> {
const referralCode = await env.KVNamespace.get(gitHubUserId);
const referralCode = await env.REFERRAL_TRACKING.get(gitHubUserId);
if (referralCode) {
return new Response(`Value for '${gitHubUserId}': ${referralCode}`, {
headers: corsHeaders,
Expand All @@ -94,11 +94,11 @@ async function handleGet(gitHubUserId: string, env: Env): Promise<Response> {
}

async function handleList(env: Env): Promise<Response> {
const gitHubUsersIds = await env.KVNamespace.list();
const gitHubUsersIds = await env.REFERRAL_TRACKING.list();
const referrals: Record<string, string | null> = {};

for (const { name: userId } of gitHubUsersIds.keys) {
const referralCode = await env.KVNamespace.get(userId);
const referralCode = await env.REFERRAL_TRACKING.get(userId);
referrals[userId] = referralCode;
}

Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"prepare": "husky install",
"test": "jest --setupFiles dotenv/config --coverage",
"cy:open": "cypress open",
"cy:run": "cypress run"
"cy:run": "cypress run",
"setup-kv": "tsx --env-file=.dev.vars deploy/setup-kv-namespace.ts"
},
"keywords": [
"typescript",
Expand Down Expand Up @@ -62,6 +63,8 @@
"lint-staged": "^15.1.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.2.5",
"toml": "3.0.0",
"tomlify-j0.4": "3.0.0",
"ts-jest": "29.1.2",
"tsx": "^4.7.1",
"typescript": "^5.3.3"
Expand Down
16 changes: 6 additions & 10 deletions src/home/search/string-similarity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,23 @@ export class StringSimilarity {
public static calculate(str1: string, str2: string): number {
const maxLen = Math.max(str1.length, str2.length);
if (maxLen === 0) return 1.0;

const distance = this._calculateLevenshteinDistance(str1, str2);
return 1 - (distance / maxLen);
return 1 - distance / maxLen;
}

private static _calculateLevenshteinDistance(str1: string, str2: string): number {
const matrix: number[][] = Array(str2.length + 1).fill(null).map(() =>
Array(str1.length + 1).fill(null)
);
const matrix: number[][] = Array(str2.length + 1)
.fill(null)
.map(() => Array(str1.length + 1).fill(null));

for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;

for (let j = 1; j <= str2.length; j++) {
for (let i = 1; i <= str1.length; i++) {
const indicator = str1[i - 1] === str2[j - 1] ? 0 : 1;
matrix[j][i] = Math.min(
matrix[j][i - 1] + 1,
matrix[j - 1][i] + 1,
matrix[j - 1][i - 1] + indicator
);
matrix[j][i] = Math.min(matrix[j][i - 1] + 1, matrix[j - 1][i] + 1, matrix[j - 1][i - 1] + indicator);
}
}

Expand Down
19 changes: 14 additions & 5 deletions wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@ compatibility_date = "2024-10-23"

pages_build_output_dir = "./static"

[[kv_namespaces]]
binding = "KVNamespace"
id = "0a6aaf0a6edb428189606b116da58ef7"
[env]
[env.dev]
[[env.dev.kv_namespaces]]
binding = "kv"
id = "9cd5fdf01971402390dbe9e95cc078d3"
Copy link
Member

Choose a reason for hiding this comment

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

This is what we are trying to resolve, it should not be here, the deployment will fail.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We gotta have the production fields else update toml fails cause it doesnt find a field to change

Copy link
Member

Choose a reason for hiding this comment

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

The kernel does not use any of these, they actually should get removed.

Yes but I think the fields can be empty, like for plugins. Example: https://github.com/ubiquity-os-marketplace/command-start-stop/blob/development/wrangler.toml


[vars]
YARN_VERSION = "1.22.22"
[env.production]
[[env.production.kv_namespaces]]
binding = "kv"
id = "TO_BE_DETERMINED"

# Enables Cloudflare Worker Logs
[observability]
enabled = true

# [vars]
# YARN_VERSION = "1.22.22"
Loading
Loading