July 6 - Optimize codes

This commit is contained in:
2026-07-06 17:18:29 -04:00
parent dc014ace13
commit 2c1d9f5e77
6 changed files with 64 additions and 131 deletions
+10 -2
View File
@@ -6,6 +6,7 @@ from flask_mail import Mail
from flask_wtf.csrf import CSRFProtect
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from werkzeug.middleware.proxy_fix import ProxyFix
from config import config
import os
import logging
@@ -30,6 +31,15 @@ def create_app(config_name='default'):
app = Flask(__name__)
app.config.from_object(config[config_name])
# ── Reverse-proxy awareness (Nginx) ──────────────────────────────────────
# Nginx terminates TLS and forwards requests over the loopback interface,
# setting X-Forwarded-For / X-Forwarded-Proto / X-Forwarded-Host. Without
# ProxyFix, request.remote_addr is always 127.0.0.1, which collapses every
# Flask-Limiter key into a single shared bucket (rate limits become global
# instead of per-client) and makes url_for(_external) emit http:// links.
# x_for=1 trusts exactly one proxy hop — our own Nginx.
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
db.init_app(app)
login_manager.init_app(app)
migrate.init_app(app, db)
@@ -199,7 +209,6 @@ def create_app(config_name='default'):
from app.api.notifications import bp as _api_notifications_bp
from app.api.stats import bp as _api_stats_bp
from app.api.comments import bp as _api_comments_bp
from app.api.devices import bp as _api_devices_bp
csrf.exempt(_api_auth_bp)
csrf.exempt(_api_facilities_bp)
csrf.exempt(_api_templates_bp)
@@ -209,7 +218,6 @@ def create_app(config_name='default'):
csrf.exempt(_api_notifications_bp)
csrf.exempt(_api_stats_bp)
csrf.exempt(_api_comments_bp)
csrf.exempt(_api_devices_bp)
register_api(app)
# ── Security response headers ─────────────────────────────────────────
-3
View File
@@ -50,7 +50,4 @@ def register_api(app):
from app.api.comments import bp as comments_bp
api_bp.register_blueprint(comments_bp)
from app.api.devices import bp as devices_bp
api_bp.register_blueprint(devices_bp)
app.register_blueprint(api_bp)
-95
View File
@@ -1,95 +0,0 @@
"""
app/api/devices.py
------------------
Mobile API endpoint for device registration.
POST /api/v1/devices/register
Upserts a device record for the authenticated user.
Called on every app foreground (active scenePhase) so last_seen_at
stays current and the admin can identify stale / outdated installs.
Request JSON
------------
{
"device_id": "stable-uuid-from-keychain", // required
"device_name": "Nguyen's iPad", // UIDevice.current.name
"app_version": "1.2.0", // CFBundleShortVersionString
"ios_version": "18.3.1" // UIDevice.current.systemVersion
}
Response 200
------------
{ "ok": true, "data": { "registered": true } }
"""
import logging
from flask import Blueprint, request, g
from app import db
from app.models.device_registration import DeviceRegistration
from app.api.errors import api_ok, api_error
from app.api.decorators import jwt_required
from app.utils.audit import log_action, ACTION_UPDATE, ACTION_CREATE
from app.utils.time_utils import now_eastern
logger = logging.getLogger(__name__)
bp = Blueprint('api_devices', __name__)
_ALLOWED_ROLES = {'admin', 'director', 'inspector', 'project_manager'}
@bp.route('/devices/register', methods=['POST'])
@jwt_required
def register_device():
user = g.api_user
if user.role not in _ALLOWED_ROLES:
return api_error('Access denied', 403)
data = request.get_json(silent=True) or {}
device_id = (data.get('device_id') or '').strip()
device_name = (data.get('device_name') or '').strip()[:255]
app_version = (data.get('app_version') or '').strip()[:32]
ios_version = (data.get('ios_version') or '').strip()[:32]
if not device_id:
return api_error('device_id is required', 400)
if len(device_id) > 64:
return api_error('device_id too long', 400)
now = now_eastern()
existing = DeviceRegistration.query.filter_by(device_id=device_id).first()
if existing:
# Update — always refresh last_seen_at and app/ios version
existing.user_id = user.id # re-bind if different user logs in same device
existing.device_name = device_name or existing.device_name
existing.app_version = app_version or existing.app_version
existing.ios_version = ios_version or existing.ios_version
existing.last_seen_at = now
db.session.commit()
log_action(ACTION_UPDATE, 'DeviceRegistration', existing.id,
f'{device_name} v{app_version}',
f'user={user.username}; ios={ios_version}')
logger.info('API DEVICES | updated | device_id=%s | user=%s | app=%s',
device_id[:8], user.username, app_version)
else:
reg = DeviceRegistration(
device_id = device_id,
user_id = user.id,
device_name = device_name,
app_version = app_version,
ios_version = ios_version,
registered_at = now,
last_seen_at = now,
)
db.session.add(reg)
db.session.commit()
log_action(ACTION_CREATE, 'DeviceRegistration', reg.id,
f'{device_name} v{app_version}',
f'user={user.username}; ios={ios_version}')
logger.info('API DEVICES | registered | device_id=%s | user=%s | app=%s',
device_id[:8], user.username, app_version)
return api_ok({'registered': True})
+1 -2
View File
@@ -5,5 +5,4 @@ from app.models.inspection import (InspectionTemplate, ChecklistItem,
from app.models.issue import Issue
from app.models.project import Project, CustomerAssignment
from app.models.api_token import RefreshToken, DeviceToken
from app.models.notification_matrix import NotificationMatrix
from app.models.device_registration import DeviceRegistration
from app.models.notification_matrix import NotificationMatrix
-24
View File
@@ -1,24 +0,0 @@
# app/models/device_registration.py
# -----------------------------------
# Tracks iOS devices that have registered with the server.
# One row per physical device — upserted on every app foreground.
from app import db
from app.utils.time_utils import now_eastern
class DeviceRegistration(db.Model):
__tablename__ = 'device_registrations'
id = db.Column(db.Integer, primary_key=True)
# Stable UUID generated on first launch and stored in iOS Keychain.
# Unique across all devices; survives app restarts but not device wipes.
device_id = db.Column(db.String(64), nullable=False, unique=True, index=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True)
device_name = db.Column(db.String(255), nullable=False, default='')
app_version = db.Column(db.String(32), nullable=False, default='')
ios_version = db.Column(db.String(32), nullable=False, default='')
registered_at = db.Column(db.DateTime, nullable=False, default=now_eastern)
last_seen_at = db.Column(db.DateTime, nullable=False, default=now_eastern)
user = db.relationship('User', backref=db.backref('devices', lazy='dynamic'))