Compare commits

..

42 Commits

Author SHA1 Message Date
hailin c37ce1dac5 b.sh 2025-07-18 22:29:18 +08:00
hailin b4178d648a . 2025-06-27 10:12:04 +08:00
hailin f7706e0abb . 2025-06-26 23:07:13 +08:00
hailin bffbc4665a . 2025-06-26 22:54:00 +08:00
hailin 94554e9c69 . 2025-06-26 21:44:11 +08:00
hailin 46a8682fbe . 2025-06-26 12:40:23 +08:00
hailin 476d33797a . 2025-06-26 12:22:51 +08:00
hailin 34d8a59fd4 . 2025-06-26 12:08:39 +08:00
hailin c6164443ae . 2025-06-26 12:07:16 +08:00
hailin 4011b94d30 . 2025-06-26 10:16:49 +08:00
hailin f6fd1b8011 . 2025-06-26 10:06:03 +08:00
hailin b98c049e58 . 2025-06-26 10:04:57 +08:00
hailin 83850fc475 . 2025-06-26 08:59:48 +08:00
hailin d4419293fb . 2025-06-26 00:15:22 +08:00
hailin f90c5361ed . 2025-06-25 23:47:30 +08:00
hailin 008bbb3643 . 2025-06-25 23:29:18 +08:00
hailin 92ca21d2cb . 2025-06-25 18:51:49 +08:00
hailin 40f5856aa0 . 2025-06-25 18:48:15 +08:00
hailin 6354af6ea1 . 2025-06-25 18:46:15 +08:00
hailin dcb30caafb . 2025-06-25 18:35:21 +08:00
hailin 32874e0d15 . 2025-06-25 18:28:57 +08:00
hailin 7d70c45db8 . 2025-06-25 18:05:05 +08:00
hailin 1a37e37f13 . 2025-06-25 18:02:00 +08:00
hailin 811efaef32 . 2025-06-25 17:59:25 +08:00
hailin 1e470bf7b5 . 2025-06-25 17:55:29 +08:00
hailin 9b8b705e97 . 2025-06-25 17:53:25 +08:00
hailin 452a58a74a . 2025-06-25 17:51:30 +08:00
hailin 27ea4f576c . 2025-06-25 17:45:45 +08:00
hailin 62b0dbd40b . 2025-06-25 17:33:32 +08:00
hailin d71078b262 . 2025-06-25 17:11:12 +08:00
hailin 072dce8517 . 2025-06-25 17:04:05 +08:00
hailin 5c6ef8366b . 2025-06-25 16:56:38 +08:00
hailin 607531f309 . 2025-06-25 16:46:05 +08:00
hailin 888bdead90 . 2025-06-25 16:38:36 +08:00
hailin 402779994e . 2025-06-25 16:32:26 +08:00
hailin 87ec871a4b . 2025-06-25 16:15:00 +08:00
hailin d61a94d66a . 2025-06-25 16:07:17 +08:00
hailin e35878178a . 2025-06-25 16:04:13 +08:00
hailin 04b5c0c94e . 2025-06-25 15:52:29 +08:00
hailin d61a2dc385 . 2025-06-25 15:46:06 +08:00
hailin 768db37011 . 2025-06-25 15:41:13 +08:00
hailin f6ae45debe . 2025-06-25 15:27:50 +08:00
33 changed files with 431 additions and 316 deletions

43
.env.local Normal file
View File

@ -0,0 +1,43 @@
# Supabase Public
NEXT_PUBLIC_SUPABASE_URL=http://67.223.119.33:8000
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlLWRlbW8iLCJleHAiOjIwNjI5MjU1NjUsImlhdCI6MTc0NzU2NTU2NX0.l08_BczmnaDNCur8AK0Z-X6MZOHNNRWh7ij5aN6sLgU
# Supabase Private
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaXNzIjoic3VwYWJhc2UtZGVtbyIsImV4cCI6MjA2MjkyNTU2NSwiaWF0IjoxNzQ3NTY1NTY1fQ.18Lxnd9JrkNyV9q38l_8oQB8pwtZK8JwpLwpH2b4JaA
# Ollama
#NEXT_PUBLIC_OLLAMA_URL=http://localhost:11434
#sglang
NEXT_PUBLIC_OLLAMA_URL=http://183.36.35.42:30000
#NEXT_PUBLIC_OLLAMA_URL=http://127.0.0.1:8000
#NEXT_PUBLIC_OLLAMA_URL=http://127.0.0.1:23333
# API Keys (Optional: Entering an API key here overrides the API keys globally for all users.)
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
GOOGLE_GEMINI_API_KEY=
MISTRAL_API_KEY=
GROQ_API_KEY=
PERPLEXITY_API_KEY=
OPENROUTER_API_KEY=
# OpenAI API Information
NEXT_PUBLIC_OPENAI_ORGANIZATION_ID=
# Azure API Information
AZURE_OPENAI_API_KEY=
AZURE_OPENAI_ENDPOINT=
AZURE_GPT_35_TURBO_NAME=
AZURE_GPT_45_VISION_NAME=
AZURE_GPT_45_TURBO_NAME=
AZURE_EMBEDDINGS_NAME=
# General Configuration (Optional)
EMAIL_DOMAIN_WHITELIST=
EMAIL_WHITELIST=
# File size limit for uploads in bytes
NEXT_PUBLIC_USER_FILE_SIZE_LIMIT=104857600
~

41
b.sh Executable file
View File

@ -0,0 +1,41 @@
#!/bin/bash
# 设置变量
REPO_NAME="chatdesk-ui"
TAG="1.0.0"
IMAGE_NAME="chatdesk-ui:$TAG"
CONTAINER_NAME="chatdesk-ui"
# 停止并删除旧的容器实例
echo "Stopping and removing any running containers of $REPO_NAME:$TAG..."
docker ps -q --filter "ancestor=$REPO_NAME:$TAG" | xargs -r docker stop
docker ps -a -q --filter "ancestor=$REPO_NAME:$TAG" | xargs -r docker rm
# 删除镜像
echo "Removing image $REPO_NAME:$TAG..."
docker rmi $IMAGE_NAME
# 执行 git pull 获取最新代码
echo "Pulling the latest code from the repository..."
git pull
# 重建 Docker 镜像
echo "Building the Docker image $IMAGE_NAME..."
docker build \
--build-arg http_proxy=http://127.0.0.1:7890 \
--build-arg https_proxy=http://127.0.0.1:7890 \
--network=host \
--no-cache \
-t $IMAGE_NAME .
# 运行新镜像
echo "Running the container with the new image..."
docker run -d \
-e SUPABASE_URL=http://67.223.119.33:8000 \
-p 3030:3030 \
-p 8000:8000 \
--name $CONTAINER_NAME \
$IMAGE_NAME
echo "Deployment complete."

View File

@ -33,4 +33,4 @@ EMAIL_DOMAIN_WHITELIST=
EMAIL_WHITELIST= EMAIL_WHITELIST=
# File size limit for uploads in bytes # File size limit for uploads in bytes
NEXT_PUBLIC_USER_FILE_SIZE_LIMIT=10485760 NEXT_PUBLIC_USER_FILE_SIZE_LIMIT=104857600

View File

@ -3,16 +3,14 @@ import { GlobalState } from "@/components/utility/global-state"
import { Providers } from "@/components/utility/providers" import { Providers } from "@/components/utility/providers"
import TranslationsProvider from "@/components/utility/translations-provider" import TranslationsProvider from "@/components/utility/translations-provider"
import initTranslations from "@/lib/i18n" import initTranslations from "@/lib/i18n"
import { Database } from "@/supabase/types"
import { getSupabaseServerClient } from "@/lib/supabase/server" import { getSupabaseServerClient } from "@/lib/supabase/server"
import { Metadata, Viewport } from "next" import { Metadata, Viewport } from "next"
import { Inter } from "next/font/google" import { Inter } from "next/font/google"
import { cookies } from "next/headers" import { cookies } from "next/headers"
import { ReactNode } from "react" import { ReactNode } from "react"
import "./globals.css" import "./globals.css"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
import Script from "next/script"
import { Suspense } from "react" import { Suspense } from "react"
import { RuntimeEnvScript } from "@/components/utility/runtime-env-provider"
const inter = Inter({ subsets: ["latin"] }) const inter = Inter({ subsets: ["latin"] })
@ -28,26 +26,6 @@ interface RootLayoutProps {
} }
} }
// 新增的 `getValidSupabaseUrl` 函数,带有超时机制和错误处理
async function getValidSupabaseUrl(): Promise<string> {
let url = getRuntimeEnv("SUPABASE_URL");
const timeout = Date.now() + 30000; // 设置最大等待时间为30秒
while (!url || !url.includes(":")) { // 检查是否包含合法的 IP 和端口
if (Date.now() > timeout) {
throw new Error("获取有效的 Supabase URL 超时");
}
console.log("等待有效的 Supabase URL...");
await new Promise(resolve => setTimeout(resolve, 1000)); // 每1秒检查一次
url = getRuntimeEnv("SUPABASE_URL");
}
return url;
}
export async function generateMetadata({ export async function generateMetadata({
params: { locale } params: { locale }
}: { }: {
@ -113,16 +91,6 @@ export default async function RootLayout({
console.log(`🍪 Cookie: ${cookie.name} = ${cookie.value}`); console.log(`🍪 Cookie: ${cookie.name} = ${cookie.value}`);
} }
let supabaseUrl = "";
try {
// 等待直到获取到有效的 Supabase URL
supabaseUrl = await getValidSupabaseUrl();
//console.log("==========>获取到有效的 Supabase URL: ", supabaseUrl);
} catch (error) {
console.error("Supabase URL 获取失败:", error);
return <div>Failed to fetch Supabase configuration, please try again later.</div>; // 出现错误时返回一个友好的提示
}
const supabase = getSupabaseServerClient() const supabase = getSupabaseServerClient()
const { data, error } = await supabase.auth.getSession(); const { data, error } = await supabase.auth.getSession();
if (error) { if (error) {
@ -137,10 +105,7 @@ export default async function RootLayout({
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<head> <head>
<Script <RuntimeEnvScript />
src="/env.js"
strategy="beforeInteractive" // 确保在 React 启动之前加载
/>
</head> </head>
<body className={inter.className}> <body className={inter.className}>

View File

@ -8,7 +8,7 @@ import { get } from "@vercel/edge-config"
import { Metadata } from "next" import { Metadata } from "next"
import { cookies, headers } from "next/headers" import { cookies, headers } from "next/headers"
import { redirect } from "next/navigation" import { redirect } from "next/navigation"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入 import { getRuntimeEnv } from "@/lib/runtime-env"
import { PostgrestError } from '@supabase/supabase-js'; import { PostgrestError } from '@supabase/supabase-js';
import initTranslations from "@/lib/i18n"; import initTranslations from "@/lib/i18n";

View File

@ -2,7 +2,7 @@ import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
import { ServerRuntime } from "next" import { ServerRuntime } from "next"
import OpenAI from "openai" import OpenAI from "openai"
export const runtime: ServerRuntime = "edge" //export const runtime: ServerRuntime = "edge"
export async function GET() { export async function GET() {
try { try {

View File

@ -5,10 +5,11 @@ import { OpenAIStream, StreamingTextResponse } from "ai"
import { ServerRuntime } from "next" import { ServerRuntime } from "next"
import OpenAI from "openai" import OpenAI from "openai"
import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs" import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入 import { getRuntimeEnvForRouterAPI } from "@/lib/runtime-env"
export const runtime: ServerRuntime = "edge" //export const runtime: ServerRuntime = "edge"
export const runtime = "nodejs"
export async function POST(request: Request) { export async function POST(request: Request) {
const json = await request.json() const json = await request.json()
@ -20,7 +21,7 @@ export async function POST(request: Request) {
try { try {
const supabaseAdmin = createClient<Database>( const supabaseAdmin = createClient<Database>(
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000", getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
process.env.SUPABASE_SERVICE_ROLE_KEY! process.env.SUPABASE_SERVICE_ROLE_KEY!
) )
@ -31,6 +32,7 @@ export async function POST(request: Request) {
.single() .single()
if (!customModel) { if (!customModel) {
console.error("❌ No custom model found:", error)
throw new Error(error.message) throw new Error(error.message)
} }

View File

@ -5,7 +5,7 @@ import { ServerRuntime } from "next"
import OpenAI from "openai" import OpenAI from "openai"
import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs" import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs"
export const runtime: ServerRuntime = "edge" //export const runtime: ServerRuntime = "edge"
export async function POST(request: Request) { export async function POST(request: Request) {
const json = await request.json() const json = await request.json()

View File

@ -5,7 +5,7 @@ import { ServerRuntime } from "next"
import OpenAI from "openai" import OpenAI from "openai"
import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs" import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs"
export const runtime: ServerRuntime = "edge" //export const runtime: ServerRuntime = "edge"
export async function POST(request: Request) { export async function POST(request: Request) {
const json = await request.json() const json = await request.json()

View File

@ -6,9 +6,11 @@ import { FileItemChunk } from "@/types"
import { createClient } from "@supabase/supabase-js" import { createClient } from "@supabase/supabase-js"
import { NextResponse } from "next/server" import { NextResponse } from "next/server"
import OpenAI from "openai" import OpenAI from "openai"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入 import { getRuntimeEnvForRouterAPI } from "@/lib/runtime-env"
import { generateBgeM3Embedding } from "@/lib/generate-bgem3-embedding" import { generateBgeM3Embedding } from "@/lib/generate-bgem3-embedding"
export const runtime = "nodejs"
export async function POST(req: Request) { export async function POST(req: Request) {
const json = await req.json() const json = await req.json()
const { text, fileId, embeddingsProvider, fileExtension } = json as { const { text, fileId, embeddingsProvider, fileExtension } = json as {
@ -20,7 +22,7 @@ export async function POST(req: Request) {
try { try {
const supabaseAdmin = createClient<Database>( const supabaseAdmin = createClient<Database>(
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000", getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
process.env.SUPABASE_SERVICE_ROLE_KEY! process.env.SUPABASE_SERVICE_ROLE_KEY!
) )

View File

@ -13,13 +13,15 @@ import { FileItemChunk } from "@/types"
import { createClient } from "@supabase/supabase-js" import { createClient } from "@supabase/supabase-js"
import { NextResponse } from "next/server" import { NextResponse } from "next/server"
import OpenAI from "openai" import OpenAI from "openai"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入 import { getRuntimeEnvForRouterAPI } from "@/lib/runtime-env"
export const runtime = "nodejs"
export async function POST(req: Request) { export async function POST(req: Request) {
try { try {
const supabaseAdmin = createClient<Database>( const supabaseAdmin = createClient<Database>(
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000", getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
process.env.SUPABASE_SERVICE_ROLE_KEY! process.env.SUPABASE_SERVICE_ROLE_KEY!
) )

View File

@ -4,7 +4,9 @@ import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
import { Database } from "@/supabase/types" import { Database } from "@/supabase/types"
import { createClient } from "@supabase/supabase-js" import { createClient } from "@supabase/supabase-js"
import OpenAI from "openai" import OpenAI from "openai"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入 import { getRuntimeEnvForRouterAPI } from "@/lib/runtime-env"
export const runtime = "nodejs"
export async function POST(request: Request) { export async function POST(request: Request) {
@ -22,13 +24,12 @@ export async function POST(request: Request) {
try { try {
const rawSupaUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000" const rawSupaUrl = getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000"
const supaUrlObj = new URL(rawSupaUrl) const supaUrlObj = new URL(rawSupaUrl)
supaUrlObj.port = "8000" supaUrlObj.port = "8000"
const supabaseAdmin = createClient<Database>( const supabaseAdmin = createClient<Database>(
supaUrlObj.origin, supaUrlObj.origin,
// getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
process.env.SUPABASE_SERVICE_ROLE_KEY! process.env.SUPABASE_SERVICE_ROLE_KEY!
) )

View File

@ -1,8 +1,8 @@
import { Database } from "@/supabase/types" import { Database } from "@/supabase/types"
import { createClient } from "@supabase/supabase-js" import { createClient } from "@supabase/supabase-js"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入 import { getRuntimeEnvForRouterAPI } from "@/lib/runtime-env"
export const runtime = "edge" export const runtime = "nodejs"
export async function POST(request: Request) { export async function POST(request: Request) {
const json = await request.json() const json = await request.json()
@ -12,7 +12,7 @@ export async function POST(request: Request) {
try { try {
const supabaseAdmin = createClient<Database>( const supabaseAdmin = createClient<Database>(
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000", getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
process.env.SUPABASE_SERVICE_ROLE_KEY! process.env.SUPABASE_SERVICE_ROLE_KEY!
) )

View File

@ -1,8 +1,9 @@
import { Database } from "@/supabase/types" import { Database } from "@/supabase/types"
import { createClient } from "@supabase/supabase-js" import { createClient } from "@supabase/supabase-js"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入 import { getRuntimeEnvForRouterAPI } from "@/lib/runtime-env"
export const runtime = "edge" // export const runtime = "edge"
export const runtime = "nodejs"
export async function POST(request: Request) { export async function POST(request: Request) {
const json = await request.json() const json = await request.json()
@ -12,7 +13,7 @@ export async function POST(request: Request) {
try { try {
const supabaseAdmin = createClient<Database>( const supabaseAdmin = createClient<Database>(
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000", getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
process.env.SUPABASE_SERVICE_ROLE_KEY! process.env.SUPABASE_SERVICE_ROLE_KEY!
) )

View File

@ -22,7 +22,7 @@ import {
import React from "react" import React from "react"
import { toast } from "sonner" import { toast } from "sonner"
import { v4 as uuidv4 } from "uuid" import { v4 as uuidv4 } from "uuid"
import { getRuntimeEnv } from "@/lib/ipconfig" import { getRuntimeEnv } from "@/lib/runtime-env"
type RetrievedFileItem = Tables<"file_items"> & { type RetrievedFileItem = Tables<"file_items"> & {

View File

@ -812,7 +812,7 @@ export const ProfileSettings: FC<ProfileSettingsProps> = ({}) => {
<div className="flex items-center space-x-1"> <div className="flex items-center space-x-1">
<ThemeSwitcher /> <ThemeSwitcher />
<WithTooltip {/* <WithTooltip
display={ display={
<div> <div>
{t("profile.downloadTooltip")} {t("profile.downloadTooltip")}
@ -825,7 +825,7 @@ export const ProfileSettings: FC<ProfileSettingsProps> = ({}) => {
onClick={exportLocalStorageAsJSON} onClick={exportLocalStorageAsJSON}
/> />
} }
/> /> */}
</div> </div>
<div className="ml-auto space-x-2"> <div className="ml-auto space-x-2">

View File

@ -0,0 +1,116 @@
// // app/_runtime-env-provider.tsx ← 文件放 app 根,保证是 Server 组件
// import { headers } from "next/headers" // ① 仅在服务器端可调用
// import { useServerInsertedHTML } from "next/navigation" // ② 往最终 HTML 插入标签
// import React from "react"
// // ------------------- 带全链路调试日志的版本 -------------------
// export function RuntimeEnvProvider({ children }: { children: React.ReactNode }) {
// /* ──────────────── 1. 解析协议 ──────────────── */
// const h = headers() // <-- Node 边界:读取当前请求头
// console.log("[RuntimeEnv] Raw headers:", Object.fromEntries(h.entries()))
// const proto =
// h.get("x-forwarded-proto") ?? // 走 Nginx / 反代时最可靠
// (h.get("host")?.includes(":443") ? "https" : "http")// 没有反代时根据端口猜
// console.log("[RuntimeEnv] Resolved protocol:", proto)
// /* ──────────────── 2. 解析主机名 ──────────────── */
// const rawHost = h.get("x-forwarded-host") ?? h.get("host")!
// console.log("[RuntimeEnv] rawHost :", rawHost)
// // 多层反代时 x-forwarded-host 可能是 "a.com, b.com"
// // host 可能附带端口,须砍掉
// const hostname = rawHost.split(",")[0].split(":")[0].trim()
// console.log("[RuntimeEnv] hostname :", hostname)
// /* ──────────────── 3. 拼最终 URL (端口固定 8000) ──────────────── */
// const supabaseUrl = `${proto}://${hostname}:8000`
// console.log("[RuntimeEnv] supabaseUrl to inject:", supabaseUrl)
// /* ──────────────── 4. 把脚本塞进 HTML ──────────────── */
// useServerInsertedHTML(() => {
// // 这一段在 React 把 HTML flush 给客户端之前执行一次
// console.log("[RuntimeEnv] <script> tag inserted into HTML")
// return (
// <script
// // 行内脚本window.RUNTIME_ENV.SUPABASE_URL = "<url>"
// dangerouslySetInnerHTML={{
// __html: `window.RUNTIME_ENV = { SUPABASE_URL: ${JSON.stringify(
// supabaseUrl,
// )} };`,
// }}
// />
// )
// })
// /* ──────────────── 5. 渲染子节点 ──────────────── */
// return <>{children}</> // Provider 本身不渲染 UI只负责注入脚本
// }
// app/_runtime-env-provider.tsx (务必保持在 app 目录 ➜ Server 组件)
// import { headers } from "next/headers"
// import { useServerInsertedHTML } from "next/navigation"
// import React from "react"
// export function RuntimeEnvProvider({ children }: { children: React.ReactNode }) {
// /* 解析请求头 */
// const h = headers()
// console.log("[RuntimeEnv] Raw headers:", Object.fromEntries(h.entries()))
// const proto =
// h.get("x-forwarded-proto") ??
// (h.get("host")?.includes(":443") ? "https" : "http")
// console.log("[RuntimeEnv] Resolved protocol:", proto)
// const rawHost = h.get("x-forwarded-host") ?? h.get("host")!
// console.log("[RuntimeEnv] rawHost :", rawHost)
// const hostname = rawHost.split(",")[0].split(":")[0].trim()
// console.log("[RuntimeEnv] hostname :", hostname)
// const supabaseUrl = `${proto}://${hostname}:8000`
// console.log("[RuntimeEnv] supabaseUrl to inject:", supabaseUrl)
// /* 把脚本塞进 HTML这一行如果不打印说明没执行 */
// useServerInsertedHTML(() => {
// console.log("[RuntimeEnv] <script> tag inserted into HTML")
// return (
// <script
// id="runtime-env" /* 方便 View-Source 检查 */
// dangerouslySetInnerHTML={{
// __html: `window.RUNTIME_ENV = { SUPABASE_URL: ${JSON.stringify(
// supabaseUrl,
// )} }; console.log('[Client] window.RUNTIME_ENV =', window.RUNTIME_ENV);`,
// }}
// />
// )
// })
// return <>{children}</>
// }
// components/utility/runtime-env-script.tsx
import { headers } from "next/headers"
export function RuntimeEnvScript() {
const h = headers()
const proto = h.get("x-forwarded-proto") ?? (h.get("host")?.includes(":443") ? "https" : "http")
const rawHost = h.get("x-forwarded-host") ?? h.get("host")!
const hostname = rawHost.split(",")[0].split(":")[0].trim()
const supabaseUrl = `${proto}://${hostname}:8000`
return (
<script
id="runtime-env"
dangerouslySetInnerHTML={{
__html: `window.RUNTIME_ENV = { SUPABASE_URL: ${JSON.stringify(supabaseUrl)} };`,
}}
/>
)
}

View File

@ -9,13 +9,17 @@ export const uploadFile = async (
file_id: string file_id: string
} }
) => { ) => {
const SIZE_LIMIT = parseInt( // const SIZE_LIMIT = parseInt(
process.env.NEXT_PUBLIC_USER_FILE_SIZE_LIMIT || "10000000" // process.env.NEXT_PUBLIC_USER_FILE_SIZE_LIMIT || "104857600"
) // )
const SIZE_LIMIT = Number(process.env.NEXT_PUBLIC_USER_FILE_SIZE_LIMIT) || 104857600;
console.log(`[uploadFile] 获取到参数: NEXT_PUBLIC_USER_FILE_SIZE_LIMIT=${process.env.NEXT_PUBLIC_USER_FILE_SIZE_LIMIT}, SIZE_LIMIT=${SIZE_LIMIT}, file.size=${file.size}`);
if (file.size > SIZE_LIMIT) { if (file.size > SIZE_LIMIT) {
throw new Error( throw new Error(
`File must be less than ${Math.floor(SIZE_LIMIT / 1000000)}MB` `File must be less than ${Math.floor(SIZE_LIMIT / 1048576)}MB`
) )
} }

View File

@ -1,13 +1,9 @@
import { getRuntimeEnv } from "@/lib/ipconfig" import { getServerRuntimeUrl } from "@/lib/lib/runtime-url"
export async function generateBgeM3Embedding(text: string): Promise<number[] | null> { export async function generateBgeM3Embedding(text: string): Promise<number[] | null> {
try { try {
// 取 Supabase URL 或本地默认 // ✅ 使用服务端真实 URL自动取协议/host→ 端口强制为 8001
const supaUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000" const apiUrl = `${getServerRuntimeUrl("8001")}/v1/embeddings`
// 构造 Embedding 服务地址:同 host + 8001 端口
const urlObj = new URL(supaUrl)
urlObj.port = "8001" // 强制改成 8001
const apiUrl = `${urlObj.origin}/v1/embeddings`
console.debug("......[generateBgeM3Embedding] apiUrl =", apiUrl) console.debug("......[generateBgeM3Embedding] apiUrl =", apiUrl)
const response = await fetch(apiUrl, { const response = await fetch(apiUrl, {

View File

@ -1,60 +0,0 @@
// lib/ipconfig.ts
// export function getRuntimeEnv(key: string): string | undefined {
// if (typeof window !== "undefined" && typeof window.RUNTIME_ENV !== "undefined") {
// return window.RUNTIME_ENV[key];
// }
// return process.env[key];
// }
let _env: Record<string, string> | null = null
// 最大重试次数
const MAX_RETRIES = 5
// 每次重试的延时(毫秒)
const RETRY_DELAY = 1000
export function getRuntimeEnv(key: string): string | undefined {
//console.log("============>>Getting Supabase API URL.")
if (typeof window !== "undefined") {
if (!_env && typeof window.RUNTIME_ENV !== "undefined") {
_env = window.RUNTIME_ENV
console.log("[browser-side] Retrieved API endpoint from window.RUNTIME_ENV:", _env)
}
// 尝试读取缓存的 _env
if (_env) {
console.log("[browser-side] Returning cached API from _env:", _env)
return _env[key]
}
console.log("[browser-side] No window.RUNTIME_ENV found in browser, _env=", _env)
let retries = 0
// 定义一个同步延时重试的函数
const tryGetEnvSync = () => {
if (typeof window.RUNTIME_ENV !== "undefined") {
_env = window.RUNTIME_ENV
console.log("[browser-side] Retrieved API endpoint from window.RUNTIME_ENV:", _env)
return _env[key] // 成功获取,返回结果
} else {
retries += 1
if (retries <= MAX_RETRIES) {
console.log(`[browser-side] Retry ${retries}/${MAX_RETRIES} - Retrying in ${RETRY_DELAY}ms...`)
// 延时并重试
setTimeout(tryGetEnvSync, RETRY_DELAY)
} else {
console.log("[browser-side] Failed to retrieve window.RUNTIME_ENV after retries.")
return undefined // 重试次数耗尽,返回 undefined
}
}
}
return tryGetEnvSync() // 调用同步重试方法
}
// 服务端始终动态读取 process.env
const val = process.env[key]
//console.log("[server-side] Falling back to process.env for key:", key, "value:", val)
return val // 服务端直接返回 process.env 的值
}

View File

@ -0,0 +1,14 @@
// lib/runtime-url.ts
import { headers } from "next/headers"
/**
* base url
* 示例: getServerRuntimeUrl("8001") => http://host:8001
*/
export function getServerRuntimeUrl(port = "8000"): string {
const h = headers()
const proto = h.get("x-forwarded-proto") ?? (h.get("host")?.includes(":443") ? "https" : "http")
const rawHost = h.get("x-forwarded-host") ?? h.get("host")!
const hostname = rawHost.split(",")[0].split(":")[0].trim()
return `${proto}://${hostname}:${port}`
}

View File

@ -0,0 +1,4 @@
// lib/runtime-env/client.ts
export function getRuntimeEnvCSR(key: string): string | undefined {
return (window as any).RUNTIME_ENV?.[key]
}

View File

@ -0,0 +1,14 @@
// lib/runtime-env/index.ts
export function getRuntimeEnv(key: string): string | undefined {
if (typeof window === "undefined") {
const { getRuntimeEnvSSR } = require("./server")
return getRuntimeEnvSSR(key)
} else {
const { getRuntimeEnvCSR } = require("./client")
return getRuntimeEnvCSR(key)
}
}
export function getRuntimeEnvForRouterAPI(key: string): string | undefined {
return process.env[key]
}

View File

@ -0,0 +1,12 @@
// lib/runtime-env/get-runtime-env-server.ts
export async function loadSupabaseUrl(): Promise<string | undefined> {
const { headers } = await import("next/headers")
const h = headers()
const proto = h.get("x-forwarded-proto") ?? (h.get("host")?.includes(":443") ? "https" : "http")
const rawHost = h.get("x-forwarded-host") ?? h.get("host")!
const hostname = rawHost.split(",")[0].split(":")[0].trim()
return `${proto}://${hostname}:8000`
}

View File

@ -1,9 +1,8 @@
import { Database, Tables } from "@/supabase/types" import { Database, Tables } from "@/supabase/types"
import { VALID_ENV_KEYS } from "@/types/valid-keys" import { VALID_ENV_KEYS } from "@/types/valid-keys"
//import { createServerClient } from "@supabase/ssr"
import { getSupabaseServerClient } from "@/lib/supabase/server" import { getSupabaseServerClient } from "@/lib/supabase/server"
import { cookies } from "next/headers" import { cookies } from "next/headers"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入 import { getRuntimeEnv } from "@/lib/runtime-env"
export async function getServerProfile() { export async function getServerProfile() {

View File

@ -1,91 +1,39 @@
// import { createBrowserClient } from "@supabase/ssr"
// import { getRuntimeEnv } from "@/lib/ipconfig"
// import { Database } from "@/supabase/types"
// export const supabase = createBrowserClient<Database>(
// getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
// process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
// )
// // lib/supabase/browser-client.ts
// import { createBrowserClient } from "@supabase/ssr"
// import { getRuntimeEnv } from "@/lib/ipconfig"
// import { Database } from "@/supabase/types"
// // 动态获取 URL防止静态打包成 localhost:8000
// let supabaseUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000"
// // 如果 URL 不是 http://localhost:8000 且不为 undefined则存储到 localStorage
// if (supabaseUrl !== "http://localhost:8000" && supabaseUrl !== undefined) {
// localStorage.setItem("supabaseUrl", supabaseUrl)
// } else {
// // 如果是 http://localhost:8000 或者 undefined则从 localStorage 获取
// const storedUrl = localStorage.getItem("supabaseUrl")
// if (storedUrl && storedUrl !== "http://localhost:8000") {
// supabaseUrl = storedUrl
// } else {
// supabaseUrl = "http://localhost:8000"
// }
// }
// // 打印获取的 URL
// console.log("=======>>Supabase URL:", supabaseUrl);
// // 编译时固定匿名 key前端安全公开
// const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
// // 导出单例,兼容所有调用旧代码方式
// export const supabase = createBrowserClient<Database>(
// supabaseUrl,
// supabaseAnonKey
// )
import { createBrowserClient } from "@supabase/ssr" import { createBrowserClient } from "@supabase/ssr"
import { getRuntimeEnv } from "@/lib/ipconfig"
import { Database } from "@/supabase/types" import { Database } from "@/supabase/types"
// 动态获取 URL防止静态打包成 localhost:8000 let supabaseUrl = "http://localhost:8000"
let supabaseUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000"
// 仅在浏览器端使用 localStorage if (typeof window === "undefined") {
if (typeof window !== "undefined") { // ✅ SSR 时动态加载,不会触发 headers() 静态报错
// 如果 URL 不是 http://localhost:8000 且不为 undefined则存储到 localStorage const load = async () => {
if (supabaseUrl !== "http://localhost:8000" && supabaseUrl !== undefined) { const { loadSupabaseUrl } = await import("@/lib/runtime-env/server")
localStorage.setItem("supabaseUrl", supabaseUrl) const url = await loadSupabaseUrl()
} else { if (url) {
// 如果是 http://localhost:8000 或者 undefined则从 localStorage 获取 supabaseUrl = url
const storedUrl = localStorage.getItem("supabaseUrl") console.log("[SSR] SUPABASE_URL =", supabaseUrl)
if (storedUrl && storedUrl !== "http://localhost:8000") {
supabaseUrl = storedUrl
} else {
supabaseUrl = "http://localhost:8000"
} }
} }
// 注意:不能阻塞同步逻辑,只是设置变量
load()
} else { } else {
// 如果在服务器端,使用默认或从环境变量获取的 URL const runtimeEnv = (window as any).RUNTIME_ENV
console.log("[server-side] Falling back to default supabaseUrl:", supabaseUrl) const envUrl = runtimeEnv?.SUPABASE_URL
if (envUrl && envUrl !== "http://localhost:8000") {
supabaseUrl = envUrl
localStorage.setItem("supabaseUrl", envUrl)
} else {
const stored = localStorage.getItem("supabaseUrl")
if (stored && stored !== "http://localhost:8000") {
supabaseUrl = stored
}
}
console.log("[CSR] SUPABASE_URL =", supabaseUrl)
} }
// 打印获取的 URL
console.log("=======>>Supabase URL:", supabaseUrl);
// 编译时固定匿名 key前端安全公开
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
// 导出单例,兼容所有调用旧代码方式
export const supabase = createBrowserClient<Database>( export const supabase = createBrowserClient<Database>(
supabaseUrl, supabaseUrl,
supabaseAnonKey supabaseAnonKey

View File

@ -1,15 +1,5 @@
// import { createBrowserClient } from "@supabase/ssr"
// import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
// export const createClient = () =>
// createBrowserClient(
// getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
// process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
// )
import { createBrowserClient } from "@supabase/ssr" import { createBrowserClient } from "@supabase/ssr"
import { getRuntimeEnv } from "@/lib/ipconfig" import { getRuntimeEnv } from "@/lib/runtime-env"
export const createClient = () => export const createClient = () =>
createBrowserClient( createBrowserClient(

View File

@ -1,6 +1,16 @@
import { createServerClient, type CookieOptions } from "@supabase/ssr" import { createServerClient, type CookieOptions } from "@supabase/ssr"
import { NextResponse, type NextRequest } from "next/server" import { NextResponse, type NextRequest } from "next/server"
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
// ✅ 用 headers 构造 host不再使用 getRuntimeEnv
function getSupabaseUrlFromRequest(req: NextRequest, port = "8000"): string {
const proto =
req.headers.get("x-forwarded-proto") ??
(req.headers.get("host")?.includes(":443") ? "https" : "http")
const rawHost = req.headers.get("x-forwarded-host") ?? req.headers.get("host")!
const hostname = rawHost.split(",")[0].split(":")[0].trim()
return `${proto}://${hostname}:${port}`
}
export const createClient = (request: NextRequest) => { export const createClient = (request: NextRequest) => {
// Create an unmodified response // Create an unmodified response
@ -11,7 +21,7 @@ export const createClient = (request: NextRequest) => {
}) })
const supabase = createServerClient( const supabase = createServerClient(
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000", getSupabaseUrlFromRequest(request), // ✅ 这里改了,其他全保留
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{ {
cookies: { cookies: {
@ -19,40 +29,14 @@ export const createClient = (request: NextRequest) => {
return request.cookies.get(name)?.value return request.cookies.get(name)?.value
}, },
set(name: string, value: string, options: CookieOptions) { set(name: string, value: string, options: CookieOptions) {
// If the cookie is updated, update the cookies for the request and response request.cookies.set({ name, value, ...options })
request.cookies.set({ response = NextResponse.next({ request: { headers: request.headers } })
name, response.cookies.set({ name, value, ...options })
value,
...options
})
response = NextResponse.next({
request: {
headers: request.headers
}
})
response.cookies.set({
name,
value,
...options
})
}, },
remove(name: string, options: CookieOptions) { remove(name: string, options: CookieOptions) {
// If the cookie is removed, update the cookies for the request and response request.cookies.set({ name, value: "", ...options })
request.cookies.set({ response = NextResponse.next({ request: { headers: request.headers } })
name, response.cookies.set({ name, value: "", ...options })
value: "",
...options
})
response = NextResponse.next({
request: {
headers: request.headers
}
})
response.cookies.set({
name,
value: "",
...options
})
} }
} }
} }

View File

@ -1,52 +1,24 @@
// import { createServerClient, type CookieOptions } from "@supabase/ssr"
// import { cookies } from "next/headers"
// import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
// export const createClient = (cookieStore: ReturnType<typeof cookies>) => {
// return createServerClient(
// getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
// process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
// {
// cookies: {
// get(name: string) {
// return cookieStore.get(name)?.value
// },
// set(name: string, value: string, options: CookieOptions) {
// try {
// cookieStore.set({ name, value, ...options })
// } catch (error) {
// // The `set` method was called from a Server Component.
// // This can be ignored if you have middleware refreshing
// // user sessions.
// }
// },
// remove(name: string, options: CookieOptions) {
// try {
// cookieStore.set({ name, value: "", ...options })
// } catch (error) {
// // The `delete` method was called from a Server Component.
// // This can be ignored if you have middleware refreshing
// // user sessions.
// }
// }
// }
// }
// )
// }
import { createServerClient, type CookieOptions } from "@supabase/ssr" import { createServerClient, type CookieOptions } from "@supabase/ssr"
import { cookies } from "next/headers" import { cookies, headers } from "next/headers"
import { getRuntimeEnv } from "@/lib/ipconfig"
import { Database } from "@/supabase/types" import { Database } from "@/supabase/types"
export function getSupabaseServerClient() { function getDynamicSupabaseUrl(): string {
const supabaseUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000" const h = headers()
const proto = h.get("x-forwarded-proto") ?? (h.get("host")?.includes(":443") ? "https" : "http")
const rawHost = h.get("x-forwarded-host") ?? h.get("host")!
const hostname = rawHost.split(",")[0].split(":")[0].trim()
return `${proto}://${hostname}:8000`
}
export function getSupabaseServerClient(urlOverride?: string) {
const supabaseUrl = urlOverride ?? getDynamicSupabaseUrl()
console.log(`✅ Supabase URL [Server]: ${supabaseUrl}`)
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
if (!supabaseAnonKey) {
throw new Error("⛔️ NEXT_PUBLIC_SUPABASE_ANON_KEY 未设置")
}
const cookieStore = cookies() const cookieStore = cookies()
@ -58,12 +30,12 @@ export function getSupabaseServerClient() {
set(name: string, value: string, options: CookieOptions) { set(name: string, value: string, options: CookieOptions) {
try { try {
cookieStore.set({ name, value, ...options }) cookieStore.set({ name, value, ...options })
} catch (_) {} } catch {}
}, },
remove(name: string, options: CookieOptions) { remove(name: string, options: CookieOptions) {
try { try {
cookieStore.set({ name, value: "", ...options }) cookieStore.set({ name, value: "", ...options })
} catch (_) {} } catch {}
} }
} }
}) })

View File

@ -39,7 +39,14 @@ CREATE TABLE IF NOT EXISTS profiles (
-- INDEXES -- -- INDEXES --
CREATE INDEX idx_profiles_user_id ON profiles (user_id); DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_indexes WHERE indexname = 'idx_profiles_user_id'
) THEN
CREATE INDEX idx_profiles_user_id ON profiles (user_id);
END IF;
END $$;
-- RLS -- -- RLS --
@ -152,7 +159,7 @@ EXECUTE PROCEDURE delete_old_profile_image();
-- STORAGE -- -- STORAGE --
INSERT INTO storage.buckets (id, name, public) VALUES ('profile_images', 'profile_images', true); INSERT INTO storage.buckets (id, name, public) VALUES ('profile_images', 'profile_images', true) ON CONFLICT (id) DO NOTHING;
CREATE POLICY "Allow public read access on profile images" CREATE POLICY "Allow public read access on profile images"
ON storage.objects FOR SELECT ON storage.objects FOR SELECT

View File

@ -4,6 +4,7 @@ set -e
# 标记文件路径(使用系统级目录) # 标记文件路径(使用系统级目录)
FLAG_DIR="/var/lib/db-init" FLAG_DIR="/var/lib/db-init"
FLAG_FILE="$FLAG_DIR/.db_initialized" FLAG_FILE="$FLAG_DIR/.db_initialized"
LOG_FILE="/var/log/postgres-init.log"
# 数据库连接配置 # 数据库连接配置
export PGUSER=supabase_admin export PGUSER=supabase_admin
@ -12,28 +13,46 @@ export PGHOST=127.0.0.1
export PGPORT=5432 export PGPORT=5432
export PGDATABASE=postgres export PGDATABASE=postgres
# 提前创建标记目录 # 创建标记目录和日志目录
mkdir -p "$FLAG_DIR" mkdir -p "$FLAG_DIR"
mkdir -p "$(dirname "$LOG_FILE")"
# 如果标记已存在,跳过执行 # 如果标记已存在,跳过执行
if [ -f "$FLAG_FILE" ]; then if [ -f "$FLAG_FILE" ]; then
echo "✅ Database has already been initialized, skipping." echo "✅ Database has already been initialized, skipping." | tee -a "$LOG_FILE"
exit 0 exit 0
fi fi
echo "🚀 Starting database initialization..." echo "🚀 Starting database initialization..." | tee -a "$LOG_FILE"
# 封装重试逻辑
retry_until_success() {
local sql_file="$1"
while true; do
echo " ▶ Executing $sql_file" | tee -a "$LOG_FILE"
if psql -v ON_ERROR_STOP=1 -f "$sql_file" >> "$LOG_FILE" 2>&1; then
echo " ✅ Success: $sql_file" | tee -a "$LOG_FILE"
break
else
echo " ❌ ERROR in $sql_file" | tee -a "$LOG_FILE"
echo " 🔁 Retrying in 5 seconds..." | tee -a "$LOG_FILE"
sleep 5
fi
done
}
# 执行 migrations # 执行 migrations
echo "📂 Running migrations..." echo "📂 Running migrations..." | tee -a "$LOG_FILE"
for file in $(ls /supabase/chatdesk/supabase/migrations/*.sql | sort); do for file in $(ls /supabase/chatdesk/supabase/migrations/*.sql | sort); do
echo " ▶ Executing $file" retry_until_success "$file"
psql -f "$file"
done done
# 执行 seed.sql # 执行 seed.sql
echo "🌱 Running seed.sql..." echo "🌱 Running seed.sql..." | tee -a "$LOG_FILE"
psql -f /supabase/chatdesk/supabase/seed.sql retry_until_success /supabase/chatdesk/supabase/seed.sql
# 写入初始化标记 # 写入初始化标记
touch "$FLAG_FILE" touch "$FLAG_FILE"
echo "✅ Database initialization complete. Marked as initialized at $FLAG_FILE" echo "✅ Database initialization complete. Marked as initialized at $FLAG_FILE" | tee -a "$LOG_FILE"

View File

@ -0,0 +1,39 @@
#!/bin/bash
set -e
# 标记文件路径(使用系统级目录)
FLAG_DIR="/var/lib/db-init"
FLAG_FILE="$FLAG_DIR/.db_initialized"
# 数据库连接配置
export PGUSER=supabase_admin
export PGPASSWORD=postgres
export PGHOST=127.0.0.1
export PGPORT=5432
export PGDATABASE=postgres
# 提前创建标记目录
mkdir -p "$FLAG_DIR"
# 如果标记已存在,跳过执行
if [ -f "$FLAG_FILE" ]; then
echo "✅ Database has already been initialized, skipping."
exit 0
fi
echo "🚀 Starting database initialization..."
# 执行 migrations
echo "📂 Running migrations..."
for file in $(ls /supabase/chatdesk/supabase/migrations/*.sql | sort); do
echo " ▶ Executing $file"
psql -f "$file"
done
# 执行 seed.sql
echo "🌱 Running seed.sql..."
psql -f /supabase/chatdesk/supabase/seed.sql
# 写入初始化标记
touch "$FLAG_FILE"
echo "✅ Database initialization complete. Marked as initialized at $FLAG_FILE"

View File

@ -9,7 +9,7 @@ export DATABASE_URL="postgres://supabase_admin:postgres@127.0.0.1:5432/postgres"
#export DATABASE_POOL_URL="postgresql://postgres:postgres@127.0.0.1:6432/postgres" #export DATABASE_POOL_URL="postgresql://postgres:postgres@127.0.0.1:6432/postgres"
#export DB_INSTALL_ROLES="true" #export DB_INSTALL_ROLES="true"
export STORAGE_BACKEND="file" export STORAGE_BACKEND="file"
export FILE_SIZE_LIMIT=52428800 # 50 * 1024 * 1024 export FILE_SIZE_LIMIT=104857600 # 100 * 1024 * 1024
export FILE_STORAGE_BACKEND_PATH="/var/lib/storage" export FILE_STORAGE_BACKEND_PATH="/var/lib/storage"
export DB_INSTALL_ROLES="true" export DB_INSTALL_ROLES="true"