diff --git a/assets/components/actor/actor.vue b/assets/components/actor/actor.vue index 09f3eaa2..3236ce93 100644 --- a/assets/components/actor/actor.vue +++ b/assets/components/actor/actor.vue @@ -113,14 +113,4 @@ export default { content: ':'; } } - -.scenes { - display: grid; - grid-gap: 1rem; - margin: 0 0 1rem 0; -} - -.scenes { - grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); -} diff --git a/assets/components/header/header.vue b/assets/components/header/header.vue index 8db1d61a..cd9c2d38 100644 --- a/assets/components/header/header.vue +++ b/assets/components/header/header.vue @@ -15,7 +15,7 @@ .header { color: $text-contrast; background: $primary; - padding: 1rem; + padding: .5rem 1rem; } .logo-link { diff --git a/assets/components/home/home.vue b/assets/components/home/home.vue index 6f711f08..8ddbb61e 100644 --- a/assets/components/home/home.vue +++ b/assets/components/home/home.vue @@ -144,10 +144,4 @@ export default { .filter { display: inline-block; } - -.scenes { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); - grid-gap: 1rem; -} diff --git a/assets/components/network/network.vue b/assets/components/network/network.vue index 2b9d8b7b..23bc7096 100644 --- a/assets/components/network/network.vue +++ b/assets/components/network/network.vue @@ -113,18 +113,13 @@ export default { height: 3rem; } -.sites, -.scenes { +.sites { display: grid; grid-gap: 1rem; margin: 0 0 1rem 0; } -.scenes { - grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); -} - .sites { - grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); + grid-template-columns: repeat(auto-fit, 15rem); } diff --git a/assets/components/site/site.vue b/assets/components/site/site.vue index a67f3453..f7ba1808 100644 --- a/assets/components/site/site.vue +++ b/assets/components/site/site.vue @@ -147,11 +147,7 @@ export default { margin: 0 0 1rem 0; } -.scenes { - grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); -} - .sites { - grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); + grid-template-columns: repeat(auto-fit, 15rem); } diff --git a/assets/components/tag/tag.vue b/assets/components/tag/tag.vue new file mode 100644 index 00000000..374a73a0 --- /dev/null +++ b/assets/components/tag/tag.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/assets/components/tile/release.vue b/assets/components/tile/release.vue index 8bf3a24b..81124e3b 100644 --- a/assets/components/tile/release.vue +++ b/assets/components/tile/release.vue @@ -131,8 +131,11 @@ export default { .thumbnail { width: 100%; + height: 12rem; + display: flex; + justify-content: center; + align-items: center; object-fit: cover; - object-position: 50% 0; background-position: center; background-size: cover; background-color: $shadow-hint; diff --git a/assets/components/tile/site.vue b/assets/components/tile/site.vue index a36906d0..6b804901 100644 --- a/assets/components/tile/site.vue +++ b/assets/components/tile/site.vue @@ -30,7 +30,7 @@ export default { flex-direction: column; align-items: center; box-sizing: border-box; - padding: 1rem; + padding: .5rem 1rem; border-radius: .25rem; box-shadow: 0 0 3px rgba(0, 0, 0, .25); height: 100%; diff --git a/assets/css/_theme.scss b/assets/css/_theme.scss index 9c8e6743..8f8f2c05 100644 --- a/assets/css/_theme.scss +++ b/assets/css/_theme.scss @@ -1,4 +1,5 @@ /* $primary: #ff886c; */ +$breakpoint: 720px; $primary: #ff6c88; $background: #fff; diff --git a/assets/css/style.scss b/assets/css/style.scss index 6820f9d3..c701c51a 100644 --- a/assets/css/style.scss +++ b/assets/css/style.scss @@ -34,3 +34,15 @@ body { fill: $primary; } } + +.scenes { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(20rem, .5fr)); + grid-gap: 1rem; +} + +@media(max-width: $breakpoint) { + .scenes { + grid-template-columns: repeat(auto-fit, minmax(22.5rem, 1fr)); + } +} diff --git a/assets/js/router.js b/assets/js/router.js index 8beb25ac..4de30722 100644 --- a/assets/js/router.js +++ b/assets/js/router.js @@ -6,6 +6,7 @@ import Release from '../components/release/release.vue'; import Site from '../components/site/site.vue'; import Network from '../components/network/network.vue'; import Actor from '../components/actor/actor.vue'; +import Tag from '../components/tag/tag.vue'; import NotFound from '../components/errors/404.vue'; Vue.use(VueRouter); @@ -19,12 +20,12 @@ const routes = [ { path: '/scene/:releaseId', component: Release, - name: 'release', + name: 'scene', }, { path: '/movie/:releaseId', component: Release, - name: 'release', + name: 'movie', }, { path: '/actor/:actorSlug', @@ -41,6 +42,11 @@ const routes = [ component: Network, name: 'network', }, + { + path: '/tag/:tagSlug', + component: Tag, + name: 'tag', + }, { path: '*', component: NotFound, diff --git a/assets/js/store.js b/assets/js/store.js index c33f5b11..9322e6d5 100644 --- a/assets/js/store.js +++ b/assets/js/store.js @@ -6,6 +6,7 @@ import initReleasesStore from './releases/releases'; import initSitesStore from './sites/sites'; import initNetworksStore from './networks/networks'; import initActorsStore from './actors/actors'; +import initTagsStore from './tags/tags'; function initStore(router) { Vue.use(Vuex); @@ -17,6 +18,7 @@ function initStore(router) { store.registerModule('actors', initActorsStore(store, router)); store.registerModule('sites', initSitesStore(store, router)); store.registerModule('networks', initNetworksStore(store, router)); + store.registerModule('tags', initTagsStore(store, router)); return store; } diff --git a/assets/js/tags/actions.js b/assets/js/tags/actions.js new file mode 100644 index 00000000..b4e2bac1 --- /dev/null +++ b/assets/js/tags/actions.js @@ -0,0 +1,22 @@ +import { get } from '../api'; + +function initTagsActions(_store, _router) { + async function fetchTags({ _commit }, tagId) { + const tags = await get(`/tags/${tagId || ''}`); + + return tags; + } + + async function fetchTagReleases({ _commit }, tagId) { + const releases = await get(`/tags/${tagId}/releases`); + + return releases; + } + + return { + fetchTags, + fetchTagReleases, + }; +} + +export default initTagsActions; diff --git a/assets/js/tags/mutations.js b/assets/js/tags/mutations.js new file mode 100644 index 00000000..ff8b4c56 --- /dev/null +++ b/assets/js/tags/mutations.js @@ -0,0 +1 @@ +export default {}; diff --git a/assets/js/tags/state.js b/assets/js/tags/state.js new file mode 100644 index 00000000..ff8b4c56 --- /dev/null +++ b/assets/js/tags/state.js @@ -0,0 +1 @@ +export default {}; diff --git a/assets/js/tags/tags.js b/assets/js/tags/tags.js new file mode 100644 index 00000000..47b5b20f --- /dev/null +++ b/assets/js/tags/tags.js @@ -0,0 +1,13 @@ +import state from './state'; +import mutations from './mutations'; +import actions from './actions'; + +function initTagsStore(store, router) { + return { + state, + mutations, + actions: actions(store, router), + }; +} + +export default initTagsStore; diff --git a/config/default.js b/config/default.js index 89f86255..60649c4e 100644 --- a/config/default.js +++ b/config/default.js @@ -84,7 +84,7 @@ module.exports = { ], media: { path: './', - thumbnailSize: 324, // width for 16:9 will be exactly 576px + thumbnailSize: 320, // width for 16:9 will be exactly 576px }, filename: { dateFormat: 'DD-MM-YYYY', diff --git a/migrations/20190325001339_releases.js b/migrations/20190325001339_releases.js index 200f6f69..53cd6898 100644 --- a/migrations/20190325001339_releases.js +++ b/migrations/20190325001339_releases.js @@ -37,7 +37,9 @@ exports.up = knex => Promise.resolve() .then(() => knex.schema.createTable('tags_groups', (table) => { table.increments('id', 12); - table.string('group', 32); + table.string('name', 32); + table.text('description'); + table.string('slug', 32) .unique(); })) @@ -45,6 +47,8 @@ exports.up = knex => Promise.resolve() table.increments('id', 12); table.string('name'); + table.text('description'); + table.integer('group_id', 12) .references('id') .inTable('tags_groups'); diff --git a/public/css/style.css b/public/css/style.css index 1c98f41a..3a748697 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -15,10 +15,12 @@ } .thumbnail[data-v-3abcf101] { width: 100%; + height: 12rem; + display: flex; + justify-content: center; + align-items: center; -o-object-fit: cover; object-fit: cover; - -o-object-position: 50% 0; - object-position: 50% 0; background-position: center; background-size: cover; background-color: rgba(0, 0, 0, 0.1); @@ -151,11 +153,6 @@ .filter[data-v-5533e378] { display: inline-block; } -.scenes[data-v-5533e378] { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); - grid-gap: 1rem; -} /* $primary: #ff886c; */ .banner[data-v-2bc41e74] { @@ -263,11 +260,8 @@ grid-gap: 1rem; margin: 0 0 1rem 0; } -.scenes[data-v-3e57cf44] { - grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); -} .sites[data-v-3e57cf44] { - grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); + grid-template-columns: repeat(auto-fit, 15rem); } /* $primary: #ff886c; */ @@ -276,7 +270,7 @@ flex-direction: column; align-items: center; box-sizing: border-box; - padding: 1rem; + padding: .5rem 1rem; border-radius: .25rem; box-shadow: 0 0 3px rgba(0, 0, 0, 0.25); height: 100%; @@ -322,17 +316,13 @@ .logo[data-v-757c14c2] { height: 3rem; } -.sites[data-v-757c14c2], -.scenes[data-v-757c14c2] { +.sites[data-v-757c14c2] { display: grid; grid-gap: 1rem; margin: 0 0 1rem 0; } -.scenes[data-v-757c14c2] { - grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); -} .sites[data-v-757c14c2] { - grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); + grid-template-columns: repeat(auto-fit, 15rem); } /* $primary: #ff886c; */ @@ -357,13 +347,29 @@ .bio-heading[data-v-677a8360]::after { content: ':'; } -.scenes[data-v-677a8360] { - display: grid; - grid-gap: 1rem; + +/* $primary: #ff886c; */ +.header[data-v-80991bcc] { + display: flex; + justify-content: space-between; + padding: 1rem; +} +.title[data-v-80991bcc] { + display: inline-block; + margin: 0 .5rem 0 0; + text-transform: capitalize; +} +.heading[data-v-80991bcc] { + padding: 0; margin: 0 0 1rem 0; } -.scenes[data-v-677a8360] { - grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); +.bio-heading[data-v-80991bcc] { + display: inline-block; + font-weight: bold; + margin: .5rem 0 0 0; +} +.bio-heading[data-v-80991bcc]::after { + content: ':'; } /* $primary: #ff886c; */ @@ -418,11 +424,20 @@ body { .icon.icon-href :hover { fill: #ff6c88; } +.scenes { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(20rem, 0.5fr)); + grid-gap: 1rem; } + +@media (max-width: 720px) { + .scenes { + grid-template-columns: repeat(auto-fit, minmax(22.5rem, 1fr)); } } + /* $primary: #ff886c; */ .header[data-v-10b7ec04] { color: #fff; background: #ff6c88; - padding: 1rem; + padding: .5rem 1rem; } .logo-link[data-v-10b7ec04] { color: inherit; diff --git a/public/img/404.png b/public/img/404.png deleted file mode 100644 index 94e46937..00000000 Binary files a/public/img/404.png and /dev/null differ diff --git a/public/img/logos/dogfartnetwork/blacksonblondes.png b/public/img/logos/dogfartnetwork/blacksonblondes.png new file mode 100644 index 00000000..1f39e977 Binary files /dev/null and b/public/img/logos/dogfartnetwork/blacksonblondes.png differ diff --git a/public/img/logos/dogfartnetwork/blacksoncougars.png b/public/img/logos/dogfartnetwork/blacksoncougars.png new file mode 100644 index 00000000..0925bbb4 Binary files /dev/null and b/public/img/logos/dogfartnetwork/blacksoncougars.png differ diff --git a/public/img/logos/dogfartnetwork/cuckoldsessions.png b/public/img/logos/dogfartnetwork/cuckoldsessions.png new file mode 100644 index 00000000..862203ce Binary files /dev/null and b/public/img/logos/dogfartnetwork/cuckoldsessions.png differ diff --git a/public/img/logos/dogfartnetwork/gloryhole.png b/public/img/logos/dogfartnetwork/gloryhole.png new file mode 100644 index 00000000..2d66d54c Binary files /dev/null and b/public/img/logos/dogfartnetwork/gloryhole.png differ diff --git a/public/img/logos/dogfartnetwork/network.png b/public/img/logos/dogfartnetwork/network.png new file mode 100644 index 00000000..61c6dafc Binary files /dev/null and b/public/img/logos/dogfartnetwork/network.png differ diff --git a/public/img/logos/dogfartnetwork/watchingmydaughtergoblack.png b/public/img/logos/dogfartnetwork/watchingmydaughtergoblack.png new file mode 100644 index 00000000..d3d99317 Binary files /dev/null and b/public/img/logos/dogfartnetwork/watchingmydaughtergoblack.png differ diff --git a/public/img/logos/dogfartnetwork/watchingmymomgoblack.png b/public/img/logos/dogfartnetwork/watchingmymomgoblack.png new file mode 100644 index 00000000..4e277a50 Binary files /dev/null and b/public/img/logos/dogfartnetwork/watchingmymomgoblack.png differ diff --git a/public/img/logos/dogfartnetwork/wefuckblackgirls.png b/public/img/logos/dogfartnetwork/wefuckblackgirls.png new file mode 100644 index 00000000..ec6f1bb2 Binary files /dev/null and b/public/img/logos/dogfartnetwork/wefuckblackgirls.png differ diff --git a/public/index.html b/public/index.html index 7e260e61..c9c3ba72 100644 --- a/public/index.html +++ b/public/index.html @@ -2,6 +2,9 @@ + + + traxxx diff --git a/seeds/02_tags.js b/seeds/02_tags.js index 6c02f0ed..b4398675 100644 --- a/seeds/02_tags.js +++ b/seeds/02_tags.js @@ -5,47 +5,47 @@ const upsert = require('../src/utils/upsert'); const groups = [ { slug: 'age', - group: 'Age', + name: 'Age', }, { slug: 'body', - group: 'Body', + name: 'Body', }, { slug: 'clothing', - group: 'Clothing', + name: 'Clothing', }, { slug: 'ethnicity', - group: 'Ethnicity', + name: 'Ethnicity', }, { slug: 'group', - group: 'Group sex', + name: 'Group sex', }, { slug: 'hair', - group: 'Hair', + name: 'Hair', }, { slug: 'location', - group: 'Location', + name: 'Location', }, { slug: 'orientation', - group: 'Orientation', + name: 'Orientation', }, { slug: 'penetration', - group: 'Penetration', + name: 'Penetration', }, { slug: 'position', - group: 'Position', + name: 'Position', }, { slug: 'roleplay', - group: 'Roleplay', + name: 'Roleplay', }, ]; @@ -54,6 +54,7 @@ function getTags(groupsMap) { { name: '4K', slug: '4k', + description: 'Available in high quality 4K resolution.', alias_for: null, }, { @@ -66,6 +67,7 @@ function getTags(groupsMap) { name: 'airtight', slug: 'airtight', alias_for: null, + description: 'A cock in every penetrable hole (of a woman); one in the mouth, one in the vagina, and one in the asshole.', group_id: groupsMap['penetration'], }, { @@ -83,15 +85,18 @@ function getTags(groupsMap) { name: 'anal creampie', slug: 'anal-creampie', alias_for: null, + description: 'Ejaculating into the asshole.' }, { name: 'anal', slug: 'anal', + description: 'Penetrating the asshole with a (real) dick.', alias_for: null, }, { - name: 'anal fingering', - slug: 'anal-fingering', + name: 'ass fingering', + slug: 'ass-fingering', + description: 'Inserting one or multiple fingers into the asshole.', alias_for: null, }, { @@ -663,6 +668,12 @@ function getTags(groupsMap) { slug: 'spanking', alias_for: null, }, + { + name: 'spooning', + slug: 'spooning', + alias_for: null, + group_id: groupsMap['position'], + }, { name: 'strapon', slug: 'strapon', @@ -827,6 +838,10 @@ function getTagAliases(tagsMap) { name: 'asians', alias_for: tagsMap['asian'], }, + { + name: 'anal fingering', + alias_for: tagsMap['ass-fingering'], + }, { name: 'ass licking', alias_for: tagsMap['ass-eating'], @@ -855,6 +870,10 @@ function getTagAliases(tagsMap) { name: 'fmf', alias_for: tagsMap['fmf'], }, + { + name: 'ffm', + alias_for: tagsMap['fmf'], + }, { name: 'bgb', alias_for: tagsMap['mfm'], @@ -947,6 +966,10 @@ function getTagAliases(tagsMap) { name: 'buttplug', alias_for: tagsMap['anal-toys'], }, + { + name: 'butt plug', + alias_for: tagsMap['anal-toys'], + }, { name: 'caning', alias_for: tagsMap['corporal-punishment'], diff --git a/src/media.js b/src/media.js index 05170312..3a7d288f 100644 --- a/src/media.js +++ b/src/media.js @@ -23,9 +23,7 @@ async function storePoster(release, releaseEntry) { console.log(`Storing poster for (${release.site.name}, ${releaseEntry.id}) "${release.title}"`); const res = await bhttp.get(release.poster); - const thumbnail = await sharp(res.body) - .resize({ width: Math.floor((config.media.thumbnailSize / 9) * 16), height: config.media.thumbnailSize }) // ensure thumbnail is 16:9 - .toBuffer(); + const thumbnail = await sharp(res.body).resize({ height: config.media.thumbnailSize }).toBuffer(); if (res.statusCode === 200) { const { pathname } = new URL(release.poster); diff --git a/src/releases.js b/src/releases.js index 2392643f..387f2fea 100644 --- a/src/releases.js +++ b/src/releases.js @@ -143,9 +143,32 @@ async function fetchActorReleases(actorId, actorSlug) { return curateReleases(releases); } +async function fetchTagReleases(tagId, tagSlug) { + const releases = await knex('tags_associated') + .where({ 'tags.id': tagId }) + .orWhere({ 'tags.slug': tagSlug }) + .select( + 'releases.*', + 'tags.name as tag_name', + 'sites.name as site_name', 'sites.slug as site_slug', 'sites.url as site_url', 'sites.network_id', + 'studios.name as studio_name', 'sites.slug as site_slug', 'studios.url as studio_url', + 'networks.name as network_name', 'networks.slug as network_slug', 'networks.url as network_url', + ) + .leftJoin('releases', 'tags_associated.release_id', 'releases.id') + .leftJoin('tags', 'tags_associated.tag_id', 'tags.id') + .leftJoin('sites', 'releases.site_id', 'sites.id') + .leftJoin('studios', 'releases.studio_id', 'studios.id') + .leftJoin('networks', 'sites.network_id', 'networks.id') + .orderBy([{ column: 'releases.date', order: 'desc' }, { column: 'releases.created_at', order: 'desc' }]) + .limit(100); + + return curateReleases(releases); +} + module.exports = { fetchReleases, fetchActorReleases, fetchSiteReleases, fetchNetworkReleases, + fetchTagReleases, }; diff --git a/src/scrapers/vixen.js b/src/scrapers/vixen.js index f772246e..8cf727f4 100644 --- a/src/scrapers/vixen.js +++ b/src/scrapers/vixen.js @@ -7,6 +7,15 @@ const moment = require('moment'); const { matchTags } = require('../tags'); +const defaultTags = { + blacked: ['bbc'], + blackedraw: ['bbc'], + tushy: ['anal'], + tushyraw: ['anal'], + vixen: [], + deeper: [], +}; + function scrapeLatest(html, site) { const $ = cheerio.load(html, { normalizeWhitespace: true }); @@ -73,7 +82,7 @@ async function scrapeScene(html, url, site) { } = scene; const date = new Date(scene.releaseDate); - const tags = await matchTags(rawTags); + const tags = await matchTags([...defaultTags[site.slug], ...rawTags]); return { url, diff --git a/src/tags.js b/src/tags.js index 87d28226..935c6090 100644 --- a/src/tags.js +++ b/src/tags.js @@ -2,6 +2,49 @@ const knex = require('./knex'); +async function curateTag(tag) { + const aliases = await knex('tags').where({ alias_for: tag.id }); + + return { + id: tag.id, + name: tag.name, + description: tag.description, + group: { + id: tag.group_id, + name: tag.group_name, + description: tag.group_description, + slug: tag.group_slug, + }, + aliases: aliases.map(({ name }) => name), + }; +} + +function curateTags(tags) { + return Promise.all(tags.map(async tag => curateTag(tag))); +} + +async function storeTags(release, releaseEntry) { + return knex('tags_associated').insert(release.tags.map(tagId => ({ + tag_id: tagId, + release_id: releaseEntry.id, + }))); +} + +async function fetchTags(tagId, tagSlug) { + const tags = await knex('tags') + .where({ 'tags.id': tagId }) + .orWhere({ 'tags.slug': tagSlug }) + .andWhere({ 'tags.alias_for': null }) + .select( + 'tags.*', + 'tags_groups.id as group_id', 'tags_groups.name as group_name', 'tags_groups.slug as group_slug', 'tags_groups.description as groups_description', + ) + .leftJoin('tags_groups', 'tags.group_id', 'tags_groups.id') + .limit(100); + + return curateTags(tags); +} + async function matchTags(rawTags) { const tags = rawTags .concat(rawTags.map(tag => tag.toLowerCase())) @@ -25,14 +68,8 @@ async function matchTags(rawTags) { return tagEntries; } -async function storeTags(release, releaseEntry) { - return knex('tags_associated').insert(release.tags.map(tagId => ({ - tag_id: tagId, - release_id: releaseEntry.id, - }))); -} - module.exports = { - matchTags, storeTags, + fetchTags, + matchTags, }; diff --git a/src/web/releases.js b/src/web/releases.js index 082a8137..5b612a70 100644 --- a/src/web/releases.js +++ b/src/web/releases.js @@ -5,6 +5,7 @@ const { fetchActorReleases, fetchNetworkReleases, fetchSiteReleases, + fetchTagReleases, } = require('../releases'); async function fetchReleasesApi(req, res) { @@ -40,9 +41,19 @@ async function fetchSiteReleasesApi(req, res) { res.send(releases); } +async function fetchTagReleasesApi(req, res) { + const tagId = typeof req.params.tagId === 'number' ? req.params.tagId : null; + const tagSlug = typeof req.params.tagId === 'string' ? req.params.tagId : null; + + const releases = await fetchTagReleases(tagId, tagSlug); + + res.send(releases); +} + module.exports = { fetchReleases: fetchReleasesApi, fetchActorReleases: fetchActorReleasesApi, fetchNetworkReleases: fetchNetworkReleasesApi, fetchSiteReleases: fetchSiteReleasesApi, + fetchTagReleases: fetchTagReleasesApi, }; diff --git a/src/web/server.js b/src/web/server.js index 2f93b2da..43dd905d 100644 --- a/src/web/server.js +++ b/src/web/server.js @@ -11,6 +11,7 @@ const { fetchActorReleases, fetchNetworkReleases, fetchSiteReleases, + fetchTagReleases, } = require('./releases'); const { @@ -20,6 +21,7 @@ const { const { fetchActors } = require('./actors'); const { fetchSites } = require('./sites'); +const { fetchTags } = require('./tags'); function initServer() { const app = express(); @@ -50,6 +52,10 @@ function initServer() { router.get('/api/sites/:siteId', fetchSites); router.get('/api/sites/:siteId/releases', fetchSiteReleases); + router.get('/api/tags', fetchTags); + router.get('/api/tags/:tagId', fetchTags); + router.get('/api/tags/:tagId/releases', fetchTagReleases); + router.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../../public/index.html')); }); diff --git a/src/web/tags.js b/src/web/tags.js new file mode 100644 index 00000000..f2073b31 --- /dev/null +++ b/src/web/tags.js @@ -0,0 +1,16 @@ +'use strict'; + +const { fetchTags } = require('../tags'); + +async function fetchTagsApi(req, res) { + const tagId = typeof req.params.tagId === 'number' ? req.params.tagId : null; + const tagSlug = typeof req.params.tagId === 'string' ? req.params.tagId : null; + + const tags = await fetchTags(tagId, tagSlug); + + res.send(tags); +} + +module.exports = { + fetchTags: fetchTagsApi, +};