Spaces:
Running
Running
Anish-530 commited on
Commit ·
942a467
1
Parent(s): 5691a72
Fix: 404 page loader stuck, navbar loading spinners, bulletproof NR import
Browse files
backend/app/core/logger.py
CHANGED
|
@@ -40,32 +40,39 @@ logger.add(
|
|
| 40 |
enqueue=True
|
| 41 |
)
|
| 42 |
|
| 43 |
-
# 6. New Relic Cloud Sink
|
| 44 |
# Set NEW_RELIC_LICENSE_KEY and NEW_RELIC_APP_NAME in your environment.
|
| 45 |
_NR_KEY = os.environ.get("NEW_RELIC_LICENSE_KEY", "")
|
| 46 |
if _NR_KEY:
|
| 47 |
-
import newrelic.agent
|
| 48 |
try:
|
| 49 |
-
newrelic.agent
|
| 50 |
-
except Exception:
|
| 51 |
-
pass
|
| 52 |
-
|
| 53 |
-
def _newrelic_sink(message):
|
| 54 |
-
record = message.record
|
| 55 |
try:
|
| 56 |
-
|
| 57 |
-
newrelic.agent.record_log_event(
|
| 58 |
-
message=record["message"],
|
| 59 |
-
level=record["level"].name,
|
| 60 |
-
timestamp=int(record["time"].timestamp() * 1000),
|
| 61 |
-
attributes={
|
| 62 |
-
"request_id": record["extra"].get("request_id", "SYSTEM"),
|
| 63 |
-
"logger": record["name"],
|
| 64 |
-
"function": record["function"],
|
| 65 |
-
"line": record["line"],
|
| 66 |
-
}
|
| 67 |
-
)
|
| 68 |
except Exception:
|
| 69 |
-
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
enqueue=True
|
| 41 |
)
|
| 42 |
|
| 43 |
+
# 6. New Relic Cloud Sink (fully optional — never crashes the app)
|
| 44 |
# Set NEW_RELIC_LICENSE_KEY and NEW_RELIC_APP_NAME in your environment.
|
| 45 |
_NR_KEY = os.environ.get("NEW_RELIC_LICENSE_KEY", "")
|
| 46 |
if _NR_KEY:
|
|
|
|
| 47 |
try:
|
| 48 |
+
import newrelic.agent
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
try:
|
| 50 |
+
newrelic.agent.initialize()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
except Exception:
|
| 52 |
+
pass
|
| 53 |
+
|
| 54 |
+
def _newrelic_sink(message):
|
| 55 |
+
record = message.record
|
| 56 |
+
try:
|
| 57 |
+
# Send to New Relic Logs APM
|
| 58 |
+
newrelic.agent.record_log_event(
|
| 59 |
+
message=record["message"],
|
| 60 |
+
level=record["level"].name,
|
| 61 |
+
timestamp=int(record["time"].timestamp() * 1000),
|
| 62 |
+
attributes={
|
| 63 |
+
"request_id": record["extra"].get("request_id", "SYSTEM"),
|
| 64 |
+
"logger": record["name"],
|
| 65 |
+
"function": record["function"],
|
| 66 |
+
"line": record["line"],
|
| 67 |
+
}
|
| 68 |
+
)
|
| 69 |
+
except Exception:
|
| 70 |
+
pass # Never let cloud logging failure affect the app
|
| 71 |
|
| 72 |
+
logger.add(_newrelic_sink, level="INFO", enqueue=True)
|
| 73 |
+
except ImportError:
|
| 74 |
+
# newrelic package not installed — skip silently
|
| 75 |
+
pass
|
| 76 |
+
except Exception:
|
| 77 |
+
# Any other error during setup — skip silently
|
| 78 |
+
pass
|
frontend/components/shared/Navbar.tsx
CHANGED
|
@@ -17,6 +17,7 @@ export default function Navbar({ onAnalyzeClick }: NavbarProps) {
|
|
| 17 |
const [dropdownOpen, setDropdownOpen] = useState(false);
|
| 18 |
const [dropdownPinned, setDropdownPinned] = useState(false);
|
| 19 |
const [soundEnabled, setSoundEnabled] = useState(true);
|
|
|
|
| 20 |
const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
| 21 |
|
| 22 |
const handleMouseEnter = () => {
|
|
@@ -153,10 +154,30 @@ export default function Navbar({ onAnalyzeClick }: NavbarProps) {
|
|
| 153 |
<div className="flex items-center gap-3">
|
| 154 |
{!isAuthenticated ? (
|
| 155 |
<>
|
| 156 |
-
<button
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
Register
|
| 158 |
</button>
|
| 159 |
-
<button
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
Login
|
| 161 |
</button>
|
| 162 |
</>
|
|
|
|
| 17 |
const [dropdownOpen, setDropdownOpen] = useState(false);
|
| 18 |
const [dropdownPinned, setDropdownPinned] = useState(false);
|
| 19 |
const [soundEnabled, setSoundEnabled] = useState(true);
|
| 20 |
+
const [navLoading, setNavLoading] = useState(false);
|
| 21 |
const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
| 22 |
|
| 23 |
const handleMouseEnter = () => {
|
|
|
|
| 154 |
<div className="flex items-center gap-3">
|
| 155 |
{!isAuthenticated ? (
|
| 156 |
<>
|
| 157 |
+
<button
|
| 158 |
+
onClick={() => { setNavLoading(true); router.push('/login'); }}
|
| 159 |
+
disabled={navLoading}
|
| 160 |
+
className="hidden sm:flex items-center gap-2 bg-transparent text-[var(--theme-text)] border border-[var(--theme-text)]/30 px-6 py-2 rounded-full text-[11px] font-bold tracking-tight hover:bg-[var(--theme-text)]/5 hover-scale disabled:opacity-60 transition-opacity"
|
| 161 |
+
>
|
| 162 |
+
{navLoading ? (
|
| 163 |
+
<svg className="animate-spin w-3 h-3" viewBox="0 0 24 24" fill="none">
|
| 164 |
+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"/>
|
| 165 |
+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"/>
|
| 166 |
+
</svg>
|
| 167 |
+
) : null}
|
| 168 |
Register
|
| 169 |
</button>
|
| 170 |
+
<button
|
| 171 |
+
onClick={() => { setNavLoading(true); router.push('/login'); }}
|
| 172 |
+
disabled={navLoading}
|
| 173 |
+
className="hidden sm:flex items-center gap-2 bg-[var(--theme-text)] text-[var(--theme-bg)] px-6 py-2 rounded-full text-[11px] font-bold tracking-tight hover:shadow-[0_0_20px_rgba(253,232,214,0.3)] hover-scale disabled:opacity-60 transition-opacity"
|
| 174 |
+
>
|
| 175 |
+
{navLoading ? (
|
| 176 |
+
<svg className="animate-spin w-3 h-3" viewBox="0 0 24 24" fill="none">
|
| 177 |
+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"/>
|
| 178 |
+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"/>
|
| 179 |
+
</svg>
|
| 180 |
+
) : null}
|
| 181 |
Login
|
| 182 |
</button>
|
| 183 |
</>
|
frontend/components/shared/PageLoader.tsx
CHANGED
|
@@ -22,6 +22,12 @@ export default function PageLoader({ children }: { children: React.ReactNode })
|
|
| 22 |
|
| 23 |
let progress = 0;
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
const interval = setInterval(() => {
|
| 26 |
if (progress < 90) {
|
| 27 |
progress += Math.random() * 15 + 5;
|
|
@@ -40,6 +46,9 @@ export default function PageLoader({ children }: { children: React.ReactNode })
|
|
| 40 |
|
| 41 |
return () => {
|
| 42 |
clearInterval(interval);
|
|
|
|
|
|
|
|
|
|
| 43 |
};
|
| 44 |
}, [pathname]);
|
| 45 |
|
|
|
|
| 22 |
|
| 23 |
let progress = 0;
|
| 24 |
|
| 25 |
+
// Safety net: force complete after 2500ms even if __PAGE_LOADED never fires
|
| 26 |
+
// (e.g. the 404 page, or any page that doesn't set the flag)
|
| 27 |
+
const safetyTimeout = setTimeout(() => {
|
| 28 |
+
(window as any).__PAGE_LOADED = true;
|
| 29 |
+
}, 2500);
|
| 30 |
+
|
| 31 |
const interval = setInterval(() => {
|
| 32 |
if (progress < 90) {
|
| 33 |
progress += Math.random() * 15 + 5;
|
|
|
|
| 46 |
|
| 47 |
return () => {
|
| 48 |
clearInterval(interval);
|
| 49 |
+
clearTimeout(safetyTimeout);
|
| 50 |
+
// Always reset the flag on cleanup so next page starts fresh
|
| 51 |
+
(window as any).__PAGE_LOADED = false;
|
| 52 |
};
|
| 53 |
}, [pathname]);
|
| 54 |
|