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,149 @@
|
||||
import re, sys
|
||||
|
||||
# Parse Chatwoot routes.rb to extract all API routes
|
||||
# Strategy: walk through the file tracking namespace nesting depth
|
||||
|
||||
with open('/home/yanghao05/Workspace/chatwoot-reference/config/routes.rb') as f:
|
||||
text = f.read()
|
||||
|
||||
# We need to track namespace nesting and extract route definitions
|
||||
# Rails routes.rb uses nested blocks like:
|
||||
# namespace :api do
|
||||
# namespace :v1 do
|
||||
# resources :accounts do
|
||||
# resources :conversations do
|
||||
# member { post :assign }
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
# Parse the file line by line, tracking nesting
|
||||
routes = []
|
||||
namespace_stack = []
|
||||
resource_stack = [] # Each entry: (resource_name, is_singular, id_param_name)
|
||||
current_scope = [] # Track scope/member/collection blocks
|
||||
in_member = False
|
||||
in_collection = False
|
||||
skip_frontend = True # Skip frontend/dashboard routes
|
||||
|
||||
lines = text.split('\n')
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i].strip()
|
||||
|
||||
# Skip comments
|
||||
if line.startswith('#'):
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Skip frontend routes (dashboard, app routes)
|
||||
if skip_frontend and ('dashboard' in line or 'app' in line and '/app' in line):
|
||||
# Need to skip entire frontend block
|
||||
if 'root' in line or '/app' in line or 'dashboard#index' in line:
|
||||
# Skip until we hit the next significant keyword
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Detect namespace declarations
|
||||
ns_match = re.match(r'namespace\s+:(\w+)\s+do', line)
|
||||
if ns_match:
|
||||
namespace_stack.append(ns_match.group(1))
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Detect scope declarations
|
||||
scope_match = re.match(r'scope\s+[\'":](\w+)[\'":]\s+do', line)
|
||||
if scope_match:
|
||||
namespace_stack.append(scope_match.group(1))
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Detect resources declarations
|
||||
res_match = re.match(r'resources\s+:(\w+)(?:\s*,\s*(.+))?\s+do', line)
|
||||
if res_match:
|
||||
resource_name = res_match.group(1)
|
||||
options = res_match.group(2) or ''
|
||||
# Check if only certain actions
|
||||
only_match = re.search(r'only:\s*\[([^\]]+)\]', options)
|
||||
resource_stack.append(resource_name)
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Also handle resources without block (one-liner)
|
||||
res_simple = re.match(r'resources\s+:(\w+)(?:\s*,\s*(.+))?(?!\s+do)', line)
|
||||
if res_simple and not res_match:
|
||||
resource_name = res_simple.group(1)
|
||||
options = res_simple.group(2) or ''
|
||||
|
||||
# Generate standard CRUD routes for this resource
|
||||
only_match = re.search(r'only:\s*\[([^\]]+)\]', options)
|
||||
except_match = re.search(r'except:\s*\[([^\]]+)\]', options)
|
||||
|
||||
allowed_actions = ['index', 'show', 'create', 'update', 'destroy', 'new', 'edit']
|
||||
if only_match:
|
||||
allowed_actions = [a.strip() for a in only_match.group(1).split(',')]
|
||||
elif except_match:
|
||||
excluded = [a.strip() for a in except_match.group(1).split(',')]
|
||||
allowed_actions = [a for a in allowed_actions if a not in excluded]
|
||||
|
||||
# Build path prefix from namespace stack + resource stack
|
||||
path_prefix = '/'.join(namespace_stack + resource_stack)
|
||||
id_param = f':{resource_name}_id' if resource_name != resource_stack[-1] if resource_stack else f':id'
|
||||
|
||||
# Map actions to routes
|
||||
action_routes = {
|
||||
'index': ('GET', f'/api/{path_prefix}/{resource_name}'),
|
||||
'show': ('GET', f'/api/{path_prefix}/{resource_name}/:{resource_name[:-1] if resource_name.endswith("s") else resource_name}_id' if resource_name.endswith('s') else f'/api/{path_prefix}/{resource_name}/:id'),
|
||||
'create': ('POST', f'/api/{path_prefix}/{resource_name}'),
|
||||
'update': ('PUT', f'/api/{path_prefix}/{resource_name}/:{resource_name[:-1] if resource_name.endswith("s") else resource_name}_id' if resource_name.endswith('s') else f'/api/{path_prefix}/{resource_name}/:id'),
|
||||
'destroy': ('DELETE', f'/api/{path_prefix}/{resource_name}/:{resource_name[:-1] if resource_name.endswith("s") else resource_name}_id' if resource_name.endswith('s') else f'/api/{path_prefix}/{resource_name}/:id'),
|
||||
}
|
||||
|
||||
# Simplify: just record the resource with its nesting
|
||||
routes.append(f'CRUD:{"/".join(namespace_stack + resource_stack)}/{resource_name}')
|
||||
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Detect member block
|
||||
if 'member' in line and 'do' in line:
|
||||
in_member = True
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Detect collection block
|
||||
if 'collection' in line and 'do' in line:
|
||||
in_collection = True
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Detect explicit route definitions (get/post/put/patch/delete)
|
||||
route_match = re.match(r'(get|post|put|patch|delete)\s+[\'":]([^\'":\s]+)[\'":]', line)
|
||||
if route_match:
|
||||
method = route_match.group(1).upper()
|
||||
path = route_match.group(2)
|
||||
routes.append(f'{method}:{"/".join(namespace_stack)}/{path}')
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Detect end blocks (pop from stacks)
|
||||
if line == 'end':
|
||||
if in_member:
|
||||
in_member = False
|
||||
elif in_collection:
|
||||
in_collection = False
|
||||
elif resource_stack:
|
||||
resource_stack.pop()
|
||||
elif namespace_stack:
|
||||
namespace_stack.pop()
|
||||
i += 1
|
||||
continue
|
||||
|
||||
i += 1
|
||||
|
||||
# Print what we found
|
||||
print("=== Parsed Chatwoot routes.rb ===")
|
||||
for r in routes:
|
||||
print(r)
|
||||
print(f"\nTotal route definitions found: {len(routes)}")
|
||||
Reference in New Issue
Block a user