Merge pull request #26 from tmshv/fix/api-pages-breadcrumbs

API pages breadcrumbs
This commit is contained in:
tmshv authored and GitHub committed 2020-02-23 03:50:56 +03:00
commit b2493a6ab2
8 files changed
+130 -46

No files matched your search

+62
View File
@@ -4,6 +4,16 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
"dev": true,
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/bson": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.1.tgz",
@@ -19,6 +29,42 @@
"integrity": "sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A==",
"dev": true
},
"@types/connect": {
"version": "3.4.33",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/express": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
"integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
"dev": true,
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz",
"integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/range-parser": "*"
}
},
"@types/mime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==",
"dev": true
},
"@types/mongodb": {
"version": "3.3.16",
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.3.16.tgz",
@@ -35,6 +81,22 @@
"integrity": "sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==",
"dev": true
},
"@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
"dev": true
},
"@types/serve-static": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
"integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
"dev": true,
"requires": {
"@types/express-serve-static-core": "*",
"@types/mime": "*"
}
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+1
View File
@@ -27,6 +27,7 @@
},
"devDependencies": {
"@types/classnames": "^2.2.9",
"@types/express": "^4.17.2",
"@types/mongodb": "^3.3.16",
"@types/node": "^13.7.1",
"ts-node": "^8.6.2",
@@ -0,0 +1,23 @@
import { getPathWaterfall } from '../lib/url'
import { Breadcrumb } from '../types'
import Page from '../core/Page'
export async function getItems(path: string): Promise<Breadcrumb> {
const bc = getPathWaterfall(path)
const parts = await Promise.all(bc.map(async href => {
const resource = await Page.findByUrl(href)
if (!resource) {
return null
}
const name = resource.title
return {
name,
href,
}
}))
return parts.filter(Boolean)
}
+6 -5
View File
@@ -1,15 +1,16 @@
import { Request, Response } from 'express'
import Page from '../core/Page'
import { getPathWithNoTrailingSlash } from '../lib/url'
import { dropTrailingSlash, getPathWaterfall } from '../lib/url'
import { encodePage } from '../factory/page'
import * as breadcrumb from './breadcrumb'
export async function getItem(req: Request, res: Response) {
const id = req.query.page
const page = getPathWithNoTrailingSlash(id)
const page = dropTrailingSlash(req.query.page)
const breadcrumbs = await breadcrumb.getItems(page)
const resource = await Page.findByUrl(page)
if (resource) {
res.json(encodePage(resource))
res.json(encodePage(resource, breadcrumbs))
} else {
res.status(404)
res.json({
+4 -1
View File
@@ -1,6 +1,8 @@
import { encodeImage } from './image'
import Page from '../core/Page'
import { Breadcrumb } from '../types'
export function encodePage(page) {
export function encodePage(page: Page, breadcrumb: Breadcrumb) {
return {
id: page.id,
title: page.title,
@@ -8,5 +10,6 @@ export function encodePage(page) {
data: page.data,
preview: encodeImage(page.preview),
tokens: page.tokens,
breadcrumb,
}
}
-40
View File
@@ -1,40 +0,0 @@
function getPathWithNoTrailingSlash(path='') {
path = path.charAt(path.length - 1) === '/'
? path.substr(0, path.length - 1)
: path
return path.length === 0
? '/'
: path
}
function isEqualPath(a, b, ignoreTrailingSlash = false) {
return ignoreTrailingSlash
? getPathWithNoTrailingSlash(a) === getPathWithNoTrailingSlash(b)
: a === b
}
function isMatchPathPattern(pattern, path, ignoreTrailingSlash = false) {
const test = ignoreTrailingSlash
? getPathWithNoTrailingSlash(path)
: path
return pattern.test(test)
}
/**
*
* /article/* -> ^\/article\/[/\w\d-_]+\/?$
*
* @param url
* @return {RegExp}
*/
function urlToPattern(url) {
const u = url.replace('/*', '[/\\w\\d-_]+')
const pattern = `^${u}/?$`
return new RegExp(pattern)
}
exports.getPathWithNoTrailingSlash = getPathWithNoTrailingSlash
exports.isMatchPathPattern = isMatchPathPattern
exports.urlToPattern = urlToPattern
exports.isEqualPath = isEqualPath
+28
View File
@@ -0,0 +1,28 @@
export function dropTrailingSlash(path: string) {
if (path === '/' || path === '') {
return '/'
}
const lastChar = path.charAt(path.length - 1)
if (lastChar === '/') {
return path.substr(0, path.length - 1)
}
return path
}
export function getPathWaterfall(path: string): string[] {
const parts = path
.split('/')
.filter(Boolean)
.reduce((acc, part, index, parts) => {
const i = index + 1
const item = `/${parts.slice(0, i).join('/')}`
return [
...acc,
item
]
}, [])
return ['/', ...parts]
}
+6
View File
@@ -0,0 +1,6 @@
export type BreadcrumbPart = {
name: string
href: string
}
export type Breadcrumb = BreadcrumbPart[]