Files
jiapu/docs/superpowers/plans/2026-07-11-auth-to-profile-flow.md
T
2026-07-11 09:36:02 +08:00

17 KiB
Raw Blame History

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:8080 for development and production, and config.js is 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_token and redirects to login.html; it never persists a registration response token.
  • Only successful password or SMS login with a nonempty response token persists genealogy_auth_token and redirects to profile.html.
  • profile.html and profile-data.html redirect to index.html before a profile request when no token exists, and after HTTP or business 401 responses.
  • Confirmed logout requests /genealogy/pc/auth/logout, clears the token regardless of the request outcome, and redirects to index.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() and getApiBaseUrl() from config.js.

  • Produces: a test that asserts the current config.js address 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 public client.login, client.loginBySms, and client.logout methods.

  • Produces: client.login(body) and client.loginBySms(body) reject with ApiError('登录响应缺少 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(), and api.logout() methods.

  • Produces: ProfilePages.shouldRedirectToHome(api, error) for token and unauthorized classification; profile reads and writes redirect to index.html when that helper returns true; the existing logout controls call api.logout() before navigating to index.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"