17 KiB
Auth To Profile Flow Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Complete the PC registration, login, profile, unauthorized, logout, and endpoint-address flow with one token owner and deterministic redirects.
Architecture: config.js remains the sole runtime owner of the API base address. utils/ApiClient.js owns token validation, persistence, and cleanup; profile-pages.js decides whether a profile request is allowed and redirects unauthenticated users to the homepage; profile-common.js delegates confirmed logout to the API client. The integration tracker records the current address and verified behavior only after verification commands complete.
Tech Stack: Browser JavaScript (UMD and jQuery), Axios, Node.js assert tests, static HTML, Markdown.
Global Constraints
- The current API base address is
http://182.61.18.23:8080for development and production, andconfig.jsis its only runtime owner. - Runtime-related tests use the same address; only documents explicitly marked historical may retain the former test domain.
- Successful registration clears
genealogy_auth_tokenand redirects tologin.html; it never persists a registration response token. - Only successful password or SMS login with a nonempty response token persists
genealogy_auth_tokenand redirects toprofile.html. profile.htmlandprofile-data.htmlredirect toindex.htmlbefore a profile request when no token exists, and after HTTP or business401responses.- Confirmed logout requests
/genealogy/pc/auth/logout, clears the token regardless of the request outcome, and redirects toindex.html. - Do not add global Axios redirects, duplicate token storage in pages, alter API paths, or change password-reset, phone-change, account-deactivation, or profile pages that do not load the profile API.
Task 1: Synchronize the configured API address test
Files:
- Modify:
tests/config.test.js:11-28 - Modify:
tests/utils.test.js:31
Interfaces:
-
Consumes:
configFactory(...).getConfig()andgetApiBaseUrl()fromconfig.js. -
Produces: a test that asserts the current
config.jsaddress for both environments. -
Step 1: Confirm the existing address-contract test fails
The current test contains these stale assertions:
assert.deepStrictEqual(developmentConfig.getConfig(), {
environment: 'development',
apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn'
});
assert.strictEqual(productionConfig.getApiBaseUrl(), 'http://test-genealogy-api.ddxcjp.cn');
Run: node tests/config.test.js
Expected: failure because config.js already returns http://182.61.18.23:8080.
- Step 2: Update the stale test expectations
Replace the two expected URL values with:
assert.deepStrictEqual(developmentConfig.getConfig(), {
environment: 'development',
apiBaseUrl: 'http://182.61.18.23:8080'
});
assert.strictEqual(productionConfig.getApiBaseUrl(), 'http://182.61.18.23:8080');
Replace the mocked requester address in tests/utils.test.js with:
baseUrl: 'http://182.61.18.23:8080',
- Step 3: Run the focused configuration test
Run: node tests/config.test.js
Expected: config tests passed.
Run: node tests/utils.test.js
Expected: utils tests passed.
- Step 4: Commit the address-test synchronization
git add -- tests/config.test.js tests/utils.test.js
git commit -m "test: sync configured api address"
Task 2: Enforce login-token and logout-cleanup contracts
Files:
- Modify:
utils/ApiClient.js:52-60, 167-191, 270-274 - Modify:
tests/api-client.test.js:83-177
Interfaces:
-
Consumes:
getTokenFromResponse(data),setToken(token),clearToken(), and publicclient.login,client.loginBySms, andclient.logoutmethods. -
Produces:
client.login(body)andclient.loginBySms(body)reject withApiError('登录响应缺少 token')when their response has no token;client.logout()clears the configured token key before resolving or rejecting. -
Step 1: Write failing API-client assertions
Append this after the registration assertions and before client.sendSmsCode(...) in tests/api-client.test.js:
const missingTokenStore = createStore();
const missingTokenClient = GenealogyApi.createClient({
baseUrl: 'http://182.61.18.23:8080',
clientId: 'client-x',
tenantId: 'tenant-x',
tokenKey: 'token-key',
tokenStore: missingTokenStore,
axiosInstance: {
request: async () => ({
status: 200,
data: { code: 200, data: {} }
})
}
});
await assert.rejects(
missingTokenClient.login({ phone: '13800000000', password: 'md5' }),
(error) => error.message === '登录响应缺少 token'
);
assert.strictEqual(missingTokenStore.getItem('token-key'), null);
store.setItem('token-key', 'logout-token');
await client.logout();
assert.strictEqual(calls[9].url, '/genealogy/pc/auth/logout');
assert.strictEqual(store.getItem('token-key'), null);
const failedLogoutStore = createStore({
'token-key': 'logout-token'
});
const failedLogoutClient = GenealogyApi.createClient({
baseUrl: 'http://182.61.18.23:8080',
clientId: 'client-x',
tenantId: 'tenant-x',
tokenKey: 'token-key',
tokenStore: failedLogoutStore,
axiosInstance: {
request: async () => {
throw new Error('logout failed');
}
}
});
await assert.rejects(failedLogoutClient.logout(), /logout failed/);
assert.strictEqual(failedLogoutStore.getItem('token-key'), null);
Move the existing SMS-code assertions from calls[9] to calls[10] and change the final call-count assertion from 10 to 11.
Replace the two existing baseUrl: 'https://test-genealogy-api.ddxcjp.cn' fixtures in this file with:
baseUrl: 'http://182.61.18.23:8080',
- Step 2: Run the focused API-client test and confirm failure
Run: node tests/api-client.test.js
Expected: failure at the missing-token rejection because the current login method resolves without writing a token, before reaching the logout assertions.
- Step 3: Add the minimal token validation and logout cleanup
Add this helper after getTokenFromResponse in utils/ApiClient.js:
function requireLoginToken(data) {
var token = getTokenFromResponse(data);
if (!token) throw new ApiError('登录响应缺少 token');
return token;
}
Replace the token writes in login() and loginBySms() with:
setToken(requireLoginToken(data));
Replace logout with:
logout: async function () {
try {
return await request('DELETE', '/genealogy/pc/auth/logout');
} finally {
clearToken();
}
},
- Step 4: Run the API-client test and confirm all token paths pass
Run: node tests/api-client.test.js
Expected: api-client tests passed.
- Step 5: Commit the API-client contract
git add -- utils/ApiClient.js tests/api-client.test.js
git commit -m "fix: complete auth token lifecycle"
Task 3: Guard profile access and connect confirmed logout
Files:
- Modify:
public/js/profile-pages.js:18-22, 165-198 - Modify:
public/js/profile-common.js:9-82 - Modify:
profile.html:80, 355 - Modify:
tests/profile-pages.test.js:1-65 - Create:
tests/profile-logout-structure.test.js
Interfaces:
-
Consumes: public
api.getToken(),api.clearToken(),api.currentProfile(),api.updateProfile(), andapi.logout()methods. -
Produces:
ProfilePages.shouldRedirectToHome(api, error)for token and unauthorized classification; profile reads and writes redirect toindex.htmlwhen that helper returns true; the existing logout controls callapi.logout()before navigating toindex.html. -
Step 1: Write failing profile and logout tests
Add these assertions before the final console.log in tests/profile-pages.test.js:
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => ''
}), true);
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => 'token-x'
}), false);
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => 'token-x'
}, { status: 401 }), true);
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => 'token-x'
}, { code: 401 }), true);
assert.strictEqual(ProfilePages.shouldRedirectToHome({
getToken: () => 'token-x'
}, { status: 500 }), false);
Create tests/profile-logout-structure.test.js with:
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const profileHtml = fs.readFileSync(path.join(__dirname, '..', 'profile.html'), 'utf8');
const profileCommon = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'profile-common.js'), 'utf8');
assert.match(profileHtml, /data-logout-confirm/);
assert.match(profileHtml, /<a class="btn primary magnetic" href="index\.html" data-logout-confirm>/);
assert.match(profileCommon, /function performLogout\(targetUrl\)/);
assert.match(profileCommon, /api\.logout\(\)/);
assert.match(profileCommon, /targetUrl \|\| 'index\.html'/);
console.log('profile logout structure tests passed');
- Step 2: Run the new focused tests and confirm failure
Run: node tests/profile-pages.test.js
Expected: failure because shouldRedirectToHome is not exported.
Run: node tests/profile-logout-structure.test.js
Expected: failure because the logout confirmation markup and performLogout function do not exist.
- Step 3: Add profile token guard and unauthorized redirect
Add these helpers after getApi() in public/js/profile-pages.js:
function shouldRedirectToHome(api, error) {
var status = error && (error.status || error.code);
return !api || !api.getToken || !api.getToken() || Number(status) === 401;
}
function redirectToHome() {
if (!root.location) return;
if (typeof root.location.replace === 'function') {
root.location.replace('index.html');
return;
}
root.location.href = 'index.html';
}
function redirectUnauthorized(api, error) {
if (!shouldRedirectToHome(api, error)) return false;
if (api && api.clearToken) api.clearToken();
redirectToHome();
return true;
}
Replace the existing combined early-return condition in loadProfile() with these two branches:
if (!api || !documentRef || !documentRef.querySelector('[data-profile-page]')) return;
if (redirectUnauthorized(api)) return;
Update its catch block to start with:
if (redirectUnauthorized(api, error)) return;
Update submitProfileForm() to place if (redirectUnauthorized(api)) return; after its existing null guard, and to start its catch block with if (redirectUnauthorized(api, error)) return; before setting an error status or message.
Export the classifier with the existing public methods:
shouldRedirectToHome: shouldRedirectToHome,
- Step 4: Connect both logout confirmations to the API client
Add this function before bindLogout() in public/js/profile-common.js:
function performLogout(targetUrl) {
var api = window.GenealogyApi && window.GenealogyApi.defaultClient;
var redirect = function () {
window.location.href = targetUrl || 'index.html';
};
if (!api || !api.logout) {
redirect();
return;
}
api.logout().catch(function () {}).then(redirect);
}
In bindLogout(), change the default target URL to index.html, replace the Layui confirmation callback body with performLogout(targetUrl);, and add this event handler before the existing [data-logout-close] handler:
$(document).on('click', '[data-logout-confirm]', function (event) {
event.preventDefault();
closeLegacyLogout();
performLogout($(this).attr('href') || 'index.html');
});
In profile.html, change the legacy confirmation anchor to:
<a class="btn primary magnetic" href="index.html" data-logout-confirm>确认退出</a>
- Step 5: Run the focused profile tests and syntax checks
Run: node tests/profile-pages.test.js
Expected: profile-pages tests passed.
Run: node tests/profile-logout-structure.test.js
Expected: profile logout structure tests passed.
Run: node --check public/js/profile-pages.js
Expected: exit code 0.
Run: node --check public/js/profile-common.js
Expected: exit code 0.
- Step 6: Commit the profile and logout flow
git add -- public/js/profile-pages.js public/js/profile-common.js profile.html tests/profile-pages.test.js tests/profile-logout-structure.test.js
git commit -m "fix: guard profile auth and logout"
Task 4: Record the current flow and run end-to-end regression checks
Files:
- Modify:
docs/pc-api-page-integration-tracker-2026-07-09.md:1-9, 32, 70, 163 - Modify:
docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md:3-5
Interfaces:
-
Consumes: the verified results of Tasks 1 through 3.
-
Produces: a tracker whose current address and authentication-flow status match the code and tests, and a spec marked approved for implementation.
-
Step 1: Run all focused flow tests
Run:
node tests/config.test.js
node tests/api-client.test.js
node tests/auth-pages.test.js
node tests/profile-pages.test.js
node tests/profile-logout-structure.test.js
Expected: each command prints its success message and exits with code 0.
- Step 2: Run syntax and complete regression checks
Run:
node --check utils/ApiClient.js
node --check public/js/profile-pages.js
node --check public/js/profile-common.js
Get-ChildItem -Path tests -Filter *.test.js | ForEach-Object { & node $_.FullName; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } }
Expected: all syntax checks exit 0; every test script prints its success message; PowerShell exits 0 on the first test failure instead of masking it.
- Step 3: Update the tracker from verified evidence
At the top of docs/pc-api-page-integration-tracker-2026-07-09.md, replace the active base-address line with:
接口基础地址:`http://182.61.18.23:8080`
Add this section immediately after the introductory historical-record lines:
## Current Endpoint Contract
- Runtime owner: `config.js`.
- Current development and production address: `http://182.61.18.23:8080`.
- Address change record: on 2026-07-11 the active address changed from the historical test domain to this IP address and port. Historical references to `test-genealogy-api.ddxcjp.cn` do not describe the current runtime configuration.
Replace the YAML-analysis item that says the project currently uses the test domain with:
2. `servers` 仍是本地地址示例;当前运行时基础地址以 `config.js` 为准,为 `http://182.61.18.23:8080`。
Replace the PC-010 row with:
| PC-010 | 已复核(当前地址已同步) | `config.js`、`tests/config.test.js`、`utils/ApiClient.js` | API 基础地址 | 当前开发和生产地址均为 `http://182.61.18.23:8080`;地址唯一运行时来源为 `config.js`,`tests/config.test.js` 已同步断言。 |
After Steps 1 and 2 pass, run Get-Date -Format "yyyy-MM-dd HH:mm:ss K" and insert its output as the 完成时间 line immediately after the 状态 line in this record. Append the rest of this record after the existing PC entries:
## PC-025 注册、登录到个人资料闭环
- 状态:已完成
- 规划文件:`docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md`
- 页面范围:`register.html`、`login.html`、`profile.html`、`profile-data.html`
- 接口范围:`/genealogy/pc/auth/register`、`/genealogy/pc/auth/login`、`/genealogy/pc/auth/login/sms`、`/genealogy/pc/auth/profile`、`/genealogy/pc/auth/logout`
- 地址契约:运行时唯一来源为 `config.js`;当前开发和生产地址均为 `http://182.61.18.23:8080`。
- 流程结果:注册成功清除 token 并跳转 `login.html`;密码和短信登录必须取得 token 后跳转 `profile.html`;个人资料无 token 或收到 HTTP/业务 `401` 时清 token 并跳转 `index.html`;确认退出无论接口结果都清 token 并跳转 `index.html`。
- 修改文件:`tests/config.test.js`、`utils/ApiClient.js`、`tests/api-client.test.js`、`public/js/profile-pages.js`、`public/js/profile-common.js`、`profile.html`、`tests/profile-pages.test.js`、`tests/profile-logout-structure.test.js`。
- 验证结果:`node tests/config.test.js`、`node tests/api-client.test.js`、`node tests/auth-pages.test.js`、`node tests/profile-pages.test.js`、`node tests/profile-logout-structure.test.js`、三个 `node --check` 命令和全量 Node 测试均通过。
Change the spec status body to:
Approved for implementation.
- Step 4: Check final whitespace and commit the record
Run: git diff --check
Expected: exit code 0; pre-existing CRLF conversion warnings are not whitespace errors.
git add -- docs/pc-api-page-integration-tracker-2026-07-09.md docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md
git commit -m "docs: record auth profile flow"