Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ typings/
.serverless

# FuseBox cache
.fusebox/
.fusebox/src/config/fresh.js
31 changes: 0 additions & 31 deletions src/config/fresh.js

This file was deleted.

60 changes: 49 additions & 11 deletions src/updaters/community.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ async function createCommunity (db, payload, blockInfo) {
// create community
await tx.communities.insert(communityData)

// The creator's network row (below) FK-references users(account), so the creator
// MUST exist in users first. On a from-genesis index of a fresh DB nobody else has
// created that row (prod historically relied on backend sign-ups pre-populating
// users), the network insert violated network_account_id_fkey, and the swallowed
// rollback left the community missing entirely — the next netlink then crash-looped
// the process. Same idempotent upsert as netlink (relies on the users(account) PK).
await tx.instance.none(
`INSERT INTO users (account, created_block, created_tx, created_eos_account, created_at)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT DO NOTHING`,
[
payload.data.creator,
blockInfo.blockNumber,
payload.transactionId,
payload.authorization[0].actor,
toUTC(blockInfo.timestamp)
]
)

const roleData = {
community_id: symbol,
name: 'member',
Expand Down Expand Up @@ -303,16 +322,17 @@ async function upsertObjective (db, payload, blockInfo, _context) {
let data = {
community_id: payload.data.community_id,
creator_id: payload.data.editor,
description: payload.data.description,
created_block: blockInfo.blockNumber,
created_tx: payload.transactionId,
created_at: toUTC(blockInfo.timestamp),
created_eos_account: payload.authorization[0].actor
description: payload.data.description
}

if (payload.data.objective_id > 0) {
// Update path: an idempotent upsert by id (massive's save() emits an UPDATE
// when the pk is present).
// when the pk is present). It must NOT touch the created_* provenance: the
// create-path replay guard below keys on created_tx, so rewriting it on edit
// would make a later replay re-execute the create (duplicate row / pkey
// crash). The 20260702120000 migration comment documents the old rewrite
// only to explain why no unique index on created_tx was added — it is not a
// rationale for keeping it.
data = Object.assign(data, { id: payload.data.objective_id })
return db.objectives
.save(data)
Expand All @@ -321,6 +341,15 @@ async function upsertObjective (db, payload, blockInfo, _context) {
)
}

// Create-path provenance — written once, on the create, and never rewritten
// by edits, so the replay guard below keeps recognizing this create.
data = Object.assign(data, {
created_block: blockInfo.blockNumber,
created_tx: payload.transactionId,
created_at: toUTC(blockInfo.timestamp),
created_eos_account: payload.authorization[0].actor
})

// Idempotency (create path only): with no objective_id the payload carries no key, so
// inserting without a guard would add a fresh row on every replay, duplicating the
// objective. Keyed on created_tx — a create runs in exactly one on-chain tx, so a
Expand Down Expand Up @@ -396,24 +425,33 @@ function upsertAction (db, payload, blockInfo, _context) {
verifications: payload.data.verifications,
verification_type: payload.data.verification_type,
deadline: payload.data.deadline > 0 ? toUTC(deadlineDateTime) : null,
created_block: blockInfo.blockNumber,
created_tx: payload.transactionId,
created_at: toUTC(blockInfo.timestamp),
created_eos_account: payload.authorization[0].actor,
has_proof_photo: payload.data.has_proof_photo === 1,
has_proof_code: payload.data.has_proof_code === 1,
photo_proof_instructions: payload.data.photo_proof_instructions === '' ? null : payload.data.photo_proof_instructions,
image: payload.data.image === '' ? null : payload.data.image
}

if (payload.data.action_id > 0) {
// Update
// Update — deliberately does NOT touch the created_* provenance: the
// create-path replay guard below keys on created_tx, so rewriting it on
// edit would make a later replay re-execute the create (duplicate row /
// pkey crash). See upsertObjective for the full rationale.
data = Object.assign(data, {
id: payload.data.action_id,
usages_left: payload.data.usages_left,
is_completed: payload.data.is_completed === 1
})
} else {
// Create-path provenance — written once, on the create, and never
// rewritten by edits, so the replay guard below keeps recognizing this
// create.
data = Object.assign(data, {
created_block: blockInfo.blockNumber,
created_tx: payload.transactionId,
created_at: toUTC(blockInfo.timestamp),
created_eos_account: payload.authorization[0].actor
})

// Idempotency (create path only): with no action_id the payload carries no key, so
// inserting without a guard would add a fresh row on every replay, duplicating the
// action (the audit found action ids 405+406 sharing one created_tx). Keyed on
Expand Down
Loading