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.
Files changed (51) hide show
  1. package/README.md +150 -19
  2. package/dist/browser/shogun-core.js +3241 -1286
  3. package/dist/browser/shogun-core.js.map +1 -1
  4. package/dist/config/simplified-config.js +230 -0
  5. package/dist/core.js +49 -571
  6. package/dist/gundb/db.js +466 -237
  7. package/dist/gundb/improved-types.js +4 -0
  8. package/dist/gundb/index.js +4 -0
  9. package/dist/gundb/simple-api.js +438 -0
  10. package/dist/index.js +8 -2
  11. package/dist/managers/AuthManager.js +225 -0
  12. package/dist/managers/CoreInitializer.js +227 -0
  13. package/dist/managers/EventManager.js +67 -0
  14. package/dist/managers/PluginManager.js +296 -0
  15. package/dist/migration-test.js +91 -0
  16. package/dist/plugins/nostr/nostrConnectorPlugin.js +1 -1
  17. package/dist/plugins/oauth/oauthPlugin.js +1 -1
  18. package/dist/plugins/webauthn/webauthnPlugin.js +1 -1
  19. package/dist/types/config/simplified-config.d.ts +114 -0
  20. package/dist/types/core.d.ts +13 -46
  21. package/dist/types/gundb/db.d.ts +92 -14
  22. package/dist/types/gundb/improved-types.d.ts +123 -0
  23. package/dist/types/gundb/index.d.ts +2 -0
  24. package/dist/types/gundb/rxjs.d.ts +3 -3
  25. package/dist/types/gundb/simple-api.d.ts +90 -0
  26. package/dist/types/index.d.ts +6 -4
  27. package/dist/types/{types → interfaces}/shogun.d.ts +8 -10
  28. package/dist/types/managers/AuthManager.d.ts +69 -0
  29. package/dist/types/managers/CoreInitializer.d.ts +40 -0
  30. package/dist/types/managers/EventManager.d.ts +49 -0
  31. package/dist/types/managers/PluginManager.d.ts +145 -0
  32. package/dist/types/migration-test.d.ts +16 -0
  33. package/dist/types/plugins/base.d.ts +2 -2
  34. package/dist/types/plugins/index.d.ts +1 -1
  35. package/dist/types/plugins/nostr/nostrConnectorPlugin.d.ts +1 -1
  36. package/dist/types/plugins/nostr/types.d.ts +2 -2
  37. package/dist/types/plugins/oauth/oauthPlugin.d.ts +1 -1
  38. package/dist/types/plugins/oauth/types.d.ts +2 -2
  39. package/dist/types/plugins/web3/types.d.ts +2 -2
  40. package/dist/types/plugins/web3/web3ConnectorPlugin.d.ts +1 -1
  41. package/dist/types/plugins/webauthn/types.d.ts +2 -2
  42. package/dist/types/plugins/webauthn/webauthnPlugin.d.ts +1 -1
  43. package/dist/types/utils/errorHandler.d.ts +1 -1
  44. package/package.json +1 -1
  45. /package/dist/{types → interfaces}/common.js +0 -0
  46. /package/dist/{types → interfaces}/events.js +0 -0
  47. /package/dist/{types → interfaces}/plugin.js +0 -0
  48. /package/dist/{types → interfaces}/shogun.js +0 -0
  49. /package/dist/types/{types → interfaces}/common.d.ts +0 -0
  50. /package/dist/types/{types → interfaces}/events.d.ts +0 -0
  51. /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/evict";
16
- import "gun/lib/les";
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
- return new Promise((resolve) => {
339
- this.navigateToPath(this.gun, path).once((data) => {
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
- return new Promise((resolve) => {
352
- this.navigateToPath(this.gun, path).put(data, (ack) => {
353
- const result = ack.err
354
- ? { success: false, error: ack.err }
355
- : { success: true };
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
- return new Promise((resolve) => {
368
- this.navigateToPath(this.gun, path).set(data, (ack) => {
369
- const result = ack.err
370
- ? { success: false, error: ack.err }
371
- : { success: true };
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
- return new Promise((resolve) => {
383
- this.navigateToPath(this.gun, path).put(null, (ack) => {
384
- const result = ack.err
385
- ? { success: false, error: ack.err }
386
- : { success: true };
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 new Promise((resolve) => {
915
- const timeout = setTimeout(() => {
916
- console.warn(`⚠️ Timeout getting user data for ${userPub} - proceeding with null`);
917
- resolve(null);
918
- }, 5000); // 5 second timeout
919
- this.gun.get(userPub).once((data) => {
920
- clearTimeout(timeout);
921
- resolve(data);
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: !existingUser || !existingUser.username,
901
+ isNewUser: isNewUser,
1006
902
  // Get the SEA pair from the user object
1007
- sea: this.gun.user()?._?.sea
903
+ sea: userSea
1008
904
  ? {
1009
- pub: this.gun.user()._?.sea.pub,
1010
- priv: this.gun.user()._?.sea.priv,
1011
- epub: this.gun.user()._?.sea.epub,
1012
- epriv: this.gun.user()._?.sea.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 new Promise((resolve, reject) => {
1282
- this.node.get(userPub)
1283
- .get("security")
1284
- .put(securityPayload, (ack) => {
1285
- if (ack.err) {
1286
- console.error("Error saving security data to public graph:", ack.err);
1287
- reject(new Error(ack.err));
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 new Promise((resolve) => {
1314
- this.node
1315
- .get("usernames")
1316
- .get(normalizedUsername)
1317
- .once((data) => {
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 new Promise((resolve) => {
1327
- this.node.get(userPub).get("security").once((data) => {
1328
- // console.log(
1329
- // `Retrieved security data for user ${username}:`,
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
- return new Promise((resolve, reject) => {
1405
- const user = this.gun.user();
1406
- if (!user.is) {
1407
- reject(new Error("User not authenticated"));
1408
- return;
1409
- }
1410
- this.navigateToPath(user, path).put(data, (ack) => {
1411
- if (ack.err) {
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
- return new Promise((resolve, reject) => {
1427
- // Validazione del path
1428
- if (!path || typeof path !== "string") {
1429
- const error = "Path must be a non-empty string";
1430
- reject(new Error(error));
1431
- return;
1432
- }
1433
- const user = this.gun.user();
1434
- if (!user.is) {
1435
- const error = "User not authenticated";
1436
- reject(new Error(error));
1437
- return;
1438
- }
1439
- // Timeout per evitare attese infinite
1440
- const timeout = setTimeout(() => {
1441
- const error = "Operation timeout";
1442
- reject(new Error(error));
1443
- }, CONFIG.TIMEOUTS.USER_DATA_OPERATION); // 10 secondi di timeout
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
- catch (error) {
1462
- clearTimeout(timeout);
1463
- const errorMsg = error instanceof Error ? error.message : "Unknown error";
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;