Commit b02ad490 authored by Mahmoud Aglan's avatar Mahmoud Aglan

new fix

parent 3102b2dd
This diff is collapsed.
...@@ -66,19 +66,16 @@ def list_chats(user: User = Depends(get_current_user), db: Session = Depends(get ...@@ -66,19 +66,16 @@ def list_chats(user: User = Depends(get_current_user), db: Session = Depends(get
@router.post("") @router.post("")
def create_chat(body: CreateChatBody, user: User = Depends(get_current_user), db: Session = Depends(get_db)): def create_chat(body: CreateChatBody, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
perms = get_user_permissions(user.id, db) perms = get_user_permissions(user.id, db)
# Enforce max chats
max_chats = perms.get("max_chats", 0) max_chats = perms.get("max_chats", 0)
if max_chats > 0: if max_chats > 0:
current_count = count_user_chats(user.id, db) current_count = count_user_chats(user.id, db)
if current_count >= max_chats: if current_count >= max_chats:
raise HTTPException(403, f"Chat limit reached ({max_chats}). Delete old chats or contact admin.") raise HTTPException(403, f"Chat limit reached ({max_chats}). Delete old chats or contact admin.")
# Validate KB permission
if body.knowledge_base_id: if body.knowledge_base_id:
if not perms.get("can_use_knowledge_base"): if not perms.get("can_use_knowledge_base"):
raise HTTPException(403, "Knowledge base access not enabled for your account.") raise HTTPException(403, "Knowledge base access not enabled for your account.")
# Validate GitLab permission
if body.linked_repo_id: if body.linked_repo_id:
if not perms.get("can_use_gitlab"): if not perms.get("can_use_gitlab"):
raise HTTPException(403, "GitLab access not enabled for your account.") raise HTTPException(403, "GitLab access not enabled for your account.")
...@@ -166,19 +163,16 @@ async def reconnect_stream(chat_id: str, user: User = Depends(get_current_user)) ...@@ -166,19 +163,16 @@ async def reconnect_stream(chat_id: str, user: User = Depends(get_current_user))
async def send_message(chat_id: str, body: SendMessageBody, user: User = Depends(get_current_user), db: Session = Depends(get_db)): async def send_message(chat_id: str, body: SendMessageBody, user: User = Depends(get_current_user), db: Session = Depends(get_db)):
perms = get_user_permissions(user.id, db) perms = get_user_permissions(user.id, db)
# Enforce daily message limit
max_per_day = perms.get("max_messages_per_day", 0) max_per_day = perms.get("max_messages_per_day", 0)
if max_per_day > 0: if max_per_day > 0:
today_count = count_user_messages_today(user.id, db) today_count = count_user_messages_today(user.id, db)
if today_count >= max_per_day: if today_count >= max_per_day:
raise HTTPException(429, f"Daily message limit reached ({max_per_day}). Try again tomorrow.") raise HTTPException(429, f"Daily message limit reached ({max_per_day}). Try again tomorrow.")
# Enforce web search permission
web_search = body.web_search web_search = body.web_search
if web_search and not perms.get("can_use_web_search"): if web_search and not perms.get("can_use_web_search"):
web_search = False web_search = False
# Enforce attachment permission
if body.attachment_ids and not perms.get("can_use_attachments"): if body.attachment_ids and not perms.get("can_use_attachments"):
raise HTTPException(403, "File attachments not enabled for your account.") raise HTTPException(403, "File attachments not enabled for your account.")
...@@ -187,7 +181,6 @@ async def send_message(chat_id: str, body: SendMessageBody, user: User = Depends ...@@ -187,7 +181,6 @@ async def send_message(chat_id: str, body: SendMessageBody, user: User = Depends
if len(body.attachment_ids) > max_att: if len(body.attachment_ids) > max_att:
raise HTTPException(400, f"Too many attachments. Max {max_att} per message.") raise HTTPException(400, f"Too many attachments. Max {max_att} per message.")
# Enforce model & limits
model = check_model_allowed(user.id, body.model or "eu.anthropic.claude-opus-4-6-v1", db) model = check_model_allowed(user.id, body.model or "eu.anthropic.claude-opus-4-6-v1", db)
max_tokens = min(body.max_tokens, perms.get("max_tokens_cap", 4096)) max_tokens = min(body.max_tokens, perms.get("max_tokens_cap", 4096))
reasoning_budget = min(body.reasoning_budget, perms.get("max_reasoning_budget", 0)) reasoning_budget = min(body.reasoning_budget, perms.get("max_reasoning_budget", 0))
...@@ -206,21 +199,100 @@ async def send_message(chat_id: str, body: SendMessageBody, user: User = Depends ...@@ -206,21 +199,100 @@ async def send_message(chat_id: str, body: SendMessageBody, user: User = Depends
@router.post("/{chat_id}/commit") @router.post("/{chat_id}/commit")
async def commit_from_chat(chat_id: str, body: CommitFromChatBody, user: User = Depends(get_current_user), db: Session = Depends(get_db)): async def commit_from_chat(
chat_id: str,
body: CommitFromChatBody,
user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
"""
Commit files from chat to linked GitLab repo.
Auto-detects whether each file should be 'create' or 'update'
by checking the repo tree, so it never fails on wrong action type.
"""
check_feature(user.id, "use_gitlab", db) check_feature(user.id, "use_gitlab", db)
chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first() chat = db.query(Chat).filter(Chat.id == chat_id, Chat.user_id == user.id).first()
if not chat: raise HTTPException(404, "Chat not found") if not chat:
if not chat.linked_repo_id: raise HTTPException(400, "No repository linked") raise HTTPException(404, "Chat not found")
if not chat.linked_repo_id:
raise HTTPException(400, "No repository linked")
repo = db.query(LinkedRepo).filter(LinkedRepo.id == chat.linked_repo_id).first() repo = db.query(LinkedRepo).filter(LinkedRepo.id == chat.linked_repo_id).first()
if not repo: raise HTTPException(404, "Linked repository not found") if not repo:
raise HTTPException(404, "Linked repository not found")
settings = db.query(GitLabSettings).first() settings = db.query(GitLabSettings).first()
if not settings or not settings.is_active: raise HTTPException(400, "GitLab not configured") if not settings or not settings.is_active:
actions = [{"action": f.get("action", "update"), "file_path": f["file_path"], "content": f["content"]} for f in body.files] raise HTTPException(400, "GitLab not configured")
if not actions: raise HTTPException(400, "No files to commit")
# ── Fetch repo tree to know which files already exist ──
existing_paths = set()
try:
tree = await gitlab_service.get_tree(
settings.gitlab_url,
settings.gitlab_token,
repo.gitlab_project_id,
ref=body.branch,
recursive=True,
)
existing_paths = {
item["path"] for item in tree if item["type"] == "blob"
}
except Exception:
# If tree fetch fails (empty repo, network issue, etc.),
# we'll try all as "create" since we can't know what exists
pass
# ── Build actions with auto-detected create/update ──
actions = []
for f in body.files:
file_path = f.get("file_path", "")
content = f.get("content", "")
requested_action = f.get("action", "auto")
if not file_path or not content:
continue
file_exists = file_path in existing_paths
# Smart action resolution
if requested_action in ("auto", "upsert"):
# Auto-detect: update if exists, create if not
actual_action = "update" if file_exists else "create"
elif requested_action == "update" and not file_exists:
# User said update but file doesn't exist → create instead
actual_action = "create"
elif requested_action == "create" and file_exists:
# User said create but file already exists → update instead
actual_action = "update"
else:
actual_action = requested_action
actions.append({
"action": actual_action,
"file_path": file_path,
"content": content,
})
if not actions:
raise HTTPException(400, "No valid files to commit")
try: try:
result = await gitlab_service.commit_files(settings.gitlab_url, settings.gitlab_token, repo.gitlab_project_id, body.branch, body.commit_message, actions) result = await gitlab_service.commit_files(
settings.gitlab_url,
settings.gitlab_token,
repo.gitlab_project_id,
body.branch,
body.commit_message,
actions,
)
gen_manager.invalidate_repo_cache(repo.id) gen_manager.invalidate_repo_cache(repo.id)
return {"ok": True, "commit": result, "files_committed": len(actions)} return {
"ok": True,
"commit": result,
"files_committed": len(actions),
}
except gitlab_service.GitLabError as e: except gitlab_service.GitLabError as e:
raise HTTPException(e.status_code, f"Commit failed: {e.detail}") raise HTTPException(e.status_code, f"Commit failed: {e.detail}")
...@@ -238,17 +310,41 @@ def _sse(data): ...@@ -238,17 +310,41 @@ def _sse(data):
def _chat_dict(c, db=None): def _chat_dict(c, db=None):
d = {"id": c.id, "title": c.title, "model": c.model, "knowledge_base_id": c.knowledge_base_id, "linked_repo_id": c.linked_repo_id, "max_tokens": c.max_tokens or 4096, "reasoning_budget": c.reasoning_budget or 0, "created_at": str(c.created_at), "updated_at": str(c.updated_at)} d = {
"id": c.id, "title": c.title, "model": c.model,
"knowledge_base_id": c.knowledge_base_id,
"linked_repo_id": c.linked_repo_id,
"max_tokens": c.max_tokens or 4096,
"reasoning_budget": c.reasoning_budget or 0,
"created_at": str(c.created_at),
"updated_at": str(c.updated_at),
}
if db and c.linked_repo_id: if db and c.linked_repo_id:
repo = db.query(LinkedRepo).filter(LinkedRepo.id == c.linked_repo_id).first() repo = db.query(LinkedRepo).filter(LinkedRepo.id == c.linked_repo_id).first()
if repo: if repo:
d["linked_repo"] = {"id": repo.id, "name": repo.name, "path_with_namespace": repo.path_with_namespace, "default_branch": repo.default_branch, "web_url": repo.web_url, "gitlab_project_id": repo.gitlab_project_id, "map_status": repo.map_status} d["linked_repo"] = {
"id": repo.id, "name": repo.name,
"path_with_namespace": repo.path_with_namespace,
"default_branch": repo.default_branch,
"web_url": repo.web_url,
"gitlab_project_id": repo.gitlab_project_id,
"map_status": repo.map_status,
}
return d return d
def _msg_dict(m): def _msg_dict(m):
return {"id": m.id, "role": m.role, "content": m.content, "thinking_content": m.thinking_content, "input_tokens": m.input_tokens, "output_tokens": m.output_tokens, "created_at": str(m.created_at)} return {
"id": m.id, "role": m.role, "content": m.content,
"thinking_content": m.thinking_content,
"input_tokens": m.input_tokens, "output_tokens": m.output_tokens,
"created_at": str(m.created_at),
}
def _att_brief(a): def _att_brief(a):
return {"id": a.id, "original_filename": a.original_filename, "mime_type": a.mime_type, "file_type": a.file_type, "file_size": a.file_size} return {
\ No newline at end of file "id": a.id, "original_filename": a.original_filename,
"mime_type": a.mime_type, "file_type": a.file_type,
"file_size": a.file_size,
}
\ No newline at end of file
""" """
GitLab CE integration routes — superadmin only. GitLab CE integration routes — superadmin only.
Son of Anton v4.0.0 Son of Anton v4.2.0
""" """
import asyncio import asyncio
...@@ -46,7 +46,7 @@ class SingleCommitBody(BaseModel): ...@@ -46,7 +46,7 @@ class SingleCommitBody(BaseModel):
file_path: str file_path: str
content: str content: str
commit_message: str commit_message: str
action: str = "update" action: str = "auto"
class BranchBody(BaseModel): class BranchBody(BaseModel):
branch_name: str branch_name: str
...@@ -188,7 +188,6 @@ async def link_repo(body: LinkRepoBody, admin: User = Depends(require_superadmin ...@@ -188,7 +188,6 @@ async def link_repo(body: LinkRepoBody, admin: User = Depends(require_superadmin
db.commit() db.commit()
db.refresh(repo) db.refresh(repo)
# Start background analysis
asyncio.create_task(_analyze_repo_background( asyncio.create_task(_analyze_repo_background(
repo.id, s.gitlab_url, s.gitlab_token, repo.id, s.gitlab_url, s.gitlab_token,
project["id"], project.get("default_branch", "main"), project["id"], project.get("default_branch", "main"),
...@@ -347,12 +346,56 @@ async def create_branch(repo_id: str, body: BranchBody, admin: User = Depends(re ...@@ -347,12 +346,56 @@ async def create_branch(repo_id: str, body: BranchBody, admin: User = Depends(re
@router.post("/repos/{repo_id}/commit") @router.post("/repos/{repo_id}/commit")
async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(require_superadmin), db: Session = Depends(get_db)): async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(require_superadmin), db: Session = Depends(get_db)):
"""
Commit multiple files. Auto-detects create vs update per file.
"""
s = _get_settings(db) s = _get_settings(db)
repo = _get_repo(repo_id, db) repo = _get_repo(repo_id, db)
# Fetch tree to know which files exist
existing_paths = set()
try:
tree = await gitlab_service.get_tree(
s.gitlab_url, s.gitlab_token, repo.gitlab_project_id,
ref=body.branch, recursive=True,
)
existing_paths = {item["path"] for item in tree if item["type"] == "blob"}
except Exception:
pass
resolved_actions = []
for a in body.actions:
file_path = a.get("file_path", "")
content = a.get("content", "")
requested = a.get("action", "auto")
if not file_path:
continue
file_exists = file_path in existing_paths
if requested in ("auto", "upsert"):
actual = "update" if file_exists else "create"
elif requested == "update" and not file_exists:
actual = "create"
elif requested == "create" and file_exists:
actual = "update"
else:
actual = requested
resolved_actions.append({
"action": actual,
"file_path": file_path,
"content": content,
})
if not resolved_actions:
raise HTTPException(400, "No valid files to commit")
try: try:
result = await gitlab_service.commit_files( result = await gitlab_service.commit_files(
s.gitlab_url, s.gitlab_token, repo.gitlab_project_id, s.gitlab_url, s.gitlab_token, repo.gitlab_project_id,
body.branch, body.commit_message, body.actions, body.branch, body.commit_message, resolved_actions,
) )
return result return result
except gitlab_service.GitLabError as e: except gitlab_service.GitLabError as e:
...@@ -360,13 +403,41 @@ async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(requ ...@@ -360,13 +403,41 @@ async def commit_code(repo_id: str, body: CommitBody, admin: User = Depends(requ
@router.post("/repos/{repo_id}/commit-single") @router.post("/repos/{repo_id}/commit-single")
async def commit_single(repo_id: str, body: SingleCommitBody, admin: User = Depends(require_superadmin), db: Session = Depends(get_db)): async def commit_single(
repo_id: str,
body: SingleCommitBody,
admin: User = Depends(require_superadmin),
db: Session = Depends(get_db),
):
"""
Commit a single file. Auto-detects create vs update.
"""
s = _get_settings(db) s = _get_settings(db)
repo = _get_repo(repo_id, db) repo = _get_repo(repo_id, db)
# Auto-detect whether file exists
action = body.action
if action in ("update", "create", "auto"):
try:
await gitlab_service.get_file_content(
s.gitlab_url, s.gitlab_token,
repo.gitlab_project_id, body.file_path, ref=body.branch,
)
file_exists = True
except gitlab_service.GitLabError:
file_exists = False
if action == "auto":
action = "update" if file_exists else "create"
elif action == "update" and not file_exists:
action = "create"
elif action == "create" and file_exists:
action = "update"
try: try:
result = await gitlab_service.commit_single_file( result = await gitlab_service.commit_single_file(
s.gitlab_url, s.gitlab_token, repo.gitlab_project_id, s.gitlab_url, s.gitlab_token, repo.gitlab_project_id,
body.branch, body.file_path, body.content, body.commit_message, body.action, body.branch, body.file_path, body.content, body.commit_message, action,
) )
return result return result
except gitlab_service.GitLabError as e: except gitlab_service.GitLabError as e:
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment