server/api/ObservationRoute.js
Rama master · réplica de lectura del repositorio original.
const rateLimit = require("express-rate-limit");
const HomeController = require("../controllers/HomeController");
const DigestController = require("../controllers/DigestController");
const MetricRecommendationController = require("../controllers/MetricRecommendationController");
const MonitorController = require("../controllers/MonitorController");
const ObservationController = require("../controllers/ObservationController");
const WorkspaceLearningController = require("../controllers/WorkspaceLearningController");
const verifyToken = require("../modules/verifyToken");
const { getObservationAccess } = require("../modules/observations/access");
const starterSuggestions = require("../modules/ai/homeSuggestions/onboarding");
const homeSuggestions = require("../modules/ai/homeSuggestions/service");
const { allowedActions } = require("../modules/ai/homeSuggestions/rules");
const apiLimiter = rateLimit({
limit: 100,
windowMs: 60 * 1000,
});
const testDigestLimiter = rateLimit({
keyGenerator: (req) => `${req.user.id}:${req.params.team_id}`,
legacyHeaders: false,
limit: 3,
standardHeaders: true,
windowMs: 60 * 60 * 1000,
handler: (req, res) => {
const resetTime = req.rateLimit?.resetTime?.getTime?.() || (Date.now() + (60 * 60 * 1000));
const retryAfterSeconds = Math.max(1, Math.ceil((resetTime - Date.now()) / 1000));
const retryAfterMinutes = Math.max(1, Math.ceil(retryAfterSeconds / 60));
res.set("Retry-After", `${retryAfterSeconds}`);
return res.status(429).send({
code: "SUMMARY_TEST_RATE_LIMITED",
error: `You have sent too many test summaries. Try again in ${retryAfterMinutes} minutes.`,
retryAfterSeconds,
});
},
});
function sendError(res, error) {
const statusCode = error.statusCode || 500;
return res.status(statusCode).send({
error: statusCode < 500 && error.message
? error.message
: "The request could not be completed",
});
}
function checkAccess() {
return async (req, res, next) => {
try {
req.observationAccess = await getObservationAccess(req.params.team_id, req.user.id);
return next();
} catch (error) {
return sendError(res, error);
}
};
}
module.exports = (app) => {
const homeController = new HomeController();
const digestController = new DigestController();
const metricRecommendationController = new MetricRecommendationController();
const monitorController = new MonitorController();
const observationController = new ObservationController();
const workspaceLearningController = new WorkspaceLearningController();
const routeAccess = [apiLimiter, verifyToken, checkAccess()];
app.get("/team/:team_id/home", ...routeAccess, async (req, res) => {
try {
res.set("Cache-Control", "no-store");
const [home, suggestions] = await Promise.all([
homeController.getHome(req.observationAccess),
homeSuggestions.readSuggestions(req.observationAccess),
]);
const config = await homeSuggestions.settings(req.observationAccess);
return res.send({
...home, suggestions,
...await starterSuggestions.readSuggestions(req.observationAccess, home.content),
suggestionActions: allowedActions(req.observationAccess, config.envelope, config.policy),
});
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/home/suggestions/refresh", ...routeAccess, async (req, res) => {
try {
await homeSuggestions.requestRefresh(req.observationAccess, app.get("homeSuggestionsQueue"));
return res.status(202).send({ success: true });
} catch (error) { return sendError(res, error); }
});
app.post("/team/:team_id/home/activity", ...routeAccess, async (req, res) => {
try {
const context = req.body.projectId ? [{ entityType: "project", entityId: req.body.projectId }] : [];
await homeSuggestions.recordActivity(req.observationAccess, context);
return res.send({ success: true });
} catch (error) { return sendError(res, error); }
});
app.get("/team/:team_id/home/suggestions/:id", ...routeAccess, async (req, res) => {
try {
res.set("Cache-Control", "no-store");
const suggestions = await homeSuggestions.readSuggestions(req.observationAccess);
const suggestion = suggestions.find((item) => item.id === req.params.id);
if (!suggestion) return res.status(404).send({ error: "This suggestion is no longer available. Choose another question." });
homeSuggestions.log("selected");
return res.send(suggestion);
} catch (error) { return sendError(res, error); }
});
app.delete("/team/:team_id/home/memories", ...routeAccess, async (req, res) => {
try {
await homeSuggestions.changeState(req.observationAccess, req.query.id ? { forgetId: req.query.id } : { forget: true });
return res.send({ success: true });
} catch (error) { return sendError(res, error); }
});
app.get("/team/:team_id/activity", ...routeAccess, async (req, res) => {
try {
return res.send(await homeController.getActivity(req.observationAccess, req.query));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/activity-counts", ...routeAccess, async (req, res) => {
try {
return res.send(await homeController.getActivityCounts(req.observationAccess));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/data-health", ...routeAccess, async (req, res) => {
try {
return res.send(await homeController.getDataHealth(req.observationAccess));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/data-health/:issue_id/dismiss", ...routeAccess, async (req, res) => {
try {
return res.send(await homeController.dismissDataHealth(
req.observationAccess,
req.params.issue_id
));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/alerts", ...routeAccess, async (req, res) => {
try {
return res.send(await homeController.getAlerts(req.observationAccess));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/workspace-learning/export", ...routeAccess, async (req, res) => {
try {
return res.send(await workspaceLearningController.export(
req.observationAccess,
req.query
));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/orchestrator-audit", ...routeAccess, async (req, res) => {
try {
return res.send(await workspaceLearningController.actionAudit(
req.observationAccess,
req.query
));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/orchestrator-egress-audit", ...routeAccess, async (req, res) => {
try {
return res.send(await workspaceLearningController.egressAudit(
req.observationAccess,
req.query
));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/observations", ...routeAccess, async (req, res) => {
try {
return res.send(await observationController.list(req.observationAccess, req.query));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/observations/:observation_id", ...routeAccess, async (req, res) => {
try {
return res.send(await observationController.detail(
req.observationAccess,
req.params.observation_id
));
} catch (error) {
return sendError(res, error);
}
});
app.put("/team/:team_id/observations/:observation_id/preference", ...routeAccess, async (req, res) => {
try {
return res.send(await observationController.updatePreference(
req.observationAccess,
req.params.observation_id,
req.body
));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/observations/:observation_id/feedback", ...routeAccess, async (req, res) => {
try {
return res.send(await observationController.feedback(
req.observationAccess,
req.params.observation_id,
req.body
));
} catch (error) {
return sendError(res, error);
}
});
app.delete("/team/:team_id/observations/:observation_id/feedback", ...routeAccess, async (req, res) => {
try {
return res.send(await observationController.deleteFeedback(
req.observationAccess,
req.params.observation_id
));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/observations/:observation_id/resolve", ...routeAccess, async (req, res) => {
try {
return res.send(await observationController.setResolved(
req.observationAccess,
req.params.observation_id,
true
));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/observations/:observation_id/reopen", ...routeAccess, async (req, res) => {
try {
return res.send(await observationController.setResolved(
req.observationAccess,
req.params.observation_id,
false
));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/observations/:observation_id/investigate", ...routeAccess, async (req, res) => {
try {
return res.send(await observationController.investigate(
req.observationAccess,
req.params.observation_id
));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/observations/:observation_id/drivers", ...routeAccess, async (req, res) => {
try {
return res.send(await observationController.drivers(
req.observationAccess,
req.params.observation_id
));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/monitors", ...routeAccess, async (req, res) => {
try {
return res.send(await monitorController.list(req.observationAccess));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/monitor-recommendations", ...routeAccess, async (req, res) => {
try {
return res.send(await metricRecommendationController.list(req.observationAccess));
} catch (error) {
return sendError(res, error);
}
});
app.post(
"/team/:team_id/monitor-recommendations/:recommendation_id/accept",
...routeAccess,
async (req, res) => {
try {
return res.status(201).send(await metricRecommendationController.accept(
req.observationAccess,
req.params.recommendation_id,
req.body,
req.user
));
} catch (error) {
return sendError(res, error);
}
}
);
app.get(
"/team/:team_id/monitor-recommendation-dismissals",
...routeAccess,
async (req, res) => {
try {
return res.send(await metricRecommendationController.listDismissals(
req.observationAccess
));
} catch (error) {
return sendError(res, error);
}
}
);
app.delete(
"/team/:team_id/monitor-recommendation-dismissals/:dismissal_id",
...routeAccess,
async (req, res) => {
try {
return res.send(await metricRecommendationController.restore(
req.observationAccess,
req.params.dismissal_id
));
} catch (error) {
return sendError(res, error);
}
}
);
app.post(
"/team/:team_id/monitor-recommendations/:recommendation_id/dismiss",
...routeAccess,
async (req, res) => {
try {
return res.send(await metricRecommendationController.dismiss(
req.observationAccess,
req.params.recommendation_id,
req.body
));
} catch (error) {
return sendError(res, error);
}
}
);
app.get("/team/:team_id/charts/:chart_id/monitor-options", ...routeAccess, async (req, res) => {
try {
return res.send(await monitorController.options(
req.observationAccess,
req.params.chart_id
));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/monitors", ...routeAccess, async (req, res) => {
try {
return res.status(201).send(await monitorController.create(
req.observationAccess,
req.body,
req.user
));
} catch (error) {
return sendError(res, error);
}
});
app.put("/team/:team_id/monitors/:monitor_id", ...routeAccess, async (req, res) => {
try {
return res.send(await monitorController.updateWithAudit(
req.observationAccess,
req.params.monitor_id,
req.body
));
} catch (error) {
return sendError(res, error);
}
});
app.delete("/team/:team_id/monitors/:monitor_id", ...routeAccess, async (req, res) => {
try {
return res.send(await monitorController.remove(
req.observationAccess,
req.params.monitor_id
));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/monitors/:monitor_id/refresh", ...routeAccess, async (req, res) => {
try {
return res.send(await monitorController.refresh(
req.observationAccess,
req.params.monitor_id,
req.user
));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/observation-digests", ...routeAccess, async (req, res) => {
try {
return res.send(await digestController.list(req.observationAccess));
} catch (error) {
return sendError(res, error);
}
});
app.get("/team/:team_id/observation-digests/options", ...routeAccess, async (req, res) => {
try {
return res.send(await digestController.options(req.observationAccess));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/observation-digests/preview", ...routeAccess, async (req, res) => {
try {
return res.send(await digestController.preview(req.observationAccess, req.body));
} catch (error) {
return sendError(res, error);
}
});
app.post("/team/:team_id/observation-digests", ...routeAccess, async (req, res) => {
try {
return res.status(201).send(await digestController.create(
req.observationAccess,
req.body,
));
} catch (error) {
return sendError(res, error);
}
});
app.put(
"/team/:team_id/observation-digests/:subscription_id",
...routeAccess,
async (req, res) => {
try {
return res.send(await digestController.updateWithAudit(
req.observationAccess,
req.params.subscription_id,
req.body,
));
} catch (error) {
return sendError(res, error);
}
},
);
app.delete(
"/team/:team_id/observation-digests/:subscription_id",
...routeAccess,
async (req, res) => {
try {
return res.send(await digestController.remove(
req.observationAccess,
req.params.subscription_id,
));
} catch (error) {
return sendError(res, error);
}
},
);
app.post(
"/team/:team_id/observation-digests/:subscription_id/send-test",
...routeAccess,
testDigestLimiter,
async (req, res) => {
try {
return res.send(await digestController.sendTest(
req.observationAccess,
req.params.subscription_id,
));
} catch (error) {
return sendError(res, error);
}
},
);
return (req, res, next) => next();
};