Skip to content

Commit 6550617

Browse files
author
Uttam Adhikari
committed
Initial commit
0 parents  commit 6550617

37 files changed

+813
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
__pycache__/
2+
.env
3+
node_modules/
4+
backend/app/uploads/

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# FAT-EIBL — Finance Audit Tracker
2+
3+
Scaffold with AI (Vani) and features.

backend/.env.example

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
DATABASE_URL=postgresql+psycopg2://fatuser:fatpass@postgres:5432/fatdb
2+
SECRET_KEY=replace-with-a-secure-secret
3+
ACCESS_TOKEN_EXPIRE_MINUTES=60
4+
UPLOAD_DIR=/app/uploads
5+
DEFAULT_ADMIN_EMAIL=[email protected]
6+
DEFAULT_ADMIN_PASSWORD=123
7+
8+
SMTP_HOST=smtp.example.com
9+
SMTP_PORT=587
10+
SMTP_USER=
11+
SMTP_PASS=
12+
OPENAI_API_KEY=
13+
OPENAI_MODEL=gpt-4o-mini
14+
OPENAI_TEMPERATURE=0.2

backend/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM python:3.11-slim
2+
WORKDIR /app
3+
COPY requirements.txt ./
4+
RUN pip install --no-cache-dir -r requirements.txt
5+
COPY . /app
6+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

backend/app/__init__.py

Whitespace-only changes.

backend/app/audit.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from .database import SessionLocal
2+
from .models import AuditLog
3+
4+
def record_audit(actor: str, action: str, resource: str = None, summary: str = None, payload: dict = None):
5+
db = SessionLocal()
6+
try:
7+
log = AuditLog(actor=actor, action=action, resource=resource, summary=summary, payload=payload)
8+
db.add(log)
9+
db.commit()
10+
finally:
11+
db.close()

backend/app/auth_utils.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from fastapi import Depends, HTTPException, status
2+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
3+
from jose import jwt, JWTError
4+
import os
5+
from .database import SessionLocal
6+
from . import models
7+
8+
security = HTTPBearer()
9+
10+
def get_current_username_from_token(token: str):
11+
secret = os.getenv('SECRET_KEY', 'dev-secret')
12+
try:
13+
payload = jwt.decode(token, secret, algorithms=['HS256'])
14+
return payload.get('sub')
15+
except JWTError:
16+
return None
17+
18+
def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
19+
token = credentials.credentials
20+
username = get_current_username_from_token(token)
21+
if not username:
22+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
23+
db = SessionLocal()
24+
user = db.query(models.User).filter(models.User.username == username).first()
25+
db.close()
26+
if not user:
27+
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
28+
return user
29+
30+
def require_roles(*roles):
31+
def _require(user = Depends(get_current_user)):
32+
user_roles = [r.name for r in getattr(user, 'roles', [])]
33+
for r in roles:
34+
if r in user_roles:
35+
return user
36+
raise HTTPException(status_code=403, detail="Insufficient role")
37+
return _require

backend/app/crud.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from sqlalchemy.orm import Session
2+
from . import models, schemas
3+
from .security import hash_password, verify_password
4+
5+
def create_user(db: Session, user: schemas.UserCreate):
6+
hashed = hash_password(user.password)
7+
db_user = models.User(username=user.username, hashed_password=hashed, role=user.role)
8+
db.add(db_user)
9+
db.commit()
10+
db.refresh(db_user)
11+
return db_user
12+
13+
def get_user_by_username(db: Session, username: str):
14+
return db.query(models.User).filter(models.User.username==username).first()
15+
16+
def authenticate_user(db: Session, username: str, password: str):
17+
user = get_user_by_username(db, username)
18+
if not user:
19+
return None
20+
if not verify_password(password, user.hashed_password):
21+
return None
22+
return user
23+
24+
def create_audit_request(db: Session, req: schemas.AuditRequestCreate, filename: str | None = None):
25+
db_req = models.AuditRequest(
26+
title=req.title,
27+
description=req.description,
28+
owner=req.owner,
29+
due_date=req.due_date,
30+
attachment=filename,
31+
)
32+
db.add(db_req)
33+
db.commit()
34+
db.refresh(db_req)
35+
return db_req
36+
37+
def list_audit_requests(db: Session, skip: int = 0, limit: int = 100):
38+
return db.query(models.AuditRequest).offset(skip).limit(limit).all()

backend/app/database.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from sqlalchemy import create_engine
2+
from sqlalchemy.ext.declarative import declarative_base
3+
from sqlalchemy.orm import sessionmaker
4+
import os
5+
from dotenv import load_dotenv
6+
7+
load_dotenv()
8+
DATABASE_URL = os.getenv('DATABASE_URL')
9+
10+
engine = create_engine(DATABASE_URL)
11+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
12+
Base = declarative_base()
13+
14+
def get_db():
15+
db = SessionLocal()
16+
try:
17+
yield db
18+
finally:
19+
db.close()

backend/app/main.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from fastapi import FastAPI
2+
from fastapi.staticfiles import StaticFiles
3+
from fastapi.openapi.docs import get_swagger_ui_html
4+
from .database import engine, Base
5+
from .routes import auth, audit_requests, imports, reports, ai, seed, roles, audit_logs
6+
from .scheduler import start_scheduler
7+
8+
Base.metadata.create_all(bind=engine)
9+
10+
app = FastAPI(title="FAT-EIBL")
11+
12+
app.mount('/static', StaticFiles(directory='static'), name='static')
13+
14+
app.include_router(auth.router)
15+
app.include_router(audit_requests.router)
16+
app.include_router(imports.router)
17+
app.include_router(reports.router)
18+
app.include_router(ai.router)
19+
app.include_router(seed.router)
20+
app.include_router(roles.router)
21+
app.include_router(audit_logs.router)
22+
23+
start_scheduler()
24+
25+
@app.get('/')
26+
def root():
27+
return {'message': 'FAT-EIBL - API'}
28+
29+
@app.get('/docs', include_in_schema=False)
30+
def custom_swagger_ui_html():
31+
return get_swagger_ui_html(openapi_url=app.openapi_url, title=app.title + ' - Docs', swagger_favicon_url='/static/logo.svg')

0 commit comments

Comments
 (0)