Added georestriction with SFW mode.
This commit is contained in:
@@ -21,7 +21,7 @@
|
|||||||
"no-console": 0,
|
"no-console": 0,
|
||||||
"no-param-reassign": ["error", {
|
"no-param-reassign": ["error", {
|
||||||
"props": true,
|
"props": true,
|
||||||
"ignorePropertyModificationsFor": ["state", "acc"]
|
"ignorePropertyModificationsFor": ["state", "acc", "req"]
|
||||||
}],
|
}],
|
||||||
"vue/multi-word-component-names": 0,
|
"vue/multi-word-component-names": 0,
|
||||||
"vue/no-reserved-component-names": 0,
|
"vue/no-reserved-component-names": 0,
|
||||||
@@ -32,7 +32,8 @@
|
|||||||
"vue/html-indent": ["error", "tab"],
|
"vue/html-indent": ["error", "tab"],
|
||||||
"vue/multiline-html-element-content-newline": 0,
|
"vue/multiline-html-element-content-newline": 0,
|
||||||
"vue/no-v-html": 0,
|
"vue/no-v-html": 0,
|
||||||
"vue/singleline-html-element-content-newline": 0
|
"vue/singleline-html-element-content-newline": 0,
|
||||||
|
"vue/comment-directive": 0,
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": {
|
"import/resolver": {
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@ config/*
|
|||||||
log/
|
log/
|
||||||
/media
|
/media
|
||||||
data/
|
data/
|
||||||
|
assets/*.mmdb
|
||||||
|
assets/.geoipupdate.lock
|
||||||
|
|||||||
125
assets/sfw.ejs
Normal file
125
assets/sfw.ejs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/img/favicon/site.webmanifest">
|
||||||
|
<link rel="mask-icon" href="/img/favicon/safari-pinned-tab.svg" color="#5bbad5">
|
||||||
|
<link rel="shortcut icon" href="/img/favicon/favicon.ico">
|
||||||
|
|
||||||
|
<meta name="msapplication-TileColor" content="#b91d47">
|
||||||
|
<meta name="msapplication-config" content="/img/favicon/browserconfig.xml">
|
||||||
|
<meta name="theme-color" content="#f65596">
|
||||||
|
|
||||||
|
<meta property="og:title" content="traxxx" />
|
||||||
|
<meta property="og:image" content="https://traxxx.me/img/og_logo.png" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width,height=device-height,initial-scale=1.0,interactive-widget=resizes-content" />
|
||||||
|
|
||||||
|
<title>traxxx - None shall pass</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary-dark-10: #e54485;
|
||||||
|
--primary: #f65596;
|
||||||
|
--primary-light-10: #f075a6;
|
||||||
|
--primary-light-20: #f2a6c4;
|
||||||
|
--primary-light-30: #f7c9dc;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.explainer {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
text-align: justify;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.useful {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 1rem 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links li {
|
||||||
|
padding: .25rem 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links a {
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
background: var(--primary);
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links a:hover {
|
||||||
|
background: var(--primary-dark-10);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="content">
|
||||||
|
<h2 class="heading">Not so fast, rascal.</h2>
|
||||||
|
|
||||||
|
<p class="explainer">The content offered by traxxx is restricted in your jurisdiction.</p>
|
||||||
|
|
||||||
|
<% if (!noVpn) { %>
|
||||||
|
<div class="useful">
|
||||||
|
Useful links:
|
||||||
|
<ul class="links">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://mullvad.net/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
class="link"
|
||||||
|
>Mullvad VPN</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="https://protonvpn.com/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
class="link"
|
||||||
|
>Proton VPN</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
class="avatar-link no-link"
|
class="avatar-link no-link"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="actor.avatar"
|
v-if="actor.avatar && !restriction"
|
||||||
:src="getPath(actor.avatar, 'thumbnail')"
|
:src="getPath(actor.avatar, 'thumbnail')"
|
||||||
:style="{ 'background-image': `url(${getPath(actor.avatar, 'lazy')})` }"
|
:style="{ 'background-image': `url(${getPath(actor.avatar, 'lazy')})` }"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@@ -103,7 +103,7 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const pageContext = inject('pageContext');
|
const pageContext = inject('pageContext');
|
||||||
const { user } = pageContext;
|
const { user, restriction } = pageContext;
|
||||||
const pageStash = pageContext.pageProps.stash;
|
const pageStash = pageContext.pageProps.stash;
|
||||||
const currentStash = pageStash || pageContext.assets?.primaryStash;
|
const currentStash = pageStash || pageContext.assets?.primaryStash;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="restriction"
|
||||||
|
class="restricted"
|
||||||
|
>
|
||||||
|
<div>Traxxx is restricted in your region</div>
|
||||||
|
<a
|
||||||
|
href="/sfw"
|
||||||
|
class="link"
|
||||||
|
>Learn more</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<iframe
|
<iframe
|
||||||
v-if="campaign?.banner?.type === 'html'"
|
v-else-if="campaign?.banner?.type === 'html'"
|
||||||
ref="iframe"
|
ref="iframe"
|
||||||
:width="campaign.banner.width"
|
:width="campaign.banner.width"
|
||||||
:height="campaign.banner.height"
|
:height="campaign.banner.height"
|
||||||
@@ -31,6 +42,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { inject } from 'vue';
|
||||||
|
|
||||||
|
const pageContext = inject('pageContext');
|
||||||
|
const { restriction } = pageContext;
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
campaign: {
|
campaign: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -75,4 +91,15 @@ const bannerSrc = (() => {
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.restricted {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: .5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: .5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const pageContext = inject('pageContext');
|
const pageContext = inject('pageContext');
|
||||||
const user = pageContext.user;
|
const { user, restriction } = pageContext;
|
||||||
const pageStash = pageContext.pageProps.stash;
|
const pageStash = pageContext.pageProps.stash;
|
||||||
const currentStash = pageStash || pageContext.assets?.primaryStash;
|
const currentStash = pageStash || pageContext.assets?.primaryStash;
|
||||||
|
|
||||||
|
|||||||
@@ -9,14 +9,13 @@
|
|||||||
v-for="photo in photos"
|
v-for="photo in photos"
|
||||||
:key="`photo-${photo.id}`"
|
:key="`photo-${photo.id}`"
|
||||||
:title="photo.comment"
|
:title="photo.comment"
|
||||||
:href="`/img/${photo.path}`"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="photo-container"
|
class="photo-container"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
:src="`/${photo.thumbnail}`"
|
:src="getPath(photo, 'thumbnail')"
|
||||||
:style="{ 'background-image': `url(/${photo.lazy})` }"
|
:style="{ 'background-image': `url(${getPath(photo, 'lazy')})` }"
|
||||||
:alt="photo.comment"
|
:alt="photo.comment"
|
||||||
:width="photo.width"
|
:width="photo.width"
|
||||||
:height="photo.height"
|
:height="photo.height"
|
||||||
@@ -47,6 +46,8 @@ import { computed, inject } from 'vue';
|
|||||||
import Logo from '#/components/tags/logo.vue';
|
import Logo from '#/components/tags/logo.vue';
|
||||||
import Campaign from '#/components/campaigns/campaign.vue';
|
import Campaign from '#/components/campaigns/campaign.vue';
|
||||||
|
|
||||||
|
import getPath from '#/src/get-path.js';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tag: {
|
tag: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|||||||
@@ -58,6 +58,91 @@ module.exports = {
|
|||||||
address: 'http://localhost:3000/script.js',
|
address: 'http://localhost:3000/script.js',
|
||||||
siteId: '1b28ac3b-d229-43bf-aec9-75cf0a72a466',
|
siteId: '1b28ac3b-d229-43bf-aec9-75cf0a72a466',
|
||||||
},
|
},
|
||||||
|
restrictions: {
|
||||||
|
enabled: false,
|
||||||
|
modes: [
|
||||||
|
null, // easier for 0 to mean disabled
|
||||||
|
'block', // 1
|
||||||
|
'censor', // 2
|
||||||
|
],
|
||||||
|
regions: {
|
||||||
|
// Europe
|
||||||
|
DE: 1, // Germany
|
||||||
|
FR: 1, // France
|
||||||
|
GB: 1, // Great Britain / United Kingdom
|
||||||
|
IT: 1, // Italy
|
||||||
|
// Asia & Oceania
|
||||||
|
AU: 1, // Australia
|
||||||
|
CN: 1, // China
|
||||||
|
// Americas
|
||||||
|
US: {
|
||||||
|
AL: 1, // Alabama
|
||||||
|
AR: 1, // Arkansas
|
||||||
|
AZ: 1, // Arizona
|
||||||
|
FL: 1, // Florida
|
||||||
|
GA: 1, // Georgia
|
||||||
|
ID: 1, // Idaho
|
||||||
|
IN: 1, // Indiana
|
||||||
|
KS: 1, // Kansas
|
||||||
|
KY: 1, // Kentucky
|
||||||
|
LA: 1, // Louisiana
|
||||||
|
MO: 1, // Missouri
|
||||||
|
MS: 1, // Mississippi
|
||||||
|
MT: 1, // Montana
|
||||||
|
NC: 1, // North Carolina
|
||||||
|
ND: 1, // North Dakota
|
||||||
|
NE: 1, // Nebraska
|
||||||
|
OH: 1, // Ohio
|
||||||
|
OK: 1, // Oklahoma
|
||||||
|
SC: 1, // South Carolina
|
||||||
|
SD: 1, // South Dakota
|
||||||
|
TN: 1, // Tennessee
|
||||||
|
TX: 1, // Texas
|
||||||
|
UT: 1, // Utah
|
||||||
|
VA: 1, // Virginia
|
||||||
|
WY: 1, // Wyoming
|
||||||
|
}, // only Florida
|
||||||
|
},
|
||||||
|
noVpn: [
|
||||||
|
'AE', // United Arab Emirates
|
||||||
|
'BY', // Belarus
|
||||||
|
'CN', // China
|
||||||
|
'IQ', // Iraq
|
||||||
|
'IR', // Iran
|
||||||
|
'KP', // North Korea
|
||||||
|
'OM', // Oman
|
||||||
|
'RU', // Russia
|
||||||
|
'TM', // Turkmenistan
|
||||||
|
'TR', // Turkey
|
||||||
|
],
|
||||||
|
censors: [ // additional to default filter
|
||||||
|
'ball',
|
||||||
|
'bisexual',
|
||||||
|
'blow',
|
||||||
|
'blowbang',
|
||||||
|
'condom',
|
||||||
|
'cowgirl',
|
||||||
|
'creampie',
|
||||||
|
'doggy',
|
||||||
|
'facial',
|
||||||
|
'finger',
|
||||||
|
'gay',
|
||||||
|
'hole',
|
||||||
|
'horny',
|
||||||
|
'lesbian',
|
||||||
|
'masturbation',
|
||||||
|
'milf',
|
||||||
|
'missionary',
|
||||||
|
'prolapse',
|
||||||
|
'nymph',
|
||||||
|
'sex',
|
||||||
|
'swallowing',
|
||||||
|
'squirt',
|
||||||
|
'sucking',
|
||||||
|
'threesome',
|
||||||
|
'trans',
|
||||||
|
],
|
||||||
|
},
|
||||||
auth: {
|
auth: {
|
||||||
login: true,
|
login: true,
|
||||||
signup: true,
|
signup: true,
|
||||||
@@ -101,7 +186,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
media: {
|
media: {
|
||||||
path: './media',
|
path: './media',
|
||||||
assetPath: '/img',
|
assetPath: '',
|
||||||
mediaPath: '/media',
|
mediaPath: '/media',
|
||||||
s3Path: 'https://s3.wasabisys.com',
|
s3Path: 'https://s3.wasabisys.com',
|
||||||
videoRestrictions: [], // entity slugs, _ prefix for networks, hides trailer and teaser videos
|
videoRestrictions: [], // entity slugs, _ prefix for networks, hides trailer and teaser videos
|
||||||
|
|||||||
205
package-lock.json
generated
205
package-lock.json
generated
@@ -14,6 +14,7 @@
|
|||||||
"@floating-ui/dom": "^1.5.3",
|
"@floating-ui/dom": "^1.5.3",
|
||||||
"@floating-ui/vue": "^1.0.2",
|
"@floating-ui/vue": "^1.0.2",
|
||||||
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
|
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
|
||||||
|
"@maxmind/geoip2-node": "^6.3.4",
|
||||||
"@resvg/resvg-js": "^2.6.0",
|
"@resvg/resvg-js": "^2.6.0",
|
||||||
"@toycode/markdown-it-class": "^1.2.4",
|
"@toycode/markdown-it-class": "^1.2.4",
|
||||||
"@vitejs/plugin-vue": "^4.5.2",
|
"@vitejs/plugin-vue": "^4.5.2",
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
"cron": "^3.1.6",
|
"cron": "^3.1.6",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"date-fns": "^3.0.0",
|
"date-fns": "^3.0.0",
|
||||||
|
"ejs": "^4.0.1",
|
||||||
"error-stack-parser": "^2.1.4",
|
"error-stack-parser": "^2.1.4",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
@@ -52,6 +54,7 @@
|
|||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"object.omit": "^3.0.0",
|
"object.omit": "^3.0.0",
|
||||||
|
"obscenity": "^0.4.6",
|
||||||
"path-to-regexp": "^6.2.1",
|
"path-to-regexp": "^6.2.1",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"redis": "^4.6.12",
|
"redis": "^4.6.12",
|
||||||
@@ -3224,6 +3227,15 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@maxmind/geoip2-node": {
|
||||||
|
"version": "6.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@maxmind/geoip2-node/-/geoip2-node-6.3.4.tgz",
|
||||||
|
"integrity": "sha512-BTRFHCX7Uie4wVSPXsWQfg0EVl4eGZgLCts0BTKAP+Eiyt1zmF2UPyuUZkaj0R59XSDYO+84o1THAtaenUoQYg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"maxmind": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@modyfi/vite-plugin-yaml": {
|
"node_modules/@modyfi/vite-plugin-yaml": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@modyfi/vite-plugin-yaml/-/vite-plugin-yaml-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@modyfi/vite-plugin-yaml/-/vite-plugin-yaml-1.1.0.tgz",
|
||||||
@@ -4527,9 +4539,10 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/async": {
|
"node_modules/async": {
|
||||||
"version": "3.2.5",
|
"version": "3.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||||
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
|
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/asynckit": {
|
"node_modules/asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
@@ -5577,6 +5590,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||||
},
|
},
|
||||||
|
"node_modules/ejs": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ejs/-/ejs-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-krvQtxc0btwSm/nvnt1UpnaFDFVJpJ0fdckmALpCgShsr/iGYHTnJiUliZTgmzq/UxTX33TtOQVKaNigMQp/6Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"jake": "^10.9.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"ejs": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.616",
|
"version": "1.4.616",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz",
|
||||||
@@ -6790,6 +6818,36 @@
|
|||||||
"moment": "^2.29.1"
|
"moment": "^2.29.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/filelist": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"minimatch": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/filelist/node_modules/brace-expansion": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/filelist/node_modules/minimatch": {
|
||||||
|
"version": "5.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||||
|
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@@ -8045,6 +8103,23 @@
|
|||||||
"@pkgjs/parseargs": "^0.11.0"
|
"@pkgjs/parseargs": "^0.11.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jake": {
|
||||||
|
"version": "10.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
|
||||||
|
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"async": "^3.2.6",
|
||||||
|
"filelist": "^1.0.4",
|
||||||
|
"picocolors": "^1.1.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"jake": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/javascript-natural-sort": {
|
"node_modules/javascript-natural-sort": {
|
||||||
"version": "0.7.1",
|
"version": "0.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||||
@@ -8510,6 +8585,20 @@
|
|||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/maxmind": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/maxmind/-/maxmind-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-1lcH2kMjbBpCFhuHaMU32vz8CuOsKttRcWMQyXvtlklopCzN7NNHSVR/h9RYa8JPuFTGmkn2vYARm+7cIGuqDw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mmdb-lib": "3.0.2",
|
||||||
|
"tiny-lru": "11.4.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12",
|
||||||
|
"npm": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mdurl": {
|
"node_modules/mdurl": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||||
@@ -8653,6 +8742,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/mmdb-lib": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-7e87vk0DdWT647wjcfEtWeMtjm+zVGqNohN/aeIymbUfjHQ2T4Sx5kM+1irVDBSloNC3CkGKxswdMoo8yhqTDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10",
|
||||||
|
"npm": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/moment": {
|
"node_modules/moment": {
|
||||||
"version": "2.30.1",
|
"version": "2.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
@@ -8991,6 +9090,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/obscenity": {
|
||||||
|
"version": "0.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/obscenity/-/obscenity-0.4.6.tgz",
|
||||||
|
"integrity": "sha512-pHk7kNN7j3L3zGhhGnwxjvXIGsPpLrcZl2r58fqWh/V/rH6b/dafscj2sMmAY+A/9/wPsocLmimgGk2DKeKsFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/on-finished": {
|
"node_modules/on-finished": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
@@ -10734,6 +10842,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-lru": {
|
||||||
|
"version": "11.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.4.7.tgz",
|
||||||
|
"integrity": "sha512-w/Te7uMUVeH0CR8vZIjr+XiN41V+30lkDdK+NRIDCUYKKuL9VcmaUEmaPISuwGhLlrTGh5yu18lENtR9axSxYw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.2.tgz",
|
||||||
@@ -13920,6 +14037,14 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@maxmind/geoip2-node": {
|
||||||
|
"version": "6.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@maxmind/geoip2-node/-/geoip2-node-6.3.4.tgz",
|
||||||
|
"integrity": "sha512-BTRFHCX7Uie4wVSPXsWQfg0EVl4eGZgLCts0BTKAP+Eiyt1zmF2UPyuUZkaj0R59XSDYO+84o1THAtaenUoQYg==",
|
||||||
|
"requires": {
|
||||||
|
"maxmind": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@modyfi/vite-plugin-yaml": {
|
"@modyfi/vite-plugin-yaml": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@modyfi/vite-plugin-yaml/-/vite-plugin-yaml-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@modyfi/vite-plugin-yaml/-/vite-plugin-yaml-1.1.0.tgz",
|
||||||
@@ -14759,9 +14884,9 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "3.2.5",
|
"version": "3.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||||
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
|
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="
|
||||||
},
|
},
|
||||||
"asynckit": {
|
"asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
@@ -15518,6 +15643,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||||
},
|
},
|
||||||
|
"ejs": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ejs/-/ejs-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-krvQtxc0btwSm/nvnt1UpnaFDFVJpJ0fdckmALpCgShsr/iGYHTnJiUliZTgmzq/UxTX33TtOQVKaNigMQp/6Q==",
|
||||||
|
"requires": {
|
||||||
|
"jake": "^10.9.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.4.616",
|
"version": "1.4.616",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz",
|
||||||
@@ -16434,6 +16567,32 @@
|
|||||||
"moment": "^2.29.1"
|
"moment": "^2.29.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"filelist": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
|
||||||
|
"requires": {
|
||||||
|
"minimatch": "^5.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||||
|
"requires": {
|
||||||
|
"balanced-match": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimatch": {
|
||||||
|
"version": "5.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||||
|
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||||
|
"requires": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"fill-range": {
|
"fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@@ -17277,6 +17436,16 @@
|
|||||||
"@pkgjs/parseargs": "^0.11.0"
|
"@pkgjs/parseargs": "^0.11.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jake": {
|
||||||
|
"version": "10.9.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
|
||||||
|
"integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
|
||||||
|
"requires": {
|
||||||
|
"async": "^3.2.6",
|
||||||
|
"filelist": "^1.0.4",
|
||||||
|
"picocolors": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"javascript-natural-sort": {
|
"javascript-natural-sort": {
|
||||||
"version": "0.7.1",
|
"version": "0.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||||
@@ -17637,6 +17806,15 @@
|
|||||||
"typed-function": "^4.1.1"
|
"typed-function": "^4.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"maxmind": {
|
||||||
|
"version": "5.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/maxmind/-/maxmind-5.0.5.tgz",
|
||||||
|
"integrity": "sha512-1lcH2kMjbBpCFhuHaMU32vz8CuOsKttRcWMQyXvtlklopCzN7NNHSVR/h9RYa8JPuFTGmkn2vYARm+7cIGuqDw==",
|
||||||
|
"requires": {
|
||||||
|
"mmdb-lib": "3.0.2",
|
||||||
|
"tiny-lru": "11.4.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mdurl": {
|
"mdurl": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||||
@@ -17738,6 +17916,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||||
},
|
},
|
||||||
|
"mmdb-lib": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/mmdb-lib/-/mmdb-lib-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-7e87vk0DdWT647wjcfEtWeMtjm+zVGqNohN/aeIymbUfjHQ2T4Sx5kM+1irVDBSloNC3CkGKxswdMoo8yhqTDg=="
|
||||||
|
},
|
||||||
"moment": {
|
"moment": {
|
||||||
"version": "2.30.1",
|
"version": "2.30.1",
|
||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||||
@@ -17989,6 +18172,11 @@
|
|||||||
"es-object-atoms": "^1.0.0"
|
"es-object-atoms": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"obscenity": {
|
||||||
|
"version": "0.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/obscenity/-/obscenity-0.4.6.tgz",
|
||||||
|
"integrity": "sha512-pHk7kNN7j3L3zGhhGnwxjvXIGsPpLrcZl2r58fqWh/V/rH6b/dafscj2sMmAY+A/9/wPsocLmimgGk2DKeKsFQ=="
|
||||||
|
},
|
||||||
"on-finished": {
|
"on-finished": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
@@ -19227,6 +19415,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
||||||
},
|
},
|
||||||
|
"tiny-lru": {
|
||||||
|
"version": "11.4.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.4.7.tgz",
|
||||||
|
"integrity": "sha512-w/Te7uMUVeH0CR8vZIjr+XiN41V+30lkDdK+NRIDCUYKKuL9VcmaUEmaPISuwGhLlrTGh5yu18lENtR9axSxYw=="
|
||||||
|
},
|
||||||
"tmp": {
|
"tmp": {
|
||||||
"version": "0.2.2",
|
"version": "0.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.2.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npm run server:dev",
|
"dev": "node ./src/app",
|
||||||
"prod": "npm run build && npm run server:prod",
|
"prod": "npm run build && npm run server:prod",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"server:dev": "node ./src/app",
|
"server:dev": "node ./src/app",
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
"@floating-ui/dom": "^1.5.3",
|
"@floating-ui/dom": "^1.5.3",
|
||||||
"@floating-ui/vue": "^1.0.2",
|
"@floating-ui/vue": "^1.0.2",
|
||||||
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
|
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
|
||||||
|
"@maxmind/geoip2-node": "^6.3.4",
|
||||||
"@resvg/resvg-js": "^2.6.0",
|
"@resvg/resvg-js": "^2.6.0",
|
||||||
"@toycode/markdown-it-class": "^1.2.4",
|
"@toycode/markdown-it-class": "^1.2.4",
|
||||||
"@vitejs/plugin-vue": "^4.5.2",
|
"@vitejs/plugin-vue": "^4.5.2",
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
"cron": "^3.1.6",
|
"cron": "^3.1.6",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"date-fns": "^3.0.0",
|
"date-fns": "^3.0.0",
|
||||||
|
"ejs": "^4.0.1",
|
||||||
"error-stack-parser": "^2.1.4",
|
"error-stack-parser": "^2.1.4",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
@@ -52,6 +54,7 @@
|
|||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"object.omit": "^3.0.0",
|
"object.omit": "^3.0.0",
|
||||||
|
"obscenity": "^0.4.6",
|
||||||
"path-to-regexp": "^6.2.1",
|
"path-to-regexp": "^6.2.1",
|
||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"redis": "^4.6.12",
|
"redis": "^4.6.12",
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import { fetchEntities } from '#/src/entities.js';
|
|||||||
export async function onBeforeRender(pageContext) {
|
export async function onBeforeRender(pageContext) {
|
||||||
const networks = await fetchEntities(pageContext.urlParsed.search.q
|
const networks = await fetchEntities(pageContext.urlParsed.search.q
|
||||||
? { query: pageContext.urlParsed.search.q }
|
? { query: pageContext.urlParsed.search.q }
|
||||||
: { type: 'primary' });
|
: { type: 'primary' }, {
|
||||||
|
restriction: pageContext.restriction,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pageContext: {
|
pageContext: {
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ async function fetchReleases(pageContext, entityId) {
|
|||||||
page: Number(pageContext.routeParams.page) || 1,
|
page: Number(pageContext.routeParams.page) || 1,
|
||||||
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
||||||
aggregate: true,
|
aggregate: true,
|
||||||
}, pageContext.user);
|
}, pageContext.user, {
|
||||||
|
restriction: pageContext.restriction,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetchScenes(await curateScenesQuery({
|
return fetchScenes(await curateScenesQuery({
|
||||||
@@ -32,7 +34,9 @@ async function fetchReleases(pageContext, entityId) {
|
|||||||
page: Number(pageContext.routeParams.page) || 1,
|
page: Number(pageContext.routeParams.page) || 1,
|
||||||
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
||||||
aggregate: true,
|
aggregate: true,
|
||||||
}, pageContext.user);
|
}, pageContext.user, {
|
||||||
|
restriction: pageContext.restriction,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function onBeforeRender(pageContext) {
|
export async function onBeforeRender(pageContext) {
|
||||||
@@ -47,7 +51,9 @@ export async function onBeforeRender(pageContext) {
|
|||||||
[entity],
|
[entity],
|
||||||
entityReleases,
|
entityReleases,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
fetchEntitiesById([Number(entityId)], { includeChildren: true }, pageContext.user),
|
fetchEntitiesById([Number(entityId)], { includeChildren: true }, pageContext.user, {
|
||||||
|
restriction: pageContext.restriction,
|
||||||
|
}),
|
||||||
fetchReleases(pageContext, entityId),
|
fetchReleases(pageContext, entityId),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ export async function onBeforeRender(pageContext) {
|
|||||||
page: Number(pageContext.routeParams.page) || 1,
|
page: Number(pageContext.routeParams.page) || 1,
|
||||||
limit: Number(pageContext.urlParsed.search.limit) || 50,
|
limit: Number(pageContext.urlParsed.search.limit) || 50,
|
||||||
dedupe: true,
|
dedupe: true,
|
||||||
}, pageContext.user);
|
}, pageContext.user, {
|
||||||
|
restriction: pageContext.restriction,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pageContext: {
|
pageContext: {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ function getTitle(movie) {
|
|||||||
|
|
||||||
export async function onBeforeRender(pageContext) {
|
export async function onBeforeRender(pageContext) {
|
||||||
const [[movie], movieScenes] = await Promise.all([
|
const [[movie], movieScenes] = await Promise.all([
|
||||||
fetchMoviesById([Number(pageContext.routeParams.movieId)], pageContext.user),
|
fetchMoviesById([Number(pageContext.routeParams.movieId)], pageContext.user, { restriction: pageContext.restriction }),
|
||||||
fetchScenes(await curateScenesQuery({
|
fetchScenes(await curateScenesQuery({
|
||||||
...pageContext.urlQuery,
|
...pageContext.urlQuery,
|
||||||
scope: 'oldest',
|
scope: 'oldest',
|
||||||
@@ -27,7 +27,9 @@ export async function onBeforeRender(pageContext) {
|
|||||||
page: Number(pageContext.routeParams.page) || 1,
|
page: Number(pageContext.routeParams.page) || 1,
|
||||||
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
||||||
aggregate: true,
|
aggregate: true,
|
||||||
}, pageContext.user),
|
}, pageContext.user, {
|
||||||
|
restriction: pageContext.restriction,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!movie) {
|
if (!movie) {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export async function onBeforeRender(pageContext) {
|
|||||||
includeAssets: true,
|
includeAssets: true,
|
||||||
includePartOf: true,
|
includePartOf: true,
|
||||||
actorStashes: true,
|
actorStashes: true,
|
||||||
|
restriction: pageContext.restriction,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [campaigns, tagIds] = await Promise.all([
|
const [campaigns, tagIds] = await Promise.all([
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ export async function onBeforeRender(pageContext) {
|
|||||||
limit: Number(pageContext.urlParsed.search.limit) || 29,
|
limit: Number(pageContext.urlParsed.search.limit) || 29,
|
||||||
aggregate: true,
|
aggregate: true,
|
||||||
dedupe: true,
|
dedupe: true,
|
||||||
}, pageContext.user),
|
}, pageContext.user, {
|
||||||
|
restriction: pageContext.restriction,
|
||||||
|
}),
|
||||||
getRandomCampaigns([
|
getRandomCampaigns([
|
||||||
{ minRatio: 0.75, maxRatio: 1.25 },
|
{ minRatio: 0.75, maxRatio: 1.25 },
|
||||||
{ minRatio: 1.5 },
|
{ minRatio: 1.5 },
|
||||||
|
|||||||
@@ -75,8 +75,8 @@
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="tag.poster"
|
v-if="tag.poster"
|
||||||
:src="`/${tag.poster.thumbnail}`"
|
:src="getPath(tag.poster, 'thumbnail')"
|
||||||
:style="{ 'background-image': `url(/${tag.poster.lazy})` }"
|
:style="{ 'background-image': `url(/${getPath(tag.poster, 'lazy')})` }"
|
||||||
:title="tag.poster.comment"
|
:title="tag.poster.comment"
|
||||||
class="thumb"
|
class="thumb"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@@ -111,6 +111,7 @@ import { ref, onMounted, inject } from 'vue';
|
|||||||
|
|
||||||
import navigate from '#/src/navigate.js';
|
import navigate from '#/src/navigate.js';
|
||||||
import events from '#/src/events.js';
|
import events from '#/src/events.js';
|
||||||
|
import getPath from '#/src/get-path.js';
|
||||||
|
|
||||||
import Logo from '#/components/tags/logo.vue';
|
import Logo from '#/components/tags/logo.vue';
|
||||||
|
|
||||||
@@ -324,6 +325,7 @@ onMounted(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
aspect-ratio: 5/3;
|
||||||
border-radius: .25rem;
|
border-radius: .25rem;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { fetchTags, fetchTagsById } from '#/src/tags.js';
|
import { fetchTags, fetchTagsById } from '#/src/tags.js';
|
||||||
|
import { censor } from '#/src/censor.js';
|
||||||
|
|
||||||
const tagSlugs = {
|
const tagSlugs = {
|
||||||
popular: [
|
popular: [
|
||||||
@@ -136,13 +137,13 @@ export async function onBeforeRender(pageContext) {
|
|||||||
return searchTags(pageContext);
|
return searchTags(pageContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tags = await fetchTagsById(Object.values(tagSlugs).flat());
|
const tags = await fetchTagsById(Object.values(tagSlugs).flat(), {}, pageContext.user, { restriction: pageContext.restriction });
|
||||||
|
|
||||||
const filteredTags = tags.filter((tag) => !pageContext.tagFilter.includes(tag.name) && !pageContext.tagFilter.includes(tag.slug));
|
const filteredTags = tags.filter((tag) => !pageContext.tagFilter.includes(tag.name) && !pageContext.tagFilter.includes(tag.slug));
|
||||||
const tagsBySlug = Object.fromEntries(filteredTags.map((tag) => [tag.slug, tag]));
|
const tagsBySlug = Object.fromEntries(filteredTags.map((tag) => [tag.slug, tag]));
|
||||||
|
|
||||||
const tagShowcase = Object.fromEntries(Object.entries(tagSlugs).map(([category, categorySlugs]) => [
|
const tagShowcase = Object.fromEntries(Object.entries(tagSlugs).map(([category, categorySlugs]) => [
|
||||||
category,
|
censor(category, pageContext.restriction),
|
||||||
categorySlugs.map((slug) => tagsBySlug[slug]).filter(Boolean),
|
categorySlugs.map((slug) => tagsBySlug[slug]).filter(Boolean),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ async function fetchReleases(pageContext) {
|
|||||||
page: Number(pageContext.routeParams.page) || 1,
|
page: Number(pageContext.routeParams.page) || 1,
|
||||||
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
||||||
aggregate: true,
|
aggregate: true,
|
||||||
}, pageContext.user);
|
}, pageContext.user, {
|
||||||
|
restriction: pageContext.restriction,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetchScenes(await curateScenesQuery({
|
return fetchScenes(await curateScenesQuery({
|
||||||
@@ -33,14 +35,16 @@ async function fetchReleases(pageContext) {
|
|||||||
page: Number(pageContext.routeParams.page) || 1,
|
page: Number(pageContext.routeParams.page) || 1,
|
||||||
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
limit: Number(pageContext.urlParsed.search.limit) || 30,
|
||||||
aggregate: true,
|
aggregate: true,
|
||||||
}, pageContext.user);
|
}, pageContext.user, {
|
||||||
|
restriction: pageContext.restriction,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function onBeforeRender(pageContext) {
|
export async function onBeforeRender(pageContext) {
|
||||||
const tagSlug = pageContext.routeParams.tagSlug;
|
const tagSlug = pageContext.routeParams.tagSlug;
|
||||||
|
|
||||||
const [[tag], tagReleases, campaigns] = await Promise.all([
|
const [[tag], tagReleases, campaigns] = await Promise.all([
|
||||||
fetchTagsById([tagSlug], {}, pageContext.user),
|
fetchTagsById([tagSlug], {}, pageContext.user, { restriction: pageContext.restriction }),
|
||||||
fetchReleases(pageContext),
|
fetchReleases(pageContext),
|
||||||
getRandomCampaigns([
|
getRandomCampaigns([
|
||||||
{ tagSlugs: [tagSlug], minRatio: 0.75, maxRatio: 1.25 },
|
{ tagSlugs: [tagSlug], minRatio: 0.75, maxRatio: 1.25 },
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export async function onBeforeRender(pageContext) {
|
|||||||
limit: Number(pageContext.urlParsed.search.limit) || 29,
|
limit: Number(pageContext.urlParsed.search.limit) || 29,
|
||||||
aggregate: withQuery,
|
aggregate: withQuery,
|
||||||
dedupe: true,
|
dedupe: true,
|
||||||
}, pageContext.user),
|
}, pageContext.user, { restriction: pageContext.restriction }),
|
||||||
getRandomCampaigns([
|
getRandomCampaigns([
|
||||||
{ minRatio: 2.0, maxRatio: 5 },
|
{ minRatio: 2.0, maxRatio: 5 },
|
||||||
{ minRatio: 0.75, maxRatio: 1.25 },
|
{ minRatio: 0.75, maxRatio: 1.25 },
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ export default {
|
|||||||
'assets',
|
'assets',
|
||||||
'campaigns',
|
'campaigns',
|
||||||
'meta',
|
'meta',
|
||||||
|
'restriction',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -122,7 +122,10 @@ export function curateActor(actor, context = {}) {
|
|||||||
state: actor.residence_state,
|
state: actor.residence_state,
|
||||||
},
|
},
|
||||||
agency: actor.agency,
|
agency: actor.agency,
|
||||||
avatar: curateMedia(actor.avatar),
|
avatar: actor.avatar && curateMedia({
|
||||||
|
...actor.avatar,
|
||||||
|
sfw_media: actor.sfw_avatar,
|
||||||
|
}),
|
||||||
socials: context.socials?.map((social) => ({
|
socials: context.socials?.map((social) => ({
|
||||||
id: social.id,
|
id: social.id,
|
||||||
url: social.url,
|
url: social.url,
|
||||||
@@ -214,18 +217,21 @@ export async function fetchActorsById(actorIds, options = {}, reqUser) {
|
|||||||
'residence_countries.alpha2 as residence_country_alpha2',
|
'residence_countries.alpha2 as residence_country_alpha2',
|
||||||
knex.raw('COALESCE(residence_countries.alias, residence_countries.name) as residence_country_name'),
|
knex.raw('COALESCE(residence_countries.alias, residence_countries.name) as residence_country_name'),
|
||||||
knex.raw('row_to_json(entities) as entity'),
|
knex.raw('row_to_json(entities) as entity'),
|
||||||
|
knex.raw('row_to_json(sfw_media) as sfw_avatar'),
|
||||||
)
|
)
|
||||||
.leftJoin('actors_meta', 'actors_meta.actor_id', 'actors.id')
|
.leftJoin('actors_meta', 'actors_meta.actor_id', 'actors.id')
|
||||||
.leftJoin('countries as birth_countries', 'birth_countries.alpha2', 'actors.birth_country_alpha2')
|
.leftJoin('countries as birth_countries', 'birth_countries.alpha2', 'actors.birth_country_alpha2')
|
||||||
.leftJoin('countries as residence_countries', 'residence_countries.alpha2', 'actors.residence_country_alpha2')
|
.leftJoin('countries as residence_countries', 'residence_countries.alpha2', 'actors.residence_country_alpha2')
|
||||||
.leftJoin('media as avatars', 'avatars.id', 'actors.avatar_media_id')
|
.leftJoin('media as avatars', 'avatars.id', 'actors.avatar_media_id')
|
||||||
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'avatars.sfw_media_id')
|
||||||
.leftJoin('entities', 'entities.id', 'actors.entity_id')
|
.leftJoin('entities', 'entities.id', 'actors.entity_id')
|
||||||
.whereIn('actors.id', actorIds)
|
.whereIn('actors.id', actorIds)
|
||||||
.modify((builder) => {
|
.modify((builder) => {
|
||||||
if (options.order) {
|
if (options.order) {
|
||||||
builder.orderBy(...options.order);
|
builder.orderBy(...options.order);
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
|
.groupBy('actors.id', 'avatars.id', 'sfw_media.id', 'entities.id', 'actors_meta.stashed', 'birth_countries.alpha2', 'residence_countries.alpha2'),
|
||||||
knex('actors_profiles')
|
knex('actors_profiles')
|
||||||
.select(
|
.select(
|
||||||
'actors_profiles.*',
|
'actors_profiles.*',
|
||||||
@@ -245,10 +251,12 @@ export async function fetchActorsById(actorIds, options = {}, reqUser) {
|
|||||||
'media.*',
|
'media.*',
|
||||||
'actors_avatars.actor_id',
|
'actors_avatars.actor_id',
|
||||||
knex.raw('json_agg(actors_avatars.profile_id) as profile_ids'),
|
knex.raw('json_agg(actors_avatars.profile_id) as profile_ids'),
|
||||||
|
knex.raw('row_to_json(sfw_media) as sfw_media'),
|
||||||
)
|
)
|
||||||
.whereIn('actor_id', actorIds)
|
.whereIn('actor_id', actorIds)
|
||||||
.leftJoin('media', 'media.id', 'actors_avatars.media_id')
|
.leftJoin('media', 'media.id', 'actors_avatars.media_id')
|
||||||
.groupBy('media.id', 'actors_avatars.actor_id')
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
|
||||||
|
.groupBy('media.id', 'sfw_media.id', 'actors_avatars.actor_id')
|
||||||
.orderBy(knex.raw('max(actors_avatars.created_at)'), 'desc'),
|
.orderBy(knex.raw('max(actors_avatars.created_at)'), 'desc'),
|
||||||
knex('actors_socials')
|
knex('actors_socials')
|
||||||
.whereIn('actor_id', actorIds),
|
.whereIn('actor_id', actorIds),
|
||||||
|
|||||||
10
src/argv.js
10
src/argv.js
@@ -1,11 +1,15 @@
|
|||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
|
|
||||||
const { argv } = yargs()
|
const { argv } = yargs(process.argv.slice(2))
|
||||||
.command('npm start')
|
|
||||||
.option('debug', {
|
.option('debug', {
|
||||||
describe: 'Show error stack traces',
|
describe: 'Show error stack traces and inputs',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: process.env.NODE_ENV === 'development',
|
default: process.env.NODE_ENV === 'development',
|
||||||
|
})
|
||||||
|
.option('ip', {
|
||||||
|
describe: 'Mock IP address',
|
||||||
|
type: 'string',
|
||||||
|
default: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default argv;
|
export default argv;
|
||||||
|
|||||||
55
src/censor.js
Normal file
55
src/censor.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import config from 'config';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TextCensor,
|
||||||
|
RegExpMatcher,
|
||||||
|
englishDataset,
|
||||||
|
englishRecommendedTransformers,
|
||||||
|
DataSet,
|
||||||
|
pattern,
|
||||||
|
// asteriskCensorStrategy,
|
||||||
|
} from 'obscenity';
|
||||||
|
|
||||||
|
const textCensor = new TextCensor();
|
||||||
|
|
||||||
|
// built-in asterisk strategy replaces entire word
|
||||||
|
textCensor.setStrategy(({
|
||||||
|
input,
|
||||||
|
startIndex,
|
||||||
|
endIndex,
|
||||||
|
matchLength,
|
||||||
|
}) => {
|
||||||
|
if (matchLength <= 2) {
|
||||||
|
return '*'.repeat(matchLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${input.at(startIndex)}${'*'.repeat(matchLength - 2)}${input.at(endIndex)}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const dataset = new DataSet().addAll(englishDataset);
|
||||||
|
|
||||||
|
config.restrictions.censors.forEach((word) => {
|
||||||
|
dataset.addPhrase((phrase) => phrase
|
||||||
|
.setMetadata({ originalWord: word })
|
||||||
|
.addPattern(pattern`${word}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
const matcher = new RegExpMatcher({
|
||||||
|
...dataset.build(),
|
||||||
|
...englishRecommendedTransformers,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function censor(text, restriction) {
|
||||||
|
if (!text) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!restriction) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
const censorMatches = matcher.getAllMatches(text);
|
||||||
|
const censoredText = textCensor.applyTo(text, censorMatches);
|
||||||
|
|
||||||
|
return censoredText;
|
||||||
|
}
|
||||||
@@ -3,29 +3,36 @@ import redis from './redis.js';
|
|||||||
import initLogger from './logger.js';
|
import initLogger from './logger.js';
|
||||||
import entityPrefixes from './entities-prefixes.js';
|
import entityPrefixes from './entities-prefixes.js';
|
||||||
import { getAffiliateEntityUrl } from './affiliates.js';
|
import { getAffiliateEntityUrl } from './affiliates.js';
|
||||||
|
import { censor } from './censor.js';
|
||||||
|
|
||||||
const logger = initLogger();
|
const logger = initLogger();
|
||||||
|
|
||||||
export function curateEntity(entity, context) {
|
export function curateEntity(entity, context = {}) {
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const curatedEntity = {
|
const curatedEntity = {
|
||||||
id: entity.id,
|
id: entity.id,
|
||||||
name: entity.name,
|
name: censor(entity.name, context.restriction),
|
||||||
slug: entity.slug,
|
slug: entity.slug,
|
||||||
type: entity.type,
|
type: entity.type,
|
||||||
url: entity.url,
|
url: entity.url,
|
||||||
isIndependent: entity.independent,
|
isIndependent: entity.independent,
|
||||||
hasLogo: entity.has_logo,
|
hasLogo: context.restriction ? false : entity.has_logo,
|
||||||
parent: curateEntity(entity.parent, context),
|
parent: curateEntity(entity.parent, context),
|
||||||
tags: context?.tags?.map((tag) => ({
|
tags: context?.tags?.map((tag) => ({
|
||||||
id: tag.id,
|
id: tag.id,
|
||||||
name: tag.name,
|
name: tag.name,
|
||||||
slug: tag.slug,
|
slug: tag.slug,
|
||||||
})),
|
})),
|
||||||
children: context?.children?.filter((child) => child.parent_id === entity.id).map((child) => curateEntity({ ...child, parent: entity }, { parent: entity })) || [],
|
children: context?.children?.filter((child) => child.parent_id === entity.id).map((child) => curateEntity({
|
||||||
|
...child,
|
||||||
|
parent: entity,
|
||||||
|
}, {
|
||||||
|
parent: entity,
|
||||||
|
restriction: context.restriction,
|
||||||
|
})) || [],
|
||||||
affiliate: entity.affiliate ? {
|
affiliate: entity.affiliate ? {
|
||||||
id: entity.affiliate.id,
|
id: entity.affiliate.id,
|
||||||
entityId: entity.affiliate.entity_id,
|
entityId: entity.affiliate.entity_id,
|
||||||
@@ -44,7 +51,7 @@ export function curateEntity(entity, context) {
|
|||||||
return curatedEntity;
|
return curatedEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchEntities(options = {}) {
|
export async function fetchEntities(options = {}, context) {
|
||||||
const entities = await knex('entities')
|
const entities = await knex('entities')
|
||||||
.select('entities.*', knex.raw('row_to_json(parents) as parent'))
|
.select('entities.*', knex.raw('row_to_json(parents) as parent'))
|
||||||
.modify((builder) => {
|
.modify((builder) => {
|
||||||
@@ -93,11 +100,12 @@ export async function fetchEntities(options = {}) {
|
|||||||
.whereIn('entity_id', entities.map((entity) => entity.id));
|
.whereIn('entity_id', entities.map((entity) => entity.id));
|
||||||
|
|
||||||
return entities.map((entityEntry) => curateEntity(entityEntry, {
|
return entities.map((entityEntry) => curateEntity(entityEntry, {
|
||||||
|
...context,
|
||||||
tags: entitiesTags.filter((tag) => tag.entity_id === entityEntry.id),
|
tags: entitiesTags.filter((tag) => tag.entity_id === entityEntry.id),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchEntitiesById(entityIds, options = {}, reqUser) {
|
export async function fetchEntitiesById(entityIds, options = {}, reqUser, context) {
|
||||||
const [entities, children, tags, alerts] = await Promise.all([
|
const [entities, children, tags, alerts] = await Promise.all([
|
||||||
knex('entities')
|
knex('entities')
|
||||||
.select(
|
.select(
|
||||||
@@ -136,6 +144,7 @@ export async function fetchEntitiesById(entityIds, options = {}, reqUser) {
|
|||||||
|
|
||||||
if (options.order) {
|
if (options.order) {
|
||||||
return entities.map((entityEntry) => curateEntity(entityEntry, {
|
return entities.map((entityEntry) => curateEntity(entityEntry, {
|
||||||
|
...context,
|
||||||
append: options.append,
|
append: options.append,
|
||||||
children: children.filter((channel) => channel.parent_id === entityEntry.id),
|
children: children.filter((channel) => channel.parent_id === entityEntry.id),
|
||||||
alerts: alerts.filter((alert) => alert.entity_id === entityEntry.id),
|
alerts: alerts.filter((alert) => alert.entity_id === entityEntry.id),
|
||||||
@@ -151,6 +160,7 @@ export async function fetchEntitiesById(entityIds, options = {}, reqUser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return curateEntity(entity, {
|
return curateEntity(entity, {
|
||||||
|
...context,
|
||||||
append: options.append,
|
append: options.append,
|
||||||
children: children.filter((channel) => channel.parent_id === entity.id),
|
children: children.filter((channel) => channel.parent_id === entity.id),
|
||||||
tags: tags.filter((tag) => tag.entity_id === entity.id),
|
tags: tags.filter((tag) => tag.entity_id === entity.id),
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
// import config from 'config';
|
|
||||||
import { pageContext } from '../renderer/usePageContext.js';
|
import { pageContext } from '../renderer/usePageContext.js';
|
||||||
|
|
||||||
function getBasePath(media, type, options) {
|
function getBasePath(media, options) {
|
||||||
/*
|
if (pageContext.restriction) {
|
||||||
if (store.state.ui.sfw) {
|
return pageContext.env.media.assetPath;
|
||||||
return config.media.assetPath;
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
if (media.isS3) {
|
if (media.isS3) {
|
||||||
return options.s3Path;
|
return options.s3Path;
|
||||||
@@ -20,15 +17,13 @@ function getBasePath(media, type, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getFilename(media, type, options) {
|
function getFilename(media, type, options) {
|
||||||
/*
|
if (pageContext.restriction && type && !options?.original) {
|
||||||
if (store.state.ui.sfw && type && !options?.original) {
|
return media.sfw?.[type];
|
||||||
return media.sfw[type];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store.state.ui.sfw) {
|
if (pageContext.restriction) {
|
||||||
return media.sfw.path;
|
return media.sfw?.path;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
if (type && !options?.original) {
|
if (type && !options?.original) {
|
||||||
return media[type];
|
return media[type];
|
||||||
@@ -42,7 +37,7 @@ export default function getPath(media, type, options) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = getBasePath(media, type, { ...pageContext.env.media, ...options });
|
const path = getBasePath(media, { ...pageContext.env.media, ...options });
|
||||||
const filename = getFilename(media, type, { ...pageContext.env.media, ...options });
|
const filename = getFilename(media, type, { ...pageContext.env.media, ...options });
|
||||||
|
|
||||||
return `${path}/${filename}`;
|
return `${path}/${filename}`;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export function curateMedia(media, context = {}) {
|
|||||||
parent: media.entity_parent,
|
parent: media.entity_parent,
|
||||||
}),
|
}),
|
||||||
type: context.type || null,
|
type: context.type || null,
|
||||||
|
sfw: curateMedia(media.sfw_media),
|
||||||
isRestricted: context.isRestricted,
|
isRestricted: context.isRestricted,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,29 +8,30 @@ import { curateMedia } from './media.js';
|
|||||||
import { fetchTagsById } from './tags.js';
|
import { fetchTagsById } from './tags.js';
|
||||||
import { fetchEntitiesById } from './entities.js';
|
import { fetchEntitiesById } from './entities.js';
|
||||||
import { curateStash } from './stashes.js';
|
import { curateStash } from './stashes.js';
|
||||||
|
import { censor } from './censor.js';
|
||||||
import escape from '../utils/escape-manticore.js';
|
import escape from '../utils/escape-manticore.js';
|
||||||
import promiseProps from '../utils/promise-props.js';
|
import promiseProps from '../utils/promise-props.js';
|
||||||
|
|
||||||
function curateMovie(rawMovie, assets) {
|
function curateMovie(rawMovie, assets, context = {}) {
|
||||||
if (!rawMovie) {
|
if (!rawMovie) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: rawMovie.id,
|
id: rawMovie.id,
|
||||||
title: rawMovie.title,
|
title: censor(rawMovie.title, context.restriction),
|
||||||
slug: rawMovie.slug,
|
slug: rawMovie.slug,
|
||||||
url: rawMovie.url,
|
url: rawMovie.url,
|
||||||
date: rawMovie.date,
|
date: rawMovie.date,
|
||||||
datePrecision: rawMovie.date_precision,
|
datePrecision: rawMovie.date_precision,
|
||||||
createdAt: rawMovie.created_at,
|
createdAt: rawMovie.created_at,
|
||||||
effectiveDate: rawMovie.effective_date,
|
effectiveDate: rawMovie.effective_date,
|
||||||
description: rawMovie.description,
|
description: censor(rawMovie.description, context.restriction),
|
||||||
duration: rawMovie.duration,
|
duration: rawMovie.duration,
|
||||||
channel: {
|
channel: {
|
||||||
id: assets.channel.id,
|
id: assets.channel.id,
|
||||||
slug: assets.channel.slug,
|
slug: assets.channel.slug,
|
||||||
name: assets.channel.name,
|
name: censor(assets.channel.name, context.restriction),
|
||||||
type: assets.channel.type,
|
type: assets.channel.type,
|
||||||
isIndependent: assets.channel.independent,
|
isIndependent: assets.channel.independent,
|
||||||
hasLogo: assets.channel.has_logo,
|
hasLogo: assets.channel.has_logo,
|
||||||
@@ -38,7 +39,7 @@ function curateMovie(rawMovie, assets) {
|
|||||||
network: assets.channel.network_id ? {
|
network: assets.channel.network_id ? {
|
||||||
id: assets.channel.network_id,
|
id: assets.channel.network_id,
|
||||||
slug: assets.channel.network_slug,
|
slug: assets.channel.network_slug,
|
||||||
name: assets.channel.network_name,
|
name: censor(assets.channel.network_name, context.restriction),
|
||||||
type: assets.channel.network_type,
|
type: assets.channel.network_type,
|
||||||
hasLogo: assets.channel.has_logo,
|
hasLogo: assets.channel.has_logo,
|
||||||
} : null,
|
} : null,
|
||||||
@@ -51,7 +52,7 @@ function curateMovie(rawMovie, assets) {
|
|||||||
tags: assets.tags.map((tag) => ({
|
tags: assets.tags.map((tag) => ({
|
||||||
id: tag.id,
|
id: tag.id,
|
||||||
slug: tag.slug,
|
slug: tag.slug,
|
||||||
name: tag.name,
|
name: censor(tag.name, context.restriction),
|
||||||
})),
|
})),
|
||||||
// poster: curateMedia(assets.poster),
|
// poster: curateMedia(assets.poster),
|
||||||
covers: assets.covers.map((cover) => curateMedia(cover, { type: 'cover' })),
|
covers: assets.covers.map((cover) => curateMedia(cover, { type: 'cover' })),
|
||||||
@@ -64,7 +65,7 @@ function curateMovie(rawMovie, assets) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchMoviesById(movieIds, reqUser) {
|
export async function fetchMoviesById(movieIds, reqUser, context) {
|
||||||
const {
|
const {
|
||||||
movies,
|
movies,
|
||||||
channels,
|
channels,
|
||||||
@@ -123,20 +124,25 @@ export async function fetchMoviesById(movieIds, reqUser) {
|
|||||||
.leftJoin('tags', 'tags.id', 'releases_tags.tag_id')
|
.leftJoin('tags', 'tags.id', 'releases_tags.tag_id')
|
||||||
.orderBy('priority', 'desc'),
|
.orderBy('priority', 'desc'),
|
||||||
covers: knex('movies_covers')
|
covers: knex('movies_covers')
|
||||||
|
.select('media.*', 'movies_covers.movie_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
|
||||||
.whereIn('movie_id', movieIds)
|
.whereIn('movie_id', movieIds)
|
||||||
.leftJoin('media', 'media.id', 'movies_covers.media_id')
|
.leftJoin('media', 'media.id', 'movies_covers.media_id')
|
||||||
.orderBy('media.index'),
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
|
||||||
photos: knex.transaction(async (trx) => {
|
.orderBy('media.index')
|
||||||
|
.groupBy('media.id', 'movies_covers.movie_id', 'sfw_media.id'),
|
||||||
|
photos: context.restriction ? [] : knex.transaction(async (trx) => {
|
||||||
if (reqUser) {
|
if (reqUser) {
|
||||||
await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id }));
|
await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id }));
|
||||||
}
|
}
|
||||||
|
|
||||||
return trx('movies_scenes')
|
return trx('movies_scenes')
|
||||||
.select('media.*', 'movies_scenes.movie_id')
|
.select('media.*', 'movies_scenes.movie_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
|
||||||
.whereIn('movies_scenes.movie_id', movieIds)
|
.whereIn('movies_scenes.movie_id', movieIds)
|
||||||
.whereNotNull('media.id')
|
.whereNotNull('media.id')
|
||||||
.leftJoin('releases_photos', 'releases_photos.release_id', 'movies_scenes.scene_id')
|
.leftJoin('releases_photos', 'releases_photos.release_id', 'movies_scenes.scene_id')
|
||||||
.leftJoin('media', 'media.id', 'releases_photos.media_id');
|
.leftJoin('media', 'media.id', 'releases_photos.media_id')
|
||||||
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
|
||||||
|
.groupBy('media.id', 'movies_scenes.movie_id', 'sfw_media.id');
|
||||||
}),
|
}),
|
||||||
caps: knex.transaction(async (trx) => {
|
caps: knex.transaction(async (trx) => {
|
||||||
if (reqUser) {
|
if (reqUser) {
|
||||||
@@ -144,11 +150,13 @@ export async function fetchMoviesById(movieIds, reqUser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return trx('movies_scenes')
|
return trx('movies_scenes')
|
||||||
.select('media.*', 'movies_scenes.movie_id')
|
.select('media.*', 'movies_scenes.movie_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
|
||||||
.whereIn('movies_scenes.movie_id', movieIds)
|
.whereIn('movies_scenes.movie_id', movieIds)
|
||||||
.whereNotNull('media.id')
|
.whereNotNull('media.id')
|
||||||
.leftJoin('releases_caps', 'releases_caps.release_id', 'movies_scenes.scene_id')
|
.leftJoin('releases_caps', 'releases_caps.release_id', 'movies_scenes.scene_id')
|
||||||
.leftJoin('media', 'media.id', 'releases_caps.media_id');
|
.leftJoin('media', 'media.id', 'releases_caps.media_id')
|
||||||
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
|
||||||
|
.groupBy('media.id', 'movies_scenes.movie_id', 'sfw_media.id');
|
||||||
}),
|
}),
|
||||||
trailers: knex('movies_trailers')
|
trailers: knex('movies_trailers')
|
||||||
.whereIn('movie_id', movieIds)
|
.whereIn('movie_id', movieIds)
|
||||||
@@ -192,7 +200,7 @@ export async function fetchMoviesById(movieIds, reqUser) {
|
|||||||
caps: movieCaps,
|
caps: movieCaps,
|
||||||
trailer: movieTrailer,
|
trailer: movieTrailer,
|
||||||
stashes: movieStashes,
|
stashes: movieStashes,
|
||||||
});
|
}, context);
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,7 +406,7 @@ function countAggregations(buckets) {
|
|||||||
return Object.fromEntries(buckets.map((bucket) => [bucket.key, { count: bucket.doc_count }]));
|
return Object.fromEntries(buckets.map((bucket) => [bucket.key, { count: bucket.doc_count }]));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchMovies(filters, rawOptions, reqUser) {
|
export async function fetchMovies(filters, rawOptions, reqUser, context) {
|
||||||
const options = curateOptions(rawOptions);
|
const options = curateOptions(rawOptions);
|
||||||
|
|
||||||
console.log(options);
|
console.log(options);
|
||||||
@@ -413,13 +421,13 @@ export async function fetchMovies(filters, rawOptions, reqUser) {
|
|||||||
const channelCounts = options.aggregateChannels && countAggregations(result.aggregations?.channelIds);
|
const channelCounts = options.aggregateChannels && countAggregations(result.aggregations?.channelIds);
|
||||||
|
|
||||||
const [aggActors, aggTags, aggChannels] = await Promise.all([
|
const [aggActors, aggTags, aggChannels] = await Promise.all([
|
||||||
options.aggregateActors ? fetchActorsById(result.aggregations.actorIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: actorCounts }) : [],
|
options.aggregateActors ? fetchActorsById(result.aggregations.actorIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: actorCounts }, reqUser) : [],
|
||||||
options.aggregateTags ? fetchTagsById(result.aggregations.tagIds.map((bucket) => bucket.key), { order: [knex.raw('lower(name)'), 'asc'], append: tagCounts }) : [],
|
options.aggregateTags ? fetchTagsById(result.aggregations.tagIds.map((bucket) => bucket.key), { order: [knex.raw('lower(name)'), 'asc'], append: tagCounts }, reqUser, context) : [],
|
||||||
options.aggregateChannels ? fetchEntitiesById(result.aggregations.channelIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: channelCounts }) : [],
|
options.aggregateChannels ? fetchEntitiesById(result.aggregations.channelIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: channelCounts }, reqUser, context) : [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const movieIds = result.movies.map((movie) => Number(movie.id));
|
const movieIds = result.movies.map((movie) => Number(movie.id));
|
||||||
const movies = await fetchMoviesById(movieIds, reqUser);
|
const movies = await fetchMoviesById(movieIds, reqUser, context);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
movies,
|
movies,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import config from 'config';
|
import config from 'config';
|
||||||
import { MerkleJson } from 'merkle-json';
|
import { MerkleJson } from 'merkle-json';
|
||||||
|
|
||||||
|
import argv from './argv.js';
|
||||||
import { knexQuery as knex, knexOwner, knexManticore } from './knex.js';
|
import { knexQuery as knex, knexOwner, knexManticore } from './knex.js';
|
||||||
import { utilsApi } from './manticore.js';
|
import { utilsApi } from './manticore.js';
|
||||||
import { HttpError } from './errors.js';
|
import { HttpError } from './errors.js';
|
||||||
@@ -15,18 +16,19 @@ import promiseProps from '../utils/promise-props.js';
|
|||||||
import initLogger from './logger.js';
|
import initLogger from './logger.js';
|
||||||
import { curateRevision } from './revisions.js';
|
import { curateRevision } from './revisions.js';
|
||||||
import { getAffiliateSceneUrl } from './affiliates.js';
|
import { getAffiliateSceneUrl } from './affiliates.js';
|
||||||
|
import { censor } from './censor.js';
|
||||||
|
|
||||||
const logger = initLogger();
|
const logger = initLogger();
|
||||||
const mj = new MerkleJson();
|
const mj = new MerkleJson();
|
||||||
|
|
||||||
function curateScene(rawScene, assets, reqUser) {
|
function curateScene(rawScene, assets, reqUser, context) {
|
||||||
if (!rawScene) {
|
if (!rawScene) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const curatedScene = {
|
const curatedScene = {
|
||||||
id: rawScene.id,
|
id: rawScene.id,
|
||||||
title: rawScene.title,
|
title: censor(rawScene.title, context.restriction),
|
||||||
slug: rawScene.slug,
|
slug: rawScene.slug,
|
||||||
url: rawScene.url,
|
url: rawScene.url,
|
||||||
entryId: rawScene.entry_id,
|
entryId: rawScene.entry_id,
|
||||||
@@ -34,14 +36,14 @@ function curateScene(rawScene, assets, reqUser) {
|
|||||||
datePrecision: rawScene.date_precision,
|
datePrecision: rawScene.date_precision,
|
||||||
createdAt: rawScene.created_at,
|
createdAt: rawScene.created_at,
|
||||||
effectiveDate: rawScene.effective_date,
|
effectiveDate: rawScene.effective_date,
|
||||||
description: rawScene.description,
|
description: censor(rawScene.description, context.restriction),
|
||||||
duration: rawScene.duration,
|
duration: rawScene.duration,
|
||||||
shootId: rawScene.shoot_id,
|
shootId: rawScene.shoot_id,
|
||||||
productionDate: rawScene.production_date,
|
productionDate: rawScene.production_date,
|
||||||
channel: {
|
channel: {
|
||||||
id: assets.channel.id,
|
id: assets.channel.id,
|
||||||
slug: assets.channel.slug,
|
slug: assets.channel.slug,
|
||||||
name: assets.channel.name,
|
name: censor(assets.channel.name, context.restriction),
|
||||||
type: assets.channel.type,
|
type: assets.channel.type,
|
||||||
isIndependent: assets.channel.independent,
|
isIndependent: assets.channel.independent,
|
||||||
hasLogo: assets.channel.has_logo,
|
hasLogo: assets.channel.has_logo,
|
||||||
@@ -49,7 +51,7 @@ function curateScene(rawScene, assets, reqUser) {
|
|||||||
network: assets.channel.network_id ? {
|
network: assets.channel.network_id ? {
|
||||||
id: assets.channel.network_id,
|
id: assets.channel.network_id,
|
||||||
slug: assets.channel.network_slug,
|
slug: assets.channel.network_slug,
|
||||||
name: assets.channel.network_name,
|
name: censor(assets.channel.network_name, context.restriction),
|
||||||
type: assets.channel.network_type,
|
type: assets.channel.network_type,
|
||||||
hasLogo: assets.channel.network_has_logo,
|
hasLogo: assets.channel.network_has_logo,
|
||||||
} : null,
|
} : null,
|
||||||
@@ -78,7 +80,7 @@ function curateScene(rawScene, assets, reqUser) {
|
|||||||
tags: assets.tags.map((tag) => ({
|
tags: assets.tags.map((tag) => ({
|
||||||
id: tag.id,
|
id: tag.id,
|
||||||
slug: tag.slug,
|
slug: tag.slug,
|
||||||
name: tag.name,
|
name: censor(tag.name, context.restriction),
|
||||||
priority: tag.priority,
|
priority: tag.priority,
|
||||||
})),
|
})),
|
||||||
chapters: assets.chapters.map((chapter) => ({
|
chapters: assets.chapters.map((chapter) => ({
|
||||||
@@ -86,7 +88,9 @@ function curateScene(rawScene, assets, reqUser) {
|
|||||||
title: chapter.title,
|
title: chapter.title,
|
||||||
time: chapter.time,
|
time: chapter.time,
|
||||||
duration: chapter.duration,
|
duration: chapter.duration,
|
||||||
poster: curateMedia(chapter.chapter_poster),
|
poster: context.restriction
|
||||||
|
? null
|
||||||
|
: (chapter.chapter_poster),
|
||||||
tags: chapter.chapter_tags.map((tag) => ({
|
tags: chapter.chapter_tags.map((tag) => ({
|
||||||
id: tag.id,
|
id: tag.id,
|
||||||
name: tag.name,
|
name: tag.name,
|
||||||
@@ -98,14 +102,18 @@ function curateScene(rawScene, assets, reqUser) {
|
|||||||
movies: assets.movies.map((movie) => ({
|
movies: assets.movies.map((movie) => ({
|
||||||
id: movie.id,
|
id: movie.id,
|
||||||
slug: movie.slug,
|
slug: movie.slug,
|
||||||
title: movie.title,
|
title: censor(movie.title, context.restriction),
|
||||||
covers: movie.movie_covers?.map((cover) => curateMedia(cover, { type: 'cover' })).toSorted((coverA, coverB) => coverA.index - coverB.index) || [],
|
covers: movie.movie_covers && !context.restriction
|
||||||
|
? movie.movie_covers.map((cover) => curateMedia(cover, { type: 'cover' })).toSorted((coverA, coverB) => coverA.index - coverB.index)
|
||||||
|
: [],
|
||||||
})),
|
})),
|
||||||
series: assets.series.map((serie) => ({
|
series: assets.series.map((serie) => ({
|
||||||
id: serie.id,
|
id: serie.id,
|
||||||
slug: serie.slug,
|
slug: serie.slug,
|
||||||
title: serie.title,
|
title: serie.title,
|
||||||
poster: curateMedia(serie.serie_poster, { type: 'poster' }),
|
poster: context.restriction
|
||||||
|
? null
|
||||||
|
: (serie.serie_poster, { type: 'poster' }),
|
||||||
})),
|
})),
|
||||||
poster: curateMedia(assets.poster, { type: 'poster' }),
|
poster: curateMedia(assets.poster, { type: 'poster' }),
|
||||||
photos: assets.photos?.map((photo) => curateMedia(photo, { type: 'photo' })) || [],
|
photos: assets.photos?.map((photo) => curateMedia(photo, { type: 'photo' })) || [],
|
||||||
@@ -189,14 +197,17 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
|
|||||||
.select(
|
.select(
|
||||||
'actors.*',
|
'actors.*',
|
||||||
knex.raw('row_to_json(avatars) as avatar'),
|
knex.raw('row_to_json(avatars) as avatar'),
|
||||||
|
knex.raw('row_to_json(sfw_media) as sfw_avatar'),
|
||||||
'countries.name as birth_country_name',
|
'countries.name as birth_country_name',
|
||||||
'countries.alias as birth_country_alias',
|
'countries.alias as birth_country_alias',
|
||||||
'releases_actors.release_id',
|
'releases_actors.release_id',
|
||||||
)
|
)
|
||||||
.leftJoin('actors', 'actors.id', 'releases_actors.actor_id')
|
.leftJoin('actors', 'actors.id', 'releases_actors.actor_id')
|
||||||
.leftJoin('media as avatars', 'avatars.id', 'actors.avatar_media_id')
|
.leftJoin('media as avatars', 'avatars.id', 'actors.avatar_media_id')
|
||||||
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'avatars.sfw_media_id')
|
||||||
.leftJoin('countries', 'countries.alpha2', 'actors.birth_country_alpha2')
|
.leftJoin('countries', 'countries.alpha2', 'actors.birth_country_alpha2')
|
||||||
.whereIn('release_id', sceneIds),
|
.whereIn('release_id', sceneIds)
|
||||||
|
.groupBy('actors.id', 'releases_actors.release_id', 'avatars.id', 'countries.name', 'countries.alias', 'sfw_media.id'),
|
||||||
directors: knex('releases_directors')
|
directors: knex('releases_directors')
|
||||||
.whereIn('release_id', sceneIds)
|
.whereIn('release_id', sceneIds)
|
||||||
.leftJoin('actors as directors', 'directors.id', 'releases_directors.director_id'),
|
.leftJoin('actors as directors', 'directors.id', 'releases_directors.director_id'),
|
||||||
@@ -234,17 +245,23 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
|
|||||||
.whereIn('scene_id', sceneIds)
|
.whereIn('scene_id', sceneIds)
|
||||||
.groupBy('series.id', 'series_scenes.scene_id', 'media.*') : [],
|
.groupBy('series.id', 'series_scenes.scene_id', 'media.*') : [],
|
||||||
posters: knex('releases_posters')
|
posters: knex('releases_posters')
|
||||||
|
.select('media.*', 'releases_posters.release_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
|
||||||
.whereIn('release_id', sceneIds)
|
.whereIn('release_id', sceneIds)
|
||||||
.leftJoin('media', 'media.id', 'releases_posters.media_id'),
|
.leftJoin('media', 'media.id', 'releases_posters.media_id')
|
||||||
photos: context.includeAssets ? knex.transaction(async (trx) => {
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
|
||||||
|
.groupBy('media.id', 'releases_posters.release_id', 'sfw_media.id'),
|
||||||
|
photos: context.includeAssets && !context.restriction ? knex.transaction(async (trx) => {
|
||||||
if (reqUser) {
|
if (reqUser) {
|
||||||
await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id }));
|
await trx.select(knex.raw('set_config(\'user.id\', :userId, true)', { userId: reqUser.id }));
|
||||||
}
|
}
|
||||||
|
|
||||||
return trx('releases_photos')
|
return trx('releases_photos')
|
||||||
|
.select('media.*', 'releases_photos.release_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
|
||||||
.leftJoin('media', 'media.id', 'releases_photos.media_id')
|
.leftJoin('media', 'media.id', 'releases_photos.media_id')
|
||||||
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
|
||||||
.whereIn('release_id', sceneIds)
|
.whereIn('release_id', sceneIds)
|
||||||
.orderBy('index');
|
.orderBy('index')
|
||||||
|
.groupBy('media.id', 'releases_photos.release_id', 'sfw_media.id');
|
||||||
}) : [],
|
}) : [],
|
||||||
caps: context.includeAssets ? knex.transaction(async (trx) => {
|
caps: context.includeAssets ? knex.transaction(async (trx) => {
|
||||||
if (reqUser) {
|
if (reqUser) {
|
||||||
@@ -252,9 +269,12 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return trx('releases_caps')
|
return trx('releases_caps')
|
||||||
|
.select('media.*', 'releases_caps.release_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
|
||||||
.leftJoin('media', 'media.id', 'releases_caps.media_id')
|
.leftJoin('media', 'media.id', 'releases_caps.media_id')
|
||||||
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
|
||||||
.whereIn('release_id', sceneIds)
|
.whereIn('release_id', sceneIds)
|
||||||
.orderBy('index');
|
.orderBy('index')
|
||||||
|
.groupBy('media.id', 'releases_caps.release_id', 'sfw_media.id');
|
||||||
}) : [],
|
}) : [],
|
||||||
trailers: context.includeAssets ? knex.transaction(async (trx) => {
|
trailers: context.includeAssets ? knex.transaction(async (trx) => {
|
||||||
if (reqUser) {
|
if (reqUser) {
|
||||||
@@ -350,7 +370,7 @@ export async function fetchScenesById(sceneIds, { reqUser, ...context } = {}) {
|
|||||||
stashes: sceneStashes,
|
stashes: sceneStashes,
|
||||||
actorStashes: sceneActorStashes,
|
actorStashes: sceneActorStashes,
|
||||||
lastBatchId: lastBatch?.id,
|
lastBatchId: lastBatch?.id,
|
||||||
}, reqUser);
|
}, reqUser, context);
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,7 +565,7 @@ async function queryManticoreSql(filters, options, _reqUser) {
|
|||||||
? sqlQuery
|
? sqlQuery
|
||||||
: sqlQuery.replace(/scenes\./g, '');
|
: sqlQuery.replace(/scenes\./g, '');
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development' && argv.debug) {
|
||||||
console.log(curatedSqlQuery);
|
console.log(curatedSqlQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,14 +623,18 @@ function countAggregations(buckets) {
|
|||||||
return Object.fromEntries(buckets.map((bucket) => [bucket.key, { count: bucket.doc_count }]));
|
return Object.fromEntries(buckets.map((bucket) => [bucket.key, { count: bucket.doc_count }]));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchScenes(filters, rawOptions, reqUser) {
|
export async function fetchScenes(filters, rawOptions, reqUser, context) {
|
||||||
const options = curateOptions(rawOptions);
|
const options = curateOptions(rawOptions);
|
||||||
|
|
||||||
console.log('filters', filters);
|
if (argv.debug) {
|
||||||
console.log('options', options);
|
console.log('filters', filters);
|
||||||
|
console.log('options', options);
|
||||||
|
}
|
||||||
|
|
||||||
console.time('manticore sql');
|
console.time('manticore sql');
|
||||||
|
|
||||||
const result = await queryManticoreSql(filters, options, reqUser);
|
const result = await queryManticoreSql(filters, options, reqUser);
|
||||||
|
|
||||||
console.timeEnd('manticore sql');
|
console.timeEnd('manticore sql');
|
||||||
|
|
||||||
const aggYears = options.aggregateYears && result.aggregations.years.map((bucket) => ({ year: bucket.key, count: bucket.doc_count }));
|
const aggYears = options.aggregateYears && result.aggregations.years.map((bucket) => ({ year: bucket.key, count: bucket.doc_count }));
|
||||||
@@ -623,16 +647,16 @@ export async function fetchScenes(filters, rawOptions, reqUser) {
|
|||||||
console.time('fetch aggregations');
|
console.time('fetch aggregations');
|
||||||
|
|
||||||
const [aggActors, aggTags, aggChannels] = await Promise.all([
|
const [aggActors, aggTags, aggChannels] = await Promise.all([
|
||||||
options.aggregateActors ? fetchActorsById(result.aggregations.actorIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: actorCounts }) : [],
|
options.aggregateActors ? fetchActorsById(result.aggregations.actorIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: actorCounts }, reqUser) : [],
|
||||||
options.aggregateTags ? fetchTagsById(result.aggregations.tagIds.map((bucket) => bucket.key), { order: [knex.raw('lower(name)'), 'asc'], append: tagCounts }) : [],
|
options.aggregateTags ? fetchTagsById(result.aggregations.tagIds.map((bucket) => bucket.key), { order: [knex.raw('lower(name)'), 'asc'], append: tagCounts }, reqUser, context) : [],
|
||||||
options.aggregateChannels ? fetchEntitiesById(entityIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: channelCounts }) : [],
|
options.aggregateChannels ? fetchEntitiesById(entityIds.map((bucket) => bucket.key), { order: ['slug', 'asc'], append: channelCounts }, reqUser, context) : [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.timeEnd('fetch aggregations');
|
console.timeEnd('fetch aggregations');
|
||||||
|
|
||||||
console.time('fetch full');
|
console.time('fetch full');
|
||||||
const sceneIds = result.scenes.map((scene) => Number(scene.id));
|
const sceneIds = result.scenes.map((scene) => Number(scene.id));
|
||||||
const scenes = await fetchScenesById(sceneIds, { reqUser });
|
const scenes = await fetchScenesById(sceneIds, { reqUser, ...context });
|
||||||
console.timeEnd('fetch full');
|
console.timeEnd('fetch full');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
21
src/tags.js
21
src/tags.js
@@ -1,6 +1,7 @@
|
|||||||
import knex from './knex.js';
|
import knex from './knex.js';
|
||||||
import redis from './redis.js';
|
import redis from './redis.js';
|
||||||
import initLogger from './logger.js';
|
import initLogger from './logger.js';
|
||||||
|
import { censor } from './censor.js';
|
||||||
|
|
||||||
import { curateMedia } from './media.js';
|
import { curateMedia } from './media.js';
|
||||||
|
|
||||||
@@ -9,9 +10,9 @@ const logger = initLogger();
|
|||||||
function curateTag(tag, context) {
|
function curateTag(tag, context) {
|
||||||
return {
|
return {
|
||||||
id: tag.id,
|
id: tag.id,
|
||||||
name: tag.name,
|
name: censor(tag.name, context.restriction),
|
||||||
slug: tag.slug,
|
slug: tag.slug,
|
||||||
description: tag.description,
|
description: context.restriction ? null : tag.description, // censor interferes with markdown
|
||||||
priority: tag.priority,
|
priority: tag.priority,
|
||||||
poster: tag.poster && curateMedia(tag.poster),
|
poster: tag.poster && curateMedia(tag.poster),
|
||||||
photos: tag.photos?.map((photo) => curateMedia(photo)) || [],
|
photos: tag.photos?.map((photo) => curateMedia(photo)) || [],
|
||||||
@@ -55,10 +56,13 @@ export async function fetchTags(options = {}) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
knex('tags_posters')
|
knex('tags_posters')
|
||||||
|
.select('media.*', 'tags_posters.tag_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
|
||||||
.select('tags_posters.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
|
.select('tags_posters.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
|
||||||
.leftJoin('media', 'media.id', 'tags_posters.media_id')
|
.leftJoin('media', 'media.id', 'tags_posters.media_id')
|
||||||
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
|
||||||
.leftJoin('entities', 'entities.id', 'media.entity_id')
|
.leftJoin('entities', 'entities.id', 'media.entity_id')
|
||||||
.leftJoin('entities as parents', 'parents.id', 'entities.parent_id'),
|
.leftJoin('entities as parents', 'parents.id', 'entities.parent_id')
|
||||||
|
.groupBy('media.id', 'tags_posters.tag_id', 'sfw_media.id', 'entities.id', 'parents.id'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const postersByTagId = Object.fromEntries(posters.map((poster) => [poster.tag_id, poster]));
|
const postersByTagId = Object.fromEntries(posters.map((poster) => [poster.tag_id, poster]));
|
||||||
@@ -69,7 +73,7 @@ export async function fetchTags(options = {}) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchTagsById(tagIds, options = {}, reqUser) {
|
export async function fetchTagsById(tagIds, options = {}, reqUser, context = {}) {
|
||||||
const [tags, posters, photos, alerts] = await Promise.all([
|
const [tags, posters, photos, alerts] = await Promise.all([
|
||||||
knex('tags')
|
knex('tags')
|
||||||
.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
|
.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
|
||||||
@@ -80,14 +84,17 @@ export async function fetchTagsById(tagIds, options = {}, reqUser) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
knex('tags_posters')
|
knex('tags_posters')
|
||||||
|
.select('media.*', 'tags_posters.tag_id', knex.raw('row_to_json(sfw_media) as sfw_media'))
|
||||||
.select('tags_posters.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
|
.select('tags_posters.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
|
||||||
.leftJoin('tags', 'tags.id', 'tags_posters.tag_id')
|
.leftJoin('tags', 'tags.id', 'tags_posters.tag_id')
|
||||||
.leftJoin('media', 'media.id', 'tags_posters.media_id')
|
.leftJoin('media', 'media.id', 'tags_posters.media_id')
|
||||||
|
.leftJoin('media as sfw_media', 'sfw_media.id', 'media.sfw_media_id')
|
||||||
.leftJoin('entities', 'entities.id', 'media.entity_id')
|
.leftJoin('entities', 'entities.id', 'media.entity_id')
|
||||||
.leftJoin('entities as parents', 'parents.id', 'entities.parent_id')
|
.leftJoin('entities as parents', 'parents.id', 'entities.parent_id')
|
||||||
.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
|
.whereIn('tags.id', tagIds.filter((tagId) => typeof tagId === 'number'))
|
||||||
.orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string')),
|
.orWhereIn('tags.slug', tagIds.filter((tagId) => typeof tagId === 'string'))
|
||||||
knex('tags_photos')
|
.groupBy('media.id', 'tags_posters.tag_id', 'sfw_media.id', 'entities.id', 'parents.id'),
|
||||||
|
context.restriction ? [] : knex('tags_photos')
|
||||||
.select('tags_photos.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
|
.select('tags_photos.tag_id', 'media.*', knex.raw('row_to_json(entities) as entity'), knex.raw('row_to_json(parents) as entity_parent'))
|
||||||
.leftJoin('tags', 'tags.id', 'tags_photos.tag_id')
|
.leftJoin('tags', 'tags.id', 'tags_photos.tag_id')
|
||||||
.leftJoin('media', 'media.id', 'tags_photos.media_id')
|
.leftJoin('media', 'media.id', 'tags_photos.media_id')
|
||||||
@@ -118,6 +125,7 @@ export async function fetchTagsById(tagIds, options = {}, reqUser) {
|
|||||||
}, {
|
}, {
|
||||||
alerts: alerts.filter((alert) => alert.tag_id === tagEntry.id),
|
alerts: alerts.filter((alert) => alert.tag_id === tagEntry.id),
|
||||||
append: options.append,
|
append: options.append,
|
||||||
|
...context,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +144,7 @@ export async function fetchTagsById(tagIds, options = {}, reqUser) {
|
|||||||
}, {
|
}, {
|
||||||
alerts: alerts.filter((alert) => alert.tag_id === tag.id),
|
alerts: alerts.filter((alert) => alert.tag_id === tag.id),
|
||||||
append: options.append,
|
append: options.append,
|
||||||
|
...context,
|
||||||
});
|
});
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
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 IPCIDR from 'ip-cidr';
|
import IPCIDR from 'ip-cidr';
|
||||||
|
import argv from '../argv.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
login,
|
login,
|
||||||
@@ -14,6 +15,10 @@ import {
|
|||||||
import { fetchUser } from '../users.js';
|
import { fetchUser } from '../users.js';
|
||||||
|
|
||||||
function getIp(req) {
|
function getIp(req) {
|
||||||
|
if (argv.ip) {
|
||||||
|
return argv.ip;
|
||||||
|
}
|
||||||
|
|
||||||
const ip = req.headers['x-forwarded-for']?.split(',')[0] || req.connection.remoteAddress;
|
const ip = req.headers['x-forwarded-for']?.split(',')[0] || req.connection.remoteAddress;
|
||||||
|
|
||||||
const unmappedIp = ip?.includes('.')
|
const unmappedIp = ip?.includes('.')
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export default async function mainHandler(req, res, next) {
|
|||||||
siteKey: config.auth.captcha.siteKey,
|
siteKey: config.auth.captcha.siteKey,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
restriction: req.restriction,
|
||||||
meta: {
|
meta: {
|
||||||
now: new Date().toISOString(),
|
now: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
|
|||||||
80
src/web/restrictions.js
Normal file
80
src/web/restrictions.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import config from 'config';
|
||||||
|
import path from 'path';
|
||||||
|
import { Reader } from '@maxmind/geoip2-node';
|
||||||
|
|
||||||
|
import initLogger from '../logger.js';
|
||||||
|
|
||||||
|
const logger = initLogger();
|
||||||
|
const regions = config.restrictions.regions;
|
||||||
|
|
||||||
|
export default async function initRestrictionHandler() {
|
||||||
|
const reader = await Reader.open('assets/GeoLite2-City.mmdb');
|
||||||
|
|
||||||
|
function getRestriction(req) {
|
||||||
|
if (req.session.restriction && req.session.country && req.session.restrictionIp === req.userIp) {
|
||||||
|
return {
|
||||||
|
restriction: req.session.restriction,
|
||||||
|
country: req.session.country,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const location = reader.city(req.userIp);
|
||||||
|
const country = location.country.isoCode;
|
||||||
|
const subdivision = location.subdivisions?.[0]?.isoCode;
|
||||||
|
|
||||||
|
if (regions[country]?.[subdivision]) {
|
||||||
|
// state or province restriction
|
||||||
|
return {
|
||||||
|
restriction: config.restrictions.modes[regions[country][subdivision]],
|
||||||
|
country,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regions[country]) {
|
||||||
|
// country restriction
|
||||||
|
return {
|
||||||
|
restriction: config.restrictions.modes[regions[country]],
|
||||||
|
country,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
restriction: null,
|
||||||
|
country,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function restrictionHandler(req, res, next) {
|
||||||
|
if (!config.restrictions.enabled) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { restriction, country } = getRestriction(req);
|
||||||
|
|
||||||
|
if (restriction === 'block' || req.path === '/sfw/') {
|
||||||
|
res.render(path.join(import.meta.dirname, '../../assets/sfw.ejs'), {
|
||||||
|
noVpn: config.restrictions.noVpn.includes(country),
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.session.restriction !== restriction) {
|
||||||
|
req.session.restrictionIp = req.userIp;
|
||||||
|
req.session.restriction = restriction;
|
||||||
|
req.session.country = country;
|
||||||
|
}
|
||||||
|
|
||||||
|
req.restriction = restriction;
|
||||||
|
req.country = country;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed Maxmind IP lookup for ${req.ip}: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return restrictionHandler;
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ import redis from '../redis.js';
|
|||||||
|
|
||||||
import errorHandler from './error.js';
|
import errorHandler from './error.js';
|
||||||
import consentHandler from './consent.js';
|
import consentHandler from './consent.js';
|
||||||
|
import initRestrictionHandler from './restrictions.js';
|
||||||
|
|
||||||
import { scenesRouter } from './scenes.js';
|
import { scenesRouter } from './scenes.js';
|
||||||
import { actorsRouter } from './actors.js';
|
import { actorsRouter } from './actors.js';
|
||||||
@@ -48,9 +49,11 @@ const isProduction = process.env.NODE_ENV === 'production';
|
|||||||
export default async function initServer() {
|
export default async function initServer() {
|
||||||
const app = express();
|
const app = express();
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
const restrictionHandler = await initRestrictionHandler();
|
||||||
|
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
app.disable('x-powered-by');
|
app.disable('x-powered-by');
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
|
||||||
router.use(boolParser());
|
router.use(boolParser());
|
||||||
|
|
||||||
@@ -58,7 +61,7 @@ export default async function initServer() {
|
|||||||
router.use('/', express.static('static'));
|
router.use('/', express.static('static'));
|
||||||
router.use('/media', express.static(config.media.path));
|
router.use('/media', express.static(config.media.path));
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
router.use((req, _res, next) => {
|
||||||
if (req.headers.cookie) {
|
if (req.headers.cookie) {
|
||||||
const cookies = cookie.parse(req.headers.cookie);
|
const cookies = cookie.parse(req.headers.cookie);
|
||||||
|
|
||||||
@@ -109,11 +112,13 @@ export default async function initServer() {
|
|||||||
router.use(viteDevMiddleware);
|
router.use(viteDevMiddleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
router.get('/consent', (req, res) => {
|
router.use(restrictionHandler);
|
||||||
|
|
||||||
|
router.get('/consent', (_req, res) => {
|
||||||
res.sendFile(path.join(import.meta.dirname, '../../assets/consent.html'));
|
res.sendFile(path.join(import.meta.dirname, '../../assets/consent.html'));
|
||||||
});
|
});
|
||||||
|
|
||||||
router.use('/api/*', async (req, res, next) => {
|
router.use('/api/*', async (req, _res, next) => {
|
||||||
if (req.headers['api-user']) {
|
if (req.headers['api-user']) {
|
||||||
await verifyKey(req.headers['api-user'], req.headers['api-key'], req);
|
await verifyKey(req.headers['api-user'], req.headers['api-key'], req);
|
||||||
|
|
||||||
@@ -159,7 +164,7 @@ export default async function initServer() {
|
|||||||
|
|
||||||
router.use(consentHandler);
|
router.use(consentHandler);
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
router.use((_req, res, next) => {
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
res.set('Accept-CH', 'Sec-CH-Prefers-Color-Scheme');
|
res.set('Accept-CH', 'Sec-CH-Prefers-Color-Scheme');
|
||||||
res.set('Vary', 'Sec-CH-Prefers-Color-Scheme');
|
res.set('Vary', 'Sec-CH-Prefers-Color-Scheme');
|
||||||
|
|||||||
2
static
2
static
Submodule static updated: 4b6b7e5e73...389d659cb2
Reference in New Issue
Block a user