mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-26 14:56:39 +00:00
Merge branch 'dev' into audit-logs
This commit is contained in:
@@ -26,8 +26,7 @@ export const orgs = pgTable("orgs", {
|
|||||||
orgId: varchar("orgId").primaryKey(),
|
orgId: varchar("orgId").primaryKey(),
|
||||||
name: varchar("name").notNull(),
|
name: varchar("name").notNull(),
|
||||||
subnet: varchar("subnet"),
|
subnet: varchar("subnet"),
|
||||||
createdAt: text("createdAt"),
|
createdAt: text("createdAt")
|
||||||
settings: text("settings") // JSON blob of org-specific settings
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orgDomains = pgTable("orgDomains", {
|
export const orgDomains = pgTable("orgDomains", {
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ export const orgs = sqliteTable("orgs", {
|
|||||||
orgId: text("orgId").primaryKey(),
|
orgId: text("orgId").primaryKey(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
subnet: text("subnet"),
|
subnet: text("subnet"),
|
||||||
createdAt: text("createdAt"),
|
createdAt: text("createdAt")
|
||||||
settings: text("settings") // JSON blob of org-specific settings
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userDomains = sqliteTable("userDomains", {
|
export const userDomains = sqliteTable("userDomains", {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
export enum AudienceIds {
|
export enum AudienceIds {
|
||||||
General = "",
|
SignUps = "",
|
||||||
Subscribed = "",
|
Subscribed = "",
|
||||||
Churned = ""
|
Churned = "",
|
||||||
|
Newsletter = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
let resend;
|
let resend;
|
||||||
@@ -12,4 +13,4 @@ export async function moveEmailToAudience(
|
|||||||
audienceId: AudienceIds
|
audienceId: AudienceIds
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,10 +200,7 @@ class TelemetryClient {
|
|||||||
event: "supporter_status",
|
event: "supporter_status",
|
||||||
properties: {
|
properties: {
|
||||||
valid: stats.supporterStatus.valid,
|
valid: stats.supporterStatus.valid,
|
||||||
tier: stats.supporterStatus.tier,
|
tier: stats.supporterStatus.tier
|
||||||
github_username: stats.supporterStatus.githubUsername
|
|
||||||
? this.anon(stats.supporterStatus.githubUsername)
|
|
||||||
: "None"
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -217,21 +214,6 @@ class TelemetryClient {
|
|||||||
install_timestamp: hostMeta.createdAt
|
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() {
|
private async collectAndSendAnalytics() {
|
||||||
@@ -262,19 +244,38 @@ class TelemetryClient {
|
|||||||
num_clients: stats.numClients,
|
num_clients: stats.numClients,
|
||||||
num_identity_providers: stats.numIdentityProviders,
|
num_identity_providers: stats.numIdentityProviders,
|
||||||
num_sites_online: stats.numSitesOnline,
|
num_sites_online: stats.numSitesOnline,
|
||||||
resources: stats.resources.map((r) => ({
|
num_resources_sso_enabled: stats.resources.filter(
|
||||||
name: this.anon(r.name),
|
(r) => r.sso
|
||||||
sso_enabled: r.sso,
|
).length,
|
||||||
protocol: r.protocol,
|
num_resources_non_http: stats.resources.filter(
|
||||||
http_enabled: r.http
|
(r) => !r.http
|
||||||
})),
|
).length,
|
||||||
sites: stats.sites.map((s) => ({
|
num_newt_sites: stats.sites.filter((s) => s.type === "newt")
|
||||||
site_name: this.anon(s.siteName),
|
.length,
|
||||||
megabytes_in: s.megabytesIn,
|
num_local_sites: stats.sites.filter(
|
||||||
megabytes_out: s.megabytesOut,
|
(s) => s.type === "local"
|
||||||
type: s.type,
|
).length,
|
||||||
online: s.online
|
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_api_keys: stats.numApiKeys,
|
||||||
num_custom_roles: stats.numCustomRoles
|
num_custom_roles: stats.numCustomRoles
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ import privateConfig from "#private/lib/config";
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|
||||||
export enum AudienceIds {
|
export enum AudienceIds {
|
||||||
General = "5cfbf99b-c592-40a9-9b8a-577a4681c158",
|
SignUps = "5cfbf99b-c592-40a9-9b8a-577a4681c158",
|
||||||
Subscribed = "870b43fd-387f-44de-8fc1-707335f30b20",
|
Subscribed = "870b43fd-387f-44de-8fc1-707335f30b20",
|
||||||
Churned = "f3ae92bd-2fdb-4d77-8746-2118afd62549"
|
Churned = "f3ae92bd-2fdb-4d77-8746-2118afd62549",
|
||||||
|
Newsletter = "5500c431-191c-42f0-a5d4-8b6d445b4ea0"
|
||||||
}
|
}
|
||||||
|
|
||||||
const resend = new Resend(
|
const resend = new Resend(
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ export async function signup(
|
|||||||
res.appendHeader("Set-Cookie", cookie);
|
res.appendHeader("Set-Cookie", cookie);
|
||||||
|
|
||||||
if (build == "saas") {
|
if (build == "saas") {
|
||||||
moveEmailToAudience(email, AudienceIds.General);
|
moveEmailToAudience(email, AudienceIds.SignUps);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.getRawConfig().flags?.require_email_verification) {
|
if (config.getRawConfig().flags?.require_email_verification) {
|
||||||
|
|||||||
@@ -443,14 +443,14 @@ authenticated.post(
|
|||||||
resource.setResourceWhitelist,
|
resource.setResourceWhitelist,
|
||||||
);
|
);
|
||||||
|
|
||||||
authenticated.get(
|
authenticated.post(
|
||||||
`/resource/:resourceId/whitelist/add`,
|
`/resource/:resourceId/whitelist/add`,
|
||||||
verifyApiKeyResourceAccess,
|
verifyApiKeyResourceAccess,
|
||||||
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
|
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
|
||||||
resource.addEmailToResourceWhitelist
|
resource.addEmailToResourceWhitelist
|
||||||
);
|
);
|
||||||
|
|
||||||
authenticated.get(
|
authenticated.post(
|
||||||
`/resource/:resourceId/whitelist/remove`,
|
`/resource/:resourceId/whitelist/remove`,
|
||||||
verifyApiKeyResourceAccess,
|
verifyApiKeyResourceAccess,
|
||||||
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
|
verifyApiKeyHasAction(ActionsEnum.setResourceWhitelist),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const getOrgSchema = z
|
|||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
export type GetOrgResponse = {
|
export type GetOrgResponse = {
|
||||||
org: Org & { settings: { } | null };
|
org: Org;
|
||||||
};
|
};
|
||||||
|
|
||||||
registry.registerPath({
|
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, {
|
return response<GetOrgResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
org: {
|
org: org[0]
|
||||||
...org[0],
|
|
||||||
settings: parsedSettings
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
|||||||
@@ -12,15 +12,13 @@ import { OpenAPITags, registry } from "@server/openApi";
|
|||||||
|
|
||||||
const updateOrgParamsSchema = z
|
const updateOrgParamsSchema = z
|
||||||
.object({
|
.object({
|
||||||
orgId: z.string(),
|
orgId: z.string()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
const updateOrgBodySchema = z
|
const updateOrgBodySchema = z
|
||||||
.object({
|
.object({
|
||||||
name: z.string().min(1).max(255).optional(),
|
name: z.string().min(1).max(255).optional()
|
||||||
settings: z.object({
|
|
||||||
}).optional(),
|
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
.refine((data) => Object.keys(data).length > 0, {
|
.refine((data) => Object.keys(data).length > 0, {
|
||||||
@@ -73,13 +71,10 @@ export async function updateOrg(
|
|||||||
|
|
||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
|
|
||||||
const settings = parsedBody.data.settings ? JSON.stringify(parsedBody.data.settings) : undefined;
|
|
||||||
|
|
||||||
const updatedOrg = await db
|
const updatedOrg = await db
|
||||||
.update(orgs)
|
.update(orgs)
|
||||||
.set({
|
.set({
|
||||||
name: parsedBody.data.name,
|
name: parsedBody.data.name
|
||||||
settings: settings
|
|
||||||
})
|
})
|
||||||
.where(eq(orgs.orgId, orgId))
|
.where(eq(orgs.orgId, orgId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|||||||
Reference in New Issue
Block a user