shogun-core 2.0.3 → 3.0.0
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/README.md +150 -19
- package/dist/browser/shogun-core.js +3241 -1286
- package/dist/browser/shogun-core.js.map +1 -1
- package/dist/config/simplified-config.js +230 -0
- package/dist/core.js +49 -571
- package/dist/gundb/db.js +466 -237
- package/dist/gundb/improved-types.js +4 -0
- package/dist/gundb/index.js +4 -0
- package/dist/gundb/simple-api.js +438 -0
- package/dist/index.js +8 -2
- package/dist/managers/AuthManager.js +225 -0
- package/dist/managers/CoreInitializer.js +227 -0
- package/dist/managers/EventManager.js +67 -0
- package/dist/managers/PluginManager.js +296 -0
- package/dist/migration-test.js +91 -0
- package/dist/plugins/nostr/nostrConnectorPlugin.js +1 -1
- package/dist/plugins/oauth/oauthPlugin.js +1 -1
- package/dist/plugins/webauthn/webauthnPlugin.js +1 -1
- package/dist/types/config/simplified-config.d.ts +114 -0
- package/dist/types/core.d.ts +13 -46
- package/dist/types/gundb/db.d.ts +92 -14
- package/dist/types/gundb/improved-types.d.ts +123 -0
- package/dist/types/gundb/index.d.ts +2 -0
- package/dist/types/gundb/rxjs.d.ts +3 -3
- package/dist/types/gundb/simple-api.d.ts +90 -0
- package/dist/types/index.d.ts +6 -4
- package/dist/types/{types → interfaces}/shogun.d.ts +8 -10
- package/dist/types/managers/AuthManager.d.ts +69 -0
- package/dist/types/managers/CoreInitializer.d.ts +40 -0
- package/dist/types/managers/EventManager.d.ts +49 -0
- package/dist/types/managers/PluginManager.d.ts +145 -0
- package/dist/types/migration-test.d.ts +16 -0
- package/dist/types/plugins/base.d.ts +2 -2
- package/dist/types/plugins/index.d.ts +1 -1
- package/dist/types/plugins/nostr/nostrConnectorPlugin.d.ts +1 -1
- package/dist/types/plugins/nostr/types.d.ts +2 -2
- package/dist/types/plugins/oauth/oauthPlugin.d.ts +1 -1
- package/dist/types/plugins/oauth/types.d.ts +2 -2
- package/dist/types/plugins/web3/types.d.ts +2 -2
- package/dist/types/plugins/web3/web3ConnectorPlugin.d.ts +1 -1
- package/dist/types/plugins/webauthn/types.d.ts +2 -2
- package/dist/types/plugins/webauthn/webauthnPlugin.d.ts +1 -1
- package/dist/types/utils/errorHandler.d.ts +1 -1
- package/package.json +1 -1
- /package/dist/{types → interfaces}/common.js +0 -0
- /package/dist/{types → interfaces}/events.js +0 -0
- /package/dist/{types → interfaces}/plugin.js +0 -0
- /package/dist/{types → interfaces}/shogun.js +0 -0
- /package/dist/types/{types → interfaces}/common.d.ts +0 -0
- /package/dist/types/{types → interfaces}/events.d.ts +0 -0
- /package/dist/types/{types → interfaces}/plugin.d.ts +0 -0
package/dist/gundb/db.js
CHANGED
|
@@ -12,8 +12,8 @@ import "gun/lib/radisk";
|
|
|
12
12
|
import "gun/lib/store";
|
|
13
13
|
import "gun/lib/rindexed";
|
|
14
14
|
import "gun/lib/webrtc";
|
|
15
|
-
import "gun/lib/
|
|
16
|
-
import "gun/lib/
|
|
15
|
+
import "gun/lib/wire";
|
|
16
|
+
import "gun/lib/axe";
|
|
17
17
|
import { restrictedPut } from "./restricted-put";
|
|
18
18
|
import derive from "./derive";
|
|
19
19
|
import { ErrorHandler, ErrorType } from "../utils/errorHandler";
|
|
@@ -335,11 +335,8 @@ class DataBase {
|
|
|
335
335
|
* @returns Promise resolving to the data
|
|
336
336
|
*/
|
|
337
337
|
async getData(path) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
resolve(data);
|
|
341
|
-
});
|
|
342
|
-
});
|
|
338
|
+
const node = await this.navigateToPath(this.gun, path).then();
|
|
339
|
+
return node;
|
|
343
340
|
}
|
|
344
341
|
/**
|
|
345
342
|
* Puts data at the specified path
|
|
@@ -348,14 +345,11 @@ class DataBase {
|
|
|
348
345
|
* @returns Promise resolving to operation result
|
|
349
346
|
*/
|
|
350
347
|
async put(path, data) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
resolve(result);
|
|
357
|
-
});
|
|
358
|
-
});
|
|
348
|
+
const ack = await this.navigateToPath(this.gun, path).put(data).then();
|
|
349
|
+
const result = ack.err
|
|
350
|
+
? { success: false, error: ack.err }
|
|
351
|
+
: { success: true };
|
|
352
|
+
return result;
|
|
359
353
|
}
|
|
360
354
|
/**
|
|
361
355
|
* Sets data at the specified path
|
|
@@ -364,14 +358,11 @@ class DataBase {
|
|
|
364
358
|
* @returns Promise resolving to operation result
|
|
365
359
|
*/
|
|
366
360
|
async set(path, data) {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
resolve(result);
|
|
373
|
-
});
|
|
374
|
-
});
|
|
361
|
+
const ack = await this.navigateToPath(this.gun, path).set(data).then();
|
|
362
|
+
const result = ack.err
|
|
363
|
+
? { success: false, error: ack.err }
|
|
364
|
+
: { success: true };
|
|
365
|
+
return result;
|
|
375
366
|
}
|
|
376
367
|
/**
|
|
377
368
|
* Removes data at the specified path
|
|
@@ -379,14 +370,11 @@ class DataBase {
|
|
|
379
370
|
* @returns Promise resolving to operation result
|
|
380
371
|
*/
|
|
381
372
|
async remove(path) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
resolve(result);
|
|
388
|
-
});
|
|
389
|
-
});
|
|
373
|
+
const ack = await this.navigateToPath(this.gun, path).put(null).then();
|
|
374
|
+
const result = ack.err
|
|
375
|
+
? { success: false, error: ack.err }
|
|
376
|
+
: { success: true };
|
|
377
|
+
return result;
|
|
390
378
|
}
|
|
391
379
|
/**
|
|
392
380
|
* Checks if a user is currently logged in
|
|
@@ -601,38 +589,26 @@ class DataBase {
|
|
|
601
589
|
}
|
|
602
590
|
// If using pair authentication, skip password validation
|
|
603
591
|
if (pair) {
|
|
592
|
+
if (!pair.pub ||
|
|
593
|
+
!pair.priv ||
|
|
594
|
+
!pair.epub ||
|
|
595
|
+
!pair.epriv) {
|
|
596
|
+
return {
|
|
597
|
+
valid: false,
|
|
598
|
+
error: "Invalid pair provided",
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
if (!pair.pub || !pair.priv || !pair.epub || !pair.epriv) {
|
|
602
|
+
return {
|
|
603
|
+
valid: false,
|
|
604
|
+
error: "Invalid pair provided",
|
|
605
|
+
};
|
|
606
|
+
}
|
|
604
607
|
return { valid: true };
|
|
605
608
|
}
|
|
606
609
|
// Validate password strength
|
|
607
610
|
return this.validatePasswordStrength(password);
|
|
608
611
|
}
|
|
609
|
-
/**
|
|
610
|
-
* Checks if user exists by attempting authentication
|
|
611
|
-
*/
|
|
612
|
-
async checkUserExistence(username, password, pair) {
|
|
613
|
-
return new Promise((resolve) => {
|
|
614
|
-
if (pair) {
|
|
615
|
-
this.gun.user().auth(pair, (ack) => {
|
|
616
|
-
if (ack.err) {
|
|
617
|
-
resolve({ exists: false, error: ack.err });
|
|
618
|
-
}
|
|
619
|
-
else {
|
|
620
|
-
resolve({ exists: true, userPub: this.gun.user().is?.pub });
|
|
621
|
-
}
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
this.gun.user().auth(username, password, (ack) => {
|
|
626
|
-
if (ack.err) {
|
|
627
|
-
resolve({ exists: false, error: ack.err });
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
resolve({ exists: true, userPub: this.gun.user().is?.pub });
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
612
|
/**
|
|
637
613
|
* Creates a new user in Gun
|
|
638
614
|
*/
|
|
@@ -786,7 +762,6 @@ class DataBase {
|
|
|
786
762
|
if (!validation.valid) {
|
|
787
763
|
return { success: false, error: validation.error };
|
|
788
764
|
}
|
|
789
|
-
// Create new user - use different method based on authentication type
|
|
790
765
|
let createResult;
|
|
791
766
|
if (pair) {
|
|
792
767
|
// For Web3/plugin authentication, use pair-based creation
|
|
@@ -911,105 +886,26 @@ class DataBase {
|
|
|
911
886
|
throw new Error("Username cannot be empty");
|
|
912
887
|
}
|
|
913
888
|
console.log(`Setting up user profile for ${normalizedUsername} with userPub: ${userPub}`);
|
|
914
|
-
const existingUser = await
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
});
|
|
923
|
-
});
|
|
924
|
-
// Check if user already has metadata to avoid overwriting
|
|
925
|
-
if (!existingUser) {
|
|
926
|
-
try {
|
|
927
|
-
await new Promise((resolve, reject) => {
|
|
928
|
-
const timeout = setTimeout(() => {
|
|
929
|
-
console.warn(`⚠️ Timeout saving user metadata for ${userPub} - continuing`);
|
|
930
|
-
resolve({ ok: 0 }); // Resolve with mock success to continue
|
|
931
|
-
}, 5000); // 5 second timeout
|
|
932
|
-
this.gun
|
|
933
|
-
.get(userPub)
|
|
934
|
-
.put({ username: normalizedUsername }, (ack) => {
|
|
935
|
-
clearTimeout(timeout);
|
|
936
|
-
if (ack.err) {
|
|
937
|
-
console.error(`Error saving user metadata: ${ack.err}`);
|
|
938
|
-
reject(ack.err);
|
|
939
|
-
}
|
|
940
|
-
else {
|
|
941
|
-
// User metadata saved successfully
|
|
942
|
-
resolve(ack);
|
|
943
|
-
}
|
|
944
|
-
});
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
catch (metadataError) {
|
|
948
|
-
console.error(`Error saving user metadata: ${metadataError}`);
|
|
949
|
-
// Don't throw here, continue with other operations
|
|
950
|
-
}
|
|
951
|
-
// Create username mapping
|
|
952
|
-
try {
|
|
953
|
-
await new Promise((resolve, reject) => {
|
|
954
|
-
const timeout = setTimeout(() => {
|
|
955
|
-
console.warn(`⚠️ Timeout creating username mapping for ${normalizedUsername} - continuing`);
|
|
956
|
-
resolve({ ok: 0 }); // Resolve with mock success to continue
|
|
957
|
-
}, 5000); // 5 second timeout
|
|
958
|
-
this.node
|
|
959
|
-
.get("usernames")
|
|
960
|
-
.get(normalizedUsername)
|
|
961
|
-
.put(userPub, (ack) => {
|
|
962
|
-
clearTimeout(timeout);
|
|
963
|
-
if (ack.err) {
|
|
964
|
-
reject(ack.err);
|
|
965
|
-
}
|
|
966
|
-
else {
|
|
967
|
-
// Username mapping created successfully
|
|
968
|
-
resolve(ack);
|
|
969
|
-
}
|
|
970
|
-
});
|
|
971
|
-
});
|
|
972
|
-
}
|
|
973
|
-
catch (mappingError) {
|
|
974
|
-
console.error(`Error creating username mapping: ${mappingError}`);
|
|
975
|
-
// Don't throw here, continue with other operations
|
|
976
|
-
}
|
|
977
|
-
// Add user to users collection
|
|
978
|
-
try {
|
|
979
|
-
await new Promise((resolve, reject) => {
|
|
980
|
-
const timeout = setTimeout(() => {
|
|
981
|
-
console.warn(`⚠️ Timeout adding user to collection for ${userPub} - continuing`);
|
|
982
|
-
resolve({ ok: 0 }); // Resolve with mock success to continue
|
|
983
|
-
}, 5000); // 5 second timeout
|
|
984
|
-
this.node.get("users").set(this.gun.get(userPub), (ack) => {
|
|
985
|
-
clearTimeout(timeout);
|
|
986
|
-
if (ack.err) {
|
|
987
|
-
reject(ack.err);
|
|
988
|
-
}
|
|
989
|
-
else {
|
|
990
|
-
// User added to collection successfully
|
|
991
|
-
resolve(ack);
|
|
992
|
-
}
|
|
993
|
-
});
|
|
994
|
-
});
|
|
995
|
-
}
|
|
996
|
-
catch (collectionError) {
|
|
997
|
-
console.error(`Error adding user to collection: ${collectionError}`);
|
|
998
|
-
// Don't throw here, continue with other operations
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
889
|
+
const existingUser = await this.gun.get(userPub).once().then();
|
|
890
|
+
const isNewUser = !existingUser || !existingUser.username;
|
|
891
|
+
// Get user's encryption public key (epub) for comprehensive tracking
|
|
892
|
+
const userInstance = this.gun.user();
|
|
893
|
+
const userSea = userInstance?._?.sea;
|
|
894
|
+
const epub = userSea?.epub || null;
|
|
895
|
+
// Enhanced user tracking system
|
|
896
|
+
await this.setupComprehensiveUserTracking(normalizedUsername, userPub, epub, isNewUser);
|
|
1001
897
|
return {
|
|
1002
898
|
success: true,
|
|
1003
899
|
userPub: userPub,
|
|
1004
900
|
username: normalizedUsername,
|
|
1005
|
-
isNewUser:
|
|
901
|
+
isNewUser: isNewUser,
|
|
1006
902
|
// Get the SEA pair from the user object
|
|
1007
|
-
sea:
|
|
903
|
+
sea: userSea
|
|
1008
904
|
? {
|
|
1009
|
-
pub:
|
|
1010
|
-
priv:
|
|
1011
|
-
epub:
|
|
1012
|
-
epriv:
|
|
905
|
+
pub: userSea.pub,
|
|
906
|
+
priv: userSea.priv,
|
|
907
|
+
epub: userSea.epub,
|
|
908
|
+
epriv: userSea.epriv,
|
|
1013
909
|
}
|
|
1014
910
|
: undefined,
|
|
1015
911
|
};
|
|
@@ -1022,6 +918,365 @@ class DataBase {
|
|
|
1022
918
|
};
|
|
1023
919
|
}
|
|
1024
920
|
}
|
|
921
|
+
/**
|
|
922
|
+
* Sets up comprehensive user tracking system for agile user lookup
|
|
923
|
+
* Creates multiple indexes for efficient user discovery
|
|
924
|
+
*/
|
|
925
|
+
async setupComprehensiveUserTracking(username, userPub, epub, isNewUser) {
|
|
926
|
+
try {
|
|
927
|
+
// 1. Create alias index: ~@alias -> userPub (for GunDB compatibility)
|
|
928
|
+
await this.createAliasIndex(username, userPub);
|
|
929
|
+
// 2. Create username mapping: usernames/alias -> userPub
|
|
930
|
+
await this.createUsernameMapping(username, userPub);
|
|
931
|
+
// 3. Create user registry: users/userPub -> user data
|
|
932
|
+
await this.createUserRegistry(username, userPub, epub);
|
|
933
|
+
// 4. Create reverse lookup: userPub -> alias
|
|
934
|
+
await this.createReverseLookup(username, userPub);
|
|
935
|
+
// 5. Create epub index: epubKeys/epub -> userPub (for encryption lookups)
|
|
936
|
+
if (epub) {
|
|
937
|
+
await this.createEpubIndex(epub, userPub);
|
|
938
|
+
}
|
|
939
|
+
// 6. Create user metadata in user's own node
|
|
940
|
+
await this.createUserMetadata(username, userPub, epub);
|
|
941
|
+
console.log(`Comprehensive user tracking setup completed for ${username}`);
|
|
942
|
+
}
|
|
943
|
+
catch (error) {
|
|
944
|
+
console.error(`Error in comprehensive user tracking setup: ${error}`);
|
|
945
|
+
// Don't throw - continue with other operations
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Creates alias index following GunDB pattern: ~@alias -> userPub
|
|
950
|
+
*/
|
|
951
|
+
async createAliasIndex(username, userPub) {
|
|
952
|
+
try {
|
|
953
|
+
const aliasNode = this.gun.get(`~@${username}`);
|
|
954
|
+
const ack = await aliasNode
|
|
955
|
+
.put({
|
|
956
|
+
"~pubKeyOfUser": this.gun.get(userPub),
|
|
957
|
+
})
|
|
958
|
+
.then();
|
|
959
|
+
if (ack.err) {
|
|
960
|
+
console.error(`Error creating alias index: ${ack.err}`);
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
console.log(`Alias index created: ~@${username} -> ${userPub}`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
catch (error) {
|
|
967
|
+
console.error(`Error creating alias index: ${error}`);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Creates username mapping: usernames/alias -> userPub
|
|
972
|
+
*/
|
|
973
|
+
async createUsernameMapping(username, userPub) {
|
|
974
|
+
try {
|
|
975
|
+
const ack = await this.node
|
|
976
|
+
.get("usernames")
|
|
977
|
+
.get(username)
|
|
978
|
+
.put(userPub)
|
|
979
|
+
.then();
|
|
980
|
+
if (ack.err) {
|
|
981
|
+
console.error(`Error creating username mapping: ${ack.err}`);
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
console.log(`Username mapping created: ${username} -> ${userPub}`);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
catch (error) {
|
|
988
|
+
console.error(`Error creating username mapping: ${error}`);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Creates user registry: users/userPub -> user data
|
|
993
|
+
*/
|
|
994
|
+
async createUserRegistry(username, userPub, epub) {
|
|
995
|
+
try {
|
|
996
|
+
const userData = {
|
|
997
|
+
username: username,
|
|
998
|
+
userPub: userPub,
|
|
999
|
+
epub: epub,
|
|
1000
|
+
registeredAt: Date.now(),
|
|
1001
|
+
lastSeen: Date.now(),
|
|
1002
|
+
};
|
|
1003
|
+
const ack = await this.node
|
|
1004
|
+
.get("users")
|
|
1005
|
+
.get(userPub)
|
|
1006
|
+
.put(userData)
|
|
1007
|
+
.then();
|
|
1008
|
+
if (ack.err) {
|
|
1009
|
+
console.error(`Error creating user registry: ${ack.err}`);
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
console.log(`User registry created: ${userPub}`);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
catch (error) {
|
|
1016
|
+
console.error(`Error creating user registry: ${error}`);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Creates reverse lookup: userPub -> alias
|
|
1021
|
+
*/
|
|
1022
|
+
async createReverseLookup(username, userPub) {
|
|
1023
|
+
try {
|
|
1024
|
+
const ack = await this.node
|
|
1025
|
+
.get("userAliases")
|
|
1026
|
+
.get(userPub)
|
|
1027
|
+
.put(username)
|
|
1028
|
+
.then();
|
|
1029
|
+
if (ack.err) {
|
|
1030
|
+
console.error(`Error creating reverse lookup: ${ack.err}`);
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
console.log(`Reverse lookup created: ${userPub} -> ${username}`);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
catch (error) {
|
|
1037
|
+
console.error(`Error creating reverse lookup: ${error}`);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Creates epub index: epubKeys/epub -> userPub
|
|
1042
|
+
*/
|
|
1043
|
+
async createEpubIndex(epub, userPub) {
|
|
1044
|
+
try {
|
|
1045
|
+
const ack = await this.node.get("epubKeys").get(epub).put(userPub).then();
|
|
1046
|
+
if (ack.err) {
|
|
1047
|
+
console.error(`Error creating epub index: ${ack.err}`);
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
console.log(`Epub index created: ${epub} -> ${userPub}`);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
catch (error) {
|
|
1054
|
+
console.error(`Error creating epub index: ${error}`);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Creates user metadata in user's own node
|
|
1059
|
+
*/
|
|
1060
|
+
async createUserMetadata(username, userPub, epub) {
|
|
1061
|
+
try {
|
|
1062
|
+
const userMetadata = {
|
|
1063
|
+
username: username,
|
|
1064
|
+
epub: epub,
|
|
1065
|
+
registeredAt: Date.now(),
|
|
1066
|
+
lastSeen: Date.now(),
|
|
1067
|
+
};
|
|
1068
|
+
const ack = await this.gun.get(userPub).put(userMetadata).then();
|
|
1069
|
+
if (ack.err) {
|
|
1070
|
+
console.error(`Error creating user metadata: ${ack.err}`);
|
|
1071
|
+
}
|
|
1072
|
+
else {
|
|
1073
|
+
console.log(`User metadata created for ${userPub}`);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
catch (error) {
|
|
1077
|
+
console.error(`Error creating user metadata: ${error}`);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Gets user information by alias using the comprehensive tracking system
|
|
1082
|
+
* @param alias Username/alias to lookup
|
|
1083
|
+
* @returns Promise resolving to user information or null if not found
|
|
1084
|
+
*/
|
|
1085
|
+
async getUserByAlias(alias) {
|
|
1086
|
+
try {
|
|
1087
|
+
const normalizedAlias = alias.trim().toLowerCase();
|
|
1088
|
+
if (!normalizedAlias) {
|
|
1089
|
+
return null;
|
|
1090
|
+
}
|
|
1091
|
+
// Method 1: Try GunDB standard alias lookup (~@alias)
|
|
1092
|
+
try {
|
|
1093
|
+
const aliasData = await this.gun
|
|
1094
|
+
.get(`~@${normalizedAlias}`)
|
|
1095
|
+
.once()
|
|
1096
|
+
.then();
|
|
1097
|
+
if (aliasData && aliasData["~pubKeyOfUser"]) {
|
|
1098
|
+
const userPub = aliasData["~pubKeyOfUser"]["#"] || aliasData["~pubKeyOfUser"];
|
|
1099
|
+
if (userPub) {
|
|
1100
|
+
const userData = await this.getUserDataByPub(userPub);
|
|
1101
|
+
if (userData) {
|
|
1102
|
+
return userData;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
catch (error) {
|
|
1108
|
+
console.log(`GunDB alias lookup failed for ${normalizedAlias}:`, error);
|
|
1109
|
+
}
|
|
1110
|
+
// Method 2: Try username mapping (usernames/alias -> userPub)
|
|
1111
|
+
try {
|
|
1112
|
+
const userPub = await this.node
|
|
1113
|
+
.get("usernames")
|
|
1114
|
+
.get(normalizedAlias)
|
|
1115
|
+
.once()
|
|
1116
|
+
.then();
|
|
1117
|
+
if (userPub) {
|
|
1118
|
+
const userData = await this.getUserDataByPub(userPub);
|
|
1119
|
+
if (userData) {
|
|
1120
|
+
return userData;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
catch (error) {
|
|
1125
|
+
console.log(`Username mapping lookup failed for ${normalizedAlias}:`, error);
|
|
1126
|
+
}
|
|
1127
|
+
return null;
|
|
1128
|
+
}
|
|
1129
|
+
catch (error) {
|
|
1130
|
+
console.error(`Error looking up user by alias ${alias}:`, error);
|
|
1131
|
+
return null;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Gets user information by public key
|
|
1136
|
+
* @param userPub User's public key
|
|
1137
|
+
* @returns Promise resolving to user information or null if not found
|
|
1138
|
+
*/
|
|
1139
|
+
async getUserDataByPub(userPub) {
|
|
1140
|
+
try {
|
|
1141
|
+
if (!userPub || typeof userPub !== "string") {
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
// Method 1: Try user registry (users/userPub -> user data)
|
|
1145
|
+
try {
|
|
1146
|
+
const userData = await this.node
|
|
1147
|
+
.get("users")
|
|
1148
|
+
.get(userPub)
|
|
1149
|
+
.once()
|
|
1150
|
+
.then();
|
|
1151
|
+
if (userData && userData.username) {
|
|
1152
|
+
return {
|
|
1153
|
+
userPub: userData.userPub || userPub,
|
|
1154
|
+
epub: userData.epub || null,
|
|
1155
|
+
username: userData.username,
|
|
1156
|
+
registeredAt: userData.registeredAt || 0,
|
|
1157
|
+
lastSeen: userData.lastSeen || 0,
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
catch (error) {
|
|
1162
|
+
console.log(`User registry lookup failed for ${userPub}:`, error);
|
|
1163
|
+
}
|
|
1164
|
+
// Method 2: Try user's own node
|
|
1165
|
+
try {
|
|
1166
|
+
const userNodeData = await this.gun.get(userPub).once().then();
|
|
1167
|
+
if (userNodeData && userNodeData.username) {
|
|
1168
|
+
return {
|
|
1169
|
+
userPub: userPub,
|
|
1170
|
+
epub: userNodeData.epub || null,
|
|
1171
|
+
username: userNodeData.username,
|
|
1172
|
+
registeredAt: userNodeData.registeredAt || 0,
|
|
1173
|
+
lastSeen: userNodeData.lastSeen || 0,
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
catch (error) {
|
|
1178
|
+
console.log(`User node lookup failed for ${userPub}:`, error);
|
|
1179
|
+
}
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
catch (error) {
|
|
1183
|
+
console.error(`Error looking up user data by pub ${userPub}:`, error);
|
|
1184
|
+
return null;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Gets user public key by encryption public key (epub)
|
|
1189
|
+
* @param epub User's encryption public key
|
|
1190
|
+
* @returns Promise resolving to user public key or null if not found
|
|
1191
|
+
*/
|
|
1192
|
+
async getUserPubByEpub(epub) {
|
|
1193
|
+
try {
|
|
1194
|
+
if (!epub || typeof epub !== "string") {
|
|
1195
|
+
return null;
|
|
1196
|
+
}
|
|
1197
|
+
const userPub = await this.node.get("epubKeys").get(epub).once().then();
|
|
1198
|
+
return userPub || null;
|
|
1199
|
+
}
|
|
1200
|
+
catch (error) {
|
|
1201
|
+
console.error(`Error looking up user pub by epub ${epub}:`, error);
|
|
1202
|
+
return null;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Gets user alias by public key
|
|
1207
|
+
* @param userPub User's public key
|
|
1208
|
+
* @returns Promise resolving to user alias or null if not found
|
|
1209
|
+
*/
|
|
1210
|
+
async getUserAliasByPub(userPub) {
|
|
1211
|
+
try {
|
|
1212
|
+
if (!userPub || typeof userPub !== "string") {
|
|
1213
|
+
return null;
|
|
1214
|
+
}
|
|
1215
|
+
const alias = await this.node
|
|
1216
|
+
.get("userAliases")
|
|
1217
|
+
.get(userPub)
|
|
1218
|
+
.once()
|
|
1219
|
+
.then();
|
|
1220
|
+
return alias || null;
|
|
1221
|
+
}
|
|
1222
|
+
catch (error) {
|
|
1223
|
+
console.error(`Error looking up user alias by pub ${userPub}:`, error);
|
|
1224
|
+
return null;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Gets all registered users (for admin purposes)
|
|
1229
|
+
* @returns Promise resolving to array of user information
|
|
1230
|
+
*/
|
|
1231
|
+
async getAllRegisteredUsers() {
|
|
1232
|
+
try {
|
|
1233
|
+
const users = [];
|
|
1234
|
+
// Get all users from the users registry
|
|
1235
|
+
const usersNode = this.node.get("users");
|
|
1236
|
+
// Note: This is a simplified approach. In a real implementation,
|
|
1237
|
+
// you might want to use Gun's map functionality or iterate through
|
|
1238
|
+
// known user public keys
|
|
1239
|
+
return users;
|
|
1240
|
+
}
|
|
1241
|
+
catch (error) {
|
|
1242
|
+
console.error(`Error getting all registered users:`, error);
|
|
1243
|
+
return [];
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Updates user's last seen timestamp
|
|
1248
|
+
* @param userPub User's public key
|
|
1249
|
+
*/
|
|
1250
|
+
async updateUserLastSeen(userPub) {
|
|
1251
|
+
try {
|
|
1252
|
+
if (!userPub || typeof userPub !== "string") {
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
const timestamp = Date.now();
|
|
1256
|
+
// Update in user registry
|
|
1257
|
+
try {
|
|
1258
|
+
await this.node
|
|
1259
|
+
.get("users")
|
|
1260
|
+
.get(userPub)
|
|
1261
|
+
.get("lastSeen")
|
|
1262
|
+
.put(timestamp)
|
|
1263
|
+
.then();
|
|
1264
|
+
}
|
|
1265
|
+
catch (error) {
|
|
1266
|
+
console.log(`Failed to update lastSeen in user registry:`, error);
|
|
1267
|
+
}
|
|
1268
|
+
// Update in user's own node
|
|
1269
|
+
try {
|
|
1270
|
+
await this.gun.get(userPub).get("lastSeen").put(timestamp).then();
|
|
1271
|
+
}
|
|
1272
|
+
catch (error) {
|
|
1273
|
+
console.log(`Failed to update lastSeen in user node:`, error);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
catch (error) {
|
|
1277
|
+
console.error(`Error updating user last seen for ${userPub}:`, error);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1025
1280
|
/**
|
|
1026
1281
|
* Performs authentication with Gun
|
|
1027
1282
|
*/
|
|
@@ -1107,6 +1362,14 @@ class DataBase {
|
|
|
1107
1362
|
console.error(`Post-auth error during login: ${postAuthError}`);
|
|
1108
1363
|
// Continue with login even if post-auth fails
|
|
1109
1364
|
}
|
|
1365
|
+
// Update user's last seen timestamp
|
|
1366
|
+
try {
|
|
1367
|
+
await this.updateUserLastSeen(userPub);
|
|
1368
|
+
}
|
|
1369
|
+
catch (lastSeenError) {
|
|
1370
|
+
console.error(`Error updating last seen: ${lastSeenError}`);
|
|
1371
|
+
// Continue with login even if last seen update fails
|
|
1372
|
+
}
|
|
1110
1373
|
// Save credentials for future sessions
|
|
1111
1374
|
try {
|
|
1112
1375
|
const userInfo = {
|
|
@@ -1278,20 +1541,14 @@ class DataBase {
|
|
|
1278
1541
|
questions: JSON.stringify(securityQuestions),
|
|
1279
1542
|
hint: encryptedHint,
|
|
1280
1543
|
};
|
|
1281
|
-
await
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
else {
|
|
1290
|
-
// console.log(`Security data saved to public graph for ${userPub}`);
|
|
1291
|
-
resolve();
|
|
1292
|
-
}
|
|
1293
|
-
});
|
|
1294
|
-
});
|
|
1544
|
+
const ack = await this.node.get(userPub)
|
|
1545
|
+
.get("security")
|
|
1546
|
+
.put(securityPayload)
|
|
1547
|
+
.then();
|
|
1548
|
+
if (ack.err) {
|
|
1549
|
+
console.error("Error saving security data to public graph:", ack.err);
|
|
1550
|
+
throw new Error(ack.err);
|
|
1551
|
+
}
|
|
1295
1552
|
return { success: true };
|
|
1296
1553
|
}
|
|
1297
1554
|
catch (error) {
|
|
@@ -1310,28 +1567,20 @@ class DataBase {
|
|
|
1310
1567
|
try {
|
|
1311
1568
|
// Find the user's data using direct lookup
|
|
1312
1569
|
const normalizedUsername = username.trim().toLowerCase();
|
|
1313
|
-
const userPub = await
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
resolve(data || null);
|
|
1319
|
-
});
|
|
1320
|
-
});
|
|
1570
|
+
const userPub = (await this.node
|
|
1571
|
+
.get("usernames")
|
|
1572
|
+
.get(normalizedUsername)
|
|
1573
|
+
.once()
|
|
1574
|
+
.then()) || null;
|
|
1321
1575
|
if (!userPub) {
|
|
1322
1576
|
return { success: false, error: "User not found" };
|
|
1323
1577
|
}
|
|
1324
1578
|
// console.log(`Found user public key for password recovery: ${userPub}`);
|
|
1325
1579
|
// Access the user's security data directly from their public key node
|
|
1326
|
-
const securityData = await
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
// data ? "found" : "not found",
|
|
1331
|
-
// );
|
|
1332
|
-
resolve(data);
|
|
1333
|
-
});
|
|
1334
|
-
});
|
|
1580
|
+
const securityData = await this.node.get(userPub)
|
|
1581
|
+
.get("security")
|
|
1582
|
+
.once()
|
|
1583
|
+
.then();
|
|
1335
1584
|
if (!securityData || !securityData.hint) {
|
|
1336
1585
|
return {
|
|
1337
1586
|
success: false,
|
|
@@ -1401,21 +1650,14 @@ class DataBase {
|
|
|
1401
1650
|
* @returns Promise that resolves when the data is saved
|
|
1402
1651
|
*/
|
|
1403
1652
|
async putUserData(path, data) {
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
reject(new Error(ack.err));
|
|
1413
|
-
}
|
|
1414
|
-
else {
|
|
1415
|
-
resolve(ack);
|
|
1416
|
-
}
|
|
1417
|
-
});
|
|
1418
|
-
});
|
|
1653
|
+
const user = this.gun.user();
|
|
1654
|
+
if (!user.is) {
|
|
1655
|
+
throw new Error("User not authenticated");
|
|
1656
|
+
}
|
|
1657
|
+
const ack = await this.navigateToPath(user, path).put(data).then();
|
|
1658
|
+
if (ack.err) {
|
|
1659
|
+
throw new Error(ack.err);
|
|
1660
|
+
}
|
|
1419
1661
|
}
|
|
1420
1662
|
/**
|
|
1421
1663
|
* Gets user data from the specified path
|
|
@@ -1423,47 +1665,34 @@ class DataBase {
|
|
|
1423
1665
|
* @returns Promise that resolves with the data
|
|
1424
1666
|
*/
|
|
1425
1667
|
async getUserData(path) {
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
try {
|
|
1445
|
-
this.navigateToPath(user, path).once((data) => {
|
|
1446
|
-
clearTimeout(timeout);
|
|
1447
|
-
// Gestisci i riferimenti GunDB
|
|
1448
|
-
if (data && typeof data === "object" && data["#"]) {
|
|
1449
|
-
// È un riferimento GunDB, carica i dati effettivi
|
|
1450
|
-
const referencePath = data["#"];
|
|
1451
|
-
this.navigateToPath(this.gun, referencePath).once((actualData) => {
|
|
1452
|
-
resolve(actualData);
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
else {
|
|
1456
|
-
// Dati diretti, restituisci così come sono
|
|
1457
|
-
resolve(data);
|
|
1458
|
-
}
|
|
1459
|
-
});
|
|
1668
|
+
// Validazione del path
|
|
1669
|
+
if (!path || typeof path !== "string") {
|
|
1670
|
+
throw new Error("Path must be a non-empty string");
|
|
1671
|
+
}
|
|
1672
|
+
const user = this.gun.user();
|
|
1673
|
+
if (!user.is) {
|
|
1674
|
+
throw new Error("User not authenticated");
|
|
1675
|
+
}
|
|
1676
|
+
try {
|
|
1677
|
+
const data = await this.navigateToPath(user, path).once().then();
|
|
1678
|
+
// Gestisci i riferimenti GunDB
|
|
1679
|
+
if (data && typeof data === "object" && data["#"]) {
|
|
1680
|
+
// È un riferimento GunDB, carica i dati effettivi
|
|
1681
|
+
const referencePath = data["#"];
|
|
1682
|
+
const actualData = await this.navigateToPath(this.gun, referencePath)
|
|
1683
|
+
.once()
|
|
1684
|
+
.then();
|
|
1685
|
+
return actualData;
|
|
1460
1686
|
}
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
reject(error);
|
|
1687
|
+
else {
|
|
1688
|
+
// Dati diretti, restituisci così come sono
|
|
1689
|
+
return data;
|
|
1465
1690
|
}
|
|
1466
|
-
}
|
|
1691
|
+
}
|
|
1692
|
+
catch (error) {
|
|
1693
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
1694
|
+
throw new Error(errorMsg);
|
|
1695
|
+
}
|
|
1467
1696
|
}
|
|
1468
1697
|
// Errors
|
|
1469
1698
|
static Errors = GunErrors;
|