Skip to content
Closed

合并 #6418

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5c47d89
ci: add GHCR workflow for custom Docker image builds
Sivyer9303 Jul 8, 2026
28d3045
fix(ci): correct GHCR workflow checkout ref handling
Sivyer9303 Jul 8, 2026
aebda55
fix(ci): lowercase GHCR image repository name
Sivyer9303 Jul 8, 2026
7aa1f42
fix(ci): grant packages write permission for GHCR manifest push
Sivyer9303 Jul 8, 2026
ca7a0dc
Merge branch 'main' of https://github.com/QuantumNous/new-api
Sivyer9303 Jul 9, 2026
8991a01
Merge branch 'QuantumNous:main' into main
Sivyer9303 Jul 10, 2026
5955359
增加兑换码查询
Sivyer9303 Jul 10, 2026
c7665b8
Merge branch 'F_optimize'
Sivyer9303 Jul 16, 2026
9dbd655
Merge branch 'QuantumNous:main' into main
Sivyer9303 Jul 17, 2026
cc9ba5b
Merge branch 'QuantumNous:main' into main
Sivyer9303 Jul 17, 2026
e1c6690
Add Connect Tool wizard for API key deep-link onboarding.
Sivyer9303 Jul 17, 2026
21c1f69
Fix Connect Tool group/model filtering by primary endpoint.
Sivyer9303 Jul 18, 2026
30e43df
Merge branch 'QuantumNous:main' into cursor/connect-tool-wizard
Sivyer9303 Jul 18, 2026
65bbbea
定制页面
Sivyer9303 Jul 18, 2026
2a98072
Merge branch 'cursor/connect-tool-wizard' into F_充值优化
Sivyer9303 Jul 18, 2026
668bc10
去除复制功能
Sivyer9303 Jul 18, 2026
d2d8852
Merge branch 'QuantumNous:main' into main
Sivyer9303 Jul 18, 2026
e38cd96
ui修改
Sivyer9303 Jul 18, 2026
3dc337d
feat: 可用性监控刷新间隔可配置,并修复拓展设置入口与保存
Sivyer9303 Jul 18, 2026
ac3779a
feat: 新增幸运老虎机抽奖,并完善 Turnstile 机器人保护
Sivyer9303 Jul 19, 2026
03f1ecc
feat: 抽奖参与改为校验兑换码,周四放行未兑换用户
Sivyer9303 Jul 19, 2026
5e30730
Merge branch 'QuantumNous:main' into main
Sivyer9303 Jul 19, 2026
bdcd8ea
ui: 优化疯狂星期四横幅,并修复兑换码软删除导致的参与校验
Sivyer9303 Jul 19, 2026
56ad272
Merge branch 'feat/lucky-slot-lottery'
Sivyer9303 Jul 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions .github/workflows/docker-ghcr-custom.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
name: Publish custom image to GHCR

on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
image_tag:
description: 'Docker image tag only (e.g. v1.0.0-custom), not a git ref'
required: true
type: string

env:
REGISTRY: ghcr.io

permissions:
contents: read
packages: write

jobs:
prepare:
name: Prepare build metadata
runs-on: ubuntu-latest
outputs:
image_name: ${{ steps.meta.outputs.image_name }}
tag: ${{ steps.meta.outputs.tag }}
steps:
- id: meta
run: |
echo "image_name=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ github.event.inputs.image_tag }}"
else
TAG=${GITHUB_REF#refs/tags/}
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"

Comment on lines +29 to +38

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Untrusted input interpolated directly into a shell script (script injection).

TAG="${{ github.event.inputs.image_tag }}" splices a workflow_dispatch string input directly into a run: block instead of passing it via env:. A crafted image_tag value containing shell metacharacters (e.g. `...` or $(...)) can execute arbitrary commands in the runner, which holds packages: write credentials. This tainted value then propagates through prepare's output and is reused via further ${{ }} interpolation in later run: steps (e.g. Lines 68, 72, 108-109, 124-125, 137-139, 144-146, 152-154), so hardening at the source doesn't automatically fix the downstream reuses — each run: step should consume the value via an env:-mapped shell variable.

🔒 Proposed fix at the source
   steps:
       - id: meta
+        env:
+          IMAGE_TAG_INPUT: ${{ github.event.inputs.image_tag }}
         run: |
           echo "image_name=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
           if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
-            TAG="${{ github.event.inputs.image_tag }}"
+            TAG="$IMAGE_TAG_INPUT"
           else
             TAG=${GITHUB_REF#refs/tags/}
           fi
           echo "tag=${TAG}" >> "$GITHUB_OUTPUT"

Apply the same env:-mapping pattern (e.g. env: TAG: ${{ needs.prepare.outputs.tag }} then reference $TAG) to the other run: steps that currently interpolate ${{ needs.prepare.outputs.tag }} / ${{ needs.prepare.outputs.image_name }} directly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- id: meta
run: |
echo "image_name=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="${{ github.event.inputs.image_tag }}"
else
TAG=${GITHUB_REF#refs/tags/}
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- id: meta
env:
IMAGE_TAG_INPUT: ${{ github.event.inputs.image_tag }}
run: |
echo "image_name=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
TAG="$IMAGE_TAG_INPUT"
else
TAG=${GITHUB_REF#refs/tags/}
fi
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
🧰 Tools
🪛 zizmor (1.26.1)

[error] 33-33: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-ghcr-custom.yml around lines 29 - 38, Harden the
Docker workflow against shell injection by passing workflow_dispatch.image_tag
through an env-mapped variable before using it in the meta step, and validate or
safely handle the resulting TAG. Update every later run step that directly
interpolates needs.prepare.outputs.tag or needs.prepare.outputs.image_name to
map those outputs through env and reference shell variables instead, including
the affected publish, tagging, and signing steps; preserve the existing image
and tag behavior.

Source: Linters/SAST tools

build_single_arch:
name: Build & push (${{ matrix.arch }})
needs: [prepare]
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
platform: linux/amd64
runner: ubuntu-latest
- arch: arm64
platform: linux/arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
outputs:
tag: ${{ needs.prepare.outputs.tag }}

permissions:
contents: read
packages: write

steps:
- name: Check out
uses: actions/checkout@v4
with:
fetch-depth: 0
Comment on lines +61 to +64

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## workflow excerpt"
sed -n '1,120p' .github/workflows/docker-ghcr-custom.yml 2>/dev/null || true

echo
echo "## dockerignore files"
fd -a '.dockerignore$|Dockerfile$|compose.*' . | sed 's#^\./##' | sort | head -200

echo
echo "## .dockerignore contents (if any)"
while IFS= read -r f; do
  echo "--- $f"
  cat -n "$f" | sed -n '1,200p'
done < <(fd '.dockerignore$' .)

echo
echo "## repo .git directory status"
python3 - <<'PY'
from pathlib import Path
p = Path('.git')
print('exists=', p.exists() or p.is_symlink(), 'is_dir=', p.is_dir(), 'is_symlink=', p.is_symlink())
if p.exists():
    print('contents=', sorted(str(x) for x in p.iterdir() if x.name not in {'config.example'})[:50])
PY

echo
echo "## checks for persist-credentials and build context usage"
rg -n "persist-credentials|docker/build-push-action|context:|\.dockerignore" .github/workflows/docker-ghcr-custom.yml .github/workflows || true

Repository: QuantumNous/new-api

Length of output: 3850


🌐 Web query:

actions/checkout v4 default persist-credentials true GITHUB_TOKEN .git/config

💡 Result:

In actions/checkout, the persist-credentials input controls whether the action configures the Git credential (such as the GITHUB_TOKEN or an SSH key) in the local Git configuration to enable authenticated Git operations [1][2]. As of version 6.0.0 and later, the behavior has been improved for security [3][4]: 1. Default Behavior: The input defaults to true, meaning the action automatically configures credentials [2][3]. 2. Storage Mechanism (v6.0.0+): Instead of writing credentials directly into the repository's .git/config file (which was the behavior in versions v5 and earlier), actions/checkout now stores them in a separate file located under $RUNNER_TEMP [3][4][5]. It uses Git's includeIf.gitdir mechanism to conditionally apply these credentials to the repository [4][5]. 3. Cleanup: The action includes a post-job step that removes the credential file to ensure it does not persist after the job finishes [1][3]. 4. Opt-Out: Users can set persist-credentials: false in their workflow step if they do not want the action to configure these Git credentials [1][6]. If you are using an older version (v5 or earlier), the credentials are written directly into the local .git/config [6][3]. In all versions, if persist-credentials is enabled, the token is available for the duration of the job to any process that can read the Git configuration [7].

Citations:


🌐 Web query:

Docker dockerignore .git excluded from build context documentation

💡 Result:

The.git directory is not automatically excluded from the build context by the.dockerignore file, though it is handled differently depending on how the build context is provided [1][2][3]. When building from a local directory, the.dockerignore file does not exclude the.git directory by default [2][4]. If you wish to exclude.git from your build context, you must explicitly add it to your.dockerignore file [3]. Using the pattern /.git ensures that all.git directories within your project, including those in subdirectories, are excluded from the build context [3]. When building directly from a remote Git repository URL, Docker BuildKit automatically excludes the.git directory by default [1]. You can override this behavior if necessary by using the --keep-git-dir=true flag [1]. In summary: - Local builds: You must explicitly add /.git to your.dockerignore file if you want to exclude it [3]. - Remote Git repository builds: The.git directory is excluded by default [1]. It is a common best practice to include.git in your.dockerignore file to reduce the size of the build context and prevent sensitive version control metadata from being included in your image [2][4].

Citations:


Prevent actions/checkout credentials from entering the Docker build

actions/checkout@v4 persists the job credential by default, and this workflow builds with context: . without a .dockerignore that excludes .git. Set persist-credentials: false if the job does not need authenticated Git operations, then add /.git to .dockerignore to avoid leaking the checkout context/token into the image.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 61-64: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker-ghcr-custom.yml around lines 61 - 64, Update the
“Check out” actions/checkout@v4 step to set persist-credentials to false, and
add /.git to the repository’s .dockerignore so the Docker build context excludes
Git metadata and checkout credentials.

Source: Linters/SAST tools


- name: Write VERSION
run: |
TAG="${{ needs.prepare.outputs.tag }}"
echo "TAG=${TAG}" >> "$GITHUB_ENV"
echo "${TAG}" > VERSION
echo "Building tag: ${TAG} for ${{ matrix.arch }}"
echo "Image: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.image_name }}"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ needs.prepare.outputs.image_name }}

- name: Build & push
id: build
uses: docker/build-push-action@v6
with:
context: .
platforms: ${{ matrix.platform }}
push: true
tags: |
${{ env.REGISTRY }}/${{ needs.prepare.outputs.image_name }}:${{ env.TAG }}-${{ matrix.arch }}
${{ env.REGISTRY }}/${{ needs.prepare.outputs.image_name }}:latest-${{ matrix.arch }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Image summary
run: |
echo "### Docker Image (${{ matrix.arch }})" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
echo "${{ env.REGISTRY }}/${{ needs.prepare.outputs.image_name }}:${TAG}-${{ matrix.arch }}" >> "$GITHUB_STEP_SUMMARY"
echo "${{ steps.build.outputs.digest }}" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"

create_manifests:
name: Create multi-arch manifests
needs: [prepare, build_single_arch]
runs-on: ubuntu-latest

permissions:
contents: read
packages: write

steps:
- name: Set version
run: |
echo "TAG=${{ needs.prepare.outputs.tag }}" >> "$GITHUB_ENV"
echo "IMAGE_NAME=${{ needs.prepare.outputs.image_name }}" >> "$GITHUB_ENV"

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create & push manifest (version)
run: |
docker buildx imagetools create \
-t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}" \
"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}-amd64" \
"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}-arm64"

- name: Create & push manifest (latest)
run: |
docker buildx imagetools create \
-t "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" \
"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-amd64" \
"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-arm64"

- name: Manifest summary
run: |
echo "### Multi-arch Manifest" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}" >> "$GITHUB_STEP_SUMMARY"
echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> "$GITHUB_STEP_SUMMARY"
docker buildx imagetools inspect "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${TAG}" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ skills-lock.json
.local-tests/
service/relayconvert/chat_responses_live_local_test.go
service/openaicompat/chat_responses_live_local_test.go
.superpowers
docs
87 changes: 87 additions & 0 deletions controller/extensions_availability.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package controller

import (
"net/http"
"sort"

"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/setting/console_setting"
"github.com/QuantumNous/new-api/setting/ratio_setting"

"github.com/gin-gonic/gin"
)

type extensionsAvailabilityGroup struct {
Group string `json:"group"`
Records []model.GroupAvailabilityRecord `json:"records"`
SuccessRate float64 `json:"success_rate"`
AvgUseTime float64 `json:"avg_use_time"`
Status string `json:"status"`
Total int `json:"total"`
SuccessCount int `json:"success_count"`
}

func GetExtensionsAvailability(c *gin.Context) {
isAdmin := c.GetInt("role") >= common.RoleAdminUser
if !console_setting.IsAvailabilityMonitorVisible(isAdmin) {
c.JSON(http.StatusForbidden, gin.H{
"success": false,
"message": "availability monitor is not available",
})
return
}

userId := c.GetInt("id")
userGroup, _ := model.GetUserGroup(userId, false)
userUsableGroups := service.GetUserUsableGroups(userGroup)

groupNames := make([]string, 0)
for groupName := range ratio_setting.GetGroupRatioCopy() {
// Match GetUserGroups: only billing groups the user can select (skip "auto").
if groupName == "auto" {
continue
}
if _, ok := userUsableGroups[groupName]; !ok {
continue
}
groupNames = append(groupNames, groupName)
}
sort.Strings(groupNames)

groups := make([]extensionsAvailabilityGroup, 0, len(groupNames))
for _, groupName := range groupNames {
records, err := model.GetRecentGroupAvailabilityLogs(groupName, 100)
if err != nil {
common.ApiError(c, err)
return
}
okCount := 0
successUseTimeSum := 0
for _, record := range records {
if record.Ok {
okCount++
successUseTimeSum += record.UseTime
}
}
successRate, avgUseTime, status := console_setting.SummarizeAvailabilityRecords(
okCount,
len(records),
successUseTimeSum,
)
groups = append(groups, extensionsAvailabilityGroup{
Group: groupName,
Records: records,
SuccessRate: successRate,
AvgUseTime: avgUseTime,
Status: status,
Total: len(records),
SuccessCount: okCount,
})
}
Comment on lines +53 to +82

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Per-request N+1 DB query pattern with no caching or rate limiting.

Each call fans out into one GetRecentGroupAvailabilityLogs query per billing group. The frontend polls this endpoint as often as every 5s (MIN_REFRESH_SECONDS), and the route has no rate-limit middleware (apiRouter.GET("/extensions/availability", middleware.UserAuth(), ...) — no CriticalRateLimit()/similar). With many groups and concurrent viewers, this multiplies DB load significantly and has no backpressure.

Since per-group log data is identical across users (only the exposed group set differs by permission), a short-TTL cache (e.g., keyed by group name, refreshed every few seconds) shared across requests would eliminate most of the duplicate work without touching per-user filtering logic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controller/extensions_availability.go` around lines 53 - 82, The extensions
availability handler currently issues one database query per group on every
request; add a short-TTL shared cache around GetRecentGroupAvailabilityLogs
keyed by group name, reusing cached records within the TTL and refreshing
expired or missing entries. Preserve the existing per-request group iteration,
permission-based group set, error handling, and availability summary
calculations in the handler.


common.ApiSuccess(c, gin.H{
"groups": groups,
})
}
83 changes: 83 additions & 0 deletions controller/lottery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package controller

import (
"fmt"
"net/http"

"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/logger"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/setting/operation_setting"
"github.com/gin-gonic/gin"
)

// GetLotteryStatus 获取抽奖状态
func GetLotteryStatus(c *gin.Context) {
if !operation_setting.IsLotteryEnabled() {
common.ApiErrorMsg(c, "抽奖功能未启用")
return
}
userId := c.GetInt("id")
data, err := model.GetUserLotteryState(userId)
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": data,
})
}

type lotteryDrawRequest struct {
BetUSD float64 `json:"bet_usd"`
}

// DoLottery 执行抽奖
func DoLottery(c *gin.Context) {
if !operation_setting.IsLotteryEnabled() {
common.ApiErrorMsg(c, "抽奖功能未启用")
return
}

var req lotteryDrawRequest
if err := c.ShouldBindJSON(&req); err != nil {
req.BetUSD = 0
}

userId := c.GetInt("id")
result, err := model.UserLotteryDraw(userId, req.BetUSD, c.ClientIP())
if err != nil {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": err.Error(),
})
return
}

usdDelta := operation_setting.QuotaToUsd(result.Draw.QuotaDelta)
msg := fmt.Sprintf("老虎机抽奖:%s,额度变化 %s(约 $%.4f)", result.Draw.PrizeName, logger.LogQuota(result.Draw.QuotaDelta), usdDelta)
model.RecordLog(userId, model.LogTypeSystem, msg)

c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "抽奖成功",
"data": gin.H{
"prize_index": result.Draw.PrizeIndex,
"prize_name": result.Draw.PrizeName,
"quota_delta": result.Draw.QuotaDelta,
"usd_delta": usdDelta,
"bet_quota": result.Draw.BetQuota,
"bet_usd": operation_setting.QuotaToUsd(result.Draw.BetQuota),
"is_thanks": result.Draw.IsThanks,
"is_pity": result.Draw.IsPity,
"is_thursday": result.Draw.IsThursday,
"remaining_pool": result.RemainingPool,
"remaining_pool_usd": operation_setting.QuotaToUsd(result.RemainingPool),
"draw_date": result.Draw.DrawDate,
},
})
}
29 changes: 29 additions & 0 deletions controller/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/QuantumNous/new-api/setting/operation_setting"
"github.com/QuantumNous/new-api/setting/system_setting"

"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)

Expand Down Expand Up @@ -122,6 +123,7 @@ func GetStatus(c *gin.Context) {
"user_agreement_enabled": legalSetting.UserAgreement != "",
"privacy_policy_enabled": legalSetting.PrivacyPolicy != "",
"checkin_enabled": operation_setting.GetCheckinSetting().Enabled,
"lottery_enabled": operation_setting.IsLotteryEnabled(),
}

// 根据启用状态注入可选内容
Expand All @@ -135,6 +137,16 @@ func GetStatus(c *gin.Context) {
data["faq"] = console_setting.GetFAQ()
}

isLoggedIn, isAdmin := statusViewerRole(c)
if isLoggedIn {
data["custom_pages"] = console_setting.GetCustomPagesForRole(isAdmin)
data["availability_monitor_visible"] = console_setting.IsAvailabilityMonitorVisible(isAdmin)
data["availability_monitor_refresh_interval"] = console_setting.GetAvailabilityMonitorRefreshInterval()
} else {
data["custom_pages"] = []map[string]interface{}{}
data["availability_monitor_visible"] = false
}

// Add enabled custom OAuth providers
customProviders := oauth.GetEnabledCustomProviders()
if len(customProviders) > 0 {
Expand Down Expand Up @@ -171,6 +183,23 @@ func GetStatus(c *gin.Context) {
return
}

func statusViewerRole(c *gin.Context) (isLoggedIn bool, isAdmin bool) {
session := sessions.Default(c)
if session.Get("id") == nil {
return false, false
}
role := 0
switch v := session.Get("role").(type) {
case int:
role = v
case int64:
role = int(v)
case float64:
role = int(v)
}
return true, role >= common.RoleAdminUser
}

func GetNotice(c *gin.Context) {
common.OptionMapRWMutex.RLock()
defer common.OptionMapRWMutex.RUnlock()
Expand Down
Loading
Loading