Monorepo for Aesthetic.Computer
aesthetic.computer
1#!/usr/bin/env node
2// Update Chat DNS, 25.11.28
3// Updates Cloudflare DNS records to point chat subdomains to session-server
4
5import dotenv from "dotenv";
6dotenv.config({ path: "../aesthetic-computer-vault/nanos/conductor.env" });
7
8const CLOUDFLARE_API_TOKEN = process.env.CLOUDFLARE_API_TOKEN;
9const CLOUDFLARE_BASE_URL = "https://api.cloudflare.com/client/v4";
10
11// Session server IP (DigitalOcean)
12const SESSION_SERVER_IP = "157.245.134.225";
13
14const headers = {
15 "Authorization": `Bearer ${CLOUDFLARE_API_TOKEN}`,
16 "Content-Type": "application/json",
17};
18
19// Chat subdomains to migrate
20const chatDomains = [
21 { subdomain: "chat-system.aesthetic.computer", zone: "aesthetic.computer" },
22 { subdomain: "chat-clock.aesthetic.computer", zone: "aesthetic.computer" },
23 { subdomain: "chat.sotce.net", zone: "sotce.net" },
24];
25
26async function fetchZones() {
27 const response = await fetch(`${CLOUDFLARE_BASE_URL}/zones`, { headers });
28 return response.json();
29}
30
31async function fetchZone(zoneName) {
32 const zones = await fetchZones();
33 return zones.result?.find((zone) => zone.name === zoneName);
34}
35
36async function fetchARecord(zoneId, recordName) {
37 const response = await fetch(
38 `${CLOUDFLARE_BASE_URL}/zones/${zoneId}/dns_records?type=A&name=${recordName}`,
39 { headers }
40 );
41 return response.json();
42}
43
44async function updateDNSRecord(zoneId, recordId, data) {
45 const response = await fetch(
46 `${CLOUDFLARE_BASE_URL}/zones/${zoneId}/dns_records/${recordId}`,
47 { method: "PUT", headers, body: JSON.stringify(data) }
48 );
49 return response.json();
50}
51
52async function createDNSRecord(zoneId, data) {
53 const response = await fetch(
54 `${CLOUDFLARE_BASE_URL}/zones/${zoneId}/dns_records`,
55 { method: "POST", headers, body: JSON.stringify(data) }
56 );
57 return response.json();
58}
59
60async function createOrUpdateARecord(subdomain, zone, ip) {
61 console.log(`\n🔄 Processing ${subdomain}...`);
62
63 const zoneId = (await fetchZone(zone))?.id;
64 if (!zoneId) {
65 console.error(` ❌ Zone ID not found for ${zone}`);
66 return false;
67 }
68 console.log(` 📍 Zone ID: ${zoneId}`);
69
70 const recordResponse = await fetchARecord(zoneId, subdomain);
71 const record = recordResponse.result?.[0];
72
73 const data = {
74 type: "A",
75 name: subdomain,
76 content: ip,
77 ttl: 1, // Auto
78 proxied: true,
79 };
80
81 let response;
82 if (record) {
83 console.log(` 📝 Updating existing record (current: ${record.content})`);
84 response = await updateDNSRecord(zoneId, record.id, data);
85 } else {
86 console.log(` ➕ Creating new record`);
87 response = await createDNSRecord(zoneId, data);
88 }
89
90 if (response.success) {
91 console.log(` ✅ Success: ${subdomain} -> ${ip}`);
92 return true;
93 } else {
94 console.log(` ❌ Failed:`, response.errors);
95 return false;
96 }
97}
98
99async function main() {
100 console.log("🌐 Chat DNS Migration Tool");
101 console.log("==========================");
102 console.log(`Target IP: ${SESSION_SERVER_IP}`);
103
104 if (!CLOUDFLARE_API_TOKEN) {
105 console.error("\n❌ Missing Cloudflare API token!");
106 console.log("Make sure aesthetic-computer-vault/nanos/conductor.env exists with:");
107 console.log(" CLOUDFLARE_API_TOKEN=...");
108 process.exit(1);
109 }
110
111 console.log(`\nUsing Bearer token authentication`);
112
113 const dryRun = process.argv.includes("--dry-run");
114 if (dryRun) {
115 console.log("\n⚠️ DRY RUN MODE - No changes will be made\n");
116 }
117
118 let successCount = 0;
119 let failCount = 0;
120
121 for (const { subdomain, zone } of chatDomains) {
122 if (dryRun) {
123 console.log(`\n🔍 Would update: ${subdomain} -> ${SESSION_SERVER_IP}`);
124 successCount++;
125 } else {
126 const success = await createOrUpdateARecord(subdomain, zone, SESSION_SERVER_IP);
127 if (success) successCount++;
128 else failCount++;
129 }
130 }
131
132 console.log("\n==========================");
133 console.log(`✅ Success: ${successCount}`);
134 console.log(`❌ Failed: ${failCount}`);
135
136 if (!dryRun && successCount === chatDomains.length) {
137 console.log("\n🎉 All DNS records updated successfully!");
138 console.log("\n⏳ DNS propagation may take a few minutes.");
139 console.log("Test with: curl https://chat-system.aesthetic.computer");
140 }
141}
142
143main().catch(console.error);