2026-04-21 22:07:29 +08:00
|
|
|
package com.xuqm.tenant.controller;
|
|
|
|
|
|
|
|
|
|
import com.xuqm.common.model.ApiResponse;
|
|
|
|
|
import com.xuqm.tenant.dto.CreateAppRequest;
|
|
|
|
|
import com.xuqm.tenant.entity.AppEntity;
|
2026-04-24 20:53:48 +08:00
|
|
|
import com.xuqm.tenant.entity.TenantEntity;
|
|
|
|
|
import com.xuqm.tenant.repository.TenantRepository;
|
2026-04-21 22:07:29 +08:00
|
|
|
import com.xuqm.tenant.service.AppService;
|
2026-04-24 20:53:48 +08:00
|
|
|
import com.xuqm.tenant.service.EmailService;
|
2026-04-21 22:07:29 +08:00
|
|
|
import jakarta.validation.Valid;
|
|
|
|
|
import org.springframework.http.ResponseEntity;
|
|
|
|
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
|
|
|
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
|
|
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
|
|
|
|
import org.springframework.web.bind.annotation.PathVariable;
|
|
|
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
|
|
|
import org.springframework.web.bind.annotation.PutMapping;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestBody;
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
2026-04-24 20:53:48 +08:00
|
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
2026-04-21 22:07:29 +08:00
|
|
|
import org.springframework.web.bind.annotation.RestController;
|
|
|
|
|
|
|
|
|
|
import java.util.List;
|
2026-04-24 20:53:48 +08:00
|
|
|
import java.util.Map;
|
2026-04-21 22:07:29 +08:00
|
|
|
|
|
|
|
|
@RestController
|
|
|
|
|
@RequestMapping("/api/apps")
|
|
|
|
|
public class AppController {
|
|
|
|
|
|
|
|
|
|
private final AppService appService;
|
2026-04-24 20:53:48 +08:00
|
|
|
private final EmailService emailService;
|
|
|
|
|
private final TenantRepository tenantRepository;
|
2026-04-21 22:07:29 +08:00
|
|
|
|
2026-04-24 20:53:48 +08:00
|
|
|
public AppController(AppService appService, EmailService emailService, TenantRepository tenantRepository) {
|
2026-04-21 22:07:29 +08:00
|
|
|
this.appService = appService;
|
2026-04-24 20:53:48 +08:00
|
|
|
this.emailService = emailService;
|
|
|
|
|
this.tenantRepository = tenantRepository;
|
2026-04-21 22:07:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GetMapping
|
|
|
|
|
public ResponseEntity<ApiResponse<List<AppEntity>>> list(@AuthenticationPrincipal String tenantId) {
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(appService.listByTenant(tenantId)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@GetMapping("/{id}")
|
|
|
|
|
public ResponseEntity<ApiResponse<AppEntity>> get(@PathVariable String id,
|
|
|
|
|
@AuthenticationPrincipal String tenantId) {
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(appService.getById(id, tenantId)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@PostMapping
|
|
|
|
|
public ResponseEntity<ApiResponse<AppEntity>> create(@Valid @RequestBody CreateAppRequest req,
|
|
|
|
|
@AuthenticationPrincipal String tenantId) {
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(appService.create(tenantId, req)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@PutMapping("/{id}")
|
|
|
|
|
public ResponseEntity<ApiResponse<AppEntity>> update(@PathVariable String id,
|
|
|
|
|
@Valid @RequestBody CreateAppRequest req,
|
|
|
|
|
@AuthenticationPrincipal String tenantId) {
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(appService.update(id, tenantId, req)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@DeleteMapping("/{id}")
|
|
|
|
|
public ResponseEntity<ApiResponse<Void>> delete(@PathVariable String id,
|
|
|
|
|
@AuthenticationPrincipal String tenantId) {
|
|
|
|
|
appService.delete(id, tenantId);
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.ok());
|
|
|
|
|
}
|
2026-04-24 20:53:48 +08:00
|
|
|
|
|
|
|
|
/** Step 1: send email verification code for secret reveal or reset. */
|
|
|
|
|
@PostMapping("/{id}/request-secret-verify")
|
|
|
|
|
public ResponseEntity<ApiResponse<Void>> requestSecretVerify(
|
|
|
|
|
@PathVariable String id,
|
|
|
|
|
@RequestParam String purpose,
|
|
|
|
|
@AuthenticationPrincipal String tenantId) {
|
|
|
|
|
appService.getById(id, tenantId);
|
|
|
|
|
TenantEntity tenant = tenantRepository.findById(tenantId)
|
|
|
|
|
.orElseThrow(() -> new RuntimeException("Tenant not found"));
|
|
|
|
|
emailService.sendVerificationCode(tenant.getEmail(), purpose);
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.ok());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Step 2a: verify code and return the full appSecret. */
|
|
|
|
|
@PostMapping("/{id}/reveal-secret")
|
|
|
|
|
public ResponseEntity<ApiResponse<Map<String, String>>> revealSecret(
|
|
|
|
|
@PathVariable String id,
|
|
|
|
|
@RequestBody Map<String, String> body,
|
|
|
|
|
@AuthenticationPrincipal String tenantId) {
|
|
|
|
|
AppEntity app = appService.getById(id, tenantId);
|
|
|
|
|
TenantEntity tenant = tenantRepository.findById(tenantId)
|
|
|
|
|
.orElseThrow(() -> new RuntimeException("Tenant not found"));
|
|
|
|
|
emailService.verify(tenant.getEmail(), body.get("code"), "REVEAL_SECRET");
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of("appSecret", app.getAppSecret())));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Step 2b: verify code and regenerate appSecret (old one invalidated immediately). */
|
|
|
|
|
@PostMapping("/{id}/reset-secret")
|
|
|
|
|
public ResponseEntity<ApiResponse<Map<String, String>>> resetSecret(
|
|
|
|
|
@PathVariable String id,
|
|
|
|
|
@RequestBody Map<String, String> body,
|
|
|
|
|
@AuthenticationPrincipal String tenantId) {
|
|
|
|
|
AppEntity app = appService.getById(id, tenantId);
|
|
|
|
|
TenantEntity tenant = tenantRepository.findById(tenantId)
|
|
|
|
|
.orElseThrow(() -> new RuntimeException("Tenant not found"));
|
|
|
|
|
emailService.verify(tenant.getEmail(), body.get("code"), "RESET_SECRET");
|
|
|
|
|
String newSecret = appService.resetSecret(id, tenantId);
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of("appSecret", newSecret)));
|
|
|
|
|
}
|
2026-04-21 22:07:29 +08:00
|
|
|
}
|