Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Backend CORS now accepts configured origins plus `*.lucassantana.tech` and `*.luk-homeserver.com.br` hosts for dashboard/API split-domain setups
- Backend auth/Last.fm redirect targets now use the primary frontend origin when `WEBAPP_FRONTEND_URL` contains multiple comma-separated domains
- Frontend API client now auto-resolves hosted API base to `lucky-api.lucassantana.tech` or `api.luk-homeserver.com.br` when `VITE_API_BASE_URL` is not set
- Deploy smoke check now falls back to `/api/health` when `/api/health/auth-config` is unavailable
- Vercel routing no longer rewrites `/api/*` back to the same Lucky host, preventing `508 INFINITE_LOOP` on OAuth login
- Frontend API base URL now supports `VITE_API_BASE_URL` for hosted deployments that use a separate backend origin
- Vercel now forwards `/api/*` directly to `https://lucky-api.lucassantana.tech/api/*` to prevent frontend-host `404 NOT_FOUND` on OAuth/API routes
- Deploy webhook trigger now uses strict curl connect/request timeouts to avoid long hangs in CI deploy jobs
- Deploy webhook trigger now retries longer on 5xx/network failures, logs every attempt, and falls back to canonical `/webhook/deploy` path for all non-2xx responses
- Music now-playing updates no longer send extra plain-text messages on every track change
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ For hosted frontend deployments, set `VITE_API_BASE_URL` to your backend API ori
(example: `https://api.yourdomain.com/api`) to avoid auth/API loop misrouting.
Without `VITE_API_BASE_URL`, frontend now auto-targets `lucky-api.lucassantana.tech` for
`*.lucassantana.tech` hosts and `api.luk-homeserver.com.br` for `*.luk-homeserver.com.br`.
When `WEBAPP_FRONTEND_URL` includes multiple origins, use comma-separated values
(example: `https://lucky.lucassantana.tech,https://lukbot.vercel.app`); backend CORS
accepts all configured entries while OAuth/Last.fm redirects use the first origin.

## Environment Variables

Expand Down
8 changes: 2 additions & 6 deletions packages/backend/src/middleware/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@ import path from 'path'
import { fileURLToPath } from 'url'
import { setupSessionMiddleware } from './session'
import { requestLogger } from './requestLogger'
import { getFrontendOrigins } from '../utils/frontendOrigin'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

export function setupMiddleware(app: Express): void {
const frontendUrl =
process.env.WEBAPP_FRONTEND_URL ?? 'http://localhost:5173'
const configuredOrigins = frontendUrl
.split(',')
.map((origin) => origin.trim())
.filter((origin) => origin.length > 0)
const configuredOrigins = getFrontendOrigins()

const isAllowedOrigin = (origin: string): boolean => {
if (configuredOrigins.includes(origin)) {
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { asyncHandler } from '../middleware/asyncHandler'
import { AppError } from '../errors/AppError'
import { authLimiter } from '../middleware/rateLimit'
import { handleOAuthCallback } from './authCallback'
import { getPrimaryFrontendUrl } from '../utils/frontendOrigin'

const getFrontendUrl = (): string => {
return process.env.WEBAPP_FRONTEND_URL ?? 'http://localhost:5173'
return getPrimaryFrontendUrl()
}

export function setupAuthRoutes(app: Express): void {
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/routes/authCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import type { Request, Response } from 'express'
import { debugLog, errorLog } from '@lucky/shared/utils'
import { discordOAuthService } from '../services/DiscordOAuthService'
import { sessionService } from '../services/SessionService'
import { getPrimaryFrontendUrl } from '../utils/frontendOrigin'

export async function handleOAuthCallback(
req: Request,
res: Response,
): Promise<void> {
try {
const { code, error } = req.query
const frontendUrl =
process.env.WEBAPP_FRONTEND_URL ?? 'http://localhost:5173'
const frontendUrl = getPrimaryFrontendUrl()

if (error) {
errorLog({ message: 'Discord OAuth error', data: { error } })
Expand Down Expand Up @@ -92,8 +92,7 @@ export async function handleOAuthCallback(
res.redirect(`${frontendUrl}/?authenticated=true`)
} catch (error) {
errorLog({ message: 'Error in Discord OAuth callback:', error })
const frontendUrl =
process.env.WEBAPP_FRONTEND_URL ?? 'http://localhost:5173'
const frontendUrl = getPrimaryFrontendUrl()
res.redirect(
`${frontendUrl}/?error=auth_failed&message=authentication_error`,
)
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/routes/lastfm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
requireAuth,
type AuthenticatedRequest,
} from '../middleware/auth'
import { getPrimaryFrontendUrl } from '../utils/frontendOrigin'

const LASTFM_STATE_COOKIE = 'lastfm_state'
const STATE_MAX_AGE_SEC = 600
Expand Down Expand Up @@ -60,7 +61,7 @@ function decodeAndVerifyState(state: string, secret: string): string | null {
}

function getFrontendUrl(): string {
return process.env.WEBAPP_FRONTEND_URL ?? 'http://localhost:5173'
return getPrimaryFrontendUrl()
}

export function setupLastFmRoutes(app: Express): void {
Expand Down
15 changes: 15 additions & 0 deletions packages/backend/src/utils/frontendOrigin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const DEFAULT_FRONTEND_URL = 'http://localhost:5173'

export function getFrontendOrigins(): string[] {
const configured = process.env.WEBAPP_FRONTEND_URL ?? DEFAULT_FRONTEND_URL
const origins = configured
.split(',')
.map((origin) => origin.trim())
.filter((origin) => origin.length > 0)

return origins.length > 0 ? origins : [DEFAULT_FRONTEND_URL]
}

export function getPrimaryFrontendUrl(): string {
return getFrontendOrigins()[0]
}
8 changes: 7 additions & 1 deletion vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
"buildCommand": "npm run db:generate && npm run build:shared && npm run build:frontend",
"outputDirectory": "packages/frontend/dist",
"installCommand": "npm ci",
"framework": "vite"
"framework": "vite",
"rewrites": [
{
"source": "/api/:path*",
"destination": "https://lucky-api.lucassantana.tech/api/:path*"
}
]
}