Compare commits
42 Commits
main
...
dynamicIPv
| Author | SHA1 | Date |
|---|---|---|
|
|
c37ce1dac5 | |
|
|
b4178d648a | |
|
|
f7706e0abb | |
|
|
bffbc4665a | |
|
|
94554e9c69 | |
|
|
46a8682fbe | |
|
|
476d33797a | |
|
|
34d8a59fd4 | |
|
|
c6164443ae | |
|
|
4011b94d30 | |
|
|
f6fd1b8011 | |
|
|
b98c049e58 | |
|
|
83850fc475 | |
|
|
d4419293fb | |
|
|
f90c5361ed | |
|
|
008bbb3643 | |
|
|
92ca21d2cb | |
|
|
40f5856aa0 | |
|
|
6354af6ea1 | |
|
|
dcb30caafb | |
|
|
32874e0d15 | |
|
|
7d70c45db8 | |
|
|
1a37e37f13 | |
|
|
811efaef32 | |
|
|
1e470bf7b5 | |
|
|
9b8b705e97 | |
|
|
452a58a74a | |
|
|
27ea4f576c | |
|
|
62b0dbd40b | |
|
|
d71078b262 | |
|
|
072dce8517 | |
|
|
5c6ef8366b | |
|
|
607531f309 | |
|
|
888bdead90 | |
|
|
402779994e | |
|
|
87ec871a4b | |
|
|
d61a94d66a | |
|
|
e35878178a | |
|
|
04b5c0c94e | |
|
|
d61a2dc385 | |
|
|
768db37011 | |
|
|
f6ae45debe |
|
|
@ -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
|
||||
~
|
||||
|
|
@ -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."
|
||||
|
||||
|
|
@ -33,4 +33,4 @@ EMAIL_DOMAIN_WHITELIST=
|
|||
EMAIL_WHITELIST=
|
||||
|
||||
# File size limit for uploads in bytes
|
||||
NEXT_PUBLIC_USER_FILE_SIZE_LIMIT=10485760
|
||||
NEXT_PUBLIC_USER_FILE_SIZE_LIMIT=104857600
|
||||
|
|
@ -3,16 +3,14 @@ import { GlobalState } from "@/components/utility/global-state"
|
|||
import { Providers } from "@/components/utility/providers"
|
||||
import TranslationsProvider from "@/components/utility/translations-provider"
|
||||
import initTranslations from "@/lib/i18n"
|
||||
import { Database } from "@/supabase/types"
|
||||
import { getSupabaseServerClient } from "@/lib/supabase/server"
|
||||
import { Metadata, Viewport } from "next"
|
||||
import { Inter } from "next/font/google"
|
||||
import { cookies } from "next/headers"
|
||||
import { ReactNode } from "react"
|
||||
import "./globals.css"
|
||||
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
||||
import Script from "next/script"
|
||||
import { Suspense } from "react"
|
||||
import { RuntimeEnvScript } from "@/components/utility/runtime-env-provider"
|
||||
|
||||
|
||||
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({
|
||||
params: { locale }
|
||||
}: {
|
||||
|
|
@ -113,16 +91,6 @@ export default async function RootLayout({
|
|||
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 { data, error } = await supabase.auth.getSession();
|
||||
if (error) {
|
||||
|
|
@ -137,10 +105,7 @@ export default async function RootLayout({
|
|||
<html lang="en" suppressHydrationWarning>
|
||||
|
||||
<head>
|
||||
<Script
|
||||
src="/env.js"
|
||||
strategy="beforeInteractive" // 确保在 React 启动之前加载
|
||||
/>
|
||||
<RuntimeEnvScript />
|
||||
</head>
|
||||
|
||||
<body className={inter.className}>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { get } from "@vercel/edge-config"
|
|||
import { Metadata } from "next"
|
||||
import { cookies, headers } from "next/headers"
|
||||
import { redirect } from "next/navigation"
|
||||
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
||||
import { getRuntimeEnv } from "@/lib/runtime-env"
|
||||
import { PostgrestError } from '@supabase/supabase-js';
|
||||
|
||||
import initTranslations from "@/lib/i18n";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
|
|||
import { ServerRuntime } from "next"
|
||||
import OpenAI from "openai"
|
||||
|
||||
export const runtime: ServerRuntime = "edge"
|
||||
//export const runtime: ServerRuntime = "edge"
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ import { OpenAIStream, StreamingTextResponse } from "ai"
|
|||
import { ServerRuntime } from "next"
|
||||
import OpenAI from "openai"
|
||||
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) {
|
||||
const json = await request.json()
|
||||
|
|
@ -20,7 +21,7 @@ export async function POST(request: Request) {
|
|||
|
||||
try {
|
||||
const supabaseAdmin = createClient<Database>(
|
||||
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
||||
)
|
||||
|
||||
|
|
@ -31,6 +32,7 @@ export async function POST(request: Request) {
|
|||
.single()
|
||||
|
||||
if (!customModel) {
|
||||
console.error("❌ No custom model found:", error)
|
||||
throw new Error(error.message)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { ServerRuntime } from "next"
|
|||
import OpenAI from "openai"
|
||||
import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs"
|
||||
|
||||
export const runtime: ServerRuntime = "edge"
|
||||
//export const runtime: ServerRuntime = "edge"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const json = await request.json()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { ServerRuntime } from "next"
|
|||
import OpenAI from "openai"
|
||||
import { ChatCompletionCreateParamsBase } from "openai/resources/chat/completions.mjs"
|
||||
|
||||
export const runtime: ServerRuntime = "edge"
|
||||
//export const runtime: ServerRuntime = "edge"
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const json = await request.json()
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ import { FileItemChunk } from "@/types"
|
|||
import { createClient } from "@supabase/supabase-js"
|
||||
import { NextResponse } from "next/server"
|
||||
import OpenAI from "openai"
|
||||
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
||||
import { getRuntimeEnvForRouterAPI } from "@/lib/runtime-env"
|
||||
import { generateBgeM3Embedding } from "@/lib/generate-bgem3-embedding"
|
||||
|
||||
export const runtime = "nodejs"
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const json = await req.json()
|
||||
const { text, fileId, embeddingsProvider, fileExtension } = json as {
|
||||
|
|
@ -20,7 +22,7 @@ export async function POST(req: Request) {
|
|||
|
||||
try {
|
||||
const supabaseAdmin = createClient<Database>(
|
||||
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,13 +13,15 @@ import { FileItemChunk } from "@/types"
|
|||
import { createClient } from "@supabase/supabase-js"
|
||||
import { NextResponse } from "next/server"
|
||||
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) {
|
||||
|
||||
try {
|
||||
const supabaseAdmin = createClient<Database>(
|
||||
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import { checkApiKey, getServerProfile } from "@/lib/server/server-chat-helpers"
|
|||
import { Database } from "@/supabase/types"
|
||||
import { createClient } from "@supabase/supabase-js"
|
||||
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) {
|
||||
|
||||
|
|
@ -22,13 +24,12 @@ export async function POST(request: Request) {
|
|||
|
||||
try {
|
||||
|
||||
const rawSupaUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000"
|
||||
const rawSupaUrl = getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000"
|
||||
const supaUrlObj = new URL(rawSupaUrl)
|
||||
supaUrlObj.port = "8000"
|
||||
|
||||
const supabaseAdmin = createClient<Database>(
|
||||
supaUrlObj.origin,
|
||||
// getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { Database } from "@/supabase/types"
|
||||
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) {
|
||||
const json = await request.json()
|
||||
|
|
@ -12,7 +12,7 @@ export async function POST(request: Request) {
|
|||
|
||||
try {
|
||||
const supabaseAdmin = createClient<Database>(
|
||||
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { Database } from "@/supabase/types"
|
||||
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) {
|
||||
const json = await request.json()
|
||||
|
|
@ -12,7 +13,7 @@ export async function POST(request: Request) {
|
|||
|
||||
try {
|
||||
const supabaseAdmin = createClient<Database>(
|
||||
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
getRuntimeEnvForRouterAPI("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import {
|
|||
import React from "react"
|
||||
import { toast } from "sonner"
|
||||
import { v4 as uuidv4 } from "uuid"
|
||||
import { getRuntimeEnv } from "@/lib/ipconfig"
|
||||
import { getRuntimeEnv } from "@/lib/runtime-env"
|
||||
|
||||
|
||||
type RetrievedFileItem = Tables<"file_items"> & {
|
||||
|
|
|
|||
|
|
@ -812,7 +812,7 @@ export const ProfileSettings: FC<ProfileSettingsProps> = ({}) => {
|
|||
<div className="flex items-center space-x-1">
|
||||
<ThemeSwitcher />
|
||||
|
||||
<WithTooltip
|
||||
{/* <WithTooltip
|
||||
display={
|
||||
<div>
|
||||
{t("profile.downloadTooltip")}
|
||||
|
|
@ -825,7 +825,7 @@ export const ProfileSettings: FC<ProfileSettingsProps> = ({}) => {
|
|||
onClick={exportLocalStorageAsJSON}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
/> */}
|
||||
</div>
|
||||
|
||||
<div className="ml-auto space-x-2">
|
||||
|
|
|
|||
|
|
@ -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)} };`,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -9,13 +9,17 @@ export const uploadFile = async (
|
|||
file_id: string
|
||||
}
|
||||
) => {
|
||||
const SIZE_LIMIT = parseInt(
|
||||
process.env.NEXT_PUBLIC_USER_FILE_SIZE_LIMIT || "10000000"
|
||||
)
|
||||
// const SIZE_LIMIT = parseInt(
|
||||
// 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) {
|
||||
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`
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
try {
|
||||
// 取 Supabase URL 或本地默认
|
||||
const supaUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000"
|
||||
// 构造 Embedding 服务地址:同 host + 8001 端口
|
||||
const urlObj = new URL(supaUrl)
|
||||
urlObj.port = "8001" // 强制改成 8001
|
||||
const apiUrl = `${urlObj.origin}/v1/embeddings`
|
||||
// ✅ 使用服务端真实 URL(自动取协议/host)→ 端口强制为 8001
|
||||
const apiUrl = `${getServerRuntimeUrl("8001")}/v1/embeddings`
|
||||
console.debug("......[generateBgeM3Embedding] apiUrl =", apiUrl)
|
||||
|
||||
const response = await fetch(apiUrl, {
|
||||
|
|
|
|||
|
|
@ -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 的值
|
||||
}
|
||||
|
|
@ -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}`
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// lib/runtime-env/client.ts
|
||||
export function getRuntimeEnvCSR(key: string): string | undefined {
|
||||
return (window as any).RUNTIME_ENV?.[key]
|
||||
}
|
||||
|
|
@ -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]
|
||||
}
|
||||
|
|
@ -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`
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
import { Database, Tables } from "@/supabase/types"
|
||||
import { VALID_ENV_KEYS } from "@/types/valid-keys"
|
||||
//import { createServerClient } from "@supabase/ssr"
|
||||
import { getSupabaseServerClient } from "@/lib/supabase/server"
|
||||
import { cookies } from "next/headers"
|
||||
import { getRuntimeEnv } from "@/lib/ipconfig" // 新增引入
|
||||
import { getRuntimeEnv } from "@/lib/runtime-env"
|
||||
|
||||
export async function getServerProfile() {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 { getRuntimeEnv } from "@/lib/ipconfig"
|
||||
import { Database } from "@/supabase/types"
|
||||
|
||||
// 动态获取 URL,防止静态打包成 localhost:8000
|
||||
let supabaseUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000"
|
||||
let supabaseUrl = "http://localhost:8000"
|
||||
|
||||
// 仅在浏览器端使用 localStorage
|
||||
if (typeof window !== "undefined") {
|
||||
// 如果 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"
|
||||
if (typeof window === "undefined") {
|
||||
// ✅ SSR 时动态加载,不会触发 headers() 静态报错
|
||||
const load = async () => {
|
||||
const { loadSupabaseUrl } = await import("@/lib/runtime-env/server")
|
||||
const url = await loadSupabaseUrl()
|
||||
if (url) {
|
||||
supabaseUrl = url
|
||||
console.log("[SSR] SUPABASE_URL =", supabaseUrl)
|
||||
}
|
||||
}
|
||||
// 注意:不能阻塞同步逻辑,只是设置变量
|
||||
load()
|
||||
} else {
|
||||
// 如果在服务器端,使用默认或从环境变量获取的 URL
|
||||
console.log("[server-side] Falling back to default supabaseUrl:", supabaseUrl)
|
||||
const runtimeEnv = (window as any).RUNTIME_ENV
|
||||
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!
|
||||
|
||||
// 导出单例,兼容所有调用旧代码方式
|
||||
export const supabase = createBrowserClient<Database>(
|
||||
supabaseUrl,
|
||||
supabaseAnonKey
|
||||
|
|
|
|||
|
|
@ -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 { getRuntimeEnv } from "@/lib/ipconfig"
|
||||
import { getRuntimeEnv } from "@/lib/runtime-env"
|
||||
|
||||
export const createClient = () =>
|
||||
createBrowserClient(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
import { createServerClient, type CookieOptions } from "@supabase/ssr"
|
||||
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) => {
|
||||
// Create an unmodified response
|
||||
|
|
@ -11,7 +21,7 @@ export const createClient = (request: NextRequest) => {
|
|||
})
|
||||
|
||||
const supabase = createServerClient(
|
||||
getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000",
|
||||
getSupabaseUrlFromRequest(request), // ✅ 这里改了,其他全保留
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
|
|
@ -19,40 +29,14 @@ export const createClient = (request: NextRequest) => {
|
|||
return request.cookies.get(name)?.value
|
||||
},
|
||||
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
|
||||
})
|
||||
response = NextResponse.next({
|
||||
request: {
|
||||
headers: request.headers
|
||||
}
|
||||
})
|
||||
response.cookies.set({
|
||||
name,
|
||||
value,
|
||||
...options
|
||||
})
|
||||
request.cookies.set({ name, value, ...options })
|
||||
response = NextResponse.next({ request: { headers: request.headers } })
|
||||
response.cookies.set({ name, value, ...options })
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
// If the cookie is removed, update the cookies for the request and response
|
||||
request.cookies.set({
|
||||
name,
|
||||
value: "",
|
||||
...options
|
||||
})
|
||||
response = NextResponse.next({
|
||||
request: {
|
||||
headers: request.headers
|
||||
}
|
||||
})
|
||||
response.cookies.set({
|
||||
name,
|
||||
value: "",
|
||||
...options
|
||||
})
|
||||
request.cookies.set({ name, value: "", ...options })
|
||||
response = NextResponse.next({ request: { headers: request.headers } })
|
||||
response.cookies.set({ name, value: "", ...options })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 { cookies } from "next/headers"
|
||||
import { getRuntimeEnv } from "@/lib/ipconfig"
|
||||
import { cookies, headers } from "next/headers"
|
||||
import { Database } from "@/supabase/types"
|
||||
|
||||
export function getSupabaseServerClient() {
|
||||
const supabaseUrl = getRuntimeEnv("SUPABASE_URL") ?? "http://localhost:8000"
|
||||
function getDynamicSupabaseUrl(): 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}: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!
|
||||
if (!supabaseAnonKey) {
|
||||
throw new Error("⛔️ NEXT_PUBLIC_SUPABASE_ANON_KEY 未设置")
|
||||
}
|
||||
|
||||
const cookieStore = cookies()
|
||||
|
||||
|
|
@ -58,12 +30,12 @@ export function getSupabaseServerClient() {
|
|||
set(name: string, value: string, options: CookieOptions) {
|
||||
try {
|
||||
cookieStore.set({ name, value, ...options })
|
||||
} catch (_) {}
|
||||
} catch {}
|
||||
},
|
||||
remove(name: string, options: CookieOptions) {
|
||||
try {
|
||||
cookieStore.set({ name, value: "", ...options })
|
||||
} catch (_) {}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -39,7 +39,14 @@ CREATE TABLE IF NOT EXISTS profiles (
|
|||
|
||||
-- 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 --
|
||||
|
||||
|
|
@ -152,7 +159,7 @@ EXECUTE PROCEDURE delete_old_profile_image();
|
|||
|
||||
-- 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"
|
||||
ON storage.objects FOR SELECT
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ set -e
|
|||
# 标记文件路径(使用系统级目录)
|
||||
FLAG_DIR="/var/lib/db-init"
|
||||
FLAG_FILE="$FLAG_DIR/.db_initialized"
|
||||
LOG_FILE="/var/log/postgres-init.log"
|
||||
|
||||
# 数据库连接配置
|
||||
export PGUSER=supabase_admin
|
||||
|
|
@ -12,28 +13,46 @@ export PGHOST=127.0.0.1
|
|||
export PGPORT=5432
|
||||
export PGDATABASE=postgres
|
||||
|
||||
# 提前创建标记目录
|
||||
# 创建标记目录和日志目录
|
||||
mkdir -p "$FLAG_DIR"
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
|
||||
# 如果标记已存在,跳过执行
|
||||
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
|
||||
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
|
||||
echo "📂 Running migrations..."
|
||||
echo "📂 Running migrations..." | tee -a "$LOG_FILE"
|
||||
for file in $(ls /supabase/chatdesk/supabase/migrations/*.sql | sort); do
|
||||
echo " ▶ Executing $file"
|
||||
psql -f "$file"
|
||||
retry_until_success "$file"
|
||||
done
|
||||
|
||||
# 执行 seed.sql
|
||||
echo "🌱 Running seed.sql..."
|
||||
psql -f /supabase/chatdesk/supabase/seed.sql
|
||||
echo "🌱 Running seed.sql..." | tee -a "$LOG_FILE"
|
||||
retry_until_success /supabase/chatdesk/supabase/seed.sql
|
||||
|
||||
# 写入初始化标记
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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 DB_INSTALL_ROLES="true"
|
||||
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 DB_INSTALL_ROLES="true"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue