AppForge: migrate off Supabase to external Postgres + Better Auth + local storage
- New Express + TypeScript backend (server/) with pg, Better Auth, local file storage - De-Supabased Postgres schema (server/db) and TS reimplementations of DB functions - Frontend data layer rewired to REST (rest-client + backend-client compat shim) - Removed all Supabase references (code, config, deps, docs) - New brand assets: gradient favicon/app icons + dark/white wordmark logos Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@@ -0,0 +1,98 @@
|
||||
# AppForge - Static SPA Configuration for cPanel/Apache
|
||||
# This file handles routing for a React Single Page Application
|
||||
|
||||
# Enable RewriteEngine
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
# Don't rewrite files or directories that exist
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# Rewrite all other requests to index.html for SPA routing
|
||||
RewriteRule ^ index.html [L]
|
||||
</IfModule>
|
||||
|
||||
# Disable directory browsing
|
||||
Options -Indexes
|
||||
|
||||
# Set default charset
|
||||
AddDefaultCharset UTF-8
|
||||
|
||||
# Security Headers
|
||||
<IfModule mod_headers.c>
|
||||
# CORS headers
|
||||
Header set Access-Control-Allow-Origin "*"
|
||||
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
|
||||
Header set Access-Control-Allow-Headers "Content-Type, Authorization"
|
||||
|
||||
# Security headers
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
Header set X-Frame-Options "SAMEORIGIN"
|
||||
Header set X-XSS-Protection "1; mode=block"
|
||||
Header set Referrer-Policy "strict-origin-when-cross-origin"
|
||||
|
||||
# Content Security Policy (CSP) - XSS Protection
|
||||
# Allows: self, Supabase APIs, Google Fonts, inline styles (required for React)
|
||||
Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.supabase.co https://cdn.gpteng.co; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' data: blob: https://*.supabase.co https://*.unsplash.com https://*.githubusercontent.com; connect-src 'self' https://*.supabase.co wss://*.supabase.co https://api.openai.com https://generativelanguage.googleapis.com https://api.elevenlabs.io; frame-src 'self' https://appetize.io; media-src 'self' blob: https://*.supabase.co; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self';"
|
||||
|
||||
# Permissions Policy (formerly Feature Policy)
|
||||
Header set Permissions-Policy "accelerometer=(), camera=(self), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(self), payment=(), usb=()"
|
||||
|
||||
# Strict Transport Security (HTTPS only)
|
||||
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
|
||||
# Prevent MIME type sniffing attacks
|
||||
Header set X-Permitted-Cross-Domain-Policies "none"
|
||||
|
||||
# Control DNS prefetching
|
||||
Header set X-DNS-Prefetch-Control "on"
|
||||
</IfModule>
|
||||
|
||||
# Compression for better performance
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/css application/json application/javascript text/xml application/xml application/xhtml+xml text/javascript
|
||||
</IfModule>
|
||||
|
||||
# Cache static assets
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
|
||||
# HTML - no cache (always get fresh for SPA)
|
||||
ExpiresByType text/html "access plus 0 seconds"
|
||||
|
||||
# Images - cache for 1 month
|
||||
ExpiresByType image/png "access plus 1 month"
|
||||
ExpiresByType image/jpg "access plus 1 month"
|
||||
ExpiresByType image/jpeg "access plus 1 month"
|
||||
ExpiresByType image/gif "access plus 1 month"
|
||||
ExpiresByType image/svg+xml "access plus 1 month"
|
||||
ExpiresByType image/webp "access plus 1 month"
|
||||
ExpiresByType image/x-icon "access plus 1 month"
|
||||
|
||||
# CSS/JS - cache for 1 year (Vite adds hashes to filenames)
|
||||
ExpiresByType text/css "access plus 1 year"
|
||||
ExpiresByType application/javascript "access plus 1 year"
|
||||
ExpiresByType text/javascript "access plus 1 year"
|
||||
|
||||
# Fonts
|
||||
ExpiresByType font/woff "access plus 1 year"
|
||||
ExpiresByType font/woff2 "access plus 1 year"
|
||||
ExpiresByType application/font-woff "access plus 1 year"
|
||||
ExpiresByType application/font-woff2 "access plus 1 year"
|
||||
</IfModule>
|
||||
|
||||
# Handle 404 errors - redirect to index.html for SPA
|
||||
ErrorDocument 404 /index.html
|
||||
|
||||
# MIME Types
|
||||
<IfModule mod_mime.c>
|
||||
AddType application/javascript .js
|
||||
AddType text/css .css
|
||||
AddType image/svg+xml .svg
|
||||
AddType application/json .json
|
||||
AddType font/woff .woff
|
||||
AddType font/woff2 .woff2
|
||||
</IfModule>
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 616 B |
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512" role="img" aria-label="AppForge">
|
||||
<defs>
|
||||
<linearGradient id="afgm" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#8B5CF6"/>
|
||||
<stop offset="0.55" stop-color="#6366F1"/>
|
||||
<stop offset="1" stop-color="#0EA5E9"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- full-bleed background for Android maskable safe zone -->
|
||||
<rect x="0" y="0" width="512" height="512" fill="url(#afgm)"/>
|
||||
<path d="M300 136 L182 316 H256 L242 392 L334 248 H272 Z"
|
||||
fill="#ffffff" stroke="#ffffff" stroke-width="5" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 641 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512" role="img" aria-label="AppForge">
|
||||
<defs>
|
||||
<linearGradient id="afg" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#8B5CF6"/>
|
||||
<stop offset="0.55" stop-color="#6366F1"/>
|
||||
<stop offset="1" stop-color="#0EA5E9"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="32" y="32" width="448" height="448" rx="112" fill="url(#afg)"/>
|
||||
<path d="M292 80 L150 296 H238 L220 432 L372 208 H280 Z"
|
||||
fill="#ffffff" stroke="#ffffff" stroke-width="6" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 585 B |
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 232 56" width="232" height="56" role="img" aria-label="AppForge">
|
||||
<defs>
|
||||
<linearGradient id="afl1" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#8B5CF6"/>
|
||||
<stop offset="0.55" stop-color="#6366F1"/>
|
||||
<stop offset="1" stop-color="#0EA5E9"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- icon mark -->
|
||||
<g transform="translate(6 8)">
|
||||
<rect x="0" y="0" width="40" height="40" rx="11" fill="url(#afl1)"/>
|
||||
<path d="M23 7 L11.5 24 H18.7 L17 33.5 L29 15.5 H21.7 Z"
|
||||
fill="#ffffff" stroke="#ffffff" stroke-width="0.8" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<!-- wordmark (dark — for light backgrounds) -->
|
||||
<text x="58" y="37" font-family="'Segoe UI', Inter, system-ui, -apple-system, Roboto, Arial, sans-serif"
|
||||
font-size="27" font-weight="700" letter-spacing="-0.5" fill="#0F172A">App<tspan fill="#6366F1">Forge</tspan></text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 933 B |
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 232 56" width="232" height="56" role="img" aria-label="AppForge">
|
||||
<defs>
|
||||
<linearGradient id="afl2" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#A78BFA"/>
|
||||
<stop offset="0.55" stop-color="#818CF8"/>
|
||||
<stop offset="1" stop-color="#38BDF8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<!-- icon mark -->
|
||||
<g transform="translate(6 8)">
|
||||
<rect x="0" y="0" width="40" height="40" rx="11" fill="url(#afl2)"/>
|
||||
<path d="M23 7 L11.5 24 H18.7 L17 33.5 L29 15.5 H21.7 Z"
|
||||
fill="#0B1020" stroke="#0B1020" stroke-width="0.8" stroke-linejoin="round"/>
|
||||
</g>
|
||||
<!-- wordmark (white — for dark backgrounds) -->
|
||||
<text x="58" y="37" font-family="'Segoe UI', Inter, system-ui, -apple-system, Roboto, Arial, sans-serif"
|
||||
font-size="27" font-weight="700" letter-spacing="-0.5" fill="#FFFFFF">App<tspan fill="#A5B4FC">Forge</tspan></text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 933 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" fill="none"><rect width="1200" height="1200" fill="#EAEAEA" rx="3"/><g opacity=".5"><g opacity=".5"><path fill="#FAFAFA" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/></g><path stroke="url(#a)" stroke-width="2.418" d="M0-1.209h553.581" transform="scale(1 -1) rotate(45 1163.11 91.165)"/><path stroke="url(#b)" stroke-width="2.418" d="M404.846 598.671h391.726"/><path stroke="url(#c)" stroke-width="2.418" d="M599.5 795.742V404.017"/><path stroke="url(#d)" stroke-width="2.418" d="m795.717 796.597-391.441-391.44"/><path fill="#fff" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/><g clip-path="url(#e)"><path fill="#666" fill-rule="evenodd" d="M616.426 586.58h-31.434v16.176l3.553-3.554.531-.531h9.068l.074-.074 8.463-8.463h2.565l7.18 7.181V586.58Zm-15.715 14.654 3.698 3.699 1.283 1.282-2.565 2.565-1.282-1.283-5.2-5.199h-6.066l-5.514 5.514-.073.073v2.876a2.418 2.418 0 0 0 2.418 2.418h26.598a2.418 2.418 0 0 0 2.418-2.418v-8.317l-8.463-8.463-7.181 7.181-.071.072Zm-19.347 5.442v4.085a6.045 6.045 0 0 0 6.046 6.045h26.598a6.044 6.044 0 0 0 6.045-6.045v-7.108l1.356-1.355-1.282-1.283-.074-.073v-17.989h-38.689v23.43l-.146.146.146.147Z" clip-rule="evenodd"/></g><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/></g><defs><linearGradient id="a" x1="554.061" x2="-.48" y1=".083" y2=".087" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="b" x1="796.912" x2="404.507" y1="599.963" y2="599.965" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="c" x1="600.792" x2="600.794" y1="403.677" y2="796.082" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="d" x1="404.85" x2="796.972" y1="403.903" y2="796.02" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><clipPath id="e"><path fill="#fff" d="M581.364 580.535h38.689v38.689h-38.689z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 72 KiB |
@@ -0,0 +1,14 @@
|
||||
User-agent: Googlebot
|
||||
Allow: /
|
||||
|
||||
User-agent: Bingbot
|
||||
Allow: /
|
||||
|
||||
User-agent: Twitterbot
|
||||
Allow: /
|
||||
|
||||
User-agent: facebookexternalhit
|
||||
Allow: /
|
||||
|
||||
User-agent: *
|
||||
Allow: /
|
||||
@@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||