• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

suculent / thinx-device-api / #252646741

01 Mar 2022 05:33PM UTC coverage: 1.809% (-2.5%) from 4.358%
#252646741

push

suculent
Merge branch 'thinx-swarm'

* thinx-swarm:
  fix alert suppression before merging these fixes
  intentional incomplete sanitization
  possible shell command dependency fix
  fixed format string injection, trying to fix uncontrolled path by replacing validator with sanitka (who is better?)
  typo fix
  possible lgtm fix

2 of 587 branches covered (0.34%)

Branch coverage included in aggregate %.

33 of 1348 relevant lines covered (2.45%)

0.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.79
/lib/thinx/database.js
1
// Database Manager
2

3
const Globals = require("./globals.js");
1✔
4
const app_config = Globals.app_config();
×
5
const fs = require("fs-extra");
×
6
module.exports = class Database {
×
7

8
        constructor() {
9
                console.log("[info] Will init DBs...");
×
10
                this.nano = require("nano")(app_config.database_uri);
×
11
                console.log("[info] Loaded module: Database");
×
12
        }
13

14
        nano() {
15
                return this.nano;
×
16
        }
17

18
        null_cb(err, body, header) {
19
                // only unexpected errors should be logged
20
                if (process.env.ENVIRONMENT === "test") {
×
21
                        // The database may already exist.
22
                        if (err.ststusCode !== 412) {
×
23
                                console.log(err, body, header);
×
24
                        }
25
                }
26
        }
27

28
        // Designated initalizer
29
        init(callback) {
30

31
                console.log("[info] Initializing databases...");
×
32

33
                let db_names = [
×
34
                        "devices", "builds", "users", "logs"
35
                ];
36

37
                this.nano.db.list((err, existing_dbs) => {
×
38

39
                        if ((typeof(existing_dbs) === "undefined") || (existing_dbs === null)) existing_dbs = [];
×
40

41
                        db_names.forEach((name) => {
×
42

43
                                if (existing_dbs.includes(name)) {
×
44
                                        console.log("DB", name, "already exists.");
×
45
                                        return;
×
46
                                }
47

48
                                console.log("[info] Creating database", name);
×
49

50
                                const dbprefix = Globals.prefix();
×
51

52
                                this.nano.db.create(dbprefix + "managed_" + name).then((/* body */) => {
×
53
                                        var couch_db = this.nano.db.use(dbprefix + "managed_" + name);
×
54
                                        this.injectDesign(couch_db, name, "/opt/thinx/thinx-device-api/design/design_" + name + ".json");
×
55
                                        this.injectReplFilter(couch_db, "/opt/thinx/thinx-device-api/design/filters_" + name + ".json");
×
56
                                        console.log("[info] managed_" + name + " db is ready now.");
×
57
                                }).catch((err2) => {
58
                                        this.handleDatabaseErrors(err2, "managed_" + name);
×
59
                                });
60
                        });
61

62
                        this.nano.db.list((err2, new_dbs) => {
×
63
                                if (typeof (callback) !== "undefined") {
×
64
                                        callback(err2, new_dbs);
×
65
                                } else {
66
                                        return new_dbs;
×
67
                                }
68
                        });
69

70
                        setTimeout(() => {
×
71
                                setInterval(this.compactDatabases, 3600 * 1000); // Compact databases once an hour        
×
72
                        }, 30000);
73
                        
74
                });
75
        }
76

77
        compactDatabases(opt_callback) {
78
                const prefix = Globals.prefix();
×
79
                this.nano = require("nano")(app_config.database_uri);
×
80
                this.nano.db.compact(prefix + "managed_logs");
×
81
                this.nano.db.compact(prefix + "managed_builds");
×
82
                this.nano.db.compact(prefix + "managed_devices");
×
83
                this.nano.db.compact(prefix + "managed_users", "owners_by_username", (err) => {
×
84
                        if (err) {
×
85
                                if (typeof(opt_callback) !== "undefined") opt_callback(err);
×
86
                        } else {
87
                                console.log("» Database compact jobs completed.");
×
88
                                if (typeof(opt_callback) !== "undefined") opt_callback(true);
×
89
                        }
90
                });
91
        }
92

93
        // Database preparation on first run
94
        getDocument(file) {
95
                if (!fs.existsSync(file)) {
×
96
                        console.log("Initializing replication filter failed, file does not exist", file);
×
97
                        return false;
×
98
                }
99
                const data = fs.readFileSync(file);
×
100
                if (typeof (data) === "undefined") {
×
101
                        console.log("» [getDocument] no data read.");
×
102
                        return false;
×
103
                }
104
                // Parser may fail
105
                try {
×
106
                        return JSON.parse(data);
×
107
                } catch (e) {
108
                        console.log("» Document File may not exist: " + e);
×
109
                        return false;
×
110
                }
111
        }
112

113
        logCouchError(err, body, header, tag) {
114
                if (err !== null) {
×
115
                        if (err.toString().indexOf("conflict") === -1) {
×
116
                                console.log("[error] Couch Init error: ", err, body, header, tag);
×
117
                        }
118
                        if (err.toString().indexOf("ENOTFOUND") !== -1) {
×
119
                                console.log("Critical DB integration error, exiting.");
×
120
                                process.exit(1);
×
121
                        }
122
                } else {
123
                        return;
×
124
                }
125
                if (typeof (body) !== "undefined") {
×
126
                        console.log("[error] Log Couch Insert body: " + body + " " + tag);
×
127
                }
128
                if (typeof (header) !== "undefined") {
×
129
                        console.log("[error] Log Couchd Insert header: " + header + " " + tag);
×
130
                }
131
        }
132

133
        injectDesign(db, design, file) {
134
                if (typeof (design) === "undefined") return;
×
135
                let design_doc = this.getDocument(file);
×
136
                if (design_doc != null) {
×
137
                        db.insert(design_doc, "_design/" + design, (err, body, header) => {
×
138
                                this.logCouchError(err, body, header, "init:design:" + design); // returns if no err
×
139
                        });
140
                } else {
141
                        console.log("[error] Design doc injection issue at " + file);
×
142
                }
143
        }
144

145
        injectReplFilter(db, file) {
146
                let filter_doc = this.getDocument(file);
×
147
                if (filter_doc !== false) {
×
148
                        db.insert(filter_doc, "_design/repl_filters", (err, body, header) => {
×
149
                                this.logCouchError(err, body, header, "init:repl:" + JSON.stringify(filter_doc)); // returns if no err
×
150
                        });
151
                } else {
152
                        console.log("[error] Filter doc injection issue (no doc) at " + file);
×
153
                }
154
        }
155

156
        handleDatabaseErrors(err, name) {
157
                if (err.toString().indexOf("the file already exists") !== -1) {
×
158
                        // silently fail, this is ok
159
                } else if (err.toString().indexOf("error happened") !== -1) {
×
160
                        console.log("[CRITICAL] 🚫 Database connectivity issue. " + err.toString() + " URI: " + app_config.database_uri);
×
161
                        // give some time for DB to wake up until next try, also prevents too fast restarts...
162
                        setTimeout(() => {
×
163
                                process.exit(1);
×
164
                        }, 1000);
165
                } else {
166
                        console.log("[CRITICAL] 🚫 Database " + name + " creation failed. " + err + " URI: " + app_config.database_uri);
×
167
                        setTimeout(() => {
×
168
                                process.exit(2);
×
169
                        }, 1000);
170
                }
171
        }
172
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc