Refactored http timeout handling.

This commit is contained in:
DebaucheryLibrarian 2021-03-17 02:09:34 +01:00
parent 36a8adbd8c
commit 336b91c872
9 changed files with 147 additions and 72 deletions

View File

@ -50,6 +50,20 @@
icon="question2" icon="question2"
/> />
</h2> </h2>
<Icon
v-show="me && isStashed"
icon="heart7"
class="stash stashed noselect"
@click="unstashScene"
/>
<Icon
v-show="me && !isStashed"
icon="heart8"
class="stash unstashed noselect"
@click="stashScene"
/>
</div> </div>
<div class="row associations"> <div class="row associations">
@ -223,7 +237,7 @@ import Actor from '../actors/tile.vue';
import Releases from './releases.vue'; import Releases from './releases.vue';
import Scroll from '../scroll/scroll.vue'; import Scroll from '../scroll/scroll.vue';
async function fetchRelease() { async function fetchRelease(scroll = true) {
if (this.$route.name === 'scene') { if (this.$route.name === 'scene') {
this.release = await this.$store.dispatch('fetchReleaseById', this.$route.params.releaseId); this.release = await this.$store.dispatch('fetchReleaseById', this.$route.params.releaseId);
} }
@ -232,11 +246,37 @@ async function fetchRelease() {
this.release = await this.$store.dispatch('fetchMovieById', this.$route.params.releaseId); this.release = await this.$store.dispatch('fetchMovieById', this.$route.params.releaseId);
} }
if (this.$refs.content) { if (scroll && this.$refs.content) {
this.$refs.content.scrollTop = 0; this.$refs.content.scrollTop = 0;
} }
} }
async function stashScene() {
this.$store.dispatch('stashScene', {
sceneId: this.release.id,
stashId: this.$store.getters.favorites.id,
});
this.fetchRelease(false);
}
async function unstashScene() {
this.$store.dispatch('unstashScene', {
sceneId: this.release.id,
stashId: this.$store.getters.favorites.id,
});
this.fetchRelease(false);
}
function me() {
return this.$store.state.auth.user;
}
function isStashed() {
return this.release.stashes?.length > 0;
}
function bannerBackground() { function bannerBackground() {
return (this.release.poster && this.getBgPath(this.release.poster, 'thumbnail')) return (this.release.poster && this.getBgPath(this.release.poster, 'thumbnail'))
|| (this.release.covers.length > 0 && this.getBgPath(this.release.covers[0], 'thumbnail')); || (this.release.covers.length > 0 && this.getBgPath(this.release.covers[0], 'thumbnail'));
@ -271,6 +311,8 @@ export default {
computed: { computed: {
pageTitle, pageTitle,
bannerBackground, bannerBackground,
isStashed,
me,
showAlbum, showAlbum,
}, },
watch: { watch: {
@ -279,6 +321,8 @@ export default {
mounted: fetchRelease, mounted: fetchRelease,
methods: { methods: {
fetchRelease, fetchRelease,
stashScene,
unstashScene,
}, },
}; };
</script> </script>
@ -353,6 +397,22 @@ export default {
color: var(--shadow); color: var(--shadow);
} }
.stash.icon {
width: 1.5rem;
height: 1.5rem;
padding: 0 1rem;
fill: var(--darken);
&.stashed {
fill: var(--primary);
}
&:hover {
fill: var(--primary);
cursor: pointer;
}
}
.album-toggle { .album-toggle {
height: fit-content; height: fit-content;
display: inline-flex; display: inline-flex;

View File

@ -346,6 +346,21 @@ const releaseFragment = `
} }
} }
} }
stashes: stashesScenesBySceneId(
filter: {
stash: {
userId: {
equalTo: $userId
}
}
}
) @include(if: $hasAuth) {
stash {
id
name
slug
}
}
} }
`; `;

View File

@ -37,11 +37,17 @@ function initReleasesActions(store, router) {
// const release = await get(`/releases/${releaseId}`); // const release = await get(`/releases/${releaseId}`);
const { release } = await graphql(` const { release } = await graphql(`
query Release($releaseId:Int!) { query Release(
$releaseId: Int!
$hasAuth: Boolean!
$userId: Int
) {
${releaseFragment} ${releaseFragment}
} }
`, { `, {
releaseId: Number(releaseId), releaseId: Number(releaseId),
hasAuth: !!store.state.auth.user,
userId: store.state.auth.user?.id,
}); });
if (!release) { if (!release) {

View File

@ -9,9 +9,19 @@ function initStashesActions(_store, _router) {
await del(`/stashes/${stashId}/actors/${actorId}`); await del(`/stashes/${stashId}/actors/${actorId}`);
} }
async function stashScene(context, { sceneId, stashId }) {
await post(`/stashes/${stashId}/scenes`, { sceneId });
}
async function unstashScene(context, { sceneId, stashId }) {
await del(`/stashes/${stashId}/scenes/${sceneId}`);
}
return { return {
stashActor, stashActor,
stashScene,
unstashActor, unstashActor,
unstashScene,
}; };
} }

View File

@ -1,4 +1,6 @@
import { graphql } from '../api'; import { graphql } from '../api';
import { releaseFields } from '../fragments';
import { curateUser } from '../curate';
function initUsersActions(_store, _router) { function initUsersActions(_store, _router) {
async function fetchUser(context, username) { async function fetchUser(context, username) {
@ -43,45 +45,7 @@ function initUsersActions(_store, _router) {
scenes: stashesScenes { scenes: stashesScenes {
comment comment
scene { scene {
id ${releaseFields}
title
slug
url
date
actors: releasesActors {
actor {
id
name
slug
}
}
tags: releasesTags {
tag {
id
name
slug
}
}
entity {
id
name
slug
independent
parent {
id
name
slug
independent
}
}
poster: releasesPosterByReleaseId {
media {
path
thumbnail
lazy
isS3
}
}
} }
} }
} }
@ -91,7 +55,7 @@ function initUsersActions(_store, _router) {
username, username,
}); });
return user; return curateUser(user);
} }
return { return {

View File

@ -1328,7 +1328,6 @@ exports.up = knex => Promise.resolve()
)); ));
`, { `, {
visitor: knex.raw(config.database.query.user), visitor: knex.raw(config.database.query.user),
password: knex.raw(config.database.query.password),
}); });
}) })
// VIEWS AND COMMENTS // VIEWS AND COMMENTS

View File

@ -21,7 +21,7 @@ function logActive() {
setTimeout(() => { setTimeout(() => {
log(); log();
logActive(); logActive();
}, 600000); }, argv.logActive || 60000);
} }
async function init() { async function init() {

View File

@ -48,19 +48,31 @@ async function stashScene(sceneId, stashId, sessionUser) {
await knex('stashes_scenes') await knex('stashes_scenes')
.insert({ .insert({
stash_id: stash.id, stash_id: stash.id,
actor_id: sceneId, scene_id: sceneId,
}); });
} }
async function unstashActor(actorId, stashId, sessionUser) { async function unstashActor(actorId, stashId, sessionUser) {
await knex await knex
.from('stashes_actors') .from('stashes_actors AS deletable')
.whereIn('stashes_actors.id', knex('stashes_actors') .where('deletable.actor_id', actorId)
.select('stashes_actors.id') .where('deletable.stash_id', stashId)
.whereExists(knex('stashes_actors') // verify user owns this stash
.leftJoin('stashes', 'stashes.id', 'stashes_actors.stash_id') .leftJoin('stashes', 'stashes.id', 'stashes_actors.stash_id')
.where('stashes.user_id', sessionUser.id) // verify user owns this stash .where('stashes_actors.stash_id', knex.raw('deletable.stash_id'))
.where('stashes_actors.actor_id', actorId) .where('stashes.user_id', sessionUser.id))
.where('stashes_actors.stash_id', stashId)) .delete();
}
async function unstashScene(sceneId, stashId, sessionUser) {
await knex
.from('stashes_scenes AS deletable')
.where('deletable.scene_id', sceneId)
.where('deletable.stash_id', stashId)
.whereExists(knex('stashes_scenes') // verify user owns this stash
.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(); .delete();
} }
@ -68,6 +80,6 @@ module.exports = {
curateStash, curateStash,
stashActor, stashActor,
stashScene, stashScene,
// unstashScene, unstashScene,
unstashActor, unstashActor,
}; };

View File

@ -80,20 +80,9 @@ function getLimiter(options = {}, url) {
return limiters[interval][concurrency]; return limiters[interval][concurrency];
} }
async function request(method = 'get', url, body, requestOptions = {}, limiter, timeout) { async function request(method = 'get', url, body, requestOptions = {}, limiter) {
const http = requestOptions.session || bhttp; const http = requestOptions.session || bhttp;
const options = requestOptions;
const options = {
...defaultOptions,
...requestOptions,
headers: {
...defaultOptions.headers,
...requestOptions.headers,
},
responseTimeout: requestOptions.responseTimeout || requestOptions.timeout || defaultOptions.timeout,
stream: !!requestOptions.destination,
session: null,
};
const withProxy = useProxy(url); const withProxy = useProxy(url);
@ -107,8 +96,10 @@ async function request(method = 'get', url, body, requestOptions = {}, limiter,
? http[method](url, body, options) ? http[method](url, body, options)
: http[method](url, options)); : http[method](url, options));
timeout.cancel(); return res;
}
async function finalizeResult(res, options) {
if (options.destination) { if (options.destination) {
// res.on('progress', (bytes, totalBytes) => logger.silly(`Downloaded ${Math.round((bytes / totalBytes) * 100)}% of ${url}`)); // res.on('progress', (bytes, totalBytes) => logger.silly(`Downloaded ${Math.round((bytes / totalBytes) * 100)}% of ${url}`));
@ -140,21 +131,39 @@ async function request(method = 'get', url, body, requestOptions = {}, limiter,
function getTimeout(options, url) { function getTimeout(options, url) {
return new Promise((resolve, reject, onCancel) => { return new Promise((resolve, reject, onCancel) => {
const timeoutId = setTimeout(() => { const timeout = setTimeout(() => {
reject(new Error(`URL ${url} timed out`)); reject(new Error(`URL ${url} timed out`));
}, (options?.timeout || defaultOptions.timeout) + 10000); }, (options?.timeout || defaultOptions.timeout) + 10000);
onCancel(() => clearTimeout(timeoutId)); onCancel(() => {
clearTimeout(timeout);
});
}); });
} }
async function scheduleRequest(method = 'get', url, body, options) { async function scheduleRequest(method = 'get', url, body, requestOptions = {}) {
const options = {
...defaultOptions,
...requestOptions,
headers: {
...defaultOptions.headers,
...requestOptions.headers,
},
responseTimeout: requestOptions.responseTimeout || requestOptions.timeout || defaultOptions.timeout,
stream: !!requestOptions.destination,
session: null,
};
const limiter = getLimiter(options, url); const limiter = getLimiter(options, url);
const timeout = getTimeout(options, url); const timeout = getTimeout(options, url);
const result = await limiter.schedule(() => Promise.race([request(method, url, body, options, limiter, timeout), timeout])); const result = await limiter.schedule(async () => Promise.race([request(method, url, body, options, limiter), timeout]));
return result; timeout.cancel();
const curatedResult = await finalizeResult(result, options);
return curatedResult;
} }
async function get(url, options) { async function get(url, options) {