diff --git a/.env.example b/.env.example index 4ea11d88..65c31d1c 100644 --- a/.env.example +++ b/.env.example @@ -3,4 +3,4 @@ PEERS=["http://gun.shock.network:8765/gun"] MS_TO_TOKEN_EXPIRATION=4500000 DISABLE_SHOCK_ENCRYPTION=false CACHE_HEADERS_MANDATORY=true -SHOCK_CACHE=true \ No newline at end of file +SHOCK_CACHE=true diff --git a/.vscode/settings.json b/.vscode/settings.json index 4718ffd9..bd496df1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "eslint.enable": true + "eslint.enable": true, + "typescript.tsdk": "node_modules/typescript/lib" } \ No newline at end of file diff --git a/README.md b/README.md index 9d3379c1..c6f8fb17 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,11 @@ For easy setup on your Laptop/Desktop, [a wizard is available here.](https://git * Requires [Node.js](https://nodejs.org) 12 #### Steps: -1) Run [LND](https://github.com/lightningnetwork/lnd/releases) - *Example mainnet startup*: +1) Run [LND](https://github.com/shocknet/lnd/releases) - *Example mainnet startup*: - ```./lnd --bitcoin.active --bitcoin.mainnet --bitcoin.node=neutrino --neutrino.connect=neutrino.shock.network --routing.assumechanvalid=1 --allow-circular-route=1``` +(Neutrino example requires builds with experimental flags, our binaries include them.) + + ```./lnd --bitcoin.active --bitcoin.mainnet --bitcoin.node=neutrino --neutrino.connect=neutrino.shock.network --routing.assumechanvalid --allow-circular-route``` 2) Download and Install API diff --git a/config/defaults.js b/config/defaults.js index d30c9ad4..4ba82f6d 100644 --- a/config/defaults.js +++ b/config/defaults.js @@ -48,7 +48,7 @@ module.exports = (mainnet = false) => { logfile: "shockapi.log", lndLogFile: parsePath(`${lndDirectory}/logs/bitcoin/${network}/lnd.log`), lndDirPath: lndDirectory, - peers: ["http://gun.shock.network:8765/gun"], + peers: ['http://gun.shock.network:8765/gun'], useTLS: false, tokenExpirationMS: 4500000 }; diff --git a/package.json b/package.json index 079ad8dc..37690caa 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "sw-server", + "name": "shockapi", "version": "1.0.0", "description": "", "main": "src/server.js", @@ -34,12 +34,12 @@ "google-proto-files": "^1.0.3", "graphviz": "0.0.8", "grpc": "^1.21.1", - "gun": "git://github.com/amark/gun#3e678b8568a4a8a825b84b26759d4dd4f3b0988e", + "gun": "git://github.com/amark/gun#97aa976c97e6219a9f93095d32c220dcd371ca62", "husky": "^4.2.5", "jsonfile": "^4.0.0", "jsonwebtoken": "^8.3.0", "localtunnel": "^1.9.0", - "lodash": "^4.17.15", + "lodash": "^4.17.19", "method-override": "^2.3.7", "promise": "^8.0.1", "ramda": "^0.27.0", @@ -78,7 +78,7 @@ "nodemon": "^1.19.3", "prettier": "^1.18.2", "ts-type": "^1.2.16", - "typescript": "^3.6.3" + "typescript": "^3.9.6" }, "lint-staged": { "*.js": [ diff --git a/services/gunDB/Mediator/index.js b/services/gunDB/Mediator/index.js index f8fc73ec..bfd8764b 100644 --- a/services/gunDB/Mediator/index.js +++ b/services/gunDB/Mediator/index.js @@ -12,8 +12,8 @@ require('gun/lib/open') // @ts-ignore require('gun/lib/load') const debounce = require('lodash/debounce') -const Encryption = require('../../../utils/encryptionStore') +const Encryption = require('../../../utils/encryptionStore') const Key = require('../contact-api/key') /** @type {import('../contact-api/SimpleGUN').ISEA} */ @@ -1275,6 +1275,56 @@ const register = async (alias, pass) => { ) } + /** + * Peers provided to gun. + */ + const peers = Object.values(gun._.opt.peers) + + const theresPeers = peers.length > 0 + const atLeastOneIsConnected = peers.some( + p => p.wire && p.wire.readyState === 1 + ) + + if (theresPeers && !atLeastOneIsConnected) { + throw new Error( + 'No connected to any peers for checking of duplicate aliases' + ) + } + + if (theresPeers && atLeastOneIsConnected) { + // this import is done here to avoid circular dependency hell + const { timeout5 } = require('../contact-api/utils') + + let userData = await timeout5( + new Promise(res => { + gun.get(`~@${alias}`).once(ud => res(ud)) + }) + ) + + if (userData) { + throw new Error( + 'The given alias has been used before, use an unique alias instead.' + ) + } + + await new Promise(res => setTimeout(res, 300)) + + userData = await timeout5( + new Promise(res => { + gun.get(`~@${alias}`).once(ud => res(ud), { + // https://github.com/amark/gun/pull/971#issue-438630761 + wait: 1500 + }) + }) + ) + + if (userData) { + throw new Error( + 'The given alias has been used before, use an unique alias instead. (Caught at 2nd try)' + ) + } + } + _isRegistering = true /** @type {import('../contact-api/SimpleGUN').CreateAck} */ @@ -1282,6 +1332,13 @@ const register = async (alias, pass) => { user.create(alias, pass, ack => res(ack)) ) + // An empty ack object seems to be caused by a duplicate alias sign up + if ('{}' === JSON.stringify(ack)) { + throw new Error( + 'The given alias has been used before, use an unique alias instead. (Empty ack)' + ) + } + _isRegistering = false if (typeof ack.err === 'string') { diff --git a/services/gunDB/config.js b/services/gunDB/config.js index 355581a1..44dab137 100644 --- a/services/gunDB/config.js +++ b/services/gunDB/config.js @@ -11,7 +11,9 @@ dotenv.config() // @ts-ignore Let it crash if undefined exports.DATA_FILE_NAME = process.env.DATA_FILE_NAME || defaults.dataFileName -// @ts-ignore Let it crash if undefined +/** + * @type {string[]} + */ exports.PEERS = process.env.PEERS ? JSON.parse(process.env.PEERS) : defaults.peers diff --git a/services/gunDB/contact-api/SimpleGUN.ts b/services/gunDB/contact-api/SimpleGUN.ts index eb5e4d11..3a9cd77b 100644 --- a/services/gunDB/contact-api/SimpleGUN.ts +++ b/services/gunDB/contact-api/SimpleGUN.ts @@ -30,9 +30,20 @@ interface OpenListenerDataObj { export type Listener = (data: ListenerData, key: string) => void export type Callback = (ack: Ack) => void +export interface Peer { + url: string + id: string + wire?: { + readyState: number + } +} + export interface Soul { get: string put: Primitive | null | object | undefined + opt: { + peers: Record + } } export type OpenListenerData = Primitive | null | OpenListenerDataObj @@ -47,7 +58,7 @@ export interface GUNNodeBase { map(): GUNNode on(this: GUNNode, cb: Listener): void - once(this: GUNNode, cb?: Listener): GUNNode + once(this: GUNNode, cb?: Listener, opts?: { wait: number }): GUNNode open(this: GUNNode, cb?: OpenListener): GUNNode load(this: GUNNode, cb?: OpenListener): GUNNode diff --git a/services/gunDB/contact-api/getters/user.js b/services/gunDB/contact-api/getters/user.js index 8f1d9cf2..fb8054e1 100644 --- a/services/gunDB/contact-api/getters/user.js +++ b/services/gunDB/contact-api/getters/user.js @@ -49,14 +49,14 @@ const getAnUser = async publicKey => { /** @type {Common.SchemaTypes.User} */ const u = { - avatar: oldProfile.avatar, + avatar: oldProfile.avatar || null, // @ts-ignore - bio, - displayName: oldProfile.displayName, + bio: bio || null, + displayName: oldProfile.displayName || null, // @ts-ignore - lastSeenApp, + lastSeenApp: lastSeenApp || 0, // @ts-ignore - lastSeenNode, + lastSeenNode: lastSeenNode || 0, // @ts-ignore publicKey } diff --git a/src/routes.js b/src/routes.js index dde027ba..8c1fc4ad 100644 --- a/src/routes.js +++ b/src/routes.js @@ -714,7 +714,7 @@ module.exports = async ( logger.error(err); return res.status(500).json({ field: "unknown", - errorMessage: err + errorMessage: err.message || err }) } }); @@ -2039,6 +2039,54 @@ module.exports = async ( ap.get(`/api/gun/feed`, apiGunFeedGet) + /** + * @type {RequestHandler<{}>} + */ + const apiGunMeGet = async (_, res) => { + try { + return res.status(200).json(await GunGetters.getMyUser()) + } catch (err) { + logger.error(err) + return res.status(500).json({ + errorMessage: err.message + }) + } + } + + + /** + * @type {RequestHandler<{}>} + */ + const apiGunMePut = async (req, res) => { + try { + const { avatar, bio , displayName} = /** @type {Partial>} */ (req.body) + + if (avatar) { + await GunActions.setAvatar(avatar, require('../services/gunDB/Mediator').getUser()) + } + + if (bio) { + await GunActions.setBio(bio, require('../services/gunDB/Mediator').getUser()) + } + + if (displayName) { + await GunActions.setDisplayName(displayName, require('../services/gunDB/Mediator').getUser()) + } + + return res.status(200).json({ + ok: true + }) + } catch (err) { + logger.error(err) + return res.status(500).json({ + errorMessage: err.message + }) + } + } + + ap.get(`/api/gun/me`, apiGunMeGet) + ap.put(`/api/gun/me`, apiGunMePut) + /** * Return app so that it can be used by express. */ diff --git a/yarn.lock b/yarn.lock index 32effd82..998c73d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4298,10 +4298,10 @@ lodash@=4.17.4: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= -lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.5.0: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== log-symbols@^3.0.0: version "3.0.0" @@ -6505,10 +6505,10 @@ typedarray-dts@^1.0.0: resolved "https://registry.yarnpkg.com/typedarray-dts/-/typedarray-dts-1.0.0.tgz#9dec9811386dbfba964c295c2606cf9a6b982d06" integrity sha512-Ka0DBegjuV9IPYFT1h0Qqk5U4pccebNIJCGl8C5uU7xtOs+jpJvKGAY4fHGK25hTmXZOEUl9Cnsg5cS6K/b5DA== -typescript@^3.6.3: - version "3.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" - integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== +typescript@^3.9.6: + version "3.9.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.6.tgz#8f3e0198a34c3ae17091b35571d3afd31999365a" + integrity sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw== uglify-js@^3.1.4: version "3.6.0"