feat(server): enhance user permissions and access control
Some checks failed
Deploy skills-market-server / deploy (push) Has been cancelled

- Introduced root admin checks in various functions to streamline permission handling for users.
- Updated skill and agent management routes to simplify access control by removing redundant role checks.
- Added favicon links to HTML files for improved branding in the admin and updates sections.
This commit is contained in:
hjjjj 2026-04-01 12:01:56 +08:00
parent f86cace07d
commit 48e999149c
6 changed files with 23 additions and 42 deletions

View File

@ -4,6 +4,9 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>权限管理平台</title>
<link rel="icon" type="image/png" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png" />
<link rel="shortcut icon" type="image/png" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png" />
<link rel="apple-touch-icon" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png" />
<style>
:root {
--bg: #f8fafc;

View File

@ -98,6 +98,7 @@ function sanitizeUserForClient(userDoc) {
}
function canAccessFeature(user, permissionKey) {
if (user?.is_root_admin) return true
const perms = sanitizePermissions(user.permissions)
return !!perms[permissionKey]
}

View File

@ -157,7 +157,12 @@ function normalizeTagList(tags) {
return out
}
const ROOT_MODE_TAGS = ['clarify', 'cowork', 'create', 'video', 'code']
function getAllowedModeTagsFromUser(user) {
if (user?.is_root_admin) {
return normalizeTagList(ROOT_MODE_TAGS)
}
const modes = Array.isArray(user?.permissions?.allowedModes) ? user.permissions.allowedModes : []
return normalizeTagList(modes)
}
@ -167,24 +172,12 @@ function buildModeIntersectionFilter(user, requestedTags = []) {
const requested = normalizeTagList(requestedTags)
const isRootAdmin = !!user?.is_root_admin
const isManagementList = requested.length === 0
if (isRootAdmin && isManagementList) return {}
const effective =
requested.length > 0
? requested.filter((tag) => allowedTags.includes(tag))
: allowedTags
if (isRootAdmin && isManagementList) {
if (effective.length === 0) {
return { $or: [{ tags: { $exists: false } }, { tags: { $size: 0 } }] }
}
return {
$or: [
{ tags: { $in: effective } },
{ tags: { $exists: false } },
{ tags: { $size: 0 } }
]
}
}
if (effective.length === 0) {
return { _id: { $exists: false } }
}
@ -192,10 +185,10 @@ function buildModeIntersectionFilter(user, requestedTags = []) {
}
function canUserAccessTaggedResource(user, resourceTags) {
if (user?.is_root_admin) return true
const allowedTags = getAllowedModeTagsFromUser(user)
const tags = normalizeTagList(resourceTags)
const isRootAdmin = !!user?.is_root_admin
if (tags.length === 0) return isRootAdmin
if (tags.length === 0) return false
if (allowedTags.length === 0) return false
return tags.some((tag) => allowedTags.includes(tag))
}
@ -495,7 +488,7 @@ app.get('/api/skills/:name/files/*', async (req, res) => {
app.post('/api/skills/:name/lock', async (req, res) => {
authRoutes.verifyToken(req, res, async () => {
try {
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
if (!authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
return res.status(403).json({ success: false, error: '无权编辑技能' })
}
const skill = await skillsCollection.findOne({ name: req.params.name })
@ -520,7 +513,7 @@ app.post('/api/skills/:name/lock', async (req, res) => {
app.delete('/api/skills/:name/lock', async (req, res) => {
authRoutes.verifyToken(req, res, async () => {
try {
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
if (!authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
return res.status(403).json({ success: false, error: '无权编辑技能' })
}
const skill = await skillsCollection.findOne({ name: req.params.name })
@ -590,7 +583,7 @@ app.get('/api/skills/mine', async (req, res, next) => {
app.patch('/api/skills/:name/tags', async (req, res) => {
authRoutes.verifyToken(req, res, async () => {
try {
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
if (!authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
return res.status(403).json({ success: false, error: '无权修改技能标签' })
}
const allowedModeTags = getAllowedModeTagsFromUser(req.user)
@ -636,7 +629,7 @@ app.patch('/api/skills/:name/tags', async (req, res) => {
app.post('/api/skills/:name/publish', async (req, res) => {
authRoutes.verifyToken(req, res, async () => {
try {
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
if (!authRoutes.hasPermission(req.user, 'canViewSkillsPage')) {
return res.status(403).json({ success: false, error: '无权发布或修改技能' })
}
@ -965,7 +958,7 @@ app.get('/api/agents/:name', async (req, res) => {
app.post('/api/agents/:name/publish', async (req, res) => {
authRoutes.verifyToken(req, res, async () => {
try {
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
if (!authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
return res.status(403).json({ success: false, error: '无权发布或修改智能体' })
}
const { content, description, tags, localModifiedAt } = req.body
@ -1045,7 +1038,7 @@ app.post('/api/agents/:name/publish', async (req, res) => {
app.patch('/api/agents/:name/tags', async (req, res) => {
authRoutes.verifyToken(req, res, async () => {
try {
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
if (!authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
return res.status(403).json({ success: false, error: '无权修改智能体标签' })
}
const allowedModeTags = getAllowedModeTagsFromUser(req.user)
@ -1087,7 +1080,7 @@ app.delete('/api/agents/:name', requireAdmin(async (req, res) => {
app.post('/api/agents/:name/lock', async (req, res, next) => {
authRoutes.verifyToken(req, res, async () => {
try {
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
if (!authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
return res.status(403).json({ success: false, error: '无权编辑智能体' })
}
const agent = await agentsCollection.findOne({ name: req.params.name })
@ -1107,7 +1100,7 @@ app.post('/api/agents/:name/lock', async (req, res, next) => {
app.delete('/api/agents/:name/lock', async (req, res, next) => {
authRoutes.verifyToken(req, res, async () => {
try {
if (req.user.role !== 'admin' && !authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
if (!authRoutes.hasPermission(req.user, 'canViewAgentsPage')) {
return res.status(403).json({ success: false, error: '无权编辑智能体' })
}
const agent = await agentsCollection.findOne({ name: req.params.name })

View File

@ -4,6 +4,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LikeCowork 安装指南</title>
<link rel="icon" type="image/png" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png">
<link rel="shortcut icon" type="image/png" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png">
<link rel="apple-touch-icon" href="https://oss.xintiao85.com/images/20260312_110209_2a7c6d79c7469d640f64cb663d6983b1.png">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {

View File

@ -1,11 +0,0 @@
version: 1.2.1
files:
- url: likecowork-1.2.1-x64-mac.zip
sha512: QIOFv41c9MYOPPKaxz4QunYHZJoobVSXhQYXQFgFJUm89Hf3AEC8PtHx3N8e7A99Nun7UuYwO16EaS9YmMJu7w==
size: 978479730
- url: likecowork-1.2.1-arm64-mac.zip
sha512: L6CFKjmxD0Aep4gXkX2ojPuhPnwphHwoEVZXcI45WXmMzQRZfSG3bXY4o+PdZ+nfE7CFYkYbGl9g9yTbc/fPKQ==
size: 973708487
path: likecowork-1.2.1-x64-mac.zip
sha512: QIOFv41c9MYOPPKaxz4QunYHZJoobVSXhQYXQFgFJUm89Hf3AEC8PtHx3N8e7A99Nun7UuYwO16EaS9YmMJu7w==
releaseDate: '2026-03-27T08:06:20.025Z'

View File

@ -1,8 +0,0 @@
version: 1.2.1
files:
- url: likecowork-1.2.1-setup.exe
sha512: c3DLm7mfzV5e94GRDSkB0RWkqvSOJO04TVbMBR3FIoVsiRCTtGn/raRXYfs+xTS9mZ5JlpkYTdM+gtS3aQ3UhQ==
size: 938877301
path: likecowork-1.2.1-setup.exe
sha512: c3DLm7mfzV5e94GRDSkB0RWkqvSOJO04TVbMBR3FIoVsiRCTtGn/raRXYfs+xTS9mZ5JlpkYTdM+gtS3aQ3UhQ==
releaseDate: '2026-03-27T07:51:13.507Z'