mirror of
https://github.com/Dvorinka/excalidraw-full.git
synced 2026-07-28 23:23:47 +00:00
添加 OIDC 认证支持,更新环境变量配置,重构 Docker Compose 文件,移除旧的 Dex 初始化脚本,优化用户模型,更新前端登录流程,支持通过 OIDC 登录。
This commit is contained in:
+13
-1
@@ -1,8 +1,20 @@
|
||||
# GitHub OAuth 配置
|
||||
GITHUB_CLIENT_ID="xxxxxxxxxxxxxxxxxxx"
|
||||
GITHUB_CLIENT_SECRET="xxxxxxxxxxxxxx"
|
||||
GITHUB_REDIRECT_URL="http://localhost:3000/auth/github/callback" # 或者你部署后的回调地址
|
||||
GITHUB_REDIRECT_URL="http://localhost:3002/auth/github/callback" # 或者你部署后的回调地址
|
||||
|
||||
# OIDC 配置
|
||||
OIDC_ISSUER_URL="http://localhost:5556/.well-known/openid-configuration" # OIDC 提供商地址
|
||||
OIDC_CLIENT_ID="excalidraw"
|
||||
OIDC_CLIENT_SECRET="excalidraw-secret"
|
||||
OIDC_REDIRECT_URL="http://localhost:3002/auth/oidc/callback"
|
||||
|
||||
# JWT 配置
|
||||
JWT_SECRET="YOUR_SUPER_SECRET_RANDOM_STRING"
|
||||
|
||||
# OpenAI 配置
|
||||
OPENAI_API_KEY=sk-xxxxxxxxxxxxxx
|
||||
OPENAI_BASE_URL=https://xxxxxx.xxxxx # 不加/v1...
|
||||
|
||||
# 存储配置
|
||||
STORAGE_TYPE=sqlite # 支持memory,filesystem, kv, s3,具体看README
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
issuer: http://localhost:5556
|
||||
|
||||
storage:
|
||||
type: memory
|
||||
|
||||
web:
|
||||
http: 0.0.0.0:5556
|
||||
allowedOrigins: ["*"]
|
||||
|
||||
logger:
|
||||
level: debug
|
||||
format: text
|
||||
|
||||
enablePasswordDB: true
|
||||
|
||||
staticClients:
|
||||
- id: excalidraw
|
||||
redirectURIs:
|
||||
- {{ .Env.OIDC_REDIRECT_URL }}
|
||||
name: Excalidraw
|
||||
public: true
|
||||
secret: excalidraw-secret
|
||||
|
||||
staticPasswords:
|
||||
- email: {{ .Env.ADMIN_EMAIL }}
|
||||
hash: {{ .Env.ADMIN_PASSWORD_HASH }}
|
||||
username: {{ .Env.ADMIN_USERNAME }}
|
||||
userID: {{ .Env.ADMIN_USER_ID }}
|
||||
@@ -1,29 +0,0 @@
|
||||
issuer: http://localhost:5556
|
||||
|
||||
storage:
|
||||
type: sqlite3
|
||||
config:
|
||||
file: /var/lib/dex/dex.db
|
||||
|
||||
web:
|
||||
http: 0.0.0.0:5556
|
||||
allowedOrigins: ["*"]
|
||||
|
||||
logger:
|
||||
level: debug
|
||||
format: text
|
||||
|
||||
enablePasswordDB: true
|
||||
|
||||
staticClients:
|
||||
- id: excalidraw
|
||||
redirectURIs:
|
||||
- http://localhost:3002/auth/oidc/callback
|
||||
name: Excalidraw
|
||||
secret: ${OIDC_CLIENT_SECRET:-excalidraw-secret}
|
||||
|
||||
staticPasswords:
|
||||
- email: ${ADMIN_EMAIL:[email protected]}
|
||||
hash: ${ADMIN_PASSWORD_HASH}
|
||||
username: ${ADMIN_USERNAME:-admin}
|
||||
userID: "admin-001"
|
||||
+3
-2
@@ -5,8 +5,9 @@ import "time"
|
||||
type (
|
||||
User struct {
|
||||
ID uint `json:"id" gorm:"primarykey"`
|
||||
GitHubID int64 `json:"githubId" gorm:"unique"`
|
||||
Login string `json:"login"`
|
||||
Subject string `json:"subject" gorm:"uniqueIndex"`
|
||||
Login string `json:"login" gorm:"uniqueIndex"`
|
||||
Email string `json:"email"`
|
||||
AvatarURL string `json:"avatarUrl"`
|
||||
Name string `json:"name"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
|
||||
+19
-9
@@ -8,20 +8,30 @@ services:
|
||||
ports:
|
||||
- "5556:5556"
|
||||
volumes:
|
||||
- ./config/dex.config.yml:/etc/dex/config.yml
|
||||
- dex-data:/var/lib/dex
|
||||
- ./config/dex.config.yaml:/etc/dex/config.yaml
|
||||
environment:
|
||||
- GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
|
||||
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
|
||||
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
|
||||
- ADMIN_EMAIL=${ADMIN_EMAIL:[email protected]}
|
||||
- OIDC_REDIRECT_URL=${OIDC_REDIRECT_URL:-http://localhost:3000/auth/oidc/callback}
|
||||
- OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET:-excalidraw-secret}
|
||||
- OIDC_CLIENT_ID=${OIDC_CLIENT_ID:-excalidraw}
|
||||
- OIDC_ISSUER=${OIDC_ISSUER:-http://localhost:5556}
|
||||
- ADMIN_USERNAME=${ADMIN_USERNAME:-admin}
|
||||
- ADMIN_PASSWORD_HASH=${ADMIN_PASSWORD_HASH:-your_secure_password}
|
||||
- ADMIN_EMAIL=${ADMIN_EMAIL:[email protected]}
|
||||
- ADMIN_USER_ID=${ADMIN_USER_ID:-'admin1234'}
|
||||
command: ["dex", "serve", "/etc/dex/config.yaml"]
|
||||
networks:
|
||||
- dex-network
|
||||
|
||||
volumes:
|
||||
dex-data:
|
||||
excalidraw:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: excalidraw-complete.Dockerfile
|
||||
ports:
|
||||
- "3003:3002"
|
||||
volumes:
|
||||
- ./data:/root/data
|
||||
- ./excalidraw.db:/root/excalidraw.db:Z
|
||||
- ./.env:/root/.env
|
||||
|
||||
networks:
|
||||
dex-network:
|
||||
|
||||
@@ -39,7 +39,9 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 // indirect
|
||||
github.com/aws/smithy-go v1.20.1 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.15.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
|
||||
@@ -38,6 +38,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 h1:J/PpTf/hllOjx8Xu9DMflff3Fajf
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.5/go.mod h1:0ih0Z83YDH/QeQ6Ori2yGE2XvWYv/Xm+cZc01LC6oK0=
|
||||
github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
|
||||
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
|
||||
github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -51,6 +53,8 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"excalidraw-complete/core"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
oidcOauthConfig *oauth2.Config
|
||||
oidcProvider *oidc.Provider
|
||||
verifier *oidc.IDTokenVerifier
|
||||
)
|
||||
|
||||
// OIDCClaims represents the claims from OIDC token
|
||||
type OIDCClaims struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
Picture string `json:"picture"`
|
||||
Sub string `json:"sub"`
|
||||
}
|
||||
|
||||
func InitDex() {
|
||||
providerURL := os.Getenv("OIDC_ISSUER_URL")
|
||||
clientID := os.Getenv("OIDC_CLIENT_ID")
|
||||
clientSecret := os.Getenv("OIDC_CLIENT_SECRET")
|
||||
redirectURL := os.Getenv("OIDC_REDIRECT_URL")
|
||||
|
||||
if providerURL == "" || clientID == "" || clientSecret == "" {
|
||||
logrus.Warn("OIDC credentials are not set. OIDC authentication routes will not work.")
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
oidcProvider, err = oidc.NewProvider(context.Background(), providerURL)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to create OIDC provider: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
oidcOauthConfig = &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectURL,
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
Endpoint: oidcProvider.Endpoint(),
|
||||
}
|
||||
|
||||
logrus.Info("OIDC provider initialized")
|
||||
|
||||
verifier = oidcProvider.Verifier(&oidc.Config{
|
||||
ClientID: clientID,
|
||||
})
|
||||
}
|
||||
|
||||
func HandleOIDCLogin(w http.ResponseWriter, r *http.Request) {
|
||||
if oidcOauthConfig == nil {
|
||||
http.Error(w, "OIDC is not configured", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
url := oidcOauthConfig.AuthCodeURL("random", oauth2.AccessTypeOffline)
|
||||
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
func HandleOIDCCallback(w http.ResponseWriter, r *http.Request) {
|
||||
if oidcOauthConfig == nil {
|
||||
http.Error(w, "OIDC is not configured", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
code := r.FormValue("code")
|
||||
if code == "" {
|
||||
logrus.Error("no code in callback")
|
||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := oidcOauthConfig.Exchange(context.Background(), code)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to exchange token: %s", err.Error())
|
||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
rawIDToken, ok := token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
logrus.Error("no id_token in token response")
|
||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
idToken, err := verifier.Verify(context.Background(), rawIDToken)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to verify ID token: %s", err.Error())
|
||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
var claims OIDCClaims
|
||||
if err := idToken.Claims(&claims); err != nil {
|
||||
logrus.Errorf("failed to extract claims from ID token: %s", err.Error())
|
||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
// Create user from OIDC claims
|
||||
user := &core.User{
|
||||
Subject: claims.Sub,
|
||||
Login: claims.PreferredUsername,
|
||||
Email: claims.Email,
|
||||
AvatarURL: claims.Picture,
|
||||
Name: claims.Name,
|
||||
}
|
||||
|
||||
// If preferred_username is not available, use email
|
||||
if user.Login == "" && user.Email != "" {
|
||||
user.Login = user.Email
|
||||
}
|
||||
|
||||
jwtToken, err := createOIDCJWT(user)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to create JWT: %s", err.Error())
|
||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
// Redirect to frontend with token
|
||||
http.Redirect(w, r, fmt.Sprintf("/?token=%s", jwtToken), http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
func createOIDCJWT(user *core.User) (string, error) {
|
||||
claims := AppClaims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: user.Subject,
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)), // 1 week
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
},
|
||||
Login: user.Login,
|
||||
AvatarURL: user.AvatarURL,
|
||||
Name: user.Name,
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString(jwtSecret)
|
||||
}
|
||||
@@ -23,7 +23,6 @@ var (
|
||||
jwtSecret []byte
|
||||
)
|
||||
|
||||
const oauthStateString = "random"
|
||||
|
||||
// AppClaims represents the custom claims for the JWT.
|
||||
type AppClaims struct {
|
||||
@@ -124,10 +123,9 @@ func HandleGitHubCallback(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// For now we don't have a user database, so we create a user object on the fly.
|
||||
// In phase 3, we will save/get the user from the database here.
|
||||
// Create user object using Subject instead of GitHubID
|
||||
user := &core.User{
|
||||
GitHubID: githubUser.ID,
|
||||
Subject: fmt.Sprintf("github:%d", githubUser.ID),
|
||||
Login: githubUser.Login,
|
||||
AvatarURL: githubUser.AvatarURL,
|
||||
Name: githubUser.Name,
|
||||
@@ -147,7 +145,7 @@ func HandleGitHubCallback(w http.ResponseWriter, r *http.Request) {
|
||||
func createJWT(user *core.User) (string, error) {
|
||||
claims := AppClaims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: fmt.Sprintf("%d", user.GitHubID),
|
||||
Subject: user.Subject,
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24 * 7)), // 1 week
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
},
|
||||
|
||||
@@ -171,6 +171,11 @@ func setupRouter(store stores.Store) *chi.Mux {
|
||||
r.Get("/callback", auth.HandleGitHubCallback)
|
||||
})
|
||||
|
||||
r.Route("/auth/oidc", func(r chi.Router) {
|
||||
r.Get("/login", auth.HandleOIDCLogin)
|
||||
r.Get("/callback", auth.HandleOIDCCallback)
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -307,6 +312,7 @@ func main() {
|
||||
})
|
||||
|
||||
auth.Init()
|
||||
auth.InitDex()
|
||||
openai.Init()
|
||||
store := stores.GetStore()
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# 检查环境变量
|
||||
if [ -z "$ADMIN_PASSWORD" ]; then
|
||||
echo "错误: 请设置 ADMIN_PASSWORD 环境变量"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 生成密码哈希
|
||||
echo "正在生成密码哈希..."
|
||||
PASSWORD_HASH=$(docker run --rm dexidp/dex:v2.38.0 hash --password="$ADMIN_PASSWORD")
|
||||
|
||||
# 创建临时配置文件
|
||||
cat > /tmp/dex-init-config.yml << EOF
|
||||
issuer: http://localhost:5556
|
||||
|
||||
storage:
|
||||
type: sqlite3
|
||||
config:
|
||||
file: /var/lib/dex/dex.db
|
||||
|
||||
web:
|
||||
http: 0.0.0.0:5556
|
||||
|
||||
logger:
|
||||
level: info
|
||||
|
||||
enablePasswordDB: true
|
||||
|
||||
staticPasswords:
|
||||
- email: ${ADMIN_EMAIL:[email protected]}
|
||||
hash: $PASSWORD_HASH
|
||||
username: ${ADMIN_USERNAME:-admin}
|
||||
userID: "admin-001"
|
||||
EOF
|
||||
|
||||
# 初始化 Dex 数据库
|
||||
echo "正在初始化 Dex 数据库..."
|
||||
docker run --rm \
|
||||
-v $(pwd)/config/dex.config.yml:/etc/dex/config.yml \
|
||||
-v dex-data:/var/lib/dex \
|
||||
dexidp/dex:v2.38.0 \
|
||||
serve /etc/dex/config.yml &
|
||||
DEX_PID=$!
|
||||
|
||||
# 等待 Dex 启动
|
||||
echo "等待 Dex 启动..."
|
||||
sleep 10
|
||||
|
||||
# 停止临时 Dex 进程
|
||||
kill $DEX_PID 2>/dev/null || true
|
||||
|
||||
echo "Dex 用户初始化完成!"
|
||||
echo "管理员账户:"
|
||||
echo " 用户名: ${ADMIN_USERNAME:-admin}"
|
||||
echo " 邮箱: ${ADMIN_EMAIL:-admin@example.com}"
|
||||
echo " 密码: $ADMIN_PASSWORD"
|
||||
echo ""
|
||||
echo "请使用以下凭据登录:"
|
||||
echo " Dex UI: http://localhost:5556"
|
||||
echo " 用户名: ${ADMIN_USERNAME:-admin}"
|
||||
echo " 密码: $ADMIN_PASSWORD"
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 生成随机密码
|
||||
generate_password() {
|
||||
openssl rand -base64 16 | tr -d "=+/" | cut -c1-16
|
||||
}
|
||||
|
||||
# 生成 JWT 密钥
|
||||
generate_jwt_secret() {
|
||||
openssl rand -base64 32 | tr -d "=+/" | cut -c1-32
|
||||
}
|
||||
|
||||
echo "正在生成环境变量配置..."
|
||||
|
||||
# 生成随机密码和密钥
|
||||
ADMIN_PASSWORD=$(generate_password)
|
||||
JWT_SECRET=$(generate_jwt_secret)
|
||||
OIDC_CLIENT_SECRET=$(generate_password)
|
||||
|
||||
# 创建 .env 文件
|
||||
cat > .env << EOF
|
||||
# === 认证配置 ===
|
||||
AUTH_TYPE=oidc
|
||||
|
||||
# GitHub OAuth 配置 (可选)
|
||||
# GITHUB_CLIENT_ID=your_github_client_id
|
||||
# GITHUB_CLIENT_SECRET=your_github_client_secret
|
||||
# GITHUB_REDIRECT_URL=http://localhost:3002/auth/github/callback
|
||||
|
||||
# OIDC 配置
|
||||
OIDC_ISSUER_URL=http://localhost:5556/.well-known/openid-configuration
|
||||
OIDC_CLIENT_ID=excalidraw
|
||||
OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET
|
||||
OIDC_REDIRECT_URL=http://localhost:3002/auth/oidc/callback
|
||||
|
||||
# Dex 配置
|
||||
OIDC_CLIENT_SECRET=$OIDC_CLIENT_SECRET
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=$ADMIN_PASSWORD
|
||||
[email protected]
|
||||
|
||||
# === JWT 配置 ===
|
||||
JWT_SECRET=$JWT_SECRET
|
||||
|
||||
# === 存储配置 ===
|
||||
STORAGE_TYPE=sqlite
|
||||
DATA_SOURCE_NAME=excalidraw.db
|
||||
LOCAL_STORAGE_PATH=./data
|
||||
|
||||
# === 应用配置 ===
|
||||
LISTEN=:3002
|
||||
LOG_LEVEL=info
|
||||
|
||||
# === OpenAI 配置 (可选) ===
|
||||
# OPENAI_API_KEY=sk-your_openai_api_key
|
||||
# OPENAI_BASE_URL=https://api.openai.com
|
||||
EOF
|
||||
|
||||
echo "环境变量配置已生成到 .env 文件"
|
||||
echo ""
|
||||
echo "重要信息请保存:"
|
||||
echo " 管理员密码: $ADMIN_PASSWORD"
|
||||
echo " JWT 密钥: $JWT_SECRET"
|
||||
echo " Dex 客户端密钥: $OIDC_CLIENT_SECRET"
|
||||
echo ""
|
||||
echo "请运行以下命令启动服务:"
|
||||
echo " 1. docker-compose -f docker-compose.dex.yml up -d"
|
||||
echo " 2. ./scripts/init-dex-users.sh"
|
||||
echo " 3. docker-compose up -d"
|
||||
Reference in New Issue
Block a user