blob: 77033be48c9ba4defa937d0e4ea8d2d13e77c074 [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,
Edwardsamaxl25305242025-06-09 21:17:29 +080087 @RequestParam("title") String title,
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080088 @RequestParam("description") String description,
Edwardsamaxlcba512d2025-06-09 21:17:29 +080089 @RequestParam("torrent") MultipartFile torrentFile
90 )
91 {
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +080092
22301102b1084372025-06-01 16:44:23 +080093 Map<String, Object> ans = new HashMap<>();
94
22301102bc6da0a2025-06-02 17:47:29 +080095 if (!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
Edwardsamaxlcba512d2025-06-09 21:17:29 +080096 ans.put("message", "Invalid token");
22301102b1084372025-06-01 16:44:23 +080097 return ResponseEntity.badRequest().body(ans);
98 }
99
100 User user = userService.findByUsername(username);
22301102bc6da0a2025-06-02 17:47:29 +0800101 if (user == null || user.getLevel() < 2) {
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800102 ans.put("message", "Insufficient permissions to publish resources");
22301102b1084372025-06-01 16:44:23 +0800103 return ResponseEntity.status(403).body(ans);
104 }
105
22301102bc6da0a2025-06-02 17:47:29 +0800106 try {
Edwardsamaxlf1bf7ad2025-06-03 23:52:16 +0800107 // 传入种子文件字节,同时传入资源其他信息
Edwardsamaxl25305242025-06-09 21:17:29 +0800108 resourceService.publishResource(torrentFile.getOriginalFilename(), title, description, username, torrentFile.getBytes(), username);
22301102bc6da0a2025-06-02 17:47:29 +0800109 } catch (Exception e) {
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800110 ans.put("message", "Failed to publish resource: " + e.getMessage());
22301102bc6da0a2025-06-02 17:47:29 +0800111 return ResponseEntity.status(500).body(ans);
112 }
22301102fe5f8412025-06-01 17:25:51 +0800113
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800114 ans.put("message", "Resource published successfully");
22301102b1084372025-06-01 16:44:23 +0800115 return ResponseEntity.ok(ans);
116 }
117
118 @GetMapping("/get/{resourceId}")
119 public ResponseEntity<?> getResourceById(@PathVariable("resourceId") int resourceId,
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800120 @RequestHeader("token") String token,
22301102b1084372025-06-01 16:44:23 +0800121 @RequestParam("username") String username) {
122
123 Map<String, Object> ans = new HashMap<>();
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800124 if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
125 ans.put("message", "Invalid token");
126 return ResponseEntity.badRequest().body(ans);
127 }
22301102b1084372025-06-01 16:44:23 +0800128
129 Resource resource = resourceService.getResourceById(resourceId);
130 if (resource == null) {
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800131 ans.put("message", "Resource not found");
132 return ResponseEntity.badRequest().body(ans);
22301102b1084372025-06-01 16:44:23 +0800133 }
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800134
135 ans.put("message", "Resource found");
136 ans.put("data", Map.of(
137 "resourceId", resource.getResourceId(),
138 "name", resource.getName(),
139 "description", resource.getDescription(),
140 "author", resource.getAuthor(),
Edwardsamaxl25305242025-06-09 21:17:29 +0800141 "publishTime", resource.getPublishTime(),
142 "title", resource.getTitle()
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800143 ));
144
145 return ResponseEntity.ok().body(ans);
22301102b1084372025-06-01 16:44:23 +0800146 }
147
148 @GetMapping("/download/{resourceId}")
149 public ResponseEntity<?> downloadResource(@PathVariable("resourceId") int resourceId,
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800150 @RequestHeader("token") String token,
22301102bc6da0a2025-06-02 17:47:29 +0800151 @RequestParam("username") String username) throws IOException {
22301102b1084372025-06-01 16:44:23 +0800152
153 Map<String, Object> ans = new HashMap<>();
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800154 if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)) {
155 ans.put("message", "Invalid token");
156 return ResponseEntity.badRequest().body(ans);
157 }
22301102b1084372025-06-01 16:44:23 +0800158
yyyangf786cfa2025-06-08 15:13:34 +0800159 User user = userService.findByUsername(username);
160 if (user == null) {
161 ans.put("message", "User not found");
162 return ResponseEntity.status(404).body(ans);
163 }
164
yyyangf786cfa2025-06-08 15:13:34 +0800165 long uploaded = user.getUploaded();
166 long downloaded = user.getDownloaded();
167
168 // 如果用户的下载量超过豁免值,则检查其共享率
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800169 if (downloaded > Constants.DOWNLOAD_EXEMPTION_BYTES) {
yyyangf786cfa2025-06-08 15:13:34 +0800170 // 防止除以零
171 double shareRatio = (downloaded == 0) ? Double.MAX_VALUE : (double) uploaded / downloaded;
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800172 if (shareRatio < Constants.MIN_SHARE_RATIO_THRESHOLD) {
yyyangf786cfa2025-06-08 15:13:34 +0800173 ans.put("message", "Share ratio is too low. Please seed more to improve your ratio before downloading new resources.");
174 ans.put("current_share_ratio", String.format("%.2f", shareRatio));
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800175 ans.put("required_share_ratio", Constants.MIN_SHARE_RATIO_THRESHOLD);
yyyangf786cfa2025-06-08 15:13:34 +0800176 return ResponseEntity.status(403).body(ans); // 403 Forbidden
177 }
178 }
179
22301102bc6da0a2025-06-02 17:47:29 +0800180 Resource resource = resourceService.getResourceById(resourceId);
181 byte[] file = resourceService.getTorrentFileByResource(resource, username);
182 if (file == null) {
183 return ResponseEntity.notFound().build();
184 }
22301102b1084372025-06-01 16:44:23 +0800185
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800186 TorrentPasskeyModifier modifier = new TorrentPasskeyModifier();
187
22301102bc6da0a2025-06-02 17:47:29 +0800188 return ResponseEntity.ok()
189 .header("Content-Type", "application/x-bittorrent")
190 .header("Content-Disposition", "attachment; filename=\"" + resource.getName() + ".torrent\"")
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800191 .body(modifier.analyzeTorrentFile(file, username)); // 返回修改后的 torrent 文件
22301102b1084372025-06-01 16:44:23 +0800192 }
193
194 @GetMapping("/search")
195 public ResponseEntity<?> searchResources(@RequestHeader("token") String token,
196 @RequestParam("username") String username,
197 @RequestParam("query") String query) {
198 Map<String, Object> ans = new HashMap<>();
199
200 if(!JWTUtils.checkToken(token, username, Constants.UserRole.USER)){
22301102f69709e2025-06-08 14:10:02 +0800201 ans.put("message", "Invalid token");
22301102b1084372025-06-01 16:44:23 +0800202 return ResponseEntity.badRequest().body(ans);
203 }
204
205 List<Resource> resources = resourceService.searchByQuery(query);
206 if (resources.isEmpty()) {
207 return ResponseEntity.noContent().build();
208 }
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800209
210 ans.put("message", "Search results found");
211 ans.put("data", Map.of(
212 "resources", resources
213 ));
22301102b1084372025-06-01 16:44:23 +0800214 return ResponseEntity.ok(resources);
215 }
216
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800217 @DeleteMapping("/delete")
22301102b1084372025-06-01 16:44:23 +0800218 public ResponseEntity<?> deleteResource(@RequestHeader("token") String token,
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800219 @RequestBody Map<String, Object> requestBody) {
220 String username = (String) requestBody.get("username");
221 Integer resourceId = (Integer) requestBody.get("resourceId");
22301102b1084372025-06-01 16:44:23 +0800222 Map<String, Object> ans = new HashMap<>();
223 Resource resource = resourceService.getResourceById(resourceId);
224
Edwardsamaxlcba512d2025-06-09 21:17:29 +0800225 if(!JWTUtils.checkToken(token, username, Constants.UserRole.ADMIN) || resource == null) {
22301102f69709e2025-06-08 14:10:02 +0800226 ans.put("message", "Invalid token or insufficient permissions");
22301102b1084372025-06-01 16:44:23 +0800227 return ResponseEntity.badRequest().body(ans);
228 }
229
22301102bc6da0a2025-06-02 17:47:29 +0800230 try {
231 resourceService.deleteResource(resourceId);
232 } catch (Exception e) {
22301102f69709e2025-06-08 14:10:02 +0800233 ans.put("message", "Failed to delete resource: " + e.getMessage());
22301102bc6da0a2025-06-02 17:47:29 +0800234 return ResponseEntity.status(500).body(ans);
235 }
22301102b1084372025-06-01 16:44:23 +0800236
22301102f69709e2025-06-08 14:10:02 +0800237 ans.put("message", "Resource deleted successfully");
22301102b1084372025-06-01 16:44:23 +0800238 return ResponseEntity.ok(ans);
239 }
22301102b1084372025-06-01 16:44:23 +0800240}