mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-03 04:40:09 +00:00
Implement file hash-based library matching and remove fuzzy ASIN matching
Adds file hash-based matching for Audiobookshelf library items to ensure 100% accurate ASIN assignment for RMAB-organized content. Removes fuzzy matching from library availability checks, making all matching ASIN-only to eliminate false positives and race conditions. Updates database schema, processors, and matcher utilities; adds new tests and documentation for the new matching strategy. Removes obsolete scripts, Dockerfile, and related tests; updates docker-compose for test environments.
This commit is contained in:
@@ -69,22 +69,20 @@ export const saveTabSettings = async (
|
||||
break;
|
||||
|
||||
case 'auth':
|
||||
// Save OIDC settings if enabled
|
||||
if (settings.oidc.enabled) {
|
||||
const oidcPayload = {
|
||||
...settings.oidc,
|
||||
allowedEmails: parseCommaSeparatedToArray(settings.oidc.allowedEmails),
|
||||
allowedUsernames: parseCommaSeparatedToArray(settings.oidc.allowedUsernames),
|
||||
};
|
||||
// Always save OIDC settings (including enabled/disabled state)
|
||||
const oidcPayload = {
|
||||
...settings.oidc,
|
||||
allowedEmails: parseCommaSeparatedToArray(settings.oidc.allowedEmails),
|
||||
allowedUsernames: parseCommaSeparatedToArray(settings.oidc.allowedUsernames),
|
||||
};
|
||||
|
||||
await fetchWithAuth('/api/admin/settings/oidc', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(oidcPayload),
|
||||
}).then(res => {
|
||||
if (!res.ok) throw new Error('Failed to save OIDC settings');
|
||||
});
|
||||
}
|
||||
await fetchWithAuth('/api/admin/settings/oidc', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(oidcPayload),
|
||||
}).then(res => {
|
||||
if (!res.ok) throw new Error('Failed to save OIDC settings');
|
||||
});
|
||||
|
||||
// Save registration settings
|
||||
await fetchWithAuth('/api/admin/settings/registration', {
|
||||
@@ -147,12 +145,22 @@ export const saveTabSettings = async (
|
||||
*/
|
||||
export const validateAuthSettings = (settings: Settings): { valid: boolean; message?: string } => {
|
||||
if (settings.backendMode === 'audiobookshelf') {
|
||||
// Case 1: No auth methods enabled and no local users - complete lockout
|
||||
if (!settings.oidc.enabled && !settings.registration.enabled && !settings.hasLocalUsers) {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'At least one authentication method must be enabled (OIDC or Manual Registration) since no local users exist. Otherwise, you will be locked out of the system.',
|
||||
};
|
||||
}
|
||||
|
||||
// Case 2: Only manual registration enabled, but no local admin users
|
||||
// This would allow new registrations, but no one can access admin features or approve registrations
|
||||
if (!settings.oidc.enabled && settings.registration.enabled && !settings.hasLocalAdmins) {
|
||||
return {
|
||||
valid: false,
|
||||
message: 'Manual registration is enabled but no local admin users exist. New users will be able to register but you will be locked out of admin features. Please enable OIDC or ensure at least one local admin user exists.',
|
||||
};
|
||||
}
|
||||
}
|
||||
return { valid: true };
|
||||
};
|
||||
@@ -178,7 +186,14 @@ export const getTabValidation = (
|
||||
case 'library':
|
||||
return settings.backendMode === 'plex' ? validated.plex : validated.audiobookshelf;
|
||||
case 'auth':
|
||||
return validated.oidc || validated.registration;
|
||||
// If OIDC is enabled, it must be validated
|
||||
// If OIDC is disabled, we don't require validation for it
|
||||
// Registration doesn't require explicit validation (just a toggle)
|
||||
if (settings.oidc.enabled) {
|
||||
return validated.oidc;
|
||||
}
|
||||
// If OIDC is disabled, allow saving without validation
|
||||
return true;
|
||||
case 'prowlarr':
|
||||
// Only require validation if URL or API key changed
|
||||
// If only indexers/flags changed, allow saving without test
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
export interface Settings {
|
||||
backendMode: 'plex' | 'audiobookshelf';
|
||||
hasLocalUsers: boolean;
|
||||
hasLocalAdmins: boolean;
|
||||
audibleRegion: string;
|
||||
plex: PlexSettings;
|
||||
audiobookshelf: AudiobookshelfSettings;
|
||||
@@ -139,7 +140,8 @@ export interface IndexerConfig {
|
||||
privacy: string;
|
||||
enabled: boolean;
|
||||
priority: number;
|
||||
seedingTimeMinutes: number;
|
||||
seedingTimeMinutes?: number; // Torrents only
|
||||
removeAfterProcessing?: boolean; // Usenet only
|
||||
rssEnabled: boolean;
|
||||
categories?: number[];
|
||||
supportsRss?: boolean;
|
||||
@@ -151,8 +153,10 @@ export interface IndexerConfig {
|
||||
export interface SavedIndexerConfig {
|
||||
id: number;
|
||||
name: string;
|
||||
protocol: string;
|
||||
priority: number;
|
||||
seedingTimeMinutes: number;
|
||||
seedingTimeMinutes?: number; // Torrents only
|
||||
removeAfterProcessing?: boolean; // Usenet only
|
||||
rssEnabled: boolean;
|
||||
categories: number[];
|
||||
}
|
||||
|
||||
@@ -99,14 +99,26 @@ export default function AdminSettings() {
|
||||
// Extract configured indexers (enabled ones)
|
||||
const configured = (data.indexers || [])
|
||||
.filter((idx: IndexerConfig) => idx.enabled)
|
||||
.map((idx: IndexerConfig) => ({
|
||||
id: idx.id,
|
||||
name: idx.name,
|
||||
priority: idx.priority,
|
||||
seedingTimeMinutes: idx.seedingTimeMinutes,
|
||||
rssEnabled: idx.rssEnabled,
|
||||
categories: idx.categories || [3030],
|
||||
}));
|
||||
.map((idx: IndexerConfig) => {
|
||||
const config: any = {
|
||||
id: idx.id,
|
||||
name: idx.name,
|
||||
protocol: idx.protocol,
|
||||
priority: idx.priority,
|
||||
rssEnabled: idx.rssEnabled,
|
||||
categories: idx.categories || [3030],
|
||||
};
|
||||
|
||||
// Add protocol-specific fields
|
||||
const isTorrent = idx.protocol?.toLowerCase() === 'torrent';
|
||||
if (isTorrent) {
|
||||
config.seedingTimeMinutes = idx.seedingTimeMinutes ?? 0;
|
||||
} else {
|
||||
config.removeAfterProcessing = idx.removeAfterProcessing ?? true;
|
||||
}
|
||||
|
||||
return config;
|
||||
});
|
||||
setConfiguredIndexers(configured);
|
||||
setOriginalConfiguredIndexers(JSON.parse(JSON.stringify(configured)));
|
||||
} else {
|
||||
|
||||
@@ -74,6 +74,12 @@ export function AuthTab({
|
||||
!settings.registration.enabled &&
|
||||
!settings.hasLocalUsers;
|
||||
|
||||
// Check if only manual registration is enabled but no admin users exist
|
||||
const showNoAdminWarning = settings.backendMode === 'audiobookshelf' &&
|
||||
!settings.oidc.enabled &&
|
||||
settings.registration.enabled &&
|
||||
!settings.hasLocalAdmins;
|
||||
|
||||
// Check if registration is disabled but local users can still log in
|
||||
const showRegistrationDisabledInfo = settings.backendMode === 'audiobookshelf' &&
|
||||
!settings.oidc.enabled &&
|
||||
@@ -122,6 +128,26 @@ export function AuthTab({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Warning: Only manual registration enabled but no admin users exist */}
|
||||
{showNoAdminWarning && (
|
||||
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4">
|
||||
<div className="flex gap-3">
|
||||
<svg className="w-5 h-5 text-red-500 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-red-800 dark:text-red-200">
|
||||
No Admin Users Exist
|
||||
</h3>
|
||||
<p className="text-sm text-red-700 dark:text-red-300 mt-1">
|
||||
Manual registration is enabled but no local admin users exist. New users will be able to register but you will be locked out of admin features.
|
||||
Please enable OIDC or ensure at least one local admin user exists before saving.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Info: Registration disabled but local users can still log in */}
|
||||
{showRegistrationDisabledInfo && (
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
|
||||
|
||||
Reference in New Issue
Block a user