mirror of
https://github.com/ld3z/new-manga.git
synced 2026-11-09 20:53:45 -07:00
Compare commits
3 commits
5639af1b90
...
cc3d0e6dc8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc3d0e6dc8 | ||
|
|
b614d9fb3f | ||
|
|
ced8f26537 |
4 changed files with 160 additions and 29 deletions
45
package-lock.json
generated
45
package-lock.json
generated
|
|
@ -10,12 +10,12 @@
|
|||
"dependencies": {
|
||||
"@astrojs/rss": "^4.0.11",
|
||||
"@astrojs/tailwind": "^6.0.0",
|
||||
"@astrojs/vercel": "^8.0.6",
|
||||
"@astrojs/vercel": "^8.0.7",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@types/node": "^22.13.1",
|
||||
"@types/node": "^22.13.4",
|
||||
"@vercel/analytics": "^1.5.0",
|
||||
"@vercel/kv": "^3.0.0",
|
||||
"astro": "^5.2.5",
|
||||
"astro": "^5.3.0",
|
||||
"feed": "^4.2.2",
|
||||
"ioredis": "^5.5.0",
|
||||
"tailwindcss": "^3.4.1"
|
||||
|
|
@ -38,9 +38,9 @@
|
|||
"integrity": "sha512-bL/O7YBxsFt55YHU021oL+xz+B/9HvGNId3F9xURN16aeqDK9juHGktdkCSXz+U4nqFACq6ZFvWomOzhV+zfPw=="
|
||||
},
|
||||
"node_modules/@astrojs/internal-helpers": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.4.2.tgz",
|
||||
"integrity": "sha512-EdDWkC3JJVcpGpqJAU/5hSk2LKXyG3mNGkzGoAuyK+xoPHbaVdSuIWoN1QTnmK3N/gGfaaAfM8gO2KDCAW7S3w=="
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.5.1.tgz",
|
||||
"integrity": "sha512-M7rAge1n2+aOSxNvKUFa0u/KFn0W+sZy7EW91KOSERotm2Ti8qs+1K0xx3zbOxtAVrmJb5/J98eohVvvEqtNkw=="
|
||||
},
|
||||
"node_modules/@astrojs/markdown-remark": {
|
||||
"version": "6.1.0",
|
||||
|
|
@ -121,15 +121,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@astrojs/vercel": {
|
||||
"version": "8.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/vercel/-/vercel-8.0.6.tgz",
|
||||
"integrity": "sha512-5lchUcf1HC8O8zQVJSTyGxZ9rxDsOj6NeueSC+qO5fZDlQXxf4avEzuJaHN2q1qNS9fZLOGEzcPgT0hNfHYBXg==",
|
||||
"version": "8.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/vercel/-/vercel-8.0.7.tgz",
|
||||
"integrity": "sha512-2DPqPGZ/Et4afpJt8CAoIql2S649rzEmLBNJPHUGBysP0JklMaZ05HhQVKr+qEhZSm3NrVccxpkiIG/eLW3nPg==",
|
||||
"dependencies": {
|
||||
"@astrojs/internal-helpers": "^0.4.2",
|
||||
"@astrojs/internal-helpers": "^0.5.1",
|
||||
"@vercel/analytics": "^1.4.1",
|
||||
"@vercel/edge": "^1.2.1",
|
||||
"@vercel/nft": "^0.29.0",
|
||||
"@vercel/routing-utils": "5.0.2",
|
||||
"@vercel/routing-utils": "^5.0.4",
|
||||
"esbuild": "^0.24.0",
|
||||
"fast-glob": "^3.3.3"
|
||||
},
|
||||
|
|
@ -1464,9 +1464,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz",
|
||||
"integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==",
|
||||
"version": "22.13.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz",
|
||||
"integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
|
|
@ -1568,9 +1568,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vercel/routing-utils": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/routing-utils/-/routing-utils-5.0.2.tgz",
|
||||
"integrity": "sha512-uJViB3+HEo+kzHYELs7cQWX5k0kCNvq9G/8nJQX8mP5Ta0fG68CBRmOaaG8A+2xbtTp/QuGORIwV8CsI9ebcNg==",
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/routing-utils/-/routing-utils-5.0.4.tgz",
|
||||
"integrity": "sha512-4ke67zkXVi2fRZdoYckABcsSkRC9CnrdadOGxoS/Bk22+ObHjGQWvUHExRSXh339anwu9YY7ZacNSGH4gUnTQA==",
|
||||
"dependencies": {
|
||||
"path-to-regexp": "6.1.0",
|
||||
"path-to-regexp-updated": "npm:path-to-regexp@6.3.0"
|
||||
|
|
@ -1753,9 +1753,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "5.2.5",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.2.5.tgz",
|
||||
"integrity": "sha512-AYXyYkc+c5xbKTm48FyQA91y81nXyNPAaoyafR0LUugE4lAwuvIUcXDBfMzmbuP1lGRvsE33G2oypv6gbGaPFg==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.3.0.tgz",
|
||||
"integrity": "sha512-e88l/Yk/6enR/ZDddLbqtM+oblBFk5mneNSmNesyVYGL/6Dj4UA67GPAZOk79VxT5dbLlclZSyyw/wlxN1aj3A==",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^2.10.3",
|
||||
"@astrojs/internal-helpers": "0.5.1",
|
||||
|
|
@ -1833,11 +1833,6 @@
|
|||
"sharp": "^0.33.3"
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@astrojs/internal-helpers": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.5.1.tgz",
|
||||
"integrity": "sha512-M7rAge1n2+aOSxNvKUFa0u/KFn0W+sZy7EW91KOSERotm2Ti8qs+1K0xx3zbOxtAVrmJb5/J98eohVvvEqtNkw=="
|
||||
},
|
||||
"node_modules/astro/node_modules/@vercel/kv": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/kv/-/kv-1.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@
|
|||
"dependencies": {
|
||||
"@astrojs/rss": "^4.0.11",
|
||||
"@astrojs/tailwind": "^6.0.0",
|
||||
"@astrojs/vercel": "^8.0.6",
|
||||
"@astrojs/vercel": "^8.0.7",
|
||||
"@floating-ui/dom": "^1.6.13",
|
||||
"@types/node": "^22.13.1",
|
||||
"@types/node": "^22.13.4",
|
||||
"@vercel/analytics": "^1.5.0",
|
||||
"@vercel/kv": "^3.0.0",
|
||||
"astro": "^5.2.5",
|
||||
"astro": "^5.3.0",
|
||||
"feed": "^4.2.2",
|
||||
"ioredis": "^5.5.0",
|
||||
"tailwindcss": "^3.4.1"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
---
|
||||
import { getFeedMapping } from '../lib/db';
|
||||
interface Props {
|
||||
currentSlugs?: string[];
|
||||
}
|
||||
|
|
@ -42,11 +43,43 @@ const languages = [
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
id="feed-id-input"
|
||||
placeholder="Enter feed ID to import..."
|
||||
class="flex-1 px-3 sm:px-4 py-2 bg-primary rounded-lg border border-hover/30 focus:outline-none focus:border-accent/50 transition-colors text-sm sm:text-base"
|
||||
/>
|
||||
<button
|
||||
id="import-feed"
|
||||
class="px-4 py-2 bg-secondary hover:bg-hover transition-colors rounded-lg text-sm sm:text-base flex items-center gap-2"
|
||||
>
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/>
|
||||
</svg>
|
||||
Import Feed
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="search-results" class="hidden space-y-2">
|
||||
<!-- Search results will be populated here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 class="text-lg font-medium">Selected Comics</h2>
|
||||
<button
|
||||
id="clear-all"
|
||||
class="px-3 py-1.5 text-sm text-red-500 hover:text-red-600 hover:bg-red-500/10 rounded-lg transition-colors flex items-center gap-2"
|
||||
style="display: none;"
|
||||
>
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
|
||||
</svg>
|
||||
Clear All
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="slug-list" class="space-y-2">
|
||||
{currentSlugs.map(slug => (
|
||||
<div class="flex items-center justify-between p-3 bg-primary rounded-lg border border-hover/30" data-slug={slug}>
|
||||
|
|
@ -95,6 +128,9 @@ const languages = [
|
|||
const slugList = document.getElementById('slug-list');
|
||||
const generateRSSLink = document.getElementById('generate-rss');
|
||||
const langSelect = document.getElementById('lang-select') as HTMLSelectElement;
|
||||
const feedIdInput = document.getElementById('feed-id-input') as HTMLInputElement;
|
||||
const importFeedButton = document.getElementById('import-feed');
|
||||
const clearAllButton = document.getElementById('clear-all');
|
||||
|
||||
// Load saved slugs from localStorage
|
||||
let slugs = new Set(JSON.parse(localStorage.getItem('comic-slugs') || '[]'));
|
||||
|
|
@ -103,8 +139,23 @@ const languages = [
|
|||
localStorage.setItem('comic-slugs', JSON.stringify([...slugs]));
|
||||
updateRSSLink();
|
||||
renderSlugs();
|
||||
updateClearAllButton();
|
||||
}
|
||||
|
||||
function updateClearAllButton() {
|
||||
if (clearAllButton) {
|
||||
clearAllButton.style.display = slugs.size > 0 ? 'flex' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all functionality
|
||||
clearAllButton?.addEventListener('click', () => {
|
||||
if (confirm('Are you sure you want to clear all comics? This cannot be undone.')) {
|
||||
slugs.clear();
|
||||
updateSlugs();
|
||||
}
|
||||
});
|
||||
|
||||
async function renderSlugs() {
|
||||
if (!slugList) return;
|
||||
|
||||
|
|
@ -410,9 +461,66 @@ const languages = [
|
|||
}
|
||||
}
|
||||
|
||||
// Import feed functionality
|
||||
importFeedButton?.addEventListener('click', async () => {
|
||||
const feedId = feedIdInput.value.trim();
|
||||
if (!feedId) return;
|
||||
|
||||
try {
|
||||
importFeedButton.disabled = true;
|
||||
importFeedButton.innerHTML = `
|
||||
<svg class="w-4 h-4 animate-spin" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none" stroke-linecap="round"/>
|
||||
</svg>
|
||||
Importing...
|
||||
`;
|
||||
|
||||
const response = await fetch(`/api/feed/${feedId}`);
|
||||
if (!response.ok) throw new Error('Failed to fetch feed');
|
||||
|
||||
const { slugs: feedSlugs, lang } = await response.json();
|
||||
|
||||
// Update language if provided
|
||||
if (lang && langSelect) {
|
||||
langSelect.value = lang;
|
||||
}
|
||||
|
||||
// Add new slugs to existing collection
|
||||
feedSlugs.forEach((slug: string) => slugs.add(slug));
|
||||
|
||||
// Update UI
|
||||
updateSlugs();
|
||||
feedIdInput.value = '';
|
||||
|
||||
// Show success message
|
||||
const successDiv = document.createElement('div');
|
||||
successDiv.className = 'text-sm text-green-500 mt-1';
|
||||
successDiv.textContent = 'Feed imported successfully!';
|
||||
feedIdInput.parentNode?.appendChild(successDiv);
|
||||
setTimeout(() => successDiv.remove(), 3000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to import feed:', error);
|
||||
const errorDiv = document.createElement('div');
|
||||
errorDiv.className = 'text-sm text-red-500 mt-1';
|
||||
errorDiv.textContent = 'Failed to import feed. Please check the feed ID and try again.';
|
||||
feedIdInput.parentNode?.appendChild(errorDiv);
|
||||
setTimeout(() => errorDiv.remove(), 3000);
|
||||
} finally {
|
||||
importFeedButton.disabled = false;
|
||||
importFeedButton.innerHTML = `
|
||||
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/>
|
||||
</svg>
|
||||
Import Feed
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
// Initial render
|
||||
renderSlugs();
|
||||
updateRSSLink();
|
||||
updateClearAllButton();
|
||||
|
||||
// Update RSS link when language changes
|
||||
langSelect?.addEventListener('change', () => updateRSSLink());
|
||||
|
|
|
|||
28
src/pages/api/feed/[feedId].ts
Normal file
28
src/pages/api/feed/[feedId].ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import type { APIRoute } from "astro";
|
||||
import { getFeedMapping } from '../../../lib/db';
|
||||
|
||||
export const GET: APIRoute = async ({ params }) => {
|
||||
const { feedId } = params;
|
||||
|
||||
if (!feedId) {
|
||||
return new Response("Feed ID not provided", { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
const mapping = await getFeedMapping(feedId);
|
||||
|
||||
if (!mapping) {
|
||||
return new Response("Feed not found", { status: 404 });
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify(mapping), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
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