add admin permission
This commit is contained in:
parent
3388eca2fe
commit
7ea73543b7
19 changed files with 1567 additions and 5 deletions
239
src/lib/core/admin/adminService.ts
Normal file
239
src/lib/core/admin/adminService.ts
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
import { doc, getDoc, updateDoc } from 'firebase/firestore';
|
||||
import { db } from '../client/firebase';
|
||||
import type {
|
||||
AdminUser,
|
||||
DocumentPermissions,
|
||||
RolesConfig,
|
||||
ToolsPermissions,
|
||||
UserRole,
|
||||
WhitelistConfig
|
||||
} from './adminTypes';
|
||||
import { AVAILABLE_REGIONS, AVAILABLE_TOOLS } from './adminTypes';
|
||||
|
||||
/**
|
||||
* Get all users from Firestore
|
||||
*/
|
||||
export async function getAllUsers(): Promise<AdminUser[]> {
|
||||
const docRef = doc(db, 'users', 'data');
|
||||
const snapshot = await getDoc(docRef);
|
||||
|
||||
if (!snapshot.exists()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const userData = snapshot.data();
|
||||
const users: AdminUser[] = [];
|
||||
|
||||
for (const uid of Object.keys(userData)) {
|
||||
const user = userData[uid];
|
||||
users.push({
|
||||
uid,
|
||||
email: user.email || '',
|
||||
displayName: user.displayName || '',
|
||||
photoURL: user.photoURL || '',
|
||||
role: user.role || 'guest',
|
||||
permissions: user.permissions || ['no_permission'],
|
||||
lastLoginAt: user.lastLoginAt,
|
||||
createdAt: user.createdAt
|
||||
});
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single user by UID
|
||||
*/
|
||||
export async function getUserByUid(uid: string): Promise<AdminUser | null> {
|
||||
const docRef = doc(db, 'users', 'data');
|
||||
const snapshot = await getDoc(docRef);
|
||||
|
||||
if (!snapshot.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const userData = snapshot.data();
|
||||
if (!userData[uid]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const user = userData[uid];
|
||||
return {
|
||||
uid,
|
||||
email: user.email || '',
|
||||
displayName: user.displayName || '',
|
||||
photoURL: user.photoURL || '',
|
||||
role: user.role || 'guest',
|
||||
permissions: user.permissions || ['no_permission'],
|
||||
lastLoginAt: user.lastLoginAt,
|
||||
createdAt: user.createdAt
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user role
|
||||
*/
|
||||
export async function updateUserRole(uid: string, role: UserRole): Promise<void> {
|
||||
const docRef = doc(db, 'users', 'data');
|
||||
const updateData: Record<string, unknown> = {};
|
||||
updateData[`${uid}.role`] = role;
|
||||
|
||||
await updateDoc(docRef, updateData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user permissions
|
||||
*/
|
||||
export async function updateUserPermissions(uid: string, permissions: string[]): Promise<void> {
|
||||
const docRef = doc(db, 'users', 'data');
|
||||
const updateData: Record<string, unknown> = {};
|
||||
updateData[`${uid}.permissions`] = permissions;
|
||||
|
||||
await updateDoc(docRef, updateData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user role and permissions together
|
||||
*/
|
||||
export async function updateUser(
|
||||
uid: string,
|
||||
role: UserRole,
|
||||
permissions: string[]
|
||||
): Promise<void> {
|
||||
const docRef = doc(db, 'users', 'data');
|
||||
const updateData: Record<string, unknown> = {};
|
||||
updateData[`${uid}.role`] = role;
|
||||
updateData[`${uid}.permissions`] = permissions;
|
||||
|
||||
await updateDoc(docRef, updateData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get role definitions from Firestore
|
||||
*/
|
||||
export async function getRoleDefinitions(): Promise<RolesConfig | null> {
|
||||
const docRef = doc(db, 'roles', 'v1');
|
||||
const snapshot = await getDoc(docRef);
|
||||
|
||||
if (!snapshot.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = snapshot.data();
|
||||
return {
|
||||
definitions: {
|
||||
guest: data.definitions?.guest || { permissions: ['no_permission'] },
|
||||
viewer: data.definitions?.viewer || { permissions: [] },
|
||||
admin: data.definitions?.admin || { permissions: [] }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update role definition
|
||||
*/
|
||||
export async function updateRoleDefinition(role: UserRole, permissions: string[]): Promise<void> {
|
||||
const docRef = doc(db, 'roles', 'v1');
|
||||
const updateData: Record<string, unknown> = {};
|
||||
updateData[`definitions.${role}.permissions`] = permissions;
|
||||
|
||||
await updateDoc(docRef, updateData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get document permissions (regions)
|
||||
*/
|
||||
export async function getDocumentPermissions(): Promise<DocumentPermissions | null> {
|
||||
const docRef = doc(db, 'permissions', 'document');
|
||||
const snapshot = await getDoc(docRef);
|
||||
|
||||
if (!snapshot.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = snapshot.data();
|
||||
return {
|
||||
read: data.read || [],
|
||||
write: data.write || []
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tools permissions
|
||||
*/
|
||||
export async function getToolsPermissions(): Promise<ToolsPermissions | null> {
|
||||
const docRef = doc(db, 'permissions', 'tools');
|
||||
const snapshot = await getDoc(docRef);
|
||||
|
||||
if (!snapshot.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = snapshot.data();
|
||||
return {
|
||||
core: data.core || []
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed domains (whitelist)
|
||||
*/
|
||||
export async function getAllowedDomains(): Promise<string[]> {
|
||||
const docRef = doc(db, 'whitelist', 'allowedDomains');
|
||||
const snapshot = await getDoc(docRef);
|
||||
|
||||
if (!snapshot.exists()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const data = snapshot.data() as WhitelistConfig;
|
||||
return data.account_email || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update allowed domains (whitelist)
|
||||
*/
|
||||
export async function updateAllowedDomains(domains: string[]): Promise<void> {
|
||||
const docRef = doc(db, 'whitelist', 'allowedDomains');
|
||||
await updateDoc(docRef, { account_email: domains });
|
||||
}
|
||||
|
||||
/**
|
||||
* Build all available permissions based on regions and tools
|
||||
* Uses AVAILABLE_REGIONS and AVAILABLE_TOOLS as fallback if Firestore data is empty
|
||||
* no_permission is expected to be in Firebase data already
|
||||
*/
|
||||
export async function buildAvailablePermissions(): Promise<string[]> {
|
||||
const permissions: string[] = [];
|
||||
|
||||
const docPerms = await getDocumentPermissions();
|
||||
|
||||
// Document Read permissions (no_permission should be in Firebase read array)
|
||||
const readRegions = docPerms?.read?.length ? docPerms.read : [...AVAILABLE_REGIONS];
|
||||
for (const region of readRegions) {
|
||||
permissions.push(`document.read.${region}`);
|
||||
}
|
||||
|
||||
// Document Write permissions (no_permission should be in Firebase write array)
|
||||
const writeRegions = docPerms?.write?.length ? docPerms.write : [...AVAILABLE_REGIONS];
|
||||
for (const region of writeRegions) {
|
||||
permissions.push(`document.write.${region}`);
|
||||
}
|
||||
|
||||
// Tools permissions
|
||||
const toolPerms = await getToolsPermissions();
|
||||
const tools = toolPerms?.core?.length ? toolPerms.core : [...AVAILABLE_TOOLS];
|
||||
for (const tool of tools) {
|
||||
permissions.push(`tools.core.${tool}`);
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current user is admin
|
||||
*/
|
||||
export async function isUserAdmin(uid: string): Promise<boolean> {
|
||||
const user = await getUserByUid(uid);
|
||||
return user?.role === 'admin';
|
||||
}
|
||||
25
src/lib/core/admin/adminStore.ts
Normal file
25
src/lib/core/admin/adminStore.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { writable } from 'svelte/store';
|
||||
import type { AdminUser, RolesConfig } from './adminTypes';
|
||||
|
||||
// Users list store
|
||||
export const adminUsers = writable<AdminUser[]>([]);
|
||||
|
||||
// Roles configuration store
|
||||
export const rolesConfig = writable<RolesConfig | null>(null);
|
||||
|
||||
// Available permissions store
|
||||
export const availablePermissions = writable<string[]>([]);
|
||||
|
||||
// Loading states
|
||||
export const adminLoading = writable<boolean>(false);
|
||||
export const usersLoading = writable<boolean>(false);
|
||||
export const rolesLoading = writable<boolean>(false);
|
||||
|
||||
// Error state
|
||||
export const adminError = writable<string | null>(null);
|
||||
|
||||
// Selected user for editing
|
||||
export const selectedUser = writable<AdminUser | null>(null);
|
||||
|
||||
// Edit sheet open state
|
||||
export const editSheetOpen = writable<boolean>(false);
|
||||
72
src/lib/core/admin/adminTypes.ts
Normal file
72
src/lib/core/admin/adminTypes.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
export interface AdminUser {
|
||||
uid: string;
|
||||
email: string;
|
||||
displayName: string;
|
||||
photoURL: string;
|
||||
role: 'guest' | 'viewer' | 'admin';
|
||||
permissions: string[];
|
||||
lastLoginAt?: string;
|
||||
createdAt?: string;
|
||||
}
|
||||
|
||||
export interface RoleDefinition {
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
export interface RolesConfig {
|
||||
definitions: {
|
||||
guest: RoleDefinition;
|
||||
viewer: RoleDefinition;
|
||||
admin: RoleDefinition;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DocumentPermissions {
|
||||
read: string[];
|
||||
write: string[];
|
||||
}
|
||||
|
||||
export interface ToolsPermissions {
|
||||
core: string[];
|
||||
}
|
||||
|
||||
export interface WhitelistConfig {
|
||||
account_email: string[];
|
||||
}
|
||||
|
||||
export type UserRole = 'guest' | 'viewer' | 'admin';
|
||||
|
||||
export const AVAILABLE_REGIONS = [
|
||||
'tha',
|
||||
'mys',
|
||||
'aus',
|
||||
'sgp',
|
||||
'uae_dubai',
|
||||
'hkg',
|
||||
'gbr',
|
||||
'rou',
|
||||
'lva',
|
||||
'est',
|
||||
'ltu'
|
||||
] as const;
|
||||
|
||||
export const REGION_LABELS: Record<string, string> = {
|
||||
tha: 'Thailand',
|
||||
mys: 'Malaysia',
|
||||
aus: 'Australia',
|
||||
sgp: 'Singapore',
|
||||
uae_dubai: 'UAE Dubai',
|
||||
hkg: 'Hong Kong',
|
||||
gbr: 'United Kingdom',
|
||||
rou: 'Romania',
|
||||
lva: 'Latvia',
|
||||
est: 'Estonia',
|
||||
ltu: 'Lithuania'
|
||||
};
|
||||
|
||||
export const AVAILABLE_TOOLS = ['connectMachine', 'allowAdbWebToUsb'] as const;
|
||||
|
||||
export const TOOL_LABELS: Record<string, string> = {
|
||||
connectMachine: 'Connect Machine',
|
||||
allowAdbWebToUsb: 'Allow ADB Web to USB'
|
||||
};
|
||||
|
|
@ -6,4 +6,6 @@ import { writable } from "svelte/store";
|
|||
// email: string,
|
||||
// };
|
||||
|
||||
export const auth = writable<User | null>(null);
|
||||
export const auth = writable<User | null>(null);
|
||||
|
||||
export const authInitialized = writable<boolean>(false);
|
||||
Loading…
Add table
Add a link
Reference in a new issue