Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ English | [简体中文](./README_cn.md)
- [Base Service](#base-service)
- [Environment Variables](#environment-variables)
- [Build and Run Application](#build-and-run-application)
- [Testing](#testing)
- [Simplified Deployment](#simplified-deployment)
- [Using OpenAPI and Swagger UI](#using-openapi-and-swagger-ui)
- [Evolution Plan](#evolution-plan)
- [Contribution Guidelines](#contribution-guidelines)
Expand Down Expand Up @@ -128,6 +130,92 @@ The application is now runnable using the following command:
java -jar target/quarkus-app/quarkus-run.jar
```

## Testing

### Running Unit Tests

```shell script
./mvnw test
```

### Running Tests with Coverage Report

```shell script
./mvnw clean verify jacoco:report
```

The coverage report will be generated at `target/site/jacoco/index.html`.

### Test Structure

The project includes the following test categories:

| Test File | Coverage |
|-----------|----------|
| `RegistryResourceTest` | Box/User/Client registration API |
| `SpaceResourceTest` | Simplified space registration API |
| `NetworkResourceTest` | Network authentication and server APIs |
| `BasicResourceTest` | Platform status and ability APIs |
| `TokenResourceTest` | Token management APIs |
| `AuthServiceTest` | Authentication service logic |
| `CommonUtilsTest` | Utility functions |
| `OperationUtilsTest` | Crypto operations |

### API Smoke Test

After deployment, you can run the smoke test:

```bash
# Set the platform base URL
export PLATFORM_BASE=http://localhost:8080

# Run smoke test
../../scripts/platform-smoke.sh

# Run full API test
../../scripts/platform-api-test.sh
```

## Simplified Deployment

For single-machine personal deployment, we provide a simplified setup that reduces complexity:

- **Containers**: 6 instead of 7 (merged mysql-update)
- **DNS Records**: 2 instead of 7 (just `@` and `*`)
- **Registration API**: 1 step instead of 4 (new `/v2/platform/spaces` endpoint)

### Quick Start (Simplified)

```bash
cd deploy/platform
cp .env.simple.example .env
# Edit .env with your domain and passwords

mkdir -p data/ssl
# Place your SSL certificate (tls.crt, tls.key) in data/ssl/

docker compose -f docker-compose.simple.yml up -d
./scripts/init-network.sh
```

### Simplified API

The new `/v2/platform/spaces` endpoint combines box, user, and client registration:

```bash
curl -X POST https://platform.example.com/v2/platform/spaces \
-H "Content-Type: application/json" \
-H "Request-Id: $(uuidgen)" \
-d '{
"boxUUID": "your-box-uuid",
"userId": "admin",
"clientUUID": "your-client-uuid",
"subdomain": "myspace"
}'
```

For detailed API changes, see [Platform API Changes](../../docs/en/platform-api-changes.md).

## Using OpenAPI and Swagger UI

OpenAPI descriptor and Swagger UI frontend to test your REST endpoints: `http://localhost:8080/platform/q/swagger-ui/`
Expand Down
88 changes: 88 additions & 0 deletions README_cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
- [Base Service](#base-service)
- [环境变量](#环境变量)
- [构建和运行应用程序](#构建和运行应用程序)
- [测试](#测试)
- [精简部署](#精简部署)
- [使用 OpenAPI 和 Swagger UI](#使用-openapi-和-swagger-ui)
- [演进计划](#演进计划)
- [贡献指南](#贡献指南)
Expand Down Expand Up @@ -125,6 +127,92 @@ app:
java -jar target/quarkus-app/quarkus-run.jar
```

## 测试

### 运行单元测试

```shell脚本
./mvnw test
```

### 运行测试并生成覆盖率报告

```shell脚本
./mvnw clean verify jacoco:report
```

覆盖率报告将生成在 `target/site/jacoco/index.html`。

### 测试结构

项目包含以下测试类别:

| 测试文件 | 覆盖范围 |
|---------|---------|
| `RegistryResourceTest` | 盒子/用户/客户端注册 API |
| `SpaceResourceTest` | 简化的空间注册 API |
| `NetworkResourceTest` | 网络认证和服务器 API |
| `BasicResourceTest` | 平台状态和能力 API |
| `TokenResourceTest` | Token 管理 API |
| `AuthServiceTest` | 认证服务逻辑 |
| `CommonUtilsTest` | 工具函数 |
| `OperationUtilsTest` | 加密操作 |

### API 烟雾测试

部署后,可以运行烟雾测试:

```bash
# 设置平台基础 URL
export PLATFORM_BASE=http://localhost:8080

# 运行烟雾测试
../../scripts/platform-smoke.sh

# 运行完整 API 测试
../../scripts/platform-api-test.sh
```

## 精简部署

对于单机个人部署,我们提供了简化的配置:

- **容器数量**: 6 个(合并了 mysql-update)
- **DNS 记录**: 2 条(只需 `@` 和 `*`)
- **注册 API**: 1 步完成(新的 `/v2/platform/spaces` 端点)

### 快速开始(精简版)

```bash
cd deploy/platform
cp .env.simple.example .env
# 编辑 .env 设置域名和密码

mkdir -p data/ssl
# 将 SSL 证书 (tls.crt, tls.key) 放入 data/ssl/

docker compose -f docker-compose.simple.yml up -d
./scripts/init-network.sh
```

### 简化 API

新的 `/v2/platform/spaces` 端点合并了盒子、用户、客户端注册:

```bash
curl -X POST https://platform.example.com/v2/platform/spaces \
-H "Content-Type: application/json" \
-H "Request-Id: $(uuidgen)" \
-d '{
"boxUUID": "your-box-uuid",
"userId": "admin",
"clientUUID": "your-client-uuid",
"subdomain": "myspace"
}'
```

详细的 API 变化请参阅 [平台 API 变更说明](../../docs/cn/platform-api-changes.md)。

## 使用 OpenAPI 和 Swagger UI

OpenAPI 描述符和 Swagger UI 前端来测试 REST 端点,访问地址:`http://localhost:8080/platform/q/swagger-ui/`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2022 Institute of Software Chinese Academy of Sciences (ISCAS)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package xyz.eulix.platform.services.registry.dto.registry;

import lombok.Data;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;

/**
* Simplified space registration request DTO.
* Combines box, user, and client registration into one step.
*/
@Data
public class SpaceRegistryInfo {
@NotBlank
@Schema(description = "Box UUID")
private String boxUUID;

@NotBlank
@Schema(description = "User ID")
private String userId;

@NotBlank
@Schema(description = "Client UUID")
private String clientUUID;

@Schema(description = "Preferred subdomain (optional, auto-generated if not provided)")
@Pattern(regexp = "^[a-z][a-z0-9]{5,19}$", message = "Subdomain must start with a letter, contain only lowercase letters and numbers, and be 6-20 characters long")
private String subdomain;

@Schema(description = "User type: user_admin or user_member, defaults to user_admin")
private String userType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2022 Institute of Software Chinese Academy of Sciences (ISCAS)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package xyz.eulix.platform.services.registry.dto.registry;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
* Simplified space registration result DTO.
* Returns all necessary information for the client to connect.
*/
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class SpaceRegistryResult {
@Schema(description = "Box UUID")
private String boxUUID;

@Schema(description = "User ID")
private String userId;

@Schema(description = "Client UUID")
private String clientUUID;

@Schema(description = "Assigned subdomain")
private String subdomain;

@Schema(description = "Full user domain for accessing the space")
private String userDomain;

@Schema(description = "User type: user_admin or user_member")
private String userType;

@Schema(description = "Network client credentials for NAT traversal")
private NetworkClient networkClient;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright (c) 2022 Institute of Software Chinese Academy of Sciences (ISCAS)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package xyz.eulix.platform.services.registry.rest;

import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import xyz.eulix.platform.common.support.log.Logged;
import xyz.eulix.platform.services.registry.dto.registry.SpaceRegistryInfo;
import xyz.eulix.platform.services.registry.dto.registry.SpaceRegistryResult;
import xyz.eulix.platform.services.registry.service.RegistryService;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

/**
* Simplified Space Registration API.
* Provides a one-step registration process for personal deployment scenarios.
*/
@RequestScoped
@Path("/v2/platform")
@Tag(name = "Platform Space Service", description = "Simplified space registration API for personal deployment")
public class SpaceResource {
private static final Logger LOG = Logger.getLogger("app.log");

@Inject
RegistryService registryService;

@Logged
@POST
@Path("/spaces")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Operation(description = "One-step space registration. Registers box, user, and client in a single call. " +
"Returns network credentials and user domain for immediate use.")
public SpaceRegistryResult registerSpace(
@Valid SpaceRegistryInfo spaceInfo,
@HeaderParam("Request-Id") @NotBlank String reqId) {
LOG.infov("Space registration request: boxUUID={0}, userId={1}, clientUUID={2}, subdomain={3}",
spaceInfo.getBoxUUID(), spaceInfo.getUserId(), spaceInfo.getClientUUID(), spaceInfo.getSubdomain());
return registryService.registerSpace(spaceInfo);
}

@Logged
@DELETE
@Path("/spaces/{box_uuid}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(description = "Delete space registration. Removes box, all users, clients, and subdomains.")
public Response deleteSpace(
@PathParam("box_uuid") @NotBlank String boxUUID,
@HeaderParam("Request-Id") @NotBlank String reqId) {
LOG.infov("Space deletion request: boxUUID={0}", boxUUID);
boolean exists = registryService.hasBoxRegistered(boxUUID);
if (!exists) {
LOG.warnv("Box not registered: boxUUID={0}", boxUUID);
throw new WebApplicationException("Box not registered", Response.Status.NOT_FOUND);
}
registryService.resetBox(boxUUID);
return Response.noContent().build();
}

@Logged
@GET
@Path("/spaces/{box_uuid}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(description = "Get space registration details including users, clients, and subdomains.")
public Response getSpace(
@PathParam("box_uuid") @NotBlank String boxUUID,
@HeaderParam("Request-Id") @NotBlank String reqId) {
LOG.infov("Space query request: boxUUID={0}", boxUUID);
return Response.ok(registryService.boxRegistryBindUserAndClientInfo(boxUUID)).build();
}
}
Loading
Loading