Merge branch 'dev' into audit-logs

This commit is contained in:
Owen
2025-10-24 10:31:54 -07:00
9 changed files with 52 additions and 70 deletions

View File

@@ -26,8 +26,7 @@ export const orgs = pgTable("orgs", {
orgId: varchar("orgId").primaryKey(),
name: varchar("name").notNull(),
subnet: varchar("subnet"),
createdAt: text("createdAt"),
settings: text("settings") // JSON blob of org-specific settings
createdAt: text("createdAt")
});
export const orgDomains = pgTable("orgDomains", {

View File

@@ -19,8 +19,7 @@ export const orgs = sqliteTable("orgs", {
orgId: text("orgId").primaryKey(),
name: text("name").notNull(),
subnet: text("subnet"),
createdAt: text("createdAt"),
settings: text("settings") // JSON blob of org-specific settings
createdAt: text("createdAt")
});
export const userDomains = sqliteTable("userDomains", {

View File

@@ -1,7 +1,8 @@
export enum AudienceIds {
General = "",
Subscribed = "",
Churned = ""
SignUps = "",
Subscribed = "",
Churned = "",
Newsletter = ""
}
let resend;
@@ -12,4 +13,4 @@ export async function moveEmailToAudience(
audienceId: AudienceIds
) {
return;
}
}

View File

@@ -200,10 +200,7 @@ class TelemetryClient {
event: "supporter_status",
properties: {
valid: stats.supporterStatus.valid,
tier: stats.supporterStatus.tier,
github_username: stats.supporterStatus.githubUsername
? this.anon(stats.supporterStatus.githubUsername)
: "None"
tier: stats.supporterStatus.tier
}
});
}
@@ -217,21 +214,6 @@ class TelemetryClient {
install_timestamp: hostMeta.createdAt
}
});
for (const email of stats.adminUsers) {
// There should only be on admin user, but just in case
if (email) {
this.client.capture({
distinctId: this.anon(email),
event: "admin_user",
properties: {
host_id: hostMeta.hostMetaId,
app_version: stats.appVersion,
hashed_email: this.anon(email)
}
});
}
}
}
private async collectAndSendAnalytics() {
@@ -262,19 +244,38 @@ class TelemetryClient {
num_clients: stats.numClients,
num_identity_providers: stats.numIdentityProviders,
num_sites_online: stats.numSitesOnline,
resources: stats.resources.map((r) => ({
name: this.anon(r.name),
sso_enabled: r.sso,
protocol: r.protocol,
http_enabled: r.http
})),
sites: stats.sites.map((s) => ({
site_name: this.anon(s.siteName),
megabytes_in: s.megabytesIn,
megabytes_out: s.megabytesOut,
type: s.type,
online: s.online
})),
num_resources_sso_enabled: stats.resources.filter(
(r) => r.sso
).length,
num_resources_non_http: stats.resources.filter(
(r) => !r.http
).length,
num_newt_sites: stats.sites.filter((s) => s.type === "newt")
.length,
num_local_sites: stats.sites.filter(
(s) => s.type === "local"
).length,
num_wg_sites: stats.sites.filter(
(s) => s.type === "wireguard"
).length,
avg_megabytes_in:
stats.sites.length > 0
? Math.round(
stats.sites.reduce(
(sum, s) => sum + (s.megabytesIn ?? 0),
0
) / stats.sites.length
)
: 0,
avg_megabytes_out:
stats.sites.length > 0
? Math.round(
stats.sites.reduce(
(sum, s) => sum + (s.megabytesOut ?? 0),
0
) / stats.sites.length
)
: 0,
num_api_keys: stats.numApiKeys,
num_custom_roles: stats.numCustomRoles
}

View File

@@ -16,9 +16,10 @@ import privateConfig from "#private/lib/config";
import logger from "@server/logger";
export enum AudienceIds {
General = "5cfbf99b-c592-40a9-9b8a-577a4681c158",
Subscribed = "870b43fd-387f-44de-8fc1-707335f30b20",
Churned = "f3ae92bd-2fdb-4d77-8746-2118afd62549"
SignUps = "5cfbf99b-c592-40a9-9b8a-577a4681c158",
Subscribed = "870b43fd-387f-44de-8fc1-707335f30b20",
Churned = "f3ae92bd-2fdb-4d77-8746-2118afd62549",
Newsletter = "5500c431-191c-42f0-a5d4-8b6d445b4ea0"
}
const resend = new Resend(

View File

@@ -224,7 +224,7 @@ export async function signup(
res.appendHeader("Set-Cookie", cookie);
if (build == "saas") {
moveEmailToAudience(email, AudienceIds.General);
moveEmailToAudience(email, AudienceIds.SignUps);
}
if (config.getRawConfig().flags?.require_email_verification) {

View File

@@ -443,14 +443,14 @@ authenticated.post(
resource.setResourceWhitelist,
);
authenticated.get(
authenticated.post(
`/resource/:resourceId/whitelist/add`,
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
resource.addEmailToResourceWhitelist
);
authenticated.get(
authenticated.post(
`/resource/:resourceId/whitelist/remove`,
verifyApiKeyResourceAccess,
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),

View File

@@ -17,7 +17,7 @@ const getOrgSchema = z
.strict();
export type GetOrgResponse = {
org: Org & { settings: { } | null };
org: Org;
};
registry.registerPath({
@@ -64,23 +64,9 @@ export async function getOrg(
);
}
// Parse settings from JSON string back to object
let parsedSettings = null;
if (org[0].settings) {
try {
parsedSettings = JSON.parse(org[0].settings);
} catch (error) {
// If parsing fails, keep as string for backward compatibility
parsedSettings = org[0].settings;
}
}
return response<GetOrgResponse>(res, {
data: {
org: {
...org[0],
settings: parsedSettings
}
org: org[0]
},
success: true,
error: false,

View File

@@ -12,15 +12,13 @@ import { OpenAPITags, registry } from "@server/openApi";
const updateOrgParamsSchema = z
.object({
orgId: z.string(),
orgId: z.string()
})
.strict();
const updateOrgBodySchema = z
.object({
name: z.string().min(1).max(255).optional(),
settings: z.object({
}).optional(),
name: z.string().min(1).max(255).optional()
})
.strict()
.refine((data) => Object.keys(data).length > 0, {
@@ -73,13 +71,10 @@ export async function updateOrg(
const { orgId } = parsedParams.data;
const settings = parsedBody.data.settings ? JSON.stringify(parsedBody.data.settings) : undefined;
const updatedOrg = await db
.update(orgs)
.set({
name: parsedBody.data.name,
settings: settings
name: parsedBody.data.name
})
.where(eq(orgs.orgId, orgId))
.returning();