-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathremove.mjs
More file actions
83 lines (77 loc) · 4.29 KB
/
remove.mjs
File metadata and controls
83 lines (77 loc) · 4.29 KB
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
72
73
74
75
76
77
78
79
80
81
82
83
#!/usr/bin/env node
// Append a validated takedown entry to removed.tsv (the archive's removal source of truth) —
// safer than hand-editing the TSV. generate.mjs then drops the item from every surface.
//
// node remove.mjs sprite <id> "<reason>" [YYYY-MM-DD]
// node remove.mjs tune <id> "<reason>" [YYYY-MM-DD]
// node remove.mjs user <id> "<reason>" [YYYY-MM-DD] # profile + de-attribute + scrub handle
// node remove.mjs comment <spriteId> # LIST a sprite's comments + their keys
// node remove.mjs comment <spriteId>#<hash> "<reason>" # remove ONE comment
//
// After adding, regenerate + deploy (see README "Removing content"):
// node generate.mjs
// aws s3 sync public/ s3://pixieengine-static --delete
// aws cloudfront create-invalidation --distribution-id E2QQUW2BPHXXNP --paths '/sprites/<id>/*' ...
//
// For an URGENT/ILLEGAL sprite takedown, ALSO delete the image object(s) on the CDN bucket —
// the static rebuild does not touch them. See README "Removing content".
import { readFileSync, existsSync } from "node:fs";
import { commentKey } from "./comment-key.mjs";
import { appendRemoval } from "./removed-list.mjs";
const TYPES = new Set(["sprite", "tune", "user", "comment"]);
const COMMENTS = new URL("build/comments.ndjson", import.meta.url);
const [type, idArg, reason, dateArg] = process.argv.slice(2);
function die(msg) {
console.error(`error: ${msg}
usage:
node remove.mjs <sprite|tune|user> <id> "<reason>" [YYYY-MM-DD]
node remove.mjs comment <spriteId> # list a sprite's comments + keys
node remove.mjs comment <spriteId>#<hash> "<reason>" # remove one comment`);
process.exit(1);
}
if (!TYPES.has(type)) die("type must be one of: sprite, tune, user, comment");
// validate + dedupe + append a row (shared write logic lives in removed-list.mjs)
function writeRow(t, idStr, why, when) {
const r = appendRemoval(t, idStr, why, when);
if (!r.ok) die(r.error === "already listed" ? `${t} ${idStr} is already in removed.tsv`
: r.error === "reason required" ? 'a reason is required (e.g. "DMCA takedown")'
: r.error === "bad date" ? "date must be YYYY-MM-DD" : r.error);
console.error(`added: ${t} ${idStr} (${r.date}) — ${why.replace(/[\t\r\n]+/g, " ").trim()}`);
console.error("next: node generate.mjs → aws s3 sync public/ s3://pixieengine-static --delete → CloudFront invalidation");
if (t === "sprite") console.error('URGENT/ILLEGAL? the CDN image is NOT removed by the rebuild — delete it too (see README "Removing content").');
}
// ---- comment: list keys, or remove one by key ----
if (type === "comment") {
const loadFor = (sid) => {
if (!existsSync(COMMENTS)) die("build/comments.ndjson not found — run the build pipeline first");
for (const l of readFileSync(COMMENTS, "utf8").split("\n")) {
if (!l) continue; const r = JSON.parse(l); if (r.id === sid) return r.c || [];
}
return [];
};
// list mode: bare "comment <spriteId>" (no key, no reason)
if (idArg && /^\d+$/.test(idArg) && !reason) {
const list = loadFor(+idArg);
if (!list.length) { console.error(`sprite ${idArg} has no recovered comments`); process.exit(0); }
console.error(`sprite ${idArg} — ${list.length} comment(s):\n`);
for (const c of list) {
const key = commentKey(c.by, c.body);
const body = String(c.body).replace(/\s+/g, " ").trim().slice(0, 100);
console.error(` ${idArg}#${key} [${c.by || "?"}${c.at ? " · " + c.at : ""}] ${body}`);
console.error(` → node remove.mjs comment ${idArg}#${key} "<reason>"\n`);
}
process.exit(0);
}
// removal mode: "comment <spriteId>#<hash> <reason>"
const m = /^(\d+)#([0-9a-f]+)$/i.exec(idArg || "");
if (!m) die('comment id must be "<spriteId>#<hash>" — run `node remove.mjs comment <spriteId>` to list keys');
const sid = +m[1], hash = m[2].toLowerCase();
if (existsSync(COMMENTS) && !loadFor(sid).some((c) => commentKey(c.by, c.body) === hash))
die(`no comment with key ${sid}#${hash} (run \`node remove.mjs comment ${sid}\` to list current keys)`);
writeRow("comment", `${sid}#${hash}`, reason, dateArg);
process.exit(0);
}
// ---- sprite / tune / user: numeric id ----
const id = Number(idArg);
if (!Number.isInteger(id) || id <= 0) die("id must be a positive integer");
writeRow(type, String(id), reason, dateArg);