CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | /**
* Reactions API — toggle + list reactions on issues, PRs, and their comments.
*
* POST /api/reactions/:targetType/:targetId/:emoji/toggle
* Body-less; auth required. Returns JSON {ok, added, counts}.
* If the request accepts text/html (form submission), redirects back.
*
* GET /api/reactions/:targetType/:targetId
* Returns the emoji -> count summary plus `reactedByMe` for the caller.
*/
import { Hono } from "hono";
import type { AuthEnv } from "../middleware/auth";
import { requireAuth, softAuth } from "../middleware/auth";
import {
isAllowedEmoji,
isAllowedTarget,
summariseReactions,
toggleReaction,
} from "../lib/reactions";
const reactions = new Hono<AuthEnv>();
reactions.use("/api/reactions/*", softAuth);
reactions.get("/api/reactions/:targetType/:targetId", async (c) => {
const user = c.get("user");
const { targetType, targetId } = c.req.param();
if (!isAllowedTarget(targetType)) {
return c.json({ ok: false, error: "unknown target type" }, 400);
}
const rows = await summariseReactions(targetType, targetId, user?.id);
return c.json({ ok: true, reactions: rows });
});
reactions.post(
"/api/reactions/:targetType/:targetId/:emoji/toggle",
requireAuth,
async (c) => {
const user = c.get("user")!;
const { targetType, targetId, emoji } = c.req.param();
if (!isAllowedTarget(targetType)) {
return c.json({ ok: false, error: "unknown target type" }, 400);
}
if (!isAllowedEmoji(emoji)) {
return c.json({ ok: false, error: "unknown emoji" }, 400);
}
try {
const { added } = await toggleReaction(
user.id,
targetType,
targetId,
emoji
);
const summary = await summariseReactions(targetType, targetId, user.id);
const accept = c.req.header("accept") || "";
if (accept.includes("text/html")) {
const ref = c.req.header("referer");
return c.redirect(ref || "/");
}
return c.json({ ok: true, added, reactions: summary });
} catch (err) {
console.error("[reactions] toggle:", err);
return c.json({ ok: false, error: "server error" }, 500);
}
}
);
export default reactions;
|