Revert "merge: integrate extension auto-update"
This reverts commit322475860a, reversing changes made tof52d9d7413.
This commit is contained in:
1 parent
b2e33e2e5d
commit
81a0cdac8e
47 files changed
+169
-3108
No files matched your search
+12
-110
@@ -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));
|
||||
|
||||
Reference in new issue
Block a user