File size: 1,650 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
const fs = require("fs");

function formatLoadError(error) {
  return error instanceof Error ? error.message : String(error);
}

function openBetterSqliteReadOnly(dbPath) {
  const Database = require("better-sqlite3");
  return new Database(dbPath, { readonly: true, fileMustExist: true });
}

function openNodeSqliteReadOnly(dbPath) {
  const { DatabaseSync } = require("node:sqlite");
  return new DatabaseSync(dbPath, { readOnly: true });
}

function openReadOnlySqliteDatabase(dbPath) {
  const errors = [];

  try {
    return openBetterSqliteReadOnly(dbPath);
  } catch (error) {
    errors.push(`better-sqlite3: ${formatLoadError(error)}`);
  }

  try {
    return openNodeSqliteReadOnly(dbPath);
  } catch (error) {
    errors.push(`node:sqlite: ${formatLoadError(error)}`);
  }

  throw new Error(errors.join("; "));
}

function hasEncryptedCredentials(dbPath, openDatabase = openReadOnlySqliteDatabase) {
  if (!fs.existsSync(dbPath)) return false;

  let db = null;
  try {
    db = openDatabase(dbPath);
    const row = db
      .prepare(
        `SELECT 1
           FROM provider_connections
          WHERE access_token LIKE 'enc:v1:%'
             OR refresh_token LIKE 'enc:v1:%'
             OR api_key LIKE 'enc:v1:%'
             OR id_token LIKE 'enc:v1:%'
          LIMIT 1`
      )
      .get();
    return !!row;
  } catch (error) {
    const message = formatLoadError(error);
    throw new Error(`Unable to inspect existing database at ${dbPath}: ${message}`);
  } finally {
    if (db) {
      db.close();
    }
  }
}

module.exports = {
  hasEncryptedCredentials,
  openNodeSqliteReadOnly,
  openReadOnlySqliteDatabase,
};