Phase 6: Permissions, Push, WebAuthn scaffolding
Backend: - internal/permissions/permissions.go: permission bitflags (VIEW_CHANNEL through SHARE_SCREEN) - internal/permissions/checker.go: permission checker with role aggregation - internal/middleware/permission.go: RequirePermission middleware - internal/push/handlers.go: push subscription CRUD, VAPID key endpoint, SendPush - internal/auth/webauthn.go: WebAuthn passkey scaffolding (begin/finish endpoints) - internal/db/db.go: is_default on roles, push_subscriptions table, webauthn_credentials table - go.mod: added webpush-go, go-webauthn dependencies Frontend: - stores/role.ts: Zustand store for role management - RoleManager.tsx: role CRUD with permission checkboxes - MemberRoleAssign.tsx: assign roles to members - App.tsx: /servers/:serverId/roles route
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import { create } from 'zustand';
|
||||
import { api } from '../lib/api.ts';
|
||||
|
||||
export interface Role {
|
||||
id: string;
|
||||
server_id: string;
|
||||
name: string;
|
||||
color: string;
|
||||
permissions: number;
|
||||
position: number;
|
||||
is_default: boolean;
|
||||
}
|
||||
|
||||
export interface CreateRoleData {
|
||||
name: string;
|
||||
color: string;
|
||||
permissions: number;
|
||||
position: number;
|
||||
}
|
||||
|
||||
export interface UpdateRoleData {
|
||||
name?: string;
|
||||
color?: string;
|
||||
permissions?: number;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
interface RoleState {
|
||||
roles: Role[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
|
||||
fetchRoles: (serverId: string) => Promise<void>;
|
||||
createRole: (serverId: string, data: CreateRoleData) => Promise<Role>;
|
||||
updateRole: (serverId: string, roleId: string, data: UpdateRoleData) => Promise<Role>;
|
||||
deleteRole: (serverId: string, roleId: string) => Promise<void>;
|
||||
setMemberRoles: (serverId: string, userId: string, roleIds: string[]) => Promise<void>;
|
||||
getMemberRoles: (serverId: string, userId: string) => Promise<Role[]>;
|
||||
}
|
||||
|
||||
export const useRoleStore = create<RoleState>((set) => ({
|
||||
roles: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
|
||||
fetchRoles: async (serverId) => {
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
const roles = await api.get<Role[]>(`/servers/${serverId}/roles`);
|
||||
set({ roles: roles.sort((a, b) => b.position - a.position), loading: false });
|
||||
} catch (error) {
|
||||
set({
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to fetch roles',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
createRole: async (serverId, data) => {
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
const role = await api.post<Role>(`/servers/${serverId}/roles`, data);
|
||||
set((state) => ({
|
||||
roles: [...state.roles, role].sort((a, b) => b.position - a.position),
|
||||
loading: false,
|
||||
}));
|
||||
return role;
|
||||
} catch (error) {
|
||||
set({
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to create role',
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
updateRole: async (serverId, roleId, data) => {
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
const role = await api.patch<Role>(`/servers/${serverId}/roles/${roleId}`, data);
|
||||
set((state) => ({
|
||||
roles: state.roles.map((r) => (r.id === roleId ? role : r)).sort((a, b) => b.position - a.position),
|
||||
loading: false,
|
||||
}));
|
||||
return role;
|
||||
} catch (error) {
|
||||
set({
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to update role',
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
deleteRole: async (serverId, roleId) => {
|
||||
set({ loading: true, error: null });
|
||||
try {
|
||||
await api.delete(`/servers/${serverId}/roles/${roleId}`);
|
||||
set((state) => ({
|
||||
roles: state.roles.filter((r) => r.id !== roleId),
|
||||
loading: false,
|
||||
}));
|
||||
} catch (error) {
|
||||
set({
|
||||
loading: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to delete role',
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
setMemberRoles: async (serverId, userId, roleIds) => {
|
||||
set({ error: null });
|
||||
try {
|
||||
await api.put(`/servers/${serverId}/members/${userId}/roles`, { role_ids: roleIds });
|
||||
} catch (error) {
|
||||
set({
|
||||
error: error instanceof Error ? error.message : 'Failed to set member roles',
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
getMemberRoles: async (serverId, userId) => {
|
||||
try {
|
||||
return await api.get<Role[]>(`/servers/${serverId}/members/${userId}/roles`);
|
||||
} catch (error) {
|
||||
set({
|
||||
error: error instanceof Error ? error.message : 'Failed to get member roles',
|
||||
});
|
||||
return [];
|
||||
}
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user