aboutsummaryrefslogtreecommitdiff
path: root/next.config.ts
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-01-24 13:09:50 +0000
committerFuwn <[email protected]>2026-01-24 13:09:50 +0000
commit396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b (patch)
treeb9df4ca6a70db45cfffbae6fdd7252e20fb8e93c /next.config.ts
downloadumami-main.tar.xz
umami-main.zip
Initial commitHEADmain
Created from https://vercel.com/new
Diffstat (limited to 'next.config.ts')
-rw-r--r--next.config.ts202
1 files changed, 202 insertions, 0 deletions
diff --git a/next.config.ts b/next.config.ts
new file mode 100644
index 0000000..99dcca0
--- /dev/null
+++ b/next.config.ts
@@ -0,0 +1,202 @@
+import 'dotenv/config';
+import pkg from './package.json' with { type: 'json' };
+
+const TRACKER_SCRIPT = '/script.js';
+
+const basePath = process.env.BASE_PATH || '';
+const cloudMode = process.env.CLOUD_MODE || '';
+const cloudUrl = process.env.CLOUD_URL || '';
+const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT || '';
+const corsMaxAge = process.env.CORS_MAX_AGE || '';
+const defaultLocale = process.env.DEFAULT_LOCALE || '';
+const forceSSL = process.env.FORCE_SSL || '';
+const frameAncestors = process.env.ALLOWED_FRAME_URLS || '';
+const trackerScriptName = process.env.TRACKER_SCRIPT_NAME || '';
+const trackerScriptURL = process.env.TRACKER_SCRIPT_URL || '';
+
+const contentSecurityPolicy = `
+ default-src 'self';
+ img-src 'self' https: data:;
+ script-src 'self' 'unsafe-eval' 'unsafe-inline';
+ style-src 'self' 'unsafe-inline';
+ connect-src 'self' https:;
+ frame-ancestors 'self' ${frameAncestors};
+`;
+
+const defaultHeaders = [
+ {
+ key: 'X-DNS-Prefetch-Control',
+ value: 'on',
+ },
+ {
+ key: 'Content-Security-Policy',
+ value: contentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(),
+ },
+];
+
+if (forceSSL) {
+ defaultHeaders.push({
+ key: 'Strict-Transport-Security',
+ value: 'max-age=63072000; includeSubDomains; preload',
+ });
+}
+
+const trackerHeaders = [
+ {
+ key: 'Access-Control-Allow-Origin',
+ value: '*',
+ },
+ {
+ key: 'Cache-Control',
+ value: 'public, max-age=86400, must-revalidate',
+ },
+];
+
+const apiHeaders = [
+ {
+ key: 'Access-Control-Allow-Origin',
+ value: '*',
+ },
+ {
+ key: 'Access-Control-Allow-Headers',
+ value: '*',
+ },
+ {
+ key: 'Access-Control-Allow-Methods',
+ value: 'GET, DELETE, POST, PUT',
+ },
+ {
+ key: 'Access-Control-Max-Age',
+ value: corsMaxAge || '86400',
+ },
+ {
+ key: 'Cache-Control',
+ value: 'no-cache',
+ },
+];
+
+const headers = [
+ {
+ source: '/api/:path*',
+ headers: apiHeaders,
+ },
+ {
+ source: '/:path*',
+ headers: defaultHeaders,
+ },
+ {
+ source: TRACKER_SCRIPT,
+ headers: trackerHeaders,
+ },
+];
+
+const rewrites = [];
+
+if (trackerScriptURL) {
+ rewrites.push({
+ source: TRACKER_SCRIPT,
+ destination: trackerScriptURL,
+ });
+}
+
+if (collectApiEndpoint) {
+ headers.push({
+ source: collectApiEndpoint,
+ headers: apiHeaders,
+ });
+
+ rewrites.push({
+ source: collectApiEndpoint,
+ destination: '/api/send',
+ });
+}
+
+const redirects = [
+ {
+ source: '/settings',
+ destination: '/settings/preferences',
+ permanent: false,
+ },
+ {
+ source: '/teams/:id',
+ destination: '/teams/:id/websites',
+ permanent: false,
+ },
+ {
+ source: '/teams/:id/settings',
+ destination: '/teams/:id/settings/preferences',
+ permanent: false,
+ },
+ {
+ source: '/admin',
+ destination: '/admin/users',
+ permanent: false,
+ },
+];
+
+// Adding rewrites + headers for all alternative tracker script names.
+if (trackerScriptName) {
+ const names = trackerScriptName?.split(',').map(name => name.trim());
+
+ if (names) {
+ names.forEach(name => {
+ const normalizedSource = `/${name.replace(/^\/+/, '')}`;
+
+ rewrites.push({
+ source: normalizedSource,
+ destination: TRACKER_SCRIPT,
+ });
+
+ headers.push({
+ source: normalizedSource,
+ headers: trackerHeaders,
+ });
+ });
+ }
+}
+
+if (cloudMode) {
+ rewrites.push({
+ source: '/script.js',
+ destination: 'https://cloud.umami.is/script.js',
+ });
+}
+
+/** @type {import('next').NextConfig} */
+export default {
+ reactStrictMode: false,
+ env: {
+ basePath,
+ cloudMode,
+ cloudUrl,
+ currentVersion: pkg.version,
+ defaultLocale,
+ },
+ basePath,
+ output: 'standalone',
+ eslint: {
+ ignoreDuringBuilds: true,
+ },
+ typescript: {
+ ignoreBuildErrors: true,
+ },
+ async headers() {
+ return headers;
+ },
+ async rewrites() {
+ return [
+ ...rewrites,
+ {
+ source: '/telemetry.js',
+ destination: '/api/scripts/telemetry',
+ },
+ {
+ source: '/teams/:teamId/:path*',
+ destination: '/:path*',
+ },
+ ];
+ },
+ async redirects() {
+ return [...redirects];
+ },
+};