cosmic数据库使用说明
我们最近使用一些最强大的开发工具构建了Story Licensing应用程序。 通过将非核心开发功能卸载到可用的API服务上,我们能够以比我想象的更快的速度构建此应用程序。 很高兴与您分享我们的做法。
TL; DR
要求
每个应用程序都始于目标和要求。 对于此应用程序,目标是吸引有兴趣购买AMI出版物网络中的出版物以重新发布在线内容的权利的销售线索。 我们的要求:
- 故事(对于我们的应用程序,在@ami网站上发布的故事)需要快速且轻松地进行搜索
- 故事需要向感兴趣的潜在客户表明公开可用的社交验证(Claps)的排名
- 能够捕获潜在客户的电子邮件(在搜索操作之前)
- 能够捕获出价请求数据:故事标题,潜在客户电子邮件,搜索字词出价并发送电子邮件给管理员
- 内容管理员可以通过内容管理系统管理复制内容
服务内容
对于这个应用程序,我们知道我们不想构建和管理“一切”。 相反,我们希望将非核心开发的工作分担给最好的可用服务,仅构建所需的内容。 任何时间紧迫的Web项目(几乎每个项目)都建议这样做,这样您就不必不必要地重新发明已经存在的高级功能。 因此,对于这个应用程序,我们决定使用:
- Cosmic JS,用于内容管理,数据存储,这也是我们使用Medium Backup Application存储Medium文章的地方。
- 搜寻故事的阿尔戈利亚 。 它绝对可以满足我们的快速搜索要求(速度非常快!)
- SendGrid用于发送电子邮件通知
- @ami提供故事库
因此,有了所有这些,我们就为各自的功能提供了一流的服务。 构建我们的应用程序将涉及将这些服务锁定在一起。
建立搜索功能
对于这个应用程序,我知道我想使用React (主要是出于个人喜好)。 因此,与“仅构建必须要做的”主题保持一致,对我来说,自然的框架选择是Next.js。 他们的价值主张是“简化应用程序”。 它可以轻松启动和运行React应用程序,而无需进行大量样板开发,同时提供良好的开发人员体验。
为了集成Algolia,我需要在React应用程序中实现搜索栏。 幸运的是,他们拥有出色的InstantSearch React组件 ,可以轻松集成到React应用程序中。 只需设置您的appId
, apiKey
和indexName
。 您可以在Algolia仪表板中找到它们。
现在已经实现了搜索栏,我需要向Algolia添加记录。 为此,我需要将Cosmic JS保存的Medium文章添加到Algolia中。 我创建了一个脚本,将Cosmic JS中的Objects添加到我们的Algolia搜索索引中。
这是add-records.js
:
require ( 'dotenv' ).config()
const Cosmic = require ( 'cosmicjs' )
const api = Cosmic()
const async = require ( 'async' )
const algoliasearch = require ( 'algoliasearch' )
const client = algoliasearch(process.env.ALGOLIA_ACCOUNT, process.env.ALGOLIA_INDEX)
const index = client.initIndex( 'Stories' )
const buckets = [ 'your-bucket-slug' , 'another-bucket-slug' ] // Add all Bucket slugs here
async .eachSeries(buckets, (bucket_slug, bucketEachCallback) => {
const bucket = api.bucket({ slug : bucket_slug })
addRecords()
let added_count = 0
const addRecords = ( skip = 0 ) => {
console .log(skip)
const locals = {}
async .series([
callback => {
const objects = bucket.getObjects({
type : 'posts' ,
limit : 1000 ,
skip
}).then( data => {
const objects = data.objects
locals.objects = objects
locals.total = data.total
console .log( 'Total:' , data.total)
console .log( 'Object Length' , locals.objects.length)
callback()
}).catch( err => {
console .log(err)
})
},
() => {
async .eachSeries(locals.objects, (object, eachCallback) => {
// Save Algolia record
delete object.content
index.addObject(object, (err, content) => {
console .log( 'objectID=' + content.objectID)
console .log( 'ADD' , object.slug)
added_count++
eachCallback()
})
}, () => {
console .log( 'Added ' + added_count, 'Total:' + locals.total)
if (added_count !== locals.total) {
addRecords(added_count)
} else {
bucketEachCallback()
console .log( 'All done FOR REAL!' )
}
})
}
])
}
})
接下来,我需要为故事鼓掌,因为故事的中型XML提要(例如: https : //hackernoon.com/feed )不包含此应用程序的重要信息。 因此,为此,我需要创建一个每天运行的工作程序脚本,以更新我们所有的故事拍手计数。
这是get-claps.js
:
require ( 'dotenv' ).config()
const async = require ( 'async' )
const axios = require ( 'axios' )
const algoliasearch = require ( 'algoliasearch' )
const client = algoliasearch(process.env.ALGOLIA_ACCOUNT, process.env.ALGOLIA_INDEX)
const index = client.initIndex( 'Stories' )
const getClaps = () => {
let hit_count = 0
index.browse( '' , {}, function browseDone ( err, content ) {
if (err) {
throw err
}
const hits = content.hits
async .eachSeries(hits, (hit, callback) => {
const medium_url = hit.metadata.medium_link
if (!medium_url)
return callback()
axios.get(medium_url).then( response => {
const str1 = '"totalClapCount":'
const str2 = ',"sectionCount'
const claps = Number (response.data.split(str1).pop().split(str2).shift())
index.partialUpdateObject({
claps : claps,
objectID : hit.objectID
}, function ( err, content ) {
if (err) throw err;
// console.log(medium_url, claps, hit.objectID)
callback()
});
}).catch( err => {
console .log(err)
callback()
})
}, () => {
hit_count = hit_count + content.hits.length
if (content.cursor) {
index.browseFrom(content.cursor, browseDone)
} else {
getClaps()
console .log( 'DONE!' , hit_count)
}
})
})
}
getClaps()
该脚本执行以下操作:获取Algolia中所有保存的记录,命中@ami故事URL,获取JSON数据,解析其拍手数,然后更新Algolia中的记录。 此过程可以在后台运行,以获取并保存每个故事的最新拍手数。 这将增加我们在阿尔戈利亚的每日配额记录行动,增加我们的成本,但是值得每个故事进行最新的社交验证。
由于搜索结果来自Algolia,因此搜索词的相关性成为优先事项,但我们还可以对故事进行重新排序,以使拍手数最高:
保存潜在客户电子邮件和出价请求
由于此应用程序的目标是保存潜在客户的出价请求,因此我们选择了SendGrid作为可靠的电子邮件服务提供商。 我创建了两个端点以在搜索之前保存潜在客户电子邮件,并在找到故事后保存出价请求信息。
这是leads.js
和bids.js
:
module .exports = function ( req, res ) {
const Cosmic = require ( 'cosmicjs' )
const api = Cosmic()
const bucket = api.bucket({
slug : 'app-bucket-slug' ,
write_key : process.env.COSMIC_WRITE_KEY
})
bucket.addObject({
title : 'Lead - ' + ( new Date ()),
type_slug : 'leads' ,
metafields : [{
title : 'Email' ,
key : 'email' ,
type : 'text' ,
value : req.body.email
}],
options : {
content_editor : false ,
slug_input : false
}
}).then( data => {
res.json(data)
})
}
module .exports = async function ( req, res ) {
const Cosmic = require ( 'cosmicjs' )
const sgMail = require ( '@sendgrid/mail' )
const async = require ( 'async' )
sgMail.setApiKey(process.env.SENDGRID_API_KEY)
const api = Cosmic()
const bucket = api.bucket({
slug : 'story-licensing' ,
write_key : process.env.COSMIC_WRITE_KEY
})
const bid = await bucket.addObject({
title : 'Bid - ' + ( new Date ()),
type_slug : 'bids' ,
metafields : [
{
title : 'Email' ,
key : 'email' ,
type : 'text' ,
value : req.body.email
},
{
title : 'Bid' ,
key : 'bid' ,
type : 'text' ,
value : req.body.bid
},
{
title : 'Post Title' ,
key : 'post_title' ,
type : 'text' ,
value : req.body.post_title
},
{
title : 'Post Link' ,
key : 'post_link' ,
type : 'text' ,
value : req.body.post_link
}
],
options : {
content_editor : false ,
slug_input : false
}
})
async .series([
callback => {
// Send to Admin
const subject = 'A Bid has been received'
const html_body = '<div>A bid has been received for <strong>' + req.body.post_title + '</strong> for <strong>$' + req.body.bid + '</strong></div>'
const message = {
// sender info
from : {
email : req.body.email
},
// Comma separated list of recipients
to: process.env.BID_EMAIL,
// Subject of the message
subject: subject, //
// plaintext body
text: subject,
// HTML body
html: html_body,
}
sgMail.send(message)
.then( () => {
//Celebrate
callback()
})
.catch( error => {
// Log friendly error
console .error(error.toString())
res.json({ success : false })
})
},
() => {
// Send to Bidder
const subject = 'Your bid has been received'
const html_body = '<div>A bid has been received for <strong>' + req.body.post_title + '</strong> for <strong>$' + req.body.bid + '</strong>. Thank you.</div>'
const message = {
// sender info
from : {
email : process.env.BID_EMAIL,
name : 'Story Licensing'
},
// Comma separated list of recipients
to: req.body.email,
// Subject of the message
subject: subject, //
// plaintext body
text: subject,
// HTML body
html: html_body,
}
sgMail.send(message)
.then( () => {
// Celebrate
res.json({ success : true })
})
.catch( error => {
// Log friendly error
console .error(error.toString())
res.json({ success : false })
})
}
])
}
请注意,对于这两项操作(潜在客户电子邮件和出价请求),我们还将数据存储在Cosmic JS中。 这样做是为了使我们可以使用此数据来保存管理员记录。 现在,Cosmic JS可以通过Cosmic API来查看,查询和交付这些数据,以供将来使用。
结论
现在可以使用完成的应用程序,以帮助您从AMI出版物网络中找到高质量的内容,以许可您的网站或博客。 在这里查看 。
使用资源
我对这个项目的实施方式感到满意。 由于使用了最好的服务和开发人员工具(Algolia,SendGrid,Next.js和Cosmic JS)来交付快速且可扩展的应用程序,因此开发速度很快。 让我知道您的想法, 加入Slack上的对话,并在Twitter上关注Cosmic JS 。
cosmic数据库使用说明