mirror of
https://github.com/v0l/route96.git
synced 2025-06-19 23:08:59 +00:00
fix: AI slop
This commit is contained in:
@ -35,19 +35,20 @@ export default function MirrorSuggestions({ servers }: MirrorSuggestionsProps) {
|
||||
|
||||
async function fetchSuggestions() {
|
||||
if (!pub || !login?.pubkey) return;
|
||||
|
||||
if (loading) return;
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(undefined);
|
||||
|
||||
|
||||
const fileMap: Map<string, FileMirrorSuggestion> = new Map();
|
||||
|
||||
|
||||
// Fetch files from each server
|
||||
for (const serverUrl of servers) {
|
||||
try {
|
||||
const blossom = new Blossom(serverUrl, pub);
|
||||
const files = await blossom.list(login.pubkey);
|
||||
|
||||
|
||||
for (const file of files) {
|
||||
const suggestion = fileMap.get(file.sha256);
|
||||
if (suggestion) {
|
||||
@ -68,7 +69,7 @@ export default function MirrorSuggestions({ servers }: MirrorSuggestionsProps) {
|
||||
// Continue with other servers instead of failing completely
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Determine missing servers for each file
|
||||
for (const suggestion of fileMap.values()) {
|
||||
for (const serverUrl of servers) {
|
||||
@ -77,12 +78,12 @@ export default function MirrorSuggestions({ servers }: MirrorSuggestionsProps) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Filter to only files that are missing from at least one server and available on at least one
|
||||
const filteredSuggestions = Array.from(fileMap.values()).filter(
|
||||
s => s.missing_from.length > 0 && s.available_on.length > 0
|
||||
);
|
||||
|
||||
|
||||
setSuggestions(filteredSuggestions);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
@ -97,23 +98,23 @@ export default function MirrorSuggestions({ servers }: MirrorSuggestionsProps) {
|
||||
|
||||
async function mirrorFile(suggestion: FileMirrorSuggestion, targetServer: string) {
|
||||
if (!pub) return;
|
||||
|
||||
|
||||
const mirrorKey = `${suggestion.sha256}-${targetServer}`;
|
||||
setMirroring(prev => new Set(prev.add(mirrorKey)));
|
||||
|
||||
|
||||
try {
|
||||
const blossom = new Blossom(targetServer, pub);
|
||||
await blossom.mirror(suggestion.url);
|
||||
|
||||
|
||||
// Update suggestions by removing this server from missing_from
|
||||
setSuggestions(prev =>
|
||||
prev.map(s =>
|
||||
s.sha256 === suggestion.sha256
|
||||
setSuggestions(prev =>
|
||||
prev.map(s =>
|
||||
s.sha256 === suggestion.sha256
|
||||
? {
|
||||
...s,
|
||||
available_on: [...s.available_on, targetServer],
|
||||
missing_from: s.missing_from.filter(server => server !== targetServer)
|
||||
}
|
||||
...s,
|
||||
available_on: [...s.available_on, targetServer],
|
||||
missing_from: s.missing_from.filter(server => server !== targetServer)
|
||||
}
|
||||
: s
|
||||
).filter(s => s.missing_from.length > 0) // Remove suggestions with no missing servers
|
||||
);
|
||||
@ -174,14 +175,14 @@ export default function MirrorSuggestions({ servers }: MirrorSuggestionsProps) {
|
||||
<p className="text-gray-400 mb-6">
|
||||
The following files are missing from some of your servers and can be mirrored:
|
||||
</p>
|
||||
|
||||
|
||||
<div className="space-y-4">
|
||||
{suggestions.map((suggestion) => (
|
||||
<div key={suggestion.sha256} className="bg-gray-800 border border-gray-700 rounded-lg p-4">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium text-gray-300 mb-1">
|
||||
File: {suggestion.sha256.substring(0, 16)}...
|
||||
File: {suggestion.sha256}
|
||||
</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
Size: {FormatBytes(suggestion.size)}
|
||||
@ -189,7 +190,7 @@ export default function MirrorSuggestions({ servers }: MirrorSuggestionsProps) {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<p className="text-xs text-green-400 mb-1">Available on:</p>
|
||||
@ -201,14 +202,14 @@ export default function MirrorSuggestions({ servers }: MirrorSuggestionsProps) {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<p className="text-xs text-red-400 mb-1">Missing from:</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{suggestion.missing_from.map((server) => {
|
||||
const mirrorKey = `${suggestion.sha256}-${server}`;
|
||||
const isMirroring = mirroring.has(mirrorKey);
|
||||
|
||||
|
||||
return (
|
||||
<div key={server} className="flex items-center gap-2">
|
||||
<span className="text-xs bg-red-900/30 text-red-300 px-2 py-1 rounded">
|
||||
|
@ -43,3 +43,6 @@ export function FormatBytes(b: number, f?: number) {
|
||||
if (b >= kiB) return (b / kiB).toFixed(f) + " KiB";
|
||||
return b.toFixed(f) + " B";
|
||||
}
|
||||
|
||||
export const ServerUrl =
|
||||
import.meta.env.VITE_API_URL || `${location.protocol}//${location.host}`;
|
@ -1,26 +1,22 @@
|
||||
import { useMemo } from "react";
|
||||
import useLogin from "./login";
|
||||
import { useRequestBuilder } from "@snort/system-react";
|
||||
import { EventKind, RequestBuilder } from "@snort/system";
|
||||
import { appendDedupe, dedupe, removeUndefined, sanitizeRelayUrl } from "@snort/shared";
|
||||
import { ServerUrl } from "../const";
|
||||
|
||||
const DefaultMediaServers = ["https://cdn.satellite.earth/", "https://cdn.self.hosted/"];
|
||||
const DefaultMediaServers = ["https://blossom.band/", "https://blossom.primal.net", ServerUrl];
|
||||
|
||||
export function useBlossomServers() {
|
||||
const login = useLogin();
|
||||
|
||||
return useMemo(() => {
|
||||
// For now, just return default servers
|
||||
// TODO: Implement proper nostr event kind 10063 querying when system supports it
|
||||
const servers = DefaultMediaServers;
|
||||
const rb = new RequestBuilder("media-servers");
|
||||
if (login?.pubkey) {
|
||||
rb.withFilter()
|
||||
.kinds([10_063 as EventKind])
|
||||
.authors([login.pubkey]);
|
||||
}
|
||||
const req = useRequestBuilder(rb);
|
||||
|
||||
return {
|
||||
servers,
|
||||
addServer: async (serverUrl: string) => {
|
||||
// TODO: Implement adding server to event kind 10063
|
||||
console.log("Adding server not implemented yet:", serverUrl);
|
||||
},
|
||||
removeServer: async (serverUrl: string) => {
|
||||
// TODO: Implement removing server from event kind 10063
|
||||
console.log("Removing server not implemented yet:", serverUrl);
|
||||
},
|
||||
};
|
||||
}, [login?.pubkey]);
|
||||
const servers = req === undefined ? undefined : dedupe(removeUndefined(req.flatMap((e) => e.tags.filter(t => t[0] === "server").map((t) => sanitizeRelayUrl(t[1])))));
|
||||
return appendDedupe(DefaultMediaServers, servers);
|
||||
}
|
@ -65,7 +65,7 @@ export class Blossom {
|
||||
const rsp = await this.#req(
|
||||
"mirror",
|
||||
"PUT",
|
||||
"mirror",
|
||||
"upload",
|
||||
JSON.stringify({ url }),
|
||||
undefined,
|
||||
{
|
||||
|
@ -11,7 +11,7 @@ import useLogin from "../hooks/login";
|
||||
import usePublisher from "../hooks/publisher";
|
||||
import { Nip96, Nip96FileList } from "../upload/nip96";
|
||||
import { AdminSelf, Route96 } from "../upload/admin";
|
||||
import { FormatBytes } from "../const";
|
||||
import { FormatBytes, ServerUrl } from "../const";
|
||||
import { UploadProgress } from "../upload/progress";
|
||||
|
||||
export default function Upload() {
|
||||
@ -25,14 +25,11 @@ export default function Upload() {
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
const [uploadProgress, setUploadProgress] = useState<UploadProgress>();
|
||||
|
||||
const { servers: blossomServers } = useBlossomServers();
|
||||
const blossomServers = useBlossomServers();
|
||||
|
||||
const login = useLogin();
|
||||
const pub = usePublisher();
|
||||
|
||||
const url =
|
||||
import.meta.env.VITE_API_URL || `${location.protocol}//${location.host}`;
|
||||
|
||||
// Check if file should have compression enabled by default
|
||||
const shouldCompress = (file: File) => {
|
||||
return file.type.startsWith('video/') || file.type.startsWith('image/');
|
||||
@ -52,7 +49,7 @@ export default function Upload() {
|
||||
setUploadProgress(progress);
|
||||
};
|
||||
|
||||
const uploader = new Blossom(url, pub);
|
||||
const uploader = new Blossom(ServerUrl, pub);
|
||||
// Use compression by default for video and image files, unless explicitly disabled
|
||||
const useCompression = shouldCompress(file) && !noCompress;
|
||||
const result = useCompression
|
||||
@ -75,11 +72,11 @@ export default function Upload() {
|
||||
|
||||
async function handleFileSelection() {
|
||||
if (isUploading) return;
|
||||
|
||||
|
||||
try {
|
||||
const files = await openFiles();
|
||||
if (!files || files.length === 0) return;
|
||||
|
||||
|
||||
// Start uploading each file immediately
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
@ -99,7 +96,7 @@ export default function Upload() {
|
||||
if (!pub) return;
|
||||
try {
|
||||
setError(undefined);
|
||||
const uploader = new Nip96(url, pub);
|
||||
const uploader = new Nip96(ServerUrl, pub);
|
||||
await uploader.loadInfo();
|
||||
const result = await uploader.listFiles(n, 50);
|
||||
setListedFiles(result);
|
||||
@ -115,14 +112,14 @@ export default function Upload() {
|
||||
}
|
||||
}
|
||||
},
|
||||
[pub, url],
|
||||
[pub],
|
||||
);
|
||||
|
||||
async function deleteFile(id: string) {
|
||||
if (!pub) return;
|
||||
try {
|
||||
setError(undefined);
|
||||
const uploader = new Blossom(url, pub);
|
||||
const uploader = new Blossom(ServerUrl, pub);
|
||||
await uploader.delete(id);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
@ -143,10 +140,10 @@ export default function Upload() {
|
||||
|
||||
useEffect(() => {
|
||||
if (pub && !self) {
|
||||
const r96 = new Route96(url, pub);
|
||||
const r96 = new Route96(ServerUrl, pub);
|
||||
r96.getSelf().then((v) => setSelf(v.data));
|
||||
}
|
||||
}, [pub, self, url]);
|
||||
}, [pub, self]);
|
||||
|
||||
if (!login) {
|
||||
return (
|
||||
@ -189,8 +186,8 @@ export default function Upload() {
|
||||
|
||||
{/* Upload Progress */}
|
||||
{isUploading && uploadProgress && (
|
||||
<ProgressBar
|
||||
progress={uploadProgress}
|
||||
<ProgressBar
|
||||
progress={uploadProgress}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -240,13 +237,12 @@ export default function Upload() {
|
||||
</div>
|
||||
<div className="w-full bg-gray-700 rounded-full h-2.5">
|
||||
<div
|
||||
className={`h-2.5 rounded-full transition-all duration-300 ${
|
||||
self.total_size / self.total_available_quota > 0.8
|
||||
className={`h-2.5 rounded-full transition-all duration-300 ${self.total_size / self.total_available_quota > 0.8
|
||||
? "bg-red-500"
|
||||
: self.total_size / self.total_available_quota > 0.6
|
||||
? "bg-yellow-500"
|
||||
: "bg-green-500"
|
||||
}`}
|
||||
}`}
|
||||
style={{
|
||||
width: `${Math.min(100, (self.total_size / self.total_available_quota) * 100)}%`,
|
||||
}}
|
||||
@ -261,13 +257,12 @@ export default function Upload() {
|
||||
% used
|
||||
</span>
|
||||
<span
|
||||
className={`${
|
||||
self.total_size / self.total_available_quota > 0.8
|
||||
className={`${self.total_size / self.total_available_quota > 0.8
|
||||
? "text-red-400"
|
||||
: self.total_size / self.total_available_quota > 0.6
|
||||
? "text-yellow-400"
|
||||
: "text-green-400"
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{FormatBytes(
|
||||
Math.max(
|
||||
@ -332,7 +327,7 @@ export default function Upload() {
|
||||
{showPaymentFlow && pub && (
|
||||
<div className="card">
|
||||
<PaymentFlow
|
||||
route96={new Route96(url, pub)}
|
||||
route96={new Route96(ServerUrl, pub)}
|
||||
onPaymentRequested={(pr) => {
|
||||
console.log("Payment requested:", pr);
|
||||
}}
|
||||
@ -342,9 +337,9 @@ export default function Upload() {
|
||||
)}
|
||||
|
||||
{/* Mirror Suggestions */}
|
||||
{blossomServers.length > 1 && (
|
||||
<MirrorSuggestions
|
||||
servers={blossomServers}
|
||||
{blossomServers && blossomServers.length > 1 && (
|
||||
<MirrorSuggestions
|
||||
servers={blossomServers}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
Reference in New Issue
Block a user