HVD2NGYM4J2PKQ72SFMXLBGQPKPCGDRB54IK3ISOQZDWKGM3WGQQC
OSNBT6AANZB3TF7HAJ35N3Z2EGDU5VQ4LGQORKMA25ACMNV35CQQC
UCDTBEK3CF6YT2H6V57HI6FAFW44BIYYAK3Z2QJ5LJE7QWX7OEYAC
LYPSC7BOH6T45FCPRHSCXILAJSJ74D5WSQTUIKPWD5ECXOYGUY5AC
YX7LU4WRAUDMWS3DEDXZDSF6DXBHLYDWVSMSRK6KIW3MO6GRXSVQC
7ESBJZLIH3TAERLH2HIZAAQHVNVHQWLWCOBLMJRFTIL33YITJNIAC
2WKGHT2TVFMQT7VUL5OCYVNE365QOYNRXA34LOZ4DBFJ2ZVB4XQQC
ROQGXQWL2V363K3W7TVVYKIAX4N4IWRERN5BJ7NYJRRVB6OMIJ4QC
J2RLNDEXTGAV4BB6ANIIR7XJLJBHSB4NFQWSBWHNAFB6DMLGS5RAC
M3JUJ2WWZGCVMBITKRM5FUJMHFYL2QRMXJUVRUE4AC2RF74AOL5AC
4M3EBLTLSS2BRCM42ZP7WVD4YMRRLGV2P2XF47IAV5XHHJD52HTQC
KGUU3ZMRY65ZN5G6QC7P7ORPAXXTIMTDBJ37IC6AOKMGQJQ7FVMQC
RLH37YB4D7O42IFM2T7GJG4AVVAURWBZ7AOTHAWR7YJZRG3JOPLQC
EUEH65HBT4XZXCNWECJXNDEQAWR2NLNSAXFPXLMQ27NOVMQBJT5QC
JHFIJJSLVMQNYIDE6CXWUK5UTB7BTUSY7NCI33WKCWIRZHCSMBHQC
4FBIL6IZUDNCXTM6EUHTEOJRHVI4LIIX4BU2IXPXKR362GKIAJMQC
O7W4FZVRKDQDAAXEW4T7P262PPRILRCSSACODMUTQZ6VNR36PVCQC
/**
* Helper function to check if a user has a specific permission for a dataset
*
* @param db - Database connection
* @param userId - User ID to check permissions for
* @param datasetId - Dataset ID to check permissions for
* @param permission - Permission to check (READ, EDIT, DELETE, etc.)
* @returns Promise<boolean> - True if user has permission, false otherwise
*/
async function checkUserPermission(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
db: any,
userId: string,
datasetId: string,
permission: string
): Promise<boolean> {
try {
// First check if user is the owner (owners have all permissions)
const ownerCheck = await db
.select({ owner: dataset.owner })
.from(dataset)
.where(eq(dataset.id, datasetId))
.limit(1);
if (ownerCheck.length > 0 && ownerCheck[0].owner === userId) {
return true;
}
// Get user's role
const userRoleResult = await db
.select({ role: userRole.role })
.from(userRole)
.where(eq(userRole.userId, userId))
.limit(1);
const userRoleName = userRoleResult.length > 0 ? userRoleResult[0].role : 'USER';
// Check access grants
const grants = await db
.select({ permission: accessGrant.permission })
.from(accessGrant)
.where(sqlExpr`
${accessGrant.datasetId} = ${datasetId} AND
${accessGrant.permission} = ${permission} AND
${accessGrant.active} = true AND
(
(${accessGrant.userId} = ${userId}) OR
(${accessGrant.userId} IS NULL AND ${accessGrant.role} = ${userRoleName})
)
`)
.limit(1);
return grants.length > 0;
} catch (error) {
console.error('Error checking user permission:', error);
return false;
}
}
* { "id": "123", "name": "Bird Sounds 2024", "description": "Costa Rica recordings", "public": true, ... }
* {
* "id": "123",
* "name": "Bird Sounds 2024",
* "description": "Costa Rica recordings",
* "public": true,
* "permissions": ["READ", "EDIT", "DELETE"]
* }
// Query datasets that are owned by the authenticated user
// const results = await db.select().from(dataset).where(eq(dataset.owner, userId));
const results = await db.select({
id: dataset.id,
name: dataset.name,
description: dataset.description,
public: dataset.public,
createdAt: dataset.createdAt,
owner: dataset.owner
}).from(dataset)
.where(eq(dataset.active, true))
.limit(20); // Add pagination with 20 datasets limit
// First, get the user's role
const userRoleResult = await db
.select({ role: userRole.role })
.from(userRole)
.where(eq(userRole.userId, userId))
.limit(1);
const userRoleName = userRoleResult.length > 0 ? userRoleResult[0].role : 'USER';
// Query to get datasets with permissions
// This complex query gets datasets where the user has READ access either through:
// 1. Direct user-specific grants
// 2. Role-based grants for their role
// 3. Being the owner of the dataset
const results = await db
.select({
id: dataset.id,
name: dataset.name,
description: dataset.description,
public: dataset.public,
createdAt: dataset.createdAt,
owner: dataset.owner,
type: dataset.type,
permission: accessGrant.permission,
userId: accessGrant.userId
})
.from(dataset)
.leftJoin(
accessGrant,
sqlExpr`${accessGrant.datasetId} = ${dataset.id} AND ${accessGrant.active} = true AND (
(${accessGrant.userId} = ${userId}) OR
(${accessGrant.userId} IS NULL AND ${accessGrant.role} = ${userRoleName})
)`
)
.where(sqlExpr`
${dataset.active} = true AND (
${dataset.owner} = ${userId} OR
${accessGrant.permission} IS NOT NULL
)
`)
.orderBy(dataset.name);
// Group results by dataset and collect permissions
const datasetMap = new Map<string, {
id: string;
name: string;
description: string | null;
public: boolean;
createdAt: string;
owner: string;
type: string;
permissions: string[];
}>();
results.forEach(row => {
if (!datasetMap.has(row.id)) {
// If user is owner, they have all permissions
const isOwner = row.owner === userId;
const basePermissions = isOwner ? ['READ', 'UPLOAD', 'DOWNLOAD', 'EDIT', 'DELETE'] : [];
datasetMap.set(row.id, {
id: row.id,
name: row.name,
description: row.description,
public: row.public ?? false,
createdAt: row.createdAt?.toISOString() ?? new Date().toISOString(),
owner: row.owner,
type: row.type,
permissions: basePermissions
});
}
// Add permission from access grants (avoid duplicates)
if (row.permission && !datasetMap.get(row.id)!.permissions.includes(row.permission)) {
datasetMap.get(row.id)!.permissions.push(row.permission);
}
});
// Filter datasets to only include those with READ permission
const datasetsWithReadAccess = Array.from(datasetMap.values())
.filter(dataset => dataset.permissions.includes('READ'))
.slice(0, 20); // Limit to 20 datasets
// Connect to the database first to check permissions
const sql = neon(c.env.DATABASE_URL);
const db = drizzle(sql);
// Check if user has permission to create datasets (ADMIN or CURATOR roles)
const userRoleResult = await db
.select({ role: userRole.role })
.from(userRole)
.where(eq(userRole.userId, userId))
.limit(1);
const userRoleName = userRoleResult.length > 0 ? userRoleResult[0].role : 'USER';
if (userRoleName !== 'ADMIN' && userRoleName !== 'CURATOR') {
return c.json({
error: "You don't have permission to create datasets"
}, 403);
}
<Button
onClick={handleCreateNew}
variant="default"
size="sm"
className="flex items-center gap-2"
>
<Plus className="h-4 w-4" />
Add Dataset
</Button>
{canCreateDatasets && (
<Button
onClick={handleCreateNew}
variant="default"
size="sm"
className="flex items-center gap-2"
>
<Plus className="h-4 w-4" />
Add Dataset
</Button>
)}
<Button
onClick={(e) => {
e.stopPropagation();
handleEditDataset(dataset);
}}
variant="ghost"
size="sm"
className="p-1 h-8 w-8"
title="Edit dataset"
>
<Edit className="h-4 w-4" />
</Button>
<Button
onClick={(e) => {
e.stopPropagation();
handleDeleteDataset(dataset);
}}
variant="ghost"
size="sm"
className="p-1 h-8 w-8 text-red-600 hover:text-red-700 hover:bg-red-50"
title="Delete dataset"
>
<Trash2 className="h-4 w-4" />
</Button>
{dataset.permissions?.includes('EDIT') && (
<Button
onClick={(e) => {
e.stopPropagation();
handleEditDataset(dataset);
}}
variant="ghost"
size="sm"
className="p-1 h-8 w-8"
title="Edit dataset"
>
<Edit className="h-4 w-4" />
</Button>
)}
{dataset.permissions?.includes('DELETE') && (
<Button
onClick={(e) => {
e.stopPropagation();
handleDeleteDataset(dataset);
}}
variant="ghost"
size="sm"
className="p-1 h-8 w-8 text-red-600 hover:text-red-700 hover:bg-red-50"
title="Delete dataset"
>
<Trash2 className="h-4 w-4" />
</Button>
)}
{/* Show placeholder if no actions available */}
{!dataset.permissions?.includes('EDIT') && !dataset.permissions?.includes('DELETE') && (
<span className="text-gray-400 text-sm">—</span>
)}