WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s

This commit is contained in:
yawan
2026-09-22 16:19:06 +08:00
commit f7fc992f57
3929 changed files with 1006607 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
const assert = require('node:assert/strict')
const fs = require('node:fs')
const path = require('node:path')
const { execFileSync } = require('node:child_process')
const root = path.resolve(__dirname, '../..')
const credentialsPath = path.join(root, 'acceptance/super-admin-login.json')
const base = 'http://127.0.0.1:18080/api/v1'
const literal = value => `'${String(value).replaceAll("'", "''")}'`
const sql = statement => execFileSync('docker', ['--context', 'colima-yawan', 'exec', '-i', 'WeKnora-postgres', 'psql', '-U', 'postgres', '-d', 'yawan_native_acceptance_20260915', '-v', 'ON_ERROR_STOP=1', '-At'], { input: statement, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim()
async function request(route, data, token) {
const response = await fetch(base + route, {
method: 'POST', headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) },
body: JSON.stringify(data), signal: AbortSignal.timeout(30000),
})
const body = await response.json()
assert.ok(response.ok && body.success !== false, `${route}: HTTP ${response.status}`)
return body
}
async function main() {
const desired = JSON.parse(fs.readFileSync(0, 'utf8'))
assert.match(desired.username, /^[a-zA-Z0-9_-]{2,50}$/)
assert.ok(typeof desired.password === 'string' && [...desired.password].length >= 3)
const credentials = JSON.parse(fs.readFileSync(credentialsPath, 'utf8'))
const oldLogin = await request('/auth/login', { email: credentials.email, password: credentials.password })
assert.equal(oldLogin.user.is_system_admin, true, 'Saved identity must already be a system administrator')
assert.equal(oldLogin.user.is_active, true)
const userID = oldLogin.user.id
assert.match(userID, /^[a-f0-9-]{36}$/)
assert.equal(sql(`SELECT count(*) FROM users WHERE id<>${literal(userID)} AND LOWER(username)=LOWER(${literal(desired.username)});`), '0', 'Requested account name already exists')
const backup = path.join(root, 'acceptance', `super-admin-login-before-${Date.now()}.json`)
fs.writeFileSync(backup, JSON.stringify(credentials, null, 2) + '\n', { flag: 'wx', mode: 0o600 })
// Keep the user ID and all ownership links; only change the login name.
sql(`BEGIN;
SELECT id FROM users WHERE id=${literal(userID)} AND is_system_admin=true AND is_active=true AND deleted_at IS NULL FOR UPDATE;
UPDATE users SET username=${literal(desired.username)},updated_at=now() WHERE id=${literal(userID)} AND is_system_admin=true AND is_active=true AND deleted_at IS NULL;
INSERT INTO audit_logs (tenant_id,actor_user_id,actor_role,action,target_type,target_id,target_user_id,outcome,details,created_at)
SELECT tenant_id,id,'admin','system.user_updated','user',id,id,'success',${literal(JSON.stringify({ username_before: oldLogin.user.username, username_after: desired.username, source: 'authorized_account_update' }))}::jsonb,now() FROM users WHERE id=${literal(userID)};
COMMIT;`)
const renamed = await request('/auth/login', { username: desired.username, password: credentials.password })
assert.equal(renamed.user.id, userID)
if (credentials.password !== desired.password) {
await request('/auth/change-password', { old_password: credentials.password, new_password: desired.password }, renamed.token)
}
const updated = { ...credentials, username: desired.username, password: desired.password }
const temporary = credentialsPath + '.next'
fs.writeFileSync(temporary, JSON.stringify(updated, null, 2) + '\n', { mode: 0o600 })
fs.renameSync(temporary, credentialsPath)
const current = await request('/auth/login', { username: desired.username, password: desired.password })
assert.equal(current.user.id, userID)
assert.equal(current.user.is_system_admin, true)
if (credentials.password !== desired.password) {
const revoked = await fetch(base + '/auth/me', { headers: { Authorization: `Bearer ${oldLogin.token}` }, signal: AbortSignal.timeout(15000) })
assert.equal(revoked.status, 401, 'Previous login must be revoked after password rotation')
}
console.log(JSON.stringify({ passed: true, username: current.user.username, identity_preserved: true, system_admin: true, password_rotated: credentials.password !== desired.password }))
}
main().catch(error => { console.error(error.message); process.exitCode = 1 })