@hogsend/db 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -197,6 +197,13 @@
|
|
|
197
197
|
"when": 1781546768414,
|
|
198
198
|
"tag": "0027_wealthy_aqueduct",
|
|
199
199
|
"breakpoints": true
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"idx": 28,
|
|
203
|
+
"version": "7",
|
|
204
|
+
"when": 1782060240750,
|
|
205
|
+
"tag": "0028_eminent_war_machine",
|
|
206
|
+
"breakpoints": true
|
|
200
207
|
}
|
|
201
208
|
]
|
|
202
209
|
}
|
package/package.json
CHANGED
package/src/schema/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from "./journey-configs.js";
|
|
|
18
18
|
export * from "./journey-logs.js";
|
|
19
19
|
export * from "./journey-states.js";
|
|
20
20
|
export * from "./link-clicks.js";
|
|
21
|
+
export * from "./links.js";
|
|
21
22
|
export * from "./provider-credentials.js";
|
|
22
23
|
export * from "./relations.js";
|
|
23
24
|
export * from "./tracked-links.js";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { index, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
2
|
+
import { timestamps } from "./_shared.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Operator-owned / standalone tracked links — the managed surface behind the
|
|
6
|
+
* Studio "Links" view and any non-email channel (Discord, SMS, share links). A
|
|
7
|
+
* `links` row is the durable, named identity of a tracked link; the click
|
|
8
|
+
* counter + per-hit `link_clicks` live in `tracked_links` (which points back
|
|
9
|
+
* here via `link_id`). Email's own per-send rewritten links do NOT create a
|
|
10
|
+
* `links` row (they keep `tracked_links.link_id` NULL) — email stays a separate
|
|
11
|
+
* consumer of the same click spine.
|
|
12
|
+
*/
|
|
13
|
+
export const links = pgTable(
|
|
14
|
+
"links",
|
|
15
|
+
{
|
|
16
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
17
|
+
originalUrl: text("original_url").notNull(),
|
|
18
|
+
// "personal" = single-recipient, identity-bearing (carries `distinctId`, may
|
|
19
|
+
// mint a SINGLE-USE `hs_t`); "public" = shareable, NEVER carries a person
|
|
20
|
+
// token (campaign/UTM attribution only). Default "public" — the safe default.
|
|
21
|
+
type: text("type").notNull().default("public"),
|
|
22
|
+
// Operator-facing name (Studio list).
|
|
23
|
+
label: text("label"),
|
|
24
|
+
// UTM-style campaign grouping for public links.
|
|
25
|
+
campaign: text("campaign"),
|
|
26
|
+
// Originating channel: "studio" | "discord" | "sms" | … (open string).
|
|
27
|
+
source: text("source").notNull(),
|
|
28
|
+
// The canonical contact key a click should stitch the visitor's anon session
|
|
29
|
+
// into — set ONLY for personal links; NULL for public/broadcast (a shareable
|
|
30
|
+
// link must never carry a person).
|
|
31
|
+
distinctId: text("distinct_id"),
|
|
32
|
+
// The admin actor who minted it (mirrors api_keys.createdBy).
|
|
33
|
+
createdBy: text("created_by"),
|
|
34
|
+
// Soft-delete: archive (not hard-delete) so historical `link_clicks` survive
|
|
35
|
+
// (the `tracked_links.link_id` FK is ON DELETE set null as a backstop).
|
|
36
|
+
archivedAt: timestamp("archived_at", { withTimezone: true }),
|
|
37
|
+
...timestamps,
|
|
38
|
+
},
|
|
39
|
+
(table) => [
|
|
40
|
+
index("links_source_idx").on(table.source),
|
|
41
|
+
index("links_campaign_idx").on(table.campaign),
|
|
42
|
+
index("links_created_at_idx").on(table.createdAt),
|
|
43
|
+
],
|
|
44
|
+
);
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "drizzle-orm/pg-core";
|
|
10
10
|
import { timestamps } from "./_shared.js";
|
|
11
11
|
import { emailSends } from "./email-sends.js";
|
|
12
|
+
import { links } from "./links.js";
|
|
12
13
|
|
|
13
14
|
export const trackedLinks = pgTable(
|
|
14
15
|
"tracked_links",
|
|
@@ -21,6 +22,13 @@ export const trackedLinks = pgTable(
|
|
|
21
22
|
emailSendId: uuid("email_send_id").references(() => emailSends.id, {
|
|
22
23
|
onDelete: "cascade",
|
|
23
24
|
}),
|
|
25
|
+
// The managed `links` row this click-counter belongs to, when the link was
|
|
26
|
+
// minted via `mintLink` (Studio / Discord / share links). NULL for email's
|
|
27
|
+
// per-send rewritten links (they resolve identity from `email_sends`). ON
|
|
28
|
+
// DELETE set null so archiving/removing a `links` row keeps the click spine.
|
|
29
|
+
linkId: uuid("link_id").references(() => links.id, {
|
|
30
|
+
onDelete: "set null",
|
|
31
|
+
}),
|
|
24
32
|
// Subject of a stitch-bearing NON-email link: the canonical contact key the
|
|
25
33
|
// click should fold the visitor's anon session into. NULL for broadcast
|
|
26
34
|
// links (Discord/referral default) — broadcast links are tracked for click
|
|
@@ -46,5 +54,8 @@ export const trackedLinks = pgTable(
|
|
|
46
54
|
}),
|
|
47
55
|
...timestamps,
|
|
48
56
|
},
|
|
49
|
-
(table) => [
|
|
57
|
+
(table) => [
|
|
58
|
+
index("tracked_links_email_send_id_idx").on(table.emailSendId),
|
|
59
|
+
index("tracked_links_link_id_idx").on(table.linkId),
|
|
60
|
+
],
|
|
50
61
|
);
|