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

suculent / thinx-device-api / #252646256

19 Mar 2022 07:32PM UTC coverage: 1.798% (-0.5%) from 2.248%
#252646256

push

suculent
Merge branch 'thinx-swarm'

* thinx-swarm:
  merging validator/sanitizer refactoring and socket fix; worker should stay stable (needs smoke test)
  worker update
  added basic test coverage to worker
  removing deprecated variables
  refactoring validator to sanitka, dropping duplicate code – both should work the same: return false or value
  possible wss cookie fix

2 of 595 branches covered (0.34%)

Branch coverage included in aggregate %.

33 of 1352 relevant lines covered (2.44%)

0.06 hits per line

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

0.72
/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

10
                let db_uri;
11
                let user = process.env.COUCHDB_USER;
×
12
                let pass = process.env.COUCHDB_PASS;
×
13

14
                if ((typeof (user) !== "undefined") && (typeof (pass) !== "undefined")) {
×
15
                        db_uri = `http://${user}:${pass}@couchdb:5984`;
×
16
                } else {
17
                        db_uri = app_config.database_uri; // fallback to old config.json; deprecated
×
18
                        console.log("⛔️ [deprecated] Using database credentials from configuration...");
×
19
                }
20

21
                this.db_uri = db_uri;
×
22
                this.nano = require("nano")(db_uri);
×
23
                console.log("✅ [info] Loaded module: Database");
×
24
        }
25

26
        nano() {
27
                return this.nano;
×
28
        }
29

30
        uri() {
31

32
                /* duplicate code, happens in constructor and that's enough
33
                let db_uri;
34
                let user = process.env.COUCHDB_USER;
35
                let pass = process.env.COUCHDB_PASS;
36

37
                if ((typeof (user) !== "undefined") && (typeof (pass) !== "undefined")) {
38
                        db_uri = `http://${user}:${pass}@couchdb:5984`;
39
                } else {
40
                        db_uri = app_config.database_uri; // fallback to old config.json; deprecated
41
                        console.log("⛔️ [deprecated] Using database credentials from configuration...");
42
                }
43

44
                this.db_uri = db_uri;
45
                */
46

47
                return this.db_uri;
×
48
        }
49
        
50

51
        null_cb(err, body, header) {
52
                // only unexpected errors should be logged
53
                if (process.env.ENVIRONMENT === "test") {
×
54
                        // The database may already exist.
55
                        if (err.ststusCode !== 412) {
×
56
                                console.log(err, body, header);
×
57
                        }
58
                }
59
        }
60

61
        // Designated initalizer
62
        init(callback) {
63

64
                console.log("ℹ️ [info] Initializing databases...");
×
65

66
                let db_names = [
×
67
                        "devices", "builds", "users", "logs"
68
                ];
69

70
                this.nano.db.list((err, existing_dbs) => {
×
71

72
                        if ((typeof(existing_dbs) === "undefined") || (existing_dbs === null)) existing_dbs = [];
×
73

74
                        db_names.forEach((name) => {
×
75

76
                                if (existing_dbs.includes(name)) {
×
77
                                        console.log(`ℹ️ [info] DB ${name} already exists.`);
×
78
                                        return;
×
79
                                }
80

81
                                console.log("ℹ️ [info] Creating database", name);
×
82

83
                                const dbprefix = Globals.prefix();
×
84

85
                                this.nano.db.create(dbprefix + "managed_" + name).then((/* body */) => {
×
86
                                        var couch_db = this.nano.db.use(dbprefix + "managed_" + name);
×
87
                                        this.injectDesign(couch_db, name, "/opt/thinx/thinx-device-api/design/design_" + name + ".json");
×
88
                                        this.injectReplFilter(couch_db, "/opt/thinx/thinx-device-api/design/filters_" + name + ".json");
×
89
                                        console.log(`ℹ️ [info] Database managed_${name} initialized.`);
×
90
                                }).catch((err2) => {
91
                                        this.handleDatabaseErrors(err2, "managed_" + name, dbprefix);
×
92
                                });
93
                        });
94

95
                        this.nano.db.list((err2, new_dbs) => {
×
96
                                if (typeof (callback) !== "undefined") {
×
97
                                        callback(err2, new_dbs);
×
98
                                } else {
99
                                        return new_dbs;
×
100
                                }
101
                        });
102

103
                        setTimeout(() => {
×
104
                                setInterval(this.compactDatabases, 3600 * 1000); // Compact databases once an hour        
×
105
                        }, 30000);
106
                        
107
                });
108
        }
109

110
        compactDatabases(opt_callback) {
111
                const prefix = Globals.prefix();
×
112
                let db_uri = new Database().uri();
×
113
                this.nano = require("nano")(db_uri);
×
114
                this.nano.db.compact(prefix + "managed_logs");
×
115
                this.nano.db.compact(prefix + "managed_builds");
×
116
                this.nano.db.compact(prefix + "managed_devices");
×
117
                this.nano.db.compact(prefix + "managed_users", "owners_by_username", (err) => {
×
118
                        if (err) {
×
119
                                if (typeof(opt_callback) !== "undefined") opt_callback(err);
×
120
                        } else {
121
                                console.log("» Database compact jobs completed.");
×
122
                                if (typeof(opt_callback) !== "undefined") opt_callback(true);
×
123
                        }
124
                });
125
        }
126

127
        // Database preparation on first run
128
        getDocument(file) {
129
                if (!fs.existsSync(file)) {
×
130
                        console.log("☣️ [error] Initializing replication filter failed, file does not exist", file);
×
131
                        return false;
×
132
                }
133
                const data = fs.readFileSync(file);
×
134
                if (typeof (data) === "undefined") {
×
135
                        console.log("☣️ [error] [getDocument] no data read.");
×
136
                        return false;
×
137
                }
138
                // Parser may fail
139
                try {
×
140
                        return JSON.parse(data);
×
141
                } catch (e) {
142
                        console.log("☣️ [error] Document File may not exist: " + e);
×
143
                        return false;
×
144
                }
145
        }
146

147
        logCouchError(err, body, header, tag) {
148
                if (err !== null) {
×
149
                        if (err.toString().indexOf("conflict") === -1) {
×
150
                                console.log("☣️ [error] Couch Init error: ", err, body, header, tag);
×
151
                        }
152
                        if (err.toString().indexOf("ENOTFOUND") !== -1) {
×
153
                                console.log("Critical DB integration error, exiting.");
×
154
                                process.exit(1);
×
155
                        }
156
                } else {
157
                        return;
×
158
                }
159
                if (typeof (body) !== "undefined") {
×
160
                        console.log("☣️ [error] Log Couch Insert body: " + body + " " + tag);
×
161
                }
162
                if (typeof (header) !== "undefined") {
×
163
                        console.log("☣️ [error] Log Couchd Insert header: " + header + " " + tag);
×
164
                }
165
        }
166

167
        injectDesign(db, design, file) {
168
                if (typeof (design) === "undefined") return;
×
169
                let design_doc = this.getDocument(file);
×
170
                if (design_doc != null) {
×
171
                        db.insert(design_doc, "_design/" + design, (err, body, header) => {
×
172
                                this.logCouchError(err, body, header, "init:design:" + design); // returns if no err
×
173
                        });
174
                } else {
175
                        console.log("☣️ [error] Design doc injection issue at " + file);
×
176
                }
177
        }
178

179
        injectReplFilter(db, file) {
180
                let filter_doc = this.getDocument(file);
×
181
                if (filter_doc !== false) {
×
182
                        db.insert(filter_doc, "_design/repl_filters", (err, body, header) => {
×
183
                                this.logCouchError(err, body, header, "init:repl:" + JSON.stringify(filter_doc)); // returns if no err
×
184
                        });
185
                } else {
186
                        console.log("☣️ [error] Filter doc injection issue (no doc) at " + file);
×
187
                }
188
        }
189

190
        handleDatabaseErrors(err, name, info) {
191
                if (err.toString().indexOf("the file already exists") !== -1) {
×
192
                        // silently fail, this is ok
193
                } else if (err.toString().indexOf("error happened") !== -1) {
×
194
                        console.log("🚫 [critical] Database connectivity issue. " + err.toString());
×
195
                        // give some time for DB to wake up until next try, also prevents too fast restarts...
196
                        setTimeout(() => {
×
197
                                process.exit(1);
×
198
                        }, 1000);
199
                } else {
200
                        console.log("🚫 [critical] Database " + name + " creation failed. " + err, " info:", info);
×
201
                        setTimeout(() => {
×
202
                                process.exit(2);
×
203
                        }, 1000);
204
                }
205
        }
206
};
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