Merge branch 'master' into feature/lnd-10
This commit is contained in:
commit
33e18bec2e
19 changed files with 1275 additions and 60 deletions
3
.dockerignore
Normal file
3
.dockerignore
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
**/.git
|
||||
**/node_modules
|
||||
**/radata
|
||||
6
.env.example
Normal file
6
.env.example
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
DATA_FILE_NAME=data3
|
||||
PEERS=["http://gun.shock.network:8765/gun"]
|
||||
MS_TO_TOKEN_EXPIRATION=4500000
|
||||
DISABLE_SHOCK_ENCRYPTION=false
|
||||
CACHE_HEADERS_MANDATORY=true
|
||||
SHOCK_CACHE=true
|
||||
29
Dockerfile
Normal file
29
Dockerfile
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
FROM node:12.18.0-alpine3.9
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
|
||||
ADD ./package.json /usr/src/app/package.json
|
||||
ADD ./yarn.lock /usr/src/app/yarn.lock
|
||||
#RUN useradd app && \
|
||||
# mkdir -p /home/app/.lnd
|
||||
RUN apk update && apk upgrade && \
|
||||
apk add --no-cache bash git openssh
|
||||
RUN yarn install
|
||||
|
||||
ADD . /usr/src/app
|
||||
RUN ls /usr/src/app
|
||||
|
||||
RUN chmod +x ./docker-start.sh
|
||||
#ADD ./tls.cert /usr/src/app/tls.cert
|
||||
#ADD ./admin.macaroon /usr/src/app/admin.macaroon
|
||||
|
||||
# && \
|
||||
# chown -R app:app /home/app && \
|
||||
# chown -R app:app /usr/src/app && \
|
||||
# chown -R app:app /start.sh
|
||||
|
||||
#ARG lnd_address
|
||||
#ENV LND_ADDR=$lnd_address
|
||||
EXPOSE 9835
|
||||
CMD ["./docker-start.sh"]
|
||||
5
docker-start.sh
Normal file
5
docker-start.sh
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/ash
|
||||
node main -h 0.0.0.0 \
|
||||
-m admin.macaroon \
|
||||
-d tls.cert \
|
||||
-l $LND_ADDR
|
||||
|
|
@ -41,11 +41,12 @@
|
|||
"lodash": "^4.17.15",
|
||||
"method-override": "^2.3.7",
|
||||
"promise": "^8.0.1",
|
||||
"ramda": "^0.27.0",
|
||||
"request": "^2.87.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"response-time": "^2.3.2",
|
||||
"shelljs": "^0.8.2",
|
||||
"shock-common": "^0.0.1",
|
||||
"shock-common": "4.0.1",
|
||||
"socket.io": "2.1.1",
|
||||
"text-encoding": "^0.7.0",
|
||||
"tingodb": "^0.6.1",
|
||||
|
|
@ -61,6 +62,7 @@
|
|||
"@types/jest": "^24.0.18",
|
||||
"@types/jsonwebtoken": "^8.3.7",
|
||||
"@types/lodash": "^4.14.141",
|
||||
"@types/ramda": "types/npm-ramda#dist",
|
||||
"@types/socket.io": "^2.1.4",
|
||||
"@types/uuid": "^3.4.5",
|
||||
"babel-eslint": "^10.0.3",
|
||||
|
|
@ -78,9 +80,12 @@
|
|||
"typescript": "^3.6.3"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,ts}": [
|
||||
"*.js": [
|
||||
"prettier --check",
|
||||
"eslint"
|
||||
],
|
||||
"*.ts": [
|
||||
"prettier --check"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
|
|
|
|||
|
|
@ -2,14 +2,20 @@
|
|||
* @format
|
||||
*/
|
||||
const Gun = require('gun')
|
||||
// @ts-ignore
|
||||
require('gun/nts')
|
||||
const logger = require('winston')
|
||||
// @ts-ignore
|
||||
Gun.log = () => {}
|
||||
// @ts-ignore
|
||||
require('gun/lib/open')
|
||||
// @ts-ignore
|
||||
require('gun/lib/load')
|
||||
const debounce = require('lodash/debounce')
|
||||
const Encryption = require('../../../utils/encryptionStore')
|
||||
|
||||
const Key = require('../contact-api/key')
|
||||
|
||||
/** @type {import('../contact-api/SimpleGUN').ISEA} */
|
||||
// @ts-ignore
|
||||
const SEAx = require('gun/sea')
|
||||
|
|
@ -265,6 +271,24 @@ const authenticate = async (alias, pass, __user) => {
|
|||
if (typeof ack.err === 'string') {
|
||||
throw new Error(ack.err)
|
||||
} else if (typeof ack.sea === 'object') {
|
||||
// clock skew
|
||||
await new Promise(res => setTimeout(res, 2000))
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
_user.get(Key.FOLLOWS).put(
|
||||
{
|
||||
unused: null
|
||||
},
|
||||
ack => {
|
||||
if (ack.err) {
|
||||
rej(new Error(`Error initializing follows: ${ack.err}`))
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return ack.sea.pub
|
||||
} else {
|
||||
throw new Error('Unknown error.')
|
||||
|
|
@ -277,6 +301,25 @@ const authenticate = async (alias, pass, __user) => {
|
|||
`Tried to re-authenticate with an alias different to that of stored one, tried: ${alias} - stored: ${_currentAlias}, logoff first if need to change aliases.`
|
||||
)
|
||||
}
|
||||
|
||||
// clock skew
|
||||
await new Promise(res => setTimeout(res, 2000))
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
_user.get(Key.FOLLOWS).put(
|
||||
{
|
||||
unused: null
|
||||
},
|
||||
ack => {
|
||||
if (ack.err) {
|
||||
rej(new Error(`Error initializing follows: ${ack.err}`))
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// move this to a subscription; implement off() ? todo
|
||||
API.Jobs.onAcceptedRequests(_user, mySEA)
|
||||
API.Jobs.onOrders(_user, gun, mySEA)
|
||||
|
|
@ -310,6 +353,21 @@ const authenticate = async (alias, pass, __user) => {
|
|||
|
||||
await new Promise(res => setTimeout(res, 5000))
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
_user.get(Key.FOLLOWS).put(
|
||||
{
|
||||
unused: null
|
||||
},
|
||||
ack => {
|
||||
if (ack.err) {
|
||||
rej(new Error(`Error initializing follows: ${ack.err}`))
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
API.Jobs.onAcceptedRequests(_user, mySEA)
|
||||
API.Jobs.onOrders(_user, gun, mySEA)
|
||||
API.Jobs.lastSeenNode(_user)
|
||||
|
|
@ -328,6 +386,17 @@ const logoff = () => {
|
|||
}
|
||||
|
||||
const instantiateGun = () => {
|
||||
if (user) {
|
||||
user.leave()
|
||||
}
|
||||
// @ts-ignore
|
||||
user = null
|
||||
if (gun) {
|
||||
gun.off()
|
||||
}
|
||||
// @ts-ignore
|
||||
gun = null
|
||||
|
||||
const _gun = /** @type {unknown} */ (new Gun({
|
||||
axe: false,
|
||||
peers: Config.PEERS
|
||||
|
|
@ -1217,7 +1286,7 @@ const register = async (alias, pass) => {
|
|||
|
||||
if (typeof ack.err === 'string') {
|
||||
throw new Error(ack.err)
|
||||
} else if (typeof ack.pub === 'string') {
|
||||
} else if (typeof ack.pub === 'string' || typeof user._.sea === 'object') {
|
||||
const mySecret = await mySEA.secret(user._.sea.epub, user._.sea)
|
||||
_currentAlias = alias
|
||||
_currentPass = await mySEA.encrypt(pass, mySecret)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ export interface Soul {
|
|||
export type OpenListenerData = Primitive | null | OpenListenerDataObj
|
||||
export type OpenListener = (data: OpenListenerData, key: string) => void
|
||||
|
||||
export type LoadListenerData = OpenListenerData
|
||||
export type LoadListener = (data: LoadListenerData, key: string) => void
|
||||
|
||||
export interface GUNNodeBase {
|
||||
_: Soul
|
||||
|
||||
|
|
@ -47,6 +50,9 @@ export interface GUNNodeBase {
|
|||
once(this: GUNNode, cb?: Listener): GUNNode
|
||||
|
||||
open(this: GUNNode, cb?: OpenListener): GUNNode
|
||||
load(this: GUNNode, cb?: OpenListener): GUNNode
|
||||
|
||||
load(this: GUNNode, cb?: LoadListener): GUNNode
|
||||
|
||||
off(): void
|
||||
user(): UserGUNNode
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
*/
|
||||
const uuidv1 = require('uuid/v1')
|
||||
const logger = require('winston')
|
||||
const { Constants, Schema } = require('shock-common')
|
||||
const Common = require('shock-common')
|
||||
const { Constants, Schema } = Common
|
||||
|
||||
const { ErrorCode } = Constants
|
||||
|
||||
|
|
@ -1133,7 +1134,21 @@ const setBio = (bio, user) =>
|
|||
resolve()
|
||||
}
|
||||
})
|
||||
}).then(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
user
|
||||
.get(Key.PROFILE)
|
||||
.get(Key.BIO)
|
||||
.put(bio, ack => {
|
||||
if (ack.err) {
|
||||
reject(new Error(ack.err))
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {string[]} mnemonicPhrase
|
||||
|
|
@ -1156,7 +1171,7 @@ const saveSeedBackup = async (mnemonicPhrase, user, SEA) => {
|
|||
return new Promise((res, rej) => {
|
||||
user.get(Key.SEED_BACKUP).put(encryptedSeed, ack => {
|
||||
if (ack.err) {
|
||||
rej(ack.err)
|
||||
rej(new Error(ack.err))
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
|
|
@ -1179,7 +1194,7 @@ const saveChannelsBackup = async (backups, user, SEA) => {
|
|||
return new Promise((res, rej) => {
|
||||
user.get(Key.CHANNELS_BACKUP).put(encryptBackups, ack => {
|
||||
if (ack.err) {
|
||||
rej(ack.err)
|
||||
rej(new Error(ack.err))
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
|
|
@ -1216,6 +1231,275 @@ const setLastSeenApp = () =>
|
|||
res()
|
||||
}
|
||||
})
|
||||
}).then(
|
||||
() =>
|
||||
new Promise((res, rej) => {
|
||||
require('../Mediator')
|
||||
.getUser()
|
||||
.get(Key.PROFILE)
|
||||
.get(Key.LAST_SEEN_APP)
|
||||
.put(Date.now(), ack => {
|
||||
if (ack.err) {
|
||||
rej(new Error(ack.err))
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {string[]} tags
|
||||
* @param {string} title
|
||||
* @param {Common.Schema.ContentItem[]} content
|
||||
* @returns {Promise<Common.Schema.Post>}
|
||||
*/
|
||||
const createPost = async (tags, title, content) => {
|
||||
if (content.length === 0) {
|
||||
throw new Error(`A post must contain at least one paragraph/image/video`)
|
||||
}
|
||||
|
||||
const numOfPages = await (async () => {
|
||||
const maybeNumOfPages = await Utils.tryAndWait(
|
||||
(_, user) =>
|
||||
user
|
||||
.get(Key.WALL)
|
||||
.get(Key.NUM_OF_PAGES)
|
||||
.then(),
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
return typeof maybeNumOfPages === 'number' ? maybeNumOfPages : 0
|
||||
})()
|
||||
|
||||
let pageIdx = Math.max(0, numOfPages - 1).toString()
|
||||
|
||||
const count = await (async () => {
|
||||
if (numOfPages === 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const maybeCount = await Utils.tryAndWait(
|
||||
(_, user) =>
|
||||
user
|
||||
.get(Key.WALL)
|
||||
.get(Key.PAGES)
|
||||
.get(pageIdx)
|
||||
.get(Key.COUNT)
|
||||
.then(),
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
return typeof maybeCount === 'number' ? maybeCount : 0
|
||||
})()
|
||||
|
||||
const shouldBeNewPage =
|
||||
count >= Common.Constants.Misc.NUM_OF_POSTS_PER_WALL_PAGE
|
||||
|
||||
if (shouldBeNewPage) {
|
||||
pageIdx = Number(pageIdx + 1).toString()
|
||||
}
|
||||
|
||||
await new Promise((res, rej) => {
|
||||
require('../Mediator')
|
||||
.getUser()
|
||||
.get(Key.WALL)
|
||||
.get(Key.PAGES)
|
||||
.get(pageIdx)
|
||||
.put(
|
||||
{
|
||||
[Key.COUNT]: shouldBeNewPage ? 1 : count + 1,
|
||||
posts: {
|
||||
unused: null
|
||||
}
|
||||
},
|
||||
ack => {
|
||||
if (ack.err) {
|
||||
rej(new Error(ack.err))
|
||||
}
|
||||
|
||||
res()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
/** @type {string} */
|
||||
const postID = await new Promise((res, rej) => {
|
||||
const _n = require('../Mediator')
|
||||
.getUser()
|
||||
.get(Key.WALL)
|
||||
.get(Key.PAGES)
|
||||
.get(pageIdx)
|
||||
.get(Key.POSTS)
|
||||
.set(
|
||||
{
|
||||
date: Date.now(),
|
||||
status: 'publish',
|
||||
tags: tags.join('-'),
|
||||
title
|
||||
},
|
||||
ack => {
|
||||
if (ack.err) {
|
||||
rej(new Error(ack.err))
|
||||
} else {
|
||||
res(_n._.get)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
if (shouldBeNewPage || numOfPages === 0) {
|
||||
await new Promise(res => {
|
||||
require('../Mediator')
|
||||
.getUser()
|
||||
.get(Key.WALL)
|
||||
.get(Key.NUM_OF_PAGES)
|
||||
.put(numOfPages + 1, ack => {
|
||||
if (ack.err) {
|
||||
throw new Error(ack.err)
|
||||
}
|
||||
|
||||
res()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const contentItems = require('../Mediator')
|
||||
.getUser()
|
||||
.get(Key.WALL)
|
||||
.get(Key.PAGES)
|
||||
.get(pageIdx)
|
||||
.get(Key.POSTS)
|
||||
.get(postID)
|
||||
.get(Key.CONTENT_ITEMS)
|
||||
|
||||
try {
|
||||
await Promise.all(
|
||||
content.map(
|
||||
ci =>
|
||||
new Promise(res => {
|
||||
// @ts-ignore
|
||||
contentItems.set(ci, ack => {
|
||||
if (ack.err) {
|
||||
throw new Error(ack.err)
|
||||
}
|
||||
|
||||
res()
|
||||
})
|
||||
})
|
||||
)
|
||||
)
|
||||
} catch (e) {
|
||||
await new Promise(res => {
|
||||
require('../Mediator')
|
||||
.getUser()
|
||||
.get(Key.WALL)
|
||||
.get(Key.PAGES)
|
||||
.get(pageIdx)
|
||||
.get(Key.POSTS)
|
||||
.get(postID)
|
||||
.put(null, ack => {
|
||||
if (ack.err) {
|
||||
throw new Error(ack.err)
|
||||
}
|
||||
|
||||
res()
|
||||
})
|
||||
})
|
||||
|
||||
throw e
|
||||
}
|
||||
|
||||
const loadedPost = await new Promise(res => {
|
||||
require('../Mediator')
|
||||
.getUser()
|
||||
.get(Key.WALL)
|
||||
.get(Key.PAGES)
|
||||
.get(pageIdx)
|
||||
.get(Key.POSTS)
|
||||
.get(postID)
|
||||
.load(data => {
|
||||
res(data)
|
||||
})
|
||||
})
|
||||
|
||||
/** @type {Common.Schema.User} */
|
||||
const userForPost = await Getters.getMyUser()
|
||||
|
||||
/** @type {Common.Schema.Post} */
|
||||
const completePost = {
|
||||
...loadedPost,
|
||||
author: userForPost,
|
||||
id: postID
|
||||
}
|
||||
|
||||
if (!Common.Schema.isPost(completePost)) {
|
||||
throw new Error(
|
||||
`completePost not a Post inside Actions.createPost(): ${JSON.stringify(
|
||||
createPost
|
||||
)}`
|
||||
)
|
||||
}
|
||||
|
||||
return completePost
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} postId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const deletePost = async postId => {
|
||||
await new Promise(res => {
|
||||
res(postId)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} publicKey
|
||||
* @param {boolean} isPrivate Will overwrite previous private status.
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
const follow = (publicKey, isPrivate) => {
|
||||
/** @type {import('shock-common').Schema.Follow} */
|
||||
const newFollow = {
|
||||
private: isPrivate,
|
||||
status: 'ok',
|
||||
user: publicKey
|
||||
}
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
require('../Mediator')
|
||||
.getUser()
|
||||
.get(Key.FOLLOWS)
|
||||
.get(publicKey)
|
||||
// @ts-ignore
|
||||
.put(newFollow, ack => {
|
||||
if (ack.err) {
|
||||
rej(new Error(ack.err))
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} publicKey
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const unfollow = publicKey =>
|
||||
new Promise((res, rej) => {
|
||||
require('../Mediator')
|
||||
.getUser()
|
||||
.get(Key.FOLLOWS)
|
||||
.get(publicKey)
|
||||
.put(null, ack => {
|
||||
if (ack.err) {
|
||||
rej(new Error(ack.err))
|
||||
} else {
|
||||
res()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
|
|
@ -1236,5 +1520,9 @@ module.exports = {
|
|||
saveSeedBackup,
|
||||
saveChannelsBackup,
|
||||
disconnect,
|
||||
setLastSeenApp
|
||||
setLastSeenApp,
|
||||
createPost,
|
||||
deletePost,
|
||||
follow,
|
||||
unfollow
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
const Key = require('./key')
|
||||
const Utils = require('./utils')
|
||||
|
||||
/**
|
||||
* @param {string} pub
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
exports.currentOrderAddress = async (pub) => {
|
||||
const currAddr = await Utils.tryAndWait((gun) => gun.user(pub).get(Key.CURRENT_ORDER_ADDRESS).then())
|
||||
|
||||
if (typeof currAddr !== 'string') {
|
||||
throw new TypeError('Expected user.currentOrderAddress to be an string')
|
||||
}
|
||||
|
||||
return currAddr
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} pub
|
||||
* @returns {Promise<string|null>}
|
||||
*/
|
||||
exports.userToIncomingID = async (pub) => {
|
||||
const incomingID = await require('../Mediator').getUser().get(Key.USER_TO_INCOMING).get(pub).then()
|
||||
|
||||
if (typeof incomingID === 'string') return incomingID
|
||||
|
||||
return null
|
||||
|
||||
}
|
||||
79
services/gunDB/contact-api/getters/feed.js
Normal file
79
services/gunDB/contact-api/getters/feed.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
const Common = require('shock-common')
|
||||
const isFinite = require('lodash/isFinite')
|
||||
const shuffle = require('lodash/shuffle')
|
||||
const R = require('ramda')
|
||||
|
||||
const Follows = require('./follows')
|
||||
const Wall = require('./wall')
|
||||
|
||||
/**
|
||||
* @param {number} numberOfPublicKeyGroups
|
||||
* @param {number} pageRequested
|
||||
* @returns {[ number , number ]}
|
||||
*/
|
||||
const calculateFeedPage = (numberOfPublicKeyGroups, pageRequested) => {
|
||||
// thanks to sebassdc
|
||||
|
||||
return [
|
||||
(pageRequested - 1) % numberOfPublicKeyGroups,
|
||||
Math.ceil(pageRequested / numberOfPublicKeyGroups)
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} page
|
||||
* @throws {TypeError}
|
||||
* @throws {RangeError}
|
||||
* @returns {Promise<Common.SchemaTypes.Post[]>}
|
||||
*/
|
||||
const getFeedPage = async page => {
|
||||
if (!isFinite(page)) {
|
||||
throw new TypeError(`Please provide an actual number for [page]`)
|
||||
}
|
||||
|
||||
if (page <= 0) {
|
||||
throw new RangeError(`Please provide only positive numbers for [page]`)
|
||||
}
|
||||
|
||||
const subbedPublicKeys = Object.values(await Follows.currentFollows()).map(
|
||||
f => f.user
|
||||
)
|
||||
|
||||
if (subbedPublicKeys.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
// say there are 20 public keys total
|
||||
// page 1: page 1 from first 10 public keys
|
||||
// page 2: page 1 from second 10 public keys
|
||||
// page 3: page 2 from first 10 public keys
|
||||
// page 4: page 2 from first 10 public keys
|
||||
// etc
|
||||
// thanks to sebassdc (github)
|
||||
|
||||
const pagedPublicKeys = R.splitEvery(10, shuffle(subbedPublicKeys))
|
||||
|
||||
const [publicKeyGroupIdx, pageToRequest] = calculateFeedPage(
|
||||
pagedPublicKeys.length,
|
||||
page
|
||||
)
|
||||
|
||||
const publicKeys = pagedPublicKeys[publicKeyGroupIdx]
|
||||
|
||||
const fetchedPages = await Promise.all(
|
||||
publicKeys.map(pk => Wall.getWallPage(pageToRequest, pk))
|
||||
)
|
||||
|
||||
const fetchedPostsGroups = fetchedPages.map(wp => Object.values(wp.posts))
|
||||
const fetchedPosts = R.flatten(fetchedPostsGroups)
|
||||
const sortered = R.sort((a, b) => b.date - a.date, fetchedPosts)
|
||||
|
||||
return sortered
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getFeedPage
|
||||
}
|
||||
59
services/gunDB/contact-api/getters/follows.js
Normal file
59
services/gunDB/contact-api/getters/follows.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
const Common = require('shock-common')
|
||||
const Logger = require('winston')
|
||||
const size = require('lodash/size')
|
||||
|
||||
const Utils = require('../utils')
|
||||
const Key = require('../key')
|
||||
|
||||
/**
|
||||
* @typedef {Common.Schema.Follow} Follow
|
||||
*/
|
||||
|
||||
/**
|
||||
* @throws {TypeError}
|
||||
* @returns {Promise<Record<string, Common.Schema.Follow>>}
|
||||
*/
|
||||
exports.currentFollows = async () => {
|
||||
/**
|
||||
* @type {Record<string, Common.Schema.Follow>}
|
||||
*/
|
||||
const raw = await Utils.tryAndWait(
|
||||
// @ts-ignore
|
||||
(_, user) => new Promise(res => user.get(Key.FOLLOWS).load(res)),
|
||||
v => {
|
||||
if (typeof v !== 'object' || v === null) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (size(v) === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
)
|
||||
|
||||
if (typeof raw !== 'object' || raw === null) {
|
||||
Logger.error(
|
||||
`Expected user.follows to be an object but instead got: ${JSON.stringify(
|
||||
raw
|
||||
)}`
|
||||
)
|
||||
throw new TypeError('Could not get follows, not an object')
|
||||
}
|
||||
|
||||
const clean = {
|
||||
...raw
|
||||
}
|
||||
|
||||
for (const [key, followOrNull] of Object.entries(clean)) {
|
||||
if (!Common.Schema.isFollow(followOrNull)) {
|
||||
delete clean[key]
|
||||
}
|
||||
}
|
||||
|
||||
return clean
|
||||
}
|
||||
101
services/gunDB/contact-api/getters/index.js
Normal file
101
services/gunDB/contact-api/getters/index.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
const Common = require('shock-common')
|
||||
|
||||
const Key = require('../key')
|
||||
const Utils = require('../utils')
|
||||
|
||||
const Wall = require('./wall')
|
||||
const Feed = require('./feed')
|
||||
const User = require('./user')
|
||||
|
||||
/**
|
||||
* @param {string} pub
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
exports.currentOrderAddress = async pub => {
|
||||
const currAddr = await Utils.tryAndWait(gun =>
|
||||
gun
|
||||
.user(pub)
|
||||
.get(Key.CURRENT_ORDER_ADDRESS)
|
||||
.then()
|
||||
)
|
||||
|
||||
if (typeof currAddr !== 'string') {
|
||||
throw new TypeError('Expected user.currentOrderAddress to be an string')
|
||||
}
|
||||
|
||||
return currAddr
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} pub
|
||||
* @returns {Promise<string|null>}
|
||||
*/
|
||||
exports.userToIncomingID = async pub => {
|
||||
const incomingID = await require('../../Mediator')
|
||||
.getUser()
|
||||
.get(Key.USER_TO_INCOMING)
|
||||
.get(pub)
|
||||
.then()
|
||||
|
||||
if (typeof incomingID === 'string') return incomingID
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<Common.SchemaTypes.User>}
|
||||
*/
|
||||
const getMyUser = async () => {
|
||||
const oldProfile = await Utils.tryAndWait(
|
||||
(_, user) => new Promise(res => user.get(Key.PROFILE).load(res)),
|
||||
v => typeof v !== 'object'
|
||||
)
|
||||
|
||||
const bio = await Utils.tryAndWait(
|
||||
(_, user) => user.get(Key.BIO).then(),
|
||||
v => typeof v !== 'string'
|
||||
)
|
||||
|
||||
const lastSeenApp = await Utils.tryAndWait(
|
||||
(_, user) => user.get(Key.LAST_SEEN_APP).then(),
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
const lastSeenNode = await Utils.tryAndWait(
|
||||
(_, user) => user.get(Key.LAST_SEEN_NODE).then(),
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
const publicKey = await Utils.tryAndWait(
|
||||
(_, user) => Promise.resolve(user.is && user.is.pub),
|
||||
v => typeof v !== 'string'
|
||||
)
|
||||
|
||||
/** @type {Common.SchemaTypes.User} */
|
||||
const u = {
|
||||
avatar: oldProfile.avatar,
|
||||
// @ts-ignore
|
||||
bio,
|
||||
displayName: oldProfile.displayName,
|
||||
// @ts-ignore
|
||||
lastSeenApp,
|
||||
// @ts-ignore
|
||||
lastSeenNode,
|
||||
// @ts-ignore
|
||||
publicKey
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
module.exports.getMyUser = getMyUser
|
||||
module.exports.Follows = require('./follows')
|
||||
|
||||
module.exports.getWallPage = Wall.getWallPage
|
||||
module.exports.getWallTotalPages = Wall.getWallTotalPages
|
||||
|
||||
module.exports.getFeedPage = Feed.getFeedPage
|
||||
module.exports.getAnUser = User.getAnUser
|
||||
111
services/gunDB/contact-api/getters/user.js
Normal file
111
services/gunDB/contact-api/getters/user.js
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
const Common = require('shock-common')
|
||||
|
||||
const Key = require('../key')
|
||||
const Utils = require('../utils')
|
||||
|
||||
/**
|
||||
* @param {string} publicKey
|
||||
* @returns {Promise<Common.SchemaTypes.User>}
|
||||
*/
|
||||
const getAnUser = async publicKey => {
|
||||
const oldProfile = await Utils.tryAndWait(
|
||||
g => {
|
||||
const user = g.get(`~${publicKey}`)
|
||||
|
||||
return new Promise(res => user.get(Key.PROFILE).load(res))
|
||||
},
|
||||
v => typeof v !== 'object'
|
||||
)
|
||||
|
||||
const bio = await Utils.tryAndWait(
|
||||
g =>
|
||||
g
|
||||
.get(`~${publicKey}`)
|
||||
.get(Key.BIO)
|
||||
.then(),
|
||||
v => typeof v !== 'string'
|
||||
)
|
||||
|
||||
const lastSeenApp = await Utils.tryAndWait(
|
||||
g =>
|
||||
g
|
||||
.get(`~${publicKey}`)
|
||||
.get(Key.LAST_SEEN_APP)
|
||||
.then(),
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
const lastSeenNode = await Utils.tryAndWait(
|
||||
(_, user) => user.get(Key.LAST_SEEN_NODE).then(),
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
/** @type {Common.SchemaTypes.User} */
|
||||
const u = {
|
||||
avatar: oldProfile.avatar,
|
||||
// @ts-ignore
|
||||
bio,
|
||||
displayName: oldProfile.displayName,
|
||||
// @ts-ignore
|
||||
lastSeenApp,
|
||||
// @ts-ignore
|
||||
lastSeenNode,
|
||||
// @ts-ignore
|
||||
publicKey
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
module.exports.getAnUser = getAnUser
|
||||
|
||||
/**
|
||||
* @returns {Promise<Common.SchemaTypes.User>}
|
||||
*/
|
||||
const getMyUser = async () => {
|
||||
const oldProfile = await Utils.tryAndWait(
|
||||
(_, user) => new Promise(res => user.get(Key.PROFILE).load(res)),
|
||||
v => typeof v !== 'object'
|
||||
)
|
||||
|
||||
const bio = await Utils.tryAndWait(
|
||||
(_, user) => user.get(Key.BIO).then(),
|
||||
v => typeof v !== 'string'
|
||||
)
|
||||
|
||||
const lastSeenApp = await Utils.tryAndWait(
|
||||
(_, user) => user.get(Key.LAST_SEEN_APP).then(),
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
const lastSeenNode = await Utils.tryAndWait(
|
||||
(_, user) => user.get(Key.LAST_SEEN_NODE).then(),
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
const publicKey = await Utils.tryAndWait(
|
||||
(_, user) => Promise.resolve(user.is && user.is.pub),
|
||||
v => typeof v !== 'string'
|
||||
)
|
||||
|
||||
/** @type {Common.SchemaTypes.User} */
|
||||
const u = {
|
||||
avatar: oldProfile.avatar,
|
||||
// @ts-ignore
|
||||
bio,
|
||||
displayName: oldProfile.displayName,
|
||||
// @ts-ignore
|
||||
lastSeenApp,
|
||||
// @ts-ignore
|
||||
lastSeenNode,
|
||||
// @ts-ignore
|
||||
publicKey
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
module.exports.getMyUser = getMyUser
|
||||
131
services/gunDB/contact-api/getters/wall.js
Normal file
131
services/gunDB/contact-api/getters/wall.js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
const Common = require('shock-common')
|
||||
|
||||
const Utils = require('../utils')
|
||||
const Key = require('../key')
|
||||
|
||||
const Wall = require('./user')
|
||||
|
||||
/**
|
||||
* @param {string=} publicKey
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
const getWallTotalPages = async publicKey => {
|
||||
const totalPages = await Utils.tryAndWait(
|
||||
(gun, u) => {
|
||||
const user = publicKey ? gun.get(`~${publicKey}`) : u
|
||||
|
||||
return user
|
||||
.get(Key.WALL)
|
||||
.get(Key.NUM_OF_PAGES)
|
||||
.then()
|
||||
},
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
return typeof totalPages === 'number' ? totalPages : 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} page
|
||||
* @param {string=} publicKey
|
||||
* @throws {TypeError}
|
||||
* @throws {RangeError}
|
||||
* @returns {Promise<Common.SchemaTypes.WallPage>}
|
||||
*/
|
||||
const getWallPage = async (page, publicKey) => {
|
||||
const totalPages = await getWallTotalPages(publicKey)
|
||||
|
||||
if (page === 0 || totalPages === 0) {
|
||||
return {
|
||||
count: 0,
|
||||
posts: {}
|
||||
}
|
||||
}
|
||||
|
||||
const actualPageIdx = page < 0 ? totalPages + page : page - 1
|
||||
|
||||
if (actualPageIdx > totalPages - 1) {
|
||||
throw new RangeError(`Requested a page out of bounds`)
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Common.SchemaTypes.WallPage}
|
||||
*/
|
||||
const thePage = await Utils.tryAndWait(
|
||||
(g, u) => {
|
||||
const user = publicKey ? g.get(`~${publicKey}`) : u
|
||||
|
||||
return new Promise(res => {
|
||||
user
|
||||
.get(Key.WALL)
|
||||
.get(Key.PAGES)
|
||||
.get(actualPageIdx.toString())
|
||||
// @ts-ignore
|
||||
.load(res)
|
||||
})
|
||||
},
|
||||
maybePage => {
|
||||
if (typeof maybePage !== 'object' || maybePage === null) {
|
||||
return true
|
||||
}
|
||||
|
||||
const clean = {
|
||||
...maybePage
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
for (const [key, post] of Object.entries(clean.posts)) {
|
||||
// delete unsuccessful writes
|
||||
if (post === null) {
|
||||
// @ts-ignore
|
||||
delete clean.posts[key]
|
||||
} else {
|
||||
post.id = key
|
||||
}
|
||||
}
|
||||
|
||||
// .load() sometimes doesn't load all data on first call
|
||||
// @ts-ignore
|
||||
if (Object.keys(clean.posts).length === 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
return !Common.Schema.isWallPage(clean)
|
||||
}
|
||||
)
|
||||
|
||||
const clean = {
|
||||
...thePage
|
||||
}
|
||||
|
||||
for (const [key, post] of Object.entries(clean.posts)) {
|
||||
// delete unsuccessful writes
|
||||
if (post === null) {
|
||||
delete clean.posts[key]
|
||||
clean.count--
|
||||
} else {
|
||||
post.author = publicKey
|
||||
? // eslint-disable-next-line no-await-in-loop
|
||||
await Wall.getAnUser(publicKey)
|
||||
: // eslint-disable-next-line no-await-in-loop
|
||||
await Wall.getMyUser()
|
||||
post.id = key
|
||||
}
|
||||
}
|
||||
|
||||
if (!Common.Schema.isWallPage(clean)) {
|
||||
throw new Error(
|
||||
`Fetched page not a wall page, instead got: ${JSON.stringify(clean)}`
|
||||
)
|
||||
}
|
||||
|
||||
return clean
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getWallTotalPages,
|
||||
getWallPage
|
||||
}
|
||||
|
|
@ -37,6 +37,15 @@ const lastSeenNode = user => {
|
|||
logger.error(`Error inside lastSeenNode job: ${ack.err}`)
|
||||
}
|
||||
})
|
||||
|
||||
user
|
||||
.get(Key.PROFILE)
|
||||
.get(Key.LAST_SEEN_NODE)
|
||||
.put(Date.now(), ack => {
|
||||
if (ack.err) {
|
||||
logger.error(`Error inside lastSeenNode job: ${ack.err}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, LAST_SEEN_NODE_INTERVAL)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,3 +42,16 @@ exports.CHANNELS_BACKUP = 'channelsBackup'
|
|||
exports.LAST_SEEN_APP = 'lastSeenApp'
|
||||
|
||||
exports.LAST_SEEN_NODE = 'lastSeenNode'
|
||||
|
||||
exports.WALL = 'wall'
|
||||
|
||||
exports.NUM_OF_PAGES = 'numOfPages'
|
||||
|
||||
exports.PAGES = 'pages'
|
||||
|
||||
exports.COUNT = 'count'
|
||||
|
||||
exports.CONTENT_ITEMS = 'contentItems'
|
||||
exports.FOLLOWS = 'follows'
|
||||
|
||||
exports.POSTS = 'posts'
|
||||
|
|
|
|||
256
src/routes.js
256
src/routes.js
|
|
@ -12,7 +12,7 @@ const httpsAgent = require("https");
|
|||
const responseTime = require("response-time");
|
||||
const uuid = require("uuid/v4");
|
||||
const Common = require('shock-common')
|
||||
|
||||
const isARealUsableNumber = require('lodash/isFinite')
|
||||
|
||||
const getListPage = require("../utils/paginate");
|
||||
const auth = require("../services/auth/auth");
|
||||
|
|
@ -22,6 +22,7 @@ const LightningServices = require("../utils/lightningServices");
|
|||
const GunDB = require("../services/gunDB/Mediator");
|
||||
const { unprotectedRoutes, nonEncryptedRoutes } = require("../utils/protectedRoutes");
|
||||
const GunActions = require("../services/gunDB/contact-api/actions")
|
||||
const GunGetters = require('../services/gunDB/contact-api/getters')
|
||||
|
||||
const DEFAULT_MAX_NUM_ROUTES_TO_QUERY = 10;
|
||||
const SESSION_ID = uuid();
|
||||
|
|
@ -33,6 +34,8 @@ module.exports = async (
|
|||
mySocketsEvents,
|
||||
{ serverPort, CA, CA_KEY, usetls }
|
||||
) => {
|
||||
const {timeout5} = require('../services/gunDB/contact-api/utils')
|
||||
|
||||
const Http = Axios.create({
|
||||
httpsAgent: new httpsAgent.Agent({
|
||||
ca: await FS.readFile(CA)
|
||||
|
|
@ -227,7 +230,7 @@ module.exports = async (
|
|||
const deviceId = req.headers["x-shockwallet-device-id"];
|
||||
logger.debug("Decrypting route...")
|
||||
try {
|
||||
if (nonEncryptedRoutes.includes(req.path)) {
|
||||
if (nonEncryptedRoutes.includes(req.path) || process.env.DISABLE_SHOCK_ENCRYPTION === "true") {
|
||||
return next();
|
||||
}
|
||||
|
||||
|
|
@ -249,7 +252,7 @@ module.exports = async (
|
|||
return res.status(401).json(error);
|
||||
}
|
||||
|
||||
if (req.method === "GET") {
|
||||
if (req.method === "GET" || req.method === "DELETE" || !req.body.encryptionKey && !req.body.iv) {
|
||||
return next();
|
||||
}
|
||||
|
||||
|
|
@ -434,6 +437,76 @@ module.exports = async (
|
|||
|
||||
});
|
||||
|
||||
/*
|
||||
const feedObj = {
|
||||
feed: [
|
||||
{
|
||||
id:'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
|
||||
paragraphs:[
|
||||
"SOme text and stuff 12"
|
||||
"SOme text and stuff"
|
||||
],
|
||||
profilePic:"",
|
||||
username:"bobni",
|
||||
media:[
|
||||
{
|
||||
type:'VIDEO',
|
||||
ratio_x: 1024,
|
||||
ratio_y: 436,
|
||||
magnetUri:'magnet:?xt=urn:btih:08ada5a7a6183aae1e09d831df6748d566095a10&dn=Sintel&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fsintel.torrent',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
id:'3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
|
||||
paragraphs:[
|
||||
"SOme text and stuff"
|
||||
],
|
||||
profilePic:"",
|
||||
username:"bobni",
|
||||
media:[
|
||||
{
|
||||
type:'VIDEO',
|
||||
ratio_x: 1920,
|
||||
ratio_y: 804,
|
||||
magnetUri:'magnet:?xt=urn:btih:c9e15763f722f23e98a29decdfae341b98d53056&dn=Cosmos+Laundromat&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fcosmos-laundromat.torrent',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
id:'58694a0f-3da1-471f-bd96-145571e29d72',
|
||||
paragraphs:[
|
||||
"SOme text and stuff"
|
||||
],
|
||||
profilePic:"",
|
||||
username:"bobni",
|
||||
media:[
|
||||
{
|
||||
type:'VIDEO',
|
||||
ratio_x: 1920,
|
||||
ratio_y: 1080,
|
||||
magnetUri:'magnet:?xt=urn:btih:dd8255ecdc7ca55fb0bbf81323d87062db1f6d1c&dn=Big+Buck+Bunny&tr=udp%3A%2F%2Fexplodie.org%3A6969&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969&tr=udp%3A%2F%2Ftracker.empire-js.us%3A1337&tr=udp%3A%2F%2Ftracker.leechers-paradise.org%3A6969&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337&tr=wss%3A%2F%2Ftracker.btorrent.xyz&tr=wss%3A%2F%2Ftracker.fastcast.nz&tr=wss%3A%2F%2Ftracker.openwebtorrent.com&ws=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2F&xs=https%3A%2F%2Fwebtorrent.io%2Ftorrents%2Fbig-buck-bunny.torrent',
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
user.get("FEED_POC").put(JSON.stringify(feedObj), ack => {
|
||||
if (ack.err) {
|
||||
//rej(new Error(ack.err))
|
||||
}*/
|
||||
const feedObj = {
|
||||
feed :{}
|
||||
}
|
||||
user.get("FEED_POC").put(feedObj, ack => {
|
||||
if (ack.err) {
|
||||
//rej(ack.err)
|
||||
logger.log(ack.err)
|
||||
} else {
|
||||
logger.log(ack.err)
|
||||
}
|
||||
})
|
||||
|
||||
//register to listen for channel backups
|
||||
const onNewChannelBackup = () => {
|
||||
logger.warn("Subscribing to channel backup ...")
|
||||
|
|
@ -1715,8 +1788,6 @@ module.exports = async (
|
|||
|
||||
const GunEvent = Common.Constants.Event
|
||||
const Key = require('../services/gunDB/contact-api/key')
|
||||
const { timeout5 } = require('../services/gunDB/contact-api/utils')
|
||||
|
||||
app.get("/api/gun/lndchanbackups", async (req,res) => {
|
||||
try{
|
||||
const user = require('../services/gunDB/Mediator').getUser()
|
||||
|
|
@ -1731,6 +1802,18 @@ module.exports = async (
|
|||
res.json({ok:"err"})
|
||||
}
|
||||
})
|
||||
app.get("/api/gun/feedpoc", async (req,res) =>{
|
||||
try{
|
||||
logger.warn("FEED POC")
|
||||
const user = require('../services/gunDB/Mediator').getUser()
|
||||
const feedObj = await timeout5(user.get("FEED_POC").then())
|
||||
logger.warn(feedObj)
|
||||
|
||||
res.json({data:feedObj})
|
||||
} catch (err) {
|
||||
//res.json({ok:"err"})
|
||||
}
|
||||
})
|
||||
|
||||
const Events = require('../services/gunDB/contact-api/events')
|
||||
|
||||
|
|
@ -1854,6 +1937,169 @@ module.exports = async (
|
|||
})
|
||||
}
|
||||
})
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
app.get(`/api/gun/wall/:publicKey?`, async (req, res) => {
|
||||
try {
|
||||
const { page } = req.query;
|
||||
const {publicKey} = req.params
|
||||
|
||||
const pageNum = Number(page)
|
||||
|
||||
if (!isARealUsableNumber(pageNum)) {
|
||||
return res.status(400).json({
|
||||
field: 'page',
|
||||
errorMessage: 'Not a number'
|
||||
})
|
||||
}
|
||||
|
||||
const totalPages = await GunGetters.getWallTotalPages(publicKey)
|
||||
const fetchedPage = await GunGetters.getWallPage(pageNum, publicKey)
|
||||
|
||||
return res.status(200).json({
|
||||
...fetchedPage,
|
||||
totalPages,
|
||||
})
|
||||
} catch (err) {
|
||||
return res.status(500).json({
|
||||
errorMessage: err.message
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
app.post(`/api/gun/wall/`, async (req,res) => {
|
||||
try{
|
||||
const {tags,title,contentItems} = req.body
|
||||
return res.status(200).json(await GunActions.createPost(
|
||||
tags,
|
||||
title,
|
||||
contentItems
|
||||
))
|
||||
} catch(e) {
|
||||
return res.status(500).json({
|
||||
errorMessage: (typeof e === 'string' ? e : e.message)
|
||||
|| 'Unknown error.'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
app.delete(`/api/gun/wall/:postID`, (_, res) => res.status(200).json({
|
||||
ok: 'true'
|
||||
}))
|
||||
/////////////////////////////////
|
||||
/**
|
||||
* @template P
|
||||
* @typedef {import('express-serve-static-core').RequestHandler<P>} RequestHandler
|
||||
*/
|
||||
|
||||
|
||||
const ap = /** @type {Application} */ (app);
|
||||
|
||||
/**
|
||||
* @typedef {object} FollowsRouteParams
|
||||
* @prop {(string|undefined)=} publicKey
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @type {RequestHandler<FollowsRouteParams>}
|
||||
*/
|
||||
const apiGunFollowsGet = async (_, res) => {
|
||||
try {
|
||||
const currFollows = await GunGetters.Follows.currentFollows()
|
||||
|
||||
return res.status(200).json(currFollows)
|
||||
} catch (err) {
|
||||
return res.status(500).json({
|
||||
errorMessage: err.message || 'Unknown ERR at GET /api/follows'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @type {RequestHandler<FollowsRouteParams>}
|
||||
*/
|
||||
const apiGunFollowsPut = async (req, res) => {
|
||||
try {
|
||||
const { publicKey } = req.params;
|
||||
if (!publicKey) {
|
||||
throw new Error(`Missing publicKey route param.`)
|
||||
}
|
||||
|
||||
await GunActions.follow(req.params.publicKey, false)
|
||||
|
||||
// 201 would be extraneous here. Implement it inside app.put
|
||||
return res.status(200).json({
|
||||
ok: true
|
||||
})
|
||||
} catch (err) {
|
||||
return res.status(500).json({
|
||||
errorMessage: err.message || 'Unknown error inside /api/gun/follows/'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {RequestHandler<FollowsRouteParams>}
|
||||
*/
|
||||
const apiGunFollowsDelete = async (req, res) => {
|
||||
try {
|
||||
const { publicKey } = req.params;
|
||||
if (!publicKey) {
|
||||
throw new Error(`Missing publicKey route param.`)
|
||||
}
|
||||
|
||||
await GunActions.unfollow(req.params.publicKey)
|
||||
|
||||
return res.status(200).json({
|
||||
ok: true
|
||||
})
|
||||
} catch (err) {
|
||||
return res.status(500).json({
|
||||
errorMessage: err.message || 'Unknown error inside /api/gun/follows/'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ap.get('/api/gun/follows/', apiGunFollowsGet)
|
||||
ap.get('/api/gun/follows/:publicKey', apiGunFollowsGet)
|
||||
ap.put(`/api/gun/follows/:publicKey`,apiGunFollowsPut)
|
||||
ap.delete(`/api/gun/follows/:publicKey`, apiGunFollowsDelete)
|
||||
|
||||
/**
|
||||
* @type {RequestHandler<{}>}
|
||||
*/
|
||||
const apiGunFeedGet = async (req, res) => {
|
||||
try {
|
||||
const { page: pageStr } = req.query;
|
||||
const page = Number(pageStr)
|
||||
|
||||
if (!isARealUsableNumber(page)) {
|
||||
return res.status(400).json({
|
||||
field: 'page',
|
||||
errorMessage: 'page must be a number'
|
||||
})
|
||||
}
|
||||
|
||||
if (page < 1) {
|
||||
return res.status(400).json({
|
||||
field: page,
|
||||
errorMessage: 'page must be a positive number'
|
||||
})
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
posts: await GunGetters.getFeedPage(page)
|
||||
})
|
||||
} catch (err) {
|
||||
return res.status(500).json({
|
||||
errorMessage: err.message || 'Unknown error inside /api/gun/follows/'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ap.get(`/api/gun/feed`, apiGunFeedGet)
|
||||
|
||||
/**
|
||||
* Return app so that it can be used by express.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const server = program => {
|
|||
const Http = require('http')
|
||||
const Express = require('express')
|
||||
const Crypto = require('crypto')
|
||||
const Dotenv = require('dotenv')
|
||||
const LightningServices = require('../utils/lightningServices')
|
||||
const Encryption = require('../utils/encryptionStore')
|
||||
const app = Express()
|
||||
|
|
@ -25,6 +26,7 @@ const server = program => {
|
|||
// load app default configuration data
|
||||
const defaults = require('../config/defaults')(program.mainnet)
|
||||
// define useful global variables ======================================
|
||||
Dotenv.config()
|
||||
module.useTLS = program.usetls
|
||||
module.serverPort = program.serverport || defaults.serverPort
|
||||
module.httpsPort = module.serverPort
|
||||
|
|
@ -41,6 +43,12 @@ const server = program => {
|
|||
|
||||
logger.info('Mainnet Mode:', !!program.mainnet)
|
||||
|
||||
if (process.env.DISABLE_SHOCK_ENCRYPTION === 'true') {
|
||||
logger.error('Encryption Mode: false')
|
||||
} else {
|
||||
logger.info('Encryption Mode: true')
|
||||
}
|
||||
|
||||
const stringifyData = data => {
|
||||
if (typeof data === 'object') {
|
||||
const stringifiedData = JSON.stringify(data)
|
||||
|
|
@ -60,6 +68,46 @@ const server = program => {
|
|||
.digest('hex')
|
||||
}
|
||||
|
||||
const cacheCheck = ({ req, res, args, send }) => {
|
||||
if (
|
||||
(process.env.SHOCK_CACHE === 'true' || !process.env.SHOCK_CACHE) &&
|
||||
req.method === 'GET'
|
||||
) {
|
||||
const dataHash = hashData(args[0]).slice(-8)
|
||||
res.set('shock-cache-hash', dataHash)
|
||||
|
||||
logger.debug('shock-cache-hash:', req.headers['shock-cache-hash'])
|
||||
logger.debug('Data Hash:', dataHash)
|
||||
if (
|
||||
!req.headers['shock-cache-hash'] &&
|
||||
(process.env.CACHE_HEADERS_MANDATORY === 'true' ||
|
||||
!process.env.CACHE_HEADERS_MANDATORY)
|
||||
) {
|
||||
logger.warn(
|
||||
"Request is missing 'shock-cache-hash' header, please make sure to include that in each GET request in order to benefit from reduced data usage"
|
||||
)
|
||||
return { cached: false, hash: dataHash }
|
||||
}
|
||||
|
||||
if (req.headers['shock-cache-hash'] === dataHash) {
|
||||
logger.debug('Same Hash Detected!')
|
||||
args[0] = null
|
||||
res.status(304)
|
||||
send.apply(res, args)
|
||||
return { cached: true, hash: dataHash }
|
||||
}
|
||||
|
||||
return { cached: false, hash: dataHash }
|
||||
}
|
||||
|
||||
return { cached: false, hash: null }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @param {(() => void)} next
|
||||
*/
|
||||
const modifyResponseBody = (req, res, next) => {
|
||||
const deviceId = req.headers['x-shockwallet-device-id']
|
||||
const oldSend = res.send
|
||||
|
|
@ -72,16 +120,9 @@ const server = program => {
|
|||
return
|
||||
}
|
||||
|
||||
const dataHash = hashData(args[0]).slice(-8)
|
||||
res.set('shock-cache-hash', dataHash)
|
||||
const { cached, hash } = cacheCheck({ req, res, args, send: oldSend })
|
||||
|
||||
logger.debug('shock-cache-hash:', req.headers['shock-cache-hash'])
|
||||
logger.debug('Data Hash:', dataHash)
|
||||
if (req.headers['shock-cache-hash'] === dataHash) {
|
||||
logger.debug('Same Hash Detected!')
|
||||
args[0] = null
|
||||
res.status(304)
|
||||
oldSend.apply(res, args)
|
||||
if (cached) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -89,10 +130,10 @@ const server = program => {
|
|||
const authorized = Encryption.isAuthorizedDevice({ deviceId })
|
||||
const encryptedMessage = authorized
|
||||
? Encryption.encryptMessage({
|
||||
message: args[0],
|
||||
message: args[0] ? args[0] : {},
|
||||
deviceId,
|
||||
metadata: {
|
||||
hash: dataHash
|
||||
hash
|
||||
}
|
||||
})
|
||||
: args[0]
|
||||
|
|
@ -249,7 +290,9 @@ const server = program => {
|
|||
// app.use(bodyParser.json({limit: '100000mb'}));
|
||||
app.use(bodyParser.json({ limit: '50mb' }))
|
||||
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }))
|
||||
if (process.env.DISABLE_SHOCK_ENCRYPTION !== 'true') {
|
||||
app.use(modifyResponseBody)
|
||||
}
|
||||
|
||||
serverInstance.listen(module.serverPort, module.serverhost)
|
||||
|
||||
|
|
|
|||
49
yarn.lock
49
yarn.lock
|
|
@ -633,6 +633,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||
|
||||
"@types/ramda@types/npm-ramda#dist":
|
||||
version "0.25.0"
|
||||
resolved "https://codeload.github.com/types/npm-ramda/tar.gz/9529aa3c8ff70ff84afcbc0be83443c00f30ea90"
|
||||
|
||||
"@types/range-parser@*":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
|
||||
|
|
@ -3168,6 +3172,11 @@ ignore@^4.0.6:
|
|||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
||||
|
||||
immer@^6.0.6:
|
||||
version "6.0.9"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-6.0.9.tgz#b9dd69b8e69b3a12391e87db1e3ff535d1b26485"
|
||||
integrity sha512-SyCYnAuiRf67Lvk0VkwFvwtDoEiCMjeamnHvRfnVDyc7re1/rQrNxuL+jJ7lA3WvdC4uznrvbmm+clJ9+XXatg==
|
||||
|
||||
import-fresh@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118"
|
||||
|
|
@ -5383,6 +5392,11 @@ ramda@^0.26.1:
|
|||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
|
||||
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
|
||||
|
||||
ramda@^0.27.0:
|
||||
version "0.27.0"
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.0.tgz#915dc29865c0800bf3f69b8fd6c279898b59de43"
|
||||
integrity sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==
|
||||
|
||||
random-bytes@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
|
||||
|
|
@ -5500,6 +5514,19 @@ rechoir@^0.6.2:
|
|||
dependencies:
|
||||
resolve "^1.1.6"
|
||||
|
||||
redux-thunk@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
|
||||
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
|
||||
|
||||
redux@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
|
||||
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
symbol-observable "^1.2.0"
|
||||
|
||||
regenerator-runtime@^0.11.0:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
|
|
@ -5876,13 +5903,17 @@ shellwords@^0.1.1:
|
|||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
||||
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
||||
|
||||
shock-common@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-0.0.1.tgz#72092c565ab65198da13656b5027fbd44881bf72"
|
||||
integrity sha512-LV2WiJDr1E6TEWel095oLN6gxpGTmsg6CUeGB6DdLHbYEz0qSpcDG4MYp2mZGpj/DejNKwYg1EiX2qf7ArpIkQ==
|
||||
shock-common@4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/shock-common/-/shock-common-4.0.1.tgz#035e7081b6e67f6721e68dcc6b4d1e4c8f2cd96d"
|
||||
integrity sha512-3xAkG8lyfyZHK8trgOy2aN75uG1ZBm0MPoIEzP4hgXhyT/b80WmQzX3DqVSSmjfhq1Di0sjmNCY7O5Nf6cEmFg==
|
||||
dependencies:
|
||||
immer "^6.0.6"
|
||||
lodash "^4.17.15"
|
||||
normalizr "^3.6.0"
|
||||
redux "^4.0.5"
|
||||
redux-thunk "^2.3.0"
|
||||
uuid "3.x.x"
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.2"
|
||||
|
|
@ -6302,6 +6333,11 @@ supports-color@^7.1.0:
|
|||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
symbol-observable@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
||||
|
||||
symbol-tree@^3.2.2:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||
|
|
@ -6672,6 +6708,11 @@ utils-merge@1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||
|
||||
uuid@3.x.x:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
uuid@^3.3.2:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue