Skip to content

Commit 92789fa

Browse files
Copilotv0l
andcommitted
Remove payments feature gate and implement mirror suggestions entirely in frontend
Co-authored-by: v0l <[email protected]>
1 parent 06aadb4 commit 92789fa

File tree

7 files changed

+67
-159
lines changed

7 files changed

+67
-159
lines changed

src/routes/admin.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,12 @@ use rocket::{routes, Responder, Route, State};
88
use sqlx::{Error, QueryBuilder, Row};
99

1010
pub fn admin_routes() -> Vec<Route> {
11-
let mut routes = routes![
11+
routes![
1212
admin_list_files,
1313
admin_get_self,
14-
];
15-
16-
#[cfg(feature = "payments")]
17-
{
18-
routes.extend(routes![
19-
admin_list_reports,
20-
admin_acknowledge_report
21-
]);
22-
}
23-
24-
routes
14+
admin_list_reports,
15+
admin_acknowledge_report,
16+
]
2517
}
2618

2719
#[derive(Serialize, Default)]
@@ -180,7 +172,6 @@ async fn admin_list_files(
180172
}
181173
}
182174

183-
#[cfg(feature = "payments")]
184175
#[rocket::get("/reports?<page>&<count>")]
185176
async fn admin_list_reports(
186177
auth: Nip98Auth,
@@ -211,7 +202,6 @@ async fn admin_list_reports(
211202
}
212203
}
213204

214-
#[cfg(feature = "payments")]
215205
#[rocket::delete("/reports/<report_id>")]
216206
async fn admin_acknowledge_report(
217207
auth: Nip98Auth,

src/routes/blossom.rs

Lines changed: 0 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use rocket::response::Responder;
1313
use rocket::serde::json::Json;
1414
use rocket::{routes, Data, Request, Response, Route, State};
1515
use serde::{Deserialize, Serialize};
16-
use std::collections::HashMap;
1716
use tokio::io::AsyncRead;
1817
use tokio_util::io::StreamReader;
1918

@@ -56,29 +55,6 @@ struct MirrorRequest {
5655
pub url: String,
5756
}
5857

59-
#[derive(Debug, Clone, Serialize, Deserialize)]
60-
#[serde(crate = "rocket::serde")]
61-
pub struct MirrorSuggestionsRequest {
62-
pub servers: Vec<String>,
63-
}
64-
65-
#[derive(Debug, Clone, Serialize, Deserialize)]
66-
#[serde(crate = "rocket::serde")]
67-
pub struct FileMirrorSuggestion {
68-
pub sha256: String,
69-
pub url: String,
70-
pub size: u64,
71-
pub mime_type: Option<String>,
72-
pub available_on: Vec<String>,
73-
pub missing_from: Vec<String>,
74-
}
75-
76-
#[derive(Debug, Clone, Serialize, Deserialize)]
77-
#[serde(crate = "rocket::serde")]
78-
pub struct MirrorSuggestionsResponse {
79-
pub suggestions: Vec<FileMirrorSuggestion>,
80-
}
81-
8258
#[cfg(feature = "media-compression")]
8359
pub fn blossom_routes() -> Vec<Route> {
8460
let mut routes = routes![
@@ -89,7 +65,6 @@ pub fn blossom_routes() -> Vec<Route> {
8965
upload_media,
9066
head_media,
9167
mirror,
92-
mirror_suggestions,
9368
];
9469

9570
#[cfg(feature = "payments")]
@@ -108,7 +83,6 @@ pub fn blossom_routes() -> Vec<Route> {
10883
list_files,
10984
upload_head,
11085
mirror,
111-
mirror_suggestions,
11286
];
11387

11488
#[cfg(feature = "payments")]
@@ -144,9 +118,6 @@ enum BlossomResponse {
144118

145119
#[response(status = 200)]
146120
BlobDescriptorList(Json<Vec<BlobDescriptor>>),
147-
148-
#[response(status = 200)]
149-
MirrorSuggestions(Json<MirrorSuggestionsResponse>),
150121
}
151122

152123
impl BlossomResponse {
@@ -306,77 +277,6 @@ async fn mirror(
306277
.await
307278
}
308279

309-
#[rocket::post("/mirror-suggestions", data = "<req>", format = "json")]
310-
async fn mirror_suggestions(
311-
auth: BlossomAuth,
312-
req: Json<MirrorSuggestionsRequest>,
313-
) -> BlossomResponse {
314-
if !check_method(&auth.event, "mirror-suggestions") {
315-
return BlossomResponse::error("Invalid request method tag");
316-
}
317-
318-
let pubkey_hex = auth.event.pubkey.to_hex();
319-
let mut file_map: HashMap<String, FileMirrorSuggestion> = HashMap::new();
320-
321-
// Fetch files from each server
322-
for server_url in &req.servers {
323-
if let Ok(_server_url_parsed) = url::Url::parse(server_url) {
324-
let list_url = format!("{}/list/{}", server_url, pubkey_hex);
325-
326-
match reqwest::get(&list_url).await {
327-
Ok(response) => {
328-
if response.status().is_success() {
329-
match response.json::<Vec<BlobDescriptor>>().await {
330-
Ok(files) => {
331-
for file in files {
332-
file_map
333-
.entry(file.sha256.clone())
334-
.and_modify(|suggestion| {
335-
suggestion.available_on.push(server_url.clone());
336-
})
337-
.or_insert_with(|| FileMirrorSuggestion {
338-
sha256: file.sha256.clone(),
339-
url: file.url.clone(),
340-
size: file.size,
341-
mime_type: file.mime_type.clone(),
342-
available_on: vec![server_url.clone()],
343-
missing_from: Vec::new(),
344-
});
345-
}
346-
}
347-
Err(e) => {
348-
error!("Failed to parse file list from {}: {}", server_url, e);
349-
}
350-
}
351-
}
352-
}
353-
Err(e) => {
354-
error!("Failed to fetch file list from {}: {}", server_url, e);
355-
}
356-
}
357-
}
358-
}
359-
360-
// Determine missing servers for each file
361-
for suggestion in file_map.values_mut() {
362-
for server_url in &req.servers {
363-
if !suggestion.available_on.contains(server_url) {
364-
suggestion.missing_from.push(server_url.clone());
365-
}
366-
}
367-
}
368-
369-
// Filter to only files that are missing from at least one server and available on at least one
370-
let filtered_suggestions: Vec<FileMirrorSuggestion> = file_map
371-
.into_values()
372-
.filter(|s| !s.missing_from.is_empty() && !s.available_on.is_empty())
373-
.collect();
374-
375-
BlossomResponse::MirrorSuggestions(Json(MirrorSuggestionsResponse {
376-
suggestions: filtered_suggestions,
377-
}))
378-
}
379-
380280
#[cfg(feature = "media-compression")]
381281
#[rocket::head("/media")]
382282
fn head_media(auth: BlossomAuth, settings: &State<Settings>) -> BlossomHead {

ui_src/src/components/mirror-suggestions.tsx

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,89 @@
11
import { useState, useEffect } from "react";
2-
import { FileMirrorSuggestion, Blossom } from "../upload/blossom";
2+
import { BlobDescriptor, Blossom } from "../upload/blossom";
33
import { FormatBytes } from "../const";
44
import Button from "./button";
55
import usePublisher from "../hooks/publisher";
6+
import useLogin from "../hooks/login";
7+
8+
interface FileMirrorSuggestion {
9+
sha256: string;
10+
url: string;
11+
size: number;
12+
mime_type?: string;
13+
available_on: string[];
14+
missing_from: string[];
15+
}
616

717
interface MirrorSuggestionsProps {
818
servers: string[];
9-
currentServer: string;
1019
}
1120

12-
export default function MirrorSuggestions({ servers, currentServer }: MirrorSuggestionsProps) {
21+
export default function MirrorSuggestions({ servers }: MirrorSuggestionsProps) {
1322
const [suggestions, setSuggestions] = useState<FileMirrorSuggestion[]>([]);
1423
const [loading, setLoading] = useState(false);
1524
const [error, setError] = useState<string>();
1625
const [mirroring, setMirroring] = useState<Set<string>>(new Set());
1726

1827
const pub = usePublisher();
28+
const login = useLogin();
1929

2030
useEffect(() => {
21-
if (servers.length > 1 && pub) {
31+
if (servers.length > 1 && pub && login?.pubkey) {
2232
fetchSuggestions();
2333
}
24-
}, [servers, pub]);
34+
}, [servers, pub, login?.pubkey]);
2535

2636
async function fetchSuggestions() {
27-
if (!pub) return;
37+
if (!pub || !login?.pubkey) return;
2838

2939
try {
3040
setLoading(true);
3141
setError(undefined);
3242

33-
const blossom = new Blossom(currentServer, pub);
34-
const result = await blossom.getMirrorSuggestions(servers);
35-
setSuggestions(result.suggestions);
43+
const fileMap: Map<string, FileMirrorSuggestion> = new Map();
44+
45+
// Fetch files from each server
46+
for (const serverUrl of servers) {
47+
try {
48+
const blossom = new Blossom(serverUrl, pub);
49+
const files = await blossom.list(login.pubkey);
50+
51+
for (const file of files) {
52+
const suggestion = fileMap.get(file.sha256);
53+
if (suggestion) {
54+
suggestion.available_on.push(serverUrl);
55+
} else {
56+
fileMap.set(file.sha256, {
57+
sha256: file.sha256,
58+
url: file.url || "",
59+
size: file.size,
60+
mime_type: file.type,
61+
available_on: [serverUrl],
62+
missing_from: [],
63+
});
64+
}
65+
}
66+
} catch (e) {
67+
console.error(`Failed to fetch files from ${serverUrl}:`, e);
68+
// Continue with other servers instead of failing completely
69+
}
70+
}
71+
72+
// Determine missing servers for each file
73+
for (const suggestion of fileMap.values()) {
74+
for (const serverUrl of servers) {
75+
if (!suggestion.available_on.includes(serverUrl)) {
76+
suggestion.missing_from.push(serverUrl);
77+
}
78+
}
79+
}
80+
81+
// Filter to only files that are missing from at least one server and available on at least one
82+
const filteredSuggestions = Array.from(fileMap.values()).filter(
83+
s => s.missing_from.length > 0 && s.available_on.length > 0
84+
);
85+
86+
setSuggestions(filteredSuggestions);
3687
} catch (e) {
3788
if (e instanceof Error) {
3889
setError(e.message);
@@ -52,7 +103,7 @@ export default function MirrorSuggestions({ servers, currentServer }: MirrorSugg
52103

53104
try {
54105
const blossom = new Blossom(targetServer, pub);
55-
await blossom.mirror(suggestion.url);
106+
await blossom.mirror(suggestion.sha256, suggestion.url);
56107

57108
// Update suggestions by removing this server from missing_from
58109
setSuggestions(prev =>

ui_src/src/upload/blossom.ts

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,6 @@ export interface BlobDescriptor {
1111
uploaded?: number;
1212
}
1313

14-
export interface FileMirrorSuggestion {
15-
sha256: string;
16-
url: string;
17-
size: number;
18-
mime_type?: string;
19-
available_on: string[];
20-
missing_from: string[];
21-
}
22-
23-
export interface MirrorSuggestionsResponse {
24-
suggestions: FileMirrorSuggestion[];
25-
}
26-
2714
export class Blossom {
2815
constructor(
2916
readonly url: string,
@@ -113,25 +100,6 @@ export class Blossom {
113100
}
114101
}
115102

116-
async getMirrorSuggestions(servers: string[]): Promise<MirrorSuggestionsResponse> {
117-
const rsp = await this.#req(
118-
"mirror-suggestions",
119-
"POST",
120-
"mirror-suggestions",
121-
JSON.stringify({ servers }),
122-
undefined,
123-
{
124-
"content-type": "application/json",
125-
},
126-
);
127-
if (rsp.ok) {
128-
return (await rsp.json()) as MirrorSuggestionsResponse;
129-
} else {
130-
await this.#handleError(rsp);
131-
throw new Error("Should not reach here");
132-
}
133-
}
134-
135103
async #req(
136104
path: string,
137105
method: "GET" | "POST" | "DELETE" | "PUT",

ui_src/src/views/upload.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,6 @@ export default function Upload() {
359359
{blossomServers.length > 1 && (
360360
<MirrorSuggestions
361361
servers={blossomServers}
362-
currentServer={url}
363362
/>
364363
)}
365364

ui_src/tsconfig.app.tsbuildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"root":["./src/App.tsx","./src/const.ts","./src/login.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/button.tsx","./src/components/mirror-suggestions.tsx","./src/components/payment.tsx","./src/components/profile.tsx","./src/components/progress-bar.tsx","./src/components/server-config.tsx","./src/hooks/login.ts","./src/hooks/publisher.ts","./src/upload/admin.ts","./src/upload/blossom.ts","./src/upload/index.ts","./src/upload/nip96.ts","./src/upload/progress.ts","./src/views/admin.tsx","./src/views/files.tsx","./src/views/header.tsx","./src/views/reports.tsx","./src/views/upload.tsx"],"version":"5.6.2"}
1+
{"root":["./src/App.tsx","./src/const.ts","./src/login.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/button.tsx","./src/components/mirror-suggestions.tsx","./src/components/payment.tsx","./src/components/profile.tsx","./src/components/progress-bar.tsx","./src/components/server-config.tsx","./src/hooks/login.ts","./src/hooks/publisher.ts","./src/upload/admin.ts","./src/upload/blossom.ts","./src/upload/index.ts","./src/upload/nip96.ts","./src/upload/progress.ts","./src/views/admin.tsx","./src/views/files.tsx","./src/views/header.tsx","./src/views/reports.tsx","./src/views/upload.tsx"],"errors":true,"version":"5.8.3"}

ui_src/tsconfig.node.tsbuildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"root":["./vite.config.ts"],"version":"5.6.2"}
1+
{"root":["./vite.config.ts"],"errors":true,"version":"5.8.3"}

0 commit comments

Comments
 (0)