From e80382c5a079bb77121841f4f5869f5dbcfc6d8e Mon Sep 17 00:00:00 2001 From: Kenny Stimson Date: Thu, 4 Jun 2026 13:19:42 -0700 Subject: [PATCH] Add IAC for webtexting db infrastructure --- app_config.php | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/app_config.php b/app_config.php index f79747f..40e784c 100644 --- a/app_config.php +++ b/app_config.php @@ -153,6 +153,12 @@ $apps[$x]['db'][$y]['fields'][$z]['name'] = "direction"; $apps[$x]['db'][$y]['fields'][$z]['type'] = "text"; $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ""; +$z++; +$apps[$x]['db'][$y]['fields'][$z]['name'] = "delivered"; +$apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = "boolean"; +$apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = "boolean"; +$apps[$x]['db'][$y]['fields'][$z]['type']['mysql'] = "boolean"; +$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "carrier delivery status: true=delivered, false=failed, NULL=pending/unknown"; $y++; @@ -251,3 +257,88 @@ $apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = 'date'; $apps[$x]['db'][$y]['fields'][$z]['type']['mysql'] = 'date'; $apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = ""; +$z++; +$apps[$x]['db'][$y]['fields'][$z]['name'] = "thread_uuid"; +$apps[$x]['db'][$y]['fields'][$z]['type']['pgsql'] = "uuid"; +$apps[$x]['db'][$y]['fields'][$z]['type']['sqlite'] = "text"; +$apps[$x]['db'][$y]['fields'][$z]['type']['mysql'] = "char(36)"; +$apps[$x]['db'][$y]['fields'][$z]['description']['en-us'] = "stable per-thread identifier; DEFAULT uuid_generate_v4() + UNIQUE applied via raw PHP below (not DSL-expressible)"; + +//webtexting_threads.thread_uuid + webtexting_threads_last_seen + webtexting_message_templates +//schema reconciliation. dsl above declares the thread_uuid column but can't express +//DEFAULT uuid_generate_v4() or UNIQUE constraints; raw migrations encoded here. +//pgsql-specific. uuid_generate_v4() requires uuid-ossp extension. + + $default_applied = $database->select( + "SELECT 1 FROM information_schema.columns + WHERE table_name = 'webtexting_threads' + AND column_name = 'thread_uuid' + AND column_default LIKE '%uuid_generate_v4%'", + [], 'column' + ); + $unique_applied = $database->select( + "SELECT 1 FROM pg_constraint WHERE conname = 'webtexting_threads_thread_uuid_key'", + [], 'column' + ); + + if (!$default_applied || !$unique_applied) { + echo "webtexting: applying webtexting_threads.thread_uuid schema migration (one-time post-install)...
"; + try { + $database->execute("BEGIN"); + $database->execute("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\""); + + // DSL's column-create runs AFTER this raw PHP block, so add the column + // defensively here. ADD COLUMN IF NOT EXISTS is the idempotent form. + $database->execute("ALTER TABLE webtexting_threads ADD COLUMN IF NOT EXISTS thread_uuid uuid"); + + if (!$default_applied) { + $database->execute("UPDATE webtexting_threads SET thread_uuid = uuid_generate_v4() WHERE thread_uuid IS NULL"); + $database->execute("ALTER TABLE webtexting_threads ALTER COLUMN thread_uuid SET DEFAULT uuid_generate_v4()"); + } + + if (!$unique_applied) { + $database->execute("ALTER TABLE webtexting_threads ADD CONSTRAINT webtexting_threads_thread_uuid_key UNIQUE (thread_uuid)"); + } + + $database->execute("COMMIT"); + echo "webtexting: thread_uuid schema migration applied.
"; + } catch (Throwable $e) { + $database->execute("ROLLBACK"); + echo "webtexting: thread_uuid schema migration FAILED: " . htmlspecialchars($e->getMessage()) . "
"; + echo "webtexting: apply manually: BEGIN; CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"; ALTER TABLE webtexting_threads ADD COLUMN IF NOT EXISTS thread_uuid uuid; UPDATE webtexting_threads SET thread_uuid = uuid_generate_v4() WHERE thread_uuid IS NULL; ALTER TABLE webtexting_threads ALTER COLUMN thread_uuid SET DEFAULT uuid_generate_v4(); ALTER TABLE webtexting_threads ADD CONSTRAINT webtexting_threads_thread_uuid_key UNIQUE (thread_uuid); COMMIT;
"; + } + } + +//webtexting_threads_last_seen — read-state tracking table for the conversation viewer. +//pgsql-specific (uuid, timestamptz). idempotent via IF NOT EXISTS. + $database->execute("CREATE TABLE IF NOT EXISTS webtexting_threads_last_seen ( + thread_uuid uuid NOT NULL, + extension_uuid uuid NOT NULL, + domain_uuid uuid NOT NULL, + timestamp timestamptz, + last_seen_uuid uuid NOT NULL DEFAULT uuid_generate_v4(), + CONSTRAINT webtexting_threads_last_seen_pkey PRIMARY KEY (last_seen_uuid) + )"); + +//webtexting_message_templates — canned-reply / template storage. +//pgsql-specific (uuid, timestamptz). idempotent via IF NOT EXISTS. +//note: pkey constraint name has misspelled plural ("messages_templates") — replicated +//verbatim to match production schema on sfo; cosmetic typo, harmless. + $database->execute("CREATE TABLE IF NOT EXISTS webtexting_message_templates ( + email_template_uuid uuid NOT NULL DEFAULT uuid_generate_v4(), + domain_uuid uuid, + template_language text, + template_category text, + template_subcategory text, + template_subject text, + template_body text, + template_type text, + template_enabled text, + template_description text, + insert_date timestamptz, + insert_user uuid, + update_date timestamptz, + update_user uuid, + template_name text, + CONSTRAINT webtexting_messages_templates_pkey PRIMARY KEY (email_template_uuid) + )");