fix(usage): introduce consented v4 without changing historical reports
This commit is contained in:
@@ -539,6 +539,9 @@ class UsageService {
|
||||
return value;
|
||||
}
|
||||
async deliver(state) {
|
||||
// A lost receipt may mean this packet is already stored by the collector.
|
||||
// Preserve its original schema, date and payload across binary upgrades;
|
||||
// only re-sign transport metadata, never rebuild under the same packet ID.
|
||||
const packet = JSON.parse(state.pending_packet);
|
||||
if (
|
||||
packet.action !== 'delete' &&
|
||||
@@ -564,31 +567,14 @@ class UsageService {
|
||||
});
|
||||
return null;
|
||||
}
|
||||
const identity = {
|
||||
public_key: state.public_key,
|
||||
private_key: this.decrypt(state.private_key_encrypted)
|
||||
};
|
||||
let envelope;
|
||||
try {
|
||||
envelope = signPacket(packet, identity, new Date(this.now()));
|
||||
} catch (error) {
|
||||
// A report queued under a catalog this build no longer ships — the
|
||||
// upgrade replaced a key in the same wire version — fails local
|
||||
// validation before anything is sent, and retrying cannot repair it.
|
||||
// Left as it was it blocked every operation behind it for good. A
|
||||
// report's payload is derived state, so rebuild it from the current
|
||||
// snapshot in place. The packet ID and sequence are kept: a re-signed
|
||||
// retry must reuse them so a lost acknowledgement does not duplicate
|
||||
// data. Reports only — a stale registration, deletion or command is a
|
||||
// genuine conflict and keeps the handling below.
|
||||
if (error.code !== 'INVALID_PACKET' || packet.action !== 'report') throw error;
|
||||
packet.payload = await this.snapshot(packet.schema_version);
|
||||
state.pending_packet = JSON.stringify(packet);
|
||||
await this.db('product_usage_state')
|
||||
.where({ id: 1, status: 'active' })
|
||||
.update({ pending_packet: state.pending_packet });
|
||||
envelope = signPacket(packet, identity, new Date(this.now()));
|
||||
}
|
||||
const envelope = signPacket(
|
||||
packet,
|
||||
{
|
||||
public_key: state.public_key,
|
||||
private_key: this.decrypt(state.private_key_encrypted)
|
||||
},
|
||||
new Date(this.now())
|
||||
);
|
||||
// Last check before anything leaves. The guard at the top of this
|
||||
// method runs before the binding lookup above, which is asynchronous —
|
||||
// so a withdrawal that COMPLETED during it would previously still have
|
||||
@@ -974,7 +960,7 @@ class UsageService {
|
||||
generated_at: now,
|
||||
features: expanded,
|
||||
gallery_layouts: [...layouts].sort(),
|
||||
...(version === 'usage.v3' ? { inventory: await require('./inventorySnapshot').inventorySnapshot(this.db) } : {})
|
||||
...(['usage.v3', 'usage.v4'].includes(version) ? { inventory: await require('./inventorySnapshot').inventorySnapshot(this.db) } : {})
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user