Initial commit — multi-tenant filtering, port constraints, chart bbox

This commit is contained in:
2026-05-04 22:41:09 -04:00
parent c3b07be67e
commit fcf1d2787a
1102 changed files with 7353 additions and 1166 deletions
Binary file not shown.
Binary file not shown.
+52
View File
@@ -0,0 +1,52 @@
"""
Organization models: Port, Company, BuoyOwnership
Used for multi-client access control and per-port default views.
"""
from sqlalchemy import Column, String, Float, Boolean, DateTime, Text
from sqlalchemy.sql import func
from database import Base
class Port(Base):
"""Geographic port / region served by this deployment."""
__tablename__ = "ports"
id = Column(String, primary_key=True)
name = Column(String, nullable=False)
country = Column(String, default="Colombia")
center_lat = Column(Float, nullable=True)
center_lon = Column(Float, nullable=True)
default_zoom = Column(Float, default=12.0)
chart_name = Column(String, nullable=True) # folder under charts/, e.g. "BAHÍA_DE_CARTAGENA"
activo = Column(Boolean, default=True)
creado_en = Column(DateTime, server_default=func.now())
class Company(Base):
"""Buoy-owner company (client). Belongs to a home port."""
__tablename__ = "companies"
id = Column(String, primary_key=True)
name = Column(String, nullable=False)
port_id = Column(String, nullable=True) # FK → ports.id
contact_email = Column(String, nullable=True)
contact_phone = Column(String, nullable=True)
activa = Column(Boolean, default=True)
notas = Column(Text, nullable=True)
creado_en = Column(DateTime, server_default=func.now())
class BuoyOwnership(Base):
"""
Which company owns (and monitors) a given Aid/MMSI.
A company user can see AIS/ATON real-time data only for buoys
listed in this table under their company_id.
"""
__tablename__ = "buoy_ownership"
id = Column(String, primary_key=True)
company_id = Column(String, nullable=False) # FK → companies.id
aid_id = Column(String, nullable=True) # FK → aids.id (nullable if aid not yet in DB)
mmsi = Column(String, nullable=True) # direct MMSI reference
notas = Column(Text, nullable=True)
creado_en = Column(DateTime, server_default=func.now())
+5 -3
View File
@@ -4,9 +4,10 @@ from database import Base
import enum
class Role(str, enum.Enum):
SUPERADMIN = "SUPERADMIN"
ADMIN = "ADMIN"
USER = "USER"
SUPERADMIN = "SUPERADMIN"
ADMIN = "ADMIN"
CLIENT_ADMIN = "CLIENT_ADMIN" # company-scoped: can start/stop recordings for own aids
USER = "USER" # company-scoped: read-only
class User(Base):
__tablename__ = "users"
@@ -17,6 +18,7 @@ class User(Base):
email = Column(String, unique=True, nullable=True)
hashed_pw = Column(String, nullable=False)
role = Column(String, default="USER")
company_id = Column(String, nullable=True)
activo = Column(Boolean, default=True)
creado_en = Column(DateTime, server_default=func.now())
ultimo_login = Column(DateTime, nullable=True)
+15 -1
View File
@@ -2,6 +2,7 @@ from sqlalchemy import Column, String, Float, DateTime, Boolean, Integer
from sqlalchemy.sql import func
from database import Base
class Vessel(Base):
__tablename__ = "vessels"
@@ -44,5 +45,18 @@ class RecordingEvent(Base):
inicio = Column(DateTime, nullable=False)
fin = Column(DateTime, nullable=True)
distancia_min_m = Column(Float, nullable=True)
trigger = Column(String) # PROXIMIDAD | PROYECCION
trigger = Column(String) # PROXIMIDAD | PROYECCION | MANUAL
cerrado = Column(Boolean, default=False)
class AtonTrack(Base):
"""Continuous position history for AIS AtoN (Type 21) devices."""
__tablename__ = "aton_tracks"
id = Column(Integer, primary_key=True, autoincrement=True)
mmsi = Column(String, nullable=False, index=True)
timestamp = Column(DateTime, server_default=func.now(), index=True)
lat = Column(Float, nullable=False)
lon = Column(Float, nullable=False)
voltage_v = Column(Float, nullable=True)
off_position = Column(Boolean, nullable=True)