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,
+};