Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/backend/app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def create_app(settings: Settings | None = None) -> Flask:
logger = logging.getLogger("finmind")
logger.info("Starting FinMind backend with log level %s", log_level)

# Extensions
# Extensions\n from .extensions import init_redis\n init_redis(cfg.redis_url)
db.init_app(app)
jwt.init_app(app)
app.extensions["observability"] = Observability()
Expand Down
15 changes: 15 additions & 0 deletions packages/backend/app/db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,18 @@ CREATE TABLE IF NOT EXISTS audit_logs (
action VARCHAR(100) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);


-- GDPR Deletion Requests
CREATE TABLE IF NOT EXISTS deletion_requests (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
requested_at TIMESTAMP NOT NULL DEFAULT NOW(),
scheduled_deletion_date TIMESTAMP NOT NULL,
ip_address VARCHAR(45),
user_agent VARCHAR(500),
cancelled BOOLEAN NOT NULL DEFAULT FALSE,
cancelled_at TIMESTAMP,
cancellation_ip VARCHAR(45)
);
CREATE INDEX IF NOT EXISTS idx_deletion_requests_user ON deletion_requests(user_id, cancelled);
33 changes: 30 additions & 3 deletions packages/backend/app/extensions.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_extended import JWTManager
import redis
from .config import Settings


db = SQLAlchemy()
jwt = JWTManager()

_settings = Settings()
redis_client = redis.Redis.from_url(_settings.redis_url, decode_responses=True)
# Lazy-initialized Redis client
_redis_client = None
_redis_url = None


def init_redis(redis_url: str):
"""Initialize Redis client with the given URL."""
global _redis_client, _redis_url
_redis_url = redis_url
_redis_client = redis.Redis.from_url(redis_url, decode_responses=True)


def get_redis():
"""Get the Redis client, initializing with default if needed."""
global _redis_client
if _redis_client is None:
from .config import Settings
_settings = Settings()
init_redis(_settings.redis_url)
return _redis_client


# For backward compatibility, expose redis_client as a property-like getter
class RedisClientProxy:
"""Proxy that delegates to the actual Redis client."""
def __getattr__(self, name):
return getattr(get_redis(), name)


redis_client = RedisClientProxy()
14 changes: 14 additions & 0 deletions packages/backend/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,17 @@ class AuditLog(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
action = db.Column(db.String(100), nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)


class DeletionRequest(db.Model):
"""Track deletion requests for grace period management."""
__tablename__ = "deletion_requests"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
requested_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
scheduled_deletion_date = db.Column(db.DateTime, nullable=False)
ip_address = db.Column(db.String(45), nullable=True)
user_agent = db.Column(db.String(500), nullable=True)
cancelled = db.Column(db.Boolean, default=False, nullable=False)
cancelled_at = db.Column(db.DateTime, nullable=True)
cancellation_ip = db.Column(db.String(45), nullable=True)
2 changes: 2 additions & 0 deletions packages/backend/app/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .categories import bp as categories_bp
from .docs import bp as docs_bp
from .dashboard import bp as dashboard_bp
from .gdpr import bp as gdpr_bp


def register_routes(app: Flask):
Expand All @@ -18,3 +19,4 @@ def register_routes(app: Flask):
app.register_blueprint(categories_bp, url_prefix="/categories")
app.register_blueprint(docs_bp, url_prefix="/docs")
app.register_blueprint(dashboard_bp, url_prefix="/dashboard")
app.register_blueprint(gdpr_bp, url_prefix="/gdpr")
Loading