Reorganize repo: backend/, deploy/, docs/ layout + AGENTS.md
Restructure the monorepo into clear top-level directories: - backend/: Go module root (cmd, internal, pkg, configs, migrations, docs/swagger, scripts, tests, go.mod, Makefile, .air.toml) - deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd - docs/: project documentation + reports/ (moved from repo root) - AGENTS.md: new AI coding-agent guide at repo root Update all references to the new layout: - Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root) - docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile, env_file ../../.env, volume mounts ../../backend:/app - deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile - CI: working-directory: backend for go commands, file deploy/docker/Dockerfile, coverage path backend/coverage.out, health_check backend/scripts/ - backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../ - README: architecture tree, quickstart, config paths updated Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh, gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to preserve history. Build, vet, SQLite tests, and docker compose config verified.
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Fix all Gin radix tree param name conflicts in router.go.
|
||||
Strategy: Replace generic `:id` with specific resource param names everywhere.
|
||||
|
||||
Rules (based on Chatwoot convention and Gin tree requirements):
|
||||
- accounts/:id → :account_id (but most already use :account_id)
|
||||
- conversations/:id → :conversation_id (already done)
|
||||
- messages/:id → :message_id
|
||||
- agent_bots/:id → :agent_bot_id
|
||||
- assistants/:id → :assistant_id
|
||||
- inboxes/:id → :inbox_id (but some already use :inbox_id)
|
||||
- teams/:id → :team_id
|
||||
- contacts/:id → :contact_id
|
||||
- campaigns/:id → :campaign_id
|
||||
- labels/:id → :label_id
|
||||
- automation_rules/:id → :automation_id
|
||||
- portals/:id → :portal_id
|
||||
- categories/:id → :category_id
|
||||
- articles/:id → :article_id
|
||||
- canned_responses/:id → :canned_id
|
||||
- macros/:id → :macro_id
|
||||
- custom_attribute_definitions/:id → :attribute_id
|
||||
- csat_survey_responses/:id → :csat_id
|
||||
- notes/:id → :note_id
|
||||
- webhook_subscriptions/:id → :webhook_id
|
||||
- team_members/:id → :member_id (or :user_id)
|
||||
- dashboard_apps/:id → :dashboard_app_id
|
||||
- notification_subscriptions/:identifier → keep (no conflict)
|
||||
- push_subscriptions/:id → :push_subscription_id
|
||||
- notifications/:id → :notification_id
|
||||
- draft_messages/:id → :draft_id
|
||||
- contacts/:id/custom_attributes → :contact_id
|
||||
- companies/:id → :company_id
|
||||
- sla_policies/:id → :policy_id
|
||||
- custom_roles/:id → :custom_role_id
|
||||
- folders/:id → :folder_id
|
||||
- inbox_members/:id → :member_id
|
||||
- hooks/:id → :hook_id
|
||||
"""
|
||||
import re
|
||||
|
||||
router_path = '/home/yanghao05/Workspace/gochat/internal/router/router.go'
|
||||
handler_dir = '/home/yanghao05/Workspace/gochat/internal/handler/api/v1'
|
||||
|
||||
# Read router.go
|
||||
with open(router_path) as f:
|
||||
router_content = f.read()
|
||||
router_lines = router_content.split('\n')
|
||||
|
||||
# Context-aware replacement map
|
||||
# For each resource type, map :id → specific param name
|
||||
# We need to know which Group variable the route is under
|
||||
|
||||
# First pass: extract group variable → resource type mapping
|
||||
# e.g. conversations := conversations.Group(...) → conversations var handles conversation routes
|
||||
# msgs := conversations.Group("/:conversation_id/messages") → msgs var handles message routes
|
||||
|
||||
# Build a mapping of: (group_var_name → (parent_group_var, path_segment, specific_param_name))
|
||||
# Then when we see varName.GET("/:id", ...), we know what :id should be renamed to
|
||||
|
||||
# Simplified approach: look at the route context to determine the resource type
|
||||
# If route is under "conversations" group → :id means :conversation_id
|
||||
# If route is under "msgs" or "messages" group → :id means :message_id
|
||||
# etc.
|
||||
|
||||
# Let's trace the group hierarchy from the router.go
|
||||
group_map = {} # var_name → (parent_var, sub_path, implied_resource_type)
|
||||
|
||||
# Parse group definitions
|
||||
for i, line in enumerate(router_lines):
|
||||
stripped = line.strip()
|
||||
# Match: varName := parent.Group("/path/:param/...")
|
||||
gmatch = re.match(r'(\w+)\s*:?=\s*(\w+)\.Group\("([^"]+)"\)', stripped)
|
||||
if gmatch:
|
||||
var_name = gmatch.group(1)
|
||||
parent = gmatch.group(2)
|
||||
path = gmatch.group(3)
|
||||
group_map[var_name] = (parent, path)
|
||||
|
||||
# Now for each line with a bare :id in a route, determine what it should be renamed to
|
||||
# based on context (which group variable is used, what resource type it represents)
|
||||
|
||||
replacements_made = []
|
||||
|
||||
for i, line in enumerate(router_lines):
|
||||
stripped = line.strip()
|
||||
if stripped.startswith('//') or not stripped:
|
||||
continue
|
||||
|
||||
# Find route registrations with bare :id
|
||||
# Pattern: varName.GET("/:id", ...) or varName.GET("/:id/subpath", ...)
|
||||
# We need to determine which resource type :id refers to
|
||||
|
||||
# Check if line has a bare :id (not part of a longer name)
|
||||
if not re.search(r'"/:id(?:"|/)', stripped):
|
||||
continue
|
||||
|
||||
# Determine the resource type from the group variable name and context
|
||||
for method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'Any']:
|
||||
pattern = rf'^(\w+)\.{method}\("([^"]+)"\s*,'
|
||||
rmatch = re.match(pattern, stripped)
|
||||
if rmatch:
|
||||
var_name = rmatch.group(1)
|
||||
path = rmatch.group(2)
|
||||
|
||||
# Trace group hierarchy to determine resource type
|
||||
# Walk up from var_name through parent groups
|
||||
resource_param = None
|
||||
|
||||
# Special cases based on variable name patterns
|
||||
var_lower = var_name.lower()
|
||||
|
||||
if var_name == 'conversations':
|
||||
resource_param = 'conversation_id'
|
||||
elif var_name in ('msgs', 'messages') or 'message' in var_lower:
|
||||
resource_param = 'message_id'
|
||||
elif var_name == 'agents' or var_name == 'agentBotGroup':
|
||||
resource_param = 'agent_id'
|
||||
elif var_name == 'agentBots' or var_name == 'agentBotsGroup':
|
||||
resource_param = 'agent_bot_id'
|
||||
elif var_name == 'teams':
|
||||
resource_param = 'team_id'
|
||||
elif var_name == 'inboxes' or var_name == 'inboxGroup':
|
||||
resource_param = 'inbox_id'
|
||||
elif var_name == 'contacts' or var_name == 'contactGroup':
|
||||
resource_param = 'contact_id'
|
||||
elif var_name == 'campaigns' or var_name == 'campaignGroup':
|
||||
resource_param = 'campaign_id'
|
||||
elif var_name == 'labels':
|
||||
resource_param = 'label_id'
|
||||
elif var_name == 'automationRules' or var_name == 'automationGroup':
|
||||
resource_param = 'automation_id'
|
||||
elif var_name == 'portals' or var_name == 'portalGroup':
|
||||
resource_param = 'portal_id'
|
||||
elif var_name == 'categories':
|
||||
resource_param = 'category_id'
|
||||
elif var_name == 'articles':
|
||||
resource_param = 'article_id'
|
||||
elif var_name == 'cannedResponses' or var_name == 'cannedGroup':
|
||||
resource_param = 'canned_id'
|
||||
elif var_name == 'macros':
|
||||
resource_param = 'macro_id'
|
||||
elif var_name == 'customAttrDefs' or var_name == 'customAttrGroup':
|
||||
resource_param = 'attribute_id'
|
||||
elif var_name == 'notes' or var_name == 'noteGroup':
|
||||
resource_param = 'note_id'
|
||||
elif var_name == 'teamMembers':
|
||||
resource_param = 'member_id'
|
||||
elif var_name == 'dashboardApps':
|
||||
resource_param = 'dashboard_app_id'
|
||||
elif var_name == 'drafts':
|
||||
resource_param = 'draft_id'
|
||||
elif var_name == 'participants' or var_name == 'participantGroup':
|
||||
resource_param = 'participant_id'
|
||||
elif var_name == 'companies':
|
||||
resource_param = 'company_id'
|
||||
elif var_name == 'slaPolicies' or var_name == 'slaGroup':
|
||||
resource_param = 'policy_id'
|
||||
elif var_name == 'customRoles':
|
||||
resource_param = 'custom_role_id'
|
||||
elif var_name == 'folders':
|
||||
resource_param = 'folder_id'
|
||||
elif var_name == 'hooks' or var_name == 'hookGroup':
|
||||
resource_param = 'hook_id'
|
||||
elif var_name == 'assistants' or var_name == 'assistantGroup':
|
||||
resource_param = 'assistant_id'
|
||||
elif var_name == 'documents':
|
||||
resource_param = 'document_id'
|
||||
elif var_name == 'tools' or var_name == 'toolGroup':
|
||||
resource_param = 'tool_id'
|
||||
elif var_name == 'inboxMembers':
|
||||
resource_param = 'member_id'
|
||||
elif var_name == 'whatsappCalls':
|
||||
resource_param = 'call_id'
|
||||
elif var_name == 'botRules' or var_name == 'botRuleGroup':
|
||||
resource_param = 'rule_id'
|
||||
elif var_name == 'preferences':
|
||||
resource_param = 'preference_id'
|
||||
elif var_name == 'scenarios':
|
||||
resource_param = 'scenario_id'
|
||||
elif var_name == 'insights':
|
||||
resource_param = 'conversation_id' # insights are conversation-scoped
|
||||
|
||||
# Platform-level routes (not under accounts group)
|
||||
elif var_name == 'g' or var_name == 'v1':
|
||||
# Check path context
|
||||
if '/notifications' in path or '/notification' in path:
|
||||
resource_param = 'notification_id'
|
||||
elif '/push_subscriptions' in path:
|
||||
resource_param = 'push_subscription_id'
|
||||
else:
|
||||
# Check nearby lines for context
|
||||
resource_param = None
|
||||
|
||||
# accountScoped routes
|
||||
elif var_name == 'accountScoped' or var_name == 'accounts':
|
||||
# Path might be like "/:id" which means account_id
|
||||
if path == '/:id' or path.startswith('/:id'):
|
||||
resource_param = 'account_id'
|
||||
|
||||
if resource_param and resource_param != 'id':
|
||||
# Replace :id with :resource_param in the path
|
||||
new_path = path.replace('/:id"', f'/:{resource_param}"')
|
||||
new_path = new_path.replace('/:id/', f'/:{resource_param}/')
|
||||
|
||||
if new_path != path:
|
||||
# Replace in the original line
|
||||
new_line = line.replace(f'"{path}"', f'"{new_path}"')
|
||||
router_lines[i] = new_line
|
||||
replacements_made.append((i+1, var_name, path, new_path, resource_param))
|
||||
break
|
||||
|
||||
# Write back
|
||||
new_content = '\n'.join(router_lines)
|
||||
with open(router_path, 'w') as f:
|
||||
f.write(new_content)
|
||||
|
||||
print(f"Made {len(replacements_made)} replacements in router.go:")
|
||||
for linenum, var, old, new, param in replacements_made:
|
||||
print(f" Line {linenum}: {var} {old} → {new} (:id → :{param})")
|
||||
Reference in New Issue
Block a user