Added various tag photos and descriptions.
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
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',
|
||||
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',
|
||||
},
|
||||
|
@ -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',
|
||||
},
|
||||
{
|
||||
|
@ -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, their cock hitting the back of your mouth may 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',
|
||||
},
|
||||
{
|
||||
|
@ -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',
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|