Filter data: URLs before creating posts

- Prevent data: URLs from being sent to backend (crash browsers)
- Only send properly uploaded asset URLs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
SocioWire 2026-01-04 22:43:56 +00:00
parent e5f9cad7df
commit 2014e3434d
2 changed files with 43 additions and 35 deletions

View File

@ -13,7 +13,7 @@ import { parseAuthError } from "./authErrors";
const AuthCtx = createContext(null); const AuthCtx = createContext(null);
const KC_URL = (import.meta?.env?.VITE_KC_URL || "https://www.sociowire.com/auth").replace(/\/+$/, ""); const KC_URL = (import.meta?.env?.VITE_KC_URL || "https://auth.sociowire.com").replace(/\/+$/, "");
const KC_REALM = import.meta?.env?.VITE_KC_REALM || "sociowire"; const KC_REALM = import.meta?.env?.VITE_KC_REALM || "sociowire";
const KC_CLIENT_ID = import.meta?.env?.VITE_KC_CLIENT_ID || "sociowire-frontend"; const KC_CLIENT_ID = import.meta?.env?.VITE_KC_CLIENT_ID || "sociowire-frontend";
const KC_SSO_ENABLED = (import.meta?.env?.VITE_KC_SSO || "true") !== "false"; const KC_SSO_ENABLED = (import.meta?.env?.VITE_KC_SSO || "true") !== "false";
@ -278,42 +278,47 @@ export function AuthProvider({ children }) {
} }
async function ensureFreshSession(existing) { async function ensureFreshSession(existing) {
if (!existing?.access_token) return { ok: false, reason: "no_access_token" }; try {
if (!existing?.access_token) return { ok: false, reason: "no_access_token" };
const exp = jwtExpSeconds(existing.access_token); const exp = jwtExpSeconds(existing.access_token);
const skew = 60; const skew = 60;
const isExpiredOrSoon = !exp || exp <= nowSec() + skew; const isExpiredOrSoon = !exp || exp <= nowSec() + skew;
if (isExpiredOrSoon) { if (isExpiredOrSoon) {
if (!existing.refresh_token) return { ok: false, reason: "expired_no_refresh" }; if (!existing.refresh_token) return { ok: false, reason: "expired_no_refresh" };
const rr = await refreshTokens(existing.refresh_token); const rr = await refreshTokens(existing.refresh_token);
if (!rr.ok || !rr.json?.access_token) { if (!rr.ok || !rr.json?.access_token) {
return { ok: false, reason: `refresh_failed_${rr.status}`, detail: rr.text }; return { ok: false, reason: `refresh_failed_${rr.status}`, detail: rr.text };
}
const merged = { ...existing, ...rr.json };
if (!merged.refresh_token) merged.refresh_token = existing.refresh_token;
setAuthFromTokens(merged);
existing = merged;
} else {
setAuthFromTokens(existing);
} }
const merged = { ...existing, ...rr.json }; const w = await whoami(existing.access_token);
if (!merged.refresh_token) merged.refresh_token = existing.refresh_token; if (w.ok && w.authenticated) {
setUser(w.username ? { username: w.username } : null);
setStatus("auth");
setError("");
setAuthFromTokens(merged); // Update email verification state from backend
existing = merged; await refreshProfile(existing.access_token).catch(() => {});
} else {
setAuthFromTokens(existing); return { ok: true };
}
return { ok: false, reason: `whoami_${w.status}`, detail: w.text || "" };
} catch (err) {
console.error("[Auth] ensureFreshSession error:", err);
return { ok: false, reason: "exception", detail: err?.message || "" };
} }
const w = await whoami(existing.access_token);
if (w.ok && w.authenticated) {
setUser(w.username ? { username: w.username } : null);
setStatus("auth");
setError("");
// Update email verification state from backend
await refreshProfile(existing.access_token);
return { ok: true };
}
return { ok: false, reason: `whoami_${w.status}`, detail: w.text || "" };
} }
function scheduleAutoRefresh() { function scheduleAutoRefresh() {
@ -362,8 +367,8 @@ export function AuthProvider({ children }) {
setError(""); setError("");
setLastInfo(""); setLastInfo("");
// Clear tokens from old/wrong issuer (keep any sociowire.com tokens) // Clear tokens from old/wrong issuer - must be from auth.sociowire.com now
clearBadTokens("sociowire.com"); clearBadTokens("auth.sociowire.com");
const saved = loadAuth(); const saved = loadAuth();
if (!saved?.access_token) { if (!saved?.access_token) {
@ -668,7 +673,7 @@ export function AuthProvider({ children }) {
}); });
} }
const onStorage = (e) => { const onStorage = (e) => {
if (e.key === "sociowire:auth") restore(); if (e.key === "sociowire:auth") restore().catch(() => setAnon(""));
}; };
window.addEventListener("storage", onStorage); window.addEventListener("storage", onStorage);
return () => window.removeEventListener("storage", onStorage); return () => window.removeEventListener("storage", onStorage);

View File

@ -1390,14 +1390,17 @@ export default function MapView({
payload.published_at = draftPublishedAt.trim(); payload.published_at = draftPublishedAt.trim();
} }
const mediaCandidates = draftMediaUrls.filter(Boolean); // Filter out data: URLs - they crash browsers and bloat the DB
const mediaCandidates = draftMediaUrls.filter(
(url) => url && !url.startsWith("data:")
);
if (mediaCandidates.length > 0) { if (mediaCandidates.length > 0) {
payload.image_small = mediaCandidates[0]; payload.image_small = mediaCandidates[0];
payload.image_medium = mediaCandidates[1] || mediaCandidates[0]; payload.image_medium = mediaCandidates[1] || mediaCandidates[0];
payload.image_large = payload.image_large =
mediaCandidates[2] || mediaCandidates[1] || mediaCandidates[0]; mediaCandidates[2] || mediaCandidates[1] || mediaCandidates[0];
payload.media_urls = mediaCandidates; payload.media_urls = mediaCandidates;
} else if (useCustomImage && draftImage.trim()) { } else if (useCustomImage && draftImage.trim() && !draftImage.trim().startsWith("data:")) {
const fallback = draftImage.trim(); const fallback = draftImage.trim();
payload.image_small = fallback; payload.image_small = fallback;
payload.image_medium = fallback; payload.image_medium = fallback;