@unvired/cordova-plugin-unvired-electron-db 0.0.46 → 0.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,75 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const xcode = require('xcode');
4
+
5
+ module.exports = function (context) {
6
+ const projectRoot = context.opts.projectRoot;
7
+ const iosPath = path.join(projectRoot, 'platforms', 'ios');
8
+
9
+ if (!fs.existsSync(iosPath)) {
10
+ console.warn('[c-flags] iOS platform folder not found, skipping patch.');
11
+ return;
12
+ }
13
+
14
+ const files = fs.readdirSync(iosPath);
15
+ const xcodeProjName = files.find(file => file.endsWith('.xcodeproj'));
16
+ if (!xcodeProjName) {
17
+ console.warn('[c-flags] xcodeproj not found, skipping patch.');
18
+ return;
19
+ }
20
+
21
+ const projectPath = path.join(iosPath, xcodeProjName, 'project.pbxproj');
22
+ const myProj = xcode.project(projectPath);
23
+
24
+ myProj.parseSync();
25
+
26
+ const cFlags = [
27
+ '-DNDEBUG',
28
+ '-DSQLCIPHER_CRYPTO_CC',
29
+ '-DSQLITE_HAS_CODEC',
30
+ '-DSQLITE_TEMP_STORE=2',
31
+ '-DSQLITE_THREADSAFE=1'
32
+ ];
33
+
34
+ const buildConfigs = myProj.pbxXCBuildConfigurationSection();
35
+ let updated = false;
36
+
37
+ for (const key in buildConfigs) {
38
+ if (key.endsWith('_comment')) continue;
39
+ const config = buildConfigs[key];
40
+ if (config && config.buildSettings) {
41
+ let currentFlags = config.buildSettings.OTHER_CFLAGS || '';
42
+
43
+ // Normalize currentFlags to a string and strip quotes
44
+ if (typeof currentFlags === 'string') {
45
+ currentFlags = currentFlags.replace(/"/g, '');
46
+ } else if (Array.isArray(currentFlags)) {
47
+ currentFlags = currentFlags.map(f => typeof f === 'string' ? f.replace(/"/g, '') : f).join(' ');
48
+ }
49
+
50
+ // Convert current flags to an array of tokens
51
+ let flagTokens = currentFlags.split(/\s+/).filter(t => t.length > 0);
52
+
53
+ // Make sure $(inherited) is the first flag if not already present
54
+ if (!flagTokens.includes('$(inherited)')) {
55
+ flagTokens.unshift('$(inherited)');
56
+ }
57
+
58
+ // Add our flags if they are not already present
59
+ cFlags.forEach(flag => {
60
+ if (!flagTokens.includes(flag)) {
61
+ flagTokens.push(flag);
62
+ }
63
+ });
64
+
65
+ // Join back as a single string wrapped in quotes
66
+ config.buildSettings.OTHER_CFLAGS = `"${flagTokens.join(' ')}"`;
67
+ updated = true;
68
+ }
69
+ }
70
+
71
+ if (updated) {
72
+ fs.writeFileSync(projectPath, myProj.writeSync());
73
+ console.log('✅ programmatically added SQLCipher C Flags to all configurations.');
74
+ }
75
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unvired/cordova-plugin-unvired-electron-db",
3
3
  "displayName": "Unvired DB",
4
- "version": "0.0.46",
4
+ "version": "0.0.48",
5
5
  "description": "Unvired DB Native Support for Cordova",
6
6
  "scripts": {},
7
7
  "keywords": [
@@ -26,7 +26,6 @@
26
26
  "url": "http://unvired.com"
27
27
  }
28
28
  ],
29
- "devDependencies": {},
30
29
  "cordova": {
31
30
  "platforms": [
32
31
  "electron",
@@ -37,5 +36,8 @@
37
36
  },
38
37
  "publishConfig": {
39
38
  "access": "public"
39
+ },
40
+ "dependencies": {
41
+ "better-sqlite3-multiple-ciphers": "^12.9.0"
40
42
  }
41
- }
43
+ }
package/plugin.xml CHANGED
@@ -1,7 +1,7 @@
1
1
  <?xml version='1.0' encoding='utf-8'?>
2
2
  <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
3
3
  id="@unvired/cordova-plugin-unvired-electron-db"
4
- version="0.0.46"
4
+ version="0.0.48"
5
5
  xmlns:android="http://schemas.android.com/apk/res/android">
6
6
  <name>Unvired DB</name>
7
7
  <description>Unvired DB Native Support for Cordova</description>
@@ -21,7 +21,9 @@
21
21
  </config-file>
22
22
  <source-file src="src/ios/UnviredDB.h" />
23
23
  <source-file src="src/ios/UnviredDB.m" />
24
- <framework src="libsqlite3.tbd" />
24
+ <source-file src="src/ios/sqlite3.h" />
25
+ <source-file src="src/ios/sqlite3.c" />
26
+ <hook type="after_prepare" src="hooks/update-c-flags.js"/>
25
27
  </platform>
26
28
  <!-- android -->
27
29
  <platform name="android">
@@ -1,6 +1,7 @@
1
1
  package com.unvired.dbplugin;
2
2
 
3
3
  import android.content.Context;
4
+ import android.content.SharedPreferences;
4
5
  import net.zetetic.database.sqlcipher.SQLiteDatabase;
5
6
  import android.database.sqlite.SQLiteException;
6
7
  import android.util.Log;
@@ -12,6 +13,10 @@ import org.json.JSONException;
12
13
  import org.json.JSONObject;
13
14
 
14
15
  import java.io.File;
16
+ import java.io.IOException;
17
+ import java.nio.file.Files;
18
+ import java.nio.file.StandardCopyOption;
19
+ import java.util.UUID;
15
20
 
16
21
  public class DBPlugin extends CordovaPlugin {
17
22
  private static final String TAG = "DBPlugin";
@@ -50,6 +55,10 @@ public class DBPlugin extends CordovaPlugin {
50
55
  return true;
51
56
  case "deleteUserData":
52
57
  deleteUserData(args, callbackContext);
58
+ return true;
59
+ case "getEncryptionKey":
60
+ getEncryptionKey(callbackContext);
61
+ return true;
53
62
  default:
54
63
  callbackContext.error("Invalid action: " + action);
55
64
  return false;
@@ -61,9 +70,46 @@ public class DBPlugin extends CordovaPlugin {
61
70
  }
62
71
  }
63
72
 
73
+ /**
74
+ * Checks if the database file at the given path is plaintext (unencrypted).
75
+ * Attempts to open the database without a key and run a simple query.
76
+ * @param dbPath Path to the database file.
77
+ * @return true if the database is plaintext, false if encrypted or unreadable.
78
+ */
79
+ private boolean isPlaintextDatabase(String dbPath) {
80
+ File f = new File(dbPath);
81
+ if (!f.exists()) return false;
82
+ SQLiteDatabase testDb = null;
83
+ try {
84
+ testDb = SQLiteDatabase.openOrCreateDatabase(dbPath, "", null, null, null);
85
+ testDb.rawQuery("SELECT count(*) FROM sqlite_master;", null).close();
86
+ return true;
87
+ } catch (Exception e) {
88
+ return false;
89
+ } finally {
90
+ if (testDb != null) { try { testDb.close(); } catch (Exception ignored) {} }
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Opens (or creates) a database with optional encryption.
96
+ * @param dbPath Path to the database file.
97
+ * @param encryptionKey Encryption key (empty string = no encryption).
98
+ * @return Opened SQLiteDatabase instance.
99
+ */
100
+ private SQLiteDatabase openDatabase(String dbPath, String encryptionKey) throws Exception {
101
+ if (!encryptionKey.isEmpty() && isPlaintextDatabase(dbPath)) {
102
+ // Existing DB is not encrypted, ignore the key.
103
+ encryptionKey = "";
104
+ }
105
+ return SQLiteDatabase.openOrCreateDatabase(dbPath, encryptionKey, null, null, null);
106
+ }
107
+
64
108
  private void create(JSONArray args, CallbackContext callbackContext) throws JSONException {
65
109
  JSONObject params = args.getJSONObject(0);
66
110
  String userId = params.getString("userId");
111
+ // optString returns "" when key is missing or JSON null — no encryption if not provided
112
+ String encryptionKey = params.optString("encryptionKey", "");
67
113
 
68
114
  if (userId == null || userId.isEmpty()) {
69
115
  callbackContext.error("userId is required");
@@ -79,34 +125,22 @@ public class DBPlugin extends CordovaPlugin {
79
125
  try {
80
126
  System.loadLibrary("sqlcipher");
81
127
 
82
-
83
- // Create framework database// Create framework database
128
+ // Open (and migrate if needed) framework database
84
129
  fwDbPath = userPath + "/framework.db";
85
- fwDb = SQLiteDatabase.openOrCreateDatabase(fwDbPath, "", null, null, null);
86
-
87
- // Enable WAL
130
+ fwDb = openDatabase(fwDbPath, encryptionKey);
88
131
  fwDb.enableWriteAheadLogging();
89
-
90
- // PRAGMAs that are safe with execSQL
91
132
  fwDb.execSQL("PRAGMA read_uncommitted = 1;");
92
- // Only if you actually need FKs in this DB:
93
133
  fwDb.execSQL("PRAGMA foreign_keys = ON;");
94
-
95
- // Create app database
134
+
135
+ // Open (and migrate if needed) app database
96
136
  appDbPath = userPath + "/app.db";
97
- appDb = SQLiteDatabase.openOrCreateDatabase(appDbPath, "", null, null, null);
98
-
99
- // Enable WAL
137
+ appDb = openDatabase(appDbPath, encryptionKey);
100
138
  appDb.enableWriteAheadLogging();
101
-
102
- // PRAGMAs that are safe with execSQL
103
139
  appDb.execSQL("PRAGMA read_uncommitted = 1;");
104
- // Only if you actually need FKs in this DB:
105
140
  appDb.execSQL("PRAGMA foreign_keys = ON;");
106
141
 
107
-
108
142
  callbackContext.success("Databases created successfully");
109
- } catch (SQLiteException e) {
143
+ } catch (Exception e) {
110
144
  Log.e(TAG, "Error creating databases", e);
111
145
  callbackContext.error("Error creating databases: " + e.getMessage());
112
146
  }
@@ -326,4 +360,27 @@ public class DBPlugin extends CordovaPlugin {
326
360
  }
327
361
  return dir.delete();
328
362
  }
363
+
364
+ private void getEncryptionKey(CallbackContext callbackContext) {
365
+ cordova.getThreadPool().execute(new Runnable() {
366
+ @Override
367
+ public void run() {
368
+ try {
369
+ Context context = cordova.getActivity().getApplicationContext();
370
+ SharedPreferences prefs = context.getSharedPreferences("UnviredDBPrefs", Context.MODE_PRIVATE);
371
+ String key = prefs.getString("unvired_db_encryption_key", null);
372
+ if (key == null) {
373
+ key = UUID.randomUUID().toString();
374
+ SharedPreferences.Editor editor = prefs.edit();
375
+ editor.putString("unvired_db_encryption_key", key);
376
+ editor.apply();
377
+ }
378
+ callbackContext.success(key);
379
+ } catch (Exception e) {
380
+ Log.e(TAG, "Error getting encryption key", e);
381
+ callbackContext.error("Error getting encryption key: " + e.getMessage());
382
+ }
383
+ }
384
+ });
385
+ }
329
386
  }
@@ -6,15 +6,15 @@ const DBType = {
6
6
  }
7
7
 
8
8
  module.exports.create = async function (sucessCallback, errorCallback, options) {
9
- const userId = options[0].userId
10
- const appName = options[0].appName
9
+ const userId = options[0].userId;
10
+ const encryptionKey = (options[0].encryptionKey !== undefined && options[0].encryptionKey !== null && options[0].encryptionKey !== "") ? options[0].encryptionKey : "";
11
11
 
12
12
  if (userId == undefined || userId == null || userId == "") {
13
13
  errorCallback("userId is required")
14
14
  return;
15
15
  }
16
16
  try {
17
- let dbCreationResponse = await webDb.initialize(userId);
17
+ let dbCreationResponse = await webDb.initialize(userId, encryptionKey);
18
18
  sucessCallback(dbCreationResponse);
19
19
  }
20
20
  catch (err) {
@@ -79,18 +79,22 @@ module.exports.close = async function (sucessCallback, errorCallback, options) {
79
79
  };
80
80
 
81
81
  module.exports.saveWebDB = async function(successCallback, errorCallback, options) {
82
- const userId = options[0].userId
82
+ const userId = options[0].userId;
83
83
  if (userId == undefined || userId == null || userId == "") {
84
84
  errorCallback("userId is required")
85
85
  return;
86
86
 
87
87
  }
88
88
  if (window.unvired_db_worker) {
89
- await window.unvired_db_worker.saveWebDb(userId + "_fw_db", webDb.fwDb.export(), "fwData");
90
- await window.unvired_db_worker.saveWebDb(userId + "_app_db", webDb.appDb.export(), "appData");
89
+ const fwData = webDb.fwDb.export();
90
+ const appData = webDb.appDb.export();
91
+ await window.unvired_db_worker.saveWebDb(userId + "_fw_db", fwData, "fwData");
92
+ await window.unvired_db_worker.saveWebDb(userId + "_app_db", appData, "appData");
93
+ successCallback();
91
94
  }
92
95
  else {
93
96
  console.error('Could not save the data in indexedDB because the script: unvired_db_worker is not loaded. Please make sure that this script is loaded in your application.')
97
+ errorCallback('unvired_db_worker is not loaded');
94
98
  }
95
99
  }
96
100
 
@@ -153,6 +157,10 @@ module.exports.deleteUserData = async function (successCallback, errorCallback,
153
157
  }
154
158
  };
155
159
 
160
+ module.exports.getEncryptionKey = async function (successCallback, errorCallback, options) {
161
+ successCallback("");
162
+ };
163
+
156
164
  //#endregion
157
165
 
158
166
 
@@ -161,16 +169,16 @@ module.exports.deleteUserData = async function (successCallback, errorCallback,
161
169
  var webDb = /** @class */ (function () {
162
170
  function webDb() { }
163
171
 
164
- webDb.initialize = async function (userId) {
172
+ webDb.initialize = async function (userId, encryptionKey) {
165
173
  var initSqlJs = window.initSqlJs;
166
174
  var config = {
167
175
  locateFile: filename => "assets/js/sql-wasm.wasm",
168
- // INITIAL_MEMORY: 268435456 // 256MB
169
176
  };
170
177
  try {
171
178
  let SQL = await initSqlJs(config);
172
179
  if (webDb.fwDb == null) {
173
- let data = await webDb.getDataFromIndexedDB(userId + "_fw_db", "fwData");
180
+ let rawData = await webDb.getDataFromIndexedDB(userId + "_fw_db", "fwData");
181
+ let data = rawData;
174
182
  if (data) {
175
183
  console.log("Initializing fwDb with data size: " + (data.byteLength || data.length));
176
184
  webDb.fwDb = new SQL.Database(data);
@@ -182,7 +190,8 @@ var webDb = /** @class */ (function () {
182
190
  webDb.fwDb.run("PRAGMA read_uncommitted = 1;");
183
191
  }
184
192
  if (webDb.appDb == null) {
185
- let data = await webDb.getDataFromIndexedDB(userId + "_app_db", "appData");
193
+ let rawData = await webDb.getDataFromIndexedDB(userId + "_app_db", "appData");
194
+ let data = rawData;
186
195
  if (data) {
187
196
  console.log("Initializing appDb with data size: " + (data.byteLength || data.length));
188
197
  webDb.appDb = new SQL.Database(data);
@@ -246,6 +255,8 @@ var webDb = /** @class */ (function () {
246
255
  });
247
256
  };
248
257
 
258
+
259
+
249
260
  // Parse SQL query and convert to prepared statement with bound values
250
261
  webDb.parseQueryToPrepared = function (query) {
251
262
  try {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cordova-plugin-unvired-electron-db",
3
- "version": "0.0.46",
3
+ "version": "0.0.48",
4
4
  "description": "Unvired DB Native Support for Cordova",
5
5
  "main": "unvired-db-proxy.js",
6
6
  "author": "Unvired Inc",
@@ -12,6 +12,6 @@
12
12
  "serviceName": "UnviredDB"
13
13
  },
14
14
  "dependencies": {
15
- "sqlite3": "5.1.6"
15
+ "better-sqlite3-multiple-ciphers": "^12.9.0"
16
16
  }
17
- }
17
+ }