초기 커밋

This commit is contained in:
2026-03-01 07:44:19 +09:00
commit 09359f30be
146 changed files with 6120 additions and 0 deletions

View File

View File

@@ -0,0 +1,39 @@
from __future__ import annotations
from datetime import datetime
from sqlmodel import Field, Relationship, SQLModel
from app.db.base import TimestampMixin
class RefreshToken(TimestampMixin, SQLModel, table=True):
__tablename__ = "refresh_tokens"
id: int | None = Field(default=None, primary_key=True)
user_id: int = Field(foreign_key="users.id", index=True)
token: str = Field(max_length=500, unique=True, index=True)
expires_at: datetime
is_revoked: bool = Field(default=False)
device_info: str = Field(default="", max_length=255)
# Relationships
user: User | None = Relationship(back_populates="refresh_tokens")
class OAuthAccount(TimestampMixin, SQLModel, table=True):
__tablename__ = "oauth_accounts"
id: int | None = Field(default=None, primary_key=True)
user_id: int = Field(foreign_key="users.id", index=True)
provider: str = Field(max_length=50)
provider_user_id: str = Field(max_length=255)
access_token: str = Field(default="", max_length=500)
refresh_token: str = Field(default="", max_length=500)
expires_at: datetime | None = Field(default=None)
# Avoid circular import
from app.models.mariadb.user import User # noqa: E402, F811
RefreshToken.model_rebuild()

View File

@@ -0,0 +1,36 @@
from __future__ import annotations
from datetime import datetime
from sqlmodel import Field, Relationship, SQLModel
from app.core.constants import DeviceStatus
from app.db.base import SoftDeleteMixin, TimestampMixin
class DeviceGroup(TimestampMixin, SQLModel, table=True):
__tablename__ = "device_groups"
id: int | None = Field(default=None, primary_key=True)
name: str = Field(max_length=100, unique=True)
description: str = Field(default="", max_length=500)
devices: list[Device] = Relationship(back_populates="group")
class Device(TimestampMixin, SoftDeleteMixin, SQLModel, table=True):
__tablename__ = "devices"
id: int | None = Field(default=None, primary_key=True)
device_uid: str = Field(max_length=100, unique=True, index=True)
name: str = Field(max_length=100)
device_type: str = Field(default="", max_length=50)
status: str = Field(default=DeviceStatus.OFFLINE, max_length=20)
firmware_version: str = Field(default="", max_length=50)
ip_address: str = Field(default="", max_length=45)
group_id: int | None = Field(default=None, foreign_key="device_groups.id")
owner_id: int | None = Field(default=None, foreign_key="users.id")
last_seen_at: datetime | None = Field(default=None)
metadata_json: str = Field(default="{}", max_length=2000)
group: DeviceGroup | None = Relationship(back_populates="devices")

View File

@@ -0,0 +1,36 @@
from __future__ import annotations
from datetime import datetime
from sqlmodel import Field, SQLModel
from app.core.constants import AlertSeverity
from app.db.base import TimestampMixin
class AlertRule(TimestampMixin, SQLModel, table=True):
__tablename__ = "alert_rules"
id: int | None = Field(default=None, primary_key=True)
name: str = Field(max_length=100)
description: str = Field(default="", max_length=500)
metric: str = Field(max_length=100)
condition: str = Field(max_length=50)
threshold: float
severity: str = Field(default=AlertSeverity.WARNING, max_length=20)
is_enabled: bool = Field(default=True)
device_group_id: int | None = Field(default=None, foreign_key="device_groups.id")
created_by: int | None = Field(default=None, foreign_key="users.id")
class Alert(TimestampMixin, SQLModel, table=True):
__tablename__ = "alerts"
id: int | None = Field(default=None, primary_key=True)
rule_id: int | None = Field(default=None, foreign_key="alert_rules.id")
device_id: int | None = Field(default=None, foreign_key="devices.id")
severity: str = Field(default=AlertSeverity.WARNING, max_length=20)
message: str = Field(max_length=500)
is_acknowledged: bool = Field(default=False)
acknowledged_by: int | None = Field(default=None, foreign_key="users.id")
acknowledged_at: datetime | None = Field(default=None)

View File

@@ -0,0 +1,27 @@
from __future__ import annotations
from sqlmodel import Field, SQLModel
from app.db.base import TimestampMixin
class SystemConfig(TimestampMixin, SQLModel, table=True):
__tablename__ = "system_configs"
id: int | None = Field(default=None, primary_key=True)
key: str = Field(max_length=100, unique=True, index=True)
value: str = Field(default="", max_length=2000)
description: str = Field(default="", max_length=500)
is_secret: bool = Field(default=False)
class AuditLog(TimestampMixin, SQLModel, table=True):
__tablename__ = "audit_logs"
id: int | None = Field(default=None, primary_key=True)
user_id: int | None = Field(default=None, foreign_key="users.id")
action: str = Field(max_length=100)
resource_type: str = Field(max_length=50)
resource_id: str = Field(default="", max_length=50)
details: str = Field(default="{}", max_length=2000)
ip_address: str = Field(default="", max_length=45)

View File

@@ -0,0 +1,44 @@
from __future__ import annotations
from datetime import datetime
from sqlmodel import Field, Relationship, SQLModel
from app.core.constants import Role
from app.db.base import SoftDeleteMixin, TimestampMixin
class User(TimestampMixin, SoftDeleteMixin, SQLModel, table=True):
__tablename__ = "users"
id: int | None = Field(default=None, primary_key=True)
email: str = Field(max_length=255, unique=True, index=True)
hashed_password: str = Field(max_length=255)
role: str = Field(default=Role.USER, max_length=20)
is_active: bool = Field(default=True)
is_verified: bool = Field(default=False)
last_login_at: datetime | None = Field(default=None)
# Relationships
profile: UserProfile | None = Relationship(back_populates="user")
refresh_tokens: list[RefreshToken] = Relationship(back_populates="user")
class UserProfile(TimestampMixin, SQLModel, table=True):
__tablename__ = "user_profiles"
id: int | None = Field(default=None, primary_key=True)
user_id: int = Field(foreign_key="users.id", unique=True, index=True)
full_name: str = Field(default="", max_length=100)
phone: str = Field(default="", max_length=20)
organization: str = Field(default="", max_length=100)
avatar_url: str = Field(default="", max_length=500)
# Relationships
user: User | None = Relationship(back_populates="profile")
# Forward reference resolution
from app.models.mariadb.auth import RefreshToken # noqa: E402
User.model_rebuild()