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
25 changes: 21 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
build:
name: Build ESP32-S3 Firmware
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}

steps:
- name: Checkout repository
Expand Down Expand Up @@ -87,9 +89,9 @@ jobs:
run: |
if [ -f build/project_description.json ]; then
VERSION=$(grep -o '"project_version":[^,]*' build/project_description.json | cut -d'"' -f4)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "📦 Build version: $VERSION"
fi
echo "version=${VERSION:-0.0.0}" >> $GITHUB_OUTPUT
echo "📦 Build version: ${VERSION:-0.0.0}"

- name: Print build size
run: |
Expand Down Expand Up @@ -118,11 +120,11 @@ jobs:
build/config/sdkconfig.h
retention-days: 7

# 可选:发布版本时创建 Release
# 编译成功后自动创建 Release:tag 推送 或 main 分支推送
release:
name: Create Release
needs: build
if: startsWith(github.ref, 'refs/tags/v')
if: startsWith(github.ref, 'refs/tags/v') || (github.ref == 'refs/heads/main' && github.event_name == 'push')
runs-on: ubuntu-latest
permissions:
contents: write # 创建 Release 需要写权限
Expand All @@ -134,9 +136,24 @@ jobs:
pattern: tianshanos-firmware-*
path: firmware

- name: Check if release already exists (main branch)
if: github.ref == 'refs/heads/main'
id: check
run: |
TAG="v${{ needs.build.outputs.version }}"
if gh release view "$TAG" 2>/dev/null; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "⏭️ Release $TAG already exists, skipping"
else
echo "skip=false" >> $GITHUB_OUTPUT
fi

- name: Create Release
if: startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && steps.check.outputs.skip != 'true')
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || format('v{0}', needs.build.outputs.version) }}
name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || format('v{0}', needs.build.outputs.version) }}
files: firmware/**/*.bin
generate_release_notes: true
env:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ esptool.py --chip esp32s3 -p /dev/ttyACM0 write_flash \

## 当前状态

**版本**: 0.4.0
**版本**: 0.4.4
**阶段**: Phase 38 完成 - WebUI 多语言支持

### 已完成功能
Expand Down
31 changes: 29 additions & 2 deletions components/ts_security/src/ts_security.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,22 @@ esp_err_t ts_security_store_cert(const char *name, ts_cert_type_t type,
return ret;
}

/**
* @brief 驱逐指定 client_id 的所有会话(释放槽位)
* @note 仅内部使用,在 create_session 槽位满时按同用户驱逐
*/
static void destroy_sessions_by_client(const char *client_id)
{
if (!client_id || client_id[0] == '\0') return;
for (int i = 0; i < MAX_SESSIONS; i++) {
if (s_sessions[i].active &&
strcmp(s_sessions[i].session.client_id, client_id) == 0) {
s_sessions[i].active = false;
TS_LOGI(TAG, "Evicted session for client: %s", client_id);
}
}
}

esp_err_t ts_security_create_session(const char *client_id, ts_perm_level_t level,
uint32_t *session_id)
{
Expand All @@ -181,8 +197,19 @@ esp_err_t ts_security_create_session(const char *client_id, ts_perm_level_t leve
}

if (slot < 0) {
TS_LOGW(TAG, "No free session slots");
return ESP_ERR_NO_MEM;
if (client_id && client_id[0] != '\0') {
destroy_sessions_by_client(client_id);
for (int i = 0; i < MAX_SESSIONS; i++) {
if (!s_sessions[i].active) {
slot = i;
break;
}
}
}
if (slot < 0) {
TS_LOGW(TAG, "No free session slots");
return ESP_ERR_NO_MEM;
}
}

// Generate session ID
Expand Down
5 changes: 5 additions & 0 deletions components/ts_webui/src/ts_webui_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@ static esp_err_t login_handler(ts_http_request_t *req, void *user_data)
cJSON_AddNumberToObject(data, "session_id", session_id);
cJSON_AddStringToObject(data, "username", username_copy);
cJSON_AddStringToObject(data, "level", level_str);
#ifdef CONFIG_TS_SECURITY_TOKEN_EXPIRE_SEC
cJSON_AddNumberToObject(data, "expires_in", CONFIG_TS_SECURITY_TOKEN_EXPIRE_SEC);
#else
cJSON_AddNumberToObject(data, "expires_in", 86400); /* 24 hours */
#endif
cJSON_AddBoolToObject(data, "password_changed", password_changed);
cJSON_AddItemToObject(response, "data", data);

Expand Down
2 changes: 2 additions & 0 deletions components/ts_webui/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,8 @@ <h2><span data-i18n="system.shutdownSettings">电压保护设置</span></h2>
var nameEl = document.getElementById('lang-name');
if (nameEl) nameEl.textContent = cur === 'zh-CN' ? '中文' : 'EN';
i18n.translateDOM();
/* translateDOM 会覆盖 #user-name 为「未登录」,需在之后恢复已登录状态 */
if (typeof updateAuthUI === 'function') setTimeout(updateAuthUI, 0);
};
document.head.appendChild(s);
})();
Expand Down
9 changes: 7 additions & 2 deletions components/ts_webui/web/js/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,10 @@ class TianShanAPI {
// 尝试从 localStorage 恢复
const savedToken = localStorage.getItem('ts_token');
const expires = localStorage.getItem('ts_expires');
if (savedToken && expires && Date.now() < parseInt(expires)) {
const parsed = expires ? parseInt(expires) : NaN;
const now = Date.now();
const valid = savedToken && expires && now < parsed;
if (valid) {
this.token = savedToken;
this.username = localStorage.getItem('ts_username');
this.level = localStorage.getItem('ts_level');
Expand All @@ -232,7 +235,9 @@ class TianShanAPI {
}
// 检查是否过期
const expires = localStorage.getItem('ts_expires');
if (expires && Date.now() >= parseInt(expires)) {
const parsed = expires ? parseInt(expires) : NaN;
const expired = expires && Date.now() >= parsed;
if (expired) {
this.logout(); // 清理过期的 token
return false;
}
Expand Down
3 changes: 1 addition & 2 deletions components/ts_webui/web/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ document.addEventListener('DOMContentLoaded', () => {
function updateAuthUI() {
const loginBtn = document.getElementById('login-btn');
const userName = document.getElementById('user-name');

if (api.isLoggedIn()) {
const username = api.getUsername();
const level = api.getLevel();
Expand Down Expand Up @@ -7673,7 +7672,7 @@ async function loadFilesPage() {

<!-- 重命名对话框 -->
<div id="rename-modal" class="modal hidden">
<div class="modal-content">
<div class="modal-content" style="max-width:420px">
<h2>${t('files.renameTitle')}</h2>
<div class="form-group">
<label>${t('files.newName')}</label>
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.4.3
0.4.4