statusCodeRules.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact [email protected]
  14. */
  15. export function parseHttpStatusCodeRules(input) {
  16. const raw = (input ?? '').toString().trim();
  17. if (raw.length === 0) {
  18. return {
  19. ok: true,
  20. ranges: [],
  21. tokens: [],
  22. normalized: '',
  23. invalidTokens: [],
  24. };
  25. }
  26. const sanitized = raw.replace(/[,]/g, ',');
  27. const segments = sanitized.split(/[,]/g);
  28. const ranges = [];
  29. const invalidTokens = [];
  30. for (const segment of segments) {
  31. const trimmed = segment.trim();
  32. if (!trimmed) continue;
  33. const parsed = parseToken(trimmed);
  34. if (!parsed) invalidTokens.push(trimmed);
  35. else ranges.push(parsed);
  36. }
  37. if (invalidTokens.length > 0) {
  38. return {
  39. ok: false,
  40. ranges: [],
  41. tokens: [],
  42. normalized: raw,
  43. invalidTokens,
  44. };
  45. }
  46. const merged = mergeRanges(ranges);
  47. const tokens = merged.map((r) =>
  48. r.start === r.end ? `${r.start}` : `${r.start}-${r.end}`,
  49. );
  50. const normalized = tokens.join(',');
  51. return {
  52. ok: true,
  53. ranges: merged,
  54. tokens,
  55. normalized,
  56. invalidTokens: [],
  57. };
  58. }
  59. function parseToken(token) {
  60. const cleaned = (token ?? '').toString().trim().replaceAll(' ', '');
  61. if (!cleaned) return null;
  62. if (cleaned.includes('-')) {
  63. const parts = cleaned.split('-');
  64. if (parts.length !== 2) return null;
  65. const [a, b] = parts;
  66. if (!isNumber(a) || !isNumber(b)) return null;
  67. const start = Number.parseInt(a, 10);
  68. const end = Number.parseInt(b, 10);
  69. if (!Number.isFinite(start) || !Number.isFinite(end)) return null;
  70. if (start > end) return null;
  71. if (start < 100 || end > 599) return null;
  72. return { start, end };
  73. }
  74. if (!isNumber(cleaned)) return null;
  75. const code = Number.parseInt(cleaned, 10);
  76. if (!Number.isFinite(code)) return null;
  77. if (code < 100 || code > 599) return null;
  78. return { start: code, end: code };
  79. }
  80. function isNumber(s) {
  81. return typeof s === 'string' && /^\d+$/.test(s);
  82. }
  83. function mergeRanges(ranges) {
  84. if (!Array.isArray(ranges) || ranges.length === 0) return [];
  85. const sorted = [...ranges].sort((a, b) =>
  86. a.start !== b.start ? a.start - b.start : a.end - b.end,
  87. );
  88. const merged = [sorted[0]];
  89. for (let i = 1; i < sorted.length; i += 1) {
  90. const current = sorted[i];
  91. const last = merged[merged.length - 1];
  92. if (current.start <= last.end + 1) {
  93. last.end = Math.max(last.end, current.end);
  94. continue;
  95. }
  96. merged.push({ ...current });
  97. }
  98. return merged;
  99. }