serve api on same origin at /api
This commit is contained in:
+1
-2
@@ -12,10 +12,9 @@ RUN npm run build
|
||||
FROM nginx:alpine
|
||||
WORKDIR /usr/share/nginx/html
|
||||
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/dist ./
|
||||
|
||||
ENV API_BASE_URL=""
|
||||
ENV API_PROXY_PASS=""
|
||||
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
@@ -1,11 +1,37 @@
|
||||
#!/bin/sh
|
||||
set -euo pipefail
|
||||
|
||||
API_BASE_URL_TRIMMED="${API_BASE_URL:-}"
|
||||
API_BASE_URL_TRIMMED="${API_BASE_URL_TRIMMED%%/}"
|
||||
API_PROXY_PASS_TRIMMED="${API_PROXY_PASS:-}"
|
||||
API_PROXY_PASS_TRIMMED="${API_PROXY_PASS_TRIMMED%%/}"
|
||||
|
||||
cat <<CONFIG > /usr/share/nginx/html/config.js
|
||||
window.__PAPERCRATE_API_BASE_URL = "${API_BASE_URL_TRIMMED}";
|
||||
CONFIG
|
||||
cat <<'BASE' > /etc/nginx/conf.d/default.conf
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
BASE
|
||||
|
||||
if [ -n "$API_PROXY_PASS_TRIMMED" ]; then
|
||||
cat <<PROXY >> /etc/nginx/conf.d/default.conf
|
||||
|
||||
location /api/ {
|
||||
proxy_pass ${API_PROXY_PASS_TRIMMED};
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
PROXY
|
||||
fi
|
||||
|
||||
cat <<'ENDCFG' >> /etc/nginx/conf.d/default.conf
|
||||
}
|
||||
ENDCFG
|
||||
|
||||
exec "$@"
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
window.__PAPERCRATE_API_BASE_URL = window.__PAPERCRATE_API_BASE_URL || '';
|
||||
@@ -6,7 +6,6 @@
|
||||
<title>Papercrate</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="/config.js"></script>
|
||||
<main id="app"></main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+29
-24
@@ -36,33 +36,28 @@ import { createPreviewSurface } from './preview/PreviewWorkspace';
|
||||
import { createDesktopSurface } from './DesktopWorkspace';
|
||||
import { AppShellContext, useAppShell } from './appShellContext';
|
||||
|
||||
const runtimeApiBase =
|
||||
typeof window !== 'undefined' && window.__PAPERCRATE_API_BASE_URL
|
||||
? window.__PAPERCRATE_API_BASE_URL
|
||||
: '';
|
||||
|
||||
const DEFAULT_DEV_API = 'http://127.0.0.1:3000';
|
||||
const ASSET_PRESIGN_TTL_MS = 240 * 1000; // backend issues 5 min tokens; refresh slightly early
|
||||
const TAG_MIME_TYPES = ['application/x-papercrate-tag', 'text/papercrate-tag'];
|
||||
|
||||
const API_ROOT = (runtimeApiBase || process.env.API_BASE_URL || DEFAULT_DEV_API).replace(/\/$/, '');
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: API_ROOT ? `${API_ROOT}/api` : '/api',
|
||||
baseURL: '/api',
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
const STORED_TOKEN = window.localStorage.getItem('papercrate_token') || '';
|
||||
const storage = typeof window !== 'undefined' ? window.sessionStorage : undefined;
|
||||
const STORED_TOKEN = storage?.getItem('papercrate_token') || '';
|
||||
let STORED_TENANT = null;
|
||||
try {
|
||||
const rawTenant = window.localStorage.getItem('papercrate_tenant');
|
||||
if (rawTenant) {
|
||||
STORED_TENANT = JSON.parse(rawTenant);
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
if (storage) {
|
||||
try {
|
||||
const rawTenant = storage.getItem('papercrate_tenant');
|
||||
if (rawTenant) {
|
||||
STORED_TENANT = JSON.parse(rawTenant);
|
||||
}
|
||||
} catch (
|
||||
// eslint-disable-next-line no-empty
|
||||
error
|
||||
) {}
|
||||
}
|
||||
if (STORED_TOKEN) {
|
||||
api.defaults.headers.common.Authorization = `Bearer ${STORED_TOKEN}`;
|
||||
}
|
||||
@@ -191,22 +186,22 @@ const AppStateProvider = ({ children }) => {
|
||||
const token = state.token || '';
|
||||
if (token) {
|
||||
api.defaults.headers.common.Authorization = `Bearer ${token}`;
|
||||
window.localStorage.setItem('papercrate_token', token);
|
||||
storage?.setItem('papercrate_token', token);
|
||||
} else {
|
||||
delete api.defaults.headers.common.Authorization;
|
||||
window.localStorage.removeItem('papercrate_token');
|
||||
storage?.removeItem('papercrate_token');
|
||||
}
|
||||
}, [state.token]);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.tenant) {
|
||||
try {
|
||||
window.localStorage.setItem('papercrate_tenant', JSON.stringify(state.tenant));
|
||||
storage?.setItem('papercrate_tenant', JSON.stringify(state.tenant));
|
||||
} catch (error) {
|
||||
console.warn('Failed to persist tenant info', error);
|
||||
}
|
||||
} else {
|
||||
window.localStorage.removeItem('papercrate_tenant');
|
||||
storage?.removeItem('papercrate_tenant');
|
||||
}
|
||||
}, [state.tenant]);
|
||||
|
||||
@@ -270,7 +265,7 @@ const ROW_KEY_SEPARATOR = ':';
|
||||
const DOCUMENT_ROW_PREFIX = 'document';
|
||||
const FOLDER_ROW_PREFIX = 'folder';
|
||||
|
||||
const resolveApiPath = (path = '') => (API_ROOT ? `${API_ROOT}${path}` : path);
|
||||
const resolveApiPath = (path = '') => path;
|
||||
|
||||
const makeRowKey = (type, id) =>
|
||||
id ? `${type}${ROW_KEY_SEPARATOR}${id}` : `${type}${ROW_KEY_SEPARATOR}`;
|
||||
@@ -571,6 +566,16 @@ const AppLayout = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const initialRefreshAttemptedRef = useRef(Boolean(token));
|
||||
|
||||
useEffect(() => {
|
||||
if (!token && !initialRefreshAttemptedRef.current && appStatus === 'logged-out') {
|
||||
initialRefreshAttemptedRef.current = true;
|
||||
console.log('[Auth] Attempting refresh at startup');
|
||||
refreshAccessToken().catch(() => {});
|
||||
}
|
||||
}, [token, appStatus, refreshAccessToken]);
|
||||
|
||||
const clearFilters = useCallback(() => {
|
||||
setSearchQuery('');
|
||||
setActiveTagFilters([]);
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const webpack = require('webpack');
|
||||
const dotenv = require('dotenv');
|
||||
|
||||
const env = dotenv.config({ path: path.resolve(__dirname, '.env.local') }).parsed || {};
|
||||
|
||||
const DEFAULT_DEV_API = 'http://127.0.0.1:3000';
|
||||
|
||||
const API_BASE_URL = env.API_BASE_URL || process.env.API_BASE_URL || DEFAULT_DEV_API;
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.jsx',
|
||||
output: {
|
||||
@@ -46,9 +37,6 @@ module.exports = {
|
||||
template: path.resolve(__dirname, 'src/index.html'),
|
||||
favicon: false,
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.API_BASE_URL': JSON.stringify(API_BASE_URL),
|
||||
}),
|
||||
],
|
||||
devServer: {
|
||||
static: {
|
||||
@@ -58,6 +46,14 @@ module.exports = {
|
||||
port: 5173,
|
||||
historyApiFallback: true,
|
||||
open: true,
|
||||
proxy: [
|
||||
{
|
||||
context: ['/api'],
|
||||
target: 'http://127.0.0.1:3000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
devtool: 'source-map',
|
||||
resolve: {
|
||||
|
||||
Reference in New Issue
Block a user