From 7082059fa472001e8f67568ecd0cab682d74c240 Mon Sep 17 00:00:00 2001 From: ProgrammerIn-wonderland <3838shah@gmail.com> Date: Fri, 5 Sep 2025 19:03:04 -0400 Subject: [PATCH] dev: Add deleteMapping to entri service, add extra validation for getConfig --- src/backend/src/om/mappings/subdomain.js | 4 +- src/backend/src/om/proptypes/__all__.js | 12 +++- src/backend/src/services/EntriService.js | 86 +++++++++++++++++++++++- 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/backend/src/om/mappings/subdomain.js b/src/backend/src/om/mappings/subdomain.js index 5b823f02c..000eac72b 100644 --- a/src/backend/src/om/mappings/subdomain.js +++ b/src/backend/src/om/mappings/subdomain.js @@ -64,7 +64,9 @@ module.exports = { // TODO: can this 'adapt' be data instead? async adapt (value) { - return value.toLowerCase(); + if (value !== null) + return value.toLowerCase(); + return null; }, }, root_dir: { diff --git a/src/backend/src/om/proptypes/__all__.js b/src/backend/src/om/proptypes/__all__.js index 1b7ee48a8..78dbaea4e 100644 --- a/src/backend/src/om/proptypes/__all__.js +++ b/src/backend/src/om/proptypes/__all__.js @@ -24,6 +24,7 @@ const { Context } = require("../../util/context"); const { is_valid_path } = require("../../filesystem/validation"); const FSNodeContext = require("../../filesystem/FSNodeContext"); const { Entity } = require("../entitystorage/Entity"); +const NULL = Symbol("NULL") class OMTypeError extends Error { constructor ({ expected, got }) { @@ -43,7 +44,10 @@ module.exports = { from: 'base', }, string: { - from: 'base', + is_set (value) { + console.log("BALAHAJAKJKAJKAJKA: ", value, value !== null || (!!value)) + return (!!value) || value === null + }, async adapt (value) { if ( value === undefined ) return ''; @@ -51,6 +55,11 @@ module.exports = { // then this should become an sql-to-entity adapt only. if ( value === null ) return ''; + if (value === NULL) { + console.log("wow! nully nulled") + return null; + } + if ( typeof value !== 'string' ) { throw new OMTypeError({ expected: 'string', got: typeof value }); } @@ -260,4 +269,5 @@ module.exports = { } } }, + NULL }; diff --git a/src/backend/src/services/EntriService.js b/src/backend/src/services/EntriService.js index 90e14b9ce..b0ad2f4e0 100644 --- a/src/backend/src/services/EntriService.js +++ b/src/backend/src/services/EntriService.js @@ -28,6 +28,8 @@ const { Endpoint } = require("../util/expressutil"); const { IncomingMessage } = require("node:http"); const { Context } = require("../util/context"); const { createHash } = require('crypto'); +const { APIError } = require("openai/error.mjs"); +const { NULL } = require("../om/proptypes/__all__"); // async function generateJWT(applicationId, secret, domain, ) { @@ -58,7 +60,6 @@ class EntriService extends BaseService { return; } if (!req.body.data.records_propagated) { - console.log("Failed to set domain records") return; } let rootDomain = false; @@ -98,8 +99,23 @@ class EntriService extends BaseService { ['entri']: { async getConfig({ domain, userHostedSite }) { const es_subdomain = this.services.get('es:subdomain'); + const svc_su = this.services.get("su"); + let rootDomain = (parseDomain(domain)).icann.subDomains.length === 0; + const exists = await svc_su.sudo(async ()=>{ + const row = (await es_subdomain.select({ predicate: new Eq({ key: "domain", value: domain }) }))[0] || (await es_subdomain.select({ predicate: new Eq({ key: "domain", value: "in-progress:" + domain }) }))[0]; + if (!!row && row.values_.subdomain === userHostedSite.replace(".puter.site", "")) { + return false; + } + return !!row; + }); + + if (exists) { + throw new APIError("already_in_use", null, {what: "domain", value: domain}); + } + + const dnsRecords = rootDomain ? [{ type: "A", host: "@", @@ -131,7 +147,15 @@ class EntriService extends BaseService { }); await es_subdomain.upsert(entity); - return { token: (await response.json()).auth_token, applicationId: this.config.applicationId, power: true, dnsRecords, prefilledDomain: domain } + + return { + token: (await response.json()).auth_token, + applicationId: this.config.applicationId, + power: true, + dnsRecords, + prefilledDomain: domain, + hostRequired: false + } // let rootDomain = (parseDomain(domain)).icann.subDomains.length === 0; @@ -155,6 +179,54 @@ class EntriService extends BaseService { + }, + async deleteMapping({domain}) { + if (domain.startsWith("in-progress")) + throw new APIError('field_invalid', null, {key: "domain", expected: 'valid domain'}); + + /** @type {import("../om/entitystorage/SubdomainES")} */ + const es_subdomain = this.services.get('es:subdomain'); + + const row = (await es_subdomain.select({ predicate: new Eq({ key: "domain", value: domain }) }))[0] || (await es_subdomain.select({ predicate: new Eq({ key: "domain", value: "in-progress:" + domain }) }))[0]; + if (!row) { + throw new APIError('forbidden', null, {}); + } + + let inProgress = false; + if (row.values_.domain.startsWith("in-progress:")) { + inProgress = true; + } + + // Get token from Entri + const { auth_token } = await (fetch('https://api.goentri.com/token', { + method: 'POST', + body: JSON.stringify({ + applicationId: this.config.applicationId, + secret: this.config.secret, + }) + }).then(r => r.json())); + + const entity = await Entity.create({ om: es_subdomain.om }, { + uid: row.values_.uid, + domain: NULL + }); + await es_subdomain.upsert(entity); + const errors = [] + // Even if the domain is in progress, still send the delete incase it's just propgation taking a while + const deleteRequest = await (fetch('https://api.goentri.com/power', { + method: 'DELETE', + headers: { + applicationId: this.config.applicationId, + "Authorization": "Bearer " + auth_token + }, + body: JSON.stringify({ domain }) + })); + if (deleteRequest.status !== 200) { + errors.push(await deleteRequest.text()) + } + + return {ok: true, errors}; + }, async fullyRegistered({ domain, userHostedSite }) { const es_subdomain = this.services.get('es:subdomain'); @@ -184,6 +256,16 @@ class EntriService extends BaseService { } }, result: { type: 'json' } + }, + deleteMapping: { + description: 'delete domain mapping from entri', + parameters: { + domain: { + type: "string", + optional: false + } + }, + result: { type: 'json' } } } });