# Sejong Server API

# **Veluga API 명세서 (v1)**

벨루가에서 제공하는 API 명세서입니다.

---

## **공통(Common)**

### **Response Template**

벨루가에서 응답하는 모든 Response 템플릿은 아래와 같은 구조를 나타냅니다.
_(이벤트 스트림 제외)_

| **필드** | **타입** | **필수 여부** | **설명** |
| --- | --- | --- | --- |
| `success` | `boolean` | Y | 응답 성공 여부 |
| `data` | `object | null` | Y |
| `message` | `string` | Y | 응답 관련 메시지 |
| `errorCode` | `string | null` | Y |

### **Error**

벨루가 서비스 이용하면서 다음과 같은 에러가 발생할 수 있습니다.

| **에러 코드** | **에러 상태** | **내용** |
| --- | --- | --- |
| `MISSING_PARAMETER` | 400 | 필수 파라미터가 없습니다. |
| `INVALID_PARAMETER` | 400 | 파라미터가 잘못되었습니다. |
| `COMMON_SYSTEM_ERROR` | 500 | 알 수 없는 오류가 발생했습니다. |
| `COMMON_BAD_REQUEST` | 400 | 잘못된 요청 입니다. |
| `COMMON_FAILED_RESPONSE` | 500 | 데이터 불러오기에 실패 했습니다. |
| `COMMON_NO_PERMISSION` | 403 | 권한이 없습니다. |
| `COMMON_NO_AUTHORIZATION` | 401 | 인증이 유효하지 않습니다. |
| `USER_NOT_FOUND` | 404 | 유저를 찾을 수 없습니다. |
| `CHANNEL_NOT_FOUND` | 404 | 채널을 찾을 수 없습니다. |
| `FILE_NAME_REQUIRED` | 400 | 파일 이름을 찾을 수 없습니다. |
| `FILE_EXTENSION_NOT_FOUND` | 400 | 파일 확장자를 찾을 수 없습니다. |
| `SESSION_NOT_FOUND` | 404 | 세션을 찾을 수 없습니다. |
| `NO_AVAILABLE_USAGE` | 402 | 사용량을 모두 소진하였습니다. |

---

# 학습API

## 파일 학습 요청

파일 학습을 요청합니다. 업로드 시 AI 서버에 학습 요청을 하고, 학습 결과를 조회하여 AI를 통해 확인할 수 있습니다.

```
curl -X POST "https://sejong-api.veluga.app/api/v1/document/file" \
     -H "X-VELUGA-API-KEY: [YOUR_API_KEY]" \
     -H "Content-Type: multipart/form-data" \
     -F "channelId=[YOUR_CHANNEL_ID]" \
     -F "file=@[PATH_TO_YOUR_FILE]"
```

### 요청

요청 형식을 설명합니다. 요청 형식은 다음과 같습니다.

| 메서드 | URL |
| --- | --- |
| POST | [https://sejong-api.veluga.app/api/v1/document/file](https://sejong-api.veluga.app/api/v1/document/file) |

### 요청 헤더

헤더에 대한 설명은 다음과 같습니다.

| 헤더 | 필수 여부 | 설명 |
| --- | --- | --- |
| X-VELUGA-API-KEY | Y | 벨루가 계정으로 발급받은 API KEY |
| Content-Type |  | multipart/form-data |

### 요청 바디

바디에 대한 설명은 다음과 같습니다.

| 필드 | 타입 | 필수 여부 | 설명 |
| --- | --- | --- | --- |
| channelId | string | Y | 벨루가에서 생성한 채널 ID |
| file | binary | Y | 요청할 파일입니다. |
| docFilter | object | N | 사용자 정의 필터 입니다. 문서 검색에 사용할 인덱스 key를 자유롭게 설정할 수 있습니다. (예: {“tag”: “korea”}) |
|  |  |  | 지원하는 확장자: pdf, txt, docx, md, csv, html, htm |

### 응답

응답 형식을 설명합니다.

### 응답 헤더

헤더에 대한 설명은 다음과 같습니다.

| 헤더 | 필수 여부 | 설명 |
| --- | --- | --- |
| Content-Type | - | application/json |

### 응답 데이터

응답 데이터에 대한 설명은 다음과 같습니다.

| 필드 | 타입 | 필수 여부 | 설명 |
| --- | --- | --- | --- |
| data | string | Y | 학습 요청한 문서 ID |

# 파일 리스트 조회

학습 요청된 파일 리스트를 조회합니다.

```
curl -X GET "https://sejong-api.veluga.app/api/v1/document/file" \
     -H "X-VELUGA-API-KEY: [YOUR_API_KEY]" \
     -G \
     -d "channelId=[YOUR_CHANNEL_ID]" \
     -d "keyword=[OPTIONAL_KEYWORD]" \
     -d "page=[PAGE_NUMBER]" \
     -d "limit=[LIMIT_NUMBER]" \
     -d "sortType=[SORT_TYPE]" \
     -d "sort=[SORT_ORDER]"
```

## 요청

요청 형식을 설명합니다. 요청 형식은 다음과 같습니다.

| 메서드 | URL |
| --- | --- |
| GET | [https://sejong-api.veluga.app/api/v1/document/file](https://sejong-api.veluga.app/api/v1/document/file) |

## 요청 헤더

헤더에 대한 설명은 다음과 같습니다.

| 헤더 | 필수 여부 | 설명 |
| --- | --- | --- |
| X-VELUGA-API-KEY | Y | 벨루가 계정으로 발급받은 API KEY |

## 요청 파라미터

파라미터에 대한 설명은 다음과 같습니다.

| 필드 | 타입 | 필수 여부 | 설명 |
| --- | --- | --- | --- |
| channelId | string | Y | 파일 리스트를 조회할 채널 ID입니다. |
| keyword | string | N | 키워드로 검색할 때 사용하는 필드입니다. |
| page | number | N | 검색할 페이지입니다. (기본값: 1) |
| limit | number | N | 페이지 별 컨텐츠 개수입니다. (기본값: 20) |
| sortType | string | N | 정렬 타입입니다. (기본값: createdAt) |
| sort | string | N | 정렬 순서입니다. (“asc” |

## 응답

응답 형식을 설명합니다.

### 응답 헤더

헤더에 대한 설명은 다음과 같습니다.

| 헤더 | 필수 여부 | 설명 |
| --- | --- | --- |
| Content-Type | - | application/json |

### 응답 데이터

응답 데이터에 대한 설명은 다음과 같습니다.

| 필드 | 타입 | 필수 여부 | 설명 |
| --- | --- | --- | --- |
| data | object | Y | 응답 데이터입니다. |
| data.totalCount | number | Y | 전체 컨텐츠 개수입니다. |
| data.page | number | Y | 현재 페이지입니다. |
| data.limit | number | Y | 페이지별 컨텐츠 개수입니다. |
| data.list | array | Y | 파일 리스트입니다. |
| data.list[0]._id | string | Y | 파일 ID입니다. |
| data.list[0].state | string | Y | 학습 상태입니다. ("training" |
| data.list[0].originalName | string | Y | 학습 문서 원본 이름입니다. |
| data.list[0].signedUrl | string | Y | 파일 다운로드 링크입니다. |
| data.list[0].size | number | Y | 파일 용량입니다. (MB) |
| data.list[0].type | string | Y | 파일 확장자입니다. |
| data.list[0].files | array | Y | 하위 파일 리스트입니다. (지원하지 않습니다.) |

Veluga API 명세서 세종대 V1 20240730 (1).pdf

[https://slashpage.com/veluga-api/d367nxm34k91v2j98pv1](https://slashpage.com/veluga-api/d367nxm34k91v2j98pv1)

공지사항 API PHP

```
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://sejong-api.veluga.app/api/v1/document/post',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS =>'{
"channelId": "66b1f77f6a1a3b4ec211b383",
"title": "공지사항 제목",
"content": "<p>공지사항 내용입니다.</p>",
"board": "공지사항 게시판 보드이름 ",
"fromUrl": "https://example.com/notice",
"regDatetime": "2024-08-13T12:00:00Z",
"metadata":{
"author": "관리자"
},
"attachments": [
{
"name": "2024년 전국의료관련감염감시체계(KONIS) 참여 추가 신청 안내.pdf",
"fileUrl": "https://www.kdca.go.kr/filepath/boardDownload.es?bid=0014&list_no=725306&seq=1"
},
{
"name": "공문KONIS참여신청게시판_사용자매뉴얼(참여기관용).pdf",
"fileUrl": "https://www.kdca.go.kr/filepath/boardDownload.es?bid=0014&list_no=725306&seq=3"
}
],
"docFilter":  {
"docType": "강의계획서"
}
}',
  CURLOPT_HTTPHEADER => array(
    'X-VELUGA-API-KEY: 78712cca19ef5e06192c21b256ce083466e192e291f0cfe8eff4109045e9bf08',
    'Content-Type: application/json'
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

```

결과 조회 

```
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://sejong-api.veluga.app/api/v1/document/post/66c460f6dbdd6fb45cef75ea',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => array(
    'X-VELUGA-API-KEY: 78712cca19ef5e06192c21b256ce083466e192e291f0cfe8eff4109045e9bf08',
    'Content-Type: application/json'
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

```

공지사항 (게시물) 삭제 예시

```
<?php

function get_failed_posts($api_key, $channel_id, $board_name = null, $keyword = null) {
    // 게시물 리스트 조회 API URL 설정
    $list_url = "https://sejong-api.veluga.app/api/v1/document/post";

    // 요청 헤더 설정
    $headers = [
        "X-VELUGA-API-KEY: $api_key",
        "Content-Type: application/json"
    ];

    // 요청 파라미터 설정
    $params = [
        "channelId" => $channel_id,
        "boardName" => $board_name,
        "keyword" => $keyword,
        "page" => 1,
        "limit" => 100, // 최대 100개의 게시물 조회
        "sortType" => "createdAt",
        "sort" => "desc"
    ];

    // 파라미터를 쿼리 스트링으로 변환
    $query = http_build_query($params);

    // cURL 초기화
    $ch = curl_init();

    // cURL 옵션 설정
    curl_setopt($ch, CURLOPT_URL, "$list_url?$query");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    // 요청 실행 및 응답 받기
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    // cURL 종료
    curl_close($ch);

    // 응답 코드가 200이면 데이터 처리
    if ($http_code == 200) {
        $response_data = json_decode($response, true);
        $failed_posts = array_filter($response_data['data']['list'], function($post) {
            return $post['state'] == 'fail';
        });
        return $failed_posts;
    } else {
        echo "게시물 리스트 조회 실패. 응답 코드: $http_code\n";
        echo "응답 내용: $response\n";
        return null;
    }
}

function delete_failed_post($api_key, $post_id) {
    // 게시물 삭제 API URL 설정
    $delete_url = "https://sejong-api.veluga.app/api/v1/document/$post_id";

    // 요청 헤더 설정
    $headers = [
        "X-VELUGA-API-KEY: $api_key"
    ];

    // cURL 초기화
    $ch = curl_init();

    // cURL 옵션 설정
    curl_setopt($ch, CURLOPT_URL, $delete_url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

    // 요청 실행 및 응답 받기
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    // cURL 종료
    curl_close($ch);

    // 응답 코드가 200이면 삭제 성공 메시지 출력
    if ($http_code == 200) {
        echo "게시물 삭제 성공. 게시물 ID: $post_id\n";
    } else {
        echo "게시물 삭제 실패. 게시물 ID: $post_id, 응답 코드: $http_code\n";
        echo "응답 내용: $response\n";
    }
}

function delete_all_failed_posts($api_key, $channel_id, $board_name = null, $keyword = null) {
    // 학습이 실패한 게시물 리스트 가져오기
    $failed_posts = get_failed_posts($api_key, $channel_id, $board_name, $keyword);

    if (!empty($failed_posts)) {
        echo "총 " . count($failed_posts) . "개의 학습 실패 게시물이 발견되었습니다.\n";
        foreach ($failed_posts as $post) {
            delete_failed_post($api_key, $post['_id']);
        }
    } else {
        echo "학습 실패한 게시물이 없습니다.\n";
    }
}

?>

```

php 파일 업로드 샘플 (강제업데이트) 에러체크 까지

```
<?php

function train_file_and_check_status($api_key, $channel_id, $file_path, $doc_filter = null) {
    // API URL 설정
    $upload_url = "https://sejong-api.veluga.app/api/v1/document/file";
    $check_url_template = "https://sejong-api.veluga.app/api/v1/document/file/{id}";

    // 헤더 설정
    $headers = [
        "X-VELUGA-API-KEY: $api_key"
    ];

    // 데이터 설정
    $post_fields = [
        'channelId' => $channel_id,
        'forceUpdate' => 'true'
    ];

    if ($doc_filter !== null) {
        $post_fields['docFilter'] = json_encode($doc_filter); // doc_filter를 JSON 문자열로 변환
    }

    // 파일 추가
    $post_fields['file'] = curl_file_create($file_path);

    // 파일 업로드 요청
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $upload_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($http_code == 200) {
        $response_data = json_decode($response, true);
        $file_id = $response_data['data'];
        $success = $response_data['success'];

        if (!$success) {
            echo "파일 업로드 실패. 응답 코드: $http_code\n";
            echo "응답 내용: $response\n";
            return;
        }
        echo "파일 업로드 성공. 파일 ID: $file_id\n";

        // 학습 상태 체크 (2초 간격)
        while (true) {
            $check_url = str_replace("{id}", $file_id, $check_url_template);
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $check_url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            
            $check_response = curl_exec($ch);
            $check_http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);

            if ($check_http_code == 200) {
                $check_data = json_decode($check_response, true)['data'];
                $file_state = $check_data['state'];
                
                echo "파일 상태: $file_state\n";
                
                if ($file_state === 'success' || $file_state === 'fail') {
                    echo "학습이 $file_state 상태로 완료되었습니다.\n";
                    break;
                }
            } else {
                echo "학습 상태 체크 실패. 응답 코드: $check_http_code\n";
                break;
            }

            sleep(2); // 2초 대기
        }
    } else {
        echo "파일 업로드 실패. 응답 코드: $http_code\n";
        echo "응답 내용: $response\n";
    }
}

// 예시 사용법
$api_key = 'your_api_key_here';
$channel_id = 'your_channel_id_here';
$file_path = '/path/to/your/file.pdf';  // 실제 파일 경로로 변경하세요
$doc_filter = [
    'docType' => '공지사항'
];

train_file_and_check_status($api_key, $channel_id, $file_path, $doc_filter);

?>
```

For the site tree, see the [root Markdown](https://slashpage.com/veluga-api.md).
