fix(sqlite,browser): resolve SQLite connection pool deadlock, disable stale SW cache, and update BRAIN.md
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 38s

This commit is contained in:
sandrodach committed 2026-10-24 21:27:51 +02:00
1 parent d6bc2976d9
commit 5c4e130862
8 files changed
+414 -231

No files matched your search

+6 -3
View File
@@ -15,10 +15,13 @@
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script>
if ('caches' in window) {
caches.keys().then((keys) => keys.forEach((k) => caches.delete(k))).catch(() => {});
}
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').catch(() => {});
});
navigator.serviceWorker.getRegistrations().then((regs) => {
regs.forEach((r) => r.unregister());
}).catch(() => {});
}
</script>
</body>
+9 -24
View File
@@ -1,33 +1,18 @@
const CACHE_NAME = 'mindsift-pwa-v1';
const ASSETS = ['/', '/index.html', '/manifest.json'];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS))
);
// Network-first / cache-clearing service worker to ensure fresh builds across all browsers
self.addEventListener('install', () => {
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
)
caches
.keys()
.then((keys) => Promise.all(keys.map((k) => caches.delete(k))))
.then(() => self.clients.claim())
);
self.clients.claim();
});
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET' || event.request.url.includes('/api/')) {
return;
}
event.respondWith(
fetch(event.request)
.then((res) => {
const clone = res.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
return res;
})
.catch(() => caches.match(event.request).then((cached) => cached || caches.match('/')))
);
self.addEventListener('fetch', () => {
// Always use native browser network fetch so API calls and fresh Vite bundles are never intercepted by stale caches
return;
});
@@ -148,40 +148,46 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
const effectiveTarget = overrideTarget || target;
const effectiveListId =
overrideListId || targetListId || (lists.length > 0 ? lists[0].id : '');
const capturedPriority = priority;
const capturedDueDate = dueDate;
const capturedSource = isListening ? 'voice' : 'manual';
// Optimistically clear input immediately so the UI feels instant (<10ms) in all browsers
setContent('');
setPriority('normal');
setDueDate('');
setTarget('inbox');
setShowOptions(false);
setIsSubmitting(true);
triggerHaptic(15);
const savedMsg =
effectiveTarget === 'task'
? 'Als Aufgabe!'
: effectiveTarget === 'list_item'
? 'Auf Liste!'
: effectiveTarget === 'document'
? 'Als Notiz!'
: 'Im Dump!';
setJustSavedLabel(savedMsg);
setTimeout(() => setJustSavedLabel(null), 1600);
inputRef.current?.focus();
try {
const created = await api.captureThought(
trimmed,
priority,
isListening ? 'voice' : 'manual',
dueDate,
capturedPriority,
capturedSource,
capturedDueDate,
{
target: effectiveTarget,
targetListId: effectiveTarget === 'list_item' ? effectiveListId : '',
}
);
setContent('');
setPriority('normal');
setDueDate('');
setTarget('inbox');
setShowOptions(false);
const savedMsg =
effectiveTarget === 'task'
? 'Als Aufgabe!'
: effectiveTarget === 'list_item'
? 'Auf Liste!'
: effectiveTarget === 'document'
? 'Als Notiz!'
: 'Im Dump!';
setJustSavedLabel(savedMsg);
setTimeout(() => setJustSavedLabel(null), 1600);
onCaptured(created, effectiveTarget);
inputRef.current?.focus();
} catch {
// Restore text if capture unexpectedly failed completely
setContent(trimmed);
} finally {
setIsSubmitting(false);
}
@@ -221,6 +227,12 @@ export const InstantCaptureBar: React.FC<InstantCaptureBarProps> = ({
type="text"
value={content}
onChange={(e) => setContent(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
executeCapture();
}
}}
placeholder={
isListening
? 'Ich höre zu…'
+16 -4
View File
@@ -40,6 +40,7 @@ async function request<T>(path: string, options: RequestInit = {}, isRetry = fal
const token = getToken();
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'Cache-Control': 'no-cache',
...(options.headers as Record<string, string>),
};
@@ -47,16 +48,27 @@ async function request<T>(path: string, options: RequestInit = {}, isRetry = fal
headers['Authorization'] = `Bearer ${token}`;
}
const res = await fetch(path, {
...options,
headers,
});
const controller = new AbortController();
const timeoutId = window.setTimeout(() => controller.abort(), 8000);
let res: Response;
try {
res = await fetch(path, {
...options,
headers,
cache: 'no-store',
signal: controller.signal,
});
} finally {
window.clearTimeout(timeoutId);
}
if (res.status === 401 && !isRetry && !path.includes('/auth/login') && !path.includes('/auth/quickstart')) {
try {
const qsRes = await fetch('/api/v1/auth/quickstart', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
cache: 'no-store',
});
if (qsRes.ok) {
const qsData = await qsRes.json();