diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 88613e5..a468c46 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -41,7 +41,7 @@ jobs: env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - + # This is a temporary workaround to fix issue #13 # On a new Cloudflare account, the terraform apply will fail with `workers.api.error.subdomain_required` # This may be due to the account not having a worker subdomain yet, so we create a dummy worker and then delete it. @@ -55,7 +55,7 @@ jobs: --header 'Authorization: Bearer '$CLOUDFLARE_API_TOKEN \ --header 'Content-Type: application/javascript' \ --data 'addEventListener('\''fetch'\'', (event) => event.respondWith(new Response('\''OK'\'')))'\ - + curl --request DELETE --fail-with-body \ --url https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/workers/scripts/dummy-ib4db6ntj5csdef3 \ --header 'Authorization: Bearer '$CLOUDFLARE_API_TOKEN \ @@ -70,6 +70,14 @@ jobs: cd worker npm install + - name: Update Package Names + run: | + node scripts/update-package-name.js + + - name: Update Wrangler Config + run: | + node scripts/update-wrangler-config.js + - name: Build worker run: | cd worker @@ -79,6 +87,14 @@ jobs: run: | npx @cloudflare/next-on-pages + - name: Get Repository Name + id: repo_name + run: | + # Get repository name and convert to lowercase for Cloudflare compatibility + REPO_NAME=$(basename $GITHUB_REPOSITORY | tr '[:upper:]' '[:lower:]') + echo "repo_name=$REPO_NAME" >> $GITHUB_OUTPUT + echo "Using repository name: $REPO_NAME for Cloudflare resources" + - name: Deploy using Terraform # We're using terraform for first-time setup here, # since we didn't setup a remote backend to store state, @@ -91,19 +107,25 @@ jobs: env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} TF_VAR_CLOUDFLARE_ACCOUNT_ID: ${{ steps.fetch_account_id.outputs.account_id }} + TF_VAR_REPO_NAME: ${{ steps.repo_name.outputs.repo_name }} # Still need to upload worker to keep it up-to-date (Terraform will fail after first-time setup) - name: Upload worker run: | - curl --fail-with-body -X PUT https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/workers/scripts/uptimeflare_worker/content --header 'Authorization: Bearer '$CLOUDFLARE_API_TOKEN -F 'index.js=@worker/dist/index.js;type=application/javascript+module' -F 'metadata={"main_module": "index.js"}' + WORKER_NAME="${REPO_NAME}-worker" + echo "Uploading worker with name: $WORKER_NAME" + curl --fail-with-body -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/workers/scripts/$WORKER_NAME/content" --header 'Authorization: Bearer '$CLOUDFLARE_API_TOKEN -F 'index.js=@worker/dist/index.js;type=application/javascript+module' -F 'metadata={"main_module": "index.js"}' env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ steps.fetch_account_id.outputs.account_id }} + REPO_NAME: ${{ steps.repo_name.outputs.repo_name }} # Currently Terraform Cloudflare provider doesn't support direct upload, use wrangler to upload instead. - name: Upload pages run: | - npx wrangler pages deploy .vercel/output/static --project-name uptimeflare + echo "Uploading pages with project name: $REPO_NAME" + npx wrangler pages deploy .vercel/output/static --project-name $REPO_NAME env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ steps.fetch_account_id.outputs.account_id }} + REPO_NAME: ${{ steps.repo_name.outputs.repo_name }} diff --git a/deploy.tf b/deploy.tf index 65e2a05..7915fe9 100644 --- a/deploy.tf +++ b/deploy.tf @@ -16,14 +16,21 @@ variable "CLOUDFLARE_ACCOUNT_ID" { type = string } +variable "REPO_NAME" { + # Repository name used for resource naming + type = string + default = "uptimeflare" + description = "The repository name used for naming Cloudflare resources" +} + resource "cloudflare_workers_kv_namespace" "uptimeflare_kv" { account_id = var.CLOUDFLARE_ACCOUNT_ID - title = "uptimeflare_kv" + title = "${var.REPO_NAME}-kv" } resource "cloudflare_worker_script" "uptimeflare" { account_id = var.CLOUDFLARE_ACCOUNT_ID - name = "uptimeflare_worker" + name = "${var.REPO_NAME}-worker" content = file("worker/dist/index.js") module = true compatibility_date = "2023-11-08" @@ -44,7 +51,7 @@ resource "cloudflare_worker_cron_trigger" "uptimeflare_worker_cron" { resource "cloudflare_pages_project" "uptimeflare" { account_id = var.CLOUDFLARE_ACCOUNT_ID - name = "uptimeflare" + name = var.REPO_NAME production_branch = "main" deployment_configs { diff --git a/next.config.js b/next.config.js index 42e9320..8a5756e 100644 --- a/next.config.js +++ b/next.config.js @@ -7,6 +7,14 @@ module.exports = nextConfig if (process.env.NODE_ENV === 'development') { const { setupDevBindings } = require('@cloudflare/next-on-pages/next-dev') + + // Get the repository name for KV namespace binding + const path = require('path'); + let repoName = path.basename(process.cwd()) || 'uptimeflare'; + + // Convert to lowercase for Cloudflare compatibility + repoName = repoName.toLowerCase(); + setupDevBindings({ bindings: { UPTIMEFLARE_STATE: { diff --git a/package.json b/package.json index dd393b9..1051a9f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "next dev", - "preview": "npx @cloudflare/next-on-pages && wrangler pages dev .vercel/output/static --compatibility-flag nodejs_compat --kv UPTIMEFLARE_STATE", + "preview": "node scripts/update-wrangler-config.js && npx @cloudflare/next-on-pages && wrangler pages dev .vercel/output/static --compatibility-flag nodejs_compat --kv UPTIMEFLARE_STATE", "build": "next build", "start": "next start", "lint": "next lint" diff --git a/scripts/update-package-name.js b/scripts/update-package-name.js new file mode 100644 index 0000000..a416133 --- /dev/null +++ b/scripts/update-package-name.js @@ -0,0 +1,67 @@ +/** + * This script updates the package.json name field with the repository name + */ + +const fs = require('fs'); +const path = require('path'); + +// Get the repository name from the current directory +function getRepoName() { + try { + const cwd = process.cwd(); + let repoName = path.basename(cwd) || 'uptimeflare'; + + // Convert to lowercase for Cloudflare compatibility + repoName = repoName.toLowerCase(); + + return repoName; + } catch (error) { + console.error('Error getting repository name:', error); + return 'uptimeflare'; + } +} + +// Update the package.json file +function updatePackageJson(packagePath, repoName, isWorker = false) { + try { + if (!fs.existsSync(packagePath)) { + console.error(`Package file not found: ${packagePath}`); + return; + } + + const packageJson = require(packagePath); + + // Update the name field + // For worker package, use dashes instead of underscores + if (isWorker) { + packageJson.name = `${repoName}-worker`; + } else { + packageJson.name = repoName; + } + + // Write the updated package.json + fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2), 'utf8'); + console.log(`Updated ${packagePath} with name = "${packageJson.name}"`); + } catch (error) { + console.error(`Error updating ${packagePath}:`, error); + } +} + +// Main function +function main() { + const repoName = getRepoName(); + console.log(`Using repository name: ${repoName}`); + + const packagePath = path.join(process.cwd(), 'package.json'); + const workerPackagePath = path.join(process.cwd(), 'worker', 'package.json'); + + updatePackageJson(packagePath, repoName); + + // Also update the worker package.json if it exists + if (fs.existsSync(workerPackagePath)) { + updatePackageJson(workerPackagePath, repoName, true); + } +} + +// Run the script +main(); diff --git a/scripts/update-wrangler-config.js b/scripts/update-wrangler-config.js new file mode 100644 index 0000000..5e04317 --- /dev/null +++ b/scripts/update-wrangler-config.js @@ -0,0 +1,65 @@ +/** + * This script updates the wrangler.toml and wrangler-dev.toml files with the repository name + * It's meant to be run before deploying the worker + */ + +const fs = require('fs'); +const path = require('path'); + +// Get the repository name from the current directory +function getRepoName() { + try { + const cwd = process.cwd(); + let repoName = path.basename(cwd) || 'uptimeflare'; + + // Convert to lowercase for Cloudflare compatibility + repoName = repoName.toLowerCase(); + + return repoName; + } catch (error) { + console.error('Error getting repository name:', error); + return 'uptimeflare'; + } +} + +// Update the wrangler.toml file +function updateWranglerConfig(configPath, repoName) { + try { + if (!fs.existsSync(configPath)) { + console.error(`Config file not found: ${configPath}`); + return; + } + + let content = fs.readFileSync(configPath, 'utf8'); + + // For worker names, we need to use dashes instead of underscores + // and ensure it's lowercase alphanumeric with dashes only + const workerName = `${repoName}-worker`; + + // Replace the name line with the repository name + content = content.replace( + /name\s*=\s*"[^"]*"/, + `name = "${workerName}"` + ); + + fs.writeFileSync(configPath, content, 'utf8'); + console.log(`Updated ${configPath} with name = "${workerName}"`); + } catch (error) { + console.error(`Error updating ${configPath}:`, error); + } +} + +// Main function +function main() { + const repoName = getRepoName(); + console.log(`Using repository name: ${repoName}`); + + const wranglerPath = path.join(process.cwd(), 'worker', 'wrangler.toml'); + const wranglerDevPath = path.join(process.cwd(), 'worker', 'wrangler-dev.toml'); + + updateWranglerConfig(wranglerPath, repoName); + updateWranglerConfig(wranglerDevPath, repoName); +} + +// Run the script +main(); diff --git a/util/repoName.ts b/util/repoName.ts new file mode 100644 index 0000000..c30cb16 --- /dev/null +++ b/util/repoName.ts @@ -0,0 +1,24 @@ +/** + * Utility function to get the repository name from the current directory path + * This is used to dynamically name Cloudflare resources based on the repository name + */ +export function getRepoName(): string { + try { + // Get the current directory path + const path = process.cwd(); + + // Extract the repository name from the path + // The repository name is the last directory in the path + let repoName = path.split(/[\/\\]/).pop() || 'uptimeflare'; + + // Convert to lowercase and ensure it's compatible with Cloudflare naming requirements + // For KV namespaces, we use underscores + repoName = repoName.toLowerCase(); + + return repoName; + } catch (error) { + // Fallback to default name if there's an error + console.error('Error getting repository name:', error); + return 'uptimeflare'; + } +} diff --git a/worker/wrangler-dev.toml b/worker/wrangler-dev.toml index 61a29c8..56307d4 100644 --- a/worker/wrangler-dev.toml +++ b/worker/wrangler-dev.toml @@ -1,3 +1,5 @@ +# The name will be set dynamically during deployment +# Default name is used for local development name = "uptimeflare_worker" main = "src/index.ts" compatibility_date = "2023-11-08" diff --git a/worker/wrangler.toml b/worker/wrangler.toml index 506b2ce..0576d56 100644 --- a/worker/wrangler.toml +++ b/worker/wrangler.toml @@ -1,3 +1,5 @@ +# The name will be set dynamically during deployment +# Default name is used for local development name = "uptimeflare_worker" main = "src/index.ts" compatibility_date = "2023-11-08"