Compare commits

...

6 Commits

14 changed files with 146 additions and 34 deletions

View File

@ -116,3 +116,6 @@ E2E_TEST_AUTHENTICATE_USER_PASSWORD="test_Password123"
# [[REDIS]]
NEXT_PRIVATE_REDIS_URL=
NEXT_PRIVATE_REDIS_TOKEN=
# [[Telemetry]]
DISABLE_TELEMETRY=false

View File

@ -28,6 +28,7 @@ const config = {
experimental: {
outputFileTracingRoot: path.join(__dirname, '../../'),
serverComponentsExternalPackages: ['@node-rs/bcrypt', '@documenso/pdf-sign', 'playwright'],
instrumentationHook: true,
serverActions: {
bodySizeLimit: '50mb',
},

View File

@ -1,12 +1,13 @@
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 { BannerForm } from './banner-form';
// import { BannerForm } from './banner-form';
export default async function AdminBannerPage() {
const banner = await getSiteSettings().then((settings) =>
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" />
<div className="mt-8">
<BannerForm banner={banner} />
<BannerForm banner={ZSiteSettingsBannerSchema.parse(banner)} />
</div>
</div>
);

View File

@ -1,10 +1,11 @@
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';
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),
);
)) as Extract<TSiteSettingSchema, { id: 'site.banner' }>;
return (
<>
@ -23,7 +24,3 @@ export const Banner = async () => {
</>
);
};
// Banner
// Custom Text
// Custom Text with Custom Icon

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { WebhookTriggerEvents } from '@prisma/client/';
import { WebhookTriggerEvents } from '@prisma/client';
import { Check, ChevronsUpDown } from 'lucide-react';
import { toFriendlyWebhookEventName } from '@documenso/lib/universal/webhook/to-friendly-webhook-event-name';

View 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 });
}
};

View File

@ -1,9 +1,12 @@
import { z } from 'zod';
import { ZSiteSettingsBannerSchema } from './schemas/banner';
import { ZSiteSettingsTelemetrySchema } from './schemas/telemetry';
// TODO: Use `z.union([...])` once we have more than one setting
export const ZSiteSettingSchema = ZSiteSettingsBannerSchema;
export const ZSiteSettingSchema = z.union([
ZSiteSettingsBannerSchema,
ZSiteSettingsTelemetrySchema,
]);
export type TSiteSettingSchema = z.infer<typeof ZSiteSettingSchema>;

View 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>;

View File

@ -3,7 +3,7 @@ import { prisma } from '@documenso/prisma';
import type { TSiteSettingSchema } from './schema';
export type UpsertSiteSettingOptions = TSiteSettingSchema & {
userId: number;
userId: number | null;
};
export const upsertSiteSetting = async ({

View 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,
});
};

View 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);
}
};

View File

@ -1,7 +1,7 @@
import { Role } from '@prisma/client';
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({
term: z.string().optional(),
@ -30,7 +30,7 @@ export type TAdminUpdateRecipientMutationSchema = z.infer<
typeof ZAdminUpdateRecipientMutationSchema
>;
export const ZAdminUpdateSiteSettingMutationSchema = ZSiteSettingSchema;
export const ZAdminUpdateSiteSettingMutationSchema = ZSiteSettingsBannerSchema;
export type TAdminUpdateSiteSettingMutationSchema = z.infer<
typeof ZAdminUpdateSiteSettingMutationSchema

View File

@ -70,6 +70,7 @@ declare namespace NodeJS {
VERCEL?: string;
VERCEL_ENV?: 'production' | 'development' | 'preview';
VERCEL_URL?: string;
NEXT_RUNTIME?: 'nodejs' | 'edge';
DEPLOYMENT_TARGET?: 'webapp' | 'marketing';
FONT_CAVEAT_URI: string;
@ -79,5 +80,7 @@ declare namespace NodeJS {
DATABASE_URL?: string;
POSTGRES_PRISMA_URL?: string;
POSTGRES_URL_NON_POOLING?: string;
DISABLE_TELEMETRY?: string;
}
}

View File

@ -2,13 +2,8 @@
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": [
"^build"
],
"outputs": [
".next/**",
"!.next/cache/**"
]
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]
},
"lint": {
"cache": false
@ -24,9 +19,7 @@
"persistent": true
},
"start": {
"dependsOn": [
"^build"
],
"dependsOn": ["^build"],
"cache": false,
"persistent": true
},
@ -34,15 +27,11 @@
"cache": false
},
"test:e2e": {
"dependsOn": [
"^build"
],
"dependsOn": ["^build"],
"cache": false
}
},
"globalDependencies": [
"**/.env.*local"
],
"globalDependencies": ["**/.env.*local"],
"globalEnv": [
"APP_VERSION",
"NEXT_PRIVATE_ENCRYPTION_KEY",
@ -121,6 +110,8 @@
"GOOGLE_APPLICATION_CREDENTIALS",
"E2E_TEST_AUTHENTICATE_USERNAME",
"E2E_TEST_AUTHENTICATE_USER_EMAIL",
"E2E_TEST_AUTHENTICATE_USER_PASSWORD"
"E2E_TEST_AUTHENTICATE_USER_PASSWORD",
"DISABLE_TELEMETRY",
"NEXT_RUNTIME"
]
}