From aac6ff14acb27e72d705077c53673885127cf0bc Mon Sep 17 00:00:00 2001 From: ProgrammerIn-wonderland <3838shah@gmail.com> Date: Thu, 14 Aug 2025 18:08:40 -0400 Subject: [PATCH] custom domains webhook --- .../om/entitystorage/WriteByOwnerOnlyES.js | 9 ++ src/backend/src/services/EntriService.js | 113 +++++++++++++----- 2 files changed, 95 insertions(+), 27 deletions(-) diff --git a/src/backend/src/om/entitystorage/WriteByOwnerOnlyES.js b/src/backend/src/om/entitystorage/WriteByOwnerOnlyES.js index dc5494cc..5ae0269f 100644 --- a/src/backend/src/om/entitystorage/WriteByOwnerOnlyES.js +++ b/src/backend/src/om/entitystorage/WriteByOwnerOnlyES.js @@ -20,6 +20,8 @@ const APIError = require("../../api/APIError"); const { Context } = require("../../util/context"); const { BaseES } = require("./BaseES"); +const WRITE_ALL_OWNER_ES = 'system:es:write-all-owners'; + class WriteByOwnerOnlyES extends BaseES { static METHODS = { async upsert (entity, extra) { @@ -41,11 +43,18 @@ class WriteByOwnerOnlyES extends BaseES { }, async _check_allowed ({ old_entity }) { + const svc_permission = this.context.get('services').get('permission'); + const has_permission_to_write_all = await svc_permission.check(Context.get("actor"), WRITE_ALL_OWNER_ES); + if (has_permission_to_write_all) { + return; + } + const owner = await old_entity.get('owner'); if ( ! owner ) { throw APIError.create('forbidden'); } const user = Context.get('user'); + if ( user.id !== owner.id ) { throw APIError.create('forbidden'); } diff --git a/src/backend/src/services/EntriService.js b/src/backend/src/services/EntriService.js index 7656e1c8..9e4e40cb 100644 --- a/src/backend/src/services/EntriService.js +++ b/src/backend/src/services/EntriService.js @@ -22,7 +22,11 @@ const fs = require("node:fs"); const { Entity } = require("../om/entitystorage/Entity");; // const { get_app, subdomain } = require("../helpers"); -const { parseDomain } = require("parse-domain") +const { parseDomain } = require("parse-domain"); +const { Eq } = require("../om/query/query"); +const { Endpoint } = require("../util/expressutil"); +const { IncomingMessage } = require("node:http"); +const { Context } = require("../util/context"); // async function generateJWT(applicationId, secret, domain, ) { @@ -33,47 +37,96 @@ class EntriService extends BaseService { _init() { } + + ['__on_install.routes'](_, { app }) { + Endpoint({ + route: '/entri/webhook', + methods: ['POST', "GET"], + /** + * + * @param {IncomingMessage} req + * @param {*} res + */ + handler: async (req, res) => { + if (!req.body.data.records_propagated) { + console.log("Failed to set domain records") + return; + } + let rootDomain = false; + if (req.body.data.records_propagated[0].type === "A") { + rootDomain = true; + } + + let realDomain = (rootDomain ? "" : (req.body.subdomain + ".")) + req.body.domain; + const svc_su = this.services.get("su"); + + const es_subdomain = this.services.get('es:subdomain'); + + await svc_su.sudo(async () => { + const rows = (await es_subdomain.select({ predicate: new Eq({ key: "domain", value: "in-progress:" + realDomain }) })); + for (const row of rows) { + const entity = await Entity.create({ om: es_subdomain.om }, { + uid: row.values_.uid, + domain: realDomain + }); + await es_subdomain.upsert(entity); + + } + return true; + }); + + + + res.end("ok") + }, + }).attach(app); + + const svc_web = this.services.get('web-server'); + svc_web.allow_undefined_origin('/entri/webhook', '/entri/webhook'); + } + static IMPLEMENTS = { ['entri']: { - async getJWT ({ domain, userHostedSite}) { + async getConfig({ domain, userHostedSite }) { const es_subdomain = this.services.get('es:subdomain'); let rootDomain = (parseDomain(domain)).icann.subDomains.length === 0; + + const dnsRecords = rootDomain ? [{ + type: "A", + host: "@", + value: "{ENTRI_SERVERS}", //This will be automatically replaced for the Entri servers IPs + ttl: 300, + applicationUrl: userHostedSite, + }] : [{ + type: "CNAME", + value: "power.goentri.com", // `{CNAME_TARGET}` will NOT automatically use the CNAME target as implied by the documentation + host: "{SUBDOMAIN}", // This will use the user inputted subdomain. If hostRequired is set to true, then this will default to "www" + ttl: 300, + applicationUrl: userHostedSite + }]; + const response = await fetch('https://api.goentri.com/token', { method: 'POST', body: JSON.stringify({ applicationId: this.config.applicationId, secret: this.config.secret, domain, - dnsRecords: rootDomain ? [ - { - type: "A", - host: "@", - value: "{ENTRI_SERVERS}", //This will be automatically replaced for the Entri servers IPs - ttl: 300, - applicationUrl: userHostedSite, - } - ] : [ - { - type: "CNAME", - value: "{CNAME_TARGET}", // `{CNAME_TARGET}` will automatically use the CNAME target entered in the dashboard - host: "{SUBDOMAIN}", // This will use the user inputted subdomain. If hostRequired is set to true, then this will default to "www" - ttl: 300, - applicationUrl: userHostedSite - } - ] - }), + // dnsRecords + }) }); + + const row = (await es_subdomain.select({ predicate: new Eq({ key: "subdomain", value: userHostedSite.replace(".puter.site", "") }) }))[0]; const entity = await Entity.create({ om: es_subdomain.om }, { - subdomain: userHostedSite.replace(".puter.site", ""), + uid: row.values_.uid, domain: "in-progress:" + domain }); await es_subdomain.upsert(entity); - return { auth_token: (await response.json()).auth_token, rootDomain} + return { token: (await response.json()).auth_token, applicationId: this.config.applicationId, power: true, dnsRecords, prefilledDomain: domain } + - // let rootDomain = (parseDomain(domain)).icann.subDomains.length === 0; - + // const response = await fetch('https://api.goentri.com/power?' + new URLSearchParams({ // domain, // rootDomain @@ -90,10 +143,16 @@ class EntriService extends BaseService { // if (!data.eligible) { // throw new APIError(); // figure this out later // } - - + + }, + async fullyRegistered({ domain, userHostedSite }) { + const es_subdomain = this.services.get('es:subdomain'); + const row = (await es_subdomain.select({ predicate: new Eq({ key: "subdomain", value: userHostedSite.replace(".puter.site", "") }) }))[0]; + + + } } } async ['__on_driver.register.interfaces']() { @@ -103,7 +162,7 @@ class EntriService extends BaseService { col_interfaces.set('entri', { description: 'Execute code with various languages.', methods: { - getJWT: { + getConfig: { description: 'get JWT for entri', parameters: { domain: {