/ src / memory_randomstore.ts
memory_randomstore.ts
 1  import { RandomStore, RandomStoreOptions } from "./index";
 2  import dayjs, { Dayjs } from "dayjs";
 3  import * as util from "node:util";
 4  
 5  type RandomStoreEntry = {
 6    string: string;
 7    valid: boolean;
 8    added: Dayjs;
 9  }
10  
11  const logger = util.debuglog('captchajs:randomstore:memory');
12  
13  const defaultOptions: RandomStoreOptions = {
14    expiryTimeSeconds: 60 * 60 * 24
15  }
16  
17  export class MemoryRandomStore implements RandomStore {
18    private opts: RandomStoreOptions;
19    private validStrings: RandomStoreEntry[] = [];
20  
21    constructor(opts?: RandomStoreOptions) {
22      logger("Constructing with options", opts);
23      this.opts = Object.assign({}, defaultOptions, opts);
24      logger("this.opts", this.opts);
25    }
26  
27    addRandom(rs: string): boolean {
28      this.expire();
29  
30      // Don't care if it's valid.
31      logger("Checking if", rs, "is already recorded")
32      const index = this.validStrings.findIndex((entry) => entry.string === rs);
33      if (index >= 0) {
34        logger("It is, reject.")
35        return false;
36      }
37  
38      this.validStrings.push({ string: rs, valid: true, added: dayjs() });
39      logger("Accepted string");
40      return true;
41    }
42  
43    validateRandom(rs: string, invalidate= true): boolean {
44      this.expire();
45  
46      logger("Validating random string", rs, "- invalidate after?", invalidate);
47      const index = this.validStrings.findIndex((entry) => entry.string === rs && entry.valid)
48      if (index < 0) {
49        logger("String not found");
50        return false;
51      } else {
52        logger("Found at position", index);
53      }
54  
55      if (invalidate) {
56        logger("Marking no longer valid");
57        this.validStrings[index].valid = false;
58      }
59  
60      return true;
61    }
62  
63    expire(): void {
64      const now = dayjs();
65      logger("Running expiry");
66      this.validStrings.forEach((entry) => {
67        if (entry.valid) {
68          // expiryTimeSeconds will exist, it comes from defaultOptions if they don't provide one.
69          if (now.isAfter(entry.added.add(this.opts.expiryTimeSeconds!, "seconds"))) {
70            logger("Expiring entry", entry);
71            entry.valid = false;
72          }
73        }
74      })
75    }
76  }