This commit is contained in:
seb
2026-09-23 10:18:31 +02:00
parent c21a951884
commit 0391e845f0
3 changed files with 42 additions and 8 deletions

View File

@@ -13,7 +13,7 @@ map $http_upgrade $connection_upgrade {
server { server {
listen 80; listen 80;
listen [::]:80; listen [::]:80;
server_name dash.example.com; # <-- Replace with your domain or server IP server_name dashdev.quixpos.com;
location ^~ /.well-known/acme-challenge/ { location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot; root /var/www/certbot;
@@ -32,11 +32,11 @@ server {
listen [::]:443 ssl; listen [::]:443 ssl;
http2 on; http2 on;
server_name dash.example.com; # <-- Replace with your domain or server IP server_name dashdev.quixpos.com;
# --- SSL / TLS Certificates --- # --- SSL / TLS Certificates ---
ssl_certificate /etc/letsencrypt/live/dash.example.com/fullchain.pem; ssl_certificate /etc/letsencrypt/live/dashdev.quixpos.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dash.example.com/privkey.pem; ssl_certificate_key /etc/letsencrypt/live/dashdev.quixpos.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3; ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
@@ -45,6 +45,9 @@ server {
ssl_session_cache shared:SSL:10m; ssl_session_cache shared:SSL:10m;
ssl_session_tickets off; ssl_session_tickets off;
# --- Header Buffer Limits (prevents HTTP/2 RST_STREAM / ERR_HTTP2_PROTOCOL_ERROR) ---
large_client_header_buffers 4 128k;
# --- Static File Root --- # --- Static File Root ---
root /home/seb/src/dashfront/dist; root /home/seb/src/dashfront/dist;
index index.html; index index.html;
@@ -87,16 +90,21 @@ server {
proxy_pass http://127.0.0.1:4208; proxy_pass http://127.0.0.1:4208;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Connection ""; # Clear Connection & Accept-Encoding to prevent upstream gzip/chunk buffering
proxy_set_header Connection "";
proxy_set_header Accept-Encoding "";
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
# Disable buffering and compression completely for immediate push
proxy_buffering off; proxy_buffering off;
proxy_cache off; proxy_cache off;
chunked_transfer_encoding off; chunked_transfer_encoding off;
gzip off;
# Prevent premature disconnections; keep SSE open for 24 hours
proxy_read_timeout 86400s; proxy_read_timeout 86400s;
proxy_send_timeout 86400s; proxy_send_timeout 86400s;
} }
@@ -106,6 +114,8 @@ server {
proxy_pass http://127.0.0.1:4208; proxy_pass http://127.0.0.1:4208;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

View File

@@ -40,6 +40,15 @@ export function setAuthEventDispatcher(dispatch) {
emit = dispatch; emit = dispatch;
} }
function normalizePicture(picture) {
if (!picture || typeof picture !== 'string') return null;
if (picture.startsWith('http://') || picture.startsWith('https://') || picture.startsWith('data:')) {
return picture;
}
const mime = picture.startsWith('/9j/') ? 'image/jpeg' : 'image/png';
return `data:${mime};base64,${picture}`;
}
/** /**
* Merges API user data while retaining the current company selection when the * Merges API user data while retaining the current company selection when the
* response does not contain one. * response does not contain one.
@@ -52,7 +61,11 @@ function mergeUser(current, incoming) {
if (!incoming) return current; if (!incoming) return current;
const selectedCompanyId = const selectedCompanyId =
incoming.selectedCompanyId ?? current?.selectedCompanyId ?? null; incoming.selectedCompanyId ?? current?.selectedCompanyId ?? null;
return { ...current, ...incoming, selectedCompanyId }; const merged = { ...current, ...incoming, selectedCompanyId };
if (merged.picture) {
merged.picture = normalizePicture(merged.picture);
}
return merged;
} }
let authAutomat; let authAutomat;
@@ -104,8 +117,9 @@ const actions = {
if (!session?.user) { if (!session?.user) {
throw new Error('Login ok but session cookie not accepted (/api/auth/me still unauthorized)'); throw new Error('Login ok but session cookie not accepted (/api/auth/me still unauthorized)');
} }
const user = mergeUser(authAutomat.state.user, session.user);
authAutomat.setState({ authAutomat.setState({
user: session.user, user,
googleClientId: session.googleClientId ?? authAutomat.state.googleClientId, googleClientId: session.googleClientId ?? authAutomat.state.googleClientId,
status: 'authed', status: 'authed',
}); });

View File

@@ -81,6 +81,16 @@ class UserMenu extends PureComponent {
const { user, anchorEl, settingsOpen, numbersOpen } = this.state; const { user, anchorEl, settingsOpen, numbersOpen } = this.state;
const open = Boolean(anchorEl); const open = Boolean(anchorEl);
const pictureSrc = (() => {
const pic = user?.picture;
if (!pic || typeof pic !== 'string') return undefined;
if (pic.startsWith('http://') || pic.startsWith('https://') || pic.startsWith('data:')) {
return pic;
}
const mime = pic.startsWith('/9j/') ? 'image/jpeg' : 'image/png';
return `data:${mime};base64,${pic}`;
})();
return ( return (
<> <>
<Box sx={{ px: 1.5, py: 1.5, alignSelf: 'flex-start' }}> <Box sx={{ px: 1.5, py: 1.5, alignSelf: 'flex-start' }}>
@@ -89,7 +99,7 @@ class UserMenu extends PureComponent {
onMouseDown={(e) => e.preventDefault()} onMouseDown={(e) => e.preventDefault()}
sx={{ p: 0, width: 32, height: 32, borderRadius: '50%' }} sx={{ p: 0, width: 32, height: 32, borderRadius: '50%' }}
> >
<Avatar src={user?.picture} alt={user?.email} sx={{ width: 32, height: 32 }}> <Avatar src={pictureSrc} alt={user?.email} sx={{ width: 32, height: 32 }}>
{user?.email?.[0]?.toUpperCase()} {user?.email?.[0]?.toUpperCase()}
</Avatar> </Avatar>
</IconButton> </IconButton>