云文件系统初始化
This commit is contained in:
@@ -44,19 +44,16 @@ public class SecurityConfig {
|
|||||||
.csrf(csrf -> csrf.disable())
|
.csrf(csrf -> csrf.disable())
|
||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
|
// API 公开接口
|
||||||
.requestMatchers("/api/auth/login", "/api/auth/register", "/api/auth/logout").permitAll()
|
.requestMatchers("/api/auth/login", "/api/auth/register", "/api/auth/logout").permitAll()
|
||||||
.requestMatchers("/api/files/test").permitAll()
|
.requestMatchers("/api/files/test").permitAll()
|
||||||
|
// WebSocket
|
||||||
.requestMatchers("/ws/**").permitAll()
|
.requestMatchers("/ws/**").permitAll()
|
||||||
.requestMatchers("/files/avatar/**").permitAll()
|
// 静态资源
|
||||||
.requestMatchers("/api/files/avatar/**").permitAll()
|
.requestMatchers("/webapp/**", "/assets/**", "/uploads/**", "/files/avatar/**", "/api/files/avatar/**", "/api/messages/file/**").permitAll()
|
||||||
.requestMatchers("/api/messages/file/**").permitAll()
|
// 前端页面路由(所有非 /api/ 的路由,由 Vue Router 处理)
|
||||||
.requestMatchers("/webapp/**").permitAll()
|
.requestMatchers("/", "/login", "/register", "/desktop/**", "/favicon.ico").permitAll()
|
||||||
.requestMatchers("/assets/**").permitAll()
|
// 其他所有请求需要认证
|
||||||
.requestMatchers("/login", "/register").permitAll()
|
|
||||||
.requestMatchers("/").permitAll()
|
|
||||||
.requestMatchers("/desktop").permitAll()
|
|
||||||
.requestMatchers("/desktop/**").permitAll()
|
|
||||||
.requestMatchers("/favicon.ico").permitAll()
|
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.exceptionHandling(exception -> exception
|
.exceptionHandling(exception -> exception
|
||||||
@@ -112,8 +109,9 @@ public class SecurityConfig {
|
|||||||
public CorsConfigurationSource corsConfigurationSource() {
|
public CorsConfigurationSource corsConfigurationSource() {
|
||||||
CorsConfiguration configuration = new CorsConfiguration();
|
CorsConfiguration configuration = new CorsConfiguration();
|
||||||
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
|
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
|
||||||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"));
|
||||||
configuration.setAllowedHeaders(Arrays.asList("*"));
|
configuration.setAllowedHeaders(Arrays.asList("*"));
|
||||||
|
configuration.setExposedHeaders(Arrays.asList("Authorization", "Content-Type"));
|
||||||
configuration.setAllowCredentials(true);
|
configuration.setAllowCredentials(true);
|
||||||
configuration.setMaxAge(3600L);
|
configuration.setMaxAge(3600L);
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public class AuthController {
|
|||||||
|
|
||||||
return ResponseEntity.ok(body);
|
return ResponseEntity.ok(body);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return ResponseEntity.status(401).body(Map.of("message", e.getMessage()));
|
return ResponseEntity.badRequest().body(Map.of("message", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,36 +109,53 @@ public class FileController {
|
|||||||
public ResponseEntity<?> deleteFile(
|
public ResponseEntity<?> deleteFile(
|
||||||
@AuthenticationPrincipal UserPrincipal principal,
|
@AuthenticationPrincipal UserPrincipal principal,
|
||||||
@PathVariable Long id) {
|
@PathVariable Long id) {
|
||||||
|
try {
|
||||||
fileService.moveToTrash(id, principal.getUserId());
|
fileService.moveToTrash(id, principal.getUserId());
|
||||||
return ResponseEntity.ok(Map.of("message", "已移至回收站"));
|
return ResponseEntity.ok(Map.of("message", "已移至回收站"));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", e.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/restore")
|
@PostMapping("/{id}/restore")
|
||||||
public ResponseEntity<?> restoreFile(
|
public ResponseEntity<?> restoreFile(
|
||||||
@AuthenticationPrincipal UserPrincipal principal,
|
@AuthenticationPrincipal UserPrincipal principal,
|
||||||
@PathVariable Long id) {
|
@PathVariable Long id) {
|
||||||
|
try {
|
||||||
fileService.restoreFile(id, principal.getUserId());
|
fileService.restoreFile(id, principal.getUserId());
|
||||||
return ResponseEntity.ok(Map.of("message", "已还原"));
|
return ResponseEntity.ok(Map.of("message", "已还原"));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", e.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}/deletePermanent")
|
@DeleteMapping("/{id}/deletePermanent")
|
||||||
public ResponseEntity<?> deletePermanently(
|
public ResponseEntity<?> deletePermanently(
|
||||||
@AuthenticationPrincipal UserPrincipal principal,
|
@AuthenticationPrincipal UserPrincipal principal,
|
||||||
@PathVariable Long id) {
|
@PathVariable Long id) {
|
||||||
|
try {
|
||||||
fileService.deletePermanently(id, principal.getUserId());
|
fileService.deletePermanently(id, principal.getUserId());
|
||||||
return ResponseEntity.ok(Map.of("message", "已彻底删除"));
|
return ResponseEntity.ok(Map.of("message", "已彻底删除"));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", e.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/emptyTrash")
|
@DeleteMapping("/emptyTrash")
|
||||||
public ResponseEntity<?> emptyTrash(@AuthenticationPrincipal UserPrincipal principal) {
|
public ResponseEntity<?> emptyTrash(@AuthenticationPrincipal UserPrincipal principal) {
|
||||||
|
try {
|
||||||
fileService.emptyTrash(principal.getUserId());
|
fileService.emptyTrash(principal.getUserId());
|
||||||
return ResponseEntity.ok(Map.of("message", "已清空回收站"));
|
return ResponseEntity.ok(Map.of("message", "已清空回收站"));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", e.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}/download")
|
@GetMapping("/{id}/download")
|
||||||
public ResponseEntity<byte[]> downloadFile(
|
public ResponseEntity<byte[]> downloadFile(
|
||||||
@AuthenticationPrincipal UserPrincipal principal,
|
@AuthenticationPrincipal UserPrincipal principal,
|
||||||
@PathVariable Long id) throws IOException {
|
@PathVariable Long id) throws IOException {
|
||||||
|
try {
|
||||||
FileEntity file = fileService.getById(id);
|
FileEntity file = fileService.getById(id);
|
||||||
if (file == null) return ResponseEntity.notFound().build();
|
if (file == null) return ResponseEntity.notFound().build();
|
||||||
byte[] content = fileService.getFileContent(file);
|
byte[] content = fileService.getFileContent(file);
|
||||||
@@ -148,12 +165,16 @@ public class FileController {
|
|||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedName + "\"")
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedName + "\"")
|
||||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
.body(content);
|
.body(content);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return ResponseEntity.badRequest().build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}/preview")
|
@GetMapping("/{id}/preview")
|
||||||
public ResponseEntity<byte[]> previewFile(
|
public ResponseEntity<byte[]> previewFile(
|
||||||
@AuthenticationPrincipal UserPrincipal principal,
|
@AuthenticationPrincipal UserPrincipal principal,
|
||||||
@PathVariable Long id) throws IOException {
|
@PathVariable Long id) throws IOException {
|
||||||
|
try {
|
||||||
FileEntity file = fileService.getById(id);
|
FileEntity file = fileService.getById(id);
|
||||||
if (file == null) return ResponseEntity.notFound().build();
|
if (file == null) return ResponseEntity.notFound().build();
|
||||||
byte[] content = fileService.getFileContent(file);
|
byte[] content = fileService.getFileContent(file);
|
||||||
@@ -163,6 +184,9 @@ public class FileController {
|
|||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + encodedName + "\"")
|
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + encodedName + "\"")
|
||||||
.contentType(getMediaType(file.getName()))
|
.contentType(getMediaType(file.getName()))
|
||||||
.body(content);
|
.body(content);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return ResponseEntity.badRequest().build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/shareFile")
|
@PostMapping("/{id}/shareFile")
|
||||||
@@ -170,18 +194,26 @@ public class FileController {
|
|||||||
@AuthenticationPrincipal UserPrincipal principal,
|
@AuthenticationPrincipal UserPrincipal principal,
|
||||||
@PathVariable Long id,
|
@PathVariable Long id,
|
||||||
@RequestBody Map<String, Object> request) {
|
@RequestBody Map<String, Object> request) {
|
||||||
|
try {
|
||||||
Long shareToUserId = Long.valueOf(request.get("userId").toString());
|
Long shareToUserId = Long.valueOf(request.get("userId").toString());
|
||||||
String permission = (String) request.getOrDefault("permission", "view");
|
String permission = (String) request.getOrDefault("permission", "view");
|
||||||
FileShare share = fileService.shareFile(id, principal.getUserId(), shareToUserId, permission);
|
FileShare share = fileService.shareFile(id, principal.getUserId(), shareToUserId, permission);
|
||||||
return ResponseEntity.ok(Map.of("data", share, "message", "共享成功"));
|
return ResponseEntity.ok(Map.of("data", share, "message", "共享成功"));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", e.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}/cancelShare")
|
@DeleteMapping("/{id}/cancelShare")
|
||||||
public ResponseEntity<?> cancelShare(
|
public ResponseEntity<?> cancelShare(
|
||||||
@AuthenticationPrincipal UserPrincipal principal,
|
@AuthenticationPrincipal UserPrincipal principal,
|
||||||
@PathVariable Long id) {
|
@PathVariable Long id) {
|
||||||
|
try {
|
||||||
fileService.cancelShare(id, principal.getUserId());
|
fileService.cancelShare(id, principal.getUserId());
|
||||||
return ResponseEntity.ok(Map.of("message", "已取消共享"));
|
return ResponseEntity.ok(Map.of("message", "已取消共享"));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", e.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,14 +6,9 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
@Controller
|
@Controller
|
||||||
public class IndexController {
|
public class IndexController {
|
||||||
|
|
||||||
@GetMapping({"/", "/login", "/register"})
|
// 所有非 /api/**、非 /ws/** 的前端路由,统一返回 index.html
|
||||||
|
@GetMapping({"/", "/login", "/register", "/desktop", "/desktop/**"})
|
||||||
public String forward() {
|
public String forward() {
|
||||||
return "forward:/webapp/index.html";
|
return "forward:/webapp/index.html";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 捕获所有前端路由,返回 index.html 让 Vue Router 处理
|
|
||||||
@GetMapping("/desktop/**")
|
|
||||||
public String desktop() {
|
|
||||||
return "forward:/webapp/index.html";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -115,12 +115,16 @@ public class UserController {
|
|||||||
public ResponseEntity<?> updateProfile(
|
public ResponseEntity<?> updateProfile(
|
||||||
@AuthenticationPrincipal UserPrincipal principal,
|
@AuthenticationPrincipal UserPrincipal principal,
|
||||||
@RequestBody Map<String, String> request) {
|
@RequestBody Map<String, String> request) {
|
||||||
|
try {
|
||||||
String nickname = request.get("nickname");
|
String nickname = request.get("nickname");
|
||||||
String signature = request.get("signature");
|
String signature = request.get("signature");
|
||||||
String phone = request.get("phone");
|
String phone = request.get("phone");
|
||||||
String email = request.get("email");
|
String email = request.get("email");
|
||||||
userService.updateProfile(principal.getUserId(), nickname, signature, phone, email);
|
userService.updateProfile(principal.getUserId(), nickname, signature, phone, email);
|
||||||
return ResponseEntity.ok(Map.of("message", "更新成功"));
|
return ResponseEntity.ok(Map.of("message", "更新成功"));
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", e.getMessage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
server:
|
server:
|
||||||
port: 18080
|
port: 18089
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
datasource:
|
datasource:
|
||||||
|
|||||||
@@ -22,7 +22,16 @@ request.interceptors.response.use(
|
|||||||
const status = error.response?.status
|
const status = error.response?.status
|
||||||
|
|
||||||
// 只有 401/403 才清理 token 并跳转登录页
|
// 只有 401/403 才清理 token 并跳转登录页
|
||||||
|
// 但在登录页时不跳转(避免死循环),登录接口的 401 也不跳转
|
||||||
if (status === 401 || status === 403) {
|
if (status === 401 || status === 403) {
|
||||||
|
const isLoginPage = window.location.pathname === '/login'
|
||||||
|
const isAuthApi = error.config?.url?.startsWith('/auth/')
|
||||||
|
|
||||||
|
if (isLoginPage || isAuthApi) {
|
||||||
|
// 登录页或登录接口,不跳转,不清理
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
|
||||||
localStorage.removeItem('token')
|
localStorage.removeItem('token')
|
||||||
localStorage.removeItem('username')
|
localStorage.removeItem('username')
|
||||||
localStorage.removeItem('userId')
|
localStorage.removeItem('userId')
|
||||||
@@ -31,12 +40,9 @@ request.interceptors.response.use(
|
|||||||
localStorage.removeItem('storageUsed')
|
localStorage.removeItem('storageUsed')
|
||||||
localStorage.removeItem('storageLimit')
|
localStorage.removeItem('storageLimit')
|
||||||
|
|
||||||
if (window.location.pathname !== '/login') {
|
|
||||||
window.location.href = '/login'
|
window.location.href = '/login'
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 其他错误(404、500、网络错误等)正常 reject,不跳转
|
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -619,8 +619,6 @@ const handleConfirmBatchMove = async (targetFolderId) => {
|
|||||||
finalFolderId = targetFolderId
|
finalFolderId = targetFolderId
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('targetFolderId:', targetFolderId, 'finalFolderId:', finalFolderId)
|
|
||||||
|
|
||||||
let successCount = 0
|
let successCount = 0
|
||||||
let failCount = 0
|
let failCount = 0
|
||||||
|
|
||||||
|
|||||||
@@ -22,19 +22,19 @@ export default defineConfig({
|
|||||||
port: 5173,
|
port: 5173,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:8080',
|
target: 'http://localhost:18089',
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
},
|
},
|
||||||
'/ws': {
|
'/ws': {
|
||||||
target: 'ws://localhost:8080',
|
target: 'ws://localhost:18089',
|
||||||
ws: true
|
ws: true
|
||||||
},
|
},
|
||||||
'/uploads': {
|
'/uploads': {
|
||||||
target: 'http://localhost:8080',
|
target: 'http://localhost:18089',
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
},
|
},
|
||||||
'/files': {
|
'/files': {
|
||||||
target: 'http://localhost:8080',
|
target: 'http://localhost:18089',
|
||||||
changeOrigin: true
|
changeOrigin: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user