blob: c663963f2705cfc418cfb7b2ec21fa4ba1ae0a47 [file] [log] [blame]
22301102b1084372025-06-01 16:44:23 +08001package com.pt.controller;
2
3import com.pt.constant.Constants;
4import com.pt.entity.Resource;
5import com.pt.entity.User;
6import com.pt.service.ResourceService;
7import com.pt.service.UserService;
Edwardsamaxlcba512d2025-06-09 21:17:29 +08008import com.pt.utils.BencodeCodec;
22301102b1084372025-06-01 16:44:23 +08009import com.pt.utils.JWTUtils;
Edwardsamaxlcba512d2025-06-09 21:17:29 +080010import com.pt.utils.TorrentPasskeyModifier;
11import jakarta.servlet.http.HttpServletRequest;
22301102b1084372025-06-01 16:44:23 +080012import org.springframework.beans.factory.annotation.Autowired;
22301102f69709e2025-06-08 14:10:02 +080013import org.springframework.context.annotation.Bean;
22301102b1084372025-06-01 16:44:23 +080014import org.springframework.http.ResponseEntity;
15import org.springframework.web.bind.annotation.*;
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080016import org.springframework.web.multipart.MultipartFile;
Edwardsamaxlcba512d2025-06-09 21:17:29 +080017import org.springframework.mock.web.MockMultipartFile;
22301102b1084372025-06-01 16:44:23 +080018
Edwardsamaxlcba512d2025-06-09 21:17:29 +080019import java.io.ByteArrayInputStream;
20import java.io.ByteArrayOutputStream;
22301102bc6da0a2025-06-02 17:47:29 +080021import java.io.IOException;
Edwardsamaxlcba512d2025-06-09 21:17:29 +080022import java.nio.charset.StandardCharsets;
22301102b1084372025-06-01 16:44:23 +080023import java.util.HashMap;
24import java.util.List;
25import java.util.Map;
22301102bc6da0a2025-06-02 17:47:29 +080026import java.io.File;
22301102b1084372025-06-01 16:44:23 +080027
28@RestController
29@RequestMapping("/api/resource")
30@CrossOrigin(origins = "*")
31public class ResourceController {
32
33 @Autowired
34 private ResourceService resourceService;
yyyangf786cfa2025-06-08 15:13:34 +080035
36 @Autowired
22301102b1084372025-06-01 16:44:23 +080037 private UserService userService;
38
39 @GetMapping("/list/all")
40 public ResponseEntity<?> getAllResources(@RequestHeader("token") String token,
41 @RequestParam("username") String username) {
22301102f69709e2025-06-08 14:10:02 +080042
22301102b1084372025-06-01 16:44:23 +080043 Map<String, Object> ans = new HashMap<>();
44
45 if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)){
22301102f69709e2025-06-08 14:10:02 +080046 ans.put("message", "Invalid token");
22301102b1084372025-06-01 16:44:23 +080047 return ResponseEntity.badRequest().body(ans);
48 }
49
50 List<Resource> resources = resourceService.getAllResources();
51 if (resources.isEmpty()) {
52 return ResponseEntity.noContent().build();
53 }
Edwardsamaxlcba512d2025-06-09 21:17:29 +080054 ans.put("message", "Resources found");
55 ans.put("data", Map.of(
56 "resources", resources
57 ));
58 return ResponseEntity.ok().body(ans);
22301102b1084372025-06-01 16:44:23 +080059 }
60
61 @GetMapping("/list/user")
62 public ResponseEntity<?> getUserResources(@RequestHeader("token") String token,
63 @RequestParam("username") String username) {
22301102f69709e2025-06-08 14:10:02 +080064
22301102b1084372025-06-01 16:44:23 +080065 Map<String, Object> ans = new HashMap<>();
66
67 if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)){
22301102f69709e2025-06-08 14:10:02 +080068 ans.put("message", "Invalid token");
22301102b1084372025-06-01 16:44:23 +080069 return ResponseEntity.badRequest().body(ans);
70 }
71
72 List<Resource> resources = resourceService.getResourcesByAuthor(username);
73 if (resources.isEmpty()) {
74 return ResponseEntity.noContent().build();
75 }
Edwardsamaxlcba512d2025-06-09 21:17:29 +080076 ans.put("message", "User resources found");
77 ans.put("data", Map.of(
78 "resources", resources
79 ));
80 return ResponseEntity.ok().body(ans);
22301102b1084372025-06-01 16:44:23 +080081 }
82
83 @PostMapping("/publish")
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080084 public ResponseEntity<?> publishResource(
85 @RequestHeader("token") String token,
86 @RequestParam("username") String username,
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080087 @RequestParam("description") String description,
Edwardsamaxlcba512d2025-06-09 21:17:29 +080088 @RequestParam("torrent") MultipartFile torrentFile
89 )
90 {
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080091
22301102b1084372025-06-01 16:44:23 +080092 Map<String, Object> ans = new HashMap<>();
93
22301102bc6da0a2025-06-02 17:47:29 +080094 if (!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
Edwardsamaxlcba512d2025-06-09 21:17:29 +080095 ans.put("message", "Invalid token");
22301102b1084372025-06-01 16:44:23 +080096 return ResponseEntity.badRequest().body(ans);
97 }
98
99 User user = userService.findByUsername(username);
22301102bc6da0a2025-06-02 17:47:29 +0800100 if (user == null || user.getLevel() < 2) {
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800101 ans.put("message", "Insufficient permissions to publish resources");
22301102b1084372025-06-01 16:44:23 +0800102 return ResponseEntity.status(403).body(ans);
103 }
104
22301102bc6da0a2025-06-02 17:47:29 +0800105 try {
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +0800106 // 传入种子文件字节,同时传入资源其他信息
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800107 resourceService.publishResource(torrentFile.getOriginalFilename(), description, username, torrentFile.getBytes(), username);
22301102bc6da0a2025-06-02 17:47:29 +0800108 } catch (Exception e) {
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800109 ans.put("message", "Failed to publish resource: " + e.getMessage());
22301102bc6da0a2025-06-02 17:47:29 +0800110 return ResponseEntity.status(500).body(ans);
111 }
22301102fe5f8412025-06-01 17:25:51 +0800112
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800113 ans.put("message", "Resource published successfully");
22301102b1084372025-06-01 16:44:23 +0800114 return ResponseEntity.ok(ans);
115 }
116
117 @GetMapping("/get/{resourceId}")
118 public ResponseEntity<?> getResourceById(@PathVariable("resourceId") int resourceId,
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800119 @RequestHeader("token") String token,
22301102b1084372025-06-01 16:44:23 +0800120 @RequestParam("username") String username) {
121
122 Map<String, Object> ans = new HashMap<>();
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800123 if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
124 ans.put("message", "Invalid token");
125 return ResponseEntity.badRequest().body(ans);
126 }
22301102b1084372025-06-01 16:44:23 +0800127
128 Resource resource = resourceService.getResourceById(resourceId);
129 if (resource == null) {
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800130 ans.put("message", "Resource not found");
131 return ResponseEntity.badRequest().body(ans);
22301102b1084372025-06-01 16:44:23 +0800132 }
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800133
134 ans.put("message", "Resource found");
135 ans.put("data", Map.of(
136 "resourceId", resource.getResourceId(),
137 "name", resource.getName(),
138 "description", resource.getDescription(),
139 "author", resource.getAuthor(),
140 "publishTime", resource.getPublishTime()
141 ));
142
143 return ResponseEntity.ok().body(ans);
22301102b1084372025-06-01 16:44:23 +0800144 }
145
146 @GetMapping("/download/{resourceId}")
147 public ResponseEntity<?> downloadResource(@PathVariable("resourceId") int resourceId,
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800148 @RequestHeader("token") String token,
22301102bc6da0a2025-06-02 17:47:29 +0800149 @RequestParam("username") String username) throws IOException {
22301102b1084372025-06-01 16:44:23 +0800150
151 Map<String, Object> ans = new HashMap<>();
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800152 if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
153 ans.put("message", "Invalid token");
154 return ResponseEntity.badRequest().body(ans);
155 }
22301102b1084372025-06-01 16:44:23 +0800156
yyyangf786cfa2025-06-08 15:13:34 +0800157 User user = userService.findByUsername(username);
158 if (user == null) {
159 ans.put("message", "User not found");
160 return ResponseEntity.status(404).body(ans);
161 }
162
yyyangf786cfa2025-06-08 15:13:34 +0800163 long uploaded = user.getUploaded();
164 long downloaded = user.getDownloaded();
165
166 // 如果用户的下载量超过豁免值,则检查其共享率
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800167 if (downloaded > Constants.DOWNLOAD_EXEMPTION_BYTES) {
yyyangf786cfa2025-06-08 15:13:34 +0800168 // 防止除以零
169 double shareRatio = (downloaded == 0) ? Double.MAX_VALUE : (double) uploaded / downloaded;
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800170 if (shareRatio < Constants.MIN_SHARE_RATIO_THRESHOLD) {
yyyangf786cfa2025-06-08 15:13:34 +0800171 ans.put("message", "Share ratio is too low. Please seed more to improve your ratio before downloading new resources.");
172 ans.put("current_share_ratio", String.format("%.2f", shareRatio));
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800173 ans.put("required_share_ratio", Constants.MIN_SHARE_RATIO_THRESHOLD);
yyyangf786cfa2025-06-08 15:13:34 +0800174 return ResponseEntity.status(403).body(ans); // 403 Forbidden
175 }
176 }
177
22301102bc6da0a2025-06-02 17:47:29 +0800178 Resource resource = resourceService.getResourceById(resourceId);
179 byte[] file = resourceService.getTorrentFileByResource(resource, username);
180 if (file == null) {
181 return ResponseEntity.notFound().build();
182 }
22301102b1084372025-06-01 16:44:23 +0800183
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800184 TorrentPasskeyModifier modifier = new TorrentPasskeyModifier();
185
22301102bc6da0a2025-06-02 17:47:29 +0800186 return ResponseEntity.ok()
187 .header("Content-Type", "application/x-bittorrent")
188 .header("Content-Disposition", "attachment; filename=\"" + resource.getName() + ".torrent\"")
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800189 .body(modifier.analyzeTorrentFile(file, username)); // 返回修改后的 torrent 文件
22301102b1084372025-06-01 16:44:23 +0800190 }
191
192 @GetMapping("/search")
193 public ResponseEntity<?> searchResources(@RequestHeader("token") String token,
194 @RequestParam("username") String username,
195 @RequestParam("query") String query) {
196 Map<String, Object> ans = new HashMap<>();
197
198 if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)){
22301102f69709e2025-06-08 14:10:02 +0800199 ans.put("message", "Invalid token");
22301102b1084372025-06-01 16:44:23 +0800200 return ResponseEntity.badRequest().body(ans);
201 }
202
203 List<Resource> resources = resourceService.searchByQuery(query);
204 if (resources.isEmpty()) {
205 return ResponseEntity.noContent().build();
206 }
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800207
208 ans.put("message", "Search results found");
209 ans.put("data", Map.of(
210 "resources", resources
211 ));
22301102b1084372025-06-01 16:44:23 +0800212 return ResponseEntity.ok(resources);
213 }
214
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800215 @DeleteMapping("/delete")
22301102b1084372025-06-01 16:44:23 +0800216 public ResponseEntity<?> deleteResource(@RequestHeader("token") String token,
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800217 @RequestBody Map<String, Object> requestBody) {
218 String username = (String) requestBody.get("username");
219 Integer resourceId = (Integer) requestBody.get("resourceId");
22301102b1084372025-06-01 16:44:23 +0800220 Map<String, Object> ans = new HashMap<>();
221 Resource resource = resourceService.getResourceById(resourceId);
222
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800223 if(!JWTUtils.checkToken(token, username, Constants.UserRole.ADMIN) || resource == null) {
22301102f69709e2025-06-08 14:10:02 +0800224 ans.put("message", "Invalid token or insufficient permissions");
22301102b1084372025-06-01 16:44:23 +0800225 return ResponseEntity.badRequest().body(ans);
226 }
227
22301102bc6da0a2025-06-02 17:47:29 +0800228 try {
229 resourceService.deleteResource(resourceId);
230 } catch (Exception e) {
22301102f69709e2025-06-08 14:10:02 +0800231 ans.put("message", "Failed to delete resource: " + e.getMessage());
22301102bc6da0a2025-06-02 17:47:29 +0800232 return ResponseEntity.status(500).body(ans);
233 }
22301102b1084372025-06-01 16:44:23 +0800234
22301102f69709e2025-06-08 14:10:02 +0800235 ans.put("message", "Resource deleted successfully");
22301102b1084372025-06-01 16:44:23 +0800236 return ResponseEntity.ok(ans);
237 }
22301102b1084372025-06-01 16:44:23 +0800238}