commit
9c8d36e380
8 changed files with 480 additions and 3 deletions
|
|
@ -7,6 +7,8 @@ const logger = require('winston')
|
|||
Gun.log = () => {}
|
||||
// @ts-ignore
|
||||
require('gun/lib/open')
|
||||
// @ts-ignore
|
||||
require('gun/lib/load')
|
||||
const debounce = require('lodash/debounce')
|
||||
const Encryption = require('../../../utils/encryptionStore')
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1216,8 +1231,229 @@ 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.
|
||||
|
|
@ -1285,6 +1521,8 @@ module.exports = {
|
|||
saveChannelsBackup,
|
||||
disconnect,
|
||||
setLastSeenApp,
|
||||
createPost,
|
||||
deletePost,
|
||||
follow,
|
||||
unfollow
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
const Common = require('shock-common')
|
||||
|
||||
const Key = require('../key')
|
||||
const Utils = require('../utils')
|
||||
|
||||
const Wall = require('./wall')
|
||||
|
||||
/**
|
||||
* @param {string} pub
|
||||
* @returns {Promise<string>}
|
||||
|
|
@ -39,4 +43,54 @@ exports.userToIncomingID = async pub => {
|
|||
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
|
||||
|
|
|
|||
114
services/gunDB/contact-api/getters/wall.js
Normal file
114
services/gunDB/contact-api/getters/wall.js
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* @format
|
||||
*/
|
||||
const Common = require('shock-common')
|
||||
const Utils = require('../utils')
|
||||
const Key = require('../key')
|
||||
|
||||
/**
|
||||
* @returns {Promise<number>}
|
||||
*/
|
||||
const getWallTotalPages = async () => {
|
||||
const totalPages = await Utils.tryAndWait(
|
||||
(_, user) =>
|
||||
user
|
||||
.get(Key.WALL)
|
||||
.get(Key.NUM_OF_PAGES)
|
||||
.then(),
|
||||
v => typeof v !== 'number'
|
||||
)
|
||||
|
||||
return typeof totalPages === 'number' ? totalPages : 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} page
|
||||
* @throws {TypeError}
|
||||
* @throws {RangeError}
|
||||
* @returns {Promise<Common.SchemaTypes.WallPage>}
|
||||
*/
|
||||
const getWallPage = async page => {
|
||||
const totalPages = await getWallTotalPages()
|
||||
|
||||
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(
|
||||
(_, user) =>
|
||||
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]
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,4 +43,15 @@ 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'
|
||||
|
|
|
|||
|
|
@ -12,6 +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");
|
||||
|
|
@ -1689,7 +1690,7 @@ module.exports = async (
|
|||
|
||||
const GunEvent = Common.Constants.Event
|
||||
const Key = require('../services/gunDB/contact-api/key')
|
||||
|
||||
|
||||
app.get("/api/gun/lndchanbackups", async (req,res) => {
|
||||
try{
|
||||
const user = require('../services/gunDB/Mediator').getUser()
|
||||
|
|
@ -1839,7 +1840,54 @@ module.exports = async (
|
|||
})
|
||||
}
|
||||
})
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
app.get(`/api/gun/wall`, async (req, res) => {
|
||||
try {
|
||||
const { page } = req.query;
|
||||
|
||||
const pageNum = Number(page)
|
||||
|
||||
if (!isARealUsableNumber(pageNum)) {
|
||||
return res.status(400).json({
|
||||
field: 'page',
|
||||
errorMessage: 'Not a number'
|
||||
})
|
||||
}
|
||||
|
||||
const totalPages = await GunGetters.getWallTotalPages()
|
||||
const fetchedPage = await GunGetters.getWallPage(pageNum)
|
||||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue