mirror of
https://github.com/ld3z/new-manga.git
synced 2026-11-09 21:23:45 -07:00
Compare commits
No commits in common. "f84046c66c3b9dd5b6cba1807756cca076d721df" and "019f86ad1300bd19e44f28bda8b35581e30130db" have entirely different histories.
f84046c66c
...
019f86ad13
9 changed files with 30 additions and 189 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
|
@ -12,7 +12,7 @@
|
|||
"@astrojs/tailwind": "^6.0.0",
|
||||
"@astrojs/vercel": "^8.1.0",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@types/node": "^22.13.9",
|
||||
"@types/node": "^22.13.7",
|
||||
"@vercel/analytics": "^1.5.0",
|
||||
"@vercel/kv": "^3.0.0",
|
||||
"astro": "^5.4.1",
|
||||
|
|
@ -1465,9 +1465,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.13.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz",
|
||||
"integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==",
|
||||
"version": "22.13.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.7.tgz",
|
||||
"integrity": "sha512-oU2q+BsQldB9lYxHNp/5aZO+/Bs0Usa74Abo9mAKulz4ahQyXRHK6UVKYIN8KSC8HXwhWSi7b49JnX+txuac0w==",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
"@astrojs/tailwind": "^6.0.0",
|
||||
"@astrojs/vercel": "^8.1.0",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@types/node": "^22.13.9",
|
||||
"@types/node": "^22.13.7",
|
||||
"@vercel/analytics": "^1.5.0",
|
||||
"@vercel/kv": "^3.0.0",
|
||||
"astro": "^5.4.1",
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 312 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.7 MiB |
|
|
@ -476,51 +476,17 @@ const languages = [
|
|||
`;
|
||||
|
||||
const response = await fetch(`/api/feed/${feedId}`);
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error(`Feed import failed with status ${response.status}: ${errorText}`);
|
||||
throw new Error(`Failed to fetch feed: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
if (!response.ok) throw new Error('Failed to fetch feed');
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Feed import data:', data); // Diagnostic log
|
||||
const { slugs: feedSlugs, lang } = await response.json();
|
||||
|
||||
// Validate response structure
|
||||
if (!data || typeof data !== 'object') {
|
||||
throw new Error('Invalid feed data format: not an object');
|
||||
}
|
||||
|
||||
const feedSlugs = data.slugs;
|
||||
const lang = data.lang;
|
||||
|
||||
// Validate slugs array
|
||||
if (!feedSlugs || !Array.isArray(feedSlugs)) {
|
||||
throw new Error('Invalid feed data: missing or invalid slugs array');
|
||||
}
|
||||
|
||||
// Validate language
|
||||
if (!lang || typeof lang !== 'string') {
|
||||
console.warn('Feed has missing or invalid language, using default');
|
||||
} else if (langSelect) {
|
||||
// Only update language if it's valid
|
||||
const validLanguages = Array.from(langSelect.options).map(opt => opt.value);
|
||||
if (validLanguages.includes(lang)) {
|
||||
langSelect.value = lang;
|
||||
} else {
|
||||
console.warn(`Feed has unsupported language: ${lang}`);
|
||||
}
|
||||
// Update language if provided
|
||||
if (lang && langSelect) {
|
||||
langSelect.value = lang;
|
||||
}
|
||||
|
||||
// Add new slugs to existing collection
|
||||
let importCount = 0;
|
||||
feedSlugs.forEach((slug: string) => {
|
||||
if (typeof slug === 'string' && slug.trim()) {
|
||||
slugs.add(slug.trim());
|
||||
importCount++;
|
||||
} else {
|
||||
console.warn('Skipping invalid slug in feed:', slug);
|
||||
}
|
||||
});
|
||||
feedSlugs.forEach((slug: string) => slugs.add(slug));
|
||||
|
||||
// Update UI
|
||||
updateSlugs();
|
||||
|
|
@ -529,7 +495,7 @@ const languages = [
|
|||
// Show success message
|
||||
const successDiv = document.createElement('div');
|
||||
successDiv.className = 'text-sm text-green-500 mt-1';
|
||||
successDiv.textContent = `Feed imported successfully! Added ${importCount} comics.`;
|
||||
successDiv.textContent = 'Feed imported successfully!';
|
||||
feedIdInput.parentNode?.appendChild(successDiv);
|
||||
setTimeout(() => successDiv.remove(), 3000);
|
||||
|
||||
|
|
@ -537,11 +503,9 @@ const languages = [
|
|||
console.error('Failed to import feed:', error);
|
||||
const errorDiv = document.createElement('div');
|
||||
errorDiv.className = 'text-sm text-red-500 mt-1';
|
||||
errorDiv.textContent = error instanceof Error
|
||||
? `Import failed: ${error.message}`
|
||||
: 'Failed to import feed. Please check the feed ID and try again.';
|
||||
errorDiv.textContent = 'Failed to import feed. Please check the feed ID and try again.';
|
||||
feedIdInput.parentNode?.appendChild(errorDiv);
|
||||
setTimeout(() => errorDiv.remove(), 5000);
|
||||
setTimeout(() => errorDiv.remove(), 3000);
|
||||
} finally {
|
||||
importFeedButton.disabled = false;
|
||||
importFeedButton.innerHTML = `
|
||||
|
|
|
|||
|
|
@ -74,43 +74,6 @@ export const backgrounds: BackgroundImage[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
url: "/sleepy.webp",
|
||||
title: "???",
|
||||
artist: "soaphisoap",
|
||||
source: "https://safebooru.org/index.php?page=post&s=view&id=5418793",
|
||||
socials: [
|
||||
{
|
||||
name: "Twitter",
|
||||
link: "https://x.com/soaphisoap",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
url: "/genshin.webp",
|
||||
title: "???",
|
||||
artist: "charlieinoni",
|
||||
source: "https://safebooru.org/index.php?page=post&s=view&id=5531630",
|
||||
socials: [
|
||||
{
|
||||
name: "Twitter",
|
||||
link: "https://x.com/charlieinoni",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
url: "/meata.webp",
|
||||
title: "???",
|
||||
artist: "???",
|
||||
source:
|
||||
"https://i.pximg.net/img-original/img/2025/03/03/15/13/24/127822472_p0.png",
|
||||
socials: [
|
||||
{
|
||||
name: "Twitter",
|
||||
link: "https://x.com/",
|
||||
},
|
||||
],
|
||||
},
|
||||
// Add more backgrounds here!
|
||||
// {
|
||||
// url: 'your-image-url',
|
||||
|
|
|
|||
|
|
@ -284,82 +284,19 @@ export async function checkCacheStatus(slugs: string[], lang: string): Promise<s
|
|||
}
|
||||
|
||||
export async function getFeedMapping(feedId: string): Promise<FeedMapping | null> {
|
||||
let foundMapping: string | null = null;
|
||||
let sourceRedisName: string = "";
|
||||
const redis = await getRedisClient();
|
||||
|
||||
// First try primary
|
||||
try {
|
||||
// Check primary database if available
|
||||
if (primaryClient?.status === 'ready') {
|
||||
try {
|
||||
await primaryClient.ping();
|
||||
const mapping = await primaryClient.get(feedId);
|
||||
if (mapping) {
|
||||
await primaryClient.expire(feedId, DEFAULT_CACHE_TTL);
|
||||
foundMapping = mapping;
|
||||
sourceRedisName = "Primary";
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Primary Redis connection failed while retrieving mapping:', error);
|
||||
// Fall through to try replicas
|
||||
}
|
||||
}
|
||||
const mapping = await redis.get(feedId);
|
||||
if (!mapping) return null;
|
||||
|
||||
// If not found in primary, check replicas
|
||||
if (!foundMapping) {
|
||||
for (const [url, client] of replicaClients.entries()) {
|
||||
if (client.status === 'ready') {
|
||||
try {
|
||||
await client.ping();
|
||||
console.log(`Trying to get key from ${getRedisName(url)}`);
|
||||
const mapping = await client.get(feedId);
|
||||
if (mapping) {
|
||||
await client.expire(feedId, DEFAULT_CACHE_TTL);
|
||||
foundMapping = mapping;
|
||||
sourceRedisName = getRedisName(url);
|
||||
break; // Exit the loop once we find the mapping
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`${getRedisName(url)} connection failed while retrieving mapping:`, error);
|
||||
// Continue to next replica
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
await redis.expire(feedId, DEFAULT_CACHE_TTL);
|
||||
|
||||
// If we found the mapping in any database, re-synchronize to all other databases
|
||||
if (foundMapping) {
|
||||
console.log(`Found mapping in ${sourceRedisName}. Re-synchronizing to all Redis instances...`);
|
||||
|
||||
// Parse data for returning
|
||||
const data = JSON.parse(foundMapping);
|
||||
const result = {
|
||||
slugs: data.slugs,
|
||||
lang: data.lang
|
||||
};
|
||||
|
||||
// Fire-and-forget re-sync to all databases
|
||||
(async () => {
|
||||
try {
|
||||
await writeToAllRedis(async (redis) => {
|
||||
await redis.set(
|
||||
feedId,
|
||||
foundMapping!,
|
||||
'EX',
|
||||
DEFAULT_CACHE_TTL
|
||||
);
|
||||
});
|
||||
console.log(`Successfully re-synchronized mapping for ${feedId} to all Redis instances`);
|
||||
} catch (syncError) {
|
||||
console.error('Error during Redis re-synchronization:', syncError);
|
||||
}
|
||||
})();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// If we get here, the key wasn't found in any database
|
||||
return null;
|
||||
const data = JSON.parse(mapping);
|
||||
return {
|
||||
slugs: data.slugs,
|
||||
lang: data.lang
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error retrieving feed mapping:', error);
|
||||
throw new Error('Failed to retrieve feed mapping');
|
||||
|
|
|
|||
|
|
@ -9,43 +9,20 @@ export const GET: APIRoute = async ({ params }) => {
|
|||
}
|
||||
|
||||
try {
|
||||
console.log(`Fetching feed mapping for ID: ${feedId}`);
|
||||
const mapping = await getFeedMapping(feedId);
|
||||
|
||||
if (!mapping) {
|
||||
console.error(`Feed not found for ID: ${feedId}`);
|
||||
return new Response(`Feed with ID "${feedId}" not found`, { status: 404 });
|
||||
return new Response("Feed not found", { status: 404 });
|
||||
}
|
||||
|
||||
// Validate mapping structure before returning
|
||||
if (!Array.isArray(mapping.slugs)) {
|
||||
console.error(`Invalid slugs in feed mapping for ID: ${feedId}`, mapping);
|
||||
return new Response(`Feed data corruption: invalid slugs format`, {
|
||||
status: 500
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure consistent response format
|
||||
const response = {
|
||||
slugs: mapping.slugs.filter(slug => typeof slug === 'string' && slug.trim()),
|
||||
lang: typeof mapping.lang === 'string' ? mapping.lang : 'en'
|
||||
};
|
||||
|
||||
console.log(`Successfully retrieved feed mapping for ID: ${feedId}`, response);
|
||||
|
||||
return new Response(JSON.stringify(response), {
|
||||
return new Response(JSON.stringify(mapping), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error fetching feed ${feedId}:`, error);
|
||||
|
||||
const errorMessage = error instanceof Error
|
||||
? `Internal server error: ${error.message}`
|
||||
: "Internal server error";
|
||||
|
||||
return new Response(errorMessage, { status: 500 });
|
||||
console.error('Error fetching feed:', error);
|
||||
return new Response("Internal server error", { status: 500 });
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue