feat: integrar credenciais rustdesk aos acessos remotos
This commit is contained in:
parent
4079f67fcb
commit
07d631de40
5 changed files with 243 additions and 5 deletions
|
|
@ -1991,6 +1991,8 @@ type RemoteAccessEntry = {
|
|||
provider: string
|
||||
identifier: string
|
||||
url: string | null
|
||||
username: string | null
|
||||
password: string | null
|
||||
notes: string | null
|
||||
lastVerifiedAt: number | null
|
||||
metadata: Record<string, unknown> | null
|
||||
|
|
@ -2032,6 +2034,8 @@ function normalizeRemoteAccessEntry(raw: unknown): RemoteAccessEntry | null {
|
|||
provider: "Remoto",
|
||||
identifier: isUrl ? trimmed : trimmed,
|
||||
url: isUrl ? trimmed : null,
|
||||
username: null,
|
||||
password: null,
|
||||
notes: null,
|
||||
lastVerifiedAt: null,
|
||||
metadata: null,
|
||||
|
|
@ -2059,6 +2063,19 @@ function normalizeRemoteAccessEntry(raw: unknown): RemoteAccessEntry | null {
|
|||
null
|
||||
const resolvedIdentifier = identifier ?? url ?? "Acesso remoto"
|
||||
const notes = coerceString(record.notes) ?? coerceString(record.note) ?? coerceString(record.description) ?? coerceString(record.obs) ?? null
|
||||
const username =
|
||||
coerceString((record as Record<string, unknown>).username) ??
|
||||
coerceString((record as Record<string, unknown>).user) ??
|
||||
coerceString((record as Record<string, unknown>).login) ??
|
||||
coerceString((record as Record<string, unknown>).email) ??
|
||||
coerceString((record as Record<string, unknown>).account) ??
|
||||
null
|
||||
const password =
|
||||
coerceString((record as Record<string, unknown>).password) ??
|
||||
coerceString((record as Record<string, unknown>).pass) ??
|
||||
coerceString((record as Record<string, unknown>).secret) ??
|
||||
coerceString((record as Record<string, unknown>).pin) ??
|
||||
null
|
||||
const timestamp =
|
||||
coerceNumber(record.lastVerifiedAt) ??
|
||||
coerceNumber(record.verifiedAt) ??
|
||||
|
|
@ -2075,6 +2092,8 @@ function normalizeRemoteAccessEntry(raw: unknown): RemoteAccessEntry | null {
|
|||
provider,
|
||||
identifier: resolvedIdentifier,
|
||||
url,
|
||||
username,
|
||||
password,
|
||||
notes,
|
||||
lastVerifiedAt: timestamp,
|
||||
metadata,
|
||||
|
|
@ -2105,12 +2124,14 @@ export const updateRemoteAccess = mutation({
|
|||
provider: v.optional(v.string()),
|
||||
identifier: v.optional(v.string()),
|
||||
url: v.optional(v.string()),
|
||||
username: v.optional(v.string()),
|
||||
password: v.optional(v.string()),
|
||||
notes: v.optional(v.string()),
|
||||
action: v.optional(v.string()),
|
||||
entryId: v.optional(v.string()),
|
||||
clear: v.optional(v.boolean()),
|
||||
},
|
||||
handler: async (ctx, { machineId, actorId, provider, identifier, url, notes, action, entryId, clear }) => {
|
||||
handler: async (ctx, { machineId, actorId, provider, identifier, url, username, password, notes, action, entryId, clear }) => {
|
||||
const machine = await ctx.db.get(machineId)
|
||||
if (!machine) {
|
||||
throw new ConvexError("Dispositivo não encontrada")
|
||||
|
|
@ -2192,6 +2213,8 @@ export const updateRemoteAccess = mutation({
|
|||
}
|
||||
|
||||
const cleanedNotes = notes?.trim() ? notes.trim() : null
|
||||
const cleanedUsername = username?.trim() ? username.trim() : null
|
||||
const cleanedPassword = password?.trim() ? password.trim() : null
|
||||
const lastVerifiedAt = Date.now()
|
||||
const targetEntryId =
|
||||
coerceString(entryId) ??
|
||||
|
|
@ -2205,12 +2228,16 @@ export const updateRemoteAccess = mutation({
|
|||
provider: trimmedProvider,
|
||||
identifier: trimmedIdentifier,
|
||||
url: normalizedUrl,
|
||||
username: cleanedUsername,
|
||||
password: cleanedPassword,
|
||||
notes: cleanedNotes,
|
||||
lastVerifiedAt,
|
||||
metadata: {
|
||||
provider: trimmedProvider,
|
||||
identifier: trimmedIdentifier,
|
||||
url: normalizedUrl,
|
||||
username: cleanedUsername,
|
||||
password: cleanedPassword,
|
||||
notes: cleanedNotes,
|
||||
lastVerifiedAt,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ const schema = z.object({
|
|||
provider: z.string().optional(),
|
||||
identifier: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
username: z.string().optional(),
|
||||
password: z.string().optional(),
|
||||
notes: z.string().optional(),
|
||||
entryId: z.string().optional(),
|
||||
action: z.enum(["save", "upsert", "clear", "delete", "remove"]).optional(),
|
||||
|
|
@ -106,6 +108,8 @@ export async function POST(request: Request) {
|
|||
if (provider) mutationArgs.provider = provider
|
||||
if (identifier) mutationArgs.identifier = identifier
|
||||
if (normalizedUrl !== undefined) mutationArgs.url = normalizedUrl
|
||||
mutationArgs.username = (parsed.username ?? "").trim()
|
||||
mutationArgs.password = (parsed.password ?? "").trim()
|
||||
if (notes.length) mutationArgs.notes = notes
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ import {
|
|||
RotateCcw,
|
||||
AlertTriangle,
|
||||
Key,
|
||||
Eye,
|
||||
EyeOff,
|
||||
MonitorSmartphone,
|
||||
Globe,
|
||||
Apple,
|
||||
Terminal,
|
||||
|
|
@ -323,6 +326,8 @@ type DeviceRemoteAccessEntry = {
|
|||
clientId: string
|
||||
provider: string | null
|
||||
identifier: string | null
|
||||
username: string | null
|
||||
password: string | null
|
||||
url: string | null
|
||||
notes: string | null
|
||||
lastVerifiedAt: number | null
|
||||
|
|
@ -332,6 +337,8 @@ type DeviceRemoteAccessEntry = {
|
|||
export type DeviceRemoteAccess = {
|
||||
provider: string | null
|
||||
identifier: string | null
|
||||
username: string | null
|
||||
password: string | null
|
||||
url: string | null
|
||||
notes: string | null
|
||||
lastVerifiedAt: number | null
|
||||
|
|
@ -411,6 +418,8 @@ function normalizeDeviceRemoteAccessEntry(raw: unknown): DeviceRemoteAccessEntry
|
|||
const identifier =
|
||||
readString(record, "identifier", "code", "id", "accessId") ??
|
||||
readString(record, "value", "label")
|
||||
const username = readString(record, "username", "user", "login", "email", "account") ?? null
|
||||
const password = readString(record, "password", "pass", "secret", "pin") ?? null
|
||||
const url = readString(record, "url", "link", "remoteUrl", "console", "viewer") ?? null
|
||||
const notes = readString(record, "notes", "note", "description", "obs") ?? null
|
||||
const timestampCandidate =
|
||||
|
|
@ -423,6 +432,8 @@ function normalizeDeviceRemoteAccessEntry(raw: unknown): DeviceRemoteAccessEntry
|
|||
clientId: id ?? createRemoteAccessClientId(),
|
||||
provider,
|
||||
identifier: identifier ?? url ?? null,
|
||||
username,
|
||||
password,
|
||||
url,
|
||||
notes,
|
||||
lastVerifiedAt,
|
||||
|
|
@ -433,8 +444,8 @@ function normalizeDeviceRemoteAccessEntry(raw: unknown): DeviceRemoteAccessEntry
|
|||
export function normalizeDeviceRemoteAccess(raw: unknown): DeviceRemoteAccess | null {
|
||||
const entry = normalizeDeviceRemoteAccessEntry(raw)
|
||||
if (!entry) return null
|
||||
const { provider, identifier, url, notes, lastVerifiedAt, metadata } = entry
|
||||
return { provider, identifier, url, notes, lastVerifiedAt, metadata }
|
||||
const { provider, identifier, username, password, url, notes, lastVerifiedAt, metadata } = entry
|
||||
return { provider, identifier, username, password, url, notes, lastVerifiedAt, metadata }
|
||||
}
|
||||
|
||||
export function normalizeDeviceRemoteAccessList(raw: unknown): DeviceRemoteAccessEntry[] {
|
||||
|
|
@ -464,6 +475,15 @@ const REMOTE_ACCESS_METADATA_IGNORED_KEYS = new Set([
|
|||
"code",
|
||||
"id",
|
||||
"accessId",
|
||||
"username",
|
||||
"user",
|
||||
"login",
|
||||
"email",
|
||||
"account",
|
||||
"password",
|
||||
"pass",
|
||||
"secret",
|
||||
"pin",
|
||||
"url",
|
||||
"link",
|
||||
"remoteUrl",
|
||||
|
|
@ -515,6 +535,25 @@ function readText(record: Record<string, unknown>, ...keys: string[]): string |
|
|||
return undefined
|
||||
}
|
||||
|
||||
function isRustDeskAccess(entry: DeviceRemoteAccessEntry | null | undefined) {
|
||||
if (!entry) return false
|
||||
const provider = (entry.provider ?? entry.metadata?.provider ?? "").toString().toLowerCase()
|
||||
if (provider.includes("rustdesk")) return true
|
||||
const url = (entry.url ?? entry.metadata?.url ?? "").toString().toLowerCase()
|
||||
return url.includes("rustdesk")
|
||||
}
|
||||
|
||||
function buildRustDeskUri(entry: DeviceRemoteAccessEntry) {
|
||||
const identifier = (entry.identifier ?? "").replace(/\s+/g, "")
|
||||
if (!identifier) return null
|
||||
const params = new URLSearchParams()
|
||||
if (entry.password) {
|
||||
params.set("password", entry.password)
|
||||
}
|
||||
const query = params.toString()
|
||||
return `rustdesk://${encodeURIComponent(identifier)}${query ? `?${query}` : ""}`
|
||||
}
|
||||
|
||||
function parseWindowsInstallDate(value: unknown): Date | null {
|
||||
if (!value) return null
|
||||
if (value instanceof Date) return value
|
||||
|
|
@ -2999,9 +3038,12 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
)
|
||||
const [remoteAccessCustomProvider, setRemoteAccessCustomProvider] = useState("")
|
||||
const [remoteAccessIdentifierInput, setRemoteAccessIdentifierInput] = useState("")
|
||||
const [remoteAccessUsernameInput, setRemoteAccessUsernameInput] = useState("")
|
||||
const [remoteAccessPasswordInput, setRemoteAccessPasswordInput] = useState("")
|
||||
const [remoteAccessUrlInput, setRemoteAccessUrlInput] = useState("")
|
||||
const [remoteAccessNotesInput, setRemoteAccessNotesInput] = useState("")
|
||||
const [remoteAccessSaving, setRemoteAccessSaving] = useState(false)
|
||||
const [visibleRemoteSecrets, setVisibleRemoteSecrets] = useState<Record<string, boolean>>({})
|
||||
const editingRemoteAccess = useMemo(
|
||||
() => remoteAccessEntries.find((entry) => entry.clientId === editingRemoteAccessClientId) ?? null,
|
||||
[editingRemoteAccessClientId, remoteAccessEntries]
|
||||
|
|
@ -3070,6 +3112,8 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
setRemoteAccessCustomProvider(providerName ?? "")
|
||||
}
|
||||
setRemoteAccessIdentifierInput(editingRemoteAccess?.identifier ?? "")
|
||||
setRemoteAccessUsernameInput(editingRemoteAccess?.username ?? "")
|
||||
setRemoteAccessPasswordInput(editingRemoteAccess?.password ?? "")
|
||||
setRemoteAccessUrlInput(editingRemoteAccess?.url ?? "")
|
||||
setRemoteAccessNotesInput(editingRemoteAccess?.notes ?? "")
|
||||
}, [remoteAccessDialog, editingRemoteAccess])
|
||||
|
|
@ -3080,6 +3124,8 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
setRemoteAccessProviderOption(REMOTE_ACCESS_PROVIDERS[0].value)
|
||||
setRemoteAccessCustomProvider("")
|
||||
setRemoteAccessIdentifierInput("")
|
||||
setRemoteAccessUsernameInput("")
|
||||
setRemoteAccessPasswordInput("")
|
||||
setRemoteAccessUrlInput("")
|
||||
setRemoteAccessNotesInput("")
|
||||
}
|
||||
|
|
@ -3087,6 +3133,7 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
|
||||
useEffect(() => {
|
||||
setShowAllWindowsSoftware(false)
|
||||
setVisibleRemoteSecrets({})
|
||||
}, [device?.id])
|
||||
|
||||
const displayedWindowsSoftware = useMemo(
|
||||
|
|
@ -3149,6 +3196,8 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
return
|
||||
}
|
||||
|
||||
const username = remoteAccessUsernameInput.trim()
|
||||
const password = remoteAccessPasswordInput.trim()
|
||||
let normalizedUrl: string | undefined
|
||||
const rawUrl = remoteAccessUrlInput.trim()
|
||||
if (rawUrl.length > 0) {
|
||||
|
|
@ -3191,6 +3240,8 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
deviceId: device.id,
|
||||
provider: providerName,
|
||||
identifier,
|
||||
username,
|
||||
password,
|
||||
url: normalizedUrl,
|
||||
notes: notes.length ? notes : undefined,
|
||||
action: "upsert",
|
||||
|
|
@ -3220,6 +3271,8 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
remoteAccessProviderOption,
|
||||
remoteAccessCustomProvider,
|
||||
remoteAccessIdentifierInput,
|
||||
remoteAccessUsernameInput,
|
||||
remoteAccessPasswordInput,
|
||||
remoteAccessUrlInput,
|
||||
remoteAccessNotesInput,
|
||||
editingRemoteAccess,
|
||||
|
|
@ -3337,6 +3390,41 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
}
|
||||
}, [])
|
||||
|
||||
const handleCopyRemoteCredential = useCallback(async (value: string | null | undefined, label: string) => {
|
||||
if (!value) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(value)
|
||||
toast.success(`${label} copiado.`)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.error(`Não foi possível copiar ${label.toLowerCase()}.`)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const toggleRemoteSecret = useCallback((clientId: string) => {
|
||||
setVisibleRemoteSecrets((prev) => ({ ...prev, [clientId]: !prev[clientId] }))
|
||||
}, [])
|
||||
|
||||
const handleRustDeskConnect = useCallback((entry: DeviceRemoteAccessEntry) => {
|
||||
if (!entry) return
|
||||
const link = buildRustDeskUri(entry)
|
||||
if (!link) {
|
||||
toast.error("Não foi possível montar o link do RustDesk (ID ou senha ausentes).")
|
||||
return
|
||||
}
|
||||
if (typeof window === "undefined") {
|
||||
toast.error("A conexão direta só funciona no navegador.")
|
||||
return
|
||||
}
|
||||
try {
|
||||
window.location.href = link
|
||||
toast.success("Abrindo o RustDesk...")
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.error("Não foi possível acionar o RustDesk neste dispositivo.")
|
||||
}
|
||||
}, [])
|
||||
|
||||
// Exportação individual (colunas personalizadas)
|
||||
const [isSingleExportOpen, setIsSingleExportOpen] = useState(false)
|
||||
const [singleExporting, setSingleExporting] = useState(false)
|
||||
|
|
@ -3804,6 +3892,8 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
entry.lastVerifiedAt && Number.isFinite(entry.lastVerifiedAt)
|
||||
? new Date(entry.lastVerifiedAt)
|
||||
: null
|
||||
const isRustDesk = isRustDeskAccess(entry)
|
||||
const secretVisible = Boolean(visibleRemoteSecrets[entry.clientId])
|
||||
return (
|
||||
<div key={entry.clientId} className="rounded-lg border border-slate-200 bg-slate-50 px-4 py-3 text-xs sm:text-sm text-slate-700">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||
|
|
@ -3828,6 +3918,51 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
{entry.username || entry.password ? (
|
||||
<div className="flex flex-col gap-1">
|
||||
{entry.username ? (
|
||||
<div className="inline-flex flex-wrap items-center gap-2">
|
||||
<span className="text-[11px] font-semibold uppercase tracking-wide text-slate-500">Usuário</span>
|
||||
<code className="rounded-md border border-slate-200 bg-white px-2 py-0.5 font-mono text-xs text-slate-700">
|
||||
{entry.username}
|
||||
</code>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 gap-1 border border-transparent px-2 text-slate-600 hover:border-slate-300 hover:bg-slate-100 hover:text-slate-900"
|
||||
onClick={() => handleCopyRemoteCredential(entry.username, "Usuário do acesso remoto")}
|
||||
>
|
||||
<ClipboardCopy className="size-3.5" /> Copiar
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
{entry.password ? (
|
||||
<div className="inline-flex flex-wrap items-center gap-2">
|
||||
<span className="text-[11px] font-semibold uppercase tracking-wide text-slate-500">Senha</span>
|
||||
<code className="rounded-md border border-slate-200 bg-white px-2 py-0.5 font-mono text-xs text-slate-700">
|
||||
{secretVisible ? entry.password : "••••••••"}
|
||||
</code>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 gap-1 border border-transparent px-2 text-slate-600 hover:border-slate-300 hover:bg-slate-100 hover:text-slate-900"
|
||||
onClick={() => toggleRemoteSecret(entry.clientId)}
|
||||
>
|
||||
{secretVisible ? <EyeOff className="size-3.5" /> : <Eye className="size-3.5" />}
|
||||
{secretVisible ? "Ocultar" : "Mostrar"}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 gap-1 border border-transparent px-2 text-slate-600 hover:border-slate-300 hover:bg-slate-100 hover:text-slate-900"
|
||||
onClick={() => handleCopyRemoteCredential(entry.password, "Senha do acesso remoto")}
|
||||
>
|
||||
<ClipboardCopy className="size-3.5" /> Copiar
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{entry.url ? (
|
||||
<a
|
||||
href={entry.url}
|
||||
|
|
@ -3838,6 +3973,16 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
Abrir console remoto
|
||||
</a>
|
||||
) : null}
|
||||
{isRustDesk && (entry.identifier || entry.password) ? (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="mt-1 inline-flex items-center gap-2 bg-white/80 text-slate-800 hover:bg-white"
|
||||
onClick={() => handleRustDeskConnect(entry)}
|
||||
>
|
||||
<MonitorSmartphone className="size-4" /> Conectar via RustDesk
|
||||
</Button>
|
||||
) : null}
|
||||
{entry.notes ? (
|
||||
<p className="whitespace-pre-wrap text-[11px] text-slate-600">{entry.notes}</p>
|
||||
) : null}
|
||||
|
|
@ -4120,6 +4265,26 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<label className="text-sm font-medium">Usuário (opcional)</label>
|
||||
<Input
|
||||
value={remoteAccessUsernameInput}
|
||||
onChange={(event) => setRemoteAccessUsernameInput(event.target.value)}
|
||||
placeholder="Ex: suporte@cliente.com"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<label className="text-sm font-medium">Senha / PIN (opcional)</label>
|
||||
<Input
|
||||
type="password"
|
||||
value={remoteAccessPasswordInput}
|
||||
onChange={(event) => setRemoteAccessPasswordInput(event.target.value)}
|
||||
placeholder="Senha permanente do RustDesk ou PIN"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Esse valor ficará disponível para os administradores do painel. Limpe o campo para remover a senha salva.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<label className="text-sm font-medium">Link (opcional)</label>
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -359,8 +359,19 @@ const PHYSICAL_DISK_COLUMN_WIDTHS = [22, 32, 18, 16, 16, 22, 16] as const
|
|||
const NETWORK_HEADERS = ["Hostname", "Interface", "MAC", "IP", "Origem"] as const
|
||||
const NETWORK_COLUMN_WIDTHS = [22, 28, 22, 24, 18] as const
|
||||
|
||||
const REMOTE_ACCESS_HEADERS = ["Hostname", "Provedor", "Identificador", "URL", "Notas", "Última verificação", "Origem", "Metadados"] as const
|
||||
const REMOTE_ACCESS_COLUMN_WIDTHS = [22, 22, 24, 36, 28, 22, 16, 40] as const
|
||||
const REMOTE_ACCESS_HEADERS = [
|
||||
"Hostname",
|
||||
"Provedor",
|
||||
"Identificador",
|
||||
"Usuário",
|
||||
"Senha",
|
||||
"URL",
|
||||
"Notas",
|
||||
"Última verificação",
|
||||
"Origem",
|
||||
"Metadados",
|
||||
] as const
|
||||
const REMOTE_ACCESS_COLUMN_WIDTHS = [22, 22, 24, 24, 20, 32, 28, 22, 16, 36] as const
|
||||
|
||||
const SERVICE_HEADERS = ["Hostname", "Nome", "Exibição", "Status", "Origem"] as const
|
||||
const SERVICE_COLUMN_WIDTHS = [22, 28, 36, 18, 18] as const
|
||||
|
|
@ -756,6 +767,8 @@ function buildRemoteAccessRows(machines: MachineInventoryRecord[]): WorksheetRow
|
|||
machine.hostname,
|
||||
entry.provider ?? "—",
|
||||
entry.identifier ?? "—",
|
||||
entry.username ?? "—",
|
||||
entry.password ?? "—",
|
||||
entry.url ?? "—",
|
||||
entry.notes ?? "—",
|
||||
entry.lastVerifiedAt ? formatDateTime(entry.lastVerifiedAt) ?? "—" : "—",
|
||||
|
|
@ -1229,6 +1242,8 @@ function stringifyMetadata(metadata: Record<string, unknown> | null | undefined)
|
|||
type RemoteAccessNormalized = {
|
||||
provider: string | null
|
||||
identifier: string | null
|
||||
username: string | null
|
||||
password: string | null
|
||||
url: string | null
|
||||
notes: string | null
|
||||
lastVerifiedAt: number | null
|
||||
|
|
@ -1249,6 +1264,8 @@ function normalizeRemoteAccessEntry(
|
|||
return {
|
||||
provider: providerHint ?? null,
|
||||
identifier: isUrl ? null : trimmed,
|
||||
username: null,
|
||||
password: null,
|
||||
url: isUrl ? trimmed : null,
|
||||
notes: null,
|
||||
lastVerifiedAt: null,
|
||||
|
|
@ -1273,6 +1290,19 @@ function normalizeRemoteAccessEntry(
|
|||
ensureString(record["value"]) ??
|
||||
ensureString(record["label"]) ??
|
||||
null
|
||||
const username =
|
||||
ensureString(record["username"]) ??
|
||||
ensureString(record["user"]) ??
|
||||
ensureString(record["login"]) ??
|
||||
ensureString(record["email"]) ??
|
||||
ensureString(record["account"]) ??
|
||||
null
|
||||
const password =
|
||||
ensureString(record["password"]) ??
|
||||
ensureString(record["pass"]) ??
|
||||
ensureString(record["secret"]) ??
|
||||
ensureString(record["pin"]) ??
|
||||
null
|
||||
const url =
|
||||
ensureString(record["url"]) ??
|
||||
ensureString(record["link"]) ??
|
||||
|
|
@ -1296,6 +1326,8 @@ function normalizeRemoteAccessEntry(
|
|||
return {
|
||||
provider,
|
||||
identifier,
|
||||
username,
|
||||
password,
|
||||
url,
|
||||
notes,
|
||||
lastVerifiedAt,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ describe("normalizeDeviceRemoteAccess", () => {
|
|||
expect(result).toEqual({
|
||||
provider: null,
|
||||
identifier: "PC-001",
|
||||
username: null,
|
||||
password: null,
|
||||
url: null,
|
||||
notes: null,
|
||||
lastVerifiedAt: null,
|
||||
|
|
@ -25,6 +27,8 @@ describe("normalizeDeviceRemoteAccess", () => {
|
|||
expect(result).toEqual({
|
||||
provider: null,
|
||||
identifier: null,
|
||||
username: null,
|
||||
password: null,
|
||||
url: "https://remote.example.com/session/123",
|
||||
notes: null,
|
||||
lastVerifiedAt: null,
|
||||
|
|
@ -39,12 +43,16 @@ describe("normalizeDeviceRemoteAccess", () => {
|
|||
code: "123-456-789",
|
||||
remoteUrl: "https://anydesk.com/session/123",
|
||||
note: "Suporte avançado",
|
||||
user: "admin",
|
||||
secret: "S3nh@",
|
||||
verifiedAt: timestamp,
|
||||
extraTag: "vip",
|
||||
})
|
||||
expect(result).toEqual({
|
||||
provider: "AnyDesk",
|
||||
identifier: "123-456-789",
|
||||
username: "admin",
|
||||
password: "S3nh@",
|
||||
url: "https://anydesk.com/session/123",
|
||||
notes: "Suporte avançado",
|
||||
lastVerifiedAt: timestamp,
|
||||
|
|
@ -53,6 +61,8 @@ describe("normalizeDeviceRemoteAccess", () => {
|
|||
code: "123-456-789",
|
||||
remoteUrl: "https://anydesk.com/session/123",
|
||||
note: "Suporte avançado",
|
||||
user: "admin",
|
||||
secret: "S3nh@",
|
||||
verifiedAt: timestamp,
|
||||
extraTag: "vip",
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue