chore: replace Caddy with Portless for local HTTPS proxy (#258)

## Summary

- Replace Caddy reverse proxy with [Portless](https://portless.sh) for
local HTTPS, eliminating manual cert generation and `/etc/hosts` editing
- Two URL strategies coexist: `composer run dev` uses worktree-aware
URLs (`https://<branch>.dev.whisper.money.localhost`), `whispermoney
start` uses a fixed alias (`https://whisper.money.localhost`)
- Remove Caddyfile, `docker/caddy/` directory, caddy service from
`compose.yaml`, and cert-related `.gitignore` entries
- Simplify `vite.config.ts` by removing caddy cert detection block (Vite
stays on plain HTTP since browsers treat localhost as secure context)
- Overhaul `public/setup.sh` to use `portless trust`, `portless proxy
start`, and `portless alias` instead of SSL cert generation and hosts
file editing
This commit is contained in:
Víctor Falcón 2026-04-02 16:39:44 +01:00 committed by GitHub
parent 83f7e83a13
commit 259a9a9712
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 62 additions and 356 deletions

View File

@ -2,7 +2,7 @@ APP_NAME="Whisper Money"
APP_ENV=local APP_ENV=local
APP_KEY= APP_KEY=
APP_DEBUG=true APP_DEBUG=true
APP_URL=https://whisper.money.local APP_URL=https://whisper.money.localhost
APP_LOCALE=en APP_LOCALE=en
APP_FALLBACK_LOCALE=en APP_FALLBACK_LOCALE=en

2
.gitignore vendored
View File

@ -28,7 +28,5 @@ yarn-error.log
/.vscode /.vscode
/.zed /.zed
.php-cs-fixer.cache .php-cs-fixer.cache
/docker/caddy/certs/*.pem
/docker/caddy/certs/*-key.pem
.claude/settings.local.json .claude/settings.local.json
.php-version .php-version

View File

@ -1,16 +0,0 @@
whisper.money.local {
# Use mkcert certificates (generated by setup script)
# These certificates are automatically trusted by browsers
tls /etc/caddy/certs/whisper.money.local.pem /etc/caddy/certs/whisper.money.local-key.pem
reverse_proxy host.docker.internal:8000
header {
-Server
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
X-XSS-Protection "1; mode=block"
}
encode zstd gzip
}

View File

@ -51,7 +51,7 @@ The easiest way to get started is using our automated setup script:
bash <(curl -fsSL https://whisper.money/setup.sh) bash <(curl -fsSL https://whisper.money/setup.sh)
``` ```
After installation, just visit **<https://whisper.money.local>** in your browser. After installation, just visit **<https://whisper.money.localhost>** in your browser.
### Manual Setup ### Manual Setup
@ -100,12 +100,12 @@ composer run dev
This will concurrently start: This will concurrently start:
- PHP development server - PHP development server (via [Portless](https://portless.sh) HTTPS proxy)
- Queue worker - Queue worker
- Log viewer (Pail) - Log viewer (Pail)
- Vite dev server - Vite dev server
The application will be available at **<https://whisper.money.local>** or **<http://localhost:8000>** (direct PHP server). The application will be available at **<https://dev.whisper.money.localhost>**. In git worktrees, the branch name is automatically prepended (e.g. `https://fix-ui.dev.whisper.money.localhost`).
## Running with Docker (Production Image) ## Running with Docker (Production Image)

View File

@ -75,6 +75,7 @@
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"jsdom": "^25.0.1", "jsdom": "^25.0.1",
"playwright": "^1.58.2", "playwright": "^1.58.2",
"portless": "^0.9.2",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"prettier-plugin-organize-imports": "^4.3.0", "prettier-plugin-organize-imports": "^4.3.0",
"prettier-plugin-tailwindcss": "^0.6.14", "prettier-plugin-tailwindcss": "^0.6.14",
@ -1459,6 +1460,8 @@
"playwright-core": ["playwright-core@1.58.2", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg=="], "playwright-core": ["playwright-core@1.58.2", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg=="],
"portless": ["portless@0.9.2", "", { "os": [ "linux", "win32", "darwin", ], "bin": { "portless": "dist/cli.js" } }, "sha512-sB8orlek3wrBwzZhggRAfEzplS+3srYBfSlArl6ZqAenFvK1hYEI2Rb1W+oFmxcrKCDn5WomNMVWDijio8ds9w=="],
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],

View File

@ -1,20 +1,4 @@
services: services:
caddy:
image: 'caddy:latest'
ports:
- '80:80'
- '443:443'
- '443:443/udp'
volumes:
- './Caddyfile:/etc/caddy/Caddyfile:ro'
- './docker/caddy/certs:/etc/caddy/certs:ro'
- 'caddy_data:/data'
- 'caddy_config:/config'
networks:
- sail
extra_hosts:
- 'host.docker.internal:host-gateway'
restart: unless-stopped
mysql: mysql:
image: 'mysql/mysql-server:8.0' image: 'mysql/mysql-server:8.0'
ports: ports:
@ -97,7 +81,3 @@ volumes:
driver: local driver: local
sail-redis: sail-redis:
driver: local driver: local
caddy_data:
driver: local
caddy_config:
driver: local

View File

@ -61,12 +61,12 @@
], ],
"dev": [ "dev": [
"Composer\\Config::disableProcessTimeout", "Composer\\Config::disableProcessTimeout",
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74,#2dd4bf\" \"php artisan serve\" \"php artisan queue:listen --tries=1 --queue=emails\" \"php artisan pail --timeout=0\" \"bun run dev\" \"stripe listen --forward-to https://whisper.money.local/stripe/webhook\" --names=server,emails-queue,logs,vite,stripe" "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74,#2dd4bf\" \"npx portless run --name dev.whisper.money --app-port 8000 php artisan serve\" \"php artisan queue:listen --tries=1 --queue=emails\" \"php artisan pail --timeout=0\" \"bun run dev\" \"stripe listen --forward-to https://dev.whisper.money.localhost/stripe/webhook\" --names=server,emails-queue,logs,vite,stripe"
], ],
"dev:ssr": [ "dev:ssr": [
"bun run build:ssr", "bun run build:ssr",
"Composer\\Config::disableProcessTimeout", "Composer\\Config::disableProcessTimeout",
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74,#2dd4bf\" \"php artisan serve\" \"php artisan queue:listen --tries=1 --queue=emails\" \"php artisan pail --timeout=0\" \"php artisan inertia:start-ssr --runtime=bun\" --names=server,emails-queue,logs,ssr,stripe --kill-others" "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74,#2dd4bf\" \"npx portless run --name dev.whisper.money --app-port 8000 php artisan serve\" \"php artisan queue:listen --tries=1 --queue=emails\" \"php artisan pail --timeout=0\" \"php artisan inertia:start-ssr --runtime=bun\" --names=server,emails-queue,logs,ssr,stripe --kill-others"
], ],
"test": [ "test": [
"@php artisan config:clear --ansi", "@php artisan config:clear --ansi",

View File

@ -1,41 +0,0 @@
#!/bin/bash
set -e
certs_dir="$(dirname "$0")/certs"
domain="whisper.money.local"
cert_file="${certs_dir}/${domain}.pem"
key_file="${certs_dir}/${domain}-key.pem"
mkdir -p "$certs_dir"
if command -v mkcert &> /dev/null; then
if [ ! -f "$(mkcert -CAROOT)/rootCA.pem" ]; then
echo "Installing local CA..."
mkcert -install
fi
echo "Generating certificates for ${domain}..."
mkcert -cert-file "$cert_file" -key-file "$key_file" "$domain" "*.${domain}"
echo "Certificates generated successfully!"
echo "Files created:"
echo " - ${cert_file}"
echo " - ${key_file}"
elif command -v openssl &> /dev/null; then
echo "Creating self-signed certificates (browser will show a warning)..."
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "$key_file" \
-out "$cert_file" \
-subj "/CN=${domain}/O=Development/C=US" \
-addext "subjectAltName=DNS:${domain},DNS:*.${domain},IP:127.0.0.1" \
2>/dev/null
chmod 644 "$cert_file"
chmod 600 "$key_file"
echo "Self-signed certificates created"
echo "Note: Your browser will show a security warning."
else
echo "Error: Neither mkcert nor openssl is available"
echo "Please install mkcert (recommended) or openssl"
exit 1
fi

View File

@ -34,6 +34,7 @@
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"jsdom": "^25.0.1", "jsdom": "^25.0.1",
"playwright": "^1.58.2", "playwright": "^1.58.2",
"portless": "^0.9.2",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"prettier-plugin-organize-imports": "^4.3.0", "prettier-plugin-organize-imports": "^4.3.0",
"prettier-plugin-tailwindcss": "^0.6.14", "prettier-plugin-tailwindcss": "^0.6.14",

View File

@ -318,19 +318,6 @@ find_available_port() {
check_and_configure_ports() { check_and_configure_ports() {
echo -e "${BLUE}Checking port availability...${NC}" echo -e "${BLUE}Checking port availability...${NC}"
# Check required ports (Caddy and PHP - these cannot be changed)
if ! is_port_available 80; then
echo -e "${RED}Port 80 is already in use. Caddy requires this port.${NC}"
echo -e "${YELLOW}Please stop the service using port 80 and try again.${NC}"
exit 1
fi
if ! is_port_available 443; then
echo -e "${RED}Port 443 is already in use. Caddy requires this port.${NC}"
echo -e "${YELLOW}Please stop the service using port 443 and try again.${NC}"
exit 1
fi
if ! is_port_available 8000; then if ! is_port_available 8000; then
echo -e "${RED}Port 8000 is already in use. PHP requires this port.${NC}" echo -e "${RED}Port 8000 is already in use. PHP requires this port.${NC}"
echo -e "${YELLOW}Please stop the service using port 8000 and try again.${NC}" echo -e "${YELLOW}Please stop the service using port 8000 and try again.${NC}"
@ -553,232 +540,47 @@ wait_for_service() {
return 1 return 1
} }
# Update /etc/hosts file # Setup portless for HTTPS reverse proxy
update_hosts_file() { setup_portless() {
local hostname="whisper.money.local"
local ip="127.0.0.1"
local hosts_line="${ip} ${hostname}"
local hosts_file="/etc/hosts"
echo "" echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW}Hosts File Update${NC}" echo -e "${YELLOW}HTTPS Setup (Portless)${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo "" echo ""
echo -e "${BLUE}To access your application at ${GREEN}https://${hostname}${NC}${BLUE}, we need to${NC}"
echo -e "${BLUE}add an entry to your system's hosts file (${hosts_file}).${NC}"
echo ""
echo -e "${YELLOW}What we'll do:${NC}"
echo -e " • Add the line: ${GREEN}${hosts_line}${NC}"
echo -e " • This maps ${hostname} to your local machine (127.0.0.1)"
echo ""
echo -e "${YELLOW}Why we need sudo access:${NC}"
echo -e " • The hosts file is a system file that requires administrator privileges"
echo -e " • We'll only add the entry if it doesn't already exist (idempotent)"
echo ""
# Check if the line already exists if ! command_exists npx; then
if grep -q "${hostname}" "${hosts_file}" 2>/dev/null; then echo -e "${RED}npx is not available. Portless requires Node.js.${NC}"
if grep -q "${hosts_line}" "${hosts_file}" 2>/dev/null; then echo -e "${YELLOW}Skipping HTTPS setup. The app will be available at http://localhost:8000${NC}"
echo -e "${GREEN}✓ The hosts entry already exists. No changes needed.${NC}"
return 0
else
echo -e "${YELLOW}⚠ Found a different entry for ${hostname} in ${hosts_file}${NC}"
echo -e "${YELLOW} Please review it manually.${NC}"
echo ""
fi
fi
read -p "Would you like us to update the hosts file automatically? (y/n) " -n 1 -r
echo ""
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Updating hosts file (sudo access required)...${NC}"
# Use sudo to add the line if it doesn't exist
if sudo sh -c "grep -q '${hosts_line}' ${hosts_file} || echo '${hosts_line}' >> ${hosts_file}"; then
echo -e "${GREEN}✓ Successfully updated ${hosts_file}${NC}"
echo -e "${GREEN} Added: ${hosts_line}${NC}"
return 0
else
echo -e "${RED}✗ Failed to update ${hosts_file}${NC}"
echo -e "${YELLOW} You'll need to add it manually.${NC}"
show_manual_hosts_instructions
return 1
fi
else
echo -e "${YELLOW}Hosts file update skipped.${NC}"
show_manual_hosts_instructions
return 1 return 1
fi fi
}
# Setup SSL certificates using mkcert for transparent trust # Trust the portless CA (one-time setup, auto-elevates with sudo)
setup_ssl_certificates() { echo -e "${YELLOW}Setting up portless HTTPS certificates...${NC}"
local certs_dir="docker/caddy/certs" if npx portless trust 2>/dev/null; then
local domain="whisper.money.local" echo -e "${GREEN}✓ Portless CA trusted${NC}"
local cert_file="${certs_dir}/${domain}.pem"
local key_file="${certs_dir}/${domain}-key.pem"
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW}SSL Certificate Setup${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
# Check if certificates already exist
if [ -f "$cert_file" ] && [ -f "$key_file" ]; then
echo -e "${GREEN}✓ SSL certificates already exist${NC}"
return 0
fi
# Check if mkcert is installed
if ! command_exists mkcert; then
echo -e "${YELLOW}mkcert is not installed. It's needed to generate trusted SSL certificates.${NC}"
echo ""
echo -e "${BLUE}mkcert creates locally-trusted certificates that browsers automatically trust.${NC}"
echo ""
if [[ "$OSTYPE" == "darwin"* ]]; then
echo -e "${YELLOW}Install mkcert:${NC}"
echo -e " ${GREEN}brew install mkcert${NC}"
echo -e " ${GREEN}brew install nss${NC} # For Firefox support"
echo ""
read -p "Would you like to install mkcert now? (y/n) " -n 1 -r
echo ""
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
if command_exists brew; then
echo -e "${YELLOW}Installing mkcert...${NC}"
brew install mkcert nss
echo -e "${GREEN}✓ mkcert installed${NC}"
else
echo -e "${RED}Homebrew is not installed. Please install mkcert manually:${NC}"
echo -e " ${YELLOW}brew install mkcert nss${NC}"
echo ""
echo -e "${YELLOW}Or visit: https://github.com/FiloSottile/mkcert#installation${NC}"
create_fallback_certificates
return 1
fi
else
echo -e "${YELLOW}Using fallback self-signed certificates...${NC}"
create_fallback_certificates
return 0
fi
else
echo -e "${YELLOW}Install mkcert:${NC}"
echo -e " ${GREEN}sudo apt install libnss3-tools${NC} # Debian/Ubuntu"
echo -e " ${GREEN}wget -O /tmp/mkcert https://github.com/FiloSottile/mkcert/releases/latest/download/mkcert-v1.4.4-linux-amd64${NC}"
echo -e " ${GREEN}chmod +x /tmp/mkcert && sudo mv /tmp/mkcert /usr/local/bin/mkcert${NC}"
echo ""
echo -e "${YELLOW}Or visit: https://github.com/FiloSottile/mkcert#installation${NC}"
echo ""
read -p "Continue with fallback self-signed certificates? (y/n) " -n 1 -r
echo ""
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Please install mkcert and run the setup again.${NC}"
exit 1
fi
create_fallback_certificates
return 0
fi
fi
# Install local CA if not already installed
if [ ! -f "$(mkcert -CAROOT)/rootCA.pem" ]; then
echo -e "${YELLOW}Installing mkcert local CA (requires sudo)...${NC}"
mkcert -install
echo -e "${GREEN}✓ Local CA installed${NC}"
else else
echo -e "${GREEN}✓ Local CA already installed${NC}" echo -e "${YELLOW}⚠ Could not trust portless CA automatically.${NC}"
echo -e "${YELLOW} You can run 'npx portless trust' manually later.${NC}"
fi fi
# Create certs directory # Start the portless proxy if not already running
mkdir -p "$certs_dir" echo -e "${YELLOW}Starting portless proxy...${NC}"
if npx portless proxy start 2>/dev/null; then
# Generate certificate echo -e "${GREEN}✓ Portless proxy running${NC}"
echo -e "${YELLOW}Generating SSL certificate for ${domain}...${NC}"
if mkcert -cert-file "$cert_file" -key-file "$key_file" "$domain" "*.${domain}"; then
echo -e "${GREEN}✓ SSL certificate generated successfully${NC}"
echo -e "${GREEN} Certificate: ${cert_file}${NC}"
echo -e "${GREEN} Key: ${key_file}${NC}"
echo ""
echo -e "${BLUE}Your browser will automatically trust this certificate!${NC}"
return 0
else else
echo -e "${RED}✗ Failed to generate certificate${NC}" echo -e "${YELLOW}⚠ Could not start portless proxy automatically.${NC}"
create_fallback_certificates echo -e "${YELLOW} It may already be running, or you can start it with 'npx portless proxy start'.${NC}"
return 1
fi
}
# Create fallback self-signed certificates
create_fallback_certificates() {
local certs_dir="docker/caddy/certs"
local domain="whisper.money.local"
local cert_file="${certs_dir}/${domain}.pem"
local key_file="${certs_dir}/${domain}-key.pem"
echo ""
echo -e "${YELLOW}Creating self-signed certificates (browser will show a warning)...${NC}"
mkdir -p "$certs_dir"
# Check if openssl is available
if command_exists openssl; then
# Create certificate with proper SAN extension
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "$key_file" \
-out "$cert_file" \
-subj "/CN=${domain}/O=Development/C=US" \
-addext "subjectAltName=DNS:${domain},DNS:*.${domain},IP:127.0.0.1" \
2>/dev/null
if [ -f "$cert_file" ] && [ -f "$key_file" ]; then
# Set proper permissions
chmod 644 "$cert_file"
chmod 600 "$key_file"
echo -e "${GREEN}✓ Self-signed certificates created${NC}"
echo -e "${YELLOW} Note: Your browser will show a security warning.${NC}"
echo -e "${YELLOW} Click 'Advanced' → 'Proceed to ${domain}' to continue.${NC}"
return 0
fi
fi fi
echo -e "${RED}✗ Could not create certificates${NC}" # Register the fixed alias for whisper.money
echo -e "${YELLOW} Please install mkcert or openssl to generate certificates.${NC}" echo -e "${YELLOW}Registering HTTPS route...${NC}"
return 1 if npx portless alias whisper.money 8000 --force 2>/dev/null; then
} echo -e "${GREEN}✓ https://whisper.money.localhost → localhost:8000${NC}"
else
echo -e "${YELLOW}⚠ Could not register portless alias.${NC}"
echo -e "${YELLOW} You can run 'npx portless alias whisper.money 8000' manually.${NC}"
fi
# Show manual hosts file instructions
show_manual_hosts_instructions() {
local hostname="whisper.money.local"
local hosts_line="127.0.0.1 ${hostname}"
echo ""
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW}Manual Hosts File Update${NC}"
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e "${BLUE}To access your application, please add this line to ${YELLOW}/etc/hosts${NC}${BLUE}:${NC}"
echo ""
echo -e " ${GREEN}${hosts_line}${NC}"
echo ""
echo -e "${YELLOW}Instructions:${NC}"
echo -e " 1. Open ${YELLOW}/etc/hosts${NC} with a text editor (requires sudo):"
echo -e " ${YELLOW}sudo nano /etc/hosts${NC} ${BLUE}(or use your preferred editor)${NC}"
echo ""
echo -e " 2. Add the line at the end of the file:"
echo -e " ${GREEN}${hosts_line}${NC}"
echo ""
echo -e " 3. Save and close the file"
echo ""
echo -e "${YELLOW}Note:${NC} If the line already exists, you can skip this step."
echo "" echo ""
} }
@ -800,11 +602,25 @@ start_services() {
wait_for_service "php" wait_for_service "php"
fi fi
echo -e "${GREEN}All services are running!${NC}" # Register portless alias for HTTPS access
if command_exists npx; then
npx portless proxy start 2>/dev/null || true
npx portless alias whisper.money 8000 --force 2>/dev/null || true
echo -e "${GREEN}All services are running!${NC}"
echo -e " ${GREEN}Visit: https://whisper.money.localhost${NC}"
else
echo -e "${GREEN}All services are running!${NC}"
echo -e " ${GREEN}Visit: http://localhost:8000${NC}"
fi
} }
# Stop Docker services # Stop Docker services
stop_services() { stop_services() {
# Remove portless alias
if command_exists npx; then
npx portless alias --remove whisper.money 2>/dev/null || true
fi
echo -e "${BLUE}Stopping Docker services...${NC}" echo -e "${BLUE}Stopping Docker services...${NC}"
docker compose down docker compose down
echo -e "${GREEN}Services stopped.${NC}" echo -e "${GREEN}Services stopped.${NC}"
@ -839,11 +655,11 @@ install() {
# Update APP_URL if needed # Update APP_URL if needed
if grep -q "APP_URL=https://whispermoney.test" .env 2>/dev/null || grep -q "APP_URL=$" .env 2>/dev/null; then if grep -q "APP_URL=https://whispermoney.test" .env 2>/dev/null || grep -q "APP_URL=$" .env 2>/dev/null; then
echo -e "${YELLOW}Updating APP_URL to https://whisper.money.local...${NC}" echo -e "${YELLOW}Updating APP_URL to https://whisper.money.localhost...${NC}"
if [[ "$OSTYPE" == "darwin"* ]]; then if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' 's|APP_URL=.*|APP_URL=https://whisper.money.local|g' .env sed -i '' 's|APP_URL=.*|APP_URL=https://whisper.money.localhost|g' .env
else else
sed -i 's|APP_URL=.*|APP_URL=https://whisper.money.local|g' .env sed -i 's|APP_URL=.*|APP_URL=https://whisper.money.localhost|g' .env
fi fi
echo -e "${GREEN}APP_URL updated.${NC}" echo -e "${GREEN}APP_URL updated.${NC}"
else else
@ -877,10 +693,6 @@ install() {
echo "" echo ""
# Update hosts file early so the domain resolves before services start
update_hosts_file
echo ""
# Install Composer dependencies BEFORE starting Docker services # Install Composer dependencies BEFORE starting Docker services
# (Docker Compose mounts files from vendor/laravel/sail that need to exist) # (Docker Compose mounts files from vendor/laravel/sail that need to exist)
echo -e "${BLUE}Installing Composer dependencies...${NC}" echo -e "${BLUE}Installing Composer dependencies...${NC}"
@ -888,16 +700,12 @@ install() {
echo -e "${GREEN}Composer dependencies installed.${NC}" echo -e "${GREEN}Composer dependencies installed.${NC}"
echo "" echo ""
# Setup SSL certificates before starting Caddy
setup_ssl_certificates
echo ""
# Check and configure ports before starting Docker services # Check and configure ports before starting Docker services
check_and_configure_ports check_and_configure_ports
# Start Docker services (but don't start PHP yet - we'll start it after APP_KEY is generated) # Start Docker services (but don't start PHP yet - we'll start it after APP_KEY is generated)
echo -e "${BLUE}Starting Docker services (excluding PHP for now)...${NC}" echo -e "${BLUE}Starting Docker services (excluding PHP for now)...${NC}"
docker compose up -d mysql redis caddy mailhog docker compose up -d mysql redis mailhog
wait_for_service "mysql" wait_for_service "mysql"
wait_for_service "redis" wait_for_service "redis"
@ -1058,6 +866,10 @@ install() {
fi fi
echo "" echo ""
# Setup portless for HTTPS access
setup_portless
echo ""
# Success message # Success message
echo "" echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
@ -1066,29 +878,13 @@ install() {
echo "" echo ""
echo -e "${BLUE}Your application is now running!${NC}" echo -e "${BLUE}Your application is now running!${NC}"
echo "" echo ""
echo -e " ${GREEN}Visit: https://whisper.money.local${NC}" echo -e " ${GREEN}Visit: https://whisper.money.localhost${NC}"
echo "" echo ""
echo -e "${GREEN}✓ Using portless for HTTPS (auto-trusted certificates)${NC}"
# Check if mkcert certificates are being used
if [ -f "docker/caddy/certs/whisper.money.local.pem" ]; then
echo -e "${GREEN}✓ Using trusted SSL certificates (mkcert)${NC}"
echo -e "${BLUE} Your browser will automatically trust the certificate!${NC}"
else
echo -e "${YELLOW}⚠ Using self-signed certificates${NC}"
echo -e "${BLUE} Your browser may show a security warning.${NC}"
echo -e "${BLUE} Click 'Advanced' → 'Proceed to whisper.money.local' to continue.${NC}"
echo ""
echo -e "${YELLOW}To avoid the warning, install mkcert and run setup again:${NC}"
if [[ "$OSTYPE" == "darwin"* ]]; then
echo -e " ${GREEN}brew install mkcert nss && mkcert -install${NC}"
else
echo -e " ${GREEN}# See: https://github.com/FiloSottile/mkcert#installation${NC}"
fi
fi
echo "" echo ""
echo -e "${BLUE}Services running:${NC}" echo -e "${BLUE}Services running:${NC}"
echo -e " • PHP development server (port 8000)" echo -e " • PHP development server (port 8000)"
echo -e " • Caddy reverse proxy (ports 80, 443)" echo -e " • Portless HTTPS proxy (port 443)"
echo -e " • MySQL database" echo -e " • MySQL database"
echo -e " • Redis cache" echo -e " • Redis cache"
echo -e " • MailHog (email testing)" echo -e " • MailHog (email testing)"

View File

@ -1,6 +1,3 @@
import fs from 'fs';
import path from 'path';
import { wayfinder } from '@laravel/vite-plugin-wayfinder'; import { wayfinder } from '@laravel/vite-plugin-wayfinder';
import { sentryVitePlugin } from '@sentry/vite-plugin'; import { sentryVitePlugin } from '@sentry/vite-plugin';
import tailwindcss from '@tailwindcss/vite'; import tailwindcss from '@tailwindcss/vite';
@ -12,18 +9,6 @@ export default defineConfig({
build: { build: {
sourcemap: true, sourcemap: true,
}, },
server: (() => {
const certPath = path.resolve(__dirname, 'docker/caddy/certs/whisper.money.local.pem');
const keyPath = path.resolve(__dirname, 'docker/caddy/certs/whisper.money.local-key.pem');
if (fs.existsSync(certPath) && fs.existsSync(keyPath)) {
return {
https: { cert: certPath, key: keyPath },
host: '0.0.0.0',
hmr: { host: 'whisper.money.local' },
};
}
return {};
})(),
plugins: [ plugins: [
laravel({ laravel({
input: ['resources/css/app.css', 'resources/js/app.tsx'], input: ['resources/css/app.css', 'resources/js/app.tsx'],