Revert "merge: integrate extension auto-update"

This reverts commit 322475860a, reversing
changes made to f52d9d7413.
This commit is contained in:
inman committed 2026-09-03 16:45:14 +08:00
1 parent b2e33e2e5d
commit 81a0cdac8e
47 files changed
+169 -3108

No files matched your search

+12 -110
View File
@@ -28,8 +28,6 @@ export interface PublicAccount {
username: string;
role: AuthRole;
erp_account: string | null;
extension_ecs_region_id: string | null;
extension_ecs_instance_id: string | null;
is_active: boolean;
authorized_business_route_ids: BusinessRouteId[];
business_authorization_revision: number;
@@ -103,42 +101,6 @@ function validateAccountRouting(role: AuthRole, value: unknown): string | null {
return erpAccount;
}
function normalizeEcsRegionId(value: unknown): string | null {
const normalized = String(value ?? '').trim().toLocaleLowerCase('en-US');
if (!normalized) return null;
if (!/^[a-z0-9][a-z0-9-]{0,63}$/u.test(normalized)) {
throw new AuthError('extension_ecs_region_invalid', 'ECS 地域 ID 格式无效。', 400);
}
return normalized;
}
function normalizeEcsInstanceId(value: unknown): string | null {
const normalized = String(value ?? '').trim();
if (!normalized) return null;
if (!/^i-[A-Za-z0-9]{6,64}$/u.test(normalized)) {
throw new AuthError('extension_ecs_instance_invalid', 'ECS 实例 ID 格式无效。', 400);
}
return normalized;
}
function validateExtensionHostBinding(
role: AuthRole,
regionValue: unknown,
instanceValue: unknown
): { regionId: string | null; instanceId: string | null } {
const regionId = normalizeEcsRegionId(regionValue);
const instanceId = normalizeEcsInstanceId(instanceValue);
if (role === 'admin' && (regionId || instanceId)) {
throw new AuthError('admin_extension_host_forbidden', '管理员账号不能绑定 ERP 插件云主机。', 409);
}
if (Boolean(regionId) !== Boolean(instanceId)) {
throw new AuthError('extension_ecs_binding_incomplete', 'ECS 地域 ID 与实例 ID 必须同时填写或同时清空。', 400);
}
return role === 'admin'
? { regionId: null, instanceId: null }
: { regionId, instanceId };
}
function normalizeRole(value: unknown): AuthRole {
if (value === 'admin' || value === 'team_lead') return value;
return 'user';
@@ -185,8 +147,6 @@ function mapAccount(row: Record<string, unknown>): PublicAccount {
username: String(row.username),
role,
erp_account: normalizeErpAccount(row.erp_account),
extension_ecs_region_id: normalizeEcsRegionId(row.extension_ecs_region_id),
extension_ecs_instance_id: normalizeEcsInstanceId(row.extension_ecs_instance_id),
is_active: row.is_active === true || String(row.is_active) === 'true',
authorized_business_route_ids: role === 'admin' ? [...ALL_BUSINESS_ROUTE_IDS] : storedRouteIds,
business_authorization_revision: Math.max(0, Number(row.business_authorization_revision || 0)),
@@ -202,8 +162,7 @@ async function loadPublicAccount(
userId: string
): Promise<PublicAccount | null> {
const result = await client.query(
`SELECT u.id, u.username, u.role, u.erp_account,
u.extension_ecs_region_id, u.extension_ecs_instance_id, u.is_active,
`SELECT u.id, u.username, u.role, u.erp_account, u.is_active,
u.business_authorization_revision,
u.last_login_at, u.created_at, u.updated_at,
COALESCE(ARRAY(
@@ -409,8 +368,7 @@ export class AuthService {
async listAccounts(actor: AuthUser): Promise<PublicAccount[]> {
this.requireAdmin(actor);
const result = await getPool(this.config).query(
`SELECT u.id, u.username, u.role, u.erp_account,
u.extension_ecs_region_id, u.extension_ecs_instance_id, u.is_active,
`SELECT u.id, u.username, u.role, u.erp_account, u.is_active,
u.business_authorization_revision,
u.last_login_at, u.created_at, u.updated_at,
COALESCE(ARRAY(
@@ -434,8 +392,6 @@ export class AuthService {
password: string;
role: AuthRole;
erpAccount?: string;
extensionEcsRegionId?: string;
extensionEcsInstanceId?: string;
businessRouteIds?: readonly string[];
},
requestId: string
@@ -445,11 +401,6 @@ export class AuthService {
const passwordHash = await argon2.hash(validatePassword(input.password), { type: argon2.argon2id });
const role = normalizeRole(input.role);
const erpAccount = validateAccountRouting(role, input.erpAccount);
const extensionHost = validateExtensionHostBinding(
role,
input.extensionEcsRegionId,
input.extensionEcsInstanceId
);
const businessRouteIds = role === 'admin' ? [] : normalizeBusinessRouteIds(input.businessRouteIds);
try {
return await withTransaction(this.config, async (client) => {
@@ -464,19 +415,10 @@ export class AuthService {
if (existing.rowCount) throw new AuthError('account_exists', '该账号已存在。', 409);
const created = await client.query(
`INSERT INTO users
(organization_id, username, password_hash, role, erp_account,
extension_ecs_region_id, extension_ecs_instance_id, password_changed_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, now())
(organization_id, username, password_hash, role, erp_account, password_changed_at)
VALUES ($1, $2, $3, $4, $5, now())
RETURNING id`,
[
actor.organizationId,
username,
passwordHash,
role,
erpAccount,
extensionHost.regionId,
extensionHost.instanceId
]
[actor.organizationId, username, passwordHash, role, erpAccount]
);
const accountId = String(created.rows[0].id);
if (businessRouteIds.length) {
@@ -493,7 +435,6 @@ export class AuthService {
await this.accountAudit(client, actor, 'account.created', account.id, requestId, {
role,
erp_account_configured: Boolean(erpAccount),
extension_host_configured: Boolean(extensionHost.instanceId),
authorized_business_route_ids: account.authorized_business_route_ids
});
return account;
@@ -512,30 +453,17 @@ export class AuthService {
async updateAccount(
actor: AuthUser,
targetUserId: string,
input: {
role?: AuthRole;
isActive?: boolean;
erpAccount?: string | null;
extensionEcsRegionId?: string | null;
extensionEcsInstanceId?: string | null;
},
input: { role?: AuthRole; isActive?: boolean; erpAccount?: string | null },
requestId: string
): Promise<PublicAccount> {
this.requireAdmin(actor);
if (
input.role === undefined
&& input.isActive === undefined
&& input.erpAccount === undefined
&& input.extensionEcsRegionId === undefined
&& input.extensionEcsInstanceId === undefined
) {
if (input.role === undefined && input.isActive === undefined && input.erpAccount === undefined) {
throw new AuthError('account_update_empty', '没有需要更新的账号字段。', 400);
}
try {
return await withTransaction(this.config, async (client) => {
const target = await client.query(
`SELECT id, username, role, erp_account,
extension_ecs_region_id, extension_ecs_instance_id, is_active,
`SELECT id, username, role, erp_account, is_active,
last_login_at, created_at, updated_at
FROM users
WHERE organization_id = $1 AND id = $2
@@ -552,19 +480,6 @@ export class AuthService {
? null
: input.erpAccount === undefined ? before.erp_account : input.erpAccount
);
const extensionHost = validateExtensionHostBinding(
role,
role === 'admin'
? null
: input.extensionEcsRegionId === undefined
? before.extension_ecs_region_id
: input.extensionEcsRegionId,
role === 'admin'
? null
: input.extensionEcsInstanceId === undefined
? before.extension_ecs_instance_id
: input.extensionEcsInstanceId
);
const removesActiveAdmin = before.role === 'admin' && before.is_active && (role !== 'admin' || !isActive);
if (actor.id === before.id && (role !== 'admin' || !isActive)) {
throw new AuthError('self_lockout_forbidden', '不能停用或降级当前登录的管理员账号。', 409);
@@ -582,24 +497,12 @@ export class AuthService {
}
const updated = await client.query(
`UPDATE users
SET role = $1, is_active = $2, erp_account = $3,
extension_ecs_region_id = $4, extension_ecs_instance_id = $5,
updated_at = now()
WHERE organization_id = $6 AND id = $7
SET role = $1, is_active = $2, erp_account = $3, updated_at = now()
WHERE organization_id = $4 AND id = $5
RETURNING id`,
[
role,
isActive,
erpAccount,
extensionHost.regionId,
extensionHost.instanceId,
actor.organizationId,
before.id
]
[role, isActive, erpAccount, actor.organizationId, before.id]
);
const routingIdentityChanged = before.erp_account !== erpAccount;
const extensionHostChanged = before.extension_ecs_region_id !== extensionHost.regionId
|| before.extension_ecs_instance_id !== extensionHost.instanceId;
if (before.role !== role || before.is_active !== isActive || routingIdentityChanged) {
await client.query(
'UPDATE sessions SET revoked_at = now() WHERE user_id = $1 AND revoked_at IS NULL',
@@ -615,7 +518,7 @@ export class AuthService {
[role === 'admin' ? '绑定账号已变更为管理员,渠道已解除绑定。' : '绑定账号已停用,渠道已解除绑定。', actor.organizationId, before.id]
);
}
if (!isActive || routingIdentityChanged || extensionHostChanged || before.role !== role) {
if (!isActive || routingIdentityChanged || before.role !== role) {
await client.query(
`UPDATE browser_connections
SET status = 'superseded', erp_account_verified = false
@@ -629,7 +532,6 @@ export class AuthService {
previous_active: before.is_active,
active: isActive,
erp_account_changed: routingIdentityChanged,
extension_host_changed: extensionHostChanged,
sessions_revoked: before.role !== role || before.is_active !== isActive || routingIdentityChanged
});
const account = await loadPublicAccount(client, actor.organizationId, String(updated.rows[0].id));