|
@@ -12,21 +12,11 @@ import {
|
|
|
Proposal,
|
|
|
Thread,
|
|
|
} from '../db/models'
|
|
|
-const models: { [key: string]: any } = {
|
|
|
- channel: Channel,
|
|
|
- proposal: Proposal,
|
|
|
- category: Category,
|
|
|
- thread: Thread,
|
|
|
- post: Post,
|
|
|
- block: Block,
|
|
|
- council: Council,
|
|
|
- member: Member,
|
|
|
- era: Era,
|
|
|
-}
|
|
|
-
|
|
|
import * as get from './lib/getters'
|
|
|
+//import {fetchReports} from './lib/github'
|
|
|
import axios from 'axios'
|
|
|
import moment from 'moment'
|
|
|
+import chalk from 'chalk'
|
|
|
|
|
|
import { VoteKind } from '@joystream/types/proposals'
|
|
|
import { EventRecord } from '@polkadot/types/interfaces'
|
|
@@ -50,50 +40,44 @@ import { AccountId, Moment, ActiveEraInfo } from '@polkadot/types/interfaces'
|
|
|
import Option from '@polkadot/types/codec/Option'
|
|
|
import { Vec } from '@polkadot/types'
|
|
|
|
|
|
-// queuing
|
|
|
+const DELAY = 0 // ms
|
|
|
let lastUpdate = 0
|
|
|
-const queue: any[] = []
|
|
|
-let inProgress = false
|
|
|
-const enqueue = (fn: any) => {
|
|
|
- queue.push(fn)
|
|
|
- processNext()
|
|
|
-}
|
|
|
-const processNext = async () => {
|
|
|
- if (inProgress) return
|
|
|
- inProgress = true
|
|
|
- const task = queue.pop()
|
|
|
- if (task) await task()
|
|
|
+let queuedAll = false
|
|
|
+let processing = false
|
|
|
+let queue: any[] = []
|
|
|
+let fetching = ''
|
|
|
|
|
|
- inProgress = false
|
|
|
- //processNext()
|
|
|
- //return queue.length
|
|
|
-}
|
|
|
+const getBlockHash = (api: Api, blockId: number) =>
|
|
|
+ api.rpc.chain.getBlockHash(blockId)
|
|
|
|
|
|
-const save = async (model: any, data: any) => {
|
|
|
- const Model = models[model]
|
|
|
- try {
|
|
|
- const exists = await Model.findByPk(data.id)
|
|
|
- if (exists) return exists.update(data)
|
|
|
- } catch (e) {}
|
|
|
- //console.debug(`saving ${data.id}`, `queued tasks: ${queue.length}`)
|
|
|
- try {
|
|
|
- return Model.create(data)
|
|
|
- } catch (e) {
|
|
|
- console.warn(`Failed to save ${Model}`, e.message)
|
|
|
- }
|
|
|
-}
|
|
|
+const getEraAtBlock = (api: Api, hash: string) =>
|
|
|
+ api.query.staking.activeEra.at(hash)
|
|
|
|
|
|
const addBlock = async (
|
|
|
api: Api,
|
|
|
io: any,
|
|
|
header: { number: number; author: string },
|
|
|
- status: Status
|
|
|
+ status: Status = {
|
|
|
+ era: 0,
|
|
|
+ members: 0,
|
|
|
+ channels: 0,
|
|
|
+ categories: 0,
|
|
|
+ threads: 0,
|
|
|
+ posts: 0,
|
|
|
+ proposals: 0,
|
|
|
+ proposalPosts: 0,
|
|
|
+ }
|
|
|
): Promise<Status> => {
|
|
|
const id = +header.number
|
|
|
const last = await Block.findByPk(id - 1)
|
|
|
+ const exists = await Block.findByPk(id)
|
|
|
+ if (exists) {
|
|
|
+ console.error(`TODO handle fork`, String(header.author))
|
|
|
+ return status
|
|
|
+ }
|
|
|
const timestamp = moment.utc(await api.query.timestamp.now()).valueOf()
|
|
|
const blocktime = last ? timestamp - last.timestamp : 6000
|
|
|
- const block = await save('block', { id, timestamp, blocktime })
|
|
|
+ const block = await Block.create({ id, timestamp, blocktime })
|
|
|
io.emit('block', block)
|
|
|
|
|
|
const author = header.author?.toString()
|
|
@@ -102,15 +86,18 @@ const addBlock = async (
|
|
|
updateBalances(api, id)
|
|
|
|
|
|
const currentEra = Number(await api.query.staking.currentEra())
|
|
|
- const era = await save('era', { id: currentEra })
|
|
|
- era.addBlock(block.id)
|
|
|
+ Era.findOrCreate({ where: { id: currentEra } }).then(() =>
|
|
|
+ block.setEra(currentEra)
|
|
|
+ )
|
|
|
|
|
|
const handle = member ? member.handle : author
|
|
|
- const queued = `(queued: ${queue.length})`
|
|
|
- console.log(`[Joystream] block ${block.id} ${handle} ${queued}`)
|
|
|
+ const f = fetching !== '' ? `, fetching ${fetching}` : ''
|
|
|
+ const q = queue.length ? ` (${queue.length} queued${f})` : ''
|
|
|
+ console.log(`[Joystream] block ${block.id} ${handle}${q}`)
|
|
|
|
|
|
- await processEvents(api, id)
|
|
|
- return updateEra(api, io, status, currentEra)
|
|
|
+ processEvents(api, id)
|
|
|
+ //updateEra(api, io, status, currentEra)
|
|
|
+ return updateStatus(api, status, currentEra)
|
|
|
}
|
|
|
|
|
|
const addBlockRange = async (
|
|
@@ -153,7 +140,7 @@ const addBlockRange = async (
|
|
|
|
|
|
const timestamp = (await api.query.timestamp.now.at(hash)) as Moment
|
|
|
const date = new Date(timestamp.toNumber())
|
|
|
- await save('era', {
|
|
|
+ await Era.create({
|
|
|
id: blockEra.unwrap().index.toNumber(),
|
|
|
waiting: waiting,
|
|
|
actives: actives,
|
|
@@ -165,8 +152,62 @@ const addBlockRange = async (
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const getBlockHash = (api: Api, blockId: number) =>
|
|
|
- api.rpc.chain.getBlockHash(blockId)
|
|
|
+const updateStatus = async (api: Api, old: Status, era: number) => {
|
|
|
+ const status = {
|
|
|
+ era,
|
|
|
+ members: (await api.query.members.nextMemberId()) - 1,
|
|
|
+ channels: await get.currentChannelId(api),
|
|
|
+
|
|
|
+ categories: await get.currentCategoryId(api),
|
|
|
+ threads: await get.currentThreadId(api),
|
|
|
+ posts: await get.currentPostId(api),
|
|
|
+
|
|
|
+ proposals: await get.proposalCount(api),
|
|
|
+ proposalPosts: await api.query.proposalsDiscussion.postCount(),
|
|
|
+ }
|
|
|
+ if (!queuedAll) fetchAll(api, status)
|
|
|
+ else {
|
|
|
+ // TODO catch if more than one are added
|
|
|
+ status.members > old.members && fetchMember(api, status.members)
|
|
|
+ status.posts > old.posts && fetchPost(api, status.posts)
|
|
|
+ status.proposals > old.proposals && fetchProposal(api, status.proposals)
|
|
|
+ status.channels > old.channels && fetchChannel(api, status.channels)
|
|
|
+ status.categories > old.categories && fetchCategory(api, status.categories)
|
|
|
+ }
|
|
|
+ return status
|
|
|
+}
|
|
|
+
|
|
|
+const fetchAll = async (api: Api, status: Status) => {
|
|
|
+ // trying to avoid SequelizeUniqueConstraintError
|
|
|
+
|
|
|
+ for (let id = status.members; id > 0; id--) {
|
|
|
+ queue.push(() => fetchMember(api, id))
|
|
|
+ }
|
|
|
+ for (let id = status.posts; id > 0; id--) {
|
|
|
+ queue.push(() => fetchPost(api, id))
|
|
|
+ }
|
|
|
+ for (let id = status.proposals; id > 0; id--) {
|
|
|
+ queue.push(() => fetchProposal(api, id))
|
|
|
+ }
|
|
|
+ for (let id = status.channels; id > 0; id--) {
|
|
|
+ queue.push(() => fetchChannel(api, id))
|
|
|
+ }
|
|
|
+ for (let id = status.categories; id > 0; id--) {
|
|
|
+ queue.push(() => fetchCategory(api, id))
|
|
|
+ }
|
|
|
+ queuedAll = true
|
|
|
+ processNext()
|
|
|
+}
|
|
|
+
|
|
|
+const processNext = async () => {
|
|
|
+ if (processing) return
|
|
|
+ processing = true
|
|
|
+ const task = queue.pop()
|
|
|
+ if (!task) return
|
|
|
+ const result = await task()
|
|
|
+ processing = false
|
|
|
+ setTimeout(() => processNext(), DELAY)
|
|
|
+}
|
|
|
|
|
|
const processEvents = async (api: Api, blockId: number) => {
|
|
|
const blockHash = await getBlockHash(api, blockId)
|
|
@@ -175,6 +216,7 @@ const processEvents = async (api: Api, blockId: number) => {
|
|
|
let { section, method, data } = event
|
|
|
Event.create({ blockId, section, method, data: JSON.stringify(data) })
|
|
|
})
|
|
|
+ // TODO catch votes, posts, proposals?
|
|
|
}
|
|
|
|
|
|
const updateEra = async (api: Api, io: any, status: any, era: number) => {
|
|
@@ -188,10 +230,9 @@ const updateEra = async (api: Api, io: any, status: any, era: number) => {
|
|
|
// imOnline.authoredBlocks: 2
|
|
|
// session.currentIndex: 17,081
|
|
|
|
|
|
- const lastReward = Number(await api.query.staking.erasValidatorReward(era))
|
|
|
- console.debug(`last reward`, era, lastReward)
|
|
|
- if (lastReward > 0) {
|
|
|
- } // TODO save lastReward
|
|
|
+ //const lastReward = Number(await api.query.staking.erasValidatorReward(era))
|
|
|
+ //console.debug(`last reward`, era, lastReward)
|
|
|
+ //if (lastReward > 0) {} // TODO save lastReward
|
|
|
|
|
|
const nominatorEntries = await api.query.staking.nominators.entries()
|
|
|
const nominators = nominatorEntries.map((n: any) => String(n[0].toHuman()))
|
|
@@ -202,9 +243,8 @@ const updateEra = async (api: Api, io: any, status: any, era: number) => {
|
|
|
|
|
|
// TODO staking.bondedEras: Vec<(EraIndex,SessionIndex)>
|
|
|
const stashes = await api.derive.staking.stashes()
|
|
|
- console.debug(`fetching stakes`)
|
|
|
-
|
|
|
- if (!stashes) return
|
|
|
+ fetching = `stakes`
|
|
|
+ //if (!stashes) return
|
|
|
|
|
|
for (let validator of stashes) {
|
|
|
try {
|
|
@@ -217,18 +257,6 @@ const updateEra = async (api: Api, io: any, status: any, era: number) => {
|
|
|
console.warn(`Failed to fetch stakes for ${validator} in era ${era}`, e)
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- return {
|
|
|
- members: (await api.query.members.nextMemberId()) - 1,
|
|
|
- categories: await get.currentCategoryId(api),
|
|
|
- threads: await get.currentThreadId(api),
|
|
|
- proposals: await get.proposalCount(api),
|
|
|
- channels: await get.currentChannelId(api),
|
|
|
- posts: await get.currentPostId(api),
|
|
|
- proposalPosts: await api.query.proposalsDiscussion.postCount(),
|
|
|
- queued: queue.length,
|
|
|
- era,
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
const validatorStatus = async (api: Api, blockId: number) => {
|
|
@@ -252,6 +280,7 @@ const getAccountAtBlock = (
|
|
|
account: string
|
|
|
): Promise<AccountInfo> => api.query.system.account.at(hash, account)
|
|
|
|
|
|
+// TODO when to cal
|
|
|
const fetchAccounts = async (api: Api, blockId: number) => {
|
|
|
api.query.system.account.entries().then((account: any) => {
|
|
|
const address = account[0].toHuman()[0]
|
|
@@ -288,88 +317,68 @@ const fetchTokenomics = async () => {
|
|
|
}
|
|
|
|
|
|
const fetchChannel = async (api: Api, id: number) => {
|
|
|
+ if (id <= 0) return
|
|
|
const exists = await Channel.findByPk(id)
|
|
|
if (exists) return exists
|
|
|
|
|
|
- console.debug(`Fetching channel ${id}`)
|
|
|
+ fetching = `channel ${id}`
|
|
|
const data = await api.query.contentWorkingGroup.channelById(id)
|
|
|
-
|
|
|
- const handle = String(data.handle)
|
|
|
- const title = String(data.title)
|
|
|
- const description = String(data.description)
|
|
|
- const avatar = String(data.avatar)
|
|
|
- const banner = String(data.banner)
|
|
|
- const content = String(data.content)
|
|
|
- const ownerId = Number(data.owner)
|
|
|
- const accountId = String(data.role_account)
|
|
|
- const publicationStatus = data.publication_status === 'Public' ? true : false
|
|
|
- const curation = String(data.curation_status)
|
|
|
- const createdAt = +data.created
|
|
|
- const principal = Number(data.principal_id)
|
|
|
-
|
|
|
+ const { handle, title, description, avatar, banner, content, created } = data
|
|
|
+ //const accountId = String(data.role_account)
|
|
|
const channel = {
|
|
|
id,
|
|
|
- handle,
|
|
|
- title,
|
|
|
- description,
|
|
|
- avatar,
|
|
|
- banner,
|
|
|
- content,
|
|
|
- publicationStatus,
|
|
|
- curation,
|
|
|
- createdAt,
|
|
|
- principal,
|
|
|
+ handle: String(handle),
|
|
|
+ title: String(title),
|
|
|
+ description: String(description),
|
|
|
+ avatar: String(avatar),
|
|
|
+ banner: String(banner),
|
|
|
+ content: String(content),
|
|
|
+ publicationStatus: data.publication_status === 'Public' ? true : false,
|
|
|
+ curation: String(data.curation_status),
|
|
|
+ createdAt: +created,
|
|
|
+ principal: Number(data.principal_id),
|
|
|
}
|
|
|
- const chan = await save('channel', channel)
|
|
|
- const owner = await fetchMember(api, ownerId)
|
|
|
+ const chan = await Channel.create(channel)
|
|
|
+ const owner = await fetchMember(api, data.owner)
|
|
|
chan.setOwner(owner)
|
|
|
- if (id > 1) fetchChannel(api, id - 1)
|
|
|
return chan
|
|
|
}
|
|
|
|
|
|
const fetchCategory = async (api: Api, id: number) => {
|
|
|
- const exists = await Category.findByPk(id)
|
|
|
+ if (id <= 0) return
|
|
|
+ const exists = await Category.findByPk(+id)
|
|
|
if (exists) return exists
|
|
|
|
|
|
- console.debug(`fetching category ${id}`)
|
|
|
+ fetching = `category ${id}`
|
|
|
const data = await api.query.forum.categoryById(id)
|
|
|
-
|
|
|
- const threadId = +data.thread_id
|
|
|
- const title = String(data.title)
|
|
|
- const description = String(data.description)
|
|
|
- const createdAt = +data.created_at.block
|
|
|
- const deleted = data.deleted
|
|
|
- const archived = data.archived
|
|
|
- const subcategories = Number(data.num_direct_subcategories)
|
|
|
- const moderatedThreads = Number(data.num_direct_moderated_threads)
|
|
|
- const unmoderatedThreads = Number(data.num_direct_unmoderated_threads)
|
|
|
- const position = +data.position_in_parent_category // TODO sometimes NaN
|
|
|
+ const { title, description, deleted, archived } = data
|
|
|
+ const threadId = +data.thread_id // TODO needed?
|
|
|
const moderator: string = String(data.moderator_id) // account
|
|
|
|
|
|
const cat = {
|
|
|
id,
|
|
|
title,
|
|
|
description,
|
|
|
- createdAt,
|
|
|
+ createdAt: +data.created_at.block,
|
|
|
deleted,
|
|
|
archived,
|
|
|
- subcategories,
|
|
|
- moderatedThreads,
|
|
|
- unmoderatedThreads,
|
|
|
- //position,
|
|
|
+ subcategories: Number(data.num_direct_subcategories),
|
|
|
+ moderatedThreads: Number(data.num_direct_moderated_threads),
|
|
|
+ unmoderatedThreads: Number(data.num_direct_unmoderated_threads),
|
|
|
+ //position:+data.position_in_parent_category // TODO sometimes NaN,
|
|
|
}
|
|
|
- const category = await save('category', cat)
|
|
|
+ const category = await Category.create(cat)
|
|
|
const mod = await fetchMemberByAccount(api, moderator)
|
|
|
if (mod) category.setModerator(mod.id)
|
|
|
- if (id > 1) fetchCategory(api, id - 1)
|
|
|
return category
|
|
|
}
|
|
|
|
|
|
const fetchPost = async (api: Api, id: number) => {
|
|
|
+ if (id <= 0) return
|
|
|
const exists = await Post.findByPk(id)
|
|
|
if (exists) return exists
|
|
|
|
|
|
- console.debug(`fetching post ${id}`)
|
|
|
+ fetching = `post ${id}`
|
|
|
const data = await api.query.forum.postById(id)
|
|
|
|
|
|
const threadId = Number(data.thread_id)
|
|
@@ -379,32 +388,32 @@ const fetchPost = async (api: Api, id: number) => {
|
|
|
const createdAt = data.created_at.block
|
|
|
const author: string = String(data.author_id)
|
|
|
|
|
|
- const post = await save('post', { id, text, createdAt })
|
|
|
+ const post = await Post.create({ id, text, createdAt })
|
|
|
const thread = await fetchThread(api, threadId)
|
|
|
if (thread) post.setThread(thread.id)
|
|
|
const member = await fetchMemberByAccount(api, author)
|
|
|
if (member) post.setAuthor(member.id)
|
|
|
const mod = await fetchMemberByAccount(api, moderation)
|
|
|
- if (id > 1) fetchPost(api, id - 1)
|
|
|
return post
|
|
|
}
|
|
|
|
|
|
const fetchThread = async (api: Api, id: number) => {
|
|
|
+ if (id <= 0) return
|
|
|
const exists = await Thread.findByPk(id)
|
|
|
if (exists) return exists
|
|
|
|
|
|
- console.debug(`fetching thread ${id}`)
|
|
|
+ fetching = `thread ${id}`
|
|
|
const data = await api.query.forum.threadById(id)
|
|
|
-
|
|
|
- const title = String(data.title)
|
|
|
- const categoryId = Number(data.category_id)
|
|
|
- const nrInCategory = Number(data.nr_in_category)
|
|
|
- const moderation = data.moderation
|
|
|
- const createdAt = +data.created_at.block
|
|
|
+ const { title, moderation, nr_in_category } = data
|
|
|
const account = String(data.author_id)
|
|
|
-
|
|
|
- const thread = await save('thread', { id, title, nrInCategory, createdAt })
|
|
|
- const category = await fetchCategory(api, categoryId)
|
|
|
+ const t = {
|
|
|
+ id,
|
|
|
+ title,
|
|
|
+ nrInCategory: +nr_in_category,
|
|
|
+ createdAt: +data.created_at.block,
|
|
|
+ }
|
|
|
+ const thread = await Thread.create(t)
|
|
|
+ const category = await fetchCategory(api, +data.category_id)
|
|
|
if (category) thread.setCategory(category.id)
|
|
|
const author = await fetchMemberByAccount(api, account)
|
|
|
if (author) thread.setAuthor(author.id)
|
|
@@ -425,7 +434,6 @@ const fetchThread = async (api: Api, id: number) => {
|
|
|
//const mod = await fetchMemberByAccount(api, moderation)
|
|
|
//if (mod) thread.setModeration(mod.id)
|
|
|
}
|
|
|
- if (id > 1) fetchThread(api, id - 1)
|
|
|
return thread
|
|
|
}
|
|
|
|
|
@@ -437,31 +445,31 @@ const fetchCouncils = async (api: Api, lastBlock: number) => {
|
|
|
for (let round = 0; round < round; round++) {
|
|
|
const block = 57601 + round * cycle
|
|
|
if (councils.find((c) => c.round === round) || block > lastBlock) continue
|
|
|
- //enqueue(() => fetchCouncil(api, block))
|
|
|
+ fetchCouncil(api, block)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const fetchCouncil = async (api: Api, block: number) => {
|
|
|
- console.debug(`Fetching council at block ${block}`)
|
|
|
+ fetching = `council at block ${block}`
|
|
|
const blockHash = await api.rpc.chain.getBlockHash(block)
|
|
|
if (!blockHash)
|
|
|
return console.error(`Error: empty blockHash fetchCouncil ${block}`)
|
|
|
const council = await api.query.council.activeCouncil.at(blockHash)
|
|
|
- return save('council', council)
|
|
|
+ return Council.create(council)
|
|
|
}
|
|
|
|
|
|
const fetchProposal = async (api: Api, id: number) => {
|
|
|
- const exists = await Proposal.findByPk(id)
|
|
|
+ if (id <= 0) return
|
|
|
+ const exists = await Proposal.findByPk(+id)
|
|
|
if (exists) return exists
|
|
|
|
|
|
//if (exists && exists.stage === 'Finalized')
|
|
|
//if (exists.votesByAccount && exists.votesByAccount.length) return
|
|
|
//else return //TODO fetchVotesPerProposal(api, exists)
|
|
|
|
|
|
- console.debug(`Fetching proposal ${id}`)
|
|
|
+ fetching = `proposal ${id}`
|
|
|
const proposal = await get.proposalDetail(api, id)
|
|
|
- if (id > 1) fetchProposal(api, id - 1)
|
|
|
- return save('proposal', proposal)
|
|
|
+ return Proposal.create(proposal)
|
|
|
//TODO fetchVotesPerProposal(api, proposal)
|
|
|
}
|
|
|
|
|
@@ -471,7 +479,7 @@ const fetchVotesPerProposal = async (api: Api, proposal: ProposalDetail) => {
|
|
|
const proposals = await Proposal.findAll()
|
|
|
const councils = await Council.findAll()
|
|
|
|
|
|
- console.debug(`Fetching proposal votes (${proposal.id})`)
|
|
|
+ fetching = `proposal votes (${proposal.id})`
|
|
|
let members: MemberType[] = []
|
|
|
councils.map((seats: Seat[]) =>
|
|
|
seats.forEach(async (seat: Seat) => {
|
|
@@ -496,7 +504,7 @@ const fetchVoteByProposalByVoter = async (
|
|
|
proposalId: number,
|
|
|
voterId: number
|
|
|
): Promise<string> => {
|
|
|
- console.debug(`Fetching vote by ${voterId} for proposal ${proposalId}`)
|
|
|
+ fetching = `vote by ${voterId} for proposal ${proposalId}`
|
|
|
const vote: VoteKind = await api.query.proposalsEngine.voteExistsByProposalByVoter(
|
|
|
proposalId,
|
|
|
voterId
|
|
@@ -523,59 +531,21 @@ const fetchMemberByAccount = async (
|
|
|
return id ? fetchMember(api, id) : undefined
|
|
|
}
|
|
|
|
|
|
-const fetchMember = async (api: Api, id: number): Promise<MemberType> => {
|
|
|
- try {
|
|
|
- const exists = await Member.findByPk(id)
|
|
|
- if (exists) return exists
|
|
|
- } catch (e) {
|
|
|
- console.debug(`Fetching member ${id}`)
|
|
|
- }
|
|
|
+const fetchMember = async (
|
|
|
+ api: Api,
|
|
|
+ id: number
|
|
|
+): Promise<MemberType | undefined> => {
|
|
|
+ if (id <= 0) return
|
|
|
+ const exists = await Member.findByPk(+id)
|
|
|
+ if (exists) return exists
|
|
|
+
|
|
|
+ fetching = `member ${id}`
|
|
|
const membership = await get.membership(api, id)
|
|
|
const handle = String(membership.handle)
|
|
|
const account = String(membership.root_account)
|
|
|
const about = String(membership.about)
|
|
|
const createdAt = +membership.registered_at_block
|
|
|
- if (id > 1) fetchMember(api, id - 1)
|
|
|
- return save('member', { id, handle, createdAt, about })
|
|
|
-}
|
|
|
-
|
|
|
-const fetchReports = () => {
|
|
|
- const domain = `https://raw.githubusercontent.com/Joystream/community-repo/master/council-reports`
|
|
|
- const apiBase = `https://api.github.com/repos/joystream/community-repo/contents/council-reports`
|
|
|
-
|
|
|
- const urls: { [key: string]: string } = {
|
|
|
- alexandria: `${apiBase}/alexandria-testnet`,
|
|
|
- archive: `${apiBase}/archived-reports`,
|
|
|
- template: `${domain}/templates/council_report_template_v1.md`,
|
|
|
- }
|
|
|
-
|
|
|
- ;['alexandria', 'archive'].map((folder) => fetchGithubDir(urls[folder]))
|
|
|
-
|
|
|
- fetchGithubFile(urls.template)
|
|
|
-}
|
|
|
-
|
|
|
-const fetchGithubFile = async (url: string): Promise<string> => {
|
|
|
- const { data } = await axios.get(url)
|
|
|
- return data
|
|
|
-}
|
|
|
-
|
|
|
-const fetchGithubDir = async (url: string) => {
|
|
|
- const { data } = await axios.get(url)
|
|
|
- data.forEach(
|
|
|
- async (o: {
|
|
|
- name: string
|
|
|
- type: string
|
|
|
- url: string
|
|
|
- download_url: string
|
|
|
- }) => {
|
|
|
- const match = o.name.match(/^(.+)\.md$/)
|
|
|
- const name = match ? match[1] : o.name
|
|
|
- if (o.type === 'file') {
|
|
|
- const file = await fetchGithubFile(o.download_url)
|
|
|
- // TODO save file
|
|
|
- } else fetchGithubDir(o.url)
|
|
|
- }
|
|
|
- )
|
|
|
+ return Member.create({ id, handle, createdAt, about })
|
|
|
}
|
|
|
|
|
|
module.exports = { addBlock, addBlockRange }
|