实现权限列表与权限渲染组件
This commit is contained in:
3
bun.lock
3
bun.lock
@@ -18,6 +18,7 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"jotai": "^2.19.0",
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"next": "^16.0.10",
|
"next": "^16.0.10",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
@@ -359,6 +360,8 @@
|
|||||||
|
|
||||||
"jiti": ["jiti@2.6.1", "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
"jiti": ["jiti@2.6.1", "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||||
|
|
||||||
|
"jotai": ["jotai@2.19.0", "https://registry.npmmirror.com/jotai/-/jotai-2.19.0.tgz", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-r2wwxEXP1F2JteDLZEOPoIpAHhV89paKsN5GWVYndPNMMP/uVZDcC+fNj0A8NjKgaPWzdyO8Vp8YcYKe0uCEqQ=="],
|
||||||
|
|
||||||
"lightningcss": ["lightningcss@1.30.2", "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.30.2.tgz", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
|
"lightningcss": ["lightningcss@1.30.2", "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.30.2.tgz", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
|
||||||
|
|
||||||
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
|
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"jotai": "^2.19.0",
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"next": "^16.0.10",
|
"next": "^16.0.10",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export async function login(params: {
|
|||||||
username: string
|
username: string
|
||||||
password: string
|
password: string
|
||||||
remember: boolean
|
remember: boolean
|
||||||
}): Promise<ApiResponse> {
|
}): Promise<ApiResponse<string[]>> {
|
||||||
const resp = await callByDevice<TokenResp>("/api/auth/token", {
|
const resp = await callByDevice<TokenResp>("/api/auth/token", {
|
||||||
grant_type: "password",
|
grant_type: "password",
|
||||||
login_type: "password",
|
login_type: "password",
|
||||||
@@ -43,7 +43,7 @@ export async function login(params: {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: undefined,
|
data: data.scope?.split(" ") || [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { useSetAtom } from "jotai"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { Controller, useForm } from "react-hook-form"
|
import { Controller, useForm } from "react-hook-form"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
@@ -16,6 +17,7 @@ import {
|
|||||||
FieldLegend,
|
FieldLegend,
|
||||||
} from "@/components/ui/field"
|
} from "@/components/ui/field"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
|
import { scopesAtom } from "@/lib/stores/scopes"
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
username: z.string().min(4).max(50),
|
username: z.string().min(4).max(50),
|
||||||
@@ -36,7 +38,7 @@ export default function LoginPage() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const setScopes = useSetAtom(scopesAtom)
|
||||||
const onSubmit = async (data: Schema) => {
|
const onSubmit = async (data: Schema) => {
|
||||||
try {
|
try {
|
||||||
const resp = await login(data)
|
const resp = await login(data)
|
||||||
@@ -45,6 +47,8 @@ export default function LoginPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 登录成功后跳转到首页
|
// 登录成功后跳转到首页
|
||||||
|
console.log("用户权限列表", resp.data)
|
||||||
|
setScopes(resp.data)
|
||||||
router.push("/")
|
router.push("/")
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
toast.error("登录失败", {
|
toast.error("登录失败", {
|
||||||
|
|||||||
13
src/components/auth/index.ts
Normal file
13
src/components/auth/index.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { useAtomValue } from "jotai"
|
||||||
|
import type { ReactNode } from "react"
|
||||||
|
import { scopesAtom } from "@/lib/stores/scopes"
|
||||||
|
|
||||||
|
export function Auth(props: { scope: string; children: ReactNode }) {
|
||||||
|
const scopes = useAtomValue(scopesAtom)
|
||||||
|
if (!scopes.length) return props.children
|
||||||
|
|
||||||
|
const hasScope = scopes.some(s => props.scope.startsWith(s))
|
||||||
|
if (!hasScope) return null
|
||||||
|
|
||||||
|
return props.children
|
||||||
|
}
|
||||||
65
src/lib/scopes.ts
Normal file
65
src/lib/scopes.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// 权限
|
||||||
|
export const ScopePermission = "permission";
|
||||||
|
export const ScopePermissionRead = "permission:read"; // 读取权限列表
|
||||||
|
export const ScopePermissionWrite = "permission:write"; // 写入权限
|
||||||
|
|
||||||
|
// 管理员角色
|
||||||
|
export const ScopeAdminRole = "admin_role";
|
||||||
|
export const ScopeAdminRoleRead = "admin_role:read"; // 读取管理员角色列表
|
||||||
|
export const ScopeAdminRoleWrite = "admin_role:write"; // 写入管理员角色
|
||||||
|
|
||||||
|
// 管理员
|
||||||
|
export const ScopeAdmin = "admin";
|
||||||
|
export const ScopeAdminRead = "admin:read"; // 读取管理员列表
|
||||||
|
export const ScopeAdminWrite = "admin:write"; // 写入管理员
|
||||||
|
|
||||||
|
// 产品
|
||||||
|
export const ScopeProduct = "product";
|
||||||
|
export const ScopeProductRead = "product:read"; // 读取产品列表
|
||||||
|
export const ScopeProductWrite = "product:write"; // 写入产品
|
||||||
|
|
||||||
|
// 产品套餐
|
||||||
|
export const ScopeProductSku = "product_sku";
|
||||||
|
export const ScopeProductSkuRead = "product_sku:read"; // 读取产品套餐列表
|
||||||
|
export const ScopeProductSkuWrite = "product_sku:write"; // 写入产品套餐
|
||||||
|
|
||||||
|
// 折扣
|
||||||
|
export const ScopeDiscount = "discount";
|
||||||
|
export const ScopeDiscountRead = "discount:read"; // 读取折扣列表
|
||||||
|
export const ScopeDiscountWrite = "discount:write"; // 写入折扣
|
||||||
|
|
||||||
|
// 用户套餐
|
||||||
|
export const ScopeResource = "resource";
|
||||||
|
export const ScopeResourceRead = "resource:read"; // 读取用户套餐列表
|
||||||
|
export const ScopeResourceWrite = "resource:write"; // 写入用户套餐
|
||||||
|
|
||||||
|
// 用户
|
||||||
|
export const ScopeUser = "user";
|
||||||
|
export const ScopeUserRead = "user:read"; // 读取用户列表
|
||||||
|
export const ScopeUserWrite = "user:write"; // 写入用户
|
||||||
|
export const ScopeUserWriteBalance = "user:write:balance"; // 写入用户余额
|
||||||
|
|
||||||
|
// 优惠券
|
||||||
|
export const ScopeCoupon = "coupon";
|
||||||
|
export const ScopeCouponRead = "coupon:read"; // 读取优惠券列表
|
||||||
|
export const ScopeCouponWrite = "coupon:write"; // 写入优惠券
|
||||||
|
|
||||||
|
// 批次
|
||||||
|
export const ScopeBatch = "batch";
|
||||||
|
export const ScopeBatchRead = "batch:read"; // 读取批次列表
|
||||||
|
export const ScopeBatchWrite = "batch:write"; // 写入批次
|
||||||
|
|
||||||
|
// IP
|
||||||
|
export const ScopeChannel = "channel";
|
||||||
|
export const ScopeChannelRead = "channel:read"; // 读取 IP 列表
|
||||||
|
export const ScopeChannelWrite = "channel:write"; // 写入 IP
|
||||||
|
|
||||||
|
// 交易
|
||||||
|
export const ScopeTrade = "trade";
|
||||||
|
export const ScopeTradeRead = "trade:read"; // 读取交易列表
|
||||||
|
export const ScopeTradeWrite = "trade:write"; // 写入交易
|
||||||
|
|
||||||
|
// 账单
|
||||||
|
export const ScopeBill = "bill";
|
||||||
|
export const ScopeBillRead = "bill:read"; // 读取账单列表
|
||||||
|
export const ScopeBillWrite = "bill:write"; // 写入账单
|
||||||
5
src/lib/stores/scopes.ts
Normal file
5
src/lib/stores/scopes.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { atom } from "jotai"
|
||||||
|
|
||||||
|
const scopesAtom = atom<string[]>([])
|
||||||
|
|
||||||
|
export { scopesAtom }
|
||||||
Reference in New Issue
Block a user