@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.
- package/hooks/update-c-flags.js +75 -0
- package/package.json +5 -3
- package/plugin.xml +4 -2
- package/src/android/DBPlugin.java +75 -18
- package/src/browser/DbPluginProxy.js +21 -10
- package/src/electron/package.json +3 -3
- package/src/electron/unvired-db-proxy.js +297 -159
- package/src/ios/UnviredDB.h +1 -0
- package/src/ios/UnviredDB.m +75 -23
- package/src/ios/sqlite3.c +262970 -0
- package/src/ios/sqlite3.h +13485 -0
- package/www/unvired-db.js +5 -1
|
@@ -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.
|
|
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.
|
|
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
|
-
<
|
|
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 =
|
|
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
|
-
//
|
|
134
|
+
|
|
135
|
+
// Open (and migrate if needed) app database
|
|
96
136
|
appDbPath = userPath + "/app.db";
|
|
97
|
-
appDb =
|
|
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 (
|
|
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
|
|
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
|
-
|
|
90
|
-
|
|
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
|
|
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
|
|
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.
|
|
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": "
|
|
15
|
+
"better-sqlite3-multiple-ciphers": "^12.9.0"
|
|
16
16
|
}
|
|
17
|
-
}
|
|
17
|
+
}
|