Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

86 строки
2.4 KiB

  1. #!/usr/bin/env node
  2. // One-shot migration: sanitize existing card.fullContent in data/cards.txt.
  3. // Backups the original to data/cards.txt.bak-<timestamp> before rewriting.
  4. // Run: `node scripts/sanitize-existing-cards.mjs`.
  5. import { readFile, writeFile, copyFile } from 'node:fs/promises';
  6. import { resolve, dirname } from 'node:path';
  7. import { fileURLToPath } from 'node:url';
  8. import sanitizeHtml from 'sanitize-html';
  9. const __dirname = dirname(fileURLToPath(import.meta.url));
  10. const CARDS_PATH = resolve(__dirname, '..', 'data', 'cards.txt');
  11. // Keep this config in sync with lib/sanitize.ts
  12. const CARD_HTML_CONFIG = {
  13. allowedTags: [
  14. 'p', 'br', 'strong', 'em', 'b', 'i', 'u',
  15. 'ul', 'ol', 'li',
  16. 'a',
  17. 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
  18. 'blockquote',
  19. 'span',
  20. ],
  21. allowedAttributes: {
  22. a: ['href', 'title', 'target', 'rel'],
  23. span: ['class'],
  24. '*': [],
  25. },
  26. allowedSchemes: ['http', 'https', 'mailto', 'tel'],
  27. allowedSchemesAppliedToAttributes: ['href'],
  28. transformTags: {
  29. a: sanitizeHtml.simpleTransform('a', { rel: 'noopener noreferrer', target: '_blank' }),
  30. },
  31. disallowedTagsMode: 'discard',
  32. };
  33. async function main() {
  34. let raw;
  35. try {
  36. raw = await readFile(CARDS_PATH, 'utf-8');
  37. } catch (err) {
  38. if (err.code === 'ENOENT') {
  39. console.log(`No cards file at ${CARDS_PATH}, nothing to do.`);
  40. return;
  41. }
  42. throw err;
  43. }
  44. let cards;
  45. try {
  46. cards = JSON.parse(raw);
  47. } catch (err) {
  48. console.error('Cards file is not valid JSON; refusing to migrate.', err.message);
  49. process.exit(1);
  50. }
  51. if (!Array.isArray(cards)) {
  52. console.error('Cards file is not an array; refusing to migrate.');
  53. process.exit(1);
  54. }
  55. const ts = new Date().toISOString().replace(/[:.]/g, '-');
  56. const backupPath = `${CARDS_PATH}.bak-${ts}`;
  57. await copyFile(CARDS_PATH, backupPath);
  58. console.log(`Backup written: ${backupPath}`);
  59. let changed = 0;
  60. for (const card of cards) {
  61. if (typeof card?.fullContent === 'string' && card.fullContent.length > 0) {
  62. const cleaned = sanitizeHtml(card.fullContent, CARD_HTML_CONFIG);
  63. if (cleaned !== card.fullContent) {
  64. card.fullContent = cleaned;
  65. changed++;
  66. }
  67. }
  68. }
  69. await writeFile(CARDS_PATH, JSON.stringify(cards, null, 2), 'utf-8');
  70. console.log(`Migration done. Cards changed: ${changed} / ${cards.length}.`);
  71. }
  72. main().catch(err => {
  73. console.error(err);
  74. process.exit(1);
  75. });