Merge branch 'dev' of https://github.com/umami-software/umami into dev
Some checks are pending
Create docker images (cloud) / Build, push, and deploy (push) Waiting to run
Node.js CI / build (postgresql, 18.18, 10) (push) Waiting to run

This commit is contained in:
Mike Cao 2025-12-03 18:39:45 -08:00
commit 1483241494
93 changed files with 3147 additions and 1296 deletions

View file

@ -1,5 +1,5 @@
import crypto from 'node:crypto';
import { v4, v5 } from 'uuid';
import { v4, v5, v7 } from 'uuid';
const ALGORITHM = 'aes-256-gcm';
const IV_LENGTH = 16;
@ -57,7 +57,9 @@ export function secret() {
}
export function uuid(...args: any) {
if (!args.length) return v4();
if (args.length) {
return v5(hash(...args, secret()), v5.DNS);
}
return v5(hash(...args, secret()), v5.DNS);
return process.env.USE_UUIDV7 ? v7() : v4();
}

View file

@ -114,9 +114,9 @@ export async function getClientInfo(request: Request, payload: Record<string, an
const country = safeDecodeURIComponent(location?.country);
const region = safeDecodeURIComponent(location?.region);
const city = safeDecodeURIComponent(location?.city);
const browser = browserName(userAgent);
const os = detectOS(userAgent) as string;
const device = getDevice(userAgent, payload?.screen);
const browser = payload?.browser ?? browserName(userAgent);
const os = payload?.os ?? (detectOS(userAgent) as string);
const device = payload?.device ?? getDevice(userAgent, payload?.screen);
return { userAgent, browser, os, ip, country, region, city, device };
}

View file

@ -206,6 +206,10 @@ async function rawQuery(sql: string, data: Record<string, any>, name?: string):
return `$${params.length}${type ?? ''}`;
});
if (process.env.DATABASE_REPLICA_URL && '$replica' in client) {
return client.$replica().$queryRawUnsafe(query, ...params);
}
return client.$queryRawUnsafe(query, ...params);
}
@ -296,10 +300,6 @@ function getSchema() {
}
function getClient() {
if (!process.env.DATABASE_URL) {
return null;
}
const url = process.env.DATABASE_URL;
const replicaUrl = process.env.DATABASE_REPLICA_URL;
const logQuery = process.env.LOG_QUERY;
@ -307,43 +307,49 @@ function getClient() {
const connectionUrl = new URL(url);
const schema = connectionUrl.searchParams.get('schema') ?? undefined;
const adapter = new PrismaPg({ connectionString: url.toString() }, { schema });
const baseAdapter = new PrismaPg({ connectionString: url }, { schema });
const prisma = new PrismaClient({
adapter,
const baseClient = new PrismaClient({
adapter: baseAdapter,
errorFormat: 'pretty',
...(logQuery ? PRISMA_LOG_OPTIONS : {}),
});
if (replicaUrl) {
const replicaAdapter = new PrismaPg({ connectionString: replicaUrl.toString() }, { schema });
const replicaClient = new PrismaClient({
adapter: replicaAdapter,
...(logQuery ? PRISMA_LOG_OPTIONS : {}),
});
prisma.$extends(
readReplicas({
replicas: [replicaClient],
}),
);
if (logQuery) {
baseClient.$on('query', log);
}
if (!replicaUrl) {
log('Prisma initialized');
globalThis[PRISMA] ??= baseClient;
return baseClient;
}
const replicaAdapter = new PrismaPg({ connectionString: replicaUrl }, { schema });
const replicaClient = new PrismaClient({
adapter: replicaAdapter,
errorFormat: 'pretty',
...(logQuery ? PRISMA_LOG_OPTIONS : {}),
});
if (logQuery) {
prisma.$on('query' as never, log);
replicaClient.$on('query', log);
}
log('Prisma initialized');
const extended = baseClient.$extends(
readReplicas({
replicas: [replicaClient],
}),
);
if (!globalThis[PRISMA]) {
globalThis[PRISMA] = prisma;
}
log('Prisma initialized (with replica)');
globalThis[PRISMA] ??= extended;
return prisma;
return extended;
}
const client: PrismaClient = globalThis[PRISMA] || getClient();
const client = (globalThis[PRISMA] || getClient()) as ReturnType<typeof getClient>;
export default {
client,