Added stash GraphQL mutations. Added movies to GraphQL queries. Moved key management to profile page, only for approved users.
This commit is contained in:
parent
09bba4fe1e
commit
1025285796
|
@ -96,8 +96,6 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(props.chapters);
|
|
||||||
|
|
||||||
const lastChapter = props.chapters.at(-1);
|
const lastChapter = props.chapters.at(-1);
|
||||||
const duration = lastChapter.time + lastChapter.duration;
|
const duration = lastChapter.time + lastChapter.duration;
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<section class="profile-section">
|
||||||
<div class="manager">
|
<div class="section-header">
|
||||||
<div class="keys-header">
|
<h3 class="heading">API Keys</h3>
|
||||||
<h2 class="heading">API keys</h2>
|
|
||||||
|
|
||||||
<div class="keys-actions">
|
<div class="keys-actions">
|
||||||
<Icon
|
<Icon
|
||||||
v-tooltip="'Flush all keys'"
|
v-tooltip="'Flush all keys'"
|
||||||
icon="stack-cancel"
|
icon="stack-cancel"
|
||||||
|
class="keys-flush"
|
||||||
@click="flushKeys"
|
@click="flushKeys"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="button"
|
class="button"
|
||||||
@click="createKey"
|
@click="createKey"
|
||||||
>New key</button>
|
>
|
||||||
|
<Icon icon="key" />
|
||||||
|
<span class="button-label">New key</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -48,6 +51,7 @@
|
||||||
<span class="key-actions">
|
<span class="key-actions">
|
||||||
<Icon
|
<Icon
|
||||||
icon="bin"
|
icon="bin"
|
||||||
|
class="key-remove"
|
||||||
@click="removeKey(key)"
|
@click="removeKey(key)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -90,8 +94,7 @@
|
||||||
API-Key: YourSecurelyStoredApiKey12345678
|
API-Key: YourSecurelyStoredApiKey12345678
|
||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
@ -108,7 +111,7 @@ const keys = ref(pageContext.pageProps.keys);
|
||||||
const newKey = ref(null);
|
const newKey = ref(null);
|
||||||
|
|
||||||
async function createKey() {
|
async function createKey() {
|
||||||
const key = await post('/keys', null, {
|
const key = await post('/me/keys', null, {
|
||||||
appendErrorMessage: true,
|
appendErrorMessage: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -158,7 +161,6 @@ function copyKey(event) {
|
||||||
width: 1200px;
|
width: 1200px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.keys-header {
|
.keys-header {
|
||||||
|
@ -170,10 +172,9 @@ function copyKey(event) {
|
||||||
|
|
||||||
.keys-actions {
|
.keys-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1rem;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.icon {
|
> .icon {
|
||||||
padding: .5rem 1rem;
|
padding: .5rem 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,12 +186,18 @@ function copyKey(event) {
|
||||||
fill: var(--glass);
|
fill: var(--glass);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
fill: var(--error);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.keys-flush,
|
||||||
|
.key-remove {
|
||||||
|
&:hover {
|
||||||
|
fill: var(--error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.keys {
|
.keys {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(25rem, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(25rem, 1fr));
|
||||||
|
@ -201,12 +208,14 @@ function copyKey(event) {
|
||||||
.key {
|
.key {
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
box-shadow: 0 0 3px var(--shadow-weak-30);
|
box-shadow: 0 0 3px var(--shadow-weak-30);
|
||||||
|
border-radius: .25rem;
|
||||||
font-size: .9rem;
|
font-size: .9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-row {
|
.key-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,16 +228,17 @@ function copyKey(event) {
|
||||||
.icon {
|
.icon {
|
||||||
width: .9rem;
|
width: .9rem;
|
||||||
height: .9rem;
|
height: .9rem;
|
||||||
fill: var(--glass-strong-10);
|
margin-right: .25rem;
|
||||||
|
fill: var(--glass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-header .key-value {
|
.key-header .key-value {
|
||||||
padding: .5rem .5rem .25rem .5rem;
|
padding: .5rem .75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-details .key-value {
|
.key-details .key-value {
|
||||||
padding: .25rem .5rem .5rem .5rem;
|
padding: .25rem .75rem .75rem .75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-identifier {
|
.key-identifier {
|
||||||
|
@ -238,7 +248,9 @@ function copyKey(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-actions .icon {
|
.key-actions .icon {
|
||||||
padding: 0 .5rem .5rem .5rem;
|
height: 1rem;
|
||||||
|
padding: .75rem .75rem .5rem .75rem;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newkey {
|
.newkey {
|
|
@ -1,14 +0,0 @@
|
||||||
import { fetchUserKeys } from '#/src/auth.js';
|
|
||||||
|
|
||||||
export async function onBeforeRender(pageContext) {
|
|
||||||
const keys = await fetchUserKeys(pageContext.user);
|
|
||||||
|
|
||||||
return {
|
|
||||||
pageContext: {
|
|
||||||
title: 'API keys',
|
|
||||||
pageProps: {
|
|
||||||
keys,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -49,6 +49,13 @@
|
||||||
class="domain nolink"
|
class="domain nolink"
|
||||||
:class="{ active: section === 'revisions' && domain === 'actors' }"
|
:class="{ active: section === 'revisions' && domain === 'actors' }"
|
||||||
>Actor Revisions</a>
|
>Actor Revisions</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-if="profile.isIdentityVerified"
|
||||||
|
:href="`/user/${profile.username}/api`"
|
||||||
|
class="domain nolink"
|
||||||
|
:class="{ active: section === 'api' }"
|
||||||
|
>API Keys</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<Stashes v-if="section === 'stashes'" />
|
<Stashes v-if="section === 'stashes'" />
|
||||||
|
@ -58,6 +65,10 @@
|
||||||
v-if="section === 'templates' && profile.id === user?.id"
|
v-if="section === 'templates' && profile.id === user?.id"
|
||||||
:release="mockupRelease"
|
:release="mockupRelease"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ApiKeys
|
||||||
|
v-if="section === 'api'"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -78,6 +89,7 @@ import Stashes from '#/components/stashes/stashes.vue';
|
||||||
import Alerts from '#/components/alerts/alerts.vue';
|
import Alerts from '#/components/alerts/alerts.vue';
|
||||||
import Summaries from '#/components/scenes/summaries.vue';
|
import Summaries from '#/components/scenes/summaries.vue';
|
||||||
import Revisions from '#/components/edit/revisions.vue';
|
import Revisions from '#/components/edit/revisions.vue';
|
||||||
|
import ApiKeys from '#/components/user/api-keys.vue';
|
||||||
|
|
||||||
const pageContext = inject('pageContext');
|
const pageContext = inject('pageContext');
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */
|
import { render } from 'vike/abort'; /* eslint-disable-line import/extensions */
|
||||||
|
|
||||||
import { fetchUser } from '#/src/users.js';
|
import { fetchUser } from '#/src/users.js';
|
||||||
|
import { fetchUserKeys } from '#/src/auth.js';
|
||||||
import { fetchUserStashes } from '#/src/stashes.js';
|
import { fetchUserStashes } from '#/src/stashes.js';
|
||||||
import { fetchAlerts } from '#/src/alerts.js';
|
import { fetchAlerts } from '#/src/alerts.js';
|
||||||
import { fetchSceneRevisions } from '#/src/scenes.js';
|
import { fetchSceneRevisions } from '#/src/scenes.js';
|
||||||
|
@ -29,12 +30,15 @@ async function fetchRevisions(pageContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function onBeforeRender(pageContext) {
|
export async function onBeforeRender(pageContext) {
|
||||||
const [profile, alerts, userRevisions] = await Promise.all([
|
const [profile, alerts, userRevisions, keys] = await Promise.all([
|
||||||
fetchUser(pageContext.routeParams.username, {}, pageContext.user),
|
fetchUser(pageContext.routeParams.username, {}, pageContext.user),
|
||||||
pageContext.routeParams.section === 'alerts' && pageContext.routeParams.username === pageContext.user?.username
|
pageContext.routeParams.section === 'alerts' && pageContext.routeParams.username === pageContext.user?.username
|
||||||
? fetchAlerts(pageContext.user)
|
? fetchAlerts(pageContext.user)
|
||||||
: [],
|
: [],
|
||||||
fetchRevisions(pageContext),
|
fetchRevisions(pageContext),
|
||||||
|
pageContext.routeParams.section === 'api'
|
||||||
|
? fetchUserKeys(pageContext.user)
|
||||||
|
: [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
|
@ -49,8 +53,6 @@ export async function onBeforeRender(pageContext) {
|
||||||
avatars,
|
avatars,
|
||||||
} = userRevisions;
|
} = userRevisions;
|
||||||
|
|
||||||
console.log(userRevisions);
|
|
||||||
|
|
||||||
const stashes = await fetchUserStashes(profile.id, pageContext.user);
|
const stashes = await fetchUserStashes(profile.id, pageContext.user);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -61,6 +63,7 @@ export async function onBeforeRender(pageContext) {
|
||||||
stashes,
|
stashes,
|
||||||
alerts,
|
alerts,
|
||||||
revisions,
|
revisions,
|
||||||
|
keys,
|
||||||
actors,
|
actors,
|
||||||
tags,
|
tags,
|
||||||
movies,
|
movies,
|
||||||
|
|
197
src/stashes.js
197
src/stashes.js
|
@ -40,6 +40,21 @@ export function curateStash(stash, assets = {}) {
|
||||||
return curatedStash;
|
return curatedStash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function curateStashed(stashed) {
|
||||||
|
if (!stashed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const curatedStashed = {
|
||||||
|
id: stashed.id,
|
||||||
|
stashId: stashed.stash_id,
|
||||||
|
actorId: stashed.actor_id,
|
||||||
|
createdAt: stashed.created_at,
|
||||||
|
};
|
||||||
|
|
||||||
|
return curatedStashed;
|
||||||
|
}
|
||||||
|
|
||||||
function curateStashEntry(stash, user) {
|
function curateStashEntry(stash, user) {
|
||||||
const curatedStashEntry = {
|
const curatedStashEntry = {
|
||||||
user_id: user?.id || undefined,
|
user_id: user?.id || undefined,
|
||||||
|
@ -57,9 +72,17 @@ function verifyStashAccess(stash, sessionUser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchStashById(stashId, sessionUser) {
|
export async function fetchStashById(stashIdOrSlug, sessionUser) {
|
||||||
const stash = await knex('stashes')
|
const stash = await knex('stashes')
|
||||||
.where('id', stashId)
|
.where((builder) => {
|
||||||
|
if (typeof stashIdOrSlug === 'number') {
|
||||||
|
builder.where('id', stashIdOrSlug);
|
||||||
|
} else {
|
||||||
|
builder
|
||||||
|
.where('slug', stashIdOrSlug)
|
||||||
|
.where('user_id', sessionUser.id);
|
||||||
|
}
|
||||||
|
})
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
verifyStashAccess(stash, sessionUser);
|
verifyStashAccess(stash, sessionUser);
|
||||||
|
@ -67,8 +90,16 @@ export async function fetchStashById(stashId, sessionUser) {
|
||||||
return curateStash(stash);
|
return curateStash(stash);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchStashByUsernameAndSlug(username, stashSlug, sessionUser) {
|
export async function fetchStashByUsernameAndSlug(usernameOrId, stashSlug, sessionUser) {
|
||||||
const user = await knex('users').where('username', username).first();
|
const user = await knex('users')
|
||||||
|
.where((builder) => {
|
||||||
|
if (typeof usernameOrId === 'number') {
|
||||||
|
builder.where('id', usernameOrId);
|
||||||
|
} else {
|
||||||
|
builder.where('username', usernameOrId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.first();
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new HttpError('This user does not exist.', 404);
|
throw new HttpError('This user does not exist.', 404);
|
||||||
|
@ -86,10 +117,22 @@ export async function fetchStashByUsernameAndSlug(username, stashSlug, sessionUs
|
||||||
return curateStash(stash, { user });
|
return curateStash(stash, { user });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchUserStashes(userId, reqUser) {
|
export async function fetchUserStashes(usernameOrId, reqUser) {
|
||||||
|
const userId = typeof usernameOrId === 'number'
|
||||||
|
? usernameOrId
|
||||||
|
: await knex('users')
|
||||||
|
.where('username', usernameOrId)
|
||||||
|
.first()
|
||||||
|
.then((user) => user?.id);
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
throw new HttpError(`Could not find user '${usernameOrId}'`);
|
||||||
|
}
|
||||||
|
|
||||||
const stashes = await knex('stashes')
|
const stashes = await knex('stashes')
|
||||||
.select('stashes.*', 'stashes_meta.*')
|
.select('stashes.*', 'stashes_meta.*')
|
||||||
.leftJoin('stashes_meta', 'stashes_meta.stash_id', 'stashes.id')
|
.leftJoin('stashes_meta', 'stashes_meta.stash_id', 'stashes.id')
|
||||||
|
.leftJoin('users', 'users.id', 'stashes.user_id')
|
||||||
.where('user_id', userId)
|
.where('user_id', userId)
|
||||||
.modify((builder) => {
|
.modify((builder) => {
|
||||||
if (userId !== reqUser?.id) {
|
if (userId !== reqUser?.id) {
|
||||||
|
@ -172,7 +215,7 @@ export async function createStash(newStash, sessionUser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateStash(stashId, updatedStash, sessionUser) {
|
export async function updateStash(stashIdOrSlug, updatedStash, sessionUser) {
|
||||||
if (!sessionUser) {
|
if (!sessionUser) {
|
||||||
throw new HttpError('You are not authenthicated', 401);
|
throw new HttpError('You are not authenthicated', 401);
|
||||||
}
|
}
|
||||||
|
@ -182,11 +225,15 @@ export async function updateStash(stashId, updatedStash, sessionUser) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stash = await knex('stashes')
|
const [stash] = await knex('stashes')
|
||||||
.where({
|
.where((builder) => {
|
||||||
id: stashId,
|
if (typeof stashIdOrSlug === 'number') {
|
||||||
user_id: sessionUser.id,
|
builder.where('id', stashIdOrSlug);
|
||||||
|
} else {
|
||||||
|
builder.where('slug', stashIdOrSlug);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
.where('user_id', sessionUser.id)
|
||||||
.update(curateStashEntry(updatedStash))
|
.update(curateStashEntry(updatedStash))
|
||||||
.returning('*');
|
.returning('*');
|
||||||
|
|
||||||
|
@ -209,28 +256,42 @@ export async function removeStash(stashId, sessionUser) {
|
||||||
throw new HttpError('You are not authenthicated', 401);
|
throw new HttpError('You are not authenthicated', 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
const removed = await knex('stashes')
|
const stash = await fetchStashById(stashId, sessionUser);
|
||||||
|
|
||||||
|
if (!stash) {
|
||||||
|
throw new HttpError(`Could not find stash '${stashId}'`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [removed] = await knex('stashes')
|
||||||
.where({
|
.where({
|
||||||
id: stashId,
|
id: stash.id,
|
||||||
user_id: sessionUser.id,
|
user_id: sessionUser.id,
|
||||||
primary: false,
|
primary: false,
|
||||||
})
|
})
|
||||||
.delete();
|
.delete()
|
||||||
|
.returning('*');
|
||||||
|
|
||||||
if (removed === 0) {
|
if (removed === 0) {
|
||||||
throw new HttpError('Unable to remove this stash', 400);
|
throw new HttpError('Unable to remove this stash', 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return curateStash(stash);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stashActor(actorId, stashId, sessionUser) {
|
export async function stashActor(actorId, stashId, sessionUser) {
|
||||||
const stash = await fetchStashById(stashId, sessionUser);
|
const stash = await fetchStashById(stashId, sessionUser);
|
||||||
|
|
||||||
|
if (!stash) {
|
||||||
|
throw new HttpError(`Could not find stash '${stashId}'`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const [stashed] = await knex('stashes_actors')
|
const [stashed] = await knex('stashes_actors')
|
||||||
.insert({
|
.insert({
|
||||||
stash_id: stash.id,
|
stash_id: stash.id,
|
||||||
actor_id: actorId,
|
actor_id: actorId,
|
||||||
})
|
})
|
||||||
.returning(['id', 'created_at']);
|
.returning('*');
|
||||||
|
|
||||||
await indexApi.replace({
|
await indexApi.replace({
|
||||||
index: 'actors_stashed',
|
index: 'actors_stashed',
|
||||||
|
@ -238,7 +299,7 @@ export async function stashActor(actorId, stashId, sessionUser) {
|
||||||
doc: {
|
doc: {
|
||||||
actor_id: actorId,
|
actor_id: actorId,
|
||||||
user_id: sessionUser.id,
|
user_id: sessionUser.id,
|
||||||
stash_id: stashId,
|
stash_id: stash.id,
|
||||||
created_at: Math.round(stashed.created_at.getTime() / 1000),
|
created_at: Math.round(stashed.created_at.getTime() / 1000),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -247,19 +308,34 @@ export async function stashActor(actorId, stashId, sessionUser) {
|
||||||
|
|
||||||
refreshView('actors');
|
refreshView('actors');
|
||||||
|
|
||||||
return fetchDomainStashes('actor', actorId, sessionUser);
|
// return fetchDomainStashes('actor', actorId, sessionUser);
|
||||||
|
return curateStashed(stashed);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.routine === '_bt_check_unique') {
|
||||||
|
throw new HttpError(`Actor ${actorId} is already stashed in '${stash.name}'`, 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function unstashActor(actorId, stashId, sessionUser) {
|
export async function unstashActor(actorId, stashId, sessionUser) {
|
||||||
await knex
|
const stash = await fetchStashById(stashId, sessionUser);
|
||||||
|
|
||||||
|
if (!stash) {
|
||||||
|
throw new HttpError(`Could not find stash '${stashId}'`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [unstashed] = 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', stash.id)
|
||||||
.whereExists(knex('stashes_actors') // verify user owns this stash, complimentary to row-level security
|
.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))
|
||||||
.delete();
|
.delete()
|
||||||
|
.returning('*');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await indexApi.callDelete({
|
await indexApi.callDelete({
|
||||||
|
@ -268,7 +344,7 @@ export async function unstashActor(actorId, stashId, sessionUser) {
|
||||||
bool: {
|
bool: {
|
||||||
must: [
|
must: [
|
||||||
{ equals: { actor_id: actorId } },
|
{ equals: { actor_id: actorId } },
|
||||||
{ equals: { stash_id: stashId } },
|
{ equals: { stash_id: stash.id } },
|
||||||
{ equals: { user_id: sessionUser.id } },
|
{ equals: { user_id: sessionUser.id } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -278,22 +354,28 @@ export async function unstashActor(actorId, stashId, sessionUser) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.verbose(`${sessionUser.username} (${sessionUser.id}) unstashed actor ${actorId} from stash ${stashId}`);
|
logger.verbose(`${sessionUser.username} (${sessionUser.id}) unstashed actor ${actorId} from stash ${stashId} (${stash.name})`);
|
||||||
|
|
||||||
refreshView('actors');
|
refreshView('actors');
|
||||||
|
|
||||||
return fetchDomainStashes('actor', actorId, sessionUser);
|
// return fetchDomainStashes('actor', actorId, sessionUser);
|
||||||
|
return curateStashed(unstashed);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stashScene(sceneId, stashId, sessionUser) {
|
export async function stashScene(sceneId, stashId, sessionUser) {
|
||||||
const stash = await fetchStashById(stashId, sessionUser);
|
const stash = await fetchStashById(stashId, sessionUser);
|
||||||
|
|
||||||
|
if (!stash) {
|
||||||
|
throw new HttpError(`Could not find stash '${stashId}'`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const [stashed] = await knex('stashes_scenes')
|
const [stashed] = await knex('stashes_scenes')
|
||||||
.insert({
|
.insert({
|
||||||
stash_id: stash.id,
|
stash_id: stash.id,
|
||||||
scene_id: sceneId,
|
scene_id: sceneId,
|
||||||
})
|
})
|
||||||
.returning(['id', 'created_at']);
|
.returning('*');
|
||||||
|
|
||||||
await indexApi.replace({
|
await indexApi.replace({
|
||||||
index: 'scenes_stashed',
|
index: 'scenes_stashed',
|
||||||
|
@ -302,7 +384,7 @@ export async function stashScene(sceneId, stashId, sessionUser) {
|
||||||
// ...doc.replace.doc,
|
// ...doc.replace.doc,
|
||||||
scene_id: sceneId,
|
scene_id: sceneId,
|
||||||
user_id: sessionUser.id,
|
user_id: sessionUser.id,
|
||||||
stash_id: stashId,
|
stash_id: stash.id,
|
||||||
created_at: Math.round(stashed.created_at.getTime() / 1000),
|
created_at: Math.round(stashed.created_at.getTime() / 1000),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -311,19 +393,34 @@ export async function stashScene(sceneId, stashId, sessionUser) {
|
||||||
|
|
||||||
refreshView('scenes');
|
refreshView('scenes');
|
||||||
|
|
||||||
return fetchDomainStashes('scene', sceneId, sessionUser);
|
// return fetchDomainStashes('scene', sceneId, sessionUser);
|
||||||
|
return curateStashed(stashed);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.routine === '_bt_check_unique') {
|
||||||
|
throw new HttpError(`Scene ${sceneId} is already stashed in '${stash.name}'`, 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function unstashScene(sceneId, stashId, sessionUser) {
|
export async function unstashScene(sceneId, stashId, sessionUser) {
|
||||||
await knex
|
const stash = await fetchStashById(stashId, sessionUser);
|
||||||
|
|
||||||
|
if (!stash) {
|
||||||
|
throw new HttpError(`Could not find stash '${stashId}'`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [unstashed] = await knex
|
||||||
.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', stash.id)
|
||||||
.whereExists(knex('stashes_scenes') // verify user owns this stash, complimentary to row-level security
|
.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()
|
||||||
|
.returning('*');
|
||||||
|
|
||||||
await indexApi.callDelete({
|
await indexApi.callDelete({
|
||||||
index: 'scenes_stashed',
|
index: 'scenes_stashed',
|
||||||
|
@ -331,29 +428,35 @@ export async function unstashScene(sceneId, stashId, sessionUser) {
|
||||||
bool: {
|
bool: {
|
||||||
must: [
|
must: [
|
||||||
{ equals: { scene_id: sceneId } },
|
{ equals: { scene_id: sceneId } },
|
||||||
{ equals: { stash_id: stashId } },
|
{ equals: { stash_id: stash.id } },
|
||||||
{ equals: { user_id: sessionUser.id } },
|
{ equals: { user_id: sessionUser.id } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.verbose(`${sessionUser.username} (${sessionUser.id}) unstashed scene ${sceneId} from stash ${stashId}`);
|
logger.verbose(`${sessionUser.username} (${sessionUser.id}) unstashed scene ${sceneId} from stash ${stash.id} (${stash.name})`);
|
||||||
|
|
||||||
refreshView('scenes');
|
refreshView('scenes');
|
||||||
|
|
||||||
return fetchDomainStashes('scene', sceneId, sessionUser);
|
// return fetchDomainStashes('scene', sceneId, sessionUser);
|
||||||
|
return curateStashed(unstashed);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stashMovie(movieId, stashId, sessionUser) {
|
export async function stashMovie(movieId, stashId, sessionUser) {
|
||||||
const stash = await fetchStashById(stashId, sessionUser);
|
const stash = await fetchStashById(stashId, sessionUser);
|
||||||
|
|
||||||
|
if (!stash) {
|
||||||
|
throw new HttpError(`Could not find stash '${stashId}'`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const [stashed] = await knex('stashes_movies')
|
const [stashed] = await knex('stashes_movies')
|
||||||
.insert({
|
.insert({
|
||||||
stash_id: stash.id,
|
stash_id: stash.id,
|
||||||
movie_id: movieId,
|
movie_id: movieId,
|
||||||
})
|
})
|
||||||
.returning(['id', 'created_at']);
|
.returning('*');
|
||||||
|
|
||||||
await indexApi.replace({
|
await indexApi.replace({
|
||||||
index: 'movies_stashed',
|
index: 'movies_stashed',
|
||||||
|
@ -361,7 +464,7 @@ export async function stashMovie(movieId, stashId, sessionUser) {
|
||||||
doc: {
|
doc: {
|
||||||
movie_id: movieId,
|
movie_id: movieId,
|
||||||
user_id: sessionUser.id,
|
user_id: sessionUser.id,
|
||||||
stash_id: stashId,
|
stash_id: stash.id,
|
||||||
created_at: Math.round(stashed.created_at.getTime() / 1000),
|
created_at: Math.round(stashed.created_at.getTime() / 1000),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -370,18 +473,33 @@ export async function stashMovie(movieId, stashId, sessionUser) {
|
||||||
|
|
||||||
refreshView('movies');
|
refreshView('movies');
|
||||||
|
|
||||||
return fetchDomainStashes('movie', movieId, sessionUser);
|
// return fetchDomainStashes('movie', movieId, sessionUser);
|
||||||
|
return curateStashed(stashed);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.routine === '_bt_check_unique') {
|
||||||
|
throw new HttpError(`Movie ${movieId} is already stashed in '${stash.name}'`, 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function unstashMovie(movieId, stashId, sessionUser) {
|
export async function unstashMovie(movieId, stashId, sessionUser) {
|
||||||
await knex
|
const stash = await fetchStashById(stashId, sessionUser);
|
||||||
|
|
||||||
|
if (!stash) {
|
||||||
|
throw new HttpError(`Could not find stash '${stashId}'`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [unstashed] = await knex
|
||||||
.from('stashes_movies AS deletable')
|
.from('stashes_movies AS deletable')
|
||||||
.where('deletable.movie_id', movieId)
|
.where('deletable.movie_id', movieId)
|
||||||
.where('deletable.stash_id', stashId)
|
.where('deletable.stash_id', stash.id)
|
||||||
.whereExists(knex('stashes_movies') // verify user owns this stash, complimentary to row-level security
|
.whereExists(knex('stashes_movies') // verify user owns this stash, complimentary to row-level security
|
||||||
.leftJoin('stashes', 'stashes.id', 'stashes_movies.stash_id')
|
.leftJoin('stashes', 'stashes.id', 'stashes_movies.stash_id')
|
||||||
.where('stashes_movies.stash_id', knex.raw('deletable.stash_id'))
|
.where('stashes_movies.stash_id', knex.raw('deletable.stash_id'))
|
||||||
.where('stashes.user_id', sessionUser.id))
|
.where('stashes.user_id', sessionUser.id))
|
||||||
|
.returning('*')
|
||||||
.delete();
|
.delete();
|
||||||
|
|
||||||
await indexApi.callDelete({
|
await indexApi.callDelete({
|
||||||
|
@ -390,18 +508,19 @@ export async function unstashMovie(movieId, stashId, sessionUser) {
|
||||||
bool: {
|
bool: {
|
||||||
must: [
|
must: [
|
||||||
{ equals: { movie_id: movieId } },
|
{ equals: { movie_id: movieId } },
|
||||||
{ equals: { stash_id: stashId } },
|
{ equals: { stash_id: stash.id } },
|
||||||
{ equals: { user_id: sessionUser.id } },
|
{ equals: { user_id: sessionUser.id } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.verbose(`${sessionUser.username} (${sessionUser.id}) unstashed movie ${movieId} from stash ${stashId}`);
|
logger.verbose(`${sessionUser.username} (${sessionUser.id}) unstashed movie ${movieId} from stash ${stash.id} (${stash.name})`);
|
||||||
|
|
||||||
refreshView('movies');
|
refreshView('movies');
|
||||||
|
|
||||||
return fetchDomainStashes('movie', movieId, sessionUser);
|
// return fetchDomainStashes('movie', movieId, sessionUser);
|
||||||
|
return curateStashed(unstashed);
|
||||||
}
|
}
|
||||||
|
|
||||||
CronJob.from({
|
CronJob.from({
|
||||||
|
|
|
@ -25,8 +25,8 @@ export function curateUser(user, _assets = {}) {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
emailVerified: user.email_verified,
|
isEmailVerified: user.email_verified,
|
||||||
identityVerified: user.identity_verified,
|
isIdentityVerified: user.identity_verified,
|
||||||
avatar: `/media/avatars/${user.id}_${user.username}.png`,
|
avatar: `/media/avatars/${user.id}_${user.username}.png`,
|
||||||
role: user.role,
|
role: user.role,
|
||||||
createdAt: user.created_at,
|
createdAt: user.created_at,
|
||||||
|
|
|
@ -8,6 +8,8 @@ import {
|
||||||
reviewActorRevision,
|
reviewActorRevision,
|
||||||
} from '../actors.js';
|
} from '../actors.js';
|
||||||
|
|
||||||
|
import { fetchStashByUsernameAndSlug } from '../stashes.js';
|
||||||
|
|
||||||
export function curateActorsQuery(query) {
|
export function curateActorsQuery(query) {
|
||||||
return {
|
return {
|
||||||
query: query.q,
|
query: query.q,
|
||||||
|
@ -49,6 +51,7 @@ export const actorsSchema = `
|
||||||
extend type Query {
|
extend type Query {
|
||||||
actors(
|
actors(
|
||||||
query: String
|
query: String
|
||||||
|
stash: String
|
||||||
limit: Int! = 30
|
limit: Int! = 30
|
||||||
page: Int! = 1
|
page: Int! = 1
|
||||||
order: [String!]
|
order: [String!]
|
||||||
|
@ -115,14 +118,37 @@ function curateGraphqlActor(actor) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchActorsGraphql(query, _req) {
|
function getOrder(query) {
|
||||||
|
if (query.order) {
|
||||||
|
return query.order;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.query) {
|
||||||
|
return ['results', 'desc'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.stash) {
|
||||||
|
return ['stashed', 'desc'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['likes', 'desc'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchActorsGraphql(query, req) {
|
||||||
|
const stash = query.stash && req.user
|
||||||
|
? await fetchStashByUsernameAndSlug(req.user.id, query.stash, req.user)
|
||||||
|
: null;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
actors,
|
actors,
|
||||||
total,
|
total,
|
||||||
} = await fetchActors(query, {
|
} = await fetchActors({
|
||||||
|
query: query.query,
|
||||||
|
stashId: stash?.id,
|
||||||
|
}, {
|
||||||
limit: query.limit,
|
limit: query.limit,
|
||||||
page: query.page,
|
page: query.page,
|
||||||
order: query.order,
|
order: getOrder(query),
|
||||||
aggregateCountries: false,
|
aggregateCountries: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,12 @@ import {
|
||||||
fetchScenesByIdGraphql,
|
fetchScenesByIdGraphql,
|
||||||
} from './scenes.js';
|
} from './scenes.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
moviesSchema,
|
||||||
|
fetchMoviesGraphql,
|
||||||
|
fetchMoviesByIdGraphql,
|
||||||
|
} from './movies.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
entitiesSchema,
|
entitiesSchema,
|
||||||
fetchEntitiesGraphql,
|
fetchEntitiesGraphql,
|
||||||
|
@ -25,20 +31,39 @@ import {
|
||||||
fetchActorsByIdGraphql,
|
fetchActorsByIdGraphql,
|
||||||
} from './actors.js';
|
} from './actors.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
stashesSchema,
|
||||||
|
fetchUserStashesGraphql,
|
||||||
|
fetchStashGraphql,
|
||||||
|
createStashGraphql,
|
||||||
|
updateStashGraphql,
|
||||||
|
removeStashGraphql,
|
||||||
|
stashSceneGraphql,
|
||||||
|
unstashSceneGraphql,
|
||||||
|
stashActorGraphql,
|
||||||
|
unstashActorGraphql,
|
||||||
|
stashMovieGraphql,
|
||||||
|
unstashMovieGraphql,
|
||||||
|
} from './stashes.js';
|
||||||
|
|
||||||
import { verifyKey } from '../auth.js';
|
import { verifyKey } from '../auth.js';
|
||||||
|
|
||||||
const schema = buildSchema(`
|
const schema = buildSchema(`
|
||||||
type Query {
|
type Query {
|
||||||
movies(
|
_: Boolean
|
||||||
limit: Int = 30
|
}
|
||||||
): ReleasesResult
|
|
||||||
|
type Mutation {
|
||||||
|
_: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
scalar Date
|
scalar Date
|
||||||
|
|
||||||
${scenesSchema}
|
${scenesSchema}
|
||||||
|
${moviesSchema}
|
||||||
${actorsSchema}
|
${actorsSchema}
|
||||||
${entitiesSchema}
|
${entitiesSchema}
|
||||||
|
${stashesSchema}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const DateTimeScalar = new GraphQLScalarType({
|
const DateTimeScalar = new GraphQLScalarType({
|
||||||
|
@ -71,6 +96,10 @@ export async function graphqlApi(req, res) {
|
||||||
|
|
||||||
await verifyKey(req.headers['api-user'], req.headers['api-key'], req);
|
await verifyKey(req.headers['api-user'], req.headers['api-key'], req);
|
||||||
|
|
||||||
|
req.user = { // eslint-disable-line no-param-reassign
|
||||||
|
id: Number(req.headers['api-user']),
|
||||||
|
};
|
||||||
|
|
||||||
const data = await graphql({
|
const data = await graphql({
|
||||||
schema,
|
schema,
|
||||||
source: req.body.query,
|
source: req.body.query,
|
||||||
|
@ -80,9 +109,13 @@ export async function graphqlApi(req, res) {
|
||||||
DateScalar,
|
DateScalar,
|
||||||
},
|
},
|
||||||
rootValue: {
|
rootValue: {
|
||||||
|
// queries
|
||||||
scenes: async (query) => fetchScenesGraphql(query, req),
|
scenes: async (query) => fetchScenesGraphql(query, req),
|
||||||
scene: async (query) => fetchScenesByIdGraphql(query, req),
|
scene: async (query) => fetchScenesByIdGraphql(query, req),
|
||||||
scenesById: async (query) => fetchScenesByIdGraphql(query, req),
|
scenesById: async (query) => fetchScenesByIdGraphql(query, req),
|
||||||
|
movies: async (query) => fetchMoviesGraphql(query, req),
|
||||||
|
movie: async (query) => fetchMoviesByIdGraphql(query, req),
|
||||||
|
moviesById: async (query) => fetchMoviesByIdGraphql(query, req),
|
||||||
actors: async (query) => fetchActorsGraphql(query, req),
|
actors: async (query) => fetchActorsGraphql(query, req),
|
||||||
actor: async (query, args, info) => fetchActorsByIdGraphql(query, req, info),
|
actor: async (query, args, info) => fetchActorsByIdGraphql(query, req, info),
|
||||||
actorsById: async (query, args, info) => fetchActorsByIdGraphql(query, req, info),
|
actorsById: async (query, args, info) => fetchActorsByIdGraphql(query, req, info),
|
||||||
|
@ -90,6 +123,18 @@ export async function graphqlApi(req, res) {
|
||||||
entity: async (query, args, info) => fetchEntitiesByIdGraphql(query, req, info),
|
entity: async (query, args, info) => fetchEntitiesByIdGraphql(query, req, info),
|
||||||
entitiesBySlug: async (query, args, info) => fetchEntitiesByIdGraphql(query, req, info),
|
entitiesBySlug: async (query, args, info) => fetchEntitiesByIdGraphql(query, req, info),
|
||||||
entitiesById: async (query, args, info) => fetchEntitiesByIdGraphql(query, req, info),
|
entitiesById: async (query, args, info) => fetchEntitiesByIdGraphql(query, req, info),
|
||||||
|
stashes: async (query) => fetchUserStashesGraphql(query, req),
|
||||||
|
stash: async (query) => fetchStashGraphql(query, req),
|
||||||
|
// mutation
|
||||||
|
createStash: async (query) => createStashGraphql(query, req),
|
||||||
|
updateStash: async (query) => updateStashGraphql(query, req),
|
||||||
|
removeStash: async (query) => removeStashGraphql(query, req),
|
||||||
|
stashScene: async (query) => stashSceneGraphql(query, req),
|
||||||
|
unstashScene: async (query) => unstashSceneGraphql(query, req),
|
||||||
|
stashActor: async (query) => stashActorGraphql(query, req),
|
||||||
|
unstashActor: async (query) => unstashActorGraphql(query, req),
|
||||||
|
stashMovie: async (query) => stashMovieGraphql(query, req),
|
||||||
|
unstashMovie: async (query) => unstashMovieGraphql(query, req),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import { stringify } from '@brillout/json-serializer/stringify'; /* eslint-disable-line import/extensions */
|
import { stringify } from '@brillout/json-serializer/stringify'; /* eslint-disable-line import/extensions */
|
||||||
|
|
||||||
import { fetchMovies } from '../movies.js';
|
import { fetchMovies, fetchMoviesById } from '../movies.js';
|
||||||
|
import { fetchStashByUsernameAndSlug } from '../stashes.js';
|
||||||
import { parseActorIdentifier } from '../query.js';
|
import { parseActorIdentifier } from '../query.js';
|
||||||
import { getIdsBySlug } from '../cache.js';
|
import { getIdsBySlug } from '../cache.js';
|
||||||
|
import slugify from '../../utils/slugify.js';
|
||||||
|
import promiseProps from '../../utils/promise-props.js';
|
||||||
|
import { HttpError } from '../errors.js';
|
||||||
|
|
||||||
export async function curateMoviesQuery(query) {
|
export async function curateMoviesQuery(query) {
|
||||||
return {
|
return {
|
||||||
|
@ -41,3 +45,112 @@ export async function fetchMoviesApi(req, res) {
|
||||||
total,
|
total,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchMovieApi(req, res) {
|
||||||
|
const [movie] = await fetchMoviesById([Number(req.params.movieId)], { reqUser: req.user });
|
||||||
|
|
||||||
|
if (!movie) {
|
||||||
|
throw new HttpError(`No movie with ID ${req.params.movieId} found`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send(movie);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const moviesSchema = `
|
||||||
|
extend type Query {
|
||||||
|
movies(
|
||||||
|
query: String
|
||||||
|
scope: String
|
||||||
|
entities: [String!]
|
||||||
|
actorIds: [String!]
|
||||||
|
tags: [String!]
|
||||||
|
stash: String
|
||||||
|
limit: Int! = 30
|
||||||
|
page: Int! = 1
|
||||||
|
): ReleasesResult
|
||||||
|
|
||||||
|
movie(
|
||||||
|
id: Int!
|
||||||
|
): Release
|
||||||
|
|
||||||
|
moviesById(
|
||||||
|
ids: [Int!]!
|
||||||
|
): [Release]
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
function getScope(query) {
|
||||||
|
if (query.scope) {
|
||||||
|
return query.scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.query) {
|
||||||
|
return 'results';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.stash) {
|
||||||
|
return 'stashed';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'latest';
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchMoviesGraphql(query, req) {
|
||||||
|
const mainEntity = query.entities?.find((entity) => entity.charAt(0) !== '!');
|
||||||
|
|
||||||
|
const stash = query.stash && req.user
|
||||||
|
? await fetchStashByUsernameAndSlug(req.user.id, query.stash, req.user)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (query.stash && !stash) {
|
||||||
|
throw new HttpError(`Could not find stash '${query.stash}'`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
tagIds,
|
||||||
|
notTagIds,
|
||||||
|
entityId,
|
||||||
|
notEntityIds,
|
||||||
|
} = await promiseProps({
|
||||||
|
tagIds: getIdsBySlug(query.tags?.filter((tag) => tag.charAt(0) !== '!'), 'tags'),
|
||||||
|
notTagIds: getIdsBySlug(query.tags?.filter((tag) => tag.charAt(0) === '!').map((tag) => tag.slice(1)).map((tag) => slugify(tag)), 'tags'),
|
||||||
|
entityId: getIdsBySlug([mainEntity], 'entities').then(([id]) => id),
|
||||||
|
notEntityIds: getIdsBySlug(query.entities?.filter((entity) => entity.charAt(0) === '!').map((entity) => entity.slice(1)), 'entities'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
movies,
|
||||||
|
total,
|
||||||
|
} = await fetchMovies({
|
||||||
|
query: query.query, // query query query query
|
||||||
|
tagIds,
|
||||||
|
// not- currently not implemented by movies module, pass here anyway for when it is
|
||||||
|
notTagIds,
|
||||||
|
entityId,
|
||||||
|
notEntityIds,
|
||||||
|
actorIds: query.actorIds?.filter((actorId) => actorId.charAt(0) !== '!').map((actorId) => Number(actorId)),
|
||||||
|
notActorIds: query.actorIds?.filter((actorId) => actorId.charAt(0) === '!').map((actorId) => Number(actorId.slice(1))),
|
||||||
|
stashId: stash?.id,
|
||||||
|
scope: getScope(query),
|
||||||
|
isShowcased: null,
|
||||||
|
}, {
|
||||||
|
page: query.page || 1,
|
||||||
|
limit: query.limit || 30,
|
||||||
|
aggregate: false,
|
||||||
|
}, req.user);
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodes: movies,
|
||||||
|
total,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchMoviesByIdGraphql(query, req) {
|
||||||
|
const movies = await fetchMoviesById([].concat(query.id, query.ids).filter(Boolean), req.user);
|
||||||
|
|
||||||
|
if (query.ids) {
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
|
||||||
|
return movies[0];
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
reviewSceneRevision,
|
reviewSceneRevision,
|
||||||
} from '../scenes.js';
|
} from '../scenes.js';
|
||||||
|
|
||||||
|
import { fetchStashByUsernameAndSlug } from '../stashes.js';
|
||||||
|
|
||||||
import { parseActorIdentifier } from '../query.js';
|
import { parseActorIdentifier } from '../query.js';
|
||||||
import { getIdsBySlug } from '../cache.js';
|
import { getIdsBySlug } from '../cache.js';
|
||||||
import slugify from '../../utils/slugify.js';
|
import slugify from '../../utils/slugify.js';
|
||||||
|
@ -87,6 +89,8 @@ export const scenesSchema = `
|
||||||
entities: [String!]
|
entities: [String!]
|
||||||
actorIds: [String!]
|
actorIds: [String!]
|
||||||
tags: [String!]
|
tags: [String!]
|
||||||
|
movieId: Int
|
||||||
|
stash: String
|
||||||
limit: Int! = 30
|
limit: Int! = 30
|
||||||
page: Int! = 1
|
page: Int! = 1
|
||||||
): ReleasesResult
|
): ReleasesResult
|
||||||
|
@ -152,9 +156,33 @@ export const scenesSchema = `
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
function getScope(query) {
|
||||||
|
if (query.scope) {
|
||||||
|
return query.scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.query) {
|
||||||
|
return 'results';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.stash) {
|
||||||
|
return 'stashed';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'latest';
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchScenesGraphql(query, req) {
|
export async function fetchScenesGraphql(query, req) {
|
||||||
const mainEntity = query.entities?.find((entity) => entity.charAt(0) !== '!');
|
const mainEntity = query.entities?.find((entity) => entity.charAt(0) !== '!');
|
||||||
|
|
||||||
|
const stash = query.stash && req.user
|
||||||
|
? await fetchStashByUsernameAndSlug(req.user.id, query.stash, req.user)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (query.stash && !stash) {
|
||||||
|
throw new HttpError(`Could not find stash '${query.stash}'`, 404);
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tagIds,
|
tagIds,
|
||||||
notTagIds,
|
notTagIds,
|
||||||
|
@ -183,9 +211,9 @@ export async function fetchScenesGraphql(query, req) {
|
||||||
notEntityIds,
|
notEntityIds,
|
||||||
actorIds: query.actorIds?.filter((actorId) => actorId.charAt(0) !== '!').map((actorId) => Number(actorId)),
|
actorIds: query.actorIds?.filter((actorId) => actorId.charAt(0) !== '!').map((actorId) => Number(actorId)),
|
||||||
notActorIds: query.actorIds?.filter((actorId) => actorId.charAt(0) === '!').map((actorId) => Number(actorId.slice(1))),
|
notActorIds: query.actorIds?.filter((actorId) => actorId.charAt(0) === '!').map((actorId) => Number(actorId.slice(1))),
|
||||||
scope: query.query && !query.scope
|
movieId: query.movieId,
|
||||||
? 'results'
|
stashId: stash?.id,
|
||||||
: query.scope,
|
scope: getScope(query),
|
||||||
isShowcased: null,
|
isShowcased: null,
|
||||||
}, {
|
}, {
|
||||||
page: query.page || 1,
|
page: query.page || 1,
|
||||||
|
|
|
@ -133,7 +133,7 @@ export default async function initServer() {
|
||||||
|
|
||||||
// API KEYS
|
// API KEYS
|
||||||
router.get('/api/me/keys', fetchUserKeysApi);
|
router.get('/api/me/keys', fetchUserKeysApi);
|
||||||
router.post('/api/keys', createKeyApi);
|
router.post('/api/me/keys', createKeyApi);
|
||||||
router.delete('/api/me/keys/:keyIdentifier', removeUserKeyApi);
|
router.delete('/api/me/keys/:keyIdentifier', removeUserKeyApi);
|
||||||
router.delete('/api/me/keys', flushUserKeysApi);
|
router.delete('/api/me/keys', flushUserKeysApi);
|
||||||
|
|
||||||
|
|
|
@ -11,66 +11,210 @@ import {
|
||||||
unstashScene,
|
unstashScene,
|
||||||
unstashMovie,
|
unstashMovie,
|
||||||
updateStash,
|
updateStash,
|
||||||
|
fetchStashByUsernameAndSlug,
|
||||||
} from '../stashes.js';
|
} from '../stashes.js';
|
||||||
|
|
||||||
|
export const stashesSchema = `
|
||||||
|
extend type Query {
|
||||||
|
stashes(username: String): [Stash]
|
||||||
|
|
||||||
|
stash(
|
||||||
|
username: String
|
||||||
|
stash: String!
|
||||||
|
): Stash
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type Mutation {
|
||||||
|
createStash(
|
||||||
|
name: String!
|
||||||
|
isPublic: Boolean
|
||||||
|
): Stash
|
||||||
|
|
||||||
|
updateStash(
|
||||||
|
stash: String!
|
||||||
|
name: String
|
||||||
|
isPublic: Boolean
|
||||||
|
): Stash
|
||||||
|
|
||||||
|
removeStash(
|
||||||
|
stash: String!
|
||||||
|
): Stash
|
||||||
|
|
||||||
|
stashScene(
|
||||||
|
sceneId: Int!
|
||||||
|
stash: String!
|
||||||
|
): Stashed
|
||||||
|
|
||||||
|
unstashScene(
|
||||||
|
sceneId: Int!
|
||||||
|
stash: String!
|
||||||
|
): Stashed
|
||||||
|
|
||||||
|
stashActor(
|
||||||
|
actorId: Int!
|
||||||
|
stash: String!
|
||||||
|
): Stashed
|
||||||
|
|
||||||
|
unstashActor(
|
||||||
|
actorId: Int!
|
||||||
|
stash: String!
|
||||||
|
): Stashed
|
||||||
|
|
||||||
|
stashMovie(
|
||||||
|
movieId: Int!
|
||||||
|
stash: String!
|
||||||
|
): Stashed
|
||||||
|
|
||||||
|
unstashMovie(
|
||||||
|
movieId: Int!
|
||||||
|
stash: String!
|
||||||
|
): Stashed
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stash {
|
||||||
|
id: Int
|
||||||
|
name: String
|
||||||
|
slug: String
|
||||||
|
isPublic: Boolean
|
||||||
|
isPrimary: Boolean
|
||||||
|
createdAt: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stashed {
|
||||||
|
id: Int
|
||||||
|
stashId: Int
|
||||||
|
sceneId: Int
|
||||||
|
actorId: Int
|
||||||
|
movieId: Int
|
||||||
|
serieId: Int
|
||||||
|
createdAt: Date
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export async function fetchUserStashesApi(req, res) {
|
export async function fetchUserStashesApi(req, res) {
|
||||||
const stashes = await fetchUserStashes(req.user.id, req.user);
|
const stashes = await fetchUserStashes(req.user.id, req.user);
|
||||||
|
|
||||||
res.send(stashes);
|
res.send(stashes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchUserStashesGraphql(query, req) {
|
||||||
|
const stashes = await fetchUserStashes(query.username || req.userId, req.user);
|
||||||
|
|
||||||
|
return stashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchStashGraphql(query, req) {
|
||||||
|
const stashes = await fetchStashByUsernameAndSlug(query.username || req.userId, query.stash, req.user);
|
||||||
|
|
||||||
|
return stashes;
|
||||||
|
}
|
||||||
|
|
||||||
export async function createStashApi(req, res) {
|
export async function createStashApi(req, res) {
|
||||||
const stash = await createStash(req.body, req.user);
|
const stash = await createStash(req.body, req.user);
|
||||||
|
|
||||||
res.send(stash);
|
res.send(stash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createStashGraphql(query, req) {
|
||||||
|
const stash = await createStash(query, req.user);
|
||||||
|
|
||||||
|
return stash;
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateStashApi(req, res) {
|
export async function updateStashApi(req, res) {
|
||||||
const stash = await updateStash(Number(req.params.stashId), req.body, req.user);
|
const stash = await updateStash(Number(req.params.stashId), req.body, req.user);
|
||||||
|
|
||||||
res.send(stash);
|
res.send(stash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateStashGraphql(query, req) {
|
||||||
|
const stash = await updateStash(query.stash, query, req.user);
|
||||||
|
|
||||||
|
return stash;
|
||||||
|
}
|
||||||
|
|
||||||
export async function removeStashApi(req, res) {
|
export async function removeStashApi(req, res) {
|
||||||
await removeStash(Number(req.params.stashId), req.user);
|
await removeStash(Number(req.params.stashId), req.user);
|
||||||
|
|
||||||
res.status(204).send();
|
res.status(204).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeStashGraphql(query, req) {
|
||||||
|
const removedId = await removeStash(query.stash, req.user);
|
||||||
|
|
||||||
|
return removedId;
|
||||||
|
}
|
||||||
|
|
||||||
export async function stashActorApi(req, res) {
|
export async function stashActorApi(req, res) {
|
||||||
const stashes = await stashActor(req.body.actorId, Number(req.params.stashId), req.user);
|
const stashed = await stashActor(req.body.actorId, Number(req.params.stashId), req.user);
|
||||||
|
|
||||||
res.send(stashes);
|
res.send(stashed);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stashSceneApi(req, res) {
|
export async function stashActorGraphql(query, req) {
|
||||||
const stashes = await stashScene(req.body.sceneId, Number(req.params.stashId), req.user);
|
const stashed = await stashActor(query.actorId, query.stash, req.user);
|
||||||
|
|
||||||
res.send(stashes);
|
return stashed;
|
||||||
}
|
|
||||||
|
|
||||||
export async function stashMovieApi(req, res) {
|
|
||||||
const stashes = await stashMovie(req.body.movieId, Number(req.params.stashId), req.user);
|
|
||||||
|
|
||||||
res.send(stashes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function unstashActorApi(req, res) {
|
export async function unstashActorApi(req, res) {
|
||||||
const stashes = await unstashActor(Number(req.params.actorId), Number(req.params.stashId), req.user);
|
const unstashed = await unstashActor(Number(req.params.actorId), Number(req.params.stashId), req.user);
|
||||||
|
|
||||||
res.send(stashes);
|
res.send(unstashed);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unstashActorGraphql(query, req) {
|
||||||
|
const stashes = await unstashActor(query.actorId, query.stash, req.user);
|
||||||
|
|
||||||
|
return stashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stashSceneApi(req, res) {
|
||||||
|
const stashed = await stashScene(req.body.sceneId, Number(req.params.stashId), req.user);
|
||||||
|
|
||||||
|
res.send(stashed);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stashSceneGraphql(query, req) {
|
||||||
|
const stashed = await stashScene(query.sceneId, query.stash, req.user);
|
||||||
|
|
||||||
|
return stashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function unstashSceneApi(req, res) {
|
export async function unstashSceneApi(req, res) {
|
||||||
const stashes = await unstashScene(Number(req.params.sceneId), Number(req.params.stashId), req.user);
|
const unstashed = await unstashScene(Number(req.params.sceneId), Number(req.params.stashId), req.user);
|
||||||
|
|
||||||
res.send(stashes);
|
res.send(unstashed);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unstashSceneGraphql(query, req) {
|
||||||
|
const unstashed = await unstashScene(query.sceneId, query.stash, req.user);
|
||||||
|
|
||||||
|
return unstashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stashMovieApi(req, res) {
|
||||||
|
const stashed = await stashMovie(req.body.movieId, Number(req.params.stashId), req.user);
|
||||||
|
|
||||||
|
res.send(stashed);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stashMovieGraphql(query, req) {
|
||||||
|
const stashed = await stashMovie(query.movieId, query.stash, req.user);
|
||||||
|
|
||||||
|
return stashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function unstashMovieApi(req, res) {
|
export async function unstashMovieApi(req, res) {
|
||||||
const stashes = await unstashMovie(Number(req.params.movieId), Number(req.params.stashId), req.user);
|
const unstashed = await unstashMovie(Number(req.params.movieId), Number(req.params.stashId), req.user);
|
||||||
|
|
||||||
res.send(stashes);
|
res.send(unstashed);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function unstashMovieGraphql(query, req) {
|
||||||
|
const unstashed = await unstashMovie(query.movieId, query.stash, req.user);
|
||||||
|
|
||||||
|
return unstashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const router = Router();
|
export const router = Router();
|
||||||
|
|
2
static
2
static
|
@ -1 +1 @@
|
||||||
Subproject commit 75b0bfabb314627472f24310557c7f2c31d109b1
|
Subproject commit 4982084fd86168758424ad9f343d729eee97a7e2
|
Loading…
Reference in New Issue