Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4d45fac
customize frontend UI overrides
Micah-Zheng Apr 29, 2026
7891a0d
Merge remote-tracking branch 'origin/main' into codex/custom-ui
Micah-Zheng Apr 29, 2026
830fddf
fix: respect auto group default in API key form
Micah-Zheng Apr 29, 2026
6d35ff6
customize API key group selection rules
Micah-Zheng Apr 29, 2026
584a1a8
customize API key group validation highlight
Micah-Zheng Apr 29, 2026
d89f78e
customize navigation query parameters
Micah-Zheng Apr 29, 2026
63a1db7
embed status monitor and model square in console
Micah-Zheng Apr 30, 2026
f2dac94
fix embedded pricing route refresh
Micah-Zheng Apr 30, 2026
3fd36a2
grant admins root-level private access
Micah-Zheng Apr 30, 2026
bc88dfa
customize console logo and hide version
Micah-Zheng Apr 30, 2026
2c468a6
use cache-busted custom logo assets
Micah-Zheng Apr 30, 2026
27746b0
document private deployment workflow
Micah-Zheng Apr 30, 2026
91bd75c
update SOP for collaborator workflow
Micah-Zheng Apr 30, 2026
4bc32f3
clarify collaborator merge and deploy rules
Micah-Zheng Apr 30, 2026
9606f58
add api request url card to keys page
jkjk02 May 1, 2026
c19561f
localize api request url card labels
jkjk02 May 1, 2026
f0f03a0
Merge pull request #1 from Micah-Zheng/teammate/add-api-request-url
jkjk02 May 1, 2026
a70c455
add uptime card to dashboard overview
jkjk02 May 1, 2026
213f58a
Merge pull request #3 from Micah-Zheng/teammate/quota-runtime-faq-fixes
jkjk02 May 1, 2026
33c690d
remove v1 suffix and openai badge from api url card
jkjk02 May 1, 2026
18c98bc
enhance uptime card styling on dashboard
jkjk02 May 1, 2026
5a7754b
move enhanced uptime card into uptime panel
jkjk02 May 1, 2026
d669e85
hide empty uptime text when runtime card is shown
jkjk02 May 1, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ data/
.test
token_estimator_test.go
skills-lock.json
.omx/
.playwright-mcp/
web/default/.omc/
11 changes: 11 additions & 0 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,17 @@ func IsValidateRole(role int) bool {
return role == RoleGuestUser || role == RoleCommonUser || role == RoleAdminUser || role == RoleRootUser
}

func HasRootPermission(role int) bool {
return role >= RoleAdminUser
}
Comment on lines +175 to +177

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.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Prefer explicit privileged-role checks over numeric threshold comparison.

Line 176 grants root-capable access to any future role value above admin. An explicit role allow-list avoids accidental privilege expansion.

💡 Suggested fix
 func HasRootPermission(role int) bool {
-	return role >= RoleAdminUser
+	return role == RoleAdminUser || role == RoleRootUser
 }
📝 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
func HasRootPermission(role int) bool {
return role >= RoleAdminUser
}
func HasRootPermission(role int) bool {
return role == RoleAdminUser || role == RoleRootUser
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/constants.go` around lines 175 - 177, The HasRootPermission function
currently uses a numeric threshold (role >= RoleAdminUser) which can
accidentally grant new future roles root access; update HasRootPermission to
check explicitly against an allow-list of privileged role constants (for example
compare role == RoleAdminUser or role == RoleRoot or use a switch/map of allowed
roles) so only explicitly named constants are treated as root-capable; modify
the function body to perform explicit equality checks (or a lookup in a set)
against those role identifiers instead of a >= comparison.


func EffectiveRole(role int) int {
if HasRootPermission(role) {
return RoleRootUser
}
return role
}

var (
FileUploadPermission = RoleGuestUser
FileDownloadPermission = RoleGuestUser
Expand Down
4 changes: 2 additions & 2 deletions controller/custom_oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ func GetUserOAuthBindingsByAdmin(c *gin.Context) {
}

myRole := c.GetInt("role")
if myRole <= targetUser.Role && myRole != common.RoleRootUser {
if myRole <= targetUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorMsg(c, "no permission")
return
}
Expand Down Expand Up @@ -560,7 +560,7 @@ func UnbindCustomOAuthByAdmin(c *gin.Context) {
}

myRole := c.GetInt("role")
if myRole <= targetUser.Role && myRole != common.RoleRootUser {
if myRole <= targetUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorMsg(c, "no permission")
return
}
Expand Down
2 changes: 1 addition & 1 deletion controller/twofa.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ func AdminDisable2FA(c *gin.Context) {
}

myRole := c.GetInt("role")
if myRole <= targetUser.Role && myRole != common.RoleRootUser {
if myRole <= targetUser.Role && !common.HasRootPermission(myRole) {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": "无权操作同级或更高级用户的2FA设置",
Expand Down
40 changes: 11 additions & 29 deletions controller/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func GetUser(c *gin.Context) {
return
}
myRole := c.GetInt("role")
if myRole <= user.Role && myRole != common.RoleRootUser {
if myRole <= user.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionSameLevel)
return
}
Expand Down Expand Up @@ -430,18 +430,10 @@ func calculateUserPermissions(userRole int) map[string]interface{} {
permissions := map[string]interface{}{}

// 根据用户角色计算权限
if userRole == common.RoleRootUser {
if common.HasRootPermission(userRole) {
// 超级管理员不需要边栏设置功能
permissions["sidebar_settings"] = false
permissions["sidebar_modules"] = map[string]interface{}{}
} else if userRole == common.RoleAdminUser {
// 管理员可以设置边栏,但不包含系统设置功能
permissions["sidebar_settings"] = true
permissions["sidebar_modules"] = map[string]interface{}{
"admin": map[string]interface{}{
"setting": false, // 管理员不能访问系统设置
},
}
} else {
// 普通用户只能设置个人功能,不包含管理员区域
permissions["sidebar_settings"] = true
Expand Down Expand Up @@ -482,18 +474,8 @@ func generateDefaultSidebarConfig(userRole int) string {
}

// 管理员区域 - 根据角色决定
if userRole == common.RoleAdminUser {
// 管理员可以访问管理员区域,但不能访问系统设置
defaultConfig["admin"] = map[string]interface{}{
"enabled": true,
"channel": true,
"models": true,
"redemption": true,
"user": true,
"setting": false, // 管理员不能访问系统设置
}
} else if userRole == common.RoleRootUser {
// 超级管理员可以访问所有功能
if common.HasRootPermission(userRole) {
// 管理员和超级管理员都可以访问所有管理功能
defaultConfig["admin"] = map[string]interface{}{
"enabled": true,
"channel": true,
Expand Down Expand Up @@ -562,11 +544,11 @@ func UpdateUser(c *gin.Context) {
return
}
myRole := c.GetInt("role")
if myRole <= originUser.Role && myRole != common.RoleRootUser {
if myRole <= originUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionHigherLevel)
return
}
if myRole <= updatedUser.Role && myRole != common.RoleRootUser {
if myRole <= updatedUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserCannotCreateHigherLevel)
Comment on lines +547 to 552

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.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Keep root-account mutations root-only.

These guards now reuse HasRootPermission, but these endpoints do more than grant private/admin access: they can assign user.Role directly and hard-delete privileged accounts. If HasRootPermission includes admins, an admin can create/update a root user here and delete the current root account, bypassing the stricter promote flow that still tops out at RoleAdminUser.

Suggested tightening
- if myRole <= originUser.Role && !common.HasRootPermission(myRole) {
+ if myRole <= originUser.Role && myRole != common.RoleRootUser {
    common.ApiErrorI18n(c, i18n.MsgUserNoPermissionHigherLevel)
    return
  }

- if myRole <= updatedUser.Role && !common.HasRootPermission(myRole) {
+ if myRole <= updatedUser.Role && myRole != common.RoleRootUser {
    common.ApiErrorI18n(c, i18n.MsgUserCannotCreateHigherLevel)
    return
  }

- if user.Role >= myRole && !common.HasRootPermission(myRole) {
+ if user.Role >= myRole && myRole != common.RoleRootUser {
    common.ApiErrorI18n(c, i18n.MsgUserCannotCreateHigherLevel)
    return
  }

Also applies to: 752-753, 803-805

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controller/user.go` around lines 547 - 552, The guards currently allow anyone
with common.HasRootPermission (which may include admins) to assign or delete
root-level accounts; tighten them to root-only by replacing checks like "if
myRole <= originUser.Role && !common.HasRootPermission(myRole)" and the similar
updatedUser check with a strict root-only check (e.g., "if myRole <=
originUser.Role && myRole != common.RoleRoot" or use a dedicated helper like
common.IsRoot(myRole) that returns true only for the root role), and apply the
same change at the other occurrences around the file (the blocks you noted at
lines ~752-753 and ~803-805) so only the root account can be
created/updated/hard-deleted.

return
}
Expand Down Expand Up @@ -605,7 +587,7 @@ func AdminClearUserBinding(c *gin.Context) {
}

myRole := c.GetInt("role")
if myRole <= user.Role && myRole != common.RoleRootUser {
if myRole <= user.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionSameLevel)
return
}
Expand Down Expand Up @@ -767,7 +749,7 @@ func DeleteUser(c *gin.Context) {
return
}
myRole := c.GetInt("role")
if myRole <= originUser.Role {
if myRole <= originUser.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionHigherLevel)
return
}
Expand Down Expand Up @@ -818,7 +800,7 @@ func CreateUser(c *gin.Context) {
user.DisplayName = user.Username
}
myRole := c.GetInt("role")
if user.Role >= myRole {
if user.Role >= myRole && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserCannotCreateHigherLevel)
return
}
Expand Down Expand Up @@ -867,7 +849,7 @@ func ManageUser(c *gin.Context) {
return
}
myRole := c.GetInt("role")
if myRole <= user.Role && myRole != common.RoleRootUser {
if myRole <= user.Role && !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserNoPermissionHigherLevel)
return
}
Expand Down Expand Up @@ -898,7 +880,7 @@ func ManageUser(c *gin.Context) {
common.SysLog(fmt.Sprintf("failed to invalidate tokens cache for user %d: %s", user.Id, err.Error()))
}
case "promote":
if myRole != common.RoleRootUser {
if !common.HasRootPermission(myRole) {
common.ApiErrorI18n(c, i18n.MsgUserAdminCannotPromote)
return
}
Expand Down
Loading