Compare commits

...

4 Commits

Author SHA1 Message Date
DebaucheryLibrarian 0a92586c53 1.186.1 2021-03-18 04:01:42 +01:00
DebaucheryLibrarian 90b3d8a4d6 Compacted warning page. 2021-03-18 04:01:35 +01:00
DebaucheryLibrarian 5a2e93e900 Added various tag photos and descriptions. 2021-03-17 05:11:17 +01:00
DebaucheryLibrarian 4e81a8a1d6 Fixed movie banner using wrong photo variable. 2021-03-17 02:12:56 +01:00
53 changed files with 203 additions and 66 deletions

View File

@ -1,22 +1,21 @@
<template>
<div class="warning-container">
<div class="warning">
<div
class="logo"
v-html="logo"
/>
<strong class="title">This website contains sexually explicit content</strong>
<span class="copy agree">By entering, you agree to the following</span>
<strong class="title">
<div
class="logo"
v-html="logo"
/>contains sexually explicit content
</strong>
<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 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">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>
<span class="preferences">You can adjust your content preferences later</span>
<div class="actions">
<a
href="https://www.google.com"
@ -40,8 +39,6 @@
<span class="button-sub">I prefer straight content</span>
</button>
</div>
<span class="preferences">You can adjust your content preferences later</span>
</div>
</div>
</template>
@ -84,21 +81,22 @@ export default {
.logo {
width: 8rem;
display: flex;
display: inline-flex;
fill: var(--primary);
margin: 1rem auto 0 auto;
margin: -.125rem .5rem 0 0;
}
.title,
.copy,
.rules {
padding: 1rem;
padding: .5rem 1rem;
}
.title {
display: block;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
margin: 0 0 1rem 0;
margin: 1rem 0 0 0;
color: var(--text-light);
}
@ -203,7 +201,7 @@ export default {
.preferences {
color: var(--lighten);
display: block;
padding: .5rem 0 2rem 0;
padding: .5rem 0 1rem 0;
text-align: center;
font-size: .9rem;
}

View File

@ -33,8 +33,8 @@
v-else-if="release.teaser && /^image\//.test(release.teaser.mime)"
:src="getPath(release.teaser, 'thumbnail', { original: true })"
:alt="release.title"
:width="photo.width"
:height="photo.height"
:width="release.teaser.width"
:height="release.teaser.height"
loading="lazy"
class="item trailer"
>
@ -68,8 +68,8 @@
<img
:src="getPath(cover, 'thumbnail')"
:style="{ 'background-image': getBgPath(cover, 'lazy') }"
:width="photo.width"
:height="photo.height"
:width="cover.width"
:height="cover.height"
class="item cover"
loading="lazy"
@load="$emit('load', $event)"

View File

@ -55,14 +55,14 @@
v-show="me && isStashed"
icon="heart7"
class="stash stashed noselect"
@click="unstashScene"
@click="unstashRelease"
/>
<Icon
v-show="me && !isStashed"
icon="heart8"
class="stash unstashed noselect"
@click="stashScene"
@click="stashRelease"
/>
</div>
@ -251,18 +251,20 @@ async function fetchRelease(scroll = true) {
}
}
async function stashScene() {
this.$store.dispatch('stashScene', {
async function stashRelease() {
this.$store.dispatch(this.$route.name === 'movie' ? 'stashMovie' : 'stashRelease', {
sceneId: this.release.id,
movieId: this.release.id,
stashId: this.$store.getters.favorites.id,
});
this.fetchRelease(false);
}
async function unstashScene() {
this.$store.dispatch('unstashScene', {
async function unstashRelease() {
this.$store.dispatch(this.$route.name === 'movie' ? 'unstashMovie' : 'unstashRelease', {
sceneId: this.release.id,
movieId: this.release.id,
stashId: this.$store.getters.favorites.id,
});
@ -321,8 +323,8 @@ export default {
mounted: fetchRelease,
methods: {
fetchRelease,
stashScene,
unstashScene,
stashRelease,
unstashRelease,
},
};
</script>

View File

@ -164,12 +164,16 @@ export default {
position: absolute;
top: 0;
left: 0;
padding: .05rem .25rem .1rem .15rem;
width: 1rem;
height: 1rem;
box-sizing: border-box;
padding: .1rem;
border-radius: 0 0 .5rem 0;
color: var(--text-light);
background: var(--primary);
font-size: .8rem;
font-weight: bold;
line-height: 1;
}
}
@ -372,7 +376,6 @@ export default {
.tile.new .poster::after {
bottom: 0;
top: auto;
padding: .1rem .25rem .05rem .15rem;
border-radius: 0 .5rem 0 0;
}
}

View File

@ -138,7 +138,11 @@ function initReleasesActions(store, router) {
// const release = await get(`/releases/${releaseId}`);
const { movie } = await graphql(`
query Movie($movieId: Int!) {
query Movie(
$movieId: Int!
$hasAuth: Boolean!
$userId: Int
) {
movie(id: $movieId) {
id
title
@ -232,10 +236,27 @@ function initReleasesActions(store, router) {
hasLogo
}
}
stashes: stashesMovies(
filter: {
stash: {
userId: {
equalTo: $userId
}
}
}
) @include(if: $hasAuth) {
stash {
id
name
slug
}
}
}
}
`, {
movieId: Number(movieId),
hasAuth: !!store.state.auth.user,
userId: store.state.auth.user?.id,
});
if (!movie) {

View File

@ -17,11 +17,21 @@ function initStashesActions(_store, _router) {
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 {
stashActor,
stashScene,
stashMovie,
unstashActor,
unstashScene,
unstashMovie,
};
}

View File

@ -1054,7 +1054,8 @@ exports.up = knex => Promise.resolve()
table.integer('user_id')
.references('id')
.inTable('users');
.inTable('users')
.onDelete('cascade');
table.string('name')
.notNullable();
@ -1074,27 +1075,48 @@ exports.up = knex => Promise.resolve()
table.integer('stash_id')
.notNullable()
.references('id')
.inTable('stashes');
.inTable('stashes')
.onDelete('cascade');
table.integer('scene_id')
.notNullable()
.references('id')
.inTable('releases');
.inTable('releases')
.onDelete('cascade');
table.unique(['stash_id', 'scene_id']);
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) => {
table.integer('stash_id')
.notNullable()
.references('id')
.inTable('stashes');
.inTable('stashes')
.onDelete('cascade');
table.integer('actor_id')
.notNullable()
.references('id')
.inTable('actors');
.inTable('actors')
.onDelete('cascade');
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_scenes ENABLE ROW LEVEL SECURITY;
ALTER TABLE stashes_movies 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());
@ -1319,6 +1342,14 @@ exports.up = knex => Promise.resolve()
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
USING (EXISTS (
SELECT *
@ -1416,6 +1447,7 @@ exports.down = (knex) => { // eslint-disable-line arrow-body-style
DROP TABLE IF EXISTS entities 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 CASCADE;

4
package-lock.json generated
View File

@ -1,11 +1,11 @@
{
"name": "traxxx",
"version": "1.186.0",
"version": "1.186.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "1.186.0",
"version": "1.186.1",
"license": "ISC",
"dependencies": {
"@casl/ability": "^5.2.2",

View File

@ -1,6 +1,6 @@
{
"name": "traxxx",
"version": "1.186.0",
"version": "1.186.1",
"description": "All the latest porn releases in one place",
"main": "src/app.js",
"scripts": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

After

Width:  |  Height:  |  Size: 3.1 MiB

View File

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 495 KiB

After

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -98,7 +98,7 @@ const tags = [
{
name: '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,
group: 'penetration',
},
@ -116,19 +116,19 @@ const tags = [
{
name: '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,
group: 'penetration',
},
{
name: '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',
slug: 'anal-fisting',
description: 'Shoving an entire hand into the asshole.',
description: 'Shoving an entire hand into your asshole.',
},
{
name: 'anal prolapse',
@ -242,13 +242,14 @@ const tags = [
name: 'blowjob',
slug: 'blowjob',
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',
},
{
name: 'blowbang',
slug: 'blowbang',
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',
},
{
@ -343,7 +344,7 @@ const tags = [
{
name: 'double anal',
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,
group: 'penetration',
},
@ -388,13 +389,14 @@ const tags = [
name: 'deepthroat',
slug: 'deepthroat',
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',
},
{
name: 'double penetration',
slug: 'dp',
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',
},
{
@ -404,7 +406,7 @@ const tags = [
{
name: 'double vaginal',
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,
group: 'penetration',
},
@ -450,6 +452,7 @@ const tags = [
name: 'facefucking',
slug: 'facefucking',
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',
},
{
@ -487,6 +490,10 @@ const tags = [
name: 'fisting DP',
slug: 'fisting-dp',
},
{
name: 'flexible',
slug: 'flexible',
},
{
name: 'MFF threesome',
slug: 'mff',
@ -953,14 +960,14 @@ const tags = [
name: 'DA triple penetration',
slug: 'da-tp',
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',
},
{
name: 'DV triple penetration',
slug: 'dv-tp',
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',
},
{

View File

@ -650,9 +650,10 @@ const tagMedia = [
['blowbang', 1, 'Nicole Black in GIO1680', 'legalporno'],
['blowjob', 1, 'Kylie Page in "Stepsis Gives Soapy Handjob In Shower"', 'spyfam'],
['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', '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', 2, 'Luna Kitsuen in "Gag Reflex"', 'evilangel'],
['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', 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', 'lara_frost_legalporno', 'Lara Frost in NRX059', 'legalporno'],
['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', 0, 'Alessandra Jane', 'private'],
@ -746,6 +748,7 @@ const tagMedia = [
['dp', 3, 'Hime Marie in AA047', 'legalporno'],
['dp', 2, 'Megan Rain in "DP Masters 4"', 'julesjordan'],
['dp', 6, 'Kira Noir', 'hardx'],
['dp', 'lara_frost_legalporno', 'Lara Frost in NRX070', 'legalporno'],
['dp', 5, 'Lana Rhoades in "Gangbang Me 3"', 'hardx'],
['dp', 'zaawaadi_roccosiffredi', 'Zaawaadi in "My Name Is Zaawaadi"', 'roccosiffredi'],
['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', 2, 'Ashly Anderson', 'hookuphotshot'],
['facial', 4, 'Kendra Heart', 'facialsforever'],
['flexible', 'lara_frost_legalporno', 'Lara Frost in NRX059', 'legalporno'],
['enhanced-boobs', 7, 'Charley Atwell', 'icandigirls'],
['enhanced-boobs', 14, 'Rikki Six', 'dreamdolls'],
['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', 22, 'Sakura Sena'],
['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', 'azul_hermosa_pornstarslikeitbig', 'Azul Hermosa in "She Likes Rough Quickies"', 'pornstarslikeitbig'],
['enhanced-boobs', 21, 'Emelie Ekström'],
@ -814,10 +819,10 @@ const tagMedia = [
['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'],
['gangbang', 5, 'Carter Cruise\'s first gangbang in "Slut Puppies 9"', 'julesjordan'],
['gangbang', 'poster', 'Kristen Scott in "Interracial Gangbang!"', 'julesjordan'],
['gangbang', 7, 'Alexa Flexy in GL376'],
['gangbang', 'kristen_scott_julesjordan', 'Kristen Scott in "Interracial Gangbang!"', 'julesjordan'],
['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', 6, 'Silvia Soprano in GIO1580', 'legalporno'],
['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.'],
['gaping', 1, 'Vina Sky in "Vina Sky Does Anal"', 'hardx'],
@ -914,8 +919,9 @@ const tagMedia = [
['titty-fucking', 4, 'Set 5532', 'tugjobs'],
['titty-fucking', 3, 'Anna Bell Peaks in "Ringing Her Bell"', 'milfvr'],
['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', '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', 0, 'Kira Noir in 1225', 'inthecrack'],
['toy-dp', 1, 'Krissy Lynn and London River in "Lesbian DP Workout"', 'lesbianx'],

View File

@ -67,7 +67,7 @@ async function signup(credentials) {
const hashedPassword = (await scrypt(credentials.password, salt, 64)).toString('hex');
const storedPassword = `${salt}/${hashedPassword}`;
const [user] = await knex('users')
const [userId] = await knex('users')
.insert({
username: credentials.username,
email: credentials.email,
@ -76,13 +76,13 @@ async function signup(credentials) {
.returning('id');
await knex('stashes').insert({
user_id: user.id,
user_id: userId,
name: 'Favorites',
slug: 'favorites',
public: false,
});
return fetchUser(user.id);
return fetchUser(userId);
}
module.exports = {

View File

@ -4,6 +4,10 @@ const knex = require('./knex');
const { HttpError } = require('./errors');
function curateStash(stash) {
if (!stash) {
return null;
}
const curatedStash = {
id: stash.id,
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) {
await knex
.from('stashes_actors AS deletable')
.where('deletable.actor_id', actorId)
.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')
.where('stashes_actors.stash_id', knex.raw('deletable.stash_id'))
.where('stashes.user_id', sessionUser.id))
@ -69,17 +83,31 @@ async function unstashScene(sceneId, stashId, sessionUser) {
.from('stashes_scenes AS deletable')
.where('deletable.scene_id', sceneId)
.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')
.where('stashes_scenes.stash_id', knex.raw('deletable.stash_id'))
.where('stashes.user_id', sessionUser.id))
.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 = {
curateStash,
stashActor,
stashScene,
stashMovie,
unstashScene,
unstashActor,
unstashMovie,
};

View File

@ -18,7 +18,7 @@ function curateUser(user) {
identityVerified: user.identity_verified,
ability,
createdAt: user.created_at,
stashes: user.stashes?.map(stash => curateStash(stash)) || [],
stashes: user.stashes?.filter(Boolean).map(stash => curateStash(stash)) || [],
};
return curatedUser;
@ -26,7 +26,7 @@ function curateUser(user) {
async function fetchUser(userId, raw) {
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) => {
if (typeof userId === 'number') {
builder.where('users.id', userId);

View File

@ -1,10 +1,15 @@
'use strict';
const argv = require('../argv');
const logger = require('../logger')(__filename);
function errorHandler(error, req, res, _next) {
logger.warn(`Failed to fulfill request to ${req.path}: ${error.message}`);
if (argv.debug) {
logger.error(error);
}
if (error.httpCode) {
res.status(error.httpCode).send(error.message);

View File

@ -46,8 +46,10 @@ const {
const {
stashActor,
stashScene,
stashMovie,
unstashActor,
unstashScene,
unstashMovie,
} = require('./stashes');
async function initServer() {
@ -83,9 +85,11 @@ async function initServer() {
router.post('/api/stashes/:stashId/actors', stashActor);
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/scenes/:sceneId', unstashScene);
router.delete('/api/stashes/:stashId/movies/:movieId', unstashMovie);
router.get('/api/scenes', fetchScenes);
router.get('/api/scenes/:releaseId', fetchScene);

View File

@ -1,6 +1,13 @@
'use strict';
const { stashActor, stashScene, unstashActor, unstashScene } = require('../stashes');
const {
stashActor,
stashScene,
stashMovie,
unstashActor,
unstashScene,
unstashMovie,
} = require('../stashes');
async function stashActorApi(req, res) {
await stashActor(req.body.actorId, req.params.stashId, req.session.user);
@ -14,6 +21,12 @@ async function stashSceneApi(req, res) {
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) {
await unstashActor(req.params.actorId, req.params.stashId, req.session.user);
@ -26,9 +39,17 @@ async function unstashSceneApi(req, res) {
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 = {
stashActor: stashActorApi,
stashScene: stashSceneApi,
stashMovie: stashMovieApi,
unstashActor: unstashActorApi,
unstashScene: unstashSceneApi,
unstashMovie: unstashMovieApi,
};