Compare commits

..

3 Commits

Author SHA1 Message Date
Owen 72d2c79793 Add migration for streaming table 2026-08-25 10:42:56 -04:00
Owen 23764feb4f Move the messaging out of the transaction 2026-08-25 09:27:02 -04:00
Owen Schwartz d2809fbfd1 Merge pull request #3637 from fosrl/fix/generic-oidc-icons
Fix: Use variant instead of type only for the Org login IDPs
2026-08-24 16:46:38 -04:00
3 changed files with 91 additions and 74 deletions
+85 -74
View File
@@ -1,5 +1,6 @@
import { import {
db, db,
primaryDb,
newts, newts,
blueprints, blueprints,
Blueprint, Blueprint,
@@ -80,93 +81,103 @@ export async function applyBlueprint({
trx, trx,
siteId siteId
); );
});
// We need to update the targets on the newts from the successfully updated information // Push updates to newts/clients only after the transaction has
for (const result of publicResourcesResults) { // committed. Doing this while the transaction is still open can
for (const target of result.targetsToUpdate) { // race with the writes (e.g. newts requesting config before the
const [site] = await trx // new targets/resources are actually visible), leaving them out
.select() // of sync until manually toggled.
.from(sites)
.innerJoin(newts, eq(sites.siteId, newts.siteId)) // We need to update the targets on the newts from the successfully updated information
.where( for (const result of publicResourcesResults) {
and( for (const target of result.targetsToUpdate) {
eq(sites.siteId, target.siteId), // read from the primary: this determines whether/how we push
eq(sites.orgId, orgId), // the just-created target to the newt, so a lagging replica
eq(sites.type, "newt"), // returning stale or missing data here would silently skip
isNotNull(sites.pubKey) // the push
) const [site] = await primaryDb
.select()
.from(sites)
.innerJoin(newts, eq(sites.siteId, newts.siteId))
.where(
and(
eq(sites.siteId, target.siteId),
eq(sites.orgId, orgId),
eq(sites.type, "newt"),
isNotNull(sites.pubKey)
) )
.limit(1); )
.limit(1);
if (site) { if (site) {
logger.debug( logger.debug(
`Updating target ${target.targetId} on site ${site.sites.siteId}` `Updating target ${target.targetId} on site ${site.sites.siteId}`
);
// see if you can find a matching target health check from the healthchecksToUpdate array
const matchingHealthcheck =
result.healthchecksToUpdate.find(
(hc) => hc.targetId === target.targetId
); );
// see if you can find a matching target health check from the healthchecksToUpdate array if (["http", "tcp", "udp"].includes(target.mode)) {
const matchingHealthcheck = await addProxyTargets(
result.healthchecksToUpdate.find( site.newt.newtId,
(hc) => hc.targetId === target.targetId [target],
); matchingHealthcheck
? [matchingHealthcheck]
if (["http", "tcp", "udp"].includes(target.mode)) { : [],
await addProxyTargets( result.proxyResource.mode === "udp"
site.newt.newtId, ? "udp"
[target], : "tcp",
matchingHealthcheck site.newt.version
? [matchingHealthcheck] );
: [], } else if (
result.proxyResource.mode === "udp" ["ssh", "rdp", "vnc"].includes(target.mode)
? "udp" ) {
: "tcp", await sendBrowserGatewayTargets(
site.newt.version site.newt.newtId,
); [target],
} else if ( site.newt.version
["ssh", "rdp", "vnc"].includes(target.mode) );
) {
await sendBrowserGatewayTargets(
site.newt.newtId,
[target],
site.newt.version
);
}
} }
} }
} }
}
logger.debug( logger.debug(
`Successfully updated public resources for org ${orgId}: ${JSON.stringify(publicResourcesResults)}` `Successfully updated public resources for org ${orgId}: ${JSON.stringify(publicResourcesResults)}`
); );
// We need to update the targets on the newts from the successfully updated information // We need to update the targets on the newts from the successfully updated information
for (const result of privateResourcesResults) { for (const result of privateResourcesResults) {
rebuildClientAssociationsFromSiteResource( rebuildClientAssociationsFromSiteResource(
result.newSiteResource result.newSiteResource
)
.then(() =>
waitForSiteResourceRebuildIdle(
result.newSiteResource.siteResourceId
)
) )
.then(() => .then(() =>
waitForSiteResourceRebuildIdle( handleMessagingForUpdatedSiteResource(
result.newSiteResource.siteResourceId result.oldSiteResource,
) result.newSiteResource,
result.oldSites.map((s) => s.siteId),
result.newSites.map((s) => s.siteId)
) )
.then(() => )
handleMessagingForUpdatedSiteResource( .catch((e) => {
result.oldSiteResource, logger.error(
result.newSiteResource, `Failed to rebuild and handle messaging for site resource ${result.newSiteResource.siteResourceId}. Error: ${e}`
result.oldSites.map((s) => s.siteId), );
result.newSites.map((s) => s.siteId) });
) }
)
.catch((e) => {
logger.error(
`Failed to rebuild and handle messaging for site resource ${result.newSiteResource.siteResourceId}. Error: ${e}`
);
});
}
logger.debug( logger.debug(
`Successfully updated private resources for org ${orgId}: ${JSON.stringify(privateResourcesResults)}` `Successfully updated private resources for org ${orgId}: ${JSON.stringify(privateResourcesResults)}`
); );
});
blueprintSucceeded = true; blueprintSucceeded = true;
blueprintMessage = "Blueprint applied successfully"; blueprintMessage = "Blueprint applied successfully";
+3
View File
@@ -345,6 +345,9 @@ export default async function migration() {
await db.execute( await db.execute(
sql`ALTER TABLE "virtualApiKeys" ADD CONSTRAINT "virtualApiKeys_createdByUserId_user_id_fk" FOREIGN KEY ("createdByUserId") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;` sql`ALTER TABLE "virtualApiKeys" ADD CONSTRAINT "virtualApiKeys_createdByUserId_user_id_fk" FOREIGN KEY ("createdByUserId") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;`
); );
await db.execute(
sql`ALTER TABLE "eventStreamingDestinations" ADD "sendAISessionLogs" boolean DEFAULT false NOT NULL;`
);
await db.execute( await db.execute(
sql`CREATE INDEX "idx_ai_budget_breach_events_budget_created" ON "aiBudgetBreachEvents" USING btree ("budgetId","createdAt");` sql`CREATE INDEX "idx_ai_budget_breach_events_budget_created" ON "aiBudgetBreachEvents" USING btree ("budgetId","createdAt");`
); );
+3
View File
@@ -402,6 +402,9 @@ export default async function migration() {
db.prepare( db.prepare(
`ALTER TABLE 'siteResources' ADD 'requiresExitNodeConnection' integer DEFAULT false NOT NULL;` `ALTER TABLE 'siteResources' ADD 'requiresExitNodeConnection' integer DEFAULT false NOT NULL;`
).run(); ).run();
db.prepare(
`ALTER TABLE 'eventStreamingDestinations' ADD 'sendAISessionLogs' integer DEFAULT false NOT NULL;`
).run();
const insertRoleAction = db.prepare(` const insertRoleAction = db.prepare(`
INSERT INTO 'roleActions' ("roleId", "actionId", "orgId") INSERT INTO 'roleActions' ("roleId", "actionId", "orgId")