拆分打手会话服务
This commit is contained in:
@@ -2,6 +2,9 @@ export * from './mappers.js'
|
|||||||
export * from './worker-session-context-service.js'
|
export * from './worker-session-context-service.js'
|
||||||
export * from './worker-service.js'
|
export * from './worker-service.js'
|
||||||
export * from './worker-auth-policy.js'
|
export * from './worker-auth-policy.js'
|
||||||
|
export * from './worker-session-view.js'
|
||||||
|
export * from './worker-session-auth-service.js'
|
||||||
|
export * from './worker-credentials-service.js'
|
||||||
export * from './worker-platform-defaults.js'
|
export * from './worker-platform-defaults.js'
|
||||||
export * from './work-order-timeout-policy.js'
|
export * from './work-order-timeout-policy.js'
|
||||||
export * from './after-sales-service.js'
|
export * from './after-sales-service.js'
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
revokeAllWorkerSessions,
|
||||||
|
updateWorkerPassword,
|
||||||
|
} from '../../repositories/worker-platform/index.js'
|
||||||
|
import type { JsonObject } from '../../types/json.js'
|
||||||
|
import { createHttpError } from '../../utils/http.js'
|
||||||
|
import { nowIso } from '../../utils/time.js'
|
||||||
|
import {
|
||||||
|
hashWorkerPassword,
|
||||||
|
mapWorkerUser,
|
||||||
|
normalizePassword,
|
||||||
|
normalizeSessionVersion,
|
||||||
|
validateWorkerPassword,
|
||||||
|
verifyWorkerPassword,
|
||||||
|
} from './mappers.js'
|
||||||
|
import { getRequiredWorker, type WorkerSession } from './worker-session-context-service.js'
|
||||||
|
|
||||||
|
export async function changeWorkerPassword(payload: JsonObject = {}, session: WorkerSession) {
|
||||||
|
const worker = await getRequiredWorker(session.workerId)
|
||||||
|
const currentPassword = normalizePassword(payload.currentPassword || payload.oldPassword)
|
||||||
|
const nextPassword = normalizePassword(payload.newPassword || payload.password)
|
||||||
|
|
||||||
|
if (!currentPassword) {
|
||||||
|
throw createHttpError('请输入当前密码', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_password_current_required',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verifyWorkerPassword(currentPassword, worker.password_hash)) {
|
||||||
|
throw createHttpError('当前密码不正确', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_password_current_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nextPassword) {
|
||||||
|
throw createHttpError('请输入新密码', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_password_new_required',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
validateWorkerPassword(nextPassword)
|
||||||
|
if (currentPassword === nextPassword) {
|
||||||
|
throw createHttpError('新密码不能与当前密码一致', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_password_unchanged',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = nowIso()
|
||||||
|
const updated = await updateWorkerPassword({
|
||||||
|
workerId: worker.id,
|
||||||
|
passwordHash: hashWorkerPassword(nextPassword),
|
||||||
|
sessionVersion: normalizeSessionVersion(worker.session_version) + 1,
|
||||||
|
now,
|
||||||
|
})
|
||||||
|
await revokeAllWorkerSessions(worker.id, now)
|
||||||
|
|
||||||
|
return {
|
||||||
|
worker: mapWorkerUser(updated || worker),
|
||||||
|
reloginRequired: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,7 +111,6 @@ import {
|
|||||||
createWorkerSession,
|
createWorkerSession,
|
||||||
ensureWorkerAuthConfigured,
|
ensureWorkerAuthConfigured,
|
||||||
hashWorkerPassword,
|
hashWorkerPassword,
|
||||||
isIgnorableWorkerAuthError,
|
|
||||||
mapCollectLookupOrder,
|
mapCollectLookupOrder,
|
||||||
mapFinanceRequest,
|
mapFinanceRequest,
|
||||||
mapWalletLedger,
|
mapWalletLedger,
|
||||||
@@ -145,8 +144,6 @@ import {
|
|||||||
resolveRequirementFields,
|
resolveRequirementFields,
|
||||||
resolveWorkerPermissions,
|
resolveWorkerPermissions,
|
||||||
canWorkerAutoAcceptWholeOrder,
|
canWorkerAutoAcceptWholeOrder,
|
||||||
safeCompare,
|
|
||||||
signWorkerPayload,
|
|
||||||
throwGrabWorkOrderFailure,
|
throwGrabWorkOrderFailure,
|
||||||
validateWorkerPassword,
|
validateWorkerPassword,
|
||||||
isWorkerPasswordStrong,
|
isWorkerPasswordStrong,
|
||||||
@@ -159,6 +156,14 @@ import {
|
|||||||
normalizeWorkerDeviceType,
|
normalizeWorkerDeviceType,
|
||||||
} from './worker-auth-policy.js'
|
} from './worker-auth-policy.js'
|
||||||
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
|
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
|
||||||
|
import { changeWorkerPassword } from './worker-credentials-service.js'
|
||||||
|
import {
|
||||||
|
deleteWorkerSession,
|
||||||
|
getWorkerSessionSummary,
|
||||||
|
getWorkerSessions,
|
||||||
|
logoutWorkerSession,
|
||||||
|
verifyWorkerSessionToken,
|
||||||
|
} from './worker-session-auth-service.js'
|
||||||
import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
|
import { normalizeWorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
|
||||||
|
|
||||||
const WORKER_DAILY_WITHDRAW_LIMIT = 3
|
const WORKER_DAILY_WITHDRAW_LIMIT = 3
|
||||||
@@ -179,6 +184,14 @@ export {
|
|||||||
WORK_ORDER_TIMEOUT_POLICIES,
|
WORK_ORDER_TIMEOUT_POLICIES,
|
||||||
} from './work-order-timeout-policy.js'
|
} from './work-order-timeout-policy.js'
|
||||||
export type { WorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
|
export type { WorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
|
||||||
|
export { changeWorkerPassword } from './worker-credentials-service.js'
|
||||||
|
export {
|
||||||
|
deleteWorkerSession,
|
||||||
|
getWorkerSessionSummary,
|
||||||
|
getWorkerSessions,
|
||||||
|
logoutWorkerSession,
|
||||||
|
verifyWorkerSessionToken,
|
||||||
|
} from './worker-session-auth-service.js'
|
||||||
|
|
||||||
const WORKER_MAX_DEVICES = 3
|
const WORKER_MAX_DEVICES = 3
|
||||||
|
|
||||||
@@ -726,164 +739,6 @@ async function authenticateWorkerCredentials(
|
|||||||
return worker
|
return worker
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logoutWorkerSession(token: unknown) {
|
|
||||||
const normalizedToken = String(token || '').trim()
|
|
||||||
if (!normalizedToken) {
|
|
||||||
return { success: true, revoked: false }
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const session = await verifyWorkerSessionToken(normalizedToken)
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
revoked: await revokeWorkerSession(session.workerId, session.sessionId, nowIso()),
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (isIgnorableWorkerAuthError(error)) {
|
|
||||||
return { success: true, revoked: false }
|
|
||||||
}
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function verifyWorkerSessionToken(token: unknown): Promise<WorkerSession> {
|
|
||||||
ensureWorkerAuthConfigured()
|
|
||||||
const normalizedToken = String(token || '').trim()
|
|
||||||
if (!normalizedToken) {
|
|
||||||
throw createHttpError('未登录或登录已失效', {
|
|
||||||
statusCode: 401,
|
|
||||||
errorCode: 'worker_auth_required',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts = normalizedToken.split('.')
|
|
||||||
const encodedPayload = parts[0]
|
|
||||||
const signature = parts[1]
|
|
||||||
if (
|
|
||||||
parts.length !== 2 ||
|
|
||||||
!encodedPayload ||
|
|
||||||
!signature ||
|
|
||||||
!safeCompare(signature, signWorkerPayload(encodedPayload))
|
|
||||||
) {
|
|
||||||
throw createHttpError('接单登录态无效', {
|
|
||||||
statusCode: 401,
|
|
||||||
errorCode: 'worker_auth_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let payload: JsonObject
|
|
||||||
try {
|
|
||||||
payload = JSON.parse(Buffer.from(encodedPayload, 'base64url').toString('utf8'))
|
|
||||||
} catch {
|
|
||||||
throw createHttpError('接单登录态无效', {
|
|
||||||
statusCode: 401,
|
|
||||||
errorCode: 'worker_auth_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (String(payload.typ || '') !== 'worker') {
|
|
||||||
throw createHttpError('接单登录态无效', {
|
|
||||||
statusCode: 401,
|
|
||||||
errorCode: 'worker_auth_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const expiresAt = String(payload.exp || '').trim()
|
|
||||||
if (!expiresAt || Date.parse(expiresAt) <= Date.now()) {
|
|
||||||
throw createHttpError('登录已过期,请重新登录', {
|
|
||||||
statusCode: 401,
|
|
||||||
errorCode: 'worker_auth_expired',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const workerId = Number(payload.uid || 0)
|
|
||||||
const worker = await getWorkerUserById(workerId)
|
|
||||||
if (!worker || worker.status !== 'active') {
|
|
||||||
throw createHttpError('接单账号已不可用,请重新登录', {
|
|
||||||
statusCode: 401,
|
|
||||||
errorCode: 'worker_auth_user_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentSessionVersion = normalizeSessionVersion(worker.session_version)
|
|
||||||
if (Number(payload.ver || 0) !== currentSessionVersion) {
|
|
||||||
throw createHttpError('登录态已失效,请重新登录', {
|
|
||||||
statusCode: 401,
|
|
||||||
errorCode: 'worker_auth_stale',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const sessionId = String(payload.sid || '').trim()
|
|
||||||
const sessionRecord = sessionId ? await getWorkerSessionBySessionId(sessionId) : null
|
|
||||||
if (
|
|
||||||
!sessionRecord ||
|
|
||||||
Number(sessionRecord.worker_id) !== Number(worker.id) ||
|
|
||||||
sessionRecord.status !== 'active' ||
|
|
||||||
Date.parse(String(sessionRecord.expires_at || '')) <= Date.now() ||
|
|
||||||
!(await touchWorkerSession(sessionId, nowIso()))
|
|
||||||
) {
|
|
||||||
throw createHttpError('登录设备已被删除,请重新登录', {
|
|
||||||
statusCode: 401,
|
|
||||||
errorCode: 'worker_auth_device_revoked',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
sessionId,
|
|
||||||
workerId: Number(worker.id),
|
|
||||||
username: worker.username,
|
|
||||||
status: worker.status,
|
|
||||||
expiresAt,
|
|
||||||
sessionVersion: currentSessionVersion,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getWorkerSessions(session: WorkerSession) {
|
|
||||||
const sessions = await listWorkerSessions(session.workerId, nowIso())
|
|
||||||
return {
|
|
||||||
maxDevices: WORKER_MAX_DEVICES,
|
|
||||||
items: sessions.map((item) => mapWorkerSession(item, session.sessionId)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deleteWorkerSession(session: WorkerSession, targetSessionId: unknown) {
|
|
||||||
const normalizedTarget = String(targetSessionId || '').trim()
|
|
||||||
if (!normalizedTarget) {
|
|
||||||
throw createHttpError('缺少登录设备标识', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_session_id_required',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
deleted: await revokeWorkerSession(session.workerId, normalizedTarget, nowIso()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapWorkerSession(session: WorkerSessionRow, currentSessionId: string) {
|
|
||||||
return {
|
|
||||||
sessionId: session.session_id,
|
|
||||||
deviceId: session.device_id,
|
|
||||||
deviceType: session.device_type,
|
|
||||||
deviceName: session.device_name,
|
|
||||||
userAgent: session.user_agent,
|
|
||||||
ipAddress: session.ip_address,
|
|
||||||
issuedAt: session.issued_at,
|
|
||||||
lastSeenAt: session.last_seen_at,
|
|
||||||
expiresAt: session.expires_at,
|
|
||||||
current: session.session_id === currentSessionId,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getWorkerSessionSummary(token: unknown) {
|
|
||||||
const session = await verifyWorkerSessionToken(token)
|
|
||||||
const worker = await getRequiredWorker(session.workerId)
|
|
||||||
return {
|
|
||||||
authenticated: true,
|
|
||||||
expiresAt: session.expiresAt,
|
|
||||||
worker: mapWorkerUser(worker),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getWorkerProfile(session: WorkerSession) {
|
export async function getWorkerProfile(session: WorkerSession) {
|
||||||
const worker = await getRequiredWorker(session.workerId)
|
const worker = await getRequiredWorker(session.workerId)
|
||||||
const [
|
const [
|
||||||
@@ -1342,55 +1197,6 @@ function maskWithdrawalAccountNo(accountNo: string) {
|
|||||||
return `${'*'.repeat(Math.max(4, normalized.length - 4))}${normalized.slice(-4)}`
|
return `${'*'.repeat(Math.max(4, normalized.length - 4))}${normalized.slice(-4)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function changeWorkerPassword(payload: JsonObject = {}, session: WorkerSession) {
|
|
||||||
const worker = await getRequiredWorker(session.workerId)
|
|
||||||
const currentPassword = normalizePassword(payload.currentPassword || payload.oldPassword)
|
|
||||||
const nextPassword = normalizePassword(payload.newPassword || payload.password)
|
|
||||||
|
|
||||||
if (!currentPassword) {
|
|
||||||
throw createHttpError('请输入当前密码', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_password_current_required',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!verifyWorkerPassword(currentPassword, worker.password_hash)) {
|
|
||||||
throw createHttpError('当前密码不正确', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_password_current_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nextPassword) {
|
|
||||||
throw createHttpError('请输入新密码', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_password_new_required',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
validateWorkerPassword(nextPassword)
|
|
||||||
|
|
||||||
if (currentPassword === nextPassword) {
|
|
||||||
throw createHttpError('新密码不能与当前密码一致', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_password_unchanged',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const updated = await updateWorkerPassword({
|
|
||||||
workerId: worker.id,
|
|
||||||
passwordHash: hashWorkerPassword(nextPassword),
|
|
||||||
sessionVersion: normalizeSessionVersion(worker.session_version) + 1,
|
|
||||||
now: nowIso(),
|
|
||||||
})
|
|
||||||
await revokeAllWorkerSessions(worker.id, nowIso())
|
|
||||||
|
|
||||||
return {
|
|
||||||
worker: mapWorkerUser(updated || worker),
|
|
||||||
reloginRequired: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function listWorkerHallOrders(query: JsonObject = {}, session: WorkerSession) {
|
export async function listWorkerHallOrders(query: JsonObject = {}, session: WorkerSession) {
|
||||||
requireActiveWorkerSession(session)
|
requireActiveWorkerSession(session)
|
||||||
const page = normalizePage(query.page)
|
const page = normalizePage(query.page)
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import {
|
||||||
|
getWorkerSessionBySessionId,
|
||||||
|
getWorkerUserById,
|
||||||
|
listWorkerSessions,
|
||||||
|
revokeWorkerSession,
|
||||||
|
touchWorkerSession,
|
||||||
|
} from '../../repositories/worker-platform/index.js'
|
||||||
|
import type { JsonObject } from '../../types/json.js'
|
||||||
|
import { createHttpError } from '../../utils/http.js'
|
||||||
|
import { nowIso } from '../../utils/time.js'
|
||||||
|
import {
|
||||||
|
ensureWorkerAuthConfigured,
|
||||||
|
isIgnorableWorkerAuthError,
|
||||||
|
mapWorkerUser,
|
||||||
|
normalizeSessionVersion,
|
||||||
|
safeCompare,
|
||||||
|
signWorkerPayload,
|
||||||
|
} from './mappers.js'
|
||||||
|
import { getRequiredWorker, type WorkerSession } from './worker-session-context-service.js'
|
||||||
|
import { mapWorkerSession } from './worker-session-view.js'
|
||||||
|
|
||||||
|
const WORKER_MAX_DEVICES = 3
|
||||||
|
|
||||||
|
export async function logoutWorkerSession(token: unknown) {
|
||||||
|
const normalizedToken = String(token || '').trim()
|
||||||
|
if (!normalizedToken) {
|
||||||
|
return { success: true, revoked: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const session = await verifyWorkerSessionToken(normalizedToken)
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
revoked: await revokeWorkerSession(session.workerId, session.sessionId, nowIso()),
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (isIgnorableWorkerAuthError(error)) {
|
||||||
|
return { success: true, revoked: false }
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyWorkerSessionToken(token: unknown): Promise<WorkerSession> {
|
||||||
|
ensureWorkerAuthConfigured()
|
||||||
|
const normalizedToken = String(token || '').trim()
|
||||||
|
if (!normalizedToken) {
|
||||||
|
throw createHttpError('未登录或登录已失效', {
|
||||||
|
statusCode: 401,
|
||||||
|
errorCode: 'worker_auth_required',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = normalizedToken.split('.')
|
||||||
|
const encodedPayload = parts[0]
|
||||||
|
const signature = parts[1]
|
||||||
|
if (
|
||||||
|
parts.length !== 2 ||
|
||||||
|
!encodedPayload ||
|
||||||
|
!signature ||
|
||||||
|
!safeCompare(signature, signWorkerPayload(encodedPayload))
|
||||||
|
) {
|
||||||
|
throw createHttpError('接单登录态无效', {
|
||||||
|
statusCode: 401,
|
||||||
|
errorCode: 'worker_auth_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload: JsonObject
|
||||||
|
try {
|
||||||
|
payload = JSON.parse(Buffer.from(encodedPayload, 'base64url').toString('utf8'))
|
||||||
|
} catch {
|
||||||
|
throw createHttpError('接单登录态无效', {
|
||||||
|
statusCode: 401,
|
||||||
|
errorCode: 'worker_auth_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String(payload.typ || '') !== 'worker') {
|
||||||
|
throw createHttpError('接单登录态无效', {
|
||||||
|
statusCode: 401,
|
||||||
|
errorCode: 'worker_auth_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const expiresAt = String(payload.exp || '').trim()
|
||||||
|
if (!expiresAt || Date.parse(expiresAt) <= Date.now()) {
|
||||||
|
throw createHttpError('登录已过期,请重新登录', {
|
||||||
|
statusCode: 401,
|
||||||
|
errorCode: 'worker_auth_expired',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const workerId = Number(payload.uid || 0)
|
||||||
|
const worker = await getWorkerUserById(workerId)
|
||||||
|
if (!worker || worker.status !== 'active') {
|
||||||
|
throw createHttpError('接单账号已不可用,请重新登录', {
|
||||||
|
statusCode: 401,
|
||||||
|
errorCode: 'worker_auth_user_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentSessionVersion = normalizeSessionVersion(worker.session_version)
|
||||||
|
if (Number(payload.ver || 0) !== currentSessionVersion) {
|
||||||
|
throw createHttpError('登录态已失效,请重新登录', {
|
||||||
|
statusCode: 401,
|
||||||
|
errorCode: 'worker_auth_stale',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionId = String(payload.sid || '').trim()
|
||||||
|
const sessionRecord = sessionId ? await getWorkerSessionBySessionId(sessionId) : null
|
||||||
|
if (
|
||||||
|
!sessionRecord ||
|
||||||
|
Number(sessionRecord.worker_id) !== Number(worker.id) ||
|
||||||
|
sessionRecord.status !== 'active' ||
|
||||||
|
Date.parse(String(sessionRecord.expires_at || '')) <= Date.now() ||
|
||||||
|
!(await touchWorkerSession(sessionId, nowIso()))
|
||||||
|
) {
|
||||||
|
throw createHttpError('登录设备已被删除,请重新登录', {
|
||||||
|
statusCode: 401,
|
||||||
|
errorCode: 'worker_auth_device_revoked',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sessionId,
|
||||||
|
workerId: Number(worker.id),
|
||||||
|
username: worker.username,
|
||||||
|
status: worker.status,
|
||||||
|
expiresAt,
|
||||||
|
sessionVersion: currentSessionVersion,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getWorkerSessions(session: WorkerSession) {
|
||||||
|
const sessions = await listWorkerSessions(session.workerId, nowIso())
|
||||||
|
return {
|
||||||
|
maxDevices: WORKER_MAX_DEVICES,
|
||||||
|
items: sessions.map((item) => mapWorkerSession(item, session.sessionId)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteWorkerSession(session: WorkerSession, targetSessionId: unknown) {
|
||||||
|
const normalizedTarget = String(targetSessionId || '').trim()
|
||||||
|
if (!normalizedTarget) {
|
||||||
|
throw createHttpError('缺少登录设备标识', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_session_id_required',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
deleted: await revokeWorkerSession(session.workerId, normalizedTarget, nowIso()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getWorkerSessionSummary(token: unknown) {
|
||||||
|
const session = await verifyWorkerSessionToken(token)
|
||||||
|
const worker = await getRequiredWorker(session.workerId)
|
||||||
|
return {
|
||||||
|
authenticated: true,
|
||||||
|
expiresAt: session.expiresAt,
|
||||||
|
worker: mapWorkerUser(worker),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { WorkerSessionRow } from '../../repositories/worker-platform/index.js'
|
||||||
|
|
||||||
|
export function mapWorkerSession(session: WorkerSessionRow, currentSessionId: string) {
|
||||||
|
return {
|
||||||
|
sessionId: session.session_id,
|
||||||
|
deviceId: session.device_id,
|
||||||
|
deviceType: session.device_type,
|
||||||
|
deviceName: session.device_name,
|
||||||
|
userAgent: session.user_agent,
|
||||||
|
ipAddress: session.ip_address,
|
||||||
|
issuedAt: session.issued_at,
|
||||||
|
lastSeenAt: session.last_seen_at,
|
||||||
|
expiresAt: session.expires_at,
|
||||||
|
current: session.session_id === currentSessionId,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user