feat: 站点提交/返回统一支持多名称多地址数组
- POST /sites/{id}/submit: names[]/urls[] 数组,兼容旧 name/baseUrl 单值
- 已有站点合并新名称/地址(去重+前置)
- 站点列表返回 names[]/urls[]
- API 文档和代码示例同步更新
This commit is contained in:
27
docs/API.md
27
docs/API.md
@@ -164,13 +164,14 @@ curl -H "If-None-Match: a1b2c3d4..." http://par:9012/api/v1/manifest
|
||||
#### POST /api/v1/sites/{siteId}/submit
|
||||
|
||||
```json
|
||||
// Request
|
||||
// Request (多名称/多地址)
|
||||
{
|
||||
"name": "PThome", // 必填
|
||||
"baseUrl": "https://pthome.net", // 必填
|
||||
"names": ["PThome", "PThome镜像"], // 必填
|
||||
"urls": ["https://pthome.net", "https://pthome.mirror"], // 必填
|
||||
// 或向后兼容单值: "name": "PThome", "baseUrl": "https://..."
|
||||
"description": "PT 资源站",
|
||||
"iconUrl": "https://...",
|
||||
"siteType": "private" // private/public
|
||||
"siteType": "private"
|
||||
}
|
||||
|
||||
// Response 200
|
||||
@@ -179,8 +180,8 @@ curl -H "If-None-Match: a1b2c3d4..." http://par:9012/api/v1/manifest
|
||||
"data": {
|
||||
"id": 1,
|
||||
"siteId": "pthome",
|
||||
"name": "PThome",
|
||||
"baseUrl": "https://pthome.net",
|
||||
"names": ["PThome", "PThome镜像"],
|
||||
"urls": ["https://pthome.net", "https://pthome.mirror"],
|
||||
"siteType": "private",
|
||||
"schemaVersion": "1.0.0",
|
||||
"status": "active",
|
||||
@@ -282,9 +283,9 @@ curl -H "If-None-Match: a1b2c3d4..." http://par:9012/api/v1/manifest
|
||||
|------|------|------|
|
||||
| id | Long | 内部 ID |
|
||||
| siteId | String | 站点唯一标识(如 `pthome`) |
|
||||
| name | String | 显示名称 |
|
||||
| names | String[] | 站点名称列表 |
|
||||
| urls | String[] | 站点地址列表 |
|
||||
| description | String | 描述 |
|
||||
| baseUrl | String | 站点 URL |
|
||||
| iconUrl | String | 图标 URL |
|
||||
| siteType | String | `private` / `public` |
|
||||
| schemaVersion | String | 当前 Schema 版本 |
|
||||
@@ -372,10 +373,10 @@ def hmac_headers(method, path):
|
||||
"X-Email": EMAIL
|
||||
}
|
||||
|
||||
def submit_site(site_id, name, base_url, site_type="private"):
|
||||
def submit_site(site_id, names, urls, site_type="private"):
|
||||
path = f"/api/v1/sites/{site_id}/submit"
|
||||
return requests.post(f"{BASE}{path}",
|
||||
json={"name": name, "baseUrl": base_url, "siteType": site_type},
|
||||
json={"names": names, "urls": urls, "siteType": site_type},
|
||||
headers=hmac_headers("POST", path)
|
||||
).json()
|
||||
|
||||
@@ -396,7 +397,7 @@ def download_config(site_id, version):
|
||||
return resp.json()
|
||||
|
||||
# ---- 使用 ----
|
||||
submit_site("pthome", "PThome", "https://pthome.net")
|
||||
submit_site("pthome", ["PThome", "PThome镜像"], ["https://pthome.net", "https://pthome.mirror"])
|
||||
submit_config("pthome", "1.0.0", '{"rules":[...]}')
|
||||
print(list_configs("pthome"))
|
||||
```
|
||||
@@ -429,12 +430,12 @@ async function hmacHeaders(method, path) {
|
||||
};
|
||||
}
|
||||
|
||||
async function submitSite(siteId, name, baseUrl, siteType = 'private') {
|
||||
async function submitSite(siteId, names, urls, siteType = 'private') {
|
||||
const path = `/api/v1/sites/${siteId}/submit`;
|
||||
const res = await fetch(`${BASE}${path}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...await hmacHeaders('POST', path) },
|
||||
body: JSON.stringify({ name, baseUrl, siteType })
|
||||
body: JSON.stringify({ names, urls, siteType })
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@ public class SiteController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交站点收录(需 HMAC 鉴权)
|
||||
* 创建或更新站点信息,自动设置为活跃状态
|
||||
* 提交站点收录(需 PSK 鉴权)
|
||||
* 支持 names[]/urls[] 数组或 name/baseUrl 单值(向后兼容)
|
||||
*/
|
||||
@PostMapping("/{siteId}/submit")
|
||||
public ApiResponse<SiteDTO> submitSite(
|
||||
@@ -75,22 +75,31 @@ public class SiteController {
|
||||
@RequestBody Map<String, Object> payload,
|
||||
HttpServletRequest request) {
|
||||
|
||||
String name = (String) payload.get("name");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> names = (List<String>) payload.get("names");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> urls = (List<String>) payload.get("urls");
|
||||
// 向后兼容单值
|
||||
if ((names == null || names.isEmpty()) && payload.containsKey("name")) {
|
||||
names = List.of((String) payload.get("name"));
|
||||
}
|
||||
if ((urls == null || urls.isEmpty()) && payload.containsKey("baseUrl")) {
|
||||
urls = List.of((String) payload.get("baseUrl"));
|
||||
}
|
||||
|
||||
if (names == null || names.isEmpty()) {
|
||||
return ApiResponse.error(400, "names or name is required");
|
||||
}
|
||||
if (urls == null || urls.isEmpty()) {
|
||||
return ApiResponse.error(400, "urls or baseUrl is required");
|
||||
}
|
||||
|
||||
String description = (String) payload.get("description");
|
||||
String baseUrl = (String) payload.get("baseUrl");
|
||||
String iconUrl = (String) payload.get("iconUrl");
|
||||
String siteType = (String) payload.get("siteType");
|
||||
|
||||
if (name == null || name.isBlank()) {
|
||||
return ApiResponse.error(400, "name is required");
|
||||
}
|
||||
if (baseUrl == null || baseUrl.isBlank()) {
|
||||
return ApiResponse.error(400, "baseUrl is required");
|
||||
}
|
||||
|
||||
Long submitterId = (Long) request.getAttribute("accountId");
|
||||
|
||||
Site site = siteService.submitSite(siteId, name, description, baseUrl, iconUrl, siteType, submitterId);
|
||||
Site site = siteService.submitSite(siteId, names, urls, description, iconUrl, siteType, submitterId);
|
||||
return ApiResponse.success(siteService.toDTO(site));
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public interface SiteService {
|
||||
/**
|
||||
* 提交站点信息(创建 pending,更新保持原状态)
|
||||
*/
|
||||
Site submitSite(String siteId, String name, String description, String baseUrl,
|
||||
Site submitSite(String siteId, List<String> names, List<String> urls, String description,
|
||||
String iconUrl, String siteType, Long submitterId);
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,18 +90,22 @@ public class SiteServiceImpl implements SiteService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Site submitSite(String siteId, String name, String description, String baseUrl,
|
||||
String iconUrl, String siteType, Long submitterId) {
|
||||
public Site submitSite(String siteId, List<String> names, List<String> urls,
|
||||
String description, String iconUrl, String siteType, Long submitterId) {
|
||||
Site existing = siteMapper.selectBySiteId(siteId);
|
||||
if (existing != null) {
|
||||
if (name != null && !name.isBlank()) {
|
||||
List<String> names = fromJsonArray(existing.getNamesJson());
|
||||
if (!names.contains(name)) { names.add(0, name); existing.setNamesJson(toJsonArray(names)); }
|
||||
// 合并名称:新名称置前,去重
|
||||
List<String> existingNames = fromJsonArray(existing.getNamesJson());
|
||||
for (String n : names) {
|
||||
if (n != null && !n.isBlank() && !existingNames.contains(n)) existingNames.add(0, n);
|
||||
}
|
||||
if (baseUrl != null && !baseUrl.isBlank()) {
|
||||
List<String> urls = fromJsonArray(existing.getUrlsJson());
|
||||
if (!urls.contains(baseUrl)) { urls.add(0, baseUrl); existing.setUrlsJson(toJsonArray(urls)); }
|
||||
existing.setNamesJson(toJsonArray(existingNames));
|
||||
// 合并地址
|
||||
List<String> existingUrls = fromJsonArray(existing.getUrlsJson());
|
||||
for (String u : urls) {
|
||||
if (u != null && !u.isBlank() && !existingUrls.contains(u)) existingUrls.add(0, u);
|
||||
}
|
||||
existing.setUrlsJson(toJsonArray(existingUrls));
|
||||
if (description != null) existing.setDescription(description);
|
||||
if (iconUrl != null) existing.setIconUrl(iconUrl);
|
||||
if (siteType != null) existing.setSiteType(SiteType.fromValue(siteType));
|
||||
@@ -111,8 +115,8 @@ public class SiteServiceImpl implements SiteService {
|
||||
|
||||
Site site = new Site();
|
||||
site.setSiteId(siteId);
|
||||
site.setNamesJson(toJsonArray(name != null ? List.of(name) : List.of()));
|
||||
site.setUrlsJson(toJsonArray(baseUrl != null ? List.of(baseUrl) : List.of()));
|
||||
site.setNamesJson(toJsonArray(names.stream().filter(n -> n != null && !n.isBlank()).toList()));
|
||||
site.setUrlsJson(toJsonArray(urls.stream().filter(u -> u != null && !u.isBlank()).toList()));
|
||||
site.setDescription(description);
|
||||
site.setIconUrl(iconUrl);
|
||||
site.setSiteType(siteType != null ? SiteType.fromValue(siteType) : SiteType.UNKNOWN);
|
||||
|
||||
Reference in New Issue
Block a user