diff --git a/next.config.js b/next.config.js index deacbda3..9af5d653 100644 --- a/next.config.js +++ b/next.config.js @@ -1,9 +1,15 @@ /** * @type {import('next').NextConfig} */ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); + module.exports = { + basePath: process.env.NEXT_PUBLIC_BASE_PATH || '', + assetPrefix: process.env.NEXT_PUBLIC_BASE_PATH || '', env: { commitTag: process.env.COMMIT_TAG || 'local', + basePath: process.env.NEXT_PUBLIC_BASE_PATH || '', }, images: { remotePatterns: [ @@ -19,6 +25,9 @@ module.exports = { issuer: /\.(js|ts)x?$/, use: ['@svgr/webpack'], }); + config.resolve.alias['next/image'] = path.resolve( + './src/components/Common/BaseImage/index.ts' + ); return config; }, diff --git a/server/index.ts b/server/index.ts index abb98be6..069ba606 100644 --- a/server/index.ts +++ b/server/index.ts @@ -154,11 +154,13 @@ app }); if (settings.network.csrfProtection) { server.use( + `${process.env.NEXT_PUBLIC_BASE_PATH || ''}`, csurf({ cookie: { httpOnly: true, sameSite: true, secure: !dev, + path: `${process.env.NEXT_PUBLIC_BASE_PATH || ''}` || '/', }, }) ); @@ -174,7 +176,7 @@ app // Set up sessions const sessionRespository = getRepository(Session); server.use( - '/api', + `${process.env.NEXT_PUBLIC_BASE_PATH || ''}/api`, session({ secret: settings.clientId, resave: false, @@ -184,6 +186,7 @@ app httpOnly: true, sameSite: settings.network.csrfProtection ? 'strict' : 'lax', secure: 'auto', + path: `${process.env.NEXT_PUBLIC_BASE_PATH || ''}` || '/', }, store: new TypeormStore({ cleanupLimit: 2, @@ -194,6 +197,7 @@ app const apiDocs = YAML.load(API_SPEC_PATH); server.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiDocs)); server.use( + `${process.env.NEXT_PUBLIC_BASE_PATH || ''}`, OpenApiValidator.middleware({ apiSpec: API_SPEC_PATH, validateRequests: true, @@ -211,11 +215,12 @@ app }; next(); }); - server.use('/api/v1', routes); + const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''; + server.use(`${basePath}/api/v1`, routes); // Do not set cookies so CDNs can cache them - server.use('/imageproxy', clearCookies, imageproxy); - server.use('/avatarproxy', clearCookies, avatarproxy); + server.use(`${basePath}/imageproxy`, clearCookies, imageproxy); + server.use(`${basePath}/avatarproxy`, clearCookies, avatarproxy); server.get('*', (req, res) => handle(req, res)); server.use( diff --git a/src/components/Common/BaseImage/index.ts b/src/components/Common/BaseImage/index.ts new file mode 100644 index 00000000..569dd1dd --- /dev/null +++ b/src/components/Common/BaseImage/index.ts @@ -0,0 +1,32 @@ +// src/components/Common/BaseImage/index.ts +import type { ImageProps } from 'next/image'; +import NextImage from 'next/image'; +import React from 'react'; + +// Instead of defining our own props, extend from Next's ImageProps +const BaseImage = React.forwardRef( + (props, ref) => { + const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''; + + const modifiedSrc = + typeof props.src === 'string' && props.src.startsWith('/') + ? `${basePath}${props.src}` + : props.src; + + const shouldUnoptimize = + typeof props.src === 'string' && props.src.endsWith('.svg'); + + return React.createElement(NextImage, { + ...props, + ref, + src: modifiedSrc, + unoptimized: shouldUnoptimize || props.unoptimized, + }); + } +); + +BaseImage.displayName = 'Image'; + +export default BaseImage; +// Re-export ImageProps type for consumers +export type { ImageProps }; diff --git a/src/components/Common/CachedImage/index.tsx b/src/components/Common/CachedImage/index.tsx index 8af2c722..37c89bb2 100644 --- a/src/components/Common/CachedImage/index.tsx +++ b/src/components/Common/CachedImage/index.tsx @@ -1,6 +1,6 @@ +import Image from '@app/components/Common/BaseImage'; import useSettings from '@app/hooks/useSettings'; import type { ImageLoader, ImageProps } from 'next/image'; -import Image from 'next/image'; const imageLoader: ImageLoader = ({ src }) => src; diff --git a/src/components/Common/SettingsTabs/index.tsx b/src/components/Common/SettingsTabs/index.tsx index 22d5b948..9371bd16 100644 --- a/src/components/Common/SettingsTabs/index.tsx +++ b/src/components/Common/SettingsTabs/index.tsx @@ -1,4 +1,5 @@ import { useUser } from '@app/hooks/useUser'; +import { getBasedPath } from '@app/utils/navigationUtil'; import type { Permission } from '@server/lib/permissions'; import { hasPermission } from '@server/lib/permissions'; import Link from 'next/link'; @@ -85,10 +86,10 @@ const SettingsTabs = ({