fix: guest and admin logic

This commit is contained in:
2026-06-22 11:41:34 +07:00
parent b1a539235b
commit 860395cb14
61 changed files with 1291 additions and 244 deletions
+83 -57
View File
@@ -1,46 +1,61 @@
const loadScript = (src: string, fallbackSrc?: string): Promise<void> => {
const loadScript = (src: string, fallbackSrcs?: string[]): Promise<void> => {
return new Promise((resolve, reject) => {
if (
document.querySelector(`script[src="${src}"]`) ||
(fallbackSrc && document.querySelector(`script[src="${fallbackSrc}"]`))
) {
const allSrcs = [src, ...(fallbackSrcs || [])];
// Check if any of the scripts are already loaded
if (allSrcs.some(s => document.querySelector(`script[src="${s}"]`))) {
resolve();
return;
}
const script = document.createElement('script');
script.src = src;
script.onload = () => resolve();
script.onerror = () => {
if (fallbackSrc) {
console.warn(`Failed to load script ${src}. Trying fallback: ${fallbackSrc}`);
const fallbackScript = document.createElement('script');
fallbackScript.src = fallbackSrc;
fallbackScript.onload = () => resolve();
fallbackScript.onerror = () => reject(new Error(`Failed to load script ${fallbackSrc}`));
document.head.appendChild(fallbackScript);
} else {
reject(new Error(`Failed to load script ${src}`));
const tryLoadScript = (index: number) => {
if (index >= allSrcs.length) {
reject(new Error(`Failed to load script from any source: ${allSrcs.join(', ')}`));
return;
}
const currentSrc = allSrcs[index];
const script = document.createElement('script');
script.src = currentSrc;
script.onload = () => resolve();
script.onerror = () => {
console.warn(`Failed to load script ${currentSrc}. Trying next fallback...`);
const nextIndex = index + 1;
if (nextIndex < allSrcs.length) {
tryLoadScript(nextIndex);
} else {
reject(new Error(`Failed to load script from all sources: ${allSrcs.join(', ')}`));
}
};
document.head.appendChild(script);
};
document.head.appendChild(script);
tryLoadScript(0);
});
};
const loadModerationLibraries = async () => {
// Load TensorFlow first with fallback
// Load TensorFlow first with fallbacks
await loadScript(
'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs',
'https://unpkg.com/@tensorflow/tfjs'
['https://unpkg.com/@tensorflow/tfjs', 'https://esm.sh/@tensorflow/tfjs']
);
// Load models after tfjs is available, with fallbacks
// Load models after tfjs is available, with multiple fallbacks
await Promise.all([
loadScript(
'https://cdn.jsdelivr.net/npm/@tensorflow-models/blazeface',
'https://unpkg.com/@tensorflow-models/blazeface'
['https://unpkg.com/@tensorflow-models/blazeface', 'https://esm.sh/@tensorflow-models/blazeface']
),
// NSFWJS with 3 CDN fallbacks
loadScript(
'https://cdn.jsdelivr.net/npm/nsfwjs@2.4.0/dist/bundle.js',
'https://unpkg.com/nsfwjs@2.4.0/dist/bundle.js'
[
'https://unpkg.com/nsfwjs@2.4.0/dist/bundle.js',
'https://esm.sh/nsfwjs@2.4.0/dist/bundle.js'
]
)
]);
};
@@ -56,7 +71,13 @@ export const processImageModeration = async (file: File): Promise<{ file: File;
return { file, blocked: false };
}
await loadModerationLibraries();
// Try to load moderation libraries, but don't fail if they're unavailable
try {
await loadModerationLibraries();
} catch (libLoadErr) {
console.warn('Moderation libraries failed to load, proceeding without NSFW/Face blur checks:', libLoadErr);
return { file, blocked: false };
}
return new Promise((resolve) => {
const img = new Image();
@@ -73,48 +94,53 @@ export const processImageModeration = async (file: File): Promise<{ file: File;
if (blockNsfw) {
try {
const nsfwModel = await (window as any).nsfwjs.load();
const predictions = await nsfwModel.classify(canvas);
const pornOrHentai = predictions.find((p: any) => p.className === 'Porn' || p.className === 'Hentai');
const pornProb = pornOrHentai ? pornOrHentai.probability : 0;
if (pornProb > 0.5) {
resolve({ file, blocked: true });
return;
const nsfwModel = await (window as any).nsfwjs?.load();
if (nsfwModel) {
const predictions = await nsfwModel.classify(canvas);
const pornOrHentai = predictions.find((p: any) => p.className === 'Porn' || p.className === 'Hentai');
const pornProb = pornOrHentai ? pornOrHentai.probability : 0;
if (pornProb > 0.5) {
console.warn(`Image blocked by NSFW filter (probability: ${pornProb})`);
resolve({ file, blocked: true });
return;
}
}
} catch (e) {
console.error('NSFW validation error:', e);
console.warn('NSFW validation error (will allow upload):', e);
}
}
let modified = false;
if (blurFaces) {
try {
const blazefaceModel = await (window as any).blazeface.load();
const predictions = await blazefaceModel.estimateFaces(canvas, false);
if (predictions && predictions.length > 0) {
modified = true;
predictions.forEach((prediction: any) => {
const startX = prediction.topLeft[0];
const startY = prediction.topLeft[1];
const endX = prediction.bottomRight[0];
const endY = prediction.bottomRight[1];
const width = endX - startX;
const height = endY - startY;
const blazefaceModel = await (window as any).blazeface?.load();
if (blazefaceModel) {
const predictions = await blazefaceModel.estimateFaces(canvas, false);
if (predictions && predictions.length > 0) {
modified = true;
predictions.forEach((prediction: any) => {
const startX = prediction.topLeft[0];
const startY = prediction.topLeft[1];
const endX = prediction.bottomRight[0];
const endY = prediction.bottomRight[1];
const width = endX - startX;
const height = endY - startY;
const faceCanvas = document.createElement('canvas');
faceCanvas.width = width;
faceCanvas.height = height;
const faceCtx = faceCanvas.getContext('2d');
if (faceCtx) {
faceCtx.drawImage(canvas, startX, startY, width, height, 0, 0, width, height);
ctx.filter = 'blur(15px)';
ctx.drawImage(faceCanvas, startX, startY, width, height);
ctx.filter = 'none';
}
});
const faceCanvas = document.createElement('canvas');
faceCanvas.width = width;
faceCanvas.height = height;
const faceCtx = faceCanvas.getContext('2d');
if (faceCtx) {
faceCtx.drawImage(canvas, startX, startY, width, height, 0, 0, width, height);
ctx.filter = 'blur(15px)';
ctx.drawImage(faceCanvas, startX, startY, width, height);
ctx.filter = 'none';
}
});
}
}
} catch (e) {
console.error('Face blur error:', e);
console.warn('Face blur error (will skip face detection):', e);
}
}
@@ -137,7 +163,7 @@ export const processImageModeration = async (file: File): Promise<{ file: File;
img.src = URL.createObjectURL(file);
});
} catch (err) {
console.error('Image moderation failed:', err);
console.error('Image moderation process failed:', err);
return { file, blocked: false };
}
};