add admin permission

This commit is contained in:
thanawat saiyota 2026-03-26 14:57:11 +07:00
parent 3388eca2fe
commit 7ea73543b7
19 changed files with 1567 additions and 5 deletions

View 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';
}

View 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);

View 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'
};

View file

@ -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);