# Open T2Editor Marketplace API v1.1.5

Open T2Editor Marketplace와 T2Editor 관리자 설치기 사이의 공개 규격입니다. Open T2Editor는 DSc(dsclub.kr)가 소유·운영하는 공식 마켓의 고유 이름입니다. 독립 운영 마켓의 유형명은 T2Editor Provider Market이고 화면 표시명은 `제공자명 · T2Editor Market`을 사용합니다. API 버전은 `1.1.5`, 매니페스트 스키마는 `t2editor-third-party-v1`입니다. PHP 관리자 비밀번호는 마켓으로 전송하지 않으며 T2Editor 서버에서 다운로드할 때마다 다시 검증해야 합니다.

## 데이터 모델

하나의 프로그램은 하나의 `package`/게시물을 가지며, 그 아래에 여러 `release`가 시간순으로 누적됩니다. 기존 릴리즈는 새 릴리즈가 공개되어도 삭제하거나 덮어쓰지 않습니다.

릴리즈 유형:

- `initial`: 최초 공개
- `patch`: 오류·보안·작은 수정
- `minor_update`: 기능 개선과 소규모 기능 추가
- `minor_upgrade`: 호환성 또는 구조 변경을 포함한 마이너 업그레이드

`GET detail`의 `releases`는 최신순입니다. `challenge`와 `download`에서 `release_id`를 생략하면 현재 최신 릴리즈를 사용합니다.

## 공통 응답

```json
{
  "api": "t2editor-third-party-market",
  "api_version": "1.1.5",
  "ok": true,
  "data": {}
}
```

실패 응답은 `ok: false`와 `error.code`, `error.message`를 반환합니다.

## 엔드포인트

### `GET ?action=spec`

마켓 기능·유형·릴리즈·보안 규격을 반환합니다.

### `GET ?action=catalog&q=&type=&page=&limit=`

프로그램 단위 목록입니다. 각 항목은 최신 버전, `current_release_id`, `release_count`를 포함합니다.

### `GET ?action=detail&id=<package id or slug>`

프로그램 상세와 전체 릴리즈를 반환합니다.

```json
{
  "id": "pkg_...",
  "slug": "word-counter",
  "name": "Word Counter",
  "version": "1.2.1",
  "current_release_id": "rel_...",
  "release_count": 3,
  "manifest": {},
  "releases": [
    {
      "id": "rel_...",
      "version": "1.2.1",
      "release_type": "patch",
      "release_type_label": "패치",
      "release_notes": "카운트 오류 수정",
      "requires": {
        "php_min": "7.4.0",
        "php_max": "8.4.99",
        "t2editor_min": "10.3.1"
      },
      "manifest": {},
      "sha256": "...",
      "file_size": 12345,
      "download_count": 10,
      "published_at": "2026-07-17 12:00:00"
    }
  ]
}
```

### `GET ?action=reviews&id=&page=&limit=`

프로그램 게시물에 달린 별점·리뷰를 반환합니다. 리뷰는 특정 릴리즈가 아니라 프로그램 전체에 연결됩니다.

### `POST ?action=challenge`

```json
{
  "package_id": "pkg_...",
  "release_id": "rel_...",
  "client_id": "t2e-고유클라이언트ID"
}
```

`release_id`는 선택입니다. 응답 티켓은 `package_id`, 선택된 `release_id`, `client_id`, 요청 IP에 묶이고 90초 후 만료됩니다.

### `POST ?action=download`

```json
{
  "package_id": "pkg_...",
  "release_id": "rel_...",
  "client_id": "t2e-고유클라이언트ID",
  "ticket": "challenge에서 받은 1회용 티켓"
}
```

성공 시 해당 릴리즈 ZIP을 반환합니다. 티켓은 한 번만 사용할 수 있습니다.

## 릴리즈 매니페스트

```json
{
  "schema": "t2editor-third-party-v1",
  "id": "word-counter",
  "name": "Word Counter",
  "version": "1.2.1",
  "type": "plugin",
  "release": {
    "type": "patch",
    "label": "패치",
    "notes": "카운트 오류 수정",
    "published_at": "2026-07-17 12:00:00"
  },
  "install": {
    "base": "/plugin",
    "source": "word-counter/",
    "target": "/plugin/word-counter",
    "entry": "word-counter/hooks.js"
  },
  "requires": {
    "php_min": "7.4.0",
    "php_max": "8.4.99",
    "t2editor_min": "10.3.1"
  },
  "license": {
    "id": "MIT",
    "name": "MIT License",
    "custom": ""
  },
  "test_version": "10.3.1",
  "demo_url": "https://example.com/demo",
  "archive": {
    "name": "word-counter.zip",
    "sha256": "64자리 소문자 SHA-256",
    "size": 12345
  }
}
```

## ZIP 설치 구조

- Plugin: `이름.zip` → `플러그인폴더/hooks.js`, 기타 파일 → `/plugin/이름`
- Extend JS: `이름.zip` → `이름.js` → `/extend/js/이름.js`
- Extend PHP: `이름.zip` → `이름.php` → `/extend/php/이름.php`
- Locales: `이름.zip` → `이름/ko.json`, `이름/en.json` 같은 언어코드 JSON → `/locales/이름`

새 릴리즈에서도 프로그램 유형과 설치 이름은 최초 게시물과 동일해야 합니다.


### Plugin 설치 ID 제한

T2Editor 코어 로더와의 호환을 위해 Plugin 설치 ID에는 점(.)을 사용할 수 없습니다. 영문 소문자, 숫자, 밑줄(`_`), 하이픈(`-`)만 사용합니다.


### 서드파티 Locale 오버레이

Locales 패키지는 `/locales/<설치이름>/<언어코드>.json`에 설치됩니다. T2Editor는 기본 `/locales/<언어코드>.json`을 먼저 읽고, `/locales`의 패키지를 설치 이름순으로 병합합니다. 같은 번역 키는 뒤에 로드된 패키지 값이 적용됩니다. 실행 가능한 PHP/JS 파일은 Locales 패키지에 넣을 수 없습니다.
