feat: complete platform features with local Supabase setup (security …#24
feat: complete platform features with local Supabase setup (security …#24saurabharch wants to merge 1 commit into
Conversation
…scrubbed and history cleaned)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Code Review
This pull request establishes a robust local development and deployment framework using Docker Compose, integrating a self-hosted Supabase stack with Infisical for secrets management and a monitoring suite (Grafana, Loki, Prometheus). It adds numerous database migrations, utility scripts, and extensive documentation regarding environment setup and data migration. Critical feedback identifies several issues: a hardcoded project URL in a SQL trigger, a security risk from a hardcoded JWT in the development environment file, and a configuration error in the functions service pointing to a non-existent directory. Furthermore, the deployment script contains redundant, buggy code, and the sprite analysis utility is duplicated across two files while referencing missing SVG assets.
|
|
||
| -- User must ensure the pg_net extension is enabled and the URL is correct for their project. | ||
| select | ||
| net.http_post( |
There was a problem hiding this comment.
The Supabase project URL is hardcoded in this trigger function. This prevents the migration from working correctly on other projects or local environments. It is better to use a relative path if supported by the environment or to store the base URL in a configuration table that can be updated per environment.
| # Use the HS256 ANON_KEY JWT — this is what Kong/PostgREST validates against JWT_SECRET | ||
| VITE_SUPABASE_PUBLISHABLE_KEY=your_jwt_here | ||
| VITE_SUPABASE_ANON_KEY=your_jwt_here | ||
| ANON_KEY_ASYMMETRIC=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImQxNTY1OWI5LTczNDEtNDQzMy05YzUwLTA3YWNmNDY2YjM5NiJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzczNzU5NDA2LCJleHAiOjE5MzE0Mzk0MDZ9.Auf7cXNmliguyY4tQUZoFNkdy0Q0qfP0-iTN-FisgfmZR1Q9nJ_Rkv-iH0_B9oq4d8X64BzxZWDQbEpBNV2d3A |
There was a problem hiding this comment.
| - start | ||
| - --main-service | ||
| - /home/deno/functions/main | ||
| healthcheck: |
There was a problem hiding this comment.
| apply_migrations() { | ||
| step "Applying DB migrations" | ||
| MIGRATIONS_DIR="${REPO_ROOT}/supabase/migrations" | ||
| [[ -d "$MIGRATIONS_DIR" ]] || { warn "No migrations dir — skipping"; return; } | ||
|
|
||
| # Create tracking table | ||
| docker exec "$DB_CONTAINER" psql -U postgres -d postgres -c " | ||
| CREATE TABLE IF NOT EXISTS _migration_log ( | ||
| filename TEXT PRIMARY KEY, | ||
| applied_at TIMESTAMPTZ DEFAULT now() | ||
| );" >/dev/null 2>&1 || true | ||
|
|
||
| for f in "$MIGRATIONS_DIR"/*.sql; do | ||
| [[ -f "$f" ]] || continue | ||
| fname="$(basename "$f")" | ||
|
|
||
| already=$(docker exec "$DB_CONTAINER" psql -U postgres -d postgres -tAc \ | ||
| "SELECT 1 FROM _migration_log WHERE filename = '$fname';" 2>/dev/null || echo "") | ||
|
|
||
| if [[ "$already" == "1" ]]; then | ||
| ok "Already applied: $fname" | ||
| continue | ||
| fi | ||
|
|
||
| echo -e " Applying: $fname" | ||
| if Get-Content "$f" | docker exec -i "$DB_CONTAINER" psql -U postgres -d postgres 2>&1; then | ||
| docker exec "$DB_CONTAINER" psql -U postgres -d postgres -c \ | ||
| "INSERT INTO _migration_log(filename) VALUES ('$fname') ON CONFLICT DO NOTHING;" >/dev/null 2>&1 || true | ||
| ok "Applied: $fname" | ||
| else | ||
| warn "Migration $fname had errors (may be safe to ignore)" | ||
| fi | ||
| done | ||
| } | ||
|
|
||
| # Bash version of apply_migrations (uses cat instead of Get-Content) |
There was a problem hiding this comment.
The apply_migrations function is defined twice. The first definition (lines 131-164) is redundant and contains a bug: it uses the PowerShell command Get-Content on line 156, which is not valid in a Bash script. Since a correct implementation using cat is provided starting on line 167, the first definition and the preceding comment should be removed to avoid confusion and potential execution errors.
| const fs = require('fs'); | ||
|
|
||
| function analyzeSprite(filePath, name) { | ||
| const txt = fs.readFileSync(filePath, 'utf8'); | ||
| console.log('\n========== ' + name + ' =========='); | ||
|
|
||
| // Extract SVG root dimensions | ||
| const rootM = txt.match(/<svg[^>]*width="([^"]+)"[^>]*height="([^"]+)"/); | ||
| if (rootM) console.log('Size: ' + rootM[1] + ' x ' + rootM[2]); | ||
|
|
||
| // Extract all clipPath rects / paths | ||
| const clipRects = [...txt.matchAll(/<clipPath[^>]*id="([^"]+)"[^>]*>[\s\S]*?<(?:rect|path)[^>]*(?:x="([^"]*)"[^>]*y="([^"]*)"[^>]*width="([^"]*)"[^>]*height="([^"]*)"[^>]*|d="M([0-9.]+)\s+([0-9.]+)h([0-9.]+)v([0-9.]+)[^"]*")[^>]*\/?>[\s\S]*?<\/clipPath>/g)]; | ||
| if (clipRects.length) { | ||
| console.log('ClipPath rects (' + clipRects.length + '):'); | ||
| clipRects.forEach(m => { | ||
| if (m[2]) console.log(' id=' + m[1] + ' x=' + m[2] + ' y=' + m[3] + ' w=' + m[4] + ' h=' + m[5]); | ||
| else if (m[6]) console.log(' id=' + m[1] + ' M=' + m[6] + ',' + m[7] + ' w=' + m[8] + ' h=' + m[9]); | ||
| }); | ||
| } | ||
|
|
||
| // Extract all mask rects | ||
| const maskRects = [...txt.matchAll(/<mask[^>]*id="([^"]+)"[^>]*>[\s\S]*?<(?:rect|path)[^>]*(?:x="([^"]*)"[^>]*y="([^"]*)"[^>]*width="([^"]*)"[^>]*height="([^"]*)"[^>]*|d="M([0-9.-]+)\s+([0-9.-]+)[^"]*w\s*=\s*"([^"]*)"[^>]*h\s*=\s*"([^"]*)"[^"]*")[^>]*\/?>[\s\S]*?<\/mask>/g)]; | ||
| if (maskRects.length) { | ||
| console.log('Mask rects (' + maskRects.length + '):'); | ||
| maskRects.forEach(m => { | ||
| if (m[2]) console.log(' id=' + m[1] + ' x=' + m[2] + ' y=' + m[3] + ' w=' + m[4] + ' h=' + m[5]); | ||
| }); | ||
| } | ||
|
|
||
| // Simpler: just find all rect elements in defs with x,y,width,height | ||
| const allRects = [...txt.matchAll(/<rect[^>]*x="([0-9.-]+)"[^>]*y="([0-9.-]+)"[^>]*width="([0-9.-]+)"[^>]*height="([0-9.-]+)"/g)]; | ||
| console.log('All rects with x,y,w,h (' + allRects.length + '):'); | ||
| allRects.forEach(m => console.log(' x=' + m[1] + ' y=' + m[2] + ' w=' + m[3] + ' h=' + m[4])); | ||
|
|
||
| // Find all absolute M commands in path data (indicator of icon corners) | ||
| const Ms = [...txt.matchAll(/(?:^|[\s,])M\s*([0-9.]+)\s+([0-9.]+)/g)]; | ||
| const unique = {}; | ||
| Ms.forEach(m => { | ||
| const x = Math.floor(parseFloat(m[1])); | ||
| const y = Math.floor(parseFloat(m[2])); | ||
| const key = x + '_' + y; | ||
| if (!unique[key]) unique[key] = {x, y, count: 0}; | ||
| unique[key].count++; | ||
| }); | ||
| const sorted = Object.values(unique).sort((a,b) => a.y-b.y || a.x-b.x); | ||
| // Only show coords with large round values (likely icon grid corners) | ||
| const corners = sorted.filter(p => p.count >= 2 && p.x % 1 === 0 && p.y % 1 === 0 && (p.x === 0 || p.x > 20)); | ||
| console.log('Likely icon corners (M cmds, count>=2): ' + corners.length); | ||
| corners.slice(0, 30).forEach(p => console.log(' x=' + p.x + ' y=' + p.y + ' count=' + p.count)); | ||
| } | ||
|
|
||
| analyzeSprite('src/assets/some-silo-sprite.svg', 'some-silo-sprite.svg'); | ||
| analyzeSprite('src/assets/common-header-sprite.svg', 'common-header-sprite.svg'); |
| analyzeSprite('src/assets/some-silo-sprite.svg', 'some-silo-sprite.svg'); | ||
| analyzeSprite('src/assets/common-header-sprite.svg', 'common-header-sprite.svg'); |
There was a problem hiding this comment.
…scrubbed and history cleaned)