Compare commits
4 Commits
83d3621441
...
0a92586c53
| Author | SHA1 | Date |
|---|---|---|
|
|
0a92586c53 | |
|
|
90b3d8a4d6 | |
|
|
5a2e93e900 | |
|
|
4e81a8a1d6 |
|
|
@ -1,22 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="warning-container">
|
<div class="warning-container">
|
||||||
<div class="warning">
|
<div class="warning">
|
||||||
<div
|
<strong class="title">
|
||||||
class="logo"
|
<div
|
||||||
v-html="logo"
|
class="logo"
|
||||||
/>
|
v-html="logo"
|
||||||
|
/>contains sexually explicit content
|
||||||
<strong class="title">This website contains sexually explicit content</strong>
|
</strong>
|
||||||
|
|
||||||
<span class="copy agree">By entering, you agree to the following</span>
|
|
||||||
|
|
||||||
<ul class="rules">
|
<ul class="rules">
|
||||||
<li class="rule">You are at least 18 years old, and legally permitted to view adult material in your jurisdiction.</li>
|
<li class="rule">You are at least 18 years old, and legally permitted to view adult material in your jurisdiction.</li>
|
||||||
<li class="rule">You are prepared see, hear and read erotic and sexual material, and do not regard such content as obscene or offensive.</li>
|
<li class="rule">You do not regard erotic, sexual and pornographic material as obscene or offensive.</li>
|
||||||
<li class="rule">You understand that most sexual scenarios depicted on this website are fictional, performed by professional actors for the purpose of entertainment, and not representative of real-life interactions.</li>
|
<li class="rule">You understand that most sexual scenarios depicted on this website are fictional, performed by professional actors for the purpose of entertainment, and not representative of real-life interactions.</li>
|
||||||
<li class="rule">Respect your sexual partners, communicate about each other's desires and limits, and take precautions to prevent sexually transmitted infections and unintended pregnancies.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<span class="preferences">You can adjust your content preferences later</span>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<a
|
<a
|
||||||
href="https://www.google.com"
|
href="https://www.google.com"
|
||||||
|
|
@ -40,8 +39,6 @@
|
||||||
<span class="button-sub">I prefer straight content</span>
|
<span class="button-sub">I prefer straight content</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="preferences">You can adjust your content preferences later</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -84,21 +81,22 @@ export default {
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
width: 8rem;
|
width: 8rem;
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
fill: var(--primary);
|
fill: var(--primary);
|
||||||
margin: 1rem auto 0 auto;
|
margin: -.125rem .5rem 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title,
|
|
||||||
.copy,
|
.copy,
|
||||||
.rules {
|
.rules {
|
||||||
padding: 1rem;
|
padding: .5rem 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
display: block;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
margin: 0 0 1rem 0;
|
margin: 1rem 0 0 0;
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,7 +201,7 @@ export default {
|
||||||
.preferences {
|
.preferences {
|
||||||
color: var(--lighten);
|
color: var(--lighten);
|
||||||
display: block;
|
display: block;
|
||||||
padding: .5rem 0 2rem 0;
|
padding: .5rem 0 1rem 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: .9rem;
|
font-size: .9rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@
|
||||||
v-else-if="release.teaser && /^image\//.test(release.teaser.mime)"
|
v-else-if="release.teaser && /^image\//.test(release.teaser.mime)"
|
||||||
:src="getPath(release.teaser, 'thumbnail', { original: true })"
|
:src="getPath(release.teaser, 'thumbnail', { original: true })"
|
||||||
:alt="release.title"
|
:alt="release.title"
|
||||||
:width="photo.width"
|
:width="release.teaser.width"
|
||||||
:height="photo.height"
|
:height="release.teaser.height"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
class="item trailer"
|
class="item trailer"
|
||||||
>
|
>
|
||||||
|
|
@ -68,8 +68,8 @@
|
||||||
<img
|
<img
|
||||||
:src="getPath(cover, 'thumbnail')"
|
:src="getPath(cover, 'thumbnail')"
|
||||||
:style="{ 'background-image': getBgPath(cover, 'lazy') }"
|
:style="{ 'background-image': getBgPath(cover, 'lazy') }"
|
||||||
:width="photo.width"
|
:width="cover.width"
|
||||||
:height="photo.height"
|
:height="cover.height"
|
||||||
class="item cover"
|
class="item cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@load="$emit('load', $event)"
|
@load="$emit('load', $event)"
|
||||||
|
|
|
||||||
|
|
@ -55,14 +55,14 @@
|
||||||
v-show="me && isStashed"
|
v-show="me && isStashed"
|
||||||
icon="heart7"
|
icon="heart7"
|
||||||
class="stash stashed noselect"
|
class="stash stashed noselect"
|
||||||
@click="unstashScene"
|
@click="unstashRelease"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Icon
|
<Icon
|
||||||
v-show="me && !isStashed"
|
v-show="me && !isStashed"
|
||||||
icon="heart8"
|
icon="heart8"
|
||||||
class="stash unstashed noselect"
|
class="stash unstashed noselect"
|
||||||
@click="stashScene"
|
@click="stashRelease"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -251,18 +251,20 @@ async function fetchRelease(scroll = true) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stashScene() {
|
async function stashRelease() {
|
||||||
this.$store.dispatch('stashScene', {
|
this.$store.dispatch(this.$route.name === 'movie' ? 'stashMovie' : 'stashRelease', {
|
||||||
sceneId: this.release.id,
|
sceneId: this.release.id,
|
||||||
|
movieId: this.release.id,
|
||||||
stashId: this.$store.getters.favorites.id,
|
stashId: this.$store.getters.favorites.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.fetchRelease(false);
|
this.fetchRelease(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unstashScene() {
|
async function unstashRelease() {
|
||||||
this.$store.dispatch('unstashScene', {
|
this.$store.dispatch(this.$route.name === 'movie' ? 'unstashMovie' : 'unstashRelease', {
|
||||||
sceneId: this.release.id,
|
sceneId: this.release.id,
|
||||||
|
movieId: this.release.id,
|
||||||
stashId: this.$store.getters.favorites.id,
|
stashId: this.$store.getters.favorites.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -321,8 +323,8 @@ export default {
|
||||||
mounted: fetchRelease,
|
mounted: fetchRelease,
|
||||||
methods: {
|
methods: {
|
||||||
fetchRelease,
|
fetchRelease,
|
||||||
stashScene,
|
stashRelease,
|
||||||
unstashScene,
|
unstashRelease,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -164,12 +164,16 @@ export default {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
padding: .05rem .25rem .1rem .15rem;
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: .1rem;
|
||||||
border-radius: 0 0 .5rem 0;
|
border-radius: 0 0 .5rem 0;
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
background: var(--primary);
|
background: var(--primary);
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -372,7 +376,6 @@ export default {
|
||||||
.tile.new .poster::after {
|
.tile.new .poster::after {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
top: auto;
|
top: auto;
|
||||||
padding: .1rem .25rem .05rem .15rem;
|
|
||||||
border-radius: 0 .5rem 0 0;
|
border-radius: 0 .5rem 0 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,11 @@ function initReleasesActions(store, router) {
|
||||||
// const release = await get(`/releases/${releaseId}`);
|
// const release = await get(`/releases/${releaseId}`);
|
||||||
|
|
||||||
const { movie } = await graphql(`
|
const { movie } = await graphql(`
|
||||||
query Movie($movieId: Int!) {
|
query Movie(
|
||||||
|
$movieId: Int!
|
||||||
|
$hasAuth: Boolean!
|
||||||
|
$userId: Int
|
||||||
|
) {
|
||||||
movie(id: $movieId) {
|
movie(id: $movieId) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
|
|
@ -232,10 +236,27 @@ function initReleasesActions(store, router) {
|
||||||
hasLogo
|
hasLogo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stashes: stashesMovies(
|
||||||
|
filter: {
|
||||||
|
stash: {
|
||||||
|
userId: {
|
||||||
|
equalTo: $userId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) @include(if: $hasAuth) {
|
||||||
|
stash {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
movieId: Number(movieId),
|
movieId: Number(movieId),
|
||||||
|
hasAuth: !!store.state.auth.user,
|
||||||
|
userId: store.state.auth.user?.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!movie) {
|
if (!movie) {
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,21 @@ function initStashesActions(_store, _router) {
|
||||||
await del(`/stashes/${stashId}/scenes/${sceneId}`);
|
await del(`/stashes/${stashId}/scenes/${sceneId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function stashMovie(context, { movieId, stashId }) {
|
||||||
|
await post(`/stashes/${stashId}/movies`, { movieId });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unstashMovie(context, { movieId, stashId }) {
|
||||||
|
await del(`/stashes/${stashId}/movies/${movieId}`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
stashActor,
|
stashActor,
|
||||||
stashScene,
|
stashScene,
|
||||||
|
stashMovie,
|
||||||
unstashActor,
|
unstashActor,
|
||||||
unstashScene,
|
unstashScene,
|
||||||
|
unstashMovie,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1054,7 +1054,8 @@ exports.up = knex => Promise.resolve()
|
||||||
|
|
||||||
table.integer('user_id')
|
table.integer('user_id')
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('users');
|
.inTable('users')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
table.string('name')
|
table.string('name')
|
||||||
.notNullable();
|
.notNullable();
|
||||||
|
|
@ -1074,27 +1075,48 @@ exports.up = knex => Promise.resolve()
|
||||||
table.integer('stash_id')
|
table.integer('stash_id')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('stashes');
|
.inTable('stashes')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
table.integer('scene_id')
|
table.integer('scene_id')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('releases');
|
.inTable('releases')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
table.unique(['stash_id', 'scene_id']);
|
table.unique(['stash_id', 'scene_id']);
|
||||||
|
|
||||||
table.string('comment');
|
table.string('comment');
|
||||||
}))
|
}))
|
||||||
|
.then(() => knex.schema.createTable('stashes_movies', (table) => {
|
||||||
|
table.integer('stash_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('stashes')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.integer('movie_id')
|
||||||
|
.notNullable()
|
||||||
|
.references('id')
|
||||||
|
.inTable('movies')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
|
table.unique(['stash_id', 'movie_id']);
|
||||||
|
|
||||||
|
table.string('comment');
|
||||||
|
}))
|
||||||
.then(() => knex.schema.createTable('stashes_actors', (table) => {
|
.then(() => knex.schema.createTable('stashes_actors', (table) => {
|
||||||
table.integer('stash_id')
|
table.integer('stash_id')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('stashes');
|
.inTable('stashes')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
table.integer('actor_id')
|
table.integer('actor_id')
|
||||||
.notNullable()
|
.notNullable()
|
||||||
.references('id')
|
.references('id')
|
||||||
.inTable('actors');
|
.inTable('actors')
|
||||||
|
.onDelete('cascade');
|
||||||
|
|
||||||
table.unique(['stash_id', 'actor_id']);
|
table.unique(['stash_id', 'actor_id']);
|
||||||
|
|
||||||
|
|
@ -1304,6 +1326,7 @@ exports.up = knex => Promise.resolve()
|
||||||
|
|
||||||
ALTER TABLE stashes ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE stashes ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE stashes_scenes ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE stashes_scenes ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE stashes_movies ENABLE ROW LEVEL SECURITY;
|
||||||
ALTER TABLE stashes_actors ENABLE ROW LEVEL SECURITY;
|
ALTER TABLE stashes_actors ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
CREATE POLICY stashes_policy_select ON stashes FOR SELECT USING (stashes.public OR stashes.user_id = current_user_id());
|
CREATE POLICY stashes_policy_select ON stashes FOR SELECT USING (stashes.public OR stashes.user_id = current_user_id());
|
||||||
|
|
@ -1319,6 +1342,14 @@ exports.up = knex => Promise.resolve()
|
||||||
AND (stashes.user_id = current_user_id() OR stashes.public)
|
AND (stashes.user_id = current_user_id() OR stashes.public)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
CREATE POLICY stashes_policy ON stashes_movies
|
||||||
|
USING (EXISTS (
|
||||||
|
SELECT *
|
||||||
|
FROM stashes
|
||||||
|
WHERE stashes.id = stashes_movies.stash_id
|
||||||
|
AND (stashes.user_id = current_user_id() OR stashes.public)
|
||||||
|
));
|
||||||
|
|
||||||
CREATE POLICY stashes_policy ON stashes_actors
|
CREATE POLICY stashes_policy ON stashes_actors
|
||||||
USING (EXISTS (
|
USING (EXISTS (
|
||||||
SELECT *
|
SELECT *
|
||||||
|
|
@ -1416,6 +1447,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
|
||||||
DROP TABLE IF EXISTS entities CASCADE;
|
DROP TABLE IF EXISTS entities CASCADE;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS stashes_scenes CASCADE;
|
DROP TABLE IF EXISTS stashes_scenes CASCADE;
|
||||||
|
DROP TABLE IF EXISTS stashes_movies CASCADE;
|
||||||
DROP TABLE IF EXISTS stashes_actors CASCADE;
|
DROP TABLE IF EXISTS stashes_actors CASCADE;
|
||||||
DROP TABLE IF EXISTS stashes CASCADE;
|
DROP TABLE IF EXISTS stashes CASCADE;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "traxxx",
|
"name": "traxxx",
|
||||||
"version": "1.186.0",
|
"version": "1.186.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"version": "1.186.0",
|
"version": "1.186.1",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@casl/ability": "^5.2.2",
|
"@casl/ability": "^5.2.2",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "traxxx",
|
"name": "traxxx",
|
||||||
"version": "1.186.0",
|
"version": "1.186.1",
|
||||||
"description": "All the latest porn releases in one place",
|
"description": "All the latest porn releases in one place",
|
||||||
"main": "src/app.js",
|
"main": "src/app.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 3.0 MiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 3.2 MiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 3.1 MiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 3.4 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 495 KiB After Width: | Height: | Size: 495 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
|
@ -98,7 +98,7 @@ const tags = [
|
||||||
{
|
{
|
||||||
name: 'airtight',
|
name: 'airtight',
|
||||||
slug: 'airtight',
|
slug: 'airtight',
|
||||||
description: 'Stuffing one cock in her ass, one in her pussy, and one in her mouth, filling all of her penetrable holes and sealing her airtight like a figurative balloon. In other words, simultaneously getting [double penetrated](/tag/dp), and giving a [blowjob](/tag/blowjob) or getting [facefucked](/tag/facefuck). Being airtight implies being [gangbanged](/tag/gangbang).', /* eslint-disable-line max-len */
|
description: 'Stuffing one cock in your ass, one in your pussy, and one in your mouth, filling up all of your penetrable holes and getting sealed airtight like a figurative balloon. In other words, simultaneously getting [double penetrated](/tag/dp), and giving a [blowjob](/tag/blowjob) or getting [facefucked](/tag/facefucking). Being airtight implies being [gangbanged](/tag/gangbang).', /* eslint-disable-line max-len */
|
||||||
priority: 9,
|
priority: 9,
|
||||||
group: 'penetration',
|
group: 'penetration',
|
||||||
},
|
},
|
||||||
|
|
@ -116,19 +116,19 @@ const tags = [
|
||||||
{
|
{
|
||||||
name: 'anal',
|
name: 'anal',
|
||||||
slug: 'anal',
|
slug: 'anal',
|
||||||
description: 'Getting fucked in the asshole.',
|
description: 'Getting your asshole fucked. Generally considered naughtier, you may or may not find it a pleasurable alternative to vaginal sex. Enjoyable anal sex tends to require practice and patience, a rectal douching ritual, as well and a generous use of water-based lubricant to ensure comfort and prevent small tears that could lead to bacterial infections. Although you cannot get pregnant through anal sex directly, there is an increased risk of passing STDs, so the use of condoms and regular health checks are strongly recommended.',
|
||||||
priority: 9,
|
priority: 9,
|
||||||
group: 'penetration',
|
group: 'penetration',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'anal fingering',
|
name: 'anal fingering',
|
||||||
slug: 'anal-fingering',
|
slug: 'anal-fingering',
|
||||||
description: 'Inserting one or multiple fingers into the asshole.',
|
description: 'Inserting one or multiple fingers into your asshole. If you use your entire hand beyond the knuckles, you are [anal fisting](/tag/anal-fisting).',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'anal fisting',
|
name: 'anal fisting',
|
||||||
slug: 'anal-fisting',
|
slug: 'anal-fisting',
|
||||||
description: 'Shoving an entire hand into the asshole.',
|
description: 'Shoving an entire hand into your asshole.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'anal prolapse',
|
name: 'anal prolapse',
|
||||||
|
|
@ -242,13 +242,14 @@ const tags = [
|
||||||
name: 'blowjob',
|
name: 'blowjob',
|
||||||
slug: 'blowjob',
|
slug: 'blowjob',
|
||||||
priority: 5,
|
priority: 5,
|
||||||
|
description: 'Taking a dick in your mouth, sucking, licking and kissing it, often while giving a [handjob](/tag/handjob). You may slide it all the way [down your throat](/tag/deepthroat), or let them [fuck your face](/tag/facefucking).',
|
||||||
group: 'oral',
|
group: 'oral',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'blowbang',
|
name: 'blowbang',
|
||||||
slug: 'blowbang',
|
slug: 'blowbang',
|
||||||
priority: 9,
|
priority: 9,
|
||||||
description: 'Pleasuring a gang of three or more cocks by sucking and jerking off as many cocks as they can, often getting [facefucked](/tag/facefuck), groped and rubbed out, and followed by a [bukkake](/tag/bukkake). If they are getting fucked, it is a [gangbang](/tag/gangbang).',
|
description: 'Pleasuring a gang of three or more cocks by sucking and jerking off as many cocks as you can, often getting [facefucked](/tag/facefucking), groped and rubbed out, and followed by a [bukkake](/tag/bukkake). If you are also getting fucked, it is a [gangbang](/tag/gangbang).',
|
||||||
group: 'group',
|
group: 'group',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -343,7 +344,7 @@ const tags = [
|
||||||
{
|
{
|
||||||
name: 'double anal',
|
name: 'double anal',
|
||||||
slug: 'dap',
|
slug: 'dap',
|
||||||
description: 'Two cocks in the ass at the same time. If there\'s a third cock in her pussy, it is [double anal TP](/tag/da-tp).',
|
description: 'Two cocks filling up your ass at the same time. If there\'s a third cock in your pussy, it is [double anal triple penetration](/tag/da-tp).',
|
||||||
priority: 8,
|
priority: 8,
|
||||||
group: 'penetration',
|
group: 'penetration',
|
||||||
},
|
},
|
||||||
|
|
@ -388,13 +389,14 @@ const tags = [
|
||||||
name: 'deepthroat',
|
name: 'deepthroat',
|
||||||
slug: 'deepthroat',
|
slug: 'deepthroat',
|
||||||
priority: 6,
|
priority: 6,
|
||||||
|
description: 'Shoving a cock down your throat during a [blowjob](/tag/blowjob) or [facefuck](/tag/facefucking), giving them a tight sensation while showing off your skills. Without practice, a cock hitting the back of your mouth will likely make you [gag](/tag/gagging).',
|
||||||
group: 'oral',
|
group: 'oral',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'double penetration',
|
name: 'double penetration',
|
||||||
slug: 'dp',
|
slug: 'dp',
|
||||||
priority: 9,
|
priority: 9,
|
||||||
description: 'Fucking two cocks at once, with one in her ass, and one in her pussy. If she has another cock in her mouth, she is [airtight](/tag/airtight).',
|
description: 'Getting your [ass](/tag/anal) and pussy fucked at the same time. If you take another cock in your mouth, you are [airtight](/tag/airtight).',
|
||||||
group: 'penetration',
|
group: 'penetration',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -404,7 +406,7 @@ const tags = [
|
||||||
{
|
{
|
||||||
name: 'double vaginal',
|
name: 'double vaginal',
|
||||||
slug: 'dvp',
|
slug: 'dvp',
|
||||||
description: 'Fucking her pussy with two cocks at the same time. If there\'s a third cock in her asshole, it is [double vaginal TP](/tag/dv-tp).',
|
description: 'Getting fucked with two cocks in your pussy at the same time. If there\'s a third cock in your asshole, it is [double vaginal triple penetration](/tag/dv-tp).',
|
||||||
priority: 8,
|
priority: 8,
|
||||||
group: 'penetration',
|
group: 'penetration',
|
||||||
},
|
},
|
||||||
|
|
@ -450,6 +452,7 @@ const tags = [
|
||||||
name: 'facefucking',
|
name: 'facefucking',
|
||||||
slug: 'facefucking',
|
slug: 'facefucking',
|
||||||
priority: 7,
|
priority: 7,
|
||||||
|
description: 'A [blowjob](/tag/blowjob) where you give up control, and let them fuck your mouth and [throat](/tag/deepthroat) as if it\'s your pussy.',
|
||||||
group: 'oral',
|
group: 'oral',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -487,6 +490,10 @@ const tags = [
|
||||||
name: 'fisting DP',
|
name: 'fisting DP',
|
||||||
slug: 'fisting-dp',
|
slug: 'fisting-dp',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'flexible',
|
||||||
|
slug: 'flexible',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'MFF threesome',
|
name: 'MFF threesome',
|
||||||
slug: 'mff',
|
slug: 'mff',
|
||||||
|
|
@ -953,14 +960,14 @@ const tags = [
|
||||||
name: 'DA triple penetration',
|
name: 'DA triple penetration',
|
||||||
slug: 'da-tp',
|
slug: 'da-tp',
|
||||||
priority: 7,
|
priority: 7,
|
||||||
description: 'Triple penetration with two cocks in the ass, and one in the pussy. Also see [double vaginal TP](/tag/dv-tp).',
|
description: 'Triple penetration with [two cocks in your ass](/tag/dap), and one in your pussy. Also see [double vaginal triple penetration](/tag/dv-tp).',
|
||||||
group: 'penetration',
|
group: 'penetration',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'DV triple penetration',
|
name: 'DV triple penetration',
|
||||||
slug: 'dv-tp',
|
slug: 'dv-tp',
|
||||||
priority: 7,
|
priority: 7,
|
||||||
description: 'Triple penetration with two cocks in the pussy, and one in the ass. Also see [double anal TP](/tag/da-tp).',
|
description: 'Triple penetration with [two cocks in your pussy](/tag/dvp), and one in [your ass](/tag/anal). Also see [double anal triple penetration](/tag/da-tp).',
|
||||||
group: 'penetration',
|
group: 'penetration',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -650,9 +650,10 @@ const tagMedia = [
|
||||||
['blowbang', 1, 'Nicole Black in GIO1680', 'legalporno'],
|
['blowbang', 1, 'Nicole Black in GIO1680', 'legalporno'],
|
||||||
['blowjob', 1, 'Kylie Page in "Stepsis Gives Soapy Handjob In Shower"', 'spyfam'],
|
['blowjob', 1, 'Kylie Page in "Stepsis Gives Soapy Handjob In Shower"', 'spyfam'],
|
||||||
['blowjob', 4, 'Chloe Cherry in "Chloe\'s Big Anal"', 'darkx'],
|
['blowjob', 4, 'Chloe Cherry in "Chloe\'s Big Anal"', 'darkx'],
|
||||||
|
['blowjob', 'cecilia_lion_wefuckblackgirls', 'Cecilia Lion in "Cecilia Lion\'s Second Appearance"', 'wefuckblackgirls'],
|
||||||
|
['blowjob', 0, 'Adriana Chechik in "The Dinner Party"', 'realwifestories'],
|
||||||
['blowjob', 5, 'Kaylynn', 'mommyblowsbest'],
|
['blowjob', 5, 'Kaylynn', 'mommyblowsbest'],
|
||||||
['blowjob', 'azul_hermosa_realitykings', 'Azul Hermosa and Scott Nails in "Diva For A Day"', 'brazzers'],
|
['blowjob', 'azul_hermosa_realitykings', 'Azul Hermosa and Scott Nails in "Diva For A Day"', 'brazzers'],
|
||||||
['blowjob', 0, 'Adriana Chechik in "The Dinner Party"', 'realwifestories'],
|
|
||||||
['blowjob', 3, 'Rose Valie', 'handsonhardcore'],
|
['blowjob', 3, 'Rose Valie', 'handsonhardcore'],
|
||||||
['blowjob', 2, 'Luna Kitsuen in "Gag Reflex"', 'evilangel'],
|
['blowjob', 2, 'Luna Kitsuen in "Gag Reflex"', 'evilangel'],
|
||||||
['bondage', 0, 'Veronica Leal', 'herlimit'],
|
['bondage', 0, 'Veronica Leal', 'herlimit'],
|
||||||
|
|
@ -675,6 +676,7 @@ const tagMedia = [
|
||||||
['cum-in-mouth', 5, 'Emma Hix in "A Big Dick"', 'darkx'],
|
['cum-in-mouth', 5, 'Emma Hix in "A Big Dick"', 'darkx'],
|
||||||
['cum-in-mouth', 4, 'Vanna Bardot and Isiah Maxwell in "Vanna Craves Isiah\'s Cock!"', 'darkx'],
|
['cum-in-mouth', 4, 'Vanna Bardot and Isiah Maxwell in "Vanna Craves Isiah\'s Cock!"', 'darkx'],
|
||||||
['cum-in-mouth', 2, 'Jaye Summers in "Double The Cum"', 'hardx'],
|
['cum-in-mouth', 2, 'Jaye Summers in "Double The Cum"', 'hardx'],
|
||||||
|
['cum-in-mouth', 'lara_frost_legalporno', 'Lara Frost in NRX059', 'legalporno'],
|
||||||
['cum-in-mouth', 0, 'Vina Sky and Avi Love', 'hardx'],
|
['cum-in-mouth', 0, 'Vina Sky and Avi Love', 'hardx'],
|
||||||
['cum-on-boobs', 1, 'Kylie Page in "Melt In Your Mouth"', 'twistyshard'],
|
['cum-on-boobs', 1, 'Kylie Page in "Melt In Your Mouth"', 'twistyshard'],
|
||||||
['cum-on-boobs', 0, 'Alessandra Jane', 'private'],
|
['cum-on-boobs', 0, 'Alessandra Jane', 'private'],
|
||||||
|
|
@ -746,6 +748,7 @@ const tagMedia = [
|
||||||
['dp', 3, 'Hime Marie in AA047', 'legalporno'],
|
['dp', 3, 'Hime Marie in AA047', 'legalporno'],
|
||||||
['dp', 2, 'Megan Rain in "DP Masters 4"', 'julesjordan'],
|
['dp', 2, 'Megan Rain in "DP Masters 4"', 'julesjordan'],
|
||||||
['dp', 6, 'Kira Noir', 'hardx'],
|
['dp', 6, 'Kira Noir', 'hardx'],
|
||||||
|
['dp', 'lara_frost_legalporno', 'Lara Frost in NRX070', 'legalporno'],
|
||||||
['dp', 5, 'Lana Rhoades in "Gangbang Me 3"', 'hardx'],
|
['dp', 5, 'Lana Rhoades in "Gangbang Me 3"', 'hardx'],
|
||||||
['dp', 'zaawaadi_roccosiffredi', 'Zaawaadi in "My Name Is Zaawaadi"', 'roccosiffredi'],
|
['dp', 'zaawaadi_roccosiffredi', 'Zaawaadi in "My Name Is Zaawaadi"', 'roccosiffredi'],
|
||||||
['dp', 7, 'Chloe Lamour in "DP Masters 7"', 'julesjordan'],
|
['dp', 7, 'Chloe Lamour in "DP Masters 7"', 'julesjordan'],
|
||||||
|
|
@ -771,6 +774,7 @@ const tagMedia = [
|
||||||
['facial', 'hope_howell_manojob', 'Hope Howell in "Super Slutty Step-Daugher"', 'manojob'],
|
['facial', 'hope_howell_manojob', 'Hope Howell in "Super Slutty Step-Daugher"', 'manojob'],
|
||||||
['facial', 2, 'Ashly Anderson', 'hookuphotshot'],
|
['facial', 2, 'Ashly Anderson', 'hookuphotshot'],
|
||||||
['facial', 4, 'Kendra Heart', 'facialsforever'],
|
['facial', 4, 'Kendra Heart', 'facialsforever'],
|
||||||
|
['flexible', 'lara_frost_legalporno', 'Lara Frost in NRX059', 'legalporno'],
|
||||||
['enhanced-boobs', 7, 'Charley Atwell', 'icandigirls'],
|
['enhanced-boobs', 7, 'Charley Atwell', 'icandigirls'],
|
||||||
['enhanced-boobs', 14, 'Rikki Six', 'dreamdolls'],
|
['enhanced-boobs', 14, 'Rikki Six', 'dreamdolls'],
|
||||||
['enhanced-boobs', 2, 'Gia Milana in "Hot Anal Latina"', 'hardx'],
|
['enhanced-boobs', 2, 'Gia Milana in "Hot Anal Latina"', 'hardx'],
|
||||||
|
|
@ -787,7 +791,8 @@ const tagMedia = [
|
||||||
['enhanced-boobs', '23d', 'Lulu Sex Bomb in "Tropical Touch"'],
|
['enhanced-boobs', '23d', 'Lulu Sex Bomb in "Tropical Touch"'],
|
||||||
['enhanced-boobs', 22, 'Sakura Sena'],
|
['enhanced-boobs', 22, 'Sakura Sena'],
|
||||||
['enhanced-boobs', 'mareeva_trudy_photodromm_1', 'Mareeva and Trudy', 'photodromm'],
|
['enhanced-boobs', 'mareeva_trudy_photodromm_1', 'Mareeva and Trudy', 'photodromm'],
|
||||||
['enhanced-boobs', 'shawna_lenee_inthecrack_1', 'Shawna Lenee', 'inthecrack'],
|
['enhanced-boobs', 'lara_frost_legalporno', 'Lara Frost in NRX059', 'legalporno'],
|
||||||
|
['enhanced-boobs', 'shawna_lenee_inthecrack_3', 'Shawna Lenee', 'inthecrack'],
|
||||||
['enhanced-boobs', 16, 'Marsha May in "Once You Go Black 7"', 'julesjordan'],
|
['enhanced-boobs', 16, 'Marsha May in "Once You Go Black 7"', 'julesjordan'],
|
||||||
['enhanced-boobs', 'azul_hermosa_pornstarslikeitbig', 'Azul Hermosa in "She Likes Rough Quickies"', 'pornstarslikeitbig'],
|
['enhanced-boobs', 'azul_hermosa_pornstarslikeitbig', 'Azul Hermosa in "She Likes Rough Quickies"', 'pornstarslikeitbig'],
|
||||||
['enhanced-boobs', 21, 'Emelie Ekström'],
|
['enhanced-boobs', 21, 'Emelie Ekström'],
|
||||||
|
|
@ -814,10 +819,10 @@ const tagMedia = [
|
||||||
['fisting', 0, 'Abella Danger and Karma Rx in "Neon Dreaming"', 'brazzers'],
|
['fisting', 0, 'Abella Danger and Karma Rx in "Neon Dreaming"', 'brazzers'],
|
||||||
['fisting-dp', 0, 'Janice Griffith and Veronica Avluv in "The Nymphomaniac\'s Apprentice', 'theupperfloor'],
|
['fisting-dp', 0, 'Janice Griffith and Veronica Avluv in "The Nymphomaniac\'s Apprentice', 'theupperfloor'],
|
||||||
['gangbang', 5, 'Carter Cruise\'s first gangbang in "Slut Puppies 9"', 'julesjordan'],
|
['gangbang', 5, 'Carter Cruise\'s first gangbang in "Slut Puppies 9"', 'julesjordan'],
|
||||||
['gangbang', 'poster', 'Kristen Scott in "Interracial Gangbang!"', 'julesjordan'],
|
['gangbang', 'kristen_scott_julesjordan', 'Kristen Scott in "Interracial Gangbang!"', 'julesjordan'],
|
||||||
['gangbang', 7, 'Alexa Flexy in GL376'],
|
['gangbang', 'lara_frost_legalporno_1', 'Lara Frost in NRX070', 'legalporno'],
|
||||||
|
['gangbang', 7, 'Alexa Flexy in GL376', 'legalporno'],
|
||||||
['gangbang', 0, '"4 On 1 Gangbangs"', 'doghousedigital'],
|
['gangbang', 0, '"4 On 1 Gangbangs"', 'doghousedigital'],
|
||||||
['gangbang', 6, 'Silvia Soprano in GIO1580', 'legalporno'],
|
|
||||||
['gangbang', 4, 'Marley Brinx in "The Gangbang of Marley Brinx"', 'julesjordan'],
|
['gangbang', 4, 'Marley Brinx in "The Gangbang of Marley Brinx"', 'julesjordan'],
|
||||||
['gangbang', 1, 'Ginger Lynn in "Gangbang Mystique", a photoset shot by Suze Randall, 1984. Depicting a woman \'airtight\' pushed the boundaries of pornography at the time.'],
|
['gangbang', 1, 'Ginger Lynn in "Gangbang Mystique", a photoset shot by Suze Randall, 1984. Depicting a woman \'airtight\' pushed the boundaries of pornography at the time.'],
|
||||||
['gaping', 1, 'Vina Sky in "Vina Sky Does Anal"', 'hardx'],
|
['gaping', 1, 'Vina Sky in "Vina Sky Does Anal"', 'hardx'],
|
||||||
|
|
@ -914,8 +919,9 @@ const tagMedia = [
|
||||||
['titty-fucking', 4, 'Set 5532', 'tugjobs'],
|
['titty-fucking', 4, 'Set 5532', 'tugjobs'],
|
||||||
['titty-fucking', 3, 'Anna Bell Peaks in "Ringing Her Bell"', 'milfvr'],
|
['titty-fucking', 3, 'Anna Bell Peaks in "Ringing Her Bell"', 'milfvr'],
|
||||||
['titty-fucking', 1, 'Chloe Lamour', 'ddfbusty'],
|
['titty-fucking', 1, 'Chloe Lamour', 'ddfbusty'],
|
||||||
['toy-anal', 1, 'Nina North and Cassidy Klein in "Nina\'s First Lesbian Anal"', 'lesbianx'],
|
|
||||||
['toy-anal', 3, 'Kelly and Leona in "Sleeping Over"', 'lezcuties'],
|
['toy-anal', 3, 'Kelly and Leona in "Sleeping Over"', 'lezcuties'],
|
||||||
|
['toy-anal', 'ember_snow_jane_wilde_lesbianx', 'Ember Snow and Jane Wilde in "Ember\'s Wilde Ride"', 'lesbianx'],
|
||||||
|
['toy-anal', 1, 'Nina North and Cassidy Klein in "Nina\'s First Lesbian Anal"', 'lesbianx'],
|
||||||
['toy-anal', 2, 'Denise, Irina and Laki in "Sexy Slumber"', 'lezcuties'],
|
['toy-anal', 2, 'Denise, Irina and Laki in "Sexy Slumber"', 'lezcuties'],
|
||||||
['toy-anal', 0, 'Kira Noir in 1225', 'inthecrack'],
|
['toy-anal', 0, 'Kira Noir in 1225', 'inthecrack'],
|
||||||
['toy-dp', 1, 'Krissy Lynn and London River in "Lesbian DP Workout"', 'lesbianx'],
|
['toy-dp', 1, 'Krissy Lynn and London River in "Lesbian DP Workout"', 'lesbianx'],
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ async function signup(credentials) {
|
||||||
const hashedPassword = (await scrypt(credentials.password, salt, 64)).toString('hex');
|
const hashedPassword = (await scrypt(credentials.password, salt, 64)).toString('hex');
|
||||||
const storedPassword = `${salt}/${hashedPassword}`;
|
const storedPassword = `${salt}/${hashedPassword}`;
|
||||||
|
|
||||||
const [user] = await knex('users')
|
const [userId] = await knex('users')
|
||||||
.insert({
|
.insert({
|
||||||
username: credentials.username,
|
username: credentials.username,
|
||||||
email: credentials.email,
|
email: credentials.email,
|
||||||
|
|
@ -76,13 +76,13 @@ async function signup(credentials) {
|
||||||
.returning('id');
|
.returning('id');
|
||||||
|
|
||||||
await knex('stashes').insert({
|
await knex('stashes').insert({
|
||||||
user_id: user.id,
|
user_id: userId,
|
||||||
name: 'Favorites',
|
name: 'Favorites',
|
||||||
slug: 'favorites',
|
slug: 'favorites',
|
||||||
public: false,
|
public: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
return fetchUser(user.id);
|
return fetchUser(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@ const knex = require('./knex');
|
||||||
const { HttpError } = require('./errors');
|
const { HttpError } = require('./errors');
|
||||||
|
|
||||||
function curateStash(stash) {
|
function curateStash(stash) {
|
||||||
|
if (!stash) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const curatedStash = {
|
const curatedStash = {
|
||||||
id: stash.id,
|
id: stash.id,
|
||||||
name: stash.name,
|
name: stash.name,
|
||||||
|
|
@ -52,12 +56,22 @@ async function stashScene(sceneId, stashId, sessionUser) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function stashMovie(movieId, stashId, sessionUser) {
|
||||||
|
const stash = await fetchStash(stashId, sessionUser);
|
||||||
|
|
||||||
|
await knex('stashes_movies')
|
||||||
|
.insert({
|
||||||
|
stash_id: stash.id,
|
||||||
|
movie_id: movieId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function unstashActor(actorId, stashId, sessionUser) {
|
async function unstashActor(actorId, stashId, sessionUser) {
|
||||||
await knex
|
await knex
|
||||||
.from('stashes_actors AS deletable')
|
.from('stashes_actors AS deletable')
|
||||||
.where('deletable.actor_id', actorId)
|
.where('deletable.actor_id', actorId)
|
||||||
.where('deletable.stash_id', stashId)
|
.where('deletable.stash_id', stashId)
|
||||||
.whereExists(knex('stashes_actors') // verify user owns this stash
|
.whereExists(knex('stashes_actors') // verify user owns this stash, complimentary to row-level security
|
||||||
.leftJoin('stashes', 'stashes.id', 'stashes_actors.stash_id')
|
.leftJoin('stashes', 'stashes.id', 'stashes_actors.stash_id')
|
||||||
.where('stashes_actors.stash_id', knex.raw('deletable.stash_id'))
|
.where('stashes_actors.stash_id', knex.raw('deletable.stash_id'))
|
||||||
.where('stashes.user_id', sessionUser.id))
|
.where('stashes.user_id', sessionUser.id))
|
||||||
|
|
@ -69,17 +83,31 @@ async function unstashScene(sceneId, stashId, sessionUser) {
|
||||||
.from('stashes_scenes AS deletable')
|
.from('stashes_scenes AS deletable')
|
||||||
.where('deletable.scene_id', sceneId)
|
.where('deletable.scene_id', sceneId)
|
||||||
.where('deletable.stash_id', stashId)
|
.where('deletable.stash_id', stashId)
|
||||||
.whereExists(knex('stashes_scenes') // verify user owns this stash
|
.whereExists(knex('stashes_scenes') // verify user owns this stash, complimentary to row-level security
|
||||||
.leftJoin('stashes', 'stashes.id', 'stashes_scenes.stash_id')
|
.leftJoin('stashes', 'stashes.id', 'stashes_scenes.stash_id')
|
||||||
.where('stashes_scenes.stash_id', knex.raw('deletable.stash_id'))
|
.where('stashes_scenes.stash_id', knex.raw('deletable.stash_id'))
|
||||||
.where('stashes.user_id', sessionUser.id))
|
.where('stashes.user_id', sessionUser.id))
|
||||||
.delete();
|
.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function unstashMovie(movieId, stashId, sessionUser) {
|
||||||
|
await knex
|
||||||
|
.from('stashes_movies AS deletable')
|
||||||
|
.where('deletable.movie_id', movieId)
|
||||||
|
.where('deletable.stash_id', stashId)
|
||||||
|
.whereExists(knex('stashes_movies') // verify user owns this stash, complimentary to row-level security
|
||||||
|
.leftJoin('stashes', 'stashes.id', 'stashes_movies.stash_id')
|
||||||
|
.where('stashes_movies.stash_id', knex.raw('deletable.stash_id'))
|
||||||
|
.where('stashes.user_id', sessionUser.id))
|
||||||
|
.delete();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
curateStash,
|
curateStash,
|
||||||
stashActor,
|
stashActor,
|
||||||
stashScene,
|
stashScene,
|
||||||
|
stashMovie,
|
||||||
unstashScene,
|
unstashScene,
|
||||||
unstashActor,
|
unstashActor,
|
||||||
|
unstashMovie,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ function curateUser(user) {
|
||||||
identityVerified: user.identity_verified,
|
identityVerified: user.identity_verified,
|
||||||
ability,
|
ability,
|
||||||
createdAt: user.created_at,
|
createdAt: user.created_at,
|
||||||
stashes: user.stashes?.map(stash => curateStash(stash)) || [],
|
stashes: user.stashes?.filter(Boolean).map(stash => curateStash(stash)) || [],
|
||||||
};
|
};
|
||||||
|
|
||||||
return curatedUser;
|
return curatedUser;
|
||||||
|
|
@ -26,7 +26,7 @@ function curateUser(user) {
|
||||||
|
|
||||||
async function fetchUser(userId, raw) {
|
async function fetchUser(userId, raw) {
|
||||||
const user = await knex('users')
|
const user = await knex('users')
|
||||||
.select(knex.raw('users.*, users_roles.abilities as role_abilities, json_agg(stashes) as stashes'))
|
.select(knex.raw('users.*, users_roles.abilities as role_abilities, COALESCE(json_agg(stashes) FILTER (WHERE stashes.id IS NOT NULL), \'[]\') as stashes'))
|
||||||
.modify((builder) => {
|
.modify((builder) => {
|
||||||
if (typeof userId === 'number') {
|
if (typeof userId === 'number') {
|
||||||
builder.where('users.id', userId);
|
builder.where('users.id', userId);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const argv = require('../argv');
|
||||||
const logger = require('../logger')(__filename);
|
const logger = require('../logger')(__filename);
|
||||||
|
|
||||||
function errorHandler(error, req, res, _next) {
|
function errorHandler(error, req, res, _next) {
|
||||||
logger.warn(`Failed to fulfill request to ${req.path}: ${error.message}`);
|
logger.warn(`Failed to fulfill request to ${req.path}: ${error.message}`);
|
||||||
|
|
||||||
|
if (argv.debug) {
|
||||||
|
logger.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
if (error.httpCode) {
|
if (error.httpCode) {
|
||||||
res.status(error.httpCode).send(error.message);
|
res.status(error.httpCode).send(error.message);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,10 @@ const {
|
||||||
const {
|
const {
|
||||||
stashActor,
|
stashActor,
|
||||||
stashScene,
|
stashScene,
|
||||||
|
stashMovie,
|
||||||
unstashActor,
|
unstashActor,
|
||||||
unstashScene,
|
unstashScene,
|
||||||
|
unstashMovie,
|
||||||
} = require('./stashes');
|
} = require('./stashes');
|
||||||
|
|
||||||
async function initServer() {
|
async function initServer() {
|
||||||
|
|
@ -83,9 +85,11 @@ async function initServer() {
|
||||||
|
|
||||||
router.post('/api/stashes/:stashId/actors', stashActor);
|
router.post('/api/stashes/:stashId/actors', stashActor);
|
||||||
router.post('/api/stashes/:stashId/scenes', stashScene);
|
router.post('/api/stashes/:stashId/scenes', stashScene);
|
||||||
|
router.post('/api/stashes/:stashId/movies', stashMovie);
|
||||||
|
|
||||||
router.delete('/api/stashes/:stashId/actors/:actorId', unstashActor);
|
router.delete('/api/stashes/:stashId/actors/:actorId', unstashActor);
|
||||||
router.delete('/api/stashes/:stashId/scenes/:sceneId', unstashScene);
|
router.delete('/api/stashes/:stashId/scenes/:sceneId', unstashScene);
|
||||||
|
router.delete('/api/stashes/:stashId/movies/:movieId', unstashMovie);
|
||||||
|
|
||||||
router.get('/api/scenes', fetchScenes);
|
router.get('/api/scenes', fetchScenes);
|
||||||
router.get('/api/scenes/:releaseId', fetchScene);
|
router.get('/api/scenes/:releaseId', fetchScene);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { stashActor, stashScene, unstashActor, unstashScene } = require('../stashes');
|
const {
|
||||||
|
stashActor,
|
||||||
|
stashScene,
|
||||||
|
stashMovie,
|
||||||
|
unstashActor,
|
||||||
|
unstashScene,
|
||||||
|
unstashMovie,
|
||||||
|
} = require('../stashes');
|
||||||
|
|
||||||
async function stashActorApi(req, res) {
|
async function stashActorApi(req, res) {
|
||||||
await stashActor(req.body.actorId, req.params.stashId, req.session.user);
|
await stashActor(req.body.actorId, req.params.stashId, req.session.user);
|
||||||
|
|
@ -14,6 +21,12 @@ async function stashSceneApi(req, res) {
|
||||||
res.status(201).send();
|
res.status(201).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function stashMovieApi(req, res) {
|
||||||
|
await stashMovie(req.body.movieId, req.params.stashId, req.session.user);
|
||||||
|
|
||||||
|
res.status(201).send();
|
||||||
|
}
|
||||||
|
|
||||||
async function unstashActorApi(req, res) {
|
async function unstashActorApi(req, res) {
|
||||||
await unstashActor(req.params.actorId, req.params.stashId, req.session.user);
|
await unstashActor(req.params.actorId, req.params.stashId, req.session.user);
|
||||||
|
|
||||||
|
|
@ -26,9 +39,17 @@ async function unstashSceneApi(req, res) {
|
||||||
res.status(204).send();
|
res.status(204).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function unstashMovieApi(req, res) {
|
||||||
|
await unstashMovie(req.params.movieId, req.params.stashId, req.session.user);
|
||||||
|
|
||||||
|
res.status(204).send();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
stashActor: stashActorApi,
|
stashActor: stashActorApi,
|
||||||
stashScene: stashSceneApi,
|
stashScene: stashSceneApi,
|
||||||
|
stashMovie: stashMovieApi,
|
||||||
unstashActor: unstashActorApi,
|
unstashActor: unstashActorApi,
|
||||||
unstashScene: unstashSceneApi,
|
unstashScene: unstashSceneApi,
|
||||||
|
unstashMovie: unstashMovieApi,
|
||||||
};
|
};
|
||||||
|
|
|
||||||