mirror of
https://github.com/ipnet-mesh/meshcore-hub.git
synced 2026-10-30 19:52:03 +01:00
Add member-node association support
Members can now have multiple associated nodes, each with a public_key and node_role (e.g., 'chat', 'repeater'). This replaces the single public_key field on members with a one-to-many relationship. Changes: - Add MemberNode model for member-node associations - Update Member model to remove public_key, add nodes relationship - Update Pydantic schemas with MemberNodeCreate/MemberNodeRead - Update member_import.py to handle nodes list in seed files - Update API routes to handle nodes in create/update/read operations - Add Alembic migration to create member_nodes table and migrate data - Update example seed file with new format
This commit is contained in:
9 files changed
+336
-54
No files matched your search
@@ -0,0 +1,116 @@
|
||||
"""Add member_nodes association table
|
||||
|
||||
Revision ID: 002
|
||||
Revises: 001
|
||||
Create Date: 2024-12-05
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "002"
|
||||
down_revision: Union[str, None] = "001"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create member_nodes table
|
||||
op.create_table(
|
||||
"member_nodes",
|
||||
sa.Column("id", sa.String(), nullable=False),
|
||||
sa.Column("member_id", sa.String(36), nullable=False),
|
||||
sa.Column("public_key", sa.String(64), nullable=False),
|
||||
sa.Column("node_role", sa.String(50), nullable=True),
|
||||
sa.Column(
|
||||
"created_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.func.now(),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"updated_at",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.func.now(),
|
||||
nullable=False,
|
||||
),
|
||||
sa.ForeignKeyConstraint(["member_id"], ["members.id"], ondelete="CASCADE"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
op.create_index("ix_member_nodes_member_id", "member_nodes", ["member_id"])
|
||||
op.create_index("ix_member_nodes_public_key", "member_nodes", ["public_key"])
|
||||
op.create_index(
|
||||
"ix_member_nodes_member_public_key",
|
||||
"member_nodes",
|
||||
["member_id", "public_key"],
|
||||
)
|
||||
|
||||
# Migrate existing public_key data from members to member_nodes
|
||||
# Get all members with a public_key
|
||||
connection = op.get_bind()
|
||||
members_with_keys = connection.execute(
|
||||
sa.text(
|
||||
"SELECT id, public_key FROM members WHERE public_key IS NOT NULL"
|
||||
)
|
||||
).fetchall()
|
||||
|
||||
# Insert into member_nodes
|
||||
for member_id, public_key in members_with_keys:
|
||||
# Generate a UUID for the new row
|
||||
import uuid
|
||||
|
||||
node_id = str(uuid.uuid4())
|
||||
connection.execute(
|
||||
sa.text(
|
||||
"""
|
||||
INSERT INTO member_nodes (id, member_id, public_key, created_at, updated_at)
|
||||
VALUES (:id, :member_id, :public_key, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
"""
|
||||
),
|
||||
{"id": node_id, "member_id": member_id, "public_key": public_key},
|
||||
)
|
||||
|
||||
# Drop the public_key column from members
|
||||
op.drop_index("ix_members_public_key", table_name="members")
|
||||
op.drop_column("members", "public_key")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Add public_key column back to members
|
||||
op.add_column(
|
||||
"members",
|
||||
sa.Column("public_key", sa.String(64), nullable=True),
|
||||
)
|
||||
op.create_index("ix_members_public_key", "members", ["public_key"])
|
||||
|
||||
# Migrate data back - take the first node for each member
|
||||
connection = op.get_bind()
|
||||
member_nodes = connection.execute(
|
||||
sa.text(
|
||||
"""
|
||||
SELECT DISTINCT member_id, public_key
|
||||
FROM member_nodes
|
||||
WHERE (member_id, created_at) IN (
|
||||
SELECT member_id, MIN(created_at)
|
||||
FROM member_nodes
|
||||
GROUP BY member_id
|
||||
)
|
||||
"""
|
||||
)
|
||||
).fetchall()
|
||||
|
||||
for member_id, public_key in member_nodes:
|
||||
connection.execute(
|
||||
sa.text(
|
||||
"UPDATE members SET public_key = :public_key WHERE id = :member_id"
|
||||
),
|
||||
{"public_key": public_key, "member_id": member_id},
|
||||
)
|
||||
|
||||
# Drop member_nodes table
|
||||
op.drop_table("member_nodes")
|
||||
Reference in new issue
Block a user