.
This commit is contained in:
@@ -0,0 +1,396 @@
|
||||
# Social Media Credentials API - Frontend Guide
|
||||
|
||||
## Обзор изменений
|
||||
|
||||
API для управления credentials социальных сетей был обновлен для поддержки разных форматов данных в зависимости от платформы. Поле `credentials` теперь может принимать как простую строку (токен), так и JSON объект для платформ, требующих несколько параметров.
|
||||
|
||||
## Базовый URL
|
||||
|
||||
```
|
||||
http://your-server:port/api/social-media/credentials
|
||||
```
|
||||
|
||||
## Аутентификация
|
||||
|
||||
Все endpoints требуют JWT токен в заголовке `Authorization`:
|
||||
|
||||
```
|
||||
Authorization: Bearer <your-jwt-token>
|
||||
```
|
||||
|
||||
## Изменения в API
|
||||
|
||||
### Поле `credentials` теперь поддерживает разные типы
|
||||
|
||||
**До изменений:**
|
||||
|
||||
- `credentials` был только `String` (токен)
|
||||
|
||||
**После изменений:**
|
||||
|
||||
- `credentials` может быть `String` (для простых токенов) или `Object` (для сложных структур)
|
||||
|
||||
### Поддерживаемые форматы
|
||||
|
||||
#### 1. Простой токен (String) - для Facebook, LinkedIn
|
||||
|
||||
```json
|
||||
{
|
||||
"platform": "facebook",
|
||||
"credentials": "EAABwzLix..."
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. JSON объект (Object) - для Telegram и других платформ с несколькими параметрами
|
||||
|
||||
```json
|
||||
{
|
||||
"platform": "telegram",
|
||||
"credentials": {
|
||||
"botToken": "123456789:ABCdefGHIjklMNOpqrsTUVwxyz",
|
||||
"chatId": "-1001234567890"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
### 1. Сохранение/обновление credentials
|
||||
|
||||
**Endpoint:** `POST /api/social-media/credentials`
|
||||
|
||||
**Headers:**
|
||||
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Request Body для простого токена (Facebook/LinkedIn):**
|
||||
|
||||
```json
|
||||
{
|
||||
"platform": "facebook",
|
||||
"credentials": "EAABwzLix..."
|
||||
}
|
||||
```
|
||||
|
||||
**Request Body для JSON объекта (Telegram):**
|
||||
|
||||
```json
|
||||
{
|
||||
"platform": "telegram",
|
||||
"credentials": {
|
||||
"botToken": "123456789:ABCdefGHIjklMNOpqrsTUVwxyz",
|
||||
"chatId": "-1001234567890"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response 200 OK:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Credentials успешно сохранены для платформы telegram",
|
||||
"data": {
|
||||
"platform": "telegram",
|
||||
"hasCredentials": true,
|
||||
"createdAt": "2024-01-15T10:30:00",
|
||||
"updatedAt": "2024-01-15T10:30:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Примеры использования (JavaScript):**
|
||||
|
||||
```javascript
|
||||
// Сохранение простого токена (Facebook/LinkedIn)
|
||||
const saveFacebookCredentials = async (accessToken) => {
|
||||
const response = await fetch('/api/social-media/credentials', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwtToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
platform: 'facebook',
|
||||
credentials: accessToken, // Простая строка
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
};
|
||||
|
||||
// Сохранение JSON объекта (Telegram)
|
||||
const saveTelegramCredentials = async (botToken, chatId) => {
|
||||
const response = await fetch('/api/social-media/credentials', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwtToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
platform: 'telegram',
|
||||
credentials: {
|
||||
// JSON объект
|
||||
botToken: botToken,
|
||||
chatId: chatId,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
};
|
||||
|
||||
// Использование
|
||||
await saveFacebookCredentials('EAABwzLix...');
|
||||
await saveTelegramCredentials('123456789:ABC...', '-1001234567890');
|
||||
```
|
||||
|
||||
### 2. Получение информации о credentials
|
||||
|
||||
**Endpoint:** `GET /api/social-media/credentials/{platform}`
|
||||
|
||||
**Headers:**
|
||||
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
**Path Parameters:**
|
||||
|
||||
- `platform` (string) - Название платформы
|
||||
|
||||
**Response 200 OK:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Операция выполнена успешно",
|
||||
"data": {
|
||||
"platform": "telegram",
|
||||
"hasCredentials": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Получение списка всех credentials
|
||||
|
||||
**Endpoint:** `GET /api/social-media/credentials`
|
||||
|
||||
**Headers:**
|
||||
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
**Response 200 OK:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Операция выполнена успешно",
|
||||
"data": [
|
||||
{
|
||||
"platform": "facebook",
|
||||
"hasCredentials": true,
|
||||
"createdAt": "2024-01-15T10:30:00",
|
||||
"updatedAt": "2024-01-15T10:30:00"
|
||||
},
|
||||
{
|
||||
"platform": "telegram",
|
||||
"hasCredentials": true,
|
||||
"createdAt": "2024-01-15T11:00:00",
|
||||
"updatedAt": "2024-01-15T11:00:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Удаление credentials
|
||||
|
||||
**Endpoint:** `DELETE /api/social-media/credentials/{platform}`
|
||||
|
||||
**Headers:**
|
||||
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
**Path Parameters:**
|
||||
|
||||
- `platform` (string) - Название платформы
|
||||
|
||||
**Response 200 OK:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Credentials для платформы telegram успешно удалены",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
## Форматы credentials по платформам
|
||||
|
||||
### Facebook
|
||||
|
||||
```json
|
||||
{
|
||||
"platform": "facebook",
|
||||
"credentials": "EAABwzLix..." // Access Token
|
||||
}
|
||||
```
|
||||
|
||||
### LinkedIn
|
||||
|
||||
```json
|
||||
{
|
||||
"platform": "linkedin",
|
||||
"credentials": "AQV..." // Access Token
|
||||
}
|
||||
```
|
||||
|
||||
### Telegram
|
||||
|
||||
```json
|
||||
{
|
||||
"platform": "telegram",
|
||||
"credentials": {
|
||||
"botToken": "123456789:ABCdefGHIjklMNOpqrsTUVwxyz",
|
||||
"chatId": "-1001234567890"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Примечание для Telegram:**
|
||||
|
||||
- `botToken` - токен бота, полученный от @BotFather
|
||||
- `chatId` - ID чата/канала (может быть отрицательным для групп и каналов)
|
||||
- Формат: `-1001234567890` для супергрупп и каналов
|
||||
|
||||
## Обработка ошибок
|
||||
|
||||
### 400 Bad Request - Ошибка валидации
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Ошибка валидации",
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Platform is required",
|
||||
"details": {
|
||||
"platform": "Platform is required"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 401 Unauthorized
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Не авторизован",
|
||||
"error": {
|
||||
"code": "UNAUTHORIZED",
|
||||
"message": "Требуется аутентификация. Пожалуйста, предоставьте валидный JWT токен."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 500 Internal Server Error
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Внутренняя ошибка сервера",
|
||||
"error": {
|
||||
"code": "INTERNAL_SERVER_ERROR",
|
||||
"message": "Произошла ошибка при сохранении credentials"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Миграция с старого API
|
||||
|
||||
Если вы использовали старый API, где `credentials` был только строкой, изменения минимальны:
|
||||
|
||||
**Старый код:**
|
||||
|
||||
```javascript
|
||||
body: JSON.stringify({
|
||||
platform: 'facebook',
|
||||
credentials: 'EAABwzLix...',
|
||||
});
|
||||
```
|
||||
|
||||
**Новый код (без изменений для простых токенов):**
|
||||
|
||||
```javascript
|
||||
// Работает как раньше
|
||||
body: JSON.stringify({
|
||||
platform: 'facebook',
|
||||
credentials: 'EAABwzLix...', // String по-прежнему поддерживается
|
||||
});
|
||||
```
|
||||
|
||||
**Для Telegram (новый формат):**
|
||||
|
||||
```javascript
|
||||
body: JSON.stringify({
|
||||
platform: 'telegram',
|
||||
credentials: {
|
||||
// Теперь можно передавать объект
|
||||
botToken: '...',
|
||||
chatId: '...',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Важные замечания
|
||||
|
||||
1. **Обратная совместимость**: API полностью обратно совместим. Старый формат (String) продолжает работать.
|
||||
|
||||
2. **Автоматическая конвертация**: Если вы передаете объект, он автоматически сериализуется в JSON строку перед сохранением.
|
||||
|
||||
3. **Безопасность**: Все credentials автоматически шифруются перед сохранением в базе данных.
|
||||
|
||||
4. **Валидация**: Поле `credentials` обязательно (`@NotNull`), но может быть как строкой, так и объектом.
|
||||
|
||||
## Примеры для TypeScript
|
||||
|
||||
```typescript
|
||||
interface SimpleCredentials {
|
||||
platform: string;
|
||||
credentials: string; // Для Facebook, LinkedIn
|
||||
}
|
||||
|
||||
interface TelegramCredentials {
|
||||
platform: 'telegram';
|
||||
credentials: {
|
||||
botToken: string;
|
||||
chatId: string;
|
||||
};
|
||||
}
|
||||
|
||||
type CredentialsRequest = SimpleCredentials | TelegramCredentials;
|
||||
|
||||
// Использование
|
||||
const saveCredentials = async (request: CredentialsRequest) => {
|
||||
const response = await fetch('/api/social-media/credentials', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwtToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
|
||||
return response.json();
|
||||
};
|
||||
```
|
||||
+204
-175
@@ -1,9 +1,12 @@
|
||||
package kz.konturai.parser.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import kz.konturai.parser.dto.*;
|
||||
import kz.konturai.parser.model.SocialMediaCredentials;
|
||||
import kz.konturai.parser.service.JwtService;
|
||||
import kz.konturai.parser.service.SocialMediaCredentialsService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
@@ -19,193 +22,219 @@ import java.util.stream.Collectors;
|
||||
@RequestMapping("/api/social-media/credentials")
|
||||
public class SocialMediaCredentialsController {
|
||||
|
||||
private final SocialMediaCredentialsService credentialsService;
|
||||
private final JwtService jwtService;
|
||||
private static final Logger logger = LoggerFactory.getLogger(SocialMediaCredentialsController.class);
|
||||
|
||||
public SocialMediaCredentialsController(
|
||||
SocialMediaCredentialsService credentialsService,
|
||||
JwtService jwtService) {
|
||||
this.credentialsService = credentialsService;
|
||||
this.jwtService = jwtService;
|
||||
}
|
||||
private final SocialMediaCredentialsService credentialsService;
|
||||
private final JwtService jwtService;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private String extractUserIdFromHeader(String authHeader) {
|
||||
if (authHeader == null || authHeader.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return jwtService.extractUserIdFromHeader(authHeader);
|
||||
}
|
||||
|
||||
private ResponseEntity<?> unauthorizedResponse() {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"UNAUTHORIZED",
|
||||
"Требуется аутентификация. Пожалуйста, предоставьте валидный JWT токен.");
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
||||
.body(ApiResponse.error("Не авторизован", error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранение или обновление credentials для платформы
|
||||
* POST /api/social-media/credentials
|
||||
*/
|
||||
@PostMapping
|
||||
public ResponseEntity<?> saveCredentials(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@Valid @RequestBody SocialMediaCredentialsRequest request) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
public SocialMediaCredentialsController(
|
||||
SocialMediaCredentialsService credentialsService,
|
||||
JwtService jwtService) {
|
||||
this.credentialsService = credentialsService;
|
||||
this.jwtService = jwtService;
|
||||
this.objectMapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
try {
|
||||
SocialMediaCredentials credentials = credentialsService.saveCredentials(
|
||||
userId,
|
||||
request.getPlatform(),
|
||||
request.getCredentials()
|
||||
);
|
||||
|
||||
SocialMediaCredentialsResponse response = new SocialMediaCredentialsResponse(
|
||||
credentials.getPlatform(),
|
||||
true,
|
||||
credentials.getCreatedAt(),
|
||||
credentials.getUpdatedAt()
|
||||
);
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(
|
||||
"Credentials успешно сохранены для платформы " + request.getPlatform(),
|
||||
response));
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", e.getMessage());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error("Ошибка валидации", error));
|
||||
} catch (Exception e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла ошибка при сохранении credentials");
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", error));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение информации о credentials для платформы
|
||||
* GET /api/social-media/credentials/{platform}
|
||||
*/
|
||||
@GetMapping("/{platform}")
|
||||
public ResponseEntity<?> getCredentials(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String platform) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
private String extractUserIdFromHeader(String authHeader) {
|
||||
if (authHeader == null || authHeader.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return jwtService.extractUserIdFromHeader(authHeader);
|
||||
}
|
||||
|
||||
boolean hasCredentials = credentialsService.hasCredentials(userId, platform);
|
||||
SocialMediaCredentialsResponse response = new SocialMediaCredentialsResponse(
|
||||
platform,
|
||||
hasCredentials
|
||||
);
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение списка всех платформ с credentials
|
||||
* GET /api/social-media/credentials
|
||||
*/
|
||||
@GetMapping
|
||||
public ResponseEntity<?> getAllCredentials(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
List<SocialMediaCredentials> credentialsList = credentialsService.getUserCredentials(userId);
|
||||
List<SocialMediaCredentialsResponse> responseList = credentialsList.stream()
|
||||
.map(cred -> new SocialMediaCredentialsResponse(
|
||||
cred.getPlatform(),
|
||||
true,
|
||||
cred.getCreatedAt(),
|
||||
cred.getUpdatedAt()
|
||||
))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(responseList));
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление credentials для платформы
|
||||
* DELETE /api/social-media/credentials/{platform}
|
||||
*/
|
||||
@DeleteMapping("/{platform}")
|
||||
public ResponseEntity<?> deleteCredentials(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String platform) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
try {
|
||||
if (!credentialsService.hasCredentials(userId, platform)) {
|
||||
private ResponseEntity<?> unauthorizedResponse() {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Credentials для платформы " + platform + " не найдены");
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(ApiResponse.error("Credentials не найдены", error));
|
||||
}
|
||||
|
||||
credentialsService.deleteCredentials(userId, platform);
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(
|
||||
"Credentials для платформы " + platform + " успешно удалены",
|
||||
null));
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", e.getMessage());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error("Ошибка валидации", error));
|
||||
} catch (Exception e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла ошибка при удалении credentials");
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", error));
|
||||
"UNAUTHORIZED",
|
||||
"Требуется аутентификация. Пожалуйста, предоставьте валидный JWT токен.");
|
||||
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
|
||||
.body(ApiResponse.error("Не авторизован", error));
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleValidationException(
|
||||
MethodArgumentNotValidException ex) {
|
||||
/**
|
||||
* Сохранение или обновление credentials для платформы
|
||||
* POST /api/social-media/credentials
|
||||
*/
|
||||
@PostMapping
|
||||
public ResponseEntity<?> saveCredentials(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@Valid @RequestBody SocialMediaCredentialsRequest request) {
|
||||
|
||||
Map<String, String> details = new HashMap<>();
|
||||
ex.getBindingResult().getFieldErrors().forEach(error -> {
|
||||
details.put(error.getField(), error.getDefaultMessage());
|
||||
});
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
ErrorResponse errorResponse = new ErrorResponse(
|
||||
"VALIDATION_ERROR",
|
||||
"Ошибка валидации входных данных",
|
||||
details);
|
||||
try {
|
||||
// Конвертируем credentials в строку: если это String - используем как есть,
|
||||
// если Object - сериализуем в JSON
|
||||
String credentialsString = convertCredentialsToString(request.getCredentials());
|
||||
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error("Ошибка валидации", errorResponse));
|
||||
}
|
||||
SocialMediaCredentials credentials = credentialsService.saveCredentials(
|
||||
userId,
|
||||
request.getPlatform(),
|
||||
credentialsString);
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleGenericException(Exception e) {
|
||||
ErrorResponse errorResponse = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла внутренняя ошибка сервера. Попробуйте позже.");
|
||||
SocialMediaCredentialsResponse response = new SocialMediaCredentialsResponse(
|
||||
credentials.getPlatform(),
|
||||
true,
|
||||
credentials.getCreatedAt(),
|
||||
credentials.getUpdatedAt());
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", errorResponse));
|
||||
}
|
||||
return ResponseEntity.ok(ApiResponse.success(
|
||||
"Credentials успешно сохранены для платформы " + request.getPlatform(),
|
||||
response));
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", e.getMessage());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error("Ошибка валидации", error));
|
||||
} catch (Exception e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла ошибка при сохранении credentials");
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", error));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение информации о credentials для платформы
|
||||
* GET /api/social-media/credentials/{platform}
|
||||
*/
|
||||
@GetMapping("/{platform}")
|
||||
public ResponseEntity<?> getCredentials(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String platform) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
boolean hasCredentials = credentialsService.hasCredentials(userId, platform);
|
||||
SocialMediaCredentialsResponse response = new SocialMediaCredentialsResponse(
|
||||
platform,
|
||||
hasCredentials);
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Получение списка всех платформ с credentials
|
||||
* GET /api/social-media/credentials
|
||||
*/
|
||||
@GetMapping
|
||||
public ResponseEntity<?> getAllCredentials(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
List<SocialMediaCredentials> credentialsList = credentialsService.getUserCredentials(userId);
|
||||
List<SocialMediaCredentialsResponse> responseList = credentialsList.stream()
|
||||
.map(cred -> new SocialMediaCredentialsResponse(
|
||||
cred.getPlatform(),
|
||||
true,
|
||||
cred.getCreatedAt(),
|
||||
cred.getUpdatedAt()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(responseList));
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаление credentials для платформы
|
||||
* DELETE /api/social-media/credentials/{platform}
|
||||
*/
|
||||
@DeleteMapping("/{platform}")
|
||||
public ResponseEntity<?> deleteCredentials(
|
||||
@RequestHeader(value = "Authorization", required = false) String authHeader,
|
||||
@PathVariable String platform) {
|
||||
|
||||
String userId = extractUserIdFromHeader(authHeader);
|
||||
if (userId == null) {
|
||||
return unauthorizedResponse();
|
||||
}
|
||||
|
||||
try {
|
||||
if (!credentialsService.hasCredentials(userId, platform)) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"NOT_FOUND",
|
||||
"Credentials для платформы " + platform + " не найдены");
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(ApiResponse.error("Credentials не найдены", error));
|
||||
}
|
||||
|
||||
credentialsService.deleteCredentials(userId, platform);
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(
|
||||
"Credentials для платформы " + platform + " успешно удалены",
|
||||
null));
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
ErrorResponse error = new ErrorResponse("VALIDATION_ERROR", e.getMessage());
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error("Ошибка валидации", error));
|
||||
} catch (Exception e) {
|
||||
ErrorResponse error = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла ошибка при удалении credentials");
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", error));
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleValidationException(
|
||||
MethodArgumentNotValidException ex) {
|
||||
|
||||
Map<String, String> details = new HashMap<>();
|
||||
ex.getBindingResult().getFieldErrors().forEach(error -> {
|
||||
details.put(error.getField(), error.getDefaultMessage());
|
||||
});
|
||||
|
||||
ErrorResponse errorResponse = new ErrorResponse(
|
||||
"VALIDATION_ERROR",
|
||||
"Ошибка валидации входных данных",
|
||||
details);
|
||||
|
||||
return ResponseEntity.badRequest()
|
||||
.body(ApiResponse.error("Ошибка валидации", errorResponse));
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResponse<ErrorResponse>> handleGenericException(Exception e) {
|
||||
ErrorResponse errorResponse = new ErrorResponse(
|
||||
"INTERNAL_SERVER_ERROR",
|
||||
"Произошла внутренняя ошибка сервера. Попробуйте позже.");
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResponse.error("Внутренняя ошибка сервера", errorResponse));
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертирует credentials (Object или String) в строку для сохранения
|
||||
* Если это String - возвращает как есть
|
||||
* Если это Object - сериализует в JSON строку
|
||||
*/
|
||||
private String convertCredentialsToString(Object credentials) {
|
||||
if (credentials == null) {
|
||||
throw new IllegalArgumentException("Credentials cannot be null");
|
||||
}
|
||||
|
||||
if (credentials instanceof String) {
|
||||
return (String) credentials;
|
||||
}
|
||||
|
||||
try {
|
||||
// Сериализуем объект в JSON строку
|
||||
return objectMapper.writeValueAsString(credentials);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to serialize credentials to JSON", e);
|
||||
throw new IllegalArgumentException("Failed to serialize credentials: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
package kz.konturai.parser.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class SocialMediaCredentialsRequest {
|
||||
|
||||
@NotBlank(message = "Platform is required")
|
||||
private String platform;
|
||||
|
||||
@NotBlank(message = "Credentials are required")
|
||||
private String credentials;
|
||||
@NotNull(message = "Credentials are required")
|
||||
@JsonProperty("credentials")
|
||||
private Object credentials; // Может быть String (токен) или Object (JSON объект для Telegram и др.)
|
||||
|
||||
public SocialMediaCredentialsRequest() {
|
||||
}
|
||||
|
||||
public SocialMediaCredentialsRequest(String platform, String credentials) {
|
||||
public SocialMediaCredentialsRequest(String platform, Object credentials) {
|
||||
this.platform = platform;
|
||||
this.credentials = credentials;
|
||||
}
|
||||
@@ -26,12 +29,11 @@ public class SocialMediaCredentialsRequest {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public String getCredentials() {
|
||||
public Object getCredentials() {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
public void setCredentials(String credentials) {
|
||||
public void setCredentials(Object credentials) {
|
||||
this.credentials = credentials;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public class SocialMediaCredentials {
|
||||
private String platform; // "facebook", "instagram", etc.
|
||||
|
||||
@Field("encrypted_credentials")
|
||||
private String encryptedCredentials;
|
||||
private Object encryptedCredentials;
|
||||
|
||||
@Field("created_at")
|
||||
private LocalDateTime createdAt;
|
||||
@@ -32,7 +32,7 @@ public class SocialMediaCredentials {
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public SocialMediaCredentials(String userId, String platform, String encryptedCredentials) {
|
||||
public SocialMediaCredentials(String userId, String platform, Object encryptedCredentials) {
|
||||
this();
|
||||
this.userId = userId;
|
||||
this.platform = platform;
|
||||
@@ -63,11 +63,11 @@ public class SocialMediaCredentials {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public String getEncryptedCredentials() {
|
||||
public Object getEncryptedCredentials() {
|
||||
return encryptedCredentials;
|
||||
}
|
||||
|
||||
public void setEncryptedCredentials(String encryptedCredentials) {
|
||||
public void setEncryptedCredentials(Object encryptedCredentials) {
|
||||
this.encryptedCredentials = encryptedCredentials;
|
||||
this.updatedAt = LocalDateTime.now();
|
||||
}
|
||||
@@ -88,4 +88,3 @@ public class SocialMediaCredentials {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,11 @@ public class SocialMediaCredentialsService {
|
||||
/**
|
||||
* Сохраняет или обновляет credentials пользователя для платформы
|
||||
*
|
||||
* @param userId ID пользователя
|
||||
* @param platform Платформа (facebook, instagram, etc.)
|
||||
* @param credentials Открытые credentials (будут зашифрованы)
|
||||
* @param userId ID пользователя
|
||||
* @param platform Платформа (facebook, instagram, telegram, etc.)
|
||||
* @param credentials Открытые credentials (могут быть String для токена или
|
||||
* JSON строка для объектов, например для Telegram:
|
||||
* {"botToken": "...", "chatId": "..."})
|
||||
* @return Сохраненные credentials
|
||||
*/
|
||||
public SocialMediaCredentials saveCredentials(String userId, String platform, String credentials) {
|
||||
@@ -61,7 +63,7 @@ public class SocialMediaCredentialsService {
|
||||
/**
|
||||
* Получает и расшифровывает credentials пользователя для платформы
|
||||
*
|
||||
* @param userId ID пользователя
|
||||
* @param userId ID пользователя
|
||||
* @param platform Платформа
|
||||
* @return Расшифрованные credentials или null если не найдены
|
||||
*/
|
||||
@@ -80,7 +82,22 @@ public class SocialMediaCredentialsService {
|
||||
|
||||
try {
|
||||
SocialMediaCredentials credentialsEntity = optCredentials.get();
|
||||
return encryptionService.decrypt(credentialsEntity.getEncryptedCredentials());
|
||||
Object encryptedCreds = credentialsEntity.getEncryptedCredentials();
|
||||
|
||||
// Если это String (старый формат или зашифрованная строка), расшифровываем
|
||||
if (encryptedCreds instanceof String) {
|
||||
return encryptionService.decrypt((String) encryptedCreds);
|
||||
}
|
||||
|
||||
// Если это Object (например, Map из MongoDB), сериализуем в JSON, затем
|
||||
// расшифровываем
|
||||
// Но обычно зашифрованные данные хранятся как String
|
||||
if (encryptedCreds != null) {
|
||||
String encryptedString = encryptedCreds.toString();
|
||||
return encryptionService.decrypt(encryptedString);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to decrypt credentials for user {} and platform {}", userId, platform, e);
|
||||
throw new RuntimeException("Failed to decrypt credentials", e);
|
||||
@@ -90,7 +107,7 @@ public class SocialMediaCredentialsService {
|
||||
/**
|
||||
* Проверяет наличие credentials для пользователя и платформы
|
||||
*
|
||||
* @param userId ID пользователя
|
||||
* @param userId ID пользователя
|
||||
* @param platform Платформа
|
||||
* @return true если credentials существуют
|
||||
*/
|
||||
@@ -105,7 +122,7 @@ public class SocialMediaCredentialsService {
|
||||
/**
|
||||
* Удаляет credentials пользователя для платформы
|
||||
*
|
||||
* @param userId ID пользователя
|
||||
* @param userId ID пользователя
|
||||
* @param platform Платформа
|
||||
*/
|
||||
public void deleteCredentials(String userId, String platform) {
|
||||
@@ -147,4 +164,3 @@ public class SocialMediaCredentialsService {
|
||||
return repository.findByUserId(userId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user