diff --git a/backend/app/__pycache__/main.cpython-313.pyc b/backend/app/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000..cd42198 Binary files /dev/null and b/backend/app/__pycache__/main.cpython-313.pyc differ diff --git a/backend/app/api/__pycache__/auth.cpython-313.pyc b/backend/app/api/__pycache__/auth.cpython-313.pyc new file mode 100644 index 0000000..df4a7b4 Binary files /dev/null and b/backend/app/api/__pycache__/auth.cpython-313.pyc differ diff --git a/backend/app/api/__pycache__/scanner.cpython-313.pyc b/backend/app/api/__pycache__/scanner.cpython-313.pyc new file mode 100644 index 0000000..75629f8 Binary files /dev/null and b/backend/app/api/__pycache__/scanner.cpython-313.pyc differ diff --git a/backend/app/api/auth.py b/backend/app/api/auth.py new file mode 100644 index 0000000..b94b240 --- /dev/null +++ b/backend/app/api/auth.py @@ -0,0 +1,106 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from fastapi.security import OAuth2PasswordBearer +from sqlalchemy.orm import Session +from pydantic import BaseModel +from jose import JWTError, jwt + +from app.db.database import get_db +from app.db.models import User +from app.core.security import ( + public_key, decrypt_password, verify_password, + get_password_hash, create_access_token +) +from app.core.config import SECRET_KEY, ALGORITHM + +router = APIRouter() +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/auth/login") + +class AuthRequest(BaseModel): + username: str + password: str # Base64 encoded RSA-encrypted password + +class ProfileUpdateRequest(BaseModel): + display_name: str = None + email_id: str = None + mobile_no: str = None + gender: str = None + password: str = None # Base64 encoded RSA-encrypted new password + +@router.get("/public-key") +def get_public_key(): + return {"public_key": public_key.decode("utf-8")} + +@router.post("/signup") +def signup(req: AuthRequest, db: Session = Depends(get_db)): + db_user = db.query(User).filter(User.username == req.username).first() + if db_user: + raise HTTPException(status_code=400, detail="Username already registered") + + # Decrypt password + decrypted_password = decrypt_password(req.password) + hashed_password = get_password_hash(decrypted_password) + + new_user = User(username=req.username, hashed_password=hashed_password) + db.add(new_user) + db.commit() + return {"message": "User created successfully"} + +@router.post("/login") +def login(req: AuthRequest, db: Session = Depends(get_db)): + db_user = db.query(User).filter(User.username == req.username).first() + if not db_user: + raise HTTPException(status_code=400, detail="Invalid credentials") + + decrypted_password = decrypt_password(req.password) + if not verify_password(decrypted_password, db_user.hashed_password): + raise HTTPException(status_code=400, detail="Invalid credentials") + + access_token = create_access_token(data={"sub": db_user.username}) + return {"access_token": access_token, "token_type": "bearer"} + +def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)): + credentials_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + username: str = payload.get("sub") + if username is None: + raise credentials_exception + except JWTError: + raise credentials_exception + + user = db.query(User).filter(User.username == username).first() + if user is None: + raise credentials_exception + return user + +@router.get("/me") +def get_me(current_user: User = Depends(get_current_user)): + return { + "username": current_user.username, + "display_name": current_user.display_name, + "email_id": current_user.email_id, + "mobile_no": current_user.mobile_no, + "gender": current_user.gender + } + +@router.put("/update-profile") +def update_profile(req: ProfileUpdateRequest, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): + if req.display_name is not None: + current_user.display_name = req.display_name + if req.email_id is not None: + current_user.email_id = req.email_id + if req.mobile_no is not None: + current_user.mobile_no = req.mobile_no + if req.gender is not None: + current_user.gender = req.gender + if req.password: + decrypted_password = decrypt_password(req.password) + hashed_password = get_password_hash(decrypted_password) + current_user.hashed_password = hashed_password + + db.commit() + return {"message": "Profile updated successfully"} diff --git a/backend/app/api/scanner.py b/backend/app/api/scanner.py new file mode 100644 index 0000000..6283430 --- /dev/null +++ b/backend/app/api/scanner.py @@ -0,0 +1,27 @@ +from fastapi import APIRouter, Depends +from app.api.auth import get_current_user +from app.services.stock_engine import ( + get_scan, get_ribbon, get_smartmoney, get_trend, get_marketintel +) + +router = APIRouter() + +@router.get("/scan") +def scan(current_user = Depends(get_current_user)): + return get_scan() + +@router.get("/ribbon") +def ribbon(current_user = Depends(get_current_user)): + return get_ribbon() + +@router.get("/smartmoney") +def smartmoney(current_user = Depends(get_current_user)): + return get_smartmoney() + +@router.get("/trend") +def trend(current_user = Depends(get_current_user)): + return get_trend() + +@router.get("/marketintel") +def marketintel(current_user = Depends(get_current_user)): + return get_marketintel() diff --git a/backend/app/core/__pycache__/config.cpython-313.pyc b/backend/app/core/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000..cab9925 Binary files /dev/null and b/backend/app/core/__pycache__/config.cpython-313.pyc differ diff --git a/backend/app/core/__pycache__/security.cpython-313.pyc b/backend/app/core/__pycache__/security.cpython-313.pyc new file mode 100644 index 0000000..12987d2 Binary files /dev/null and b/backend/app/core/__pycache__/security.cpython-313.pyc differ diff --git a/backend/app/core/config.py b/backend/app/core/config.py new file mode 100644 index 0000000..3aa511d --- /dev/null +++ b/backend/app/core/config.py @@ -0,0 +1,6 @@ +import os + +DATABASE_URL = "postgresql://postgres:M%40triXPostgr3s%406202@103.125.129.116:5333/stock_scanner" +SECRET_KEY = "stock-scanner-super-secret-key-12345" +ALGORITHM = "HS256" +ACCESS_TOKEN_EXPIRE_MINUTES = 60 diff --git a/backend/app/core/security.py b/backend/app/core/security.py new file mode 100644 index 0000000..1bfceb6 --- /dev/null +++ b/backend/app/core/security.py @@ -0,0 +1,61 @@ +import os +import base64 +from datetime import datetime, timedelta +from typing import Optional +from jose import JWTError, jwt +import bcrypt +from Crypto.PublicKey import RSA +from Crypto.Cipher import PKCS1_OAEP +from fastapi import HTTPException + +from app.core.config import SECRET_KEY, ALGORITHM + +RSA_PRIVATE_KEY_PATH = "private.pem" +RSA_PUBLIC_KEY_PATH = "public.pem" + +if not os.path.exists(RSA_PRIVATE_KEY_PATH) or not os.path.exists(RSA_PUBLIC_KEY_PATH): + key = RSA.generate(2048) + private_key = key.export_key() + with open(RSA_PRIVATE_KEY_PATH, "wb") as file_out: + file_out.write(private_key) + + public_key = key.publickey().export_key() + with open(RSA_PUBLIC_KEY_PATH, "wb") as file_out: + file_out.write(public_key) +else: + with open(RSA_PRIVATE_KEY_PATH, "rb") as f: + private_key = f.read() + with open(RSA_PUBLIC_KEY_PATH, "rb") as f: + public_key = f.read() + +rsa_private_key = RSA.import_key(private_key) +cipher_rsa = PKCS1_OAEP.new(rsa_private_key) + +def decrypt_password(encrypted_b64_password: str) -> str: + try: + encrypted_bytes = base64.b64decode(encrypted_b64_password) + decrypted = cipher_rsa.decrypt(encrypted_bytes) + return decrypted.decode("utf-8") + except Exception as e: + raise HTTPException(status_code=400, detail="Invalid RSA payload or decryption failed") + +def verify_password(plain_password, hashed_password): + if isinstance(plain_password, str): + plain_password = plain_password.encode('utf-8') + if isinstance(hashed_password, str): + hashed_password = hashed_password.encode('utf-8') + return bcrypt.checkpw(plain_password, hashed_password) + +def get_password_hash(password): + if isinstance(password, str): + password = password.encode('utf-8') + return bcrypt.hashpw(password, bcrypt.gensalt()).decode('utf-8') + +def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): + to_encode = data.copy() + if expires_delta: + expire = datetime.utcnow() + expires_delta + else: + expire = datetime.utcnow() + timedelta(minutes=15) + to_encode.update({"exp": expire}) + return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) diff --git a/backend/app/db/__pycache__/database.cpython-313.pyc b/backend/app/db/__pycache__/database.cpython-313.pyc new file mode 100644 index 0000000..6679c7b Binary files /dev/null and b/backend/app/db/__pycache__/database.cpython-313.pyc differ diff --git a/backend/app/db/__pycache__/models.cpython-313.pyc b/backend/app/db/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000..7491bf3 Binary files /dev/null and b/backend/app/db/__pycache__/models.cpython-313.pyc differ diff --git a/backend/app/db/database.py b/backend/app/db/database.py new file mode 100644 index 0000000..c97b2de --- /dev/null +++ b/backend/app/db/database.py @@ -0,0 +1,15 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from app.core.config import DATABASE_URL + +engine = create_engine(DATABASE_URL) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) +Base = declarative_base() + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() diff --git a/backend/app/db/models.py b/backend/app/db/models.py new file mode 100644 index 0000000..3ae74a9 --- /dev/null +++ b/backend/app/db/models.py @@ -0,0 +1,13 @@ +from sqlalchemy import Column, Integer, String +from app.db.database import Base + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + username = Column(String, unique=True, index=True) + hashed_password = Column(String) + display_name = Column(String, nullable=True) + email_id = Column(String, nullable=True) + mobile_no = Column(String, nullable=True) + gender = Column(String, nullable=True) diff --git a/backend/app/main.py b/backend/app/main.py new file mode 100644 index 0000000..dfc1fe3 --- /dev/null +++ b/backend/app/main.py @@ -0,0 +1,26 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from app.api import auth, scanner +from app.db.database import engine, Base + +# Initialize the database tables +Base.metadata.create_all(bind=engine) + +app = FastAPI(title="Stock Scanner Pro API") + +# Configure CORS for frontend access +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Include Routers +app.include_router(auth.router, prefix="/api/auth", tags=["auth"]) +app.include_router(scanner.router, prefix="/api/scanner", tags=["scanner"]) + +@app.get("/") +def read_root(): + return {"message": "Stock Scanner Backend is running."} diff --git a/backend/app/services/__pycache__/stock_engine.cpython-313.pyc b/backend/app/services/__pycache__/stock_engine.cpython-313.pyc new file mode 100644 index 0000000..2136e4c Binary files /dev/null and b/backend/app/services/__pycache__/stock_engine.cpython-313.pyc differ diff --git a/server.py b/backend/app/services/stock_engine.py similarity index 94% rename from server.py rename to backend/app/services/stock_engine.py index 01b7e56..83ed439 100644 --- a/server.py +++ b/backend/app/services/stock_engine.py @@ -1,20 +1,8 @@ -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware import yfinance as yf import pandas as pd import numpy as np from concurrent.futures import ThreadPoolExecutor -app = FastAPI() - -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - # ✅ PERSONAL # ✅ PERSONAL personal_stocks = [ @@ -437,8 +425,7 @@ def analyze(stock): # ========================================= # ✅ API - FAST SCAN # ========================================= -@app.get("/scan") -def scan(): +def get_scan(): sector_output = {} all_stocks = [] @@ -549,8 +536,7 @@ def analyze_ribbon(stock): return None -@app.get("/ribbon") -def ribbon(): +def get_ribbon(): results = [] @@ -561,8 +547,7 @@ def ribbon(): return results -@app.get("/smartmoney") -def smartmoney(): +def get_smartmoney(): results = [] @@ -590,8 +575,7 @@ def smartmoney(): "all": results } -@app.get("/trend") -def trend(): +def get_trend(): results = [] @@ -623,8 +607,7 @@ def trend(): # - Full sector_map (too large) # - Deep MACD + Supertrend # - 6 month data calls -@app.get("/marketintel") -def marketintel(): +def get_marketintel(): try: nifty_news = yf.Ticker("^NSEI").news or [] reliance_news = yf.Ticker("RELIANCE.NS").news or [] diff --git a/backend/private.pem b/backend/private.pem new file mode 100644 index 0000000..b1af8f6 --- /dev/null +++ b/backend/private.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtqxw/s/eCLosxX61Zvd0PYicYUoKjbn6gsxe+jb4p0hMNK36 +YsOqYqRPKIh7OosX9CaKDDn8ws6LUt5MAOjwTkNBF8P3bPJ+R+qVXeiqFBIAkRNX +VqxU8eoWNaoNDcNQWSxOIJJanv6UZHbxlv04WLtw5WUtYUbLwO2TcAYghmzk/UTw +p0YjQ0kw6O2E7iQ3i/H7aNqKlEoZoXHcsnDaUsIa3qy6LG7UkGz0LQ3RKu9RqoVO +I8DOV8i1lO3Z5SERiSulfhqzoaD2vWU/sXBRC+yJ3UxZvqIUpR6taToZxPL0aZG9 +XoxFcS2ywogI1XGKWJAYxosgbA8KxKilQFs+EQIDAQABAoIBAE2cThW0sxz6HHN8 +Ng4dLGHIXMho8LruPSS9N80O9e38pYPsNuknQIjQTmFmOxTQa8jrZtNy/5S0tDTt +BVWNoiteH5W9SK4dCNH9NKDFbR1B2MPKd9z3Ms/lKLJ8ITert8NrM3ZbN+09NEbK +0jHYO8xXNsv/hJxDu+WoCnAZ6k+x1IFDXRfV4MrTBX27t4xtfA5KeXQyPzotUeBn +VjVIoRqkCSIJLr+f6ATa+OHlfBjyTbBQscG1/0JNfDDQFmvOYyyc4zpVn0bAINw7 +klvJaXP++DRaZsvNnM1SGpsJ+U86O56ODkX4v2a+H3czhpOA+q0yePHQ/nWr73Nf +7WQYxdECgYEA1OVJZoNPxqVefooY9Tm5mDRXeRVdOormEI8GZkrIAG85w+MD9MMr +2s8FjXDniN4khSClpF9YunB8e77ufu0TgpqhHhXSnn/5JEQ4UO5wiO1EqBvhkFUJ +CXitjYehgwDc5WKvUfPxeMCMvF+rYoHhN1aYIczuN36fhDAXA6pxGyMCgYEA26iy +2RHgu0XNa37Z9KeJCDGNtpj2dYttRAua4UVudxPYn+Q2IdKZ9HPjgWPKgdUjAEup +uuIDhbk1zmQiquvyNMP0HX+p2yG8DX77cdajyvBG5p3BxUX1MxXx4Q6ICVAr4K51 +cZg3YWsIsCsCCO7DkgW1kFRlVptUMMmbJjNoXzsCgYAE32qaqg69YTOUedywYC3b +SfdmkhKcMGmrn1pqJPQG7oTH8v44L+9lBq/92MOz4kG7uk+QP45sVf7DZk9XIF39 +80QUyDMV5Z/yMI2JbKuutp+HqXu0Lf4S9WwjfSM5OF/V8DhLC+ZO+Tk/ZoEptAdP +mO/KdkJNitxjziX4s4H7OQKBgB77oJ51oxlHMz5iWiPkLbP2KWMEGF9kFzlt2Z7E +yFwLdJa4/dmvdv/ACOsLRFkj0xgLlBlEH/MQuMIv5aPuO++tZBV1GGRMUdYlfxoD +iH7rfVSyE87bm0ZlZgS0pAOMR2Qdt3saWVVoX4VZy6Ou6e8C1yVQgirBJhLrnPK6 +dZJZAoGBAK/Nob7Lrxi2kXuJbUOp+Jcir/sPeWE0ZPH9vnAdDkDLrUFbyNWJHHgI +tn1rrPCJeF3+gGdbKcrubHGxJMZGDh3qALs35L6195ZaGys6l+Imp1FxsSFROf/S +Hy2wbjf7fz/gb0U7dfD7ZtVPDvqdFax7NrEdvdcslA0IUiVPqGkC +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/backend/public.pem b/backend/public.pem new file mode 100644 index 0000000..830f7ee --- /dev/null +++ b/backend/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtqxw/s/eCLosxX61Zvd0 +PYicYUoKjbn6gsxe+jb4p0hMNK36YsOqYqRPKIh7OosX9CaKDDn8ws6LUt5MAOjw +TkNBF8P3bPJ+R+qVXeiqFBIAkRNXVqxU8eoWNaoNDcNQWSxOIJJanv6UZHbxlv04 +WLtw5WUtYUbLwO2TcAYghmzk/UTwp0YjQ0kw6O2E7iQ3i/H7aNqKlEoZoXHcsnDa +UsIa3qy6LG7UkGz0LQ3RKu9RqoVOI8DOV8i1lO3Z5SERiSulfhqzoaD2vWU/sXBR +C+yJ3UxZvqIUpR6taToZxPL0aZG9XoxFcS2ywogI1XGKWJAYxosgbA8KxKilQFs+ +EQIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f00c45b..4d48ef8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,10 @@ "@testing-library/user-event": "^13.5.0", "axios": "^1.18.1", "lightweight-charts": "^5.2.0", + "node-forge": "^1.4.0", "react": "^19.2.7", "react-dom": "^19.2.7", - "react-router-dom": "^7.18.0", + "react-router-dom": "^7.18.2", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" } @@ -13958,9 +13959,9 @@ } }, "node_modules/react-router": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.0.tgz", - "integrity": "sha512-pTTGt8J+ji1NOmYnjzT+bAJy/1zD+Jp4ziO6cL7T3ZLvXKtusO7BpFqlRXitqpcPVqllsIXFHRMt+2/k3Xn6HQ==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz", + "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -13980,12 +13981,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.0.tgz", - "integrity": "sha512-Fi0yY6kgtKae/Th2xibdWK0KSdYZ4B53Gyf6wRtomOKWgpNm7H7+DyfDhncdz9FKbpS+1jmDhg3F4WoGJ+yFOA==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz", + "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", "license": "MIT", "dependencies": { - "react-router": "7.18.0" + "react-router": "7.18.2" }, "engines": { "node": ">=20.0.0" diff --git a/package.json b/package.json index 0355f93..1724a3c 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,10 @@ "@testing-library/user-event": "^13.5.0", "axios": "^1.18.1", "lightweight-charts": "^5.2.0", + "node-forge": "^1.4.0", "react": "^19.2.7", "react-dom": "^19.2.7", - "react-router-dom": "^7.18.0", + "react-router-dom": "^7.18.2", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, diff --git a/private.pem b/private.pem new file mode 100644 index 0000000..b87066d --- /dev/null +++ b/private.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAuUUB5HsRK6mvKyjAClLckNxDK69MZTW2Yn9qOJZvw3j0R4sl +IhoyBRAm1fxFYthj+klxqw0XKUR7F6DcDuK+3z+a+Ds/uP3tDYbnfT4cr9VEhgq9 +FoLi7AgfH98mqp4VkqDCuYubt4XRtfhpN0TuShT/xImmqLfxkmvZSP/syPoFytEq +X66QZ2QyT4gqzz9/ZMo7EFMeuKA44IBv6qeQzFxSBt0xpeyMXHChn8AeocaOySC3 +kyZBefzmUF2lkOyZKlaPC+0/mUb6ndT1OBfK+EZXzWEbugBWOg/NSy9pnzPRGjAY +qknWs1912+H7t2FEA8BCRZCOK85B05BYeZM+SwIDAQABAoIBAAdhNs6Vkl0FYMtc +z615mHySsYBDds0PQykQPzhq0Z/dKQnSgpOlrHlkJ8/dQRxLV0woccUo+9OyfR63 +UxyqroSxAWjC32psfu1LgtwLxdPBMH4QXnDPnN9fAXJhDqa8LkrNaYQ/id/ZDj3r +27EOZ1l/FaMMAp302cQF1L1iKFnm/ksN9s4G2+8UwrtUWum5LOeD6x22H8443uRf +Lg4IvMC4nLYCQRUAeaKJoTZnw8KeOmKpF2opfu8L1vQ8ubYoZqBX+gNP6GflFG6j +zgoNWfZvRQnDfofH+ZOPcB7vbVKcXKFZ0kHzfw40Nq49GxZndsK30xCUtdDiW7eE +Mgfv9EUCgYEA1q/HUTKBFpvKGu1BwCVTIdvqI0qScWpYst525CbeI36MCkP6ReCX +kvi7259saMRdAaEhIj8EN3BuDu5k8jjNRqkAnkq0pNHkYD3lzXBc4H3RC0LdmEmn +cUUS16sLSkcciI3+SnGjZUbCjso2r2Sy1d/iPhR7L2QpmlzqtPNZxr0CgYEA3OwK +NJMNN1ARv+aOka+mxn6+MsbbWWB0CLSbhg5WRRnmsefDMBjtWsRr+/ufQO7rC+q1 +rv83jiCgJl3u5idS4Hrm/k77iZZzrvJruuR5W5GgvqMTcdut7l4pdqSlvV96D4z7 +29/sze3vVoZv4zIeWxj5QDeT7xLwvjoZpUbHDacCgYAsCjsVCQs6HBNFms4WIJIB +LB/HxZBs+6feaYxyGRcQqPEJWhCJLR1q5OOElhujEkUSBH/LiqnOxZ2OKpFCryxN +BnY+Ao00EmqK46e0kQw8cRLlAH58sv9KWSUYYNocDqJn0NkNZGpkaDOZHxpAuKOH +BDphCcqLWjy+kbkEDbeo8QKBgQCD9m7GJsyvLKHdmi+xMFYTnWOpWwVtZuMIzDFW +Kzw2/JjDzifWlB07qbbDBvOCyvQV4zZxeLvLpwtiv5tTWUv1ERTn9W/lKLyjVOUq +9wzSuLNnDGwyB8Hmb9KerwzdiKmVnmZXWXPPMoBTk+xDrw1Y5xsD0+8G0K6DQptN +EXEXYwKBgQDS5DR5IDGvTycguDhU1QGSW0iEzMfBlZftKAucB/KYcQj8A4+AECKM +9SqcTI2Aneou+7PubpCFDSEckSKZkxb6ibw/ynA1EE55wyjuctyHTpqeHJjoK7jS +R5hyVL5iFBwGxv6oiWeKld+ZbDSB+i89km+kARaXjIB2gZmkepAIyA== +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/public.pem b/public.pem new file mode 100644 index 0000000..bea9d69 --- /dev/null +++ b/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuUUB5HsRK6mvKyjAClLc +kNxDK69MZTW2Yn9qOJZvw3j0R4slIhoyBRAm1fxFYthj+klxqw0XKUR7F6DcDuK+ +3z+a+Ds/uP3tDYbnfT4cr9VEhgq9FoLi7AgfH98mqp4VkqDCuYubt4XRtfhpN0Tu +ShT/xImmqLfxkmvZSP/syPoFytEqX66QZ2QyT4gqzz9/ZMo7EFMeuKA44IBv6qeQ +zFxSBt0xpeyMXHChn8AeocaOySC3kyZBefzmUF2lkOyZKlaPC+0/mUb6ndT1OBfK ++EZXzWEbugBWOg/NSy9pnzPRGjAYqknWs1912+H7t2FEA8BCRZCOK85B05BYeZM+ +SwIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/src/App.js b/src/App.js index c7f4f07..b10f14d 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,6 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; +import { BrowserRouter as Router, Routes, Route, useNavigate } from "react-router-dom"; +import axios from "axios"; import Chart from "./components/Chart"; import Scanner from "./components/Scanner"; import Ribbon from "./components/Ribbon"; @@ -7,52 +9,76 @@ import Popup from "./components/Popup"; import AboutScanner from "./components/AboutScanner"; import MarketIntel from "./components/MarketIntel"; import TrendScanner from "./components/TrendScanner"; +import Login from "./components/Login"; +import Signup from "./components/Signup"; +import ProtectedRoute from "./components/ProtectedRoute"; +import Profile from "./components/Profile"; import "./index.css"; -function App() { +function Dashboard() { const [selectedStock, setSelectedStock] = useState("NSE:RELIANCE"); const [signal, setSignal] = useState(null); const [view, setView] = useState("scanner"); + const [userProfile, setUserProfile] = useState(null); + const [dropdownOpen, setDropdownOpen] = useState(false); + const navigate = useNavigate(); + + const fetchProfile = () => { + axios.get("http://127.0.0.1:8000/api/auth/me") + .then(res => setUserProfile(res.data)) + .catch(err => { + if (err.response?.status === 401) handleLogout(); + }); + }; + + useEffect(() => { + fetchProfile(); + }, []); + + const handleLogout = () => { + localStorage.removeItem("token"); + navigate("/login"); + }; return (
Don't have an account? Sign up
+Manage your personal details and security
+Already have an account? Login
+