mirror of
https://github.com/umami-software/umami.git
synced 2026-02-14 09:35:36 +01:00
Some checks are pending
Node.js CI / build (push) Waiting to run
This introduces a user-scoped dashboard with board-style view/edit pages while keeping it unavailable in team context, and moves website targeting to component config so dashboard components can each select their own website.
215 lines
4.4 KiB
TypeScript
215 lines
4.4 KiB
TypeScript
import 'dotenv/config';
|
|
import createNextIntlPlugin from 'next-intl/plugin';
|
|
import pkg from './package.json' with { type: 'json' };
|
|
|
|
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
|
|
|
|
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 defaultCurrency = process.env.DEFAULT_CURRENCY || '';
|
|
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: '/teams/:id/dashboard/edit',
|
|
destination: '/dashboard/edit',
|
|
permanent: false,
|
|
},
|
|
{
|
|
source: '/teams/:id/dashboard',
|
|
destination: '/dashboard',
|
|
permanent: false,
|
|
},
|
|
{
|
|
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 withNextIntl({
|
|
reactStrictMode: false,
|
|
env: {
|
|
basePath,
|
|
cloudMode,
|
|
cloudUrl,
|
|
currentVersion: pkg.version,
|
|
defaultCurrency,
|
|
defaultLocale,
|
|
},
|
|
basePath,
|
|
output: 'standalone',
|
|
typescript: {
|
|
ignoreBuildErrors: true,
|
|
},
|
|
devIndicators: false,
|
|
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];
|
|
},
|
|
});
|