From b8f590708da19a1883116799a71900300e8a4265 Mon Sep 17 00:00:00 2001 From: isnowglobal-admin Date: Tue, 30 Jun 2026 02:15:53 -0400 Subject: [PATCH] feat: Auth-guard custom tickers and config endpoints --- client/src/components/symbol-selector.tsx | 40 +++++++++++++++-------- server/routes.ts | 12 +++---- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/client/src/components/symbol-selector.tsx b/client/src/components/symbol-selector.tsx index 9332fd0..0e86213 100644 --- a/client/src/components/symbol-selector.tsx +++ b/client/src/components/symbol-selector.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import type { SymbolInfo } from "@shared/schema"; import { useSymbol } from "@/lib/symbol-context"; +import { useAuth } from "@/lib/auth-context"; import { Select, SelectContent, @@ -14,14 +15,16 @@ import { import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; -import { Trash2, Plus, X } from "lucide-react"; +import { Trash2, Plus, X, Lock } from "lucide-react"; export function SymbolSelector() { const { symbol, setSymbol } = useSymbol(); + const { user } = useAuth(); const queryClient = useQueryClient(); const [newTicker, setNewTicker] = useState(""); const [newName, setNewName] = useState(""); const [showAdd, setShowAdd] = useState(false); + const isAuthenticated = !!user; const { data } = useQuery({ queryKey: ["/api/symbols"] }); const symbols = data ?? []; @@ -113,17 +116,21 @@ export function SymbolSelector() { {s.name} custom - + {isAuthenticated ? ( + + ) : ( + + )} ))} @@ -139,10 +146,17 @@ export function SymbolSelector() { variant="ghost" size="sm" className="h-7 text-xs gap-1" - onClick={() => setShowAdd(true)} + onClick={() => { + if (!isAuthenticated) { + window.location.hash = "/login"; + return; + } + setShowAdd(true); + }} > Add Symbol + {!isAuthenticated && } ) : (
diff --git a/server/routes.ts b/server/routes.ts index 45fc5c9..6436959 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -15,7 +15,7 @@ import { } from "./marketData"; import { oratsClient } from "./oratsClient"; import { tanukiClient } from "./tanukiClient"; -import { registerAuthRoutes } from "./authRoutes"; +import { registerAuthRoutes, requireAuth } from "./authRoutes"; import { initDb, getAllCustomSymbols, createCustomSymbol, deleteCustomSymbol } from "./db"; import { rateLimit, securityHeaders } from "./middleware"; @@ -113,7 +113,7 @@ export async function registerRoutes(httpServer: Server, app: Express): Promise< }); // Add a custom symbol - app.post("/api/symbols", async (req, res) => { + app.post("/api/symbols", requireAuth, async (req, res) => { const { ticker, name, type, sector } = req.body as Record; if (!ticker || !name) { return res.status(400).json({ error: "ticker and name are required" }); @@ -141,7 +141,7 @@ export async function registerRoutes(httpServer: Server, app: Express): Promise< }); // Delete a custom symbol - app.delete("/api/symbols/:ticker", async (req, res) => { + app.delete("/api/symbols/:ticker", requireAuth, async (req, res) => { const ticker = String(req.params.ticker || "").toUpperCase(); if (BUILTIN_TICKERS.has(ticker)) { return res.status(400).json({ error: "Cannot remove built-in symbol" }); @@ -168,7 +168,7 @@ export async function registerRoutes(httpServer: Server, app: Express): Promise< }); // Save ORATS API key + base URL to session - app.post("/api/orats/config", (req, res) => { + app.post("/api/orats/config", requireAuth, (req, res) => { const { apiKey, baseUrl } = req.body as Record; const session = req.session as Record; if (!apiKey || typeof apiKey !== "string") { @@ -185,7 +185,7 @@ export async function registerRoutes(httpServer: Server, app: Express): Promise< }); // Clear ORATS API key from session - app.delete("/api/orats/config", (req, res) => { + app.delete("/api/orats/config", requireAuth, (req, res) => { const session = req.session as Record; delete session.oratsApiKey; oratsClient.setApiKey(""); @@ -202,7 +202,7 @@ export async function registerRoutes(httpServer: Server, app: Express): Promise< }); // Save Tanuki config - app.post("/api/tanuki/config", (req, res) => { + app.post("/api/tanuki/config", requireAuth, (req, res) => { const { email, tvUser } = req.body as Record; if (!email || !tvUser) { return res.status(400).json({ error: "email and tvUser are required" });