dsh 0.2.0-rc.2: in-container update (experimental channel), path-agnostic compose, upstream patches adapted to 0.1.x/0.2.x
This commit is contained in:
commit
e6dcef9bf5
302 files changed
+75178
No files matched your search
@@ -0,0 +1,62 @@
|
||||
import { randomBytes } from 'node:crypto'
|
||||
import { ProxyConfigurationError } from './errors.mjs'
|
||||
import { validProviderId } from './contracts.mjs'
|
||||
|
||||
const HANDLE_BYTES = 32
|
||||
const HANDLE_PATTERN = /^[A-Za-z0-9_-]{43}$/
|
||||
export const PROVIDER_HANDLE_TTL_MS = 15 * 60 * 1000
|
||||
|
||||
function invalidHandle() {
|
||||
return new ProxyConfigurationError('provider policy handle is invalid', {
|
||||
code: 'PROVIDER_HANDLE_INVALID', statusCode: 407, stage: 'authorize', retryable: true,
|
||||
})
|
||||
}
|
||||
|
||||
export class ProviderHandleStore {
|
||||
constructor({ ttlMs = PROVIDER_HANDLE_TTL_MS, now = () => Date.now(), random = randomBytes } = {}) {
|
||||
if (!Number.isSafeInteger(ttlMs) || ttlMs < 1) throw new TypeError('provider handle TTL is invalid')
|
||||
this.ttlMs = ttlMs
|
||||
this.now = now
|
||||
this.random = random
|
||||
this.handles = new Map()
|
||||
}
|
||||
|
||||
issue({ providerId, policyRevision }, snapshot) {
|
||||
if (!validProviderId(providerId)) throw new ProxyConfigurationError('model API provider ID is invalid')
|
||||
if (typeof policyRevision !== 'string' || policyRevision !== snapshot.revision) {
|
||||
throw new ProxyConfigurationError('provider policy revision changed', {
|
||||
code: 'REVISION_CONFLICT', statusCode: 409, stage: 'authorize', retryable: true,
|
||||
})
|
||||
}
|
||||
this.prune()
|
||||
const handle = this.random(HANDLE_BYTES).toString('base64url')
|
||||
if (!HANDLE_PATTERN.test(handle) || this.handles.has(handle)) throw new Error('provider handle generation failed')
|
||||
const expiresAtMs = this.now() + this.ttlMs
|
||||
this.handles.set(handle, Object.freeze({ providerId, policyRevision, expiresAtMs }))
|
||||
return Object.freeze({ handle, policyRevision, expiresAt: new Date(expiresAtMs).toISOString() })
|
||||
}
|
||||
|
||||
resolve(authorization, snapshot) {
|
||||
const match = typeof authorization === 'string' ? /^DSH-Provider ([A-Za-z0-9_-]{43})$/.exec(authorization) : null
|
||||
if (match === null) throw invalidHandle()
|
||||
const entry = this.handles.get(match[1])
|
||||
if (entry === undefined || entry.expiresAtMs <= this.now() || entry.policyRevision !== snapshot.revision) {
|
||||
if (entry !== undefined) this.handles.delete(match[1])
|
||||
throw invalidHandle()
|
||||
}
|
||||
return entry.providerId
|
||||
}
|
||||
|
||||
prune() {
|
||||
const now = this.now()
|
||||
for (const [handle, entry] of this.handles) {
|
||||
if (entry.expiresAtMs <= now) this.handles.delete(handle)
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.handles.clear()
|
||||
}
|
||||
}
|
||||
|
||||
export const providerHandleInternals = Object.freeze({ HANDLE_BYTES, HANDLE_PATTERN })
|
||||
Reference in new issue
Block a user