Implement multi-user auth + agent management + terminal proxy
All checks were successful
Build and Push Backend / build (push) Successful in 5s

- Add userId to agents and projects tables (with migrations)
- Create auth middleware for session validation
- Protect MCP endpoints with authentication and user filtering
- Implement agent management API (launch, my, delete)
- Add terminal proxy at /agent-terminal/:agentId with auth
- Update all agent endpoints to verify user ownership

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hector Ros
2026-01-20 17:21:53 +01:00
parent 08e6f66c7d
commit 8382f6645e
8 changed files with 1161 additions and 107 deletions

View File

@@ -0,0 +1,634 @@
{
"version": "5",
"dialect": "mysql",
"id": "28e89c13-2bd0-4fcd-9b01-e8433c08d71e",
"prevId": "ada6a640-177d-4c35-b7e0-e1d20c9adabb",
"tables": {
"agents": {
"name": "agents",
"columns": {
"id": {
"name": "id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"pod_name": {
"name": "pod_name",
"type": "varchar(253)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"k8s_namespace": {
"name": "k8s_namespace",
"type": "varchar(63)",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'agents'"
},
"status": {
"name": "status",
"type": "enum('idle','busy','error','offline')",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'idle'"
},
"current_task_id": {
"name": "current_task_id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"tasks_completed": {
"name": "tasks_completed",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": 0
},
"last_heartbeat": {
"name": "last_heartbeat",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "(now())"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {
"idx_status": {
"name": "idx_status",
"columns": [
"status"
],
"isUnique": false
},
"idx_user_id": {
"name": "idx_user_id",
"columns": [
"user_id"
],
"isUnique": false
}
},
"foreignKeys": {
"agents_user_id_users_id_fk": {
"name": "agents_user_id_users_id_fk",
"tableFrom": "agents",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"agents_id": {
"name": "agents_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"agents_pod_name_unique": {
"name": "agents_pod_name_unique",
"columns": [
"pod_name"
]
}
},
"checkConstraint": {}
},
"projects": {
"name": "projects",
"columns": {
"id": {
"name": "id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"gitea_repo_id": {
"name": "gitea_repo_id",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"gitea_repo_url": {
"name": "gitea_repo_url",
"type": "varchar(512)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"gitea_owner": {
"name": "gitea_owner",
"type": "varchar(100)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"gitea_repo_name": {
"name": "gitea_repo_name",
"type": "varchar(100)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"default_branch": {
"name": "default_branch",
"type": "varchar(100)",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'main'"
},
"k8s_namespace": {
"name": "k8s_namespace",
"type": "varchar(63)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"docker_image": {
"name": "docker_image",
"type": "varchar(512)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"env_vars": {
"name": "env_vars",
"type": "json",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"replicas": {
"name": "replicas",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": 1
},
"cpu_limit": {
"name": "cpu_limit",
"type": "varchar(20)",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'500m'"
},
"memory_limit": {
"name": "memory_limit",
"type": "varchar(20)",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'512Mi'"
},
"status": {
"name": "status",
"type": "enum('active','paused','archived')",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'active'"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "(now())"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {
"idx_status": {
"name": "idx_status",
"columns": [
"status"
],
"isUnique": false
},
"idx_user_id": {
"name": "idx_user_id",
"columns": [
"user_id"
],
"isUnique": false
}
},
"foreignKeys": {
"projects_user_id_users_id_fk": {
"name": "projects_user_id_users_id_fk",
"tableFrom": "projects",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"projects_id": {
"name": "projects_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"projects_k8s_namespace_unique": {
"name": "projects_k8s_namespace_unique",
"columns": [
"k8s_namespace"
]
}
},
"checkConstraint": {}
},
"sessions": {
"name": "sessions",
"columns": {
"id": {
"name": "id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {
"idx_user_id": {
"name": "idx_user_id",
"columns": [
"user_id"
],
"isUnique": false
},
"idx_expires_at": {
"name": "idx_expires_at",
"columns": [
"expires_at"
],
"isUnique": false
}
},
"foreignKeys": {
"sessions_user_id_users_id_fk": {
"name": "sessions_user_id_users_id_fk",
"tableFrom": "sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"sessions_id": {
"name": "sessions_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"tasks": {
"name": "tasks",
"columns": {
"id": {
"name": "id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"project_id": {
"name": "project_id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"priority": {
"name": "priority",
"type": "enum('low','medium','high','urgent')",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'medium'"
},
"state": {
"name": "state",
"type": "enum('backlog','in_progress','needs_input','ready_to_test','approved','staging','production')",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'backlog'"
},
"assigned_agent_id": {
"name": "assigned_agent_id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"branch_name": {
"name": "branch_name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"pr_url": {
"name": "pr_url",
"type": "varchar(512)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"preview_url": {
"name": "preview_url",
"type": "varchar(512)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "(now())"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {
"idx_project_state": {
"name": "idx_project_state",
"columns": [
"project_id",
"state"
],
"isUnique": false
}
},
"foreignKeys": {
"tasks_project_id_projects_id_fk": {
"name": "tasks_project_id_projects_id_fk",
"tableFrom": "tasks",
"tableTo": "projects",
"columnsFrom": [
"project_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"tasks_assigned_agent_id_agents_id_fk": {
"name": "tasks_assigned_agent_id_agents_id_fk",
"tableFrom": "tasks",
"tableTo": "agents",
"columnsFrom": [
"assigned_agent_id"
],
"columnsTo": [
"id"
],
"onDelete": "set null",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"tasks_id": {
"name": "tasks_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "varchar(36)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"username": {
"name": "username",
"type": "varchar(100)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"password_hash": {
"name": "password_hash",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "(now())"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {
"idx_email": {
"name": "idx_email",
"columns": [
"email"
],
"isUnique": false
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"users_id": {
"name": "users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"users_email_unique": {
"name": "users_email_unique",
"columns": [
"email"
]
},
"users_username_unique": {
"name": "users_username_unique",
"columns": [
"username"
]
}
},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

View File

@@ -15,6 +15,13 @@
"when": 1768868010676,
"tag": "0001_opposite_warbird",
"breakpoints": true
},
{
"idx": 2,
"version": "5",
"when": 1768924360408,
"tag": "0002_next_xorn",
"breakpoints": true
}
]
}