Merge branch 'staging' of github.com:EbookFoundation/river-of-ebooks into staging

pull/54/head
unknown 2019-04-01 17:15:31 -04:00
commit dcdde17cdd
8 changed files with 75 additions and 10 deletions

View File

@ -22,12 +22,15 @@ module.exports = {
if (!body.metadata) throw new HttpError(400, 'Missing OPDS metadata')
if (!body.metadata['@type'] || body.metadata['@type'] !== 'http://schema.org/Book') throw new HttpError(400, 'Invalid \'@type\': expected \'http://schema.org/Book\'')
let tags = (body.metadata.tags || '').split(/,\s*/)
if (!tags.length && body.metadata.title) tags = body.metadata.title.split(/\s+/).filter(x => x.length < 3)
const query = {
hostname: host,
title: body.metadata.title,
author: body.metadata.author,
publisher: body.metadata.publisher,
identifier: body.metadata.identifier,
tags: JSON.stringify(tags),
version: body.metadata.modified.replace(/\D/g, '')
}
@ -49,7 +52,10 @@ module.exports = {
}
sendUpdatesAsync(result)
return res.json(result)
return res.json({
...result,
tags: JSON.parse(result.tags || '[]')
})
} catch (e) {
if (e instanceof HttpError) return e.send(res)
return res.status(500).json({
@ -67,9 +73,8 @@ async function sendUpdatesAsync (book) {
try {
const item = targets[i]
const user = await User.findOne({ id: item.user })
const { author: fAuthor, publisher: fPublisher, title: fTitle, identifier: fIsbn, url } = item
const { author: bAuthor, publisher: bPublisher, title: bTitle, identifier: bIsbn, opds } = book
sails.log('sending ' + book.id + ' info to ' + url)
const { author: fAuthor, publisher: fPublisher, title: fTitle, identifier: fIsbn, tags: fTags, url } = item
const { author: bAuthor, publisher: bPublisher, title: bTitle, identifier: bIsbn, tags: bTags, opds } = book
if (uriRegex.test(url)) {
if (fAuthor && !((bAuthor || '').includes(fAuthor))) continue
@ -77,6 +82,13 @@ async function sendUpdatesAsync (book) {
if (fTitle && !((bTitle || '').includes(fTitle))) continue
if (fIsbn && !((bIsbn || '').includes(fIsbn))) continue
const filterTags = JSON.parse(fTags || '[]')
if (filterTags.length && filterTags[0].length) {
const otherSet = new Set(filterTags)
if (!([...new Set(JSON.parse(bTags || '[]'))].filter(x => otherSet.has(x)).length)) continue
}
sails.log('sending ' + book.id + ' info to ' + url)
let content = opds
const timestamp = Date.now()
request.post({

View File

@ -42,7 +42,17 @@ module.exports = {
page = Math.abs(+body.page) || 1
delete body.page
}
let books = await Book.find(body || {}).skip((page * perPage) - perPage).limit(perPage)
const searchBody = { ...body }
if (searchBody.tags) {
const tags = searchBody.tags.split(/,\s*/)
searchBody.tags = {
or: [
...tags.map(tag => ({ contains: tag })),
{ in: tags }
]
}
}
let books = await Book.find(body ? searchBody : {}).skip((page * perPage) - perPage).limit(perPage)
if (!books.length) {
throw new HttpError(404, 'No books matching those parameters were found.')

View File

@ -25,13 +25,15 @@ module.exports = {
const publisher = req.param('publisher') || ''
const title = req.param('title') || ''
const isbn = req.param('isbn') || ''
const tags = req.param('tags') || ''
if (value.length) {
const url = await TargetUrl.update({ id, user: req.user.id }, {
url: value,
author,
publisher,
title,
isbn
isbn,
tags: JSON.stringify(tags.split(/,\s*/))
}).fetch()
return res.json(url)
} else {
@ -52,9 +54,13 @@ module.exports = {
},
list: async function (req, res) {
try {
const urls = await TargetUrl.find({
let urls = await TargetUrl.find({
user: req.user.id
})
urls = urls.map(url => ({
...url,
tags: JSON.parse(url.tags || '[]')
}))
return res.json(urls)
} catch (e) {
return (new HttpError(500, e.message)).send(res)

View File

@ -23,7 +23,8 @@ module.exports = {
identifier: { type: 'string' },
version: { type: 'string' },
hostname: { type: 'string' },
opds: { type: 'json' }
opds: { type: 'json' },
tags: { type: 'string' }
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
// ║╣ ║║║╠╩╗║╣ ║║╚═╗

View File

@ -15,6 +15,7 @@ module.exports = {
author: 'string',
publisher: 'string',
title: 'string',
isbn: 'string'
isbn: 'string',
tags: 'string'
}
}

View File

@ -30,7 +30,7 @@ class UriListItem extends React.Component {
</div>
<div className='col stack flex flex-container flex-vertical'>
<span className='label'>Filters</span>
<span className='value'>{['publisher', 'title', 'author', 'isbn'].reduce((a, x) => a + (this.props.item[x] ? 1 : 0), 0) || 'None'}</span>
<span className='value'>{['publisher', 'title', 'author', 'isbn', 'tags'].reduce((a, x) => a + (this.props.item[x] ? 1 : 0), 0) || 'None'}</span>
</div>
<ConfirmIconButton icon='delete' onClick={() => this.props.dispatch(removeUrl(this.props.item.id))} />
</li>
@ -86,6 +86,15 @@ class UriListItem extends React.Component {
value={'' + this.props.item.isbn}
onChange={(e) => this.props.dispatch(changeUrlField(this.props.item.id, 'isbn', e.target.value))}
onBlur={(e) => this.props.dispatch(setUrl(this.props.item))} />
<UnderlineInput
className='uri flex'
type='text'
name={'tags-' + this.props.item.id}
placeholder='Tags'
value={this.props.item.tags ? this.props.item.tags.join(', ') : ''}
pattern={/^.*?(?:,\s+\S.+?)*$/}
onChange={(e) => this.props.dispatch(changeUrlField(this.props.item.id, 'tags', e.target.value.split(/,\s+/)))}
onBlur={(e) => this.props.dispatch(setUrl(this.props.item))} />
</div>
</li>
)

View File

@ -18,6 +18,7 @@ and opds2 publication body with type `application/json`:
"title": "Moby-Dick",
"author": "Herman Melville",
"identifier": "urn:isbn:978031600000X",
"tags": ["story", "classic"],
"publisher": "Ebook Publisher.com",
"language": "en",
"modified": "2015-09-29T17:00:00Z"
@ -46,6 +47,7 @@ The server will respond with either:
"id": number,
"title": string,
"author": string,
"tags": array,
"publisher": string,
"identifier": string,
"version": string,
@ -72,6 +74,7 @@ title: The ebook's title (optional)
author: The author (optional)
version: A version number (optional)
isbn: The ISBN (optional)
tags: Comma-separated search tags (optional)
page: The page of results to view (200 results per page)
```
@ -107,6 +110,7 @@ The server will respond with either:
"@type": "http://schema.org/Book",
"title": "Moby-Dick",
"author": "Herman Melville",
"tags": ["story", "classic"],
"publisher": "Ebook Publisher.com",
"identifier": "urn:isbn:978031600000X",
"language": "en",
@ -165,6 +169,7 @@ HTTP Body:
"@type": "http://schema.org/Book",
"title": "Moby-Dick",
"author": "Herman Melville",
"tags": ["story", "classic"],
"publisher": "Ebook Publisher.com",
"identifier": "urn:isbn:978031600000X",
"language": "en",

View File

@ -0,0 +1,21 @@
exports.up = function (knex, Promise) {
return Promise.all([
knex.schema.table('book', t => {
t.string('tags')
}),
knex.schema.table('targeturl', t => {
t.string('tags')
})
])
}
exports.down = function (knex, Promise) {
return Promise.all([
knex.schema.table('book', t => {
t.dropColumns('tags')
}),
knex.schema.table('targeturl', t => {
t.dropColumns('tags')
})
])
}