mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 12:32:34 +10:00
Compare commits
6 Commits
v1.7.2-rc.
...
feat/telem
| Author | SHA1 | Date | |
|---|---|---|---|
| e79ba8aa68 | |||
| 8b344f5c96 | |||
| cb981bffb7 | |||
| bafaf3b95e | |||
| 0dfc41f423 | |||
| c1cc242527 |
@ -116,3 +116,6 @@ E2E_TEST_AUTHENTICATE_USER_PASSWORD="test_Password123"
|
|||||||
# [[REDIS]]
|
# [[REDIS]]
|
||||||
NEXT_PRIVATE_REDIS_URL=
|
NEXT_PRIVATE_REDIS_URL=
|
||||||
NEXT_PRIVATE_REDIS_TOKEN=
|
NEXT_PRIVATE_REDIS_TOKEN=
|
||||||
|
|
||||||
|
# [[Telemetry]]
|
||||||
|
DISABLE_TELEMETRY=false
|
||||||
|
|||||||
@ -28,6 +28,7 @@ const config = {
|
|||||||
experimental: {
|
experimental: {
|
||||||
outputFileTracingRoot: path.join(__dirname, '../../'),
|
outputFileTracingRoot: path.join(__dirname, '../../'),
|
||||||
serverComponentsExternalPackages: ['@node-rs/bcrypt', '@documenso/pdf-sign', 'playwright'],
|
serverComponentsExternalPackages: ['@node-rs/bcrypt', '@documenso/pdf-sign', 'playwright'],
|
||||||
|
instrumentationHook: true,
|
||||||
serverActions: {
|
serverActions: {
|
||||||
bodySizeLimit: '50mb',
|
bodySizeLimit: '50mb',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import { getSiteSettings } from '@documenso/lib/server-only/site-settings/get-site-settings';
|
import { getSiteSettings } from '@documenso/lib/server-only/site-settings/get-site-settings';
|
||||||
import { SITE_SETTINGS_BANNER_ID } from '@documenso/lib/server-only/site-settings/schemas/banner';
|
import {
|
||||||
|
SITE_SETTINGS_BANNER_ID,
|
||||||
|
ZSiteSettingsBannerSchema,
|
||||||
|
} from '@documenso/lib/server-only/site-settings/schemas/banner';
|
||||||
|
|
||||||
import { SettingsHeader } from '~/components/(dashboard)/settings/layout/header';
|
import { SettingsHeader } from '~/components/(dashboard)/settings/layout/header';
|
||||||
|
|
||||||
import { BannerForm } from './banner-form';
|
import { BannerForm } from './banner-form';
|
||||||
|
|
||||||
// import { BannerForm } from './banner-form';
|
|
||||||
|
|
||||||
export default async function AdminBannerPage() {
|
export default async function AdminBannerPage() {
|
||||||
const banner = await getSiteSettings().then((settings) =>
|
const banner = await getSiteSettings().then((settings) =>
|
||||||
settings.find((setting) => setting.id === SITE_SETTINGS_BANNER_ID),
|
settings.find((setting) => setting.id === SITE_SETTINGS_BANNER_ID),
|
||||||
@ -17,7 +18,7 @@ export default async function AdminBannerPage() {
|
|||||||
<SettingsHeader title="Site Settings" subtitle="Manage your site settings here" />
|
<SettingsHeader title="Site Settings" subtitle="Manage your site settings here" />
|
||||||
|
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
<BannerForm banner={banner} />
|
<BannerForm banner={ZSiteSettingsBannerSchema.parse(banner)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { getSiteSettings } from '@documenso/lib/server-only/site-settings/get-site-settings';
|
import { getSiteSettings } from '@documenso/lib/server-only/site-settings/get-site-settings';
|
||||||
|
import type { TSiteSettingSchema } from '@documenso/lib/server-only/site-settings/schema';
|
||||||
import { SITE_SETTINGS_BANNER_ID } from '@documenso/lib/server-only/site-settings/schemas/banner';
|
import { SITE_SETTINGS_BANNER_ID } from '@documenso/lib/server-only/site-settings/schemas/banner';
|
||||||
|
|
||||||
export const Banner = async () => {
|
export const Banner = async () => {
|
||||||
const banner = await getSiteSettings().then((settings) =>
|
const banner = (await getSiteSettings().then((settings) =>
|
||||||
settings.find((setting) => setting.id === SITE_SETTINGS_BANNER_ID),
|
settings.find((setting) => setting.id === SITE_SETTINGS_BANNER_ID),
|
||||||
);
|
)) as Extract<TSiteSettingSchema, { id: 'site.banner' }>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -23,7 +24,3 @@ export const Banner = async () => {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Banner
|
|
||||||
// Custom Text
|
|
||||||
// Custom Text with Custom Icon
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { WebhookTriggerEvents } from '@prisma/client/';
|
import { WebhookTriggerEvents } from '@prisma/client';
|
||||||
import { Check, ChevronsUpDown } from 'lucide-react';
|
import { Check, ChevronsUpDown } from 'lucide-react';
|
||||||
|
|
||||||
import { toFriendlyWebhookEventName } from '@documenso/lib/universal/webhook/to-friendly-webhook-event-name';
|
import { toFriendlyWebhookEventName } from '@documenso/lib/universal/webhook/to-friendly-webhook-event-name';
|
||||||
|
|||||||
9
apps/web/src/instrumentation.ts
Normal file
9
apps/web/src/instrumentation.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { registerInstance } from '@documenso/lib/server-only/telemetry/register-instance';
|
||||||
|
|
||||||
|
import packageInfo from '../package.json';
|
||||||
|
|
||||||
|
export const register = async () => {
|
||||||
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
||||||
|
await registerInstance({ version: packageInfo.version });
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,9 +1,12 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { ZSiteSettingsBannerSchema } from './schemas/banner';
|
import { ZSiteSettingsBannerSchema } from './schemas/banner';
|
||||||
|
import { ZSiteSettingsTelemetrySchema } from './schemas/telemetry';
|
||||||
|
|
||||||
// TODO: Use `z.union([...])` once we have more than one setting
|
export const ZSiteSettingSchema = z.union([
|
||||||
export const ZSiteSettingSchema = ZSiteSettingsBannerSchema;
|
ZSiteSettingsBannerSchema,
|
||||||
|
ZSiteSettingsTelemetrySchema,
|
||||||
|
]);
|
||||||
|
|
||||||
export type TSiteSettingSchema = z.infer<typeof ZSiteSettingSchema>;
|
export type TSiteSettingSchema = z.infer<typeof ZSiteSettingSchema>;
|
||||||
|
|
||||||
|
|||||||
20
packages/lib/server-only/site-settings/schemas/telemetry.ts
Normal file
20
packages/lib/server-only/site-settings/schemas/telemetry.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { ZSiteSettingsBaseSchema } from './_base';
|
||||||
|
|
||||||
|
export const SITE_SETTINGS_TELEMETRY_ID = 'site.instance-id';
|
||||||
|
|
||||||
|
export const ZSiteSettingsTelemetrySchema = ZSiteSettingsBaseSchema.extend({
|
||||||
|
id: z.literal(SITE_SETTINGS_TELEMETRY_ID),
|
||||||
|
data: z
|
||||||
|
.object({
|
||||||
|
instanceId: z.string(),
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.default({
|
||||||
|
instanceId: nanoid(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TSiteSettingsTelemetrySchema = z.infer<typeof ZSiteSettingsTelemetrySchema>;
|
||||||
@ -3,7 +3,7 @@ import { prisma } from '@documenso/prisma';
|
|||||||
import type { TSiteSettingSchema } from './schema';
|
import type { TSiteSettingSchema } from './schema';
|
||||||
|
|
||||||
export type UpsertSiteSettingOptions = TSiteSettingSchema & {
|
export type UpsertSiteSettingOptions = TSiteSettingSchema & {
|
||||||
userId: number;
|
userId: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const upsertSiteSetting = async ({
|
export const upsertSiteSetting = async ({
|
||||||
|
|||||||
46
packages/lib/server-only/telemetry/register-instance.ts
Normal file
46
packages/lib/server-only/telemetry/register-instance.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
|
import { getSiteSettings } from '../site-settings/get-site-settings';
|
||||||
|
import {
|
||||||
|
SITE_SETTINGS_TELEMETRY_ID,
|
||||||
|
ZSiteSettingsTelemetrySchema,
|
||||||
|
} from '../site-settings/schemas/telemetry';
|
||||||
|
import { upsertSiteSetting } from '../site-settings/upsert-site-setting';
|
||||||
|
import { sendInstance } from './send-instance';
|
||||||
|
|
||||||
|
type RegisterInstanceOptions = {
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerInstance = async ({ version }: RegisterInstanceOptions) => {
|
||||||
|
const instanceResponse = await getSiteSettings().then((settings) =>
|
||||||
|
settings.find((setting) => setting.id === SITE_SETTINGS_TELEMETRY_ID),
|
||||||
|
);
|
||||||
|
|
||||||
|
const instance = ZSiteSettingsTelemetrySchema.parse(instanceResponse);
|
||||||
|
|
||||||
|
if (!instance) {
|
||||||
|
const upsert = await upsertSiteSetting({
|
||||||
|
data: {
|
||||||
|
instanceId: nanoid(),
|
||||||
|
},
|
||||||
|
enabled: true,
|
||||||
|
id: SITE_SETTINGS_TELEMETRY_ID,
|
||||||
|
userId: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
const instance = ZSiteSettingsTelemetrySchema.parse(upsert);
|
||||||
|
|
||||||
|
return await sendInstance({
|
||||||
|
uniqueId: instance.data?.instanceId,
|
||||||
|
timestamp: new Date(),
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return await sendInstance({
|
||||||
|
uniqueId: instance.data.instanceId,
|
||||||
|
timestamp: new Date(),
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
};
|
||||||
38
packages/lib/server-only/telemetry/send-instance.ts
Normal file
38
packages/lib/server-only/telemetry/send-instance.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
export type SendInstance = {
|
||||||
|
uniqueId: string;
|
||||||
|
timestamp: Date;
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sendInstance = async ({ uniqueId, timestamp, version }: SendInstance) => {
|
||||||
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
const isTelemetryDisabled = process.env.DISABLE_TELEMETRY === 'true';
|
||||||
|
|
||||||
|
if (!isProduction || isTelemetryDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = 'https://documenso-instances.fly.dev/ping';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
uniqueId: String(uniqueId),
|
||||||
|
timestamp: new Date(timestamp).toISOString(),
|
||||||
|
version,
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to record instance, failed with status code ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { Role } from '@prisma/client';
|
import { Role } from '@prisma/client';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
|
|
||||||
import { ZSiteSettingSchema } from '@documenso/lib/server-only/site-settings/schema';
|
import { ZSiteSettingsBannerSchema } from '@documenso/lib/server-only/site-settings/schemas/banner';
|
||||||
|
|
||||||
export const ZAdminFindDocumentsQuerySchema = z.object({
|
export const ZAdminFindDocumentsQuerySchema = z.object({
|
||||||
term: z.string().optional(),
|
term: z.string().optional(),
|
||||||
@ -30,7 +30,7 @@ export type TAdminUpdateRecipientMutationSchema = z.infer<
|
|||||||
typeof ZAdminUpdateRecipientMutationSchema
|
typeof ZAdminUpdateRecipientMutationSchema
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const ZAdminUpdateSiteSettingMutationSchema = ZSiteSettingSchema;
|
export const ZAdminUpdateSiteSettingMutationSchema = ZSiteSettingsBannerSchema;
|
||||||
|
|
||||||
export type TAdminUpdateSiteSettingMutationSchema = z.infer<
|
export type TAdminUpdateSiteSettingMutationSchema = z.infer<
|
||||||
typeof ZAdminUpdateSiteSettingMutationSchema
|
typeof ZAdminUpdateSiteSettingMutationSchema
|
||||||
|
|||||||
3
packages/tsconfig/process-env.d.ts
vendored
3
packages/tsconfig/process-env.d.ts
vendored
@ -70,6 +70,7 @@ declare namespace NodeJS {
|
|||||||
VERCEL?: string;
|
VERCEL?: string;
|
||||||
VERCEL_ENV?: 'production' | 'development' | 'preview';
|
VERCEL_ENV?: 'production' | 'development' | 'preview';
|
||||||
VERCEL_URL?: string;
|
VERCEL_URL?: string;
|
||||||
|
NEXT_RUNTIME?: 'nodejs' | 'edge';
|
||||||
|
|
||||||
DEPLOYMENT_TARGET?: 'webapp' | 'marketing';
|
DEPLOYMENT_TARGET?: 'webapp' | 'marketing';
|
||||||
FONT_CAVEAT_URI: string;
|
FONT_CAVEAT_URI: string;
|
||||||
@ -79,5 +80,7 @@ declare namespace NodeJS {
|
|||||||
DATABASE_URL?: string;
|
DATABASE_URL?: string;
|
||||||
POSTGRES_PRISMA_URL?: string;
|
POSTGRES_PRISMA_URL?: string;
|
||||||
POSTGRES_URL_NON_POOLING?: string;
|
POSTGRES_URL_NON_POOLING?: string;
|
||||||
|
|
||||||
|
DISABLE_TELEMETRY?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
turbo.json
27
turbo.json
@ -2,13 +2,8 @@
|
|||||||
"$schema": "https://turbo.build/schema.json",
|
"$schema": "https://turbo.build/schema.json",
|
||||||
"pipeline": {
|
"pipeline": {
|
||||||
"build": {
|
"build": {
|
||||||
"dependsOn": [
|
"dependsOn": ["^build"],
|
||||||
"^build"
|
"outputs": [".next/**", "!.next/cache/**"]
|
||||||
],
|
|
||||||
"outputs": [
|
|
||||||
".next/**",
|
|
||||||
"!.next/cache/**"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"lint": {
|
"lint": {
|
||||||
"cache": false
|
"cache": false
|
||||||
@ -24,9 +19,7 @@
|
|||||||
"persistent": true
|
"persistent": true
|
||||||
},
|
},
|
||||||
"start": {
|
"start": {
|
||||||
"dependsOn": [
|
"dependsOn": ["^build"],
|
||||||
"^build"
|
|
||||||
],
|
|
||||||
"cache": false,
|
"cache": false,
|
||||||
"persistent": true
|
"persistent": true
|
||||||
},
|
},
|
||||||
@ -34,15 +27,11 @@
|
|||||||
"cache": false
|
"cache": false
|
||||||
},
|
},
|
||||||
"test:e2e": {
|
"test:e2e": {
|
||||||
"dependsOn": [
|
"dependsOn": ["^build"],
|
||||||
"^build"
|
|
||||||
],
|
|
||||||
"cache": false
|
"cache": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"globalDependencies": [
|
"globalDependencies": ["**/.env.*local"],
|
||||||
"**/.env.*local"
|
|
||||||
],
|
|
||||||
"globalEnv": [
|
"globalEnv": [
|
||||||
"APP_VERSION",
|
"APP_VERSION",
|
||||||
"NEXT_PRIVATE_ENCRYPTION_KEY",
|
"NEXT_PRIVATE_ENCRYPTION_KEY",
|
||||||
@ -121,6 +110,8 @@
|
|||||||
"GOOGLE_APPLICATION_CREDENTIALS",
|
"GOOGLE_APPLICATION_CREDENTIALS",
|
||||||
"E2E_TEST_AUTHENTICATE_USERNAME",
|
"E2E_TEST_AUTHENTICATE_USERNAME",
|
||||||
"E2E_TEST_AUTHENTICATE_USER_EMAIL",
|
"E2E_TEST_AUTHENTICATE_USER_EMAIL",
|
||||||
"E2E_TEST_AUTHENTICATE_USER_PASSWORD"
|
"E2E_TEST_AUTHENTICATE_USER_PASSWORD",
|
||||||
|
"DISABLE_TELEMETRY",
|
||||||
|
"NEXT_RUNTIME"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user