fix(backups): получать нативный экспорт и проверить восстановление из S3
This commit is contained in:
1 parent
03b0896369
commit
c3fb66dcbf
18 files changed
+1405
-120
No files matched your search
@@ -27,7 +27,7 @@
|
|||||||
"test:config-sync": "tsx src/utils/server-id.test.ts && tsx src/services/config-apply-plan.test.ts && tsx src/services/entity-snapshots.test.ts && tsx src/services/gre-native-identity.test.ts && tsx src/services/gre-ipv6.test.ts && tsx src/routes/gre-ipv6-validation.test.ts && tsx src/services/network-disabled-server.test.ts",
|
"test:config-sync": "tsx src/utils/server-id.test.ts && tsx src/services/config-apply-plan.test.ts && tsx src/services/entity-snapshots.test.ts && tsx src/services/gre-native-identity.test.ts && tsx src/services/gre-ipv6.test.ts && tsx src/routes/gre-ipv6-validation.test.ts && tsx src/services/network-disabled-server.test.ts",
|
||||||
"test:ros-file": "tsx src/services/ros-file-contents.test.ts && tsx src/services/mikrotik-response.test.ts",
|
"test:ros-file": "tsx src/services/ros-file-contents.test.ts && tsx src/services/mikrotik-response.test.ts",
|
||||||
"test:scheduler": "tsx src/services/scheduler-running.test.ts && tsx src/services/scheduler-recorded-run.test.ts && tsx src/routes/scheduler.test.ts",
|
"test:scheduler": "tsx src/services/scheduler-running.test.ts && tsx src/services/scheduler-recorded-run.test.ts && tsx src/routes/scheduler.test.ts",
|
||||||
"test:backups": "tsx src/services/backup-scheduler-run.test.ts && tsx src/services/backup-restore.test.ts && tsx src/services/backup-deletion.test.ts && tsx src/services/backup-dto.test.ts && tsx src/services/backup-storage-update.test.ts && tsx src/services/backup-job-execution.test.ts && tsx src/routes/backups-operations.test.ts && tsx src/services/s3-backup-client.test.ts",
|
"test:backups": "tsx src/services/router-config-export.test.ts && tsx src/services/backup-scheduler-run.test.ts && tsx src/services/backup-restore.test.ts && tsx src/services/backup-deletion.test.ts && tsx src/services/backup-dto.test.ts && tsx src/services/backup-storage-update.test.ts && tsx src/services/backup-job-execution.test.ts && tsx src/routes/backups-operations.test.ts && tsx src/services/s3-backup-client.test.ts",
|
||||||
"test:ipsec": "tsx src/services/ipsec-config.test.ts && tsx src/services/ipsec-state.test.ts && tsx src/routes/ipsec-history.test.ts && tsx src/services/ipsec-restore.test.ts && tsx src/services/ipsec-operation.test.ts && tsx src/routes/ipsec-mutation.test.ts && tsx src/routes/ipsec-nat.test.ts && tsx src/routes/ipsec-group.test.ts",
|
"test:ipsec": "tsx src/services/ipsec-config.test.ts && tsx src/services/ipsec-state.test.ts && tsx src/routes/ipsec-history.test.ts && tsx src/services/ipsec-restore.test.ts && tsx src/services/ipsec-operation.test.ts && tsx src/routes/ipsec-mutation.test.ts && tsx src/routes/ipsec-nat.test.ts && tsx src/routes/ipsec-group.test.ts",
|
||||||
"test:live-maps": "tsx src/services/gre-keepalive.test.ts && tsx src/services/gre-read.test.ts && tsx src/services/ospf-route-parse.test.ts && tsx src/services/vxlan-live.test.ts && tsx src/routes/vxlan.test.ts && tsx src/services/containers-live.test.ts && tsx src/services/firewall-poll.test.ts && tsx src/services/gre-catalog-poll.test.ts && tsx src/routes/filters-gre-catalog.test.ts && tsx src/routes/bgp-catalog.test.ts && tsx src/services/bgp-catalog-parse.test.ts && tsx src/routes/ospf-catalog.test.ts && tsx src/services/ospf-observed-values.test.ts && tsx src/routes/ospf-legacy.test.ts",
|
"test:live-maps": "tsx src/services/gre-keepalive.test.ts && tsx src/services/gre-read.test.ts && tsx src/services/ospf-route-parse.test.ts && tsx src/services/vxlan-live.test.ts && tsx src/routes/vxlan.test.ts && tsx src/services/containers-live.test.ts && tsx src/services/firewall-poll.test.ts && tsx src/services/gre-catalog-poll.test.ts && tsx src/routes/filters-gre-catalog.test.ts && tsx src/routes/bgp-catalog.test.ts && tsx src/services/bgp-catalog-parse.test.ts && tsx src/routes/ospf-catalog.test.ts && tsx src/services/ospf-observed-values.test.ts && tsx src/routes/ospf-legacy.test.ts",
|
||||||
"test": "node scripts/test.mjs",
|
"test": "node scripts/test.mjs",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import http from "node:http"
|
import http from "node:http"
|
||||||
import https from "node:https"
|
import https from "node:https"
|
||||||
import { randomBytes } from "node:crypto"
|
import { randomBytes } from "node:crypto"
|
||||||
|
import { exportRouterConfiguration } from "./router-config-export.js"
|
||||||
import type { Server } from "../db/schema.js"
|
import type { Server } from "../db/schema.js"
|
||||||
import { parseRosDataSizeBytes } from "./ros-metric-parse.js"
|
import { parseRosDataSizeBytes } from "./ros-metric-parse.js"
|
||||||
import { readOspfEffectiveDisabled } from "./ospf-effective-disabled.js"
|
import { readOspfEffectiveDisabled } from "./ospf-effective-disabled.js"
|
||||||
@@ -122,6 +123,8 @@ function rosPost(
|
|||||||
|
|
||||||
const lib = params.useSsl ? https : http
|
const lib = params.useSsl ? https : http
|
||||||
|
|
||||||
|
// An already-aborted signal can settle before the request is assigned.
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
let req: ReturnType<typeof lib.request> | undefined
|
let req: ReturnType<typeof lib.request> | undefined
|
||||||
|
|
||||||
let settled = false
|
let settled = false
|
||||||
@@ -900,7 +903,7 @@ export class MikrotikClient {
|
|||||||
target: { name: string; id?: string; size?: number },
|
target: { name: string; id?: string; size?: number },
|
||||||
timeoutMs = 30_000,
|
timeoutMs = 30_000,
|
||||||
): Promise<Buffer> {
|
): Promise<Buffer> {
|
||||||
let entry = target.id
|
const entry = target.id
|
||||||
? { id: target.id, name: target.name, size: target.size ?? 0 }
|
? { id: target.id, name: target.name, size: target.size ?? 0 }
|
||||||
: await this.findFileEntry(target.name)
|
: await this.findFileEntry(target.name)
|
||||||
if (!entry) throw new Error(`Файл ${routerFileBasename(target.name)} не найден на RouterOS`)
|
if (!entry) throw new Error(`Файл ${routerFileBasename(target.name)} не найден на RouterOS`)
|
||||||
@@ -1045,116 +1048,14 @@ export class MikrotikClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async exportConfigScript(): Promise<string> {
|
async exportConfigScript(): Promise<string> {
|
||||||
const raw = await this.post<unknown>("/console/export", {}, 30_000)
|
return exportRouterConfiguration({
|
||||||
|
list: () => this.get<unknown>("/file"),
|
||||||
const asText = (v: unknown): string | null => {
|
export: name => this.post("/export", { file: name }, 120_000),
|
||||||
if (typeof v === "string") return v.trim().length > 0 ? v : null
|
download: file => this.downloadFile(file, 30_000),
|
||||||
if (Array.isArray(v)) {
|
remove: id => this.removeFile(id),
|
||||||
const parts = v
|
})
|
||||||
.map((item) => asText(item))
|
|
||||||
.filter((s): s is string => typeof s === "string" && s.length > 0)
|
|
||||||
return parts.length > 0 ? parts.join("\n") : null
|
|
||||||
}
|
|
||||||
if (v && typeof v === "object") {
|
|
||||||
const rec = v as Record<string, unknown>
|
|
||||||
const direct =
|
|
||||||
asText(rec.output) ??
|
|
||||||
asText(rec.stdout) ??
|
|
||||||
asText(rec.data) ??
|
|
||||||
asText(rec.ret) ??
|
|
||||||
asText(rec["!re"])
|
|
||||||
if (direct) return direct
|
|
||||||
const serialized = JSON.stringify(rec, null, 2)
|
|
||||||
return serialized.length > 2 ? serialized : null
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const txt = asText(raw)
|
|
||||||
if (txt && txt.trim().length > 0) return txt
|
|
||||||
|
|
||||||
// Fallback: на части RouterOS /console/export возвращает пустое тело.
|
|
||||||
// Тогда строим .rsc-скрипт из основных read-only разделов REST.
|
|
||||||
return this.buildSyntheticExportScript()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async buildSyntheticExportScript(): Promise<string> {
|
|
||||||
const now = new Date().toISOString()
|
|
||||||
const lines: string[] = [
|
|
||||||
"# synthetic export generated by MikrotikManager",
|
|
||||||
`# generated-at: ${now}`,
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
|
|
||||||
const identity = await this.getIdentity().catch(() => null)
|
|
||||||
if (identity?.name) {
|
|
||||||
lines.push("/system identity")
|
|
||||||
lines.push(`set name="${identity.name.replace(/"/g, "\\\"")}"`)
|
|
||||||
lines.push("")
|
|
||||||
}
|
|
||||||
|
|
||||||
const interfaces = await this.getInterfaces().catch(() => [])
|
|
||||||
if (interfaces.length > 0) {
|
|
||||||
lines.push("/interface")
|
|
||||||
for (const i of interfaces) {
|
|
||||||
if (!i.name) continue
|
|
||||||
const mtu = i["actual-mtu"] ?? i.mtu
|
|
||||||
const parts = [
|
|
||||||
`name="${String(i.name).replace(/"/g, "\\\"")}"`,
|
|
||||||
mtu ? `mtu=${mtu}` : null,
|
|
||||||
i.disabled === "true" ? "disabled=yes" : "disabled=no",
|
|
||||||
].filter((v): v is string => typeof v === "string")
|
|
||||||
lines.push(`:put "interface ${parts.join(" ")}"`)
|
|
||||||
}
|
|
||||||
lines.push("")
|
|
||||||
}
|
|
||||||
|
|
||||||
const addrs = await this.getIpAddresses().catch(() => [])
|
|
||||||
if (addrs.length > 0) {
|
|
||||||
lines.push("/ip address")
|
|
||||||
for (const a of addrs) {
|
|
||||||
if (!a.address || !a.interface) continue
|
|
||||||
const comment = a.comment ? ` comment="${String(a.comment).replace(/"/g, "\\\"")}"` : ""
|
|
||||||
lines.push(`add address=${a.address} interface="${String(a.interface).replace(/"/g, "\\\"")}"${comment}`)
|
|
||||||
}
|
|
||||||
lines.push("")
|
|
||||||
}
|
|
||||||
|
|
||||||
const routes = await this.getIpRoutes().catch(() => [])
|
|
||||||
if (routes.length > 0) {
|
|
||||||
lines.push("/ip route")
|
|
||||||
for (const r of routes) {
|
|
||||||
const dst = r["dst-address"]
|
|
||||||
const gw = r["gateway"]
|
|
||||||
if (!dst || !gw) continue
|
|
||||||
const distance = r.distance ? ` distance=${r.distance}` : ""
|
|
||||||
lines.push(`add dst-address=${dst} gateway=${gw}${distance}`)
|
|
||||||
}
|
|
||||||
lines.push("")
|
|
||||||
}
|
|
||||||
|
|
||||||
const firewall = await this.getFirewallFilters().catch(() => [])
|
|
||||||
if (firewall.length > 0) {
|
|
||||||
lines.push("/ip firewall filter")
|
|
||||||
for (const f of firewall) {
|
|
||||||
if (!f.chain || !f.action) continue
|
|
||||||
const parts = [`chain=${f.chain}`, `action=${f.action}`]
|
|
||||||
if (f.protocol) parts.push(`protocol=${f.protocol}`)
|
|
||||||
if (f["src-address"]) parts.push(`src-address=${f["src-address"]}`)
|
|
||||||
if (f["dst-address"]) parts.push(`dst-address=${f["dst-address"]}`)
|
|
||||||
if (f["dst-port"]) parts.push(`dst-port=${f["dst-port"]}`)
|
|
||||||
if (f["src-port"]) parts.push(`src-port=${f["src-port"]}`)
|
|
||||||
if (f.disabled === "true") parts.push("disabled=yes")
|
|
||||||
lines.push(`add ${parts.join(" ")}`)
|
|
||||||
}
|
|
||||||
lines.push("")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lines.length <= 3) {
|
|
||||||
throw new Error("RouterOS вернул пустой export и fallback-данные недоступны")
|
|
||||||
}
|
|
||||||
return lines.join("\n")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Error type ─────────────────────────────────────────────────────────────────
|
// ── Error type ─────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import {createServer} from 'node:http'
|
||||||
|
import {MikrotikClient} from './mikrotik.js'
|
||||||
|
const good='# oct/14/2026 by RouterOS 7.24.5\n/system identity\nset name=export-test\n'
|
||||||
|
for(const mode of ['success','malformed-list','export-error','truncated','section-error','oversize','cleanup-error','foreign-id']){
|
||||||
|
let ownName='',exportCalls=0,readCalls=0,deletes=0
|
||||||
|
const original={'.id':'*ORIG',name:'important.rsc',size:'2'}
|
||||||
|
let own:Record<string,string>|null=null
|
||||||
|
const content=mode==='section-error'?good+'#error exporting /routing (timeout)\n':mode==='oversize'?good+'#'.repeat(70000):good
|
||||||
|
const fixture=createServer(async(req,res)=>{let input='';for await(const b of req)input+=b;const body=input?JSON.parse(input):{};const reply=(status:number,value:unknown)=>{res.statusCode=status;res.setHeader('content-type','application/json');res.end(JSON.stringify(value))}
|
||||||
|
if(req.method==='GET'&&req.url==='/rest/file')return reply(200,mode==='malformed-list'?{}:[original,...(own?[own]:[])])
|
||||||
|
if(req.method==='POST'&&req.url==='/rest/export'){exportCalls++;ownName=body.file;assert(/^mmapp-export-[a-f0-9-]{36}\.rsc$/.test(ownName));assert.deepEqual(Object.keys(body),['file']);own={'.id':mode==='foreign-id'?'*ORIG':'*TEMP',name:ownName,size:String(Buffer.byteLength(content))};return reply(mode==='export-error'?500:200,mode==='export-error'?{error:'export failed'}:[])}
|
||||||
|
if(req.method==='POST'&&req.url==='/rest/file/get'){readCalls++;return reply(200,[{ret:mode==='truncated'?content.slice(0,20):content}])}
|
||||||
|
if(req.method==='DELETE'&&req.url==='/rest/file/*TEMP'){deletes++;if(mode==='cleanup-error')return reply(500,{error:'cleanup denied'});own=null;return reply(200,{})}
|
||||||
|
assert.fail('Unexpected request '+req.method+' '+req.url)
|
||||||
|
});await new Promise<void>(r=>fixture.listen(0,'127.0.0.1',r));const address=fixture.address();assert(address&&typeof address!=='string');const c=new MikrotikClient({host:'127.0.0.1',port:address.port,useSsl:false,verifySsl:false,username:'fixture',password:'fixture'});
|
||||||
|
try{if(mode==='success'){assert.equal(await c.exportConfigScript(),good);assert.equal(readCalls,1)}else await assert.rejects(c.exportConfigScript(), mode);assert.equal(original.name,'important.rsc');if(mode==='malformed-list')assert.equal(exportCalls,0);else assert.equal(exportCalls,1);if(!['malformed-list','foreign-id'].includes(mode))assert.equal(deletes,1);if(!['cleanup-error','foreign-id'].includes(mode))assert.equal(own,null);if(mode==='oversize')assert.equal(readCalls,0)}finally{await new Promise<void>(r=>fixture.close(()=>r()))}
|
||||||
|
}
|
||||||
|
console.log('native configuration export: 8 HTTP scenarios passed; original file never deleted')
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { randomUUID } from "node:crypto"
|
||||||
|
|
||||||
|
type ExportFile = { id: string; name: string; size: number }
|
||||||
|
type ExportDeps = {
|
||||||
|
list: () => Promise<unknown>
|
||||||
|
export: (name: string) => Promise<unknown>
|
||||||
|
download: (file: ExportFile) => Promise<Buffer>
|
||||||
|
remove: (id: string) => Promise<void>
|
||||||
|
}
|
||||||
|
|
||||||
|
function files(raw: unknown): ExportFile[] {
|
||||||
|
if (!Array.isArray(raw)) throw new Error("Не подтверждён список файлов RouterOS")
|
||||||
|
const ids = new Set<string>(), names = new Set<string>()
|
||||||
|
return raw.map(row => {
|
||||||
|
if (!row || typeof row !== "object" || typeof row[".id"] !== "string" || !row[".id"] || typeof row.name !== "string" || !row.name || ids.has(row[".id"]) || names.has(row.name)) throw new Error("Некорректный список файлов RouterOS")
|
||||||
|
ids.add(row[".id"]); names.add(row.name)
|
||||||
|
const size = Number(row.size ?? 0)
|
||||||
|
if (!Number.isSafeInteger(size) || size < 0) throw new Error("Не подтверждён размер файла RouterOS")
|
||||||
|
return { id: row[".id"], name: row.name, size }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Native export is file based. Never replace a failed backup with a partial synthetic script. */
|
||||||
|
export async function exportRouterConfiguration(deps: ExportDeps): Promise<string> {
|
||||||
|
const name = `mmapp-export-${randomUUID()}.rsc`
|
||||||
|
const ownName = (value: string) => value === name || value === `flash/${name}`
|
||||||
|
const before = files(await deps.list())
|
||||||
|
if (before.some(row => ownName(row.name))) throw new Error("Временный файл экспорта уже существует")
|
||||||
|
const originalIds = new Set(before.map(row => row.id))
|
||||||
|
try {
|
||||||
|
await deps.export(name)
|
||||||
|
let target: ExportFile | undefined
|
||||||
|
for (let attempt = 0; attempt < 8; attempt++) {
|
||||||
|
const candidates = files(await deps.list()).filter(row => ownName(row.name))
|
||||||
|
if (candidates.length > 1) throw new Error("Неоднозначный временный файл экспорта")
|
||||||
|
target = candidates[0]
|
||||||
|
if (target?.size) break
|
||||||
|
if (attempt < 7) await new Promise(resolve => setTimeout(resolve, 250))
|
||||||
|
}
|
||||||
|
if (!target?.size || originalIds.has(target.id)) throw new Error("RouterOS не создал полный файл экспорта")
|
||||||
|
const content = await deps.download(target)
|
||||||
|
const script = content.toString("utf8")
|
||||||
|
if (content.length !== target.size || !/^#.*by RouterOS/m.test(script) || /^\s*#\s*error\b/im.test(script) || script.includes("\uFFFD")) throw new Error("Не подтверждён полный экспорт конфигурации RouterOS")
|
||||||
|
return script
|
||||||
|
} finally {
|
||||||
|
// A failed/unknown command can still have created the file. Delete only this run's name and ID.
|
||||||
|
const current = files(await deps.list()).filter(row => ownName(row.name))
|
||||||
|
for (const row of current) {
|
||||||
|
if (originalIds.has(row.id)) throw new Error("ID временного экспорта принадлежит исходному файлу")
|
||||||
|
await deps.remove(row.id)
|
||||||
|
}
|
||||||
|
if (files(await deps.list()).some(row => ownName(row.name))) throw new Error(`Не подтверждена очистка временного экспорта ${name}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# UX/UI: протокол внешней приёмки 12.5
|
# UX/UI: протокол внешней приёмки 12.5
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-14: Selectel S3 принят228–229:2 real roundtrip objects и12 unique compiled backup API/owned SQL сценариев, включая upload denial/local fallback/sync/delete. Все собственные DB/roles/files/current objects очищены; enabled versioning обработан отдельно —8versions+8delete markers удалены из3 own prefixes, remaining0. Keys не в Git/reports, private env удалены. Native export — HTTP fixture, CHR contacts0; real native backup/restore/live UI остаются230. Backend ux-regression222/frontend ux-routing218; этап12.5 открыт.
|
Актуальное продолжение 2026-10-14: блок230 — исправлена подмена нативного экспорта частичным синтетическим скриптом. Реальные конфигурации обоих CHR выгружены в Selectel S3 и скачаны; ограниченный restore собственного bridge подтверждён RouterOS. Исходные файлы/bridge сохранены, собственные DB/roles/объекты удалены;6 versions+6 delete markers очищены. Live UI1440/390 с real Portal/API принят без изменения storage settings. Backend ux-backup230/frontend ux-routing218. Далее231 — чтение файлов>=61439 байт; S3 retention/смена bucket и расширенная приёмка открыты. Этап12.5 открыт.
|
||||||
|
|
||||||
IPsec78: реальный PSK responder established, mode-config198.18.78.10 и online=true подтверждены API; точные собственные объекты удалены. Сертификатный/DN online, late failure и history restore остаются открытыми. [Отчёт](ux-ui/acceptance/ipsec-mode-config-2026-10-09.json), [журнал](ux-ui-progress.md).
|
IPsec78: реальный PSK responder established, mode-config198.18.78.10 и online=true подтверждены API; точные собственные объекты удалены. Сертификатный/DN online, late failure и history restore остаются открытыми. [Отчёт](ux-ui/acceptance/ipsec-mode-config-2026-10-09.json), [журнал](ux-ui-progress.md).
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# План реализации UX/UI по пятичасовым окнам
|
# План реализации UX/UI по пятичасовым окнам
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-14: Selectel S3 принят228–229:2 real roundtrip objects и12 unique compiled backup API/owned SQL сценариев, включая upload denial/local fallback/sync/delete. Все собственные DB/roles/files/current objects очищены; enabled versioning обработан отдельно —8versions+8delete markers удалены из3 own prefixes, remaining0. Keys не в Git/reports, private env удалены. Native export — HTTP fixture, CHR contacts0; real native backup/restore/live UI остаются230. Backend ux-regression222/frontend ux-routing218; этап12.5 открыт.
|
Актуальное продолжение 2026-10-14: блок230 — исправлена подмена нативного экспорта частичным синтетическим скриптом. Реальные конфигурации обоих CHR выгружены в Selectel S3 и скачаны; ограниченный restore собственного bridge подтверждён RouterOS. Исходные файлы/bridge сохранены, собственные DB/roles/объекты удалены;6 versions+6 delete markers очищены. Live UI1440/390 с real Portal/API принят без изменения storage settings. Backend ux-backup230/frontend ux-routing218. Далее231 — чтение файлов>=61439 байт; S3 retention/смена bucket и расширенная приёмка открыты. Этап12.5 открыт.
|
||||||
|
|
||||||
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-14. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-14. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
|
||||||
|
|
||||||
|
|||||||
+16
-2
@@ -1,6 +1,6 @@
|
|||||||
# UX/UI: журнал реализации
|
# UX/UI: журнал реализации
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-14: Selectel S3 принят228–229:2 real roundtrip objects и12 unique compiled backup API/owned SQL сценариев, включая upload denial/local fallback/sync/delete. Все собственные DB/roles/files/current objects очищены; enabled versioning обработан отдельно —8versions+8delete markers удалены из3 own prefixes, remaining0. Keys не в Git/reports, private env удалены. Native export — HTTP fixture, CHR contacts0; real native backup/restore/live UI остаются230. Backend ux-regression222/frontend ux-routing218; этап12.5 открыт.
|
Актуальное продолжение 2026-10-14: блок230 — исправлена подмена нативного экспорта частичным синтетическим скриптом. Реальные конфигурации обоих CHR выгружены в Selectel S3 и скачаны; ограниченный restore собственного bridge подтверждён RouterOS. Исходные файлы/bridge сохранены, собственные DB/roles/объекты удалены;6 versions+6 delete markers очищены. Live UI1440/390 с real Portal/API принят без изменения storage settings. Backend ux-backup230/frontend ux-routing218. Далее231 — чтение файлов>=61439 байт; S3 retention/смена bucket и расширенная приёмка открыты. Этап12.5 открыт.
|
||||||
|
|
||||||
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
|
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
|
||||||
|
|
||||||
@@ -36,7 +36,21 @@
|
|||||||
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
|
||||||
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
|
||||||
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
|
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
|
||||||
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка12.3.83 в границах журнала. Внешняя12.5: блоки1–229 завершены в записанных scopes, этап открыт. Runtime backend ux-regression222/frontend ux-routing218; backend25/UI52 и дополнительные SQL/UDP222 прошли. |
|
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка12.3.83 в границах журнала. Внешняя12.5: блоки1–230 завершены в записанных scopes, этап открыт. Runtime backend ux-backup230/frontend ux-routing218; backend25/UI52 и дополнительные SQL/UDP222 прошли. |
|
||||||
|
|
||||||
|
## 12.5.230 — нативные резервные копии CHR в S3 и ограниченное восстановление (2026-10-14)
|
||||||
|
|
||||||
|
Нативные POST /console/export и /export без file вернули пустые массивы на обоих CHR. Старый fallback составлял частичный скрипт из нескольких разделов и мог выдавать его за полную резервную копию. Fallback удалён: выполняется однократный /export file со случайным собственным именем, проверяются список/ID/размер/RouterOS header/ошибки разделов/целостность UTF-8, временный файл удаляется с readback. Неполный экспорт или неподтверждённая очистка завершаются ошибкой. Повтор mutations не добавлен.
|
||||||
|
|
||||||
|
8 HTTP-сценариев нового exporter, test:backups, test:ros-file, backend/contracts build и scoped ESLint прошли. Уточнены два существующих let: entry стал const; req остаётся let, чтобы не нарушить обработку уже отменённого запроса. Production image ux-backup230 проверен:500 source files/1060 compiled outputs совпали; main backend развёрнут и healthy, frontend ux-routing218 сохранён. [Сборка](ux-ui/acceptance/backup230-deployment.json).
|
||||||
|
|
||||||
|
После отдельного согласия пользователя реальные native конфигурации194.226.121.150 и104.165.244.7 выгружены через compiled backup API/owned SQL в Selectel S3 (1063/782 bytes), downloads/hash подтверждены. Полное восстановление конфигурации не выполнялось: из native export выделена только команда собственного bridge без ports/addresses; S3 download→upload→import→native readback подтвердили его восстановление. [Native/S3](ux-ui/acceptance/s3-native230.json).
|
||||||
|
|
||||||
|
Первый прогон выполнил backup/restore, но cleanup assertion полного file metadata завершился ошибкой. Для повторного прогона baseline сохранён приватно; у directory исключены только изменяемые creation-time/last-modified, остальные поля и все поля обычных файлов/bridge сравниваются точно. Новый полный прогон passed; причина первого несовпадения по сохранённому baseline не доказана. [Первый результат](ux-ui/acceptance/s3-native230-first.json). Собственные bridge/files/catalog/SQL DB/roles удалены, passwords changes0. Из двух собственных prefixes удалены6 S3 versions+6 delete markers, readback0, bucket settings writes0. [Очистка](ux-ui/acceptance/s3-native230-version-cleanup.json). Private credentials env удалён локально/на Debian.
|
||||||
|
|
||||||
|
Live браузер с real Portal SSO/API, без mocks: /backups и настройки на1440/390, успешные API чтения, page errors0, horizontal overflow0. Первый harness ошибочно ожидал /dashboard; Portal правильно вернул /backups, скорректированный harness прошёл. Настройки хранилища в main не изменялись; S3 save/test и restore через UI этим не приняты. [UI](ux-ui/acceptance/backups230-live-ui.json).
|
||||||
|
|
||||||
|
Ограничение: текущий /file/get отклоняет файлы>=61439 bytes; частичная подмена больше не допускается. Следующий231 — документированный /file/read с порциями, затем проверка смены S3 bucket/endpoint и retention semantics. Общая UI-регрессия повторно не запускалась; этап12.5 открыт.
|
||||||
|
|
||||||
## 12.5.229 — API резервных копий с real S3, owned SQL и очистка версий (2026-10-14)
|
## 12.5.229 — API резервных копий с real S3, owned SQL и очистка версий (2026-10-14)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Оставшаяся внешняя приёмка — 2026-10-14
|
# Оставшаяся внешняя приёмка — 2026-10-14
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-14: Selectel S3 принят228–229:2 real roundtrip objects и12 unique compiled backup API/owned SQL сценариев, включая upload denial/local fallback/sync/delete. Все собственные DB/roles/files/current objects очищены; enabled versioning обработан отдельно —8versions+8delete markers удалены из3 own prefixes, remaining0. Keys не в Git/reports, private env удалены. Native export — HTTP fixture, CHR contacts0; real native backup/restore/live UI остаются230. Backend ux-regression222/frontend ux-routing218; этап12.5 открыт.
|
Актуальное продолжение 2026-10-14: блок230 — исправлена подмена нативного экспорта частичным синтетическим скриптом. Реальные конфигурации обоих CHR выгружены в Selectel S3 и скачаны; ограниченный restore собственного bridge подтверждён RouterOS. Исходные файлы/bridge сохранены, собственные DB/roles/объекты удалены;6 versions+6 delete markers очищены. Live UI1440/390 с real Portal/API принят без изменения storage settings. Backend ux-backup230/frontend ux-routing218. Далее231 — чтение файлов>=61439 байт; S3 retention/смена bucket и расширенная приёмка открыты. Этап12.5 открыт.
|
||||||
|
|
||||||
Это актуальная матрица после 12.5.229. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
|
Это актуальная матрица после 12.5.230. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
|
||||||
|
|
||||||
Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-regression222`, frontend `ux-routing218`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
|
Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-backup230`, frontend `ux-routing218`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
|
||||||
|
|
||||||
## Остаток приёмки по группам — 2026-10-14
|
## Остаток приёмки по группам — 2026-10-14
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
| REST и достоверность данных | Ошибки OSPF/BFD/BGP/GRE/WAN215–221 явные; latest API smoke222 прошёл | Причина единичных native401/500 неизвестна. Короткое независимое наблюдение возможно сейчас; оно не даст SLA |
|
| REST и достоверность данных | Ошибки OSPF/BFD/BGP/GRE/WAN215–221 явные; latest API smoke222 прошёл | Причина единичных native401/500 неизвестна. Короткое независимое наблюдение возможно сейчас; оно не даст SLA |
|
||||||
| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux, private IPv4/ULA и synthetic IPv6/IPFIX; Android full-tunnel/reconnect227 подтверждены пользователем | Windows/macOS/iOS VPN, расширенная Android приёмка, независимые WAN/multi-WAN и public IPv6; public IPv6 на текущем стенде отсутствует |
|
| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux, private IPv4/ULA и synthetic IPv6/IPFIX; Android full-tunnel/reconnect227 подтверждены пользователем | Windows/macOS/iOS VPN, расширенная Android приёмка, независимые WAN/multi-WAN и public IPv6; public IPv6 на текущем стенде отсутствует |
|
||||||
| Масштаб и длительная устойчивость | Контролируемые lock/dedupe/loss/recovery и короткие native проверки | Несколько backend/worker/writers, длительный rekey/REST/traffic, physical disconnect и maximum throughput |
|
| Масштаб и длительная устойчивость | Контролируемые lock/dedupe/loss/recovery и короткие native проверки | Несколько backend/worker/writers, длительный rekey/REST/traffic, physical disconnect и maximum throughput |
|
||||||
| Внешние хранилища и jobs | Локальные API/errors/RBAC/owned SQL; реальные Portal1.3.1/Telegram195–201 и Selectel S3/client/API/ownSQL228–229 | Native S3 backup/restore/live UI/retention; provider/container/Gitea/MaxMind jobs с соответствующими endpoints/аккаунтами; private/paid credentials не выдумывать |
|
| Внешние хранилища и jobs | Локальные API/errors/RBAC/owned SQL; реальные Portal1.3.1/Telegram195–201 и Selectel S3/client/API/ownSQL228–229 | Large native exports, S3 save/test/restore через UI, смена bucket/endpoint и retention; provider/container/Gitea/MaxMind jobs с соответствующими endpoints/аккаунтами; private/paid credentials не выдумывать |
|
||||||
| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP сценарии и точные границы записаны | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/policy/path-length и длительные переходы; BFD-only gate гарантией не стал |
|
| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP сценарии и точные границы записаны | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/policy/path-length и длительные переходы; BFD-only gate гарантией не стал |
|
||||||
| Общий UX, доступность и производительность | Полная UI192 исторически; latest backend25/UI52, scoped contrast/live218 и keyboard206 | Свежая UI226 принята в записанных границах; профильный performance и ручная accessibility/screen-reader приёмка; scoped axe не полный WCAG |
|
| Общий UX, доступность и производительность | Полная UI192 исторически; latest backend25/UI52, scoped contrast/live218 и keyboard206 | Свежая UI226 принята в записанных границах; профильный performance и ручная accessibility/screen-reader приёмка; scoped axe не полный WCAG |
|
||||||
| Границы выпуска | План/доказательства/датированные блоки сохранены; коммиты локальные | Определить требуемые конфигурации и принять результат в этих границах; невыполненные варианты остаются явными ограничениями |
|
| Границы выпуска | План/доказательства/датированные блоки сохранены; коммиты локальные | Определить требуемые конфигурации и принять результат в этих границах; невыполненные варианты остаются явными ограничениями |
|
||||||
|
|||||||
+16
-1
@@ -1,6 +1,6 @@
|
|||||||
# Этап 12: итоговая приёмка UX/UI
|
# Этап 12: итоговая приёмка UX/UI
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-14: Selectel S3 принят228–229:2 real roundtrip objects и12 unique compiled backup API/owned SQL сценариев, включая upload denial/local fallback/sync/delete. Все собственные DB/roles/files/current objects очищены; enabled versioning обработан отдельно —8versions+8delete markers удалены из3 own prefixes, remaining0. Keys не в Git/reports, private env удалены. Native export — HTTP fixture, CHR contacts0; real native backup/restore/live UI остаются230. Backend ux-regression222/frontend ux-routing218; этап12.5 открыт.
|
Актуальное продолжение 2026-10-14: блок230 — исправлена подмена нативного экспорта частичным синтетическим скриптом. Реальные конфигурации обоих CHR выгружены в Selectel S3 и скачаны; ограниченный restore собственного bridge подтверждён RouterOS. Исходные файлы/bridge сохранены, собственные DB/roles/объекты удалены;6 versions+6 delete markers очищены. Live UI1440/390 с real Portal/API принят без изменения storage settings. Backend ux-backup230/frontend ux-routing218. Далее231 — чтение файлов>=61439 байт; S3 retention/смена bucket и расширенная приёмка открыты. Этап12.5 открыт.
|
||||||
|
|
||||||
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
|
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
|
||||||
|
|
||||||
@@ -4085,3 +4085,18 @@ Runner вызвал существующий compiled production S3 client в о
|
|||||||
|
|
||||||
Следующий230 — real S3 с native backup/ограниченным restore и live UI в согласованном scope; отдельно проверить смену bucket/endpoint при имеющемся каталоге и retention/version semantics. Остальные группы матрицы остаются открытыми. Runtime backend ux-regression222/frontend ux-routing218. Этап12.5 не завершён.
|
Следующий230 — real S3 с native backup/ограниченным restore и live UI в согласованном scope; отдельно проверить смену bucket/endpoint при имеющемся каталоге и retention/version semantics. Остальные группы матрицы остаются открытыми. Runtime backend ux-regression222/frontend ux-routing218. Этап12.5 не завершён.
|
||||||
|
|
||||||
|
|
||||||
|
## 12.5.230 — нативные резервные копии CHR в S3 и ограниченное восстановление (2026-10-14)
|
||||||
|
|
||||||
|
Нативные POST /console/export и /export без file вернули пустые массивы на обоих CHR. Старый fallback составлял частичный скрипт из нескольких разделов и мог выдавать его за полную резервную копию. Fallback удалён: выполняется однократный /export file со случайным собственным именем, проверяются список/ID/размер/RouterOS header/ошибки разделов/целостность UTF-8, временный файл удаляется с readback. Неполный экспорт или неподтверждённая очистка завершаются ошибкой. Повтор mutations не добавлен.
|
||||||
|
|
||||||
|
8 HTTP-сценариев нового exporter, test:backups, test:ros-file, backend/contracts build и scoped ESLint прошли. Уточнены два существующих let: entry стал const; req остаётся let, чтобы не нарушить обработку уже отменённого запроса. Production image ux-backup230 проверен:500 source files/1060 compiled outputs совпали; main backend развёрнут и healthy, frontend ux-routing218 сохранён. [Сборка](ux-ui/acceptance/backup230-deployment.json).
|
||||||
|
|
||||||
|
После отдельного согласия пользователя реальные native конфигурации194.226.121.150 и104.165.244.7 выгружены через compiled backup API/owned SQL в Selectel S3 (1063/782 bytes), downloads/hash подтверждены. Полное восстановление конфигурации не выполнялось: из native export выделена только команда собственного bridge без ports/addresses; S3 download→upload→import→native readback подтвердили его восстановление. [Native/S3](ux-ui/acceptance/s3-native230.json).
|
||||||
|
|
||||||
|
Первый прогон выполнил backup/restore, но cleanup assertion полного file metadata завершился ошибкой. Для повторного прогона baseline сохранён приватно; у directory исключены только изменяемые creation-time/last-modified, остальные поля и все поля обычных файлов/bridge сравниваются точно. Новый полный прогон passed; причина первого несовпадения по сохранённому baseline не доказана. [Первый результат](ux-ui/acceptance/s3-native230-first.json). Собственные bridge/files/catalog/SQL DB/roles удалены, passwords changes0. Из двух собственных prefixes удалены6 S3 versions+6 delete markers, readback0, bucket settings writes0. [Очистка](ux-ui/acceptance/s3-native230-version-cleanup.json). Private credentials env удалён локально/на Debian.
|
||||||
|
|
||||||
|
Live браузер с real Portal SSO/API, без mocks: /backups и настройки на1440/390, успешные API чтения, page errors0, horizontal overflow0. Первый harness ошибочно ожидал /dashboard; Portal правильно вернул /backups, скорректированный harness прошёл. Настройки хранилища в main не изменялись; S3 save/test и restore через UI этим не приняты. [UI](ux-ui/acceptance/backups230-live-ui.json).
|
||||||
|
|
||||||
|
Ограничение: текущий /file/get отклоняет файлы>=61439 bytes; частичная подмена больше не допускается. Следующий231 — документированный /file/read с порциями, затем проверка смены S3 bucket/endpoint и retention semantics. Общая UI-регрессия повторно не запускалась; этап12.5 открыт.
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Тестовый сервер UX/UI — 2026-10-10
|
# Тестовый сервер UX/UI — 2026-10-10
|
||||||
|
|
||||||
Актуальное продолжение 2026-10-14: Selectel S3 принят228–229:2 real roundtrip objects и12 unique compiled backup API/owned SQL сценариев, включая upload denial/local fallback/sync/delete. Все собственные DB/roles/files/current objects очищены; enabled versioning обработан отдельно —8versions+8delete markers удалены из3 own prefixes, remaining0. Keys не в Git/reports, private env удалены. Native export — HTTP fixture, CHR contacts0; real native backup/restore/live UI остаются230. Backend ux-regression222/frontend ux-routing218; этап12.5 открыт.
|
Актуальное продолжение 2026-10-14: блок230 — исправлена подмена нативного экспорта частичным синтетическим скриптом. Реальные конфигурации обоих CHR выгружены в Selectel S3 и скачаны; ограниченный restore собственного bridge подтверждён RouterOS. Исходные файлы/bridge сохранены, собственные DB/roles/объекты удалены;6 versions+6 delete markers очищены. Live UI1440/390 с real Portal/API принят без изменения storage settings. Backend ux-backup230/frontend ux-routing218. Далее231 — чтение файлов>=61439 байт; S3 retention/смена bucket и расширенная приёмка открыты. Этап12.5 открыт.
|
||||||
|
|
||||||
| Параметр | Проверенное состояние |
|
| Параметр | Проверенное состояние |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-14",
|
||||||
|
"block": "12.5.230",
|
||||||
|
"backendSourceMatched": 500,
|
||||||
|
"compiledMatched": 1060,
|
||||||
|
"frontendChanged": false,
|
||||||
|
"services": {
|
||||||
|
"backend": {
|
||||||
|
"image": "mmapp-test/backend:ux-backup230",
|
||||||
|
"imageId": "sha256:796aef2b62f03218e4e07f15cedb7bc14613494b4ec6e61af85480703a4d5e37",
|
||||||
|
"running": true,
|
||||||
|
"health": "healthy"
|
||||||
|
},
|
||||||
|
"frontend": {
|
||||||
|
"image": "mmapp-test/frontend:ux-routing218",
|
||||||
|
"imageId": "sha256:ec658b9da88c19d60987be5bfc0c15fb339a0310d7a42dc761b40a70a3428c5a",
|
||||||
|
"running": true,
|
||||||
|
"health": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-14",
|
||||||
|
"block": "12.5.230",
|
||||||
|
"kinds": [
|
||||||
|
{
|
||||||
|
"kind": "backend",
|
||||||
|
"matched": 500
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-14",
|
||||||
|
"block": "12.5.230",
|
||||||
|
"realPortalLogin": true,
|
||||||
|
"noMocks": true,
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"width": 1440,
|
||||||
|
"settingsVisible": true,
|
||||||
|
"successfulApiRead": true,
|
||||||
|
"horizontalOverflow": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 390,
|
||||||
|
"settingsVisible": true,
|
||||||
|
"successfulApiRead": true,
|
||||||
|
"horizontalOverflow": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pageErrors": 0,
|
||||||
|
"storageMutations": 0,
|
||||||
|
"nativeRestoreViaUI": false
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-14",
|
||||||
|
"block": "12.5.230",
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"host": "194.226.121.150",
|
||||||
|
"passed": true,
|
||||||
|
"bytes": 917,
|
||||||
|
"sha256": "994c5f06cfc65feb55ecc7e98548d47438fa1abede1e18b189d0a17ca6fd3990",
|
||||||
|
"native": true,
|
||||||
|
"filesBaselineMatched": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"host": "104.165.244.7",
|
||||||
|
"passed": true,
|
||||||
|
"bytes": 782,
|
||||||
|
"sha256": "43b7fbfd82651da91bd92d8e2c8140d727db7069338080aebb0803bc3782d375",
|
||||||
|
"native": true,
|
||||||
|
"filesBaselineMatched": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configurationWrites": 0,
|
||||||
|
"passed": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-14",
|
||||||
|
"block": "12.5.230",
|
||||||
|
"prefix": "mmapp-acceptance/a395b3db-8f34-4335-ae67-9a635f5e7c6a",
|
||||||
|
"passed": false,
|
||||||
|
"backups": [
|
||||||
|
{
|
||||||
|
"serverId": 1,
|
||||||
|
"host": "194.226.121.150",
|
||||||
|
"bytes": 1063,
|
||||||
|
"sha256": "efa4f3e6c86cf9bb3d4941a472018bbb82c945bef7df9e93321191034eab059b",
|
||||||
|
"native": true,
|
||||||
|
"s3Only": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"serverId": 2,
|
||||||
|
"host": "104.165.244.7",
|
||||||
|
"bytes": 782,
|
||||||
|
"sha256": "47f1f0da003c31cdce61f84ff1f30c49a119f319042d7eca54de77d6ed800b5c",
|
||||||
|
"native": true,
|
||||||
|
"s3Only": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checks": [
|
||||||
|
{
|
||||||
|
"name": "two-native-backups-S3-and-download",
|
||||||
|
"passed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "limited-restore-payload-only-native-marker-command",
|
||||||
|
"passed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "S3-download-upload-import-native-readback",
|
||||||
|
"passed": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cleanup": [
|
||||||
|
{
|
||||||
|
"host": "194.226.121.150",
|
||||||
|
"failureName": "AssertionError"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"host": "104.165.244.7",
|
||||||
|
"originalFilesAndBridgesMatched": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"passwordChanges": 0,
|
||||||
|
"currentS3ObjectsRemaining": 0,
|
||||||
|
"bounds": [
|
||||||
|
"Full .rsc import onto a configured router was not attempted; restore only own unused bridge",
|
||||||
|
"Current objects removed; own S3 versions require separate cleanup",
|
||||||
|
"Files>=61439 bytes explicitly rejected pending chunked-reader work"
|
||||||
|
],
|
||||||
|
"ownedDatabaseAndRoleRemoved": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"at": "2026-10-14T03:27:26.571Z",
|
||||||
|
"bucketConfigurationWrites": 0,
|
||||||
|
"prefixes": [
|
||||||
|
{
|
||||||
|
"prefix": "mmapp-acceptance/a395b3db-8f34-4335-ae67-9a635f5e7c6a",
|
||||||
|
"versionsBefore": 3,
|
||||||
|
"deleteMarkersBefore": 3,
|
||||||
|
"removed": 6,
|
||||||
|
"versionsAndMarkersRemaining": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"prefix": "mmapp-acceptance/f9051fac-f481-44cf-9410-e7ff153da0ed",
|
||||||
|
"versionsBefore": 3,
|
||||||
|
"deleteMarkersBefore": 3,
|
||||||
|
"removed": 6,
|
||||||
|
"versionsAndMarkersRemaining": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"passed": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"date": "2026-10-14",
|
||||||
|
"block": "12.5.230",
|
||||||
|
"prefix": "mmapp-acceptance/f9051fac-f481-44cf-9410-e7ff153da0ed",
|
||||||
|
"passed": true,
|
||||||
|
"backups": [
|
||||||
|
{
|
||||||
|
"serverId": 1,
|
||||||
|
"host": "194.226.121.150",
|
||||||
|
"bytes": 1063,
|
||||||
|
"sha256": "900d873740150831deb71cdf45dfda1a22ccdc123f6e5167589a07f49e81f62e",
|
||||||
|
"native": true,
|
||||||
|
"s3Only": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"serverId": 2,
|
||||||
|
"host": "104.165.244.7",
|
||||||
|
"bytes": 782,
|
||||||
|
"sha256": "94050abb7b5da6a2a579b3f4c322f17cf56a7f48bdcaf1bb71ea59b7fb87c6a9",
|
||||||
|
"native": true,
|
||||||
|
"s3Only": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checks": [
|
||||||
|
{
|
||||||
|
"name": "two-native-backups-S3-and-download",
|
||||||
|
"passed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "limited-restore-payload-only-native-marker-command",
|
||||||
|
"passed": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "S3-download-upload-import-native-readback",
|
||||||
|
"passed": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cleanup": [
|
||||||
|
{
|
||||||
|
"host": "194.226.121.150",
|
||||||
|
"originalFilesAndBridgesMatched": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"host": "104.165.244.7",
|
||||||
|
"originalFilesAndBridgesMatched": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"passwordChanges": 0,
|
||||||
|
"currentS3ObjectsRemaining": 0,
|
||||||
|
"bounds": [
|
||||||
|
"Full .rsc import onto a configured router was not attempted; restore only own unused bridge",
|
||||||
|
"Current objects removed; own S3 versions require separate cleanup",
|
||||||
|
"Files>=61439 bytes explicitly rejected pending chunked-reader work"
|
||||||
|
],
|
||||||
|
"ownedDatabaseAndRoleRemoved": true
|
||||||
|
}
|
||||||
Reference in new issue
Block a user