@@ -12,6 +12,9 @@ invidious_companion:
|
||||
public_url: "http://companion.example.com/companion"
|
||||
```
|
||||
|
||||
### Proof-of-work Captcha
|
||||
Will only work while using HTTPS. If in HTTP set `PUBLIC_CAPTCHA_DISABLED` to `true`
|
||||
|
||||
### Docker Compose
|
||||
|
||||
```yaml
|
||||
@@ -42,6 +45,14 @@ services:
|
||||
# Allow anyone to register
|
||||
PUBLIC_REGISTRATION_ALLOWED: "false"
|
||||
|
||||
# Disable POW Captcha
|
||||
PUBLIC_CAPTCHA_DISABLED: "false"
|
||||
|
||||
# Allows you to whitelist additional domains in proxy.
|
||||
# Should be comma separated and be the base domain
|
||||
# e.g. "youtube.com,google.com" NOT "https://youtube.com,https://videos.google.com"
|
||||
WHITELIST_BASE_DOMAIN: ""
|
||||
|
||||
# Allow any domain in proxy
|
||||
# This shouldn't be used unless you KNOW what your doing
|
||||
# requires VITE_REGISTRATION_ALLOWED to be false
|
||||
|
||||
@@ -7,8 +7,8 @@ android {
|
||||
applicationId "us.materialio.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 216
|
||||
versionName "1.15.8"
|
||||
versionCode 217
|
||||
versionName "1.15.9"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -81,7 +81,11 @@
|
||||
|
||||
|
||||
|
||||
<release version="1.15.8" date="2026-2-17">
|
||||
|
||||
<release version="1.15.9" date="2026-2-17">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.15.9</url>
|
||||
</release>
|
||||
<release version="1.15.8" date="2026-2-17">
|
||||
<url>https://github.com/Materialious/Materialious/releases/tag/1.15.8</url>
|
||||
</release>
|
||||
<release version="1.15.7" date="2026-2-16">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Materialious",
|
||||
"version": "1.15.8",
|
||||
"version": "1.15.9",
|
||||
"description": "Modern material design for YouTube and Invidious.",
|
||||
"author": {
|
||||
"name": "Ward Pearce",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "materialious",
|
||||
"version": "1.15.8",
|
||||
"version": "1.15.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run patch:github && vite dev",
|
||||
|
||||
@@ -137,27 +137,25 @@ export async function parseChannelRSS(channelId: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
updateLastRssFetch(channelId, channelName ?? 'Unknown');
|
||||
await updateLastRssFetch(channelId, channelName ?? 'Unknown');
|
||||
}
|
||||
|
||||
async function updateLastRssFetch(channelId: string, channelName: string) {
|
||||
localDb.channelSubscriptions
|
||||
const subscription = await localDb.channelSubscriptions
|
||||
.where('channelId')
|
||||
.equals(channelId)
|
||||
.first()
|
||||
.then((subscription) => {
|
||||
if (subscription) {
|
||||
localDb.channelSubscriptions.where('channelId').equals(channelId).modify({
|
||||
lastRSSFetch: new Date()
|
||||
});
|
||||
} else {
|
||||
localDb.channelSubscriptions.add({
|
||||
channelId: channelId,
|
||||
channelName: channelName,
|
||||
lastRSSFetch: new Date()
|
||||
});
|
||||
}
|
||||
.first();
|
||||
if (subscription) {
|
||||
await localDb.channelSubscriptions.where('channelId').equals(channelId).modify({
|
||||
lastRSSFetch: new Date()
|
||||
});
|
||||
} else {
|
||||
await localDb.channelSubscriptions.add({
|
||||
channelId: channelId,
|
||||
channelName: channelName,
|
||||
lastRSSFetch: new Date()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function clearFeedYTjs() {
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
} from '../store';
|
||||
import { setStatusBarColor } from '../theme';
|
||||
import { getVideoYTjs } from '$lib/api/youtubejs/video';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import {
|
||||
goToNextVideo,
|
||||
goToPreviousVideo,
|
||||
@@ -60,7 +59,7 @@
|
||||
import { Network, type ConnectionStatus } from '@capacitor/network';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { addToast } from './Toast.svelte';
|
||||
import { isMobile, isUnrestrictedPlatform, isYTBackend, truncate } from '$lib/misc';
|
||||
import { getPublicEnv, isMobile, isUnrestrictedPlatform, isYTBackend, truncate } from '$lib/misc';
|
||||
import {
|
||||
generateThumbnailWebVTT,
|
||||
drawTimelineThumbnail,
|
||||
@@ -515,11 +514,8 @@
|
||||
// Due to CORs issues with redirects, hosted instances of Materialious
|
||||
// dirctly provide the companion instance
|
||||
// while clients can just use the reirect provided by Invidious' API
|
||||
if (
|
||||
import.meta.env.VITE_DEFAULT_COMPANION_INSTANCE ||
|
||||
env.PUBLIC_DEFAULT_COMPANION_INSTANCE
|
||||
) {
|
||||
dashUrl = `${import.meta.env.VITE_DEFAULT_COMPANION_INSTANCE || env.PUBLIC_DEFAULT_COMPANION_INSTANCE}/api/manifest/dash/id/${data.video.videoId}`;
|
||||
if (getPublicEnv('DEFAULT_COMPANION_INSTANCE')) {
|
||||
dashUrl = `${getPublicEnv('DEFAULT_COMPANION_INSTANCE')}/api/manifest/dash/id/${data.video.videoId}`;
|
||||
} else {
|
||||
if (!data.video.dashUrl) {
|
||||
error(500, 'No dash manifest found');
|
||||
@@ -582,16 +578,12 @@
|
||||
if (data.video.captions) {
|
||||
for (const caption of data.video.captions) {
|
||||
let captionUrl: string;
|
||||
if (
|
||||
!import.meta.env.VITE_DEFAULT_COMPANION_INSTANCE &&
|
||||
!env.PUBLIC_DEFAULT_COMPANION_INSTANCE &&
|
||||
$invidiousInstanceStore
|
||||
) {
|
||||
if (!getPublicEnv('DEFAULT_COMPANION_INSTANCE') && $invidiousInstanceStore) {
|
||||
captionUrl = caption.url.startsWith('http')
|
||||
? caption.url
|
||||
: `${new URL($invidiousInstanceStore).origin}${caption.url}`;
|
||||
} else {
|
||||
captionUrl = `${import.meta.env.VITE_DEFAULT_COMPANION_INSTANCE || env.PUBLIC_DEFAULT_COMPANION_INSTANCE}${caption.url}`;
|
||||
captionUrl = `${getPublicEnv('DEFAULT_COMPANION_INSTANCE')}${caption.url}`;
|
||||
}
|
||||
|
||||
await player.addTextTrackAsync(
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
isUnrestrictedPlatform,
|
||||
setInvidiousInstance,
|
||||
goToInvidiousLogin,
|
||||
invidiousLogout
|
||||
invidiousLogout,
|
||||
timeout
|
||||
} from '../../misc';
|
||||
import { getPages, type Pages } from '../../navPages';
|
||||
import ColorPicker from 'svelte-awesome-color-picker';
|
||||
@@ -82,12 +83,16 @@
|
||||
async function setInstance(event: Event) {
|
||||
event.preventDefault();
|
||||
invalidInstance = !(await setInvidiousInstance(invidiousInstance));
|
||||
|
||||
await timeout(100);
|
||||
location.reload();
|
||||
}
|
||||
|
||||
async function setBackend(event: Event) {
|
||||
const select = event.target as HTMLSelectElement;
|
||||
backendInUseStore.set(select.value as 'ivg' | 'yt');
|
||||
|
||||
await timeout(100);
|
||||
location.reload();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import { page } from '$app/state';
|
||||
import { get } from 'svelte/store';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { persistedStores } from './settings';
|
||||
|
||||
import { isOwnBackend } from '$lib/shared';
|
||||
import { addOrUpdateKeyValue, getKeyValue } from '$lib/api/backend';
|
||||
import { rawMasterKeyStore } from '$lib/store';
|
||||
import { getPublicEnv } from '$lib/misc';
|
||||
|
||||
export async function syncSettingsToBackend() {
|
||||
if (!isOwnBackend() || !get(rawMasterKeyStore)) return;
|
||||
@@ -74,13 +74,11 @@ function setStores(toSet: Record<string, unknown>, overwriteExisting = false) {
|
||||
}
|
||||
|
||||
export function loadSettingsFromEnv() {
|
||||
if (
|
||||
typeof import.meta.env.VITE_DEFAULT_SETTINGS !== 'string' &&
|
||||
typeof env.PUBLIC_DEFAULT_SETTINGS !== 'string'
|
||||
)
|
||||
return;
|
||||
const defaultSettings = getPublicEnv('DEFAULT_SETTINGS');
|
||||
|
||||
let raw = import.meta.env.VITE_DEFAULT_SETTINGS || env.PUBLIC_DEFAULT_SETTINGS;
|
||||
if (typeof defaultSettings !== 'string') return;
|
||||
|
||||
let raw = defaultSettings;
|
||||
|
||||
// Docker wraps env vars in quotes
|
||||
if (raw.startsWith('"')) raw = raw.slice(1);
|
||||
|
||||
@@ -34,17 +34,18 @@ import { isOwnBackend } from './shared';
|
||||
import { Browser } from '@capacitor/browser';
|
||||
import { clearFeedYTjs } from './api/youtubejs/subscriptions';
|
||||
|
||||
export function getPublicEnv(envName: string): string | undefined {
|
||||
return env[`PUBLIC_${envName}`] ?? import.meta.env[`VITE_${envName}`];
|
||||
}
|
||||
|
||||
export function isMobile(): boolean {
|
||||
const userAgent = navigator.userAgent;
|
||||
|
||||
const hasTouchSupport = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||
const isSmallScreen = window.innerWidth < 768;
|
||||
const isUserAgentMobile =
|
||||
/Mobi|Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
|
||||
|
||||
return (
|
||||
(hasTouchSupport && isSmallScreen && isUserAgentMobile) || Capacitor.getPlatform() === 'android'
|
||||
);
|
||||
return (hasTouchSupport && isUserAgentMobile) || Capacitor.getPlatform() === 'android';
|
||||
}
|
||||
|
||||
export function isVideoID(videoId: string): boolean {
|
||||
@@ -86,10 +87,9 @@ export interface PeerInstance {
|
||||
|
||||
export function determinePeerJsInstance(): PeerInstance {
|
||||
return {
|
||||
host:
|
||||
import.meta.env.VITE_DEFAULT_PEERJS_HOST || env.PUBLIC_DEFAULT_PEERJS_HOST || '0.peerjs.com',
|
||||
path: import.meta.env.VITE_DEFAULT_PEERJS_PATH || env.PUBLIC_DEFAULT_PEERJS_PATH || '/',
|
||||
port: import.meta.env.VITE_DEFAULT_PEERJS_PORT || env.PUBLIC_DEFAULT_PEERJS_PORT || 443
|
||||
host: getPublicEnv('DEFAULT_PEERJS_HOST') || '0.peerjs.com',
|
||||
path: getPublicEnv('DEFAULT_PEERJS_PATH') || '/',
|
||||
port: getPublicEnv('DEFAULT_PEERJS_PORT') ? Number(getPublicEnv('DEFAULT_PEERJS_PORT')) : 443
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { verifySolution } from 'altcha-lib';
|
||||
import { getSequelize } from './database';
|
||||
import { isOwnBackend } from '$lib/shared';
|
||||
|
||||
export async function verifyCaptcha(payload: string, key: string, maxUses: number = -1) {
|
||||
if (isOwnBackend()?.captchaDisabled) return;
|
||||
|
||||
const passedCaptcha = await verifySolution(payload, key, true);
|
||||
if (!passedCaptcha) {
|
||||
throw error(400, 'Unsupported payload');
|
||||
|
||||
@@ -6,6 +6,7 @@ export type IsOwnBackend = {
|
||||
requireAuth: boolean;
|
||||
registrationAllowed: boolean;
|
||||
allowAnyProxy: boolean;
|
||||
captchaDisabled: boolean;
|
||||
};
|
||||
|
||||
export function isOwnBackend(): IsOwnBackend | null {
|
||||
@@ -16,6 +17,7 @@ export function isOwnBackend(): IsOwnBackend | null {
|
||||
internalAuth: env.PUBLIC_INTERNAL_AUTH !== 'false',
|
||||
requireAuth: env.PUBLIC_REQUIRE_AUTH !== 'false',
|
||||
registrationAllowed: env.PUBLIC_REGISTRATION_ALLOWED === 'true',
|
||||
allowAnyProxy: env.PUBLIC_DANGEROUS_ALLOW_ANY_PROXY === 'true'
|
||||
allowAnyProxy: env.PUBLIC_DANGEROUS_ALLOW_ANY_PROXY === 'true',
|
||||
captchaDisabled: env.PUBLIC_CAPTCHA_DISABLED === 'true'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import type Peer from 'peerjs';
|
||||
import type { DataConnection } from 'peerjs';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { Preferences } from '@capacitor/preferences';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import {
|
||||
persist,
|
||||
createLocalStorage,
|
||||
@@ -23,7 +22,7 @@ import type {
|
||||
VideoPlay
|
||||
} from './api/model';
|
||||
import type { ParsedDescription } from './description';
|
||||
import { ensureNoTrailingSlash } from './misc';
|
||||
import { ensureNoTrailingSlash, getPublicEnv } from './misc';
|
||||
import type { EngineFallback } from './api/misc';
|
||||
|
||||
function createListenerFunctions(): {
|
||||
@@ -89,11 +88,9 @@ function ifNotWebDefault(givenValue: any, defaultValue: any): any {
|
||||
|
||||
export const invidiousInstanceStore: Writable<string | undefined> = persist(
|
||||
writable(
|
||||
!import.meta.env.VITE_DEFAULT_INVIDIOUS_INSTANCE || !env.PUBLIC_DEFAULT_INVIDIOUS_INSTANCE
|
||||
!getPublicEnv('DEFAULT_INVIDIOUS_INSTANCE')
|
||||
? undefined
|
||||
: ensureNoTrailingSlash(
|
||||
import.meta.env.VITE_DEFAULT_INVIDIOUS_INSTANCE || env.PUBLIC_DEFAULT_INVIDIOUS_INSTANCE
|
||||
)
|
||||
: ensureNoTrailingSlash(getPublicEnv('DEFAULT_INVIDIOUS_INSTANCE'))
|
||||
),
|
||||
createStorage(),
|
||||
'invidiousInstance'
|
||||
@@ -198,8 +195,7 @@ export const returnYtDislikesStore = persist(writable(false), createStorage(), '
|
||||
export const returnYTDislikesInstanceStore: Writable<string | null | undefined> = persist(
|
||||
writable(
|
||||
ifNotWebDefault(
|
||||
import.meta.env.VITE_DEFAULT_RETURNYTDISLIKES_INSTANCE ||
|
||||
env.PUBLIC_DEFAULT_RETURNYTDISLIKES_INSTANCE,
|
||||
getPublicEnv('DEFAULT_RETURNYTDISLIKES_INSTANCE'),
|
||||
'https://ryd-proxy.materialio.us'
|
||||
)
|
||||
),
|
||||
@@ -211,9 +207,7 @@ export const synciousStore = persist(writable(false), createStorage(), 'syncious
|
||||
export const synciousInstanceStore: Writable<string | null | undefined> = persist(
|
||||
writable(
|
||||
ifNotWebDefault(
|
||||
import.meta.env.VITE_DEFAULT_SYNCIOUS_INSTANCE ||
|
||||
import.meta.env.VITE_DEFAULT_API_EXTENDED_INSTANCE ||
|
||||
env.PUBLIC_DEFAULT_RETURNYTDISLIKES_INSTANCE,
|
||||
getPublicEnv('DEFAULT_SYNCIOUS_INSTANCE') || getPublicEnv('DEFAULT_API_EXTENDED_INSTANCE'),
|
||||
'https://extended-api.materialio.us'
|
||||
)
|
||||
),
|
||||
@@ -286,11 +280,7 @@ export const interfaceAndroidUseNativeShare = persist(
|
||||
|
||||
export const sponsorBlockStore = persist(writable(true), createStorage(), 'sponsorBlock');
|
||||
export const sponsorBlockUrlStore: Writable<string | null | undefined> = persist(
|
||||
writable(
|
||||
import.meta.env.VITE_DEFAULT_SPONSERBLOCK_INSTANCE ||
|
||||
env.PUBLIC_DEFAULT_SPONSERBLOCK_INSTANCE ||
|
||||
'https://sponsor.ajay.app'
|
||||
),
|
||||
writable(getPublicEnv('DEFAULT_SPONSERBLOCK_INSTANCE') || 'https://sponsor.ajay.app'),
|
||||
createStorage(),
|
||||
'sponsorBlockUrl'
|
||||
);
|
||||
@@ -311,22 +301,14 @@ export const sponsorBlockTimelineStore: Writable<boolean> = persist(
|
||||
);
|
||||
|
||||
export const deArrowInstanceStore = persist(
|
||||
writable(
|
||||
import.meta.env.VITE_DEFAULT_DEARROW_INSTANCE ||
|
||||
env.PUBLIC_DEFAULT_DEARROW_INSTANCE ||
|
||||
'https://sponsor.ajay.app'
|
||||
),
|
||||
writable(getPublicEnv('DEFAULT_DEARROW_INSTANCE') || 'https://sponsor.ajay.app'),
|
||||
createStorage(),
|
||||
'deArrowInstance'
|
||||
);
|
||||
export const deArrowEnabledStore = persist(writable(false), createStorage(), 'deArrowEnabled');
|
||||
export const deArrowTitlesOnly = persist(writable(true), createStorage(), 'deArrowTitlesOnly');
|
||||
export const deArrowThumbnailInstanceStore = persist(
|
||||
writable(
|
||||
import.meta.env.VITE_DEFAULT_DEARROW_THUMBNAIL_INSTANCE ||
|
||||
env.PUBLIC_DEFAULT_DEARROW_THUMBNAIL_INSTANCE ||
|
||||
'https://dearrow-thumb.ajay.app'
|
||||
),
|
||||
writable(getPublicEnv('DEFAULT_DEARROW_THUMBNAIL_INSTANCE') || 'https://dearrow-thumb.ajay.app'),
|
||||
createStorage(),
|
||||
'deArrowThumbnailInstance'
|
||||
);
|
||||
|
||||
@@ -81,22 +81,24 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<article
|
||||
class="surface-container-highest no-padding"
|
||||
style="width: 100%;height: fit-content;"
|
||||
>
|
||||
<altcha-widget
|
||||
challengeurl="/api/captcha"
|
||||
hidelogo
|
||||
hidefooter
|
||||
onstatechange={(ev) => {
|
||||
const { payload, state } = ev.detail;
|
||||
if (state === 'verified' && payload) {
|
||||
captchaPayload = payload;
|
||||
}
|
||||
}}
|
||||
></altcha-widget>
|
||||
</article>
|
||||
{#if !isOwnBackend()?.captchaDisabled}
|
||||
<article
|
||||
class="surface-container-highest no-padding"
|
||||
style="width: 100%;height: fit-content;"
|
||||
>
|
||||
<altcha-widget
|
||||
challengeurl="/api/captcha"
|
||||
hidelogo
|
||||
hidefooter
|
||||
onstatechange={(ev) => {
|
||||
const { payload, state } = ev.detail;
|
||||
if (state === 'verified' && payload) {
|
||||
captchaPayload = payload;
|
||||
}
|
||||
}}
|
||||
></altcha-widget>
|
||||
</article>
|
||||
{/if}
|
||||
|
||||
<nav class="right-align">
|
||||
<button
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { isOwnBackend } from '$lib/shared';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { env as privateEnv } from '$env/dynamic/private';
|
||||
|
||||
import { error } from '@sveltejs/kit';
|
||||
import { parse as tldParse } from 'tldts';
|
||||
@@ -14,6 +15,12 @@ const allowedBaseDomains: string[] = [
|
||||
'ajay.app'
|
||||
];
|
||||
|
||||
if (privateEnv.WHITELIST_BASE_DOMAIN) {
|
||||
for (const baseDomain of privateEnv.WHITELIST_BASE_DOMAIN.split(',')) {
|
||||
if (baseDomain) allowedBaseDomains.push(baseDomain);
|
||||
}
|
||||
}
|
||||
|
||||
const dynamicAllowDomainsEnvVars = [
|
||||
env.PUBLIC_DEFAULT_DEARROW_THUMBNAIL_INSTANCE,
|
||||
env.PUBLIC_DEFAULT_DEARROW_INSTANCE,
|
||||
@@ -73,18 +80,7 @@ async function proxyRequest(
|
||||
}
|
||||
}
|
||||
|
||||
const requestHeaders = new Headers(request.headers);
|
||||
for (const key of [
|
||||
'referer',
|
||||
'x-forwarded-for',
|
||||
'x-requested-with',
|
||||
'sec-ch-ua-mobile',
|
||||
'sec-ch-ua',
|
||||
'sec-ch-ua-platform',
|
||||
'content-length'
|
||||
]) {
|
||||
requestHeaders.delete(key);
|
||||
}
|
||||
const requestHeaders = new Headers();
|
||||
|
||||
requestHeaders.set('host', urlToProxyObj.host);
|
||||
requestHeaders.set('origin', urlToProxyObj.origin);
|
||||
@@ -123,7 +119,7 @@ async function proxyRequest(
|
||||
throw error(500, errorMsg);
|
||||
}
|
||||
|
||||
const responseHeaders = new Headers(response.headers);
|
||||
const responseHeaders = new Headers();
|
||||
responseHeaders.set('transfer-encoding', 'chunked');
|
||||
responseHeaders.delete('content-encoding');
|
||||
responseHeaders.set('access-control-allow-origin', request.headers.get('origin') ?? '');
|
||||
|
||||
@@ -16,7 +16,7 @@ const zUserCreate = z.object({
|
||||
cipher: z.string().max(255),
|
||||
nonce: z.string().max(255)
|
||||
}),
|
||||
captchaPayload: z.string()
|
||||
captchaPayload: z.string().optional()
|
||||
});
|
||||
|
||||
export async function POST({ request, cookies, locals }) {
|
||||
@@ -28,7 +28,7 @@ export async function POST({ request, cookies, locals }) {
|
||||
|
||||
if (!userToCreate.success) throw error(400);
|
||||
|
||||
await verifyCaptcha(userToCreate.data.captchaPayload, locals.captchaKey, 1);
|
||||
await verifyCaptcha(userToCreate.data.captchaPayload ?? '', locals.captchaKey, 1);
|
||||
|
||||
const createdUser = await createUser({
|
||||
username: userToCreate.data.username,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { verifyCaptcha } from '$lib/server/captcha';
|
||||
const zUserLogin = z.object({
|
||||
username: z.string(),
|
||||
passwordHash: z.string(),
|
||||
captchaPayload: z.string()
|
||||
captchaPayload: z.string().optional()
|
||||
});
|
||||
|
||||
export async function POST({ request, cookies, locals }) {
|
||||
@@ -20,7 +20,7 @@ export async function POST({ request, cookies, locals }) {
|
||||
|
||||
if (!userLogin.success) throw error(401);
|
||||
|
||||
await verifyCaptcha(userLogin.data.captchaPayload, locals.captchaKey, 1);
|
||||
await verifyCaptcha(userLogin.data.captchaPayload ?? '', locals.captchaKey, 1);
|
||||
|
||||
const userModel = await authenticateUser(userLogin.data.username, userLogin.data.passwordHash);
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
@@ -3,7 +3,7 @@ import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
LATEST_VERSION = "1.15.8"
|
||||
LATEST_VERSION = "1.15.9"
|
||||
RELEASE_DATE = datetime.now().strftime("%Y-%-m-%d") # Format: YYYY-M-D
|
||||
|
||||
WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious")
|
||||
|
||||
Reference in New Issue
Block a user