}
});
/**
* Protected API route to get a specific cluster by ID
*
* @route GET /api/clusters/:id
* @authentication Required
* @param {string} id - Cluster ID in URL path
* @returns {Object} Response containing:
* - data: Cluster object with id, name, description, recordingPattern, etc.
* @description Fetches a specific cluster by ID. Requires READ permission on the dataset.
*/
clusters.get("/:id", authenticate, async (c) => {
try {
const jwtPayload = (c as unknown as { jwtPayload: JWTPayload }).jwtPayload;
const userId = jwtPayload.sub;
const clusterId = c.req.param("id");
const db = createDatabase(c.env);
// Get the cluster with its recording pattern
const result = await db.select({
id: cluster.id,
datasetId: cluster.datasetId,
locationId: cluster.locationId,
name: cluster.name,
description: cluster.description,
createdBy: cluster.createdBy,
createdAt: cluster.createdAt,
lastModified: cluster.lastModified,
modifiedBy: cluster.modifiedBy,
active: cluster.active,
timezoneId: cluster.timezoneId,
cyclicRecordingPatternId: cluster.cyclicRecordingPatternId,
sampleRate: cluster.sampleRate,
recordS: cyclicRecordingPattern.recordS,
sleepS: cyclicRecordingPattern.sleepS
})
.from(cluster)
.leftJoin(
cyclicRecordingPattern,
and(
eq(cluster.cyclicRecordingPatternId, cyclicRecordingPattern.id),
eq(cyclicRecordingPattern.active, true)
)
)
.where(and(eq(cluster.id, clusterId), eq(cluster.active, true)))
.limit(1);
if (result.length === 0) {
return c.json({
error: "Cluster not found"
}, 404);
}
const clusterRecord = result[0];
// Check if user has READ permission on the dataset
const hasPermission = await checkUserPermission(db, userId, clusterRecord.datasetId, 'READ');
if (!hasPermission) {
return c.json({
error: "Access denied: No READ permission for this cluster's dataset"
}, 403);
}
// Format the response with recording pattern if it exists
const enrichedCluster = {
id: clusterRecord.id,
datasetId: clusterRecord.datasetId,
locationId: clusterRecord.locationId,
name: clusterRecord.name,
description: clusterRecord.description,
createdBy: clusterRecord.createdBy,
createdAt: clusterRecord.createdAt,
lastModified: clusterRecord.lastModified,
modifiedBy: clusterRecord.modifiedBy,
active: clusterRecord.active,
timezoneId: clusterRecord.timezoneId,
cyclicRecordingPatternId: clusterRecord.cyclicRecordingPatternId,
sampleRate: clusterRecord.sampleRate,
recordingPattern: (clusterRecord.recordS !== null && clusterRecord.sleepS !== null) ? {
recordS: clusterRecord.recordS,
sleepS: clusterRecord.sleepS
} : null
};
return c.json({
data: enrichedCluster
});
} catch (error) {
return c.json(standardErrorResponse(error, "fetching cluster"), 500);