mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-07 07:55:16 +02:00
fix: use runtime BACKEND_URL for proxying (#411)
* fix: use runtime BACKEND_URL for proxying Fixes #400 * chore: run formatter
This commit is contained in:
parent
acc2ef9e96
commit
cdb27c1d4f
6 changed files with 130 additions and 17 deletions
|
|
@ -5,28 +5,38 @@ Revises: 6bd9f67ec994
|
|||
Create Date: 2026-06-02 07:58:00.002359
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '384be6596b36'
|
||||
down_revision: Union[str, None] = '6bd9f67ec994'
|
||||
revision: str = "384be6596b36"
|
||||
down_revision: Union[str, None] = "6bd9f67ec994"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_users_email'), table_name='users')
|
||||
op.create_index('ix_users_email_lower', 'users', [sa.literal_column('lower(email)')], unique=True, postgresql_where=sa.text('email IS NOT NULL'))
|
||||
op.drop_index(op.f("ix_users_email"), table_name="users")
|
||||
op.create_index(
|
||||
"ix_users_email_lower",
|
||||
"users",
|
||||
[sa.literal_column("lower(email)")],
|
||||
unique=True,
|
||||
postgresql_where=sa.text("email IS NOT NULL"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index('ix_users_email_lower', table_name='users', postgresql_where=sa.text('email IS NOT NULL'))
|
||||
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
|
||||
op.drop_index(
|
||||
"ix_users_email_lower",
|
||||
table_name="users",
|
||||
postgresql_where=sa.text("email IS NOT NULL"),
|
||||
)
|
||||
op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True)
|
||||
# ### end Alembic commands ###
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# generated by datamodel-codegen:
|
||||
# filename: dograh-openapi-rs5H7P.json
|
||||
# timestamp: 2026-06-02T06:01:29+00:00
|
||||
# filename: dograh-openapi-uraOZf.json
|
||||
# timestamp: 2026-06-03T11:53:30+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,6 @@ const nextConfig: NextConfig = {
|
|||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
// API proxy for backend calls (excluding Next.js API routes)
|
||||
{
|
||||
source: "/api/:path((?!config|auth).*)*",
|
||||
destination: `${process.env.BACKEND_URL || 'http://localhost:8000'}/api/:path*`,
|
||||
},
|
||||
{
|
||||
source: "/ingest/static/:path*",
|
||||
destination: "https://us-assets.i.posthog.com/static/:path*",
|
||||
|
|
|
|||
104
ui/src/app/api/v1/[...path]/route.ts
Normal file
104
ui/src/app/api/v1/[...path]/route.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
import { getServerBackendUrl } from "@/lib/apiClient";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const runtime = "nodejs";
|
||||
|
||||
const HOP_BY_HOP_HEADERS = [
|
||||
"connection",
|
||||
"keep-alive",
|
||||
"proxy-authenticate",
|
||||
"proxy-authorization",
|
||||
"te",
|
||||
"trailer",
|
||||
"transfer-encoding",
|
||||
"upgrade",
|
||||
];
|
||||
|
||||
function trimTrailingSlash(url: string) {
|
||||
return url.endsWith("/") ? url.slice(0, -1) : url;
|
||||
}
|
||||
|
||||
function buildBackendUrl(request: NextRequest) {
|
||||
const backendUrl = trimTrailingSlash(getServerBackendUrl());
|
||||
return `${backendUrl}${request.nextUrl.pathname}${request.nextUrl.search}`;
|
||||
}
|
||||
|
||||
function createRequestHeaders(request: NextRequest) {
|
||||
const headers = new Headers(request.headers);
|
||||
|
||||
for (const header of HOP_BY_HOP_HEADERS) {
|
||||
headers.delete(header);
|
||||
}
|
||||
|
||||
headers.delete("accept-encoding");
|
||||
headers.delete("content-length");
|
||||
headers.delete("host");
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
function createResponseHeaders(response: Response) {
|
||||
const headers = new Headers(response.headers);
|
||||
const setCookies = response.headers.getSetCookie();
|
||||
|
||||
for (const header of HOP_BY_HOP_HEADERS) {
|
||||
headers.delete(header);
|
||||
}
|
||||
|
||||
headers.delete("content-encoding");
|
||||
headers.delete("content-length");
|
||||
headers.delete("set-cookie");
|
||||
|
||||
for (const cookie of setCookies) {
|
||||
headers.append("set-cookie", cookie);
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
async function getRequestBody(request: NextRequest) {
|
||||
if (request.method === "GET" || request.method === "HEAD") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return request.arrayBuffer();
|
||||
}
|
||||
|
||||
async function proxyRequest(request: NextRequest) {
|
||||
const backendUrl = buildBackendUrl(request);
|
||||
|
||||
try {
|
||||
const response = await fetch(backendUrl, {
|
||||
method: request.method,
|
||||
headers: createRequestHeaders(request),
|
||||
body: await getRequestBody(request),
|
||||
cache: "no-store",
|
||||
});
|
||||
|
||||
return new Response(request.method === "HEAD" ? null : response.body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: createResponseHeaders(response),
|
||||
});
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : "Unknown backend proxy error";
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
detail: `Backend request failed while proxying to ${backendUrl}: ${message}`,
|
||||
},
|
||||
{ status: 502 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = proxyRequest;
|
||||
export const POST = proxyRequest;
|
||||
export const PUT = proxyRequest;
|
||||
export const PATCH = proxyRequest;
|
||||
export const DELETE = proxyRequest;
|
||||
export const OPTIONS = proxyRequest;
|
||||
export const HEAD = proxyRequest;
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import "server-only";
|
||||
|
||||
import { getServerBackendUrl } from "@/lib/apiClient";
|
||||
|
||||
let cachedAuthProvider: string | null = null;
|
||||
|
||||
/**
|
||||
|
|
@ -12,7 +14,7 @@ export async function getAuthProvider(): Promise<string> {
|
|||
}
|
||||
|
||||
try {
|
||||
const backendUrl = process.env.BACKEND_URL || "http://localhost:8000";
|
||||
const backendUrl = getServerBackendUrl();
|
||||
const res = await fetch(`${backendUrl}/api/v1/health`, {
|
||||
next: { revalidate: 300 },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import type { NextRequest } from 'next/server';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { getServerBackendUrl } from '@/lib/apiClient';
|
||||
|
||||
const OSS_TOKEN_COOKIE = 'dograh_auth_token';
|
||||
|
||||
// Paths that don't require authentication in OSS mode
|
||||
|
|
@ -14,7 +16,7 @@ async function fetchAuthProvider(): Promise<string> {
|
|||
}
|
||||
|
||||
try {
|
||||
const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000';
|
||||
const backendUrl = getServerBackendUrl();
|
||||
const res = await fetch(`${backendUrl}/api/v1/health`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue