@nexpress/core 0.1.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.
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/audit-54XLVCWD.js +14 -0
- package/dist/audit-54XLVCWD.js.map +1 -0
- package/dist/auth.d.ts +640 -0
- package/dist/auth.js +94 -0
- package/dist/auth.js.map +1 -0
- package/dist/can-YLUHRJAB.js +19 -0
- package/dist/can-YLUHRJAB.js.map +1 -0
- package/dist/chunk-2G264RCD.js +68 -0
- package/dist/chunk-2G264RCD.js.map +1 -0
- package/dist/chunk-2YDGE7YX.js +92 -0
- package/dist/chunk-2YDGE7YX.js.map +1 -0
- package/dist/chunk-473S4TER.js +538 -0
- package/dist/chunk-473S4TER.js.map +1 -0
- package/dist/chunk-4ZLMEKFX.js +18 -0
- package/dist/chunk-4ZLMEKFX.js.map +1 -0
- package/dist/chunk-55FU6WED.js +179 -0
- package/dist/chunk-55FU6WED.js.map +1 -0
- package/dist/chunk-6YI5K2TI.js +1959 -0
- package/dist/chunk-6YI5K2TI.js.map +1 -0
- package/dist/chunk-BHK3AD3Q.js +41 -0
- package/dist/chunk-BHK3AD3Q.js.map +1 -0
- package/dist/chunk-CRUQBZUF.js +39 -0
- package/dist/chunk-CRUQBZUF.js.map +1 -0
- package/dist/chunk-CTSQ7BRI.js +175 -0
- package/dist/chunk-CTSQ7BRI.js.map +1 -0
- package/dist/chunk-DK2JBJH7.js +81 -0
- package/dist/chunk-DK2JBJH7.js.map +1 -0
- package/dist/chunk-DP2PREDU.js +597 -0
- package/dist/chunk-DP2PREDU.js.map +1 -0
- package/dist/chunk-EQ2Z3KMD.js +24 -0
- package/dist/chunk-EQ2Z3KMD.js.map +1 -0
- package/dist/chunk-FZ7O6DWI.js +305 -0
- package/dist/chunk-FZ7O6DWI.js.map +1 -0
- package/dist/chunk-ISLYFQWL.js +1270 -0
- package/dist/chunk-ISLYFQWL.js.map +1 -0
- package/dist/chunk-JJL74ZPK.js +68 -0
- package/dist/chunk-JJL74ZPK.js.map +1 -0
- package/dist/chunk-JKXAPSU4.js +24 -0
- package/dist/chunk-JKXAPSU4.js.map +1 -0
- package/dist/chunk-KU5M27ZC.js +24 -0
- package/dist/chunk-KU5M27ZC.js.map +1 -0
- package/dist/chunk-LSHHRDVR.js +34 -0
- package/dist/chunk-LSHHRDVR.js.map +1 -0
- package/dist/chunk-M43PGOQY.js +715 -0
- package/dist/chunk-M43PGOQY.js.map +1 -0
- package/dist/chunk-MEJAHXIO.js +150 -0
- package/dist/chunk-MEJAHXIO.js.map +1 -0
- package/dist/chunk-NUCGHWCF.js +101 -0
- package/dist/chunk-NUCGHWCF.js.map +1 -0
- package/dist/chunk-OK5HOCQI.js +845 -0
- package/dist/chunk-OK5HOCQI.js.map +1 -0
- package/dist/chunk-OROPGO65.js +13 -0
- package/dist/chunk-OROPGO65.js.map +1 -0
- package/dist/chunk-PPAS4SZR.js +176 -0
- package/dist/chunk-PPAS4SZR.js.map +1 -0
- package/dist/chunk-PPBWRKO2.js +171 -0
- package/dist/chunk-PPBWRKO2.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-QO7LAQZH.js +321 -0
- package/dist/chunk-QO7LAQZH.js.map +1 -0
- package/dist/chunk-QVJ2HCAX.js +225 -0
- package/dist/chunk-QVJ2HCAX.js.map +1 -0
- package/dist/chunk-RIPHIRPP.js +68 -0
- package/dist/chunk-RIPHIRPP.js.map +1 -0
- package/dist/chunk-S27S42QY.js +134 -0
- package/dist/chunk-S27S42QY.js.map +1 -0
- package/dist/chunk-SBCVAC2Z.js +40 -0
- package/dist/chunk-SBCVAC2Z.js.map +1 -0
- package/dist/chunk-TFJ4MKPH.js +694 -0
- package/dist/chunk-TFJ4MKPH.js.map +1 -0
- package/dist/chunk-THX3SHYA.js +75 -0
- package/dist/chunk-THX3SHYA.js.map +1 -0
- package/dist/chunk-UGQSQO5B.js +222 -0
- package/dist/chunk-UGQSQO5B.js.map +1 -0
- package/dist/chunk-V2UNHGAP.js +26 -0
- package/dist/chunk-V2UNHGAP.js.map +1 -0
- package/dist/chunk-VGTPQXNQ.js +2790 -0
- package/dist/chunk-VGTPQXNQ.js.map +1 -0
- package/dist/chunk-VNIHXQ7W.js +194 -0
- package/dist/chunk-VNIHXQ7W.js.map +1 -0
- package/dist/chunk-WV272MPW.js +31 -0
- package/dist/chunk-WV272MPW.js.map +1 -0
- package/dist/chunk-X5KKBOUS.js +26 -0
- package/dist/chunk-X5KKBOUS.js.map +1 -0
- package/dist/chunk-XANPEOJC.js +17 -0
- package/dist/chunk-XANPEOJC.js.map +1 -0
- package/dist/chunk-XPVQIHAQ.js +83 -0
- package/dist/chunk-XPVQIHAQ.js.map +1 -0
- package/dist/chunk-ZCINJSS4.js +75 -0
- package/dist/chunk-ZCINJSS4.js.map +1 -0
- package/dist/community.d.ts +1425 -0
- package/dist/community.js +206 -0
- package/dist/community.js.map +1 -0
- package/dist/config-2GDU7PCK.js +32 -0
- package/dist/config-2GDU7PCK.js.map +1 -0
- package/dist/context-MNZ4QXPC.js +16 -0
- package/dist/context-MNZ4QXPC.js.map +1 -0
- package/dist/db-schema.d.ts +4 -0
- package/dist/db-schema.js +102 -0
- package/dist/db-schema.js.map +1 -0
- package/dist/db.d.ts +7 -0
- package/dist/db.js +117 -0
- package/dist/db.js.map +1 -0
- package/dist/digest-SY42GQSU.js +17 -0
- package/dist/digest-SY42GQSU.js.map +1 -0
- package/dist/errors-5OS3S2J3.js +22 -0
- package/dist/errors-5OS3S2J3.js.map +1 -0
- package/dist/host-OBOI4MJK.js +51 -0
- package/dist/host-OBOI4MJK.js.map +1 -0
- package/dist/i18n.d.ts +301 -0
- package/dist/i18n.js +68 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index-B6-_vr_m.d.ts +590 -0
- package/dist/index-CY55LC0u.d.ts +4722 -0
- package/dist/index-CeiTvwbp.d.ts +168 -0
- package/dist/index-XwP1ET8b.d.ts +61 -0
- package/dist/index.d.ts +2037 -0
- package/dist/index.js +2205 -0
- package/dist/index.js.map +1 -0
- package/dist/job-log-VZXWQUDK.js +24 -0
- package/dist/job-log-VZXWQUDK.js.map +1 -0
- package/dist/jobs.d.ts +4 -0
- package/dist/jobs.js +76 -0
- package/dist/jobs.js.map +1 -0
- package/dist/logger-DqGaOU_j.d.ts +29 -0
- package/dist/logger-S7REWDNE.js +16 -0
- package/dist/logger-S7REWDNE.js.map +1 -0
- package/dist/media.d.ts +5 -0
- package/dist/media.js +41 -0
- package/dist/media.js.map +1 -0
- package/dist/mentions-2IHFVSHW.js +23 -0
- package/dist/mentions-2IHFVSHW.js.map +1 -0
- package/dist/mutes-EWAE5FZR.js +21 -0
- package/dist/mutes-EWAE5FZR.js.map +1 -0
- package/dist/notification-prefs-VPJDU7I6.js +21 -0
- package/dist/notification-prefs-VPJDU7I6.js.map +1 -0
- package/dist/observability.d.ts +156 -0
- package/dist/observability.js +32 -0
- package/dist/observability.js.map +1 -0
- package/dist/profanity-adapter-NU2JQSLX.js +12 -0
- package/dist/profanity-adapter-NU2JQSLX.js.map +1 -0
- package/dist/queue-XE5BC75T.js +14 -0
- package/dist/queue-XE5BC75T.js.map +1 -0
- package/dist/rate-limit.d.ts +99 -0
- package/dist/rate-limit.js +14 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/registry-XIXDEPVI.js +31 -0
- package/dist/registry-XIXDEPVI.js.map +1 -0
- package/dist/reputation-JRL2YQHM.js +11 -0
- package/dist/reputation-JRL2YQHM.js.map +1 -0
- package/dist/routes.d.ts +43 -0
- package/dist/routes.js +12 -0
- package/dist/routes.js.map +1 -0
- package/dist/scheduled-CIQM57HT.js +20 -0
- package/dist/scheduled-CIQM57HT.js.map +1 -0
- package/dist/seo.d.ts +410 -0
- package/dist/seo.js +44 -0
- package/dist/seo.js.map +1 -0
- package/dist/settings-FOBIESPB.js +17 -0
- package/dist/settings-FOBIESPB.js.map +1 -0
- package/dist/spam-adapter-XX3G737Z.js +12 -0
- package/dist/spam-adapter-XX3G737Z.js.map +1 -0
- package/dist/strings-VAE47B2C.js +29 -0
- package/dist/strings-VAE47B2C.js.map +1 -0
- package/dist/templates-IFVJMCJ6.js +12 -0
- package/dist/templates-IFVJMCJ6.js.map +1 -0
- package/dist/types-TlsbXS0T.d.ts +871 -0
- package/package.json +129 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
// src/db/schema/system.ts
|
|
2
|
+
import {
|
|
3
|
+
boolean as boolean2,
|
|
4
|
+
index as index3,
|
|
5
|
+
integer as integer3,
|
|
6
|
+
jsonb as jsonb3,
|
|
7
|
+
pgEnum as pgEnum3,
|
|
8
|
+
pgTable as pgTable3,
|
|
9
|
+
primaryKey as primaryKey2,
|
|
10
|
+
text as text3,
|
|
11
|
+
timestamp as timestamp3,
|
|
12
|
+
unique as unique2,
|
|
13
|
+
uuid as uuid3
|
|
14
|
+
} from "drizzle-orm/pg-core";
|
|
15
|
+
|
|
16
|
+
// src/db/schema/media.ts
|
|
17
|
+
import {
|
|
18
|
+
bigint,
|
|
19
|
+
index as index2,
|
|
20
|
+
integer as integer2,
|
|
21
|
+
jsonb as jsonb2,
|
|
22
|
+
pgEnum as pgEnum2,
|
|
23
|
+
pgTable as pgTable2,
|
|
24
|
+
text as text2,
|
|
25
|
+
timestamp as timestamp2,
|
|
26
|
+
uuid as uuid2
|
|
27
|
+
} from "drizzle-orm/pg-core";
|
|
28
|
+
|
|
29
|
+
// src/db/schema/community.ts
|
|
30
|
+
import {
|
|
31
|
+
boolean,
|
|
32
|
+
index,
|
|
33
|
+
integer,
|
|
34
|
+
jsonb,
|
|
35
|
+
pgEnum,
|
|
36
|
+
pgTable,
|
|
37
|
+
primaryKey,
|
|
38
|
+
text,
|
|
39
|
+
timestamp,
|
|
40
|
+
unique,
|
|
41
|
+
uuid
|
|
42
|
+
} from "drizzle-orm/pg-core";
|
|
43
|
+
var npMemberStatusEnum = pgEnum("np_member_status", [
|
|
44
|
+
"active",
|
|
45
|
+
"pending",
|
|
46
|
+
"suspended",
|
|
47
|
+
"deleted",
|
|
48
|
+
"imported"
|
|
49
|
+
]);
|
|
50
|
+
var npBanScopeEnum = pgEnum("np_ban_scope", ["site", "category", "collection"]);
|
|
51
|
+
var npBanKindEnum = pgEnum("np_ban_kind", ["temporary", "permanent"]);
|
|
52
|
+
var npCommentStatusEnum = pgEnum("np_comment_status", [
|
|
53
|
+
"visible",
|
|
54
|
+
"pending",
|
|
55
|
+
"hidden",
|
|
56
|
+
"deleted"
|
|
57
|
+
]);
|
|
58
|
+
var npMemberRoleScopeEnum = pgEnum("np_member_role_scope", [
|
|
59
|
+
"site",
|
|
60
|
+
"category",
|
|
61
|
+
"collection",
|
|
62
|
+
"thread"
|
|
63
|
+
]);
|
|
64
|
+
var npMembers = pgTable(
|
|
65
|
+
"np_members",
|
|
66
|
+
{
|
|
67
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
68
|
+
handle: text("handle").notNull().unique(),
|
|
69
|
+
email: text("email").notNull().unique(),
|
|
70
|
+
emailVerified: boolean("email_verified").default(false).notNull(),
|
|
71
|
+
/** Argon2 hash. Nullable so SSO-only members can exist without a password. */
|
|
72
|
+
password: text("password"),
|
|
73
|
+
displayName: text("display_name").notNull(),
|
|
74
|
+
avatar: uuid("avatar").references(() => npMedia.id),
|
|
75
|
+
bio: text("bio"),
|
|
76
|
+
status: npMemberStatusEnum("status").default("pending").notNull(),
|
|
77
|
+
reputation: integer("reputation").default(0).notNull(),
|
|
78
|
+
loginAttempts: integer("login_attempts").default(0).notNull(),
|
|
79
|
+
lockUntil: timestamp("lock_until", { withTimezone: true, mode: "date" }),
|
|
80
|
+
/** Bumped to invalidate every issued JWT (logout-everywhere, password reset). */
|
|
81
|
+
tokenVersion: integer("token_version").default(0).notNull(),
|
|
82
|
+
passwordResetTokenHash: text("password_reset_token_hash"),
|
|
83
|
+
passwordResetExpiresAt: timestamp("password_reset_expires_at", {
|
|
84
|
+
withTimezone: true,
|
|
85
|
+
mode: "date"
|
|
86
|
+
}),
|
|
87
|
+
emailVerifyTokenHash: text("email_verify_token_hash"),
|
|
88
|
+
emailVerifyExpiresAt: timestamp("email_verify_expires_at", {
|
|
89
|
+
withTimezone: true,
|
|
90
|
+
mode: "date"
|
|
91
|
+
}),
|
|
92
|
+
/** Plugin-extensible bag — preferences, custom profile fields, etc. */
|
|
93
|
+
meta: jsonb("meta").$type().default({}).notNull(),
|
|
94
|
+
/**
|
|
95
|
+
* Phase 16.3 — per-member notification preferences. Shape:
|
|
96
|
+
* { disabled?: string[] } — kinds the member opted out of
|
|
97
|
+
* { digest?: "off"|"daily"|"weekly" } — email digest cadence (16.4)
|
|
98
|
+
* Empty default = every kind enabled, no email digest.
|
|
99
|
+
*/
|
|
100
|
+
notificationPrefs: jsonb("notification_prefs").$type().default({}).notNull(),
|
|
101
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
102
|
+
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
103
|
+
},
|
|
104
|
+
(table) => [index("np_members_status_idx").on(table.status)]
|
|
105
|
+
);
|
|
106
|
+
var npMemberSessions = pgTable("np_member_sessions", {
|
|
107
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
108
|
+
memberId: uuid("member_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
109
|
+
tokenHash: text("token_hash").notNull(),
|
|
110
|
+
userAgent: text("user_agent"),
|
|
111
|
+
ip: text("ip"),
|
|
112
|
+
expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date" }).notNull(),
|
|
113
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
114
|
+
});
|
|
115
|
+
var npMemberIdentities = pgTable(
|
|
116
|
+
"np_member_identities",
|
|
117
|
+
{
|
|
118
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
119
|
+
memberId: uuid("member_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
120
|
+
provider: text("provider").notNull(),
|
|
121
|
+
subject: text("subject").notNull(),
|
|
122
|
+
email: text("email"),
|
|
123
|
+
/** Free-form per-provider metadata (avatar URL, scopes granted, etc.). */
|
|
124
|
+
metadata: jsonb("metadata").$type().default({}).notNull(),
|
|
125
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
126
|
+
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
127
|
+
},
|
|
128
|
+
(table) => [
|
|
129
|
+
unique("np_member_identities_provider_subject_uq").on(table.provider, table.subject),
|
|
130
|
+
unique("np_member_identities_member_provider_uq").on(table.memberId, table.provider),
|
|
131
|
+
index("np_member_identities_member_idx").on(table.memberId)
|
|
132
|
+
]
|
|
133
|
+
);
|
|
134
|
+
var npMemberRoles = pgTable(
|
|
135
|
+
"np_member_roles",
|
|
136
|
+
{
|
|
137
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
138
|
+
memberId: uuid("member_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
139
|
+
role: text("role").notNull(),
|
|
140
|
+
scopeType: npMemberRoleScopeEnum("scope_type").notNull(),
|
|
141
|
+
/** Nullable for `scope_type='site'`. Otherwise an opaque string id. */
|
|
142
|
+
scopeId: text("scope_id"),
|
|
143
|
+
/**
|
|
144
|
+
* Phase 18 — the tenant the grant applies on. For
|
|
145
|
+
* `scope_type='site'` this column IS the site identifier
|
|
146
|
+
* (`scope_id` stays null because site is the root scope).
|
|
147
|
+
* For category / collection / thread grants, `site_id` says
|
|
148
|
+
* which tenant's category/collection/thread this row
|
|
149
|
+
* targets — the same slug exists on every site.
|
|
150
|
+
*/
|
|
151
|
+
siteId: text("site_id").default("default").notNull(),
|
|
152
|
+
grantedBy: uuid("granted_by").references(() => npUsers.id),
|
|
153
|
+
grantedAt: timestamp("granted_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
154
|
+
expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date" })
|
|
155
|
+
},
|
|
156
|
+
(table) => [
|
|
157
|
+
// Two indexes mirror the two access patterns: "what can this member
|
|
158
|
+
// do?" (memberId scan) and "who mods this scope?" (scope scan).
|
|
159
|
+
index("np_member_roles_member_idx").on(table.memberId),
|
|
160
|
+
index("np_member_roles_scope_idx").on(table.scopeType, table.scopeId),
|
|
161
|
+
index("np_member_roles_site_idx").on(table.siteId, table.memberId),
|
|
162
|
+
// `scope_id` is null for site-wide grants. NULLS NOT
|
|
163
|
+
// DISTINCT makes two null `scope_id`s collide so the
|
|
164
|
+
// unique constraint enforces "one grant per (member, role,
|
|
165
|
+
// scope, site)." `site_id` widens the key so the same
|
|
166
|
+
// member can hold the same role on different tenants.
|
|
167
|
+
unique("np_member_roles_grant_uq").on(table.memberId, table.role, table.scopeType, table.scopeId, table.siteId).nullsNotDistinct()
|
|
168
|
+
]
|
|
169
|
+
);
|
|
170
|
+
var npComments = pgTable(
|
|
171
|
+
"np_comments",
|
|
172
|
+
{
|
|
173
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
174
|
+
targetType: text("target_type").notNull(),
|
|
175
|
+
targetId: uuid("target_id").notNull(),
|
|
176
|
+
parentId: uuid("parent_id").references(() => npComments.id, {
|
|
177
|
+
onDelete: "cascade"
|
|
178
|
+
}),
|
|
179
|
+
memberId: uuid("member_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
180
|
+
bodyMd: text("body_md").notNull(),
|
|
181
|
+
bodyHtml: text("body_html").notNull(),
|
|
182
|
+
status: npCommentStatusEnum("status").default("visible").notNull(),
|
|
183
|
+
hiddenByUserId: uuid("hidden_by_user_id").references(() => npUsers.id),
|
|
184
|
+
hiddenByMemberId: uuid("hidden_by_member_id").references(() => npMembers.id),
|
|
185
|
+
hiddenReason: text("hidden_reason"),
|
|
186
|
+
editedAt: timestamp("edited_at", { withTimezone: true, mode: "date" }),
|
|
187
|
+
/**
|
|
188
|
+
* Phase 18 — site this comment belongs to. Filled at insert
|
|
189
|
+
* time from the target document's site (canonical) so a
|
|
190
|
+
* forged request resolver can't smuggle a comment into the
|
|
191
|
+
* wrong site. Defaults to `'default'` for legacy single-
|
|
192
|
+
* tenant rows so the migration backfill is a no-op.
|
|
193
|
+
*/
|
|
194
|
+
siteId: text("site_id").default("default").notNull(),
|
|
195
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
196
|
+
},
|
|
197
|
+
(table) => [
|
|
198
|
+
index("np_comments_target_idx").on(table.targetType, table.targetId, table.createdAt),
|
|
199
|
+
index("np_comments_member_idx").on(table.memberId, table.createdAt),
|
|
200
|
+
index("np_comments_site_idx").on(table.siteId, table.createdAt)
|
|
201
|
+
]
|
|
202
|
+
);
|
|
203
|
+
var npReactions = pgTable(
|
|
204
|
+
"np_reactions",
|
|
205
|
+
{
|
|
206
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
207
|
+
targetType: text("target_type").notNull(),
|
|
208
|
+
targetId: uuid("target_id").notNull(),
|
|
209
|
+
memberId: uuid("member_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
210
|
+
kind: text("kind").notNull(),
|
|
211
|
+
/** Phase 18 — site this reaction belongs to (derived from target). */
|
|
212
|
+
siteId: text("site_id").default("default").notNull(),
|
|
213
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
214
|
+
},
|
|
215
|
+
(table) => [
|
|
216
|
+
index("np_reactions_target_idx").on(table.targetType, table.targetId),
|
|
217
|
+
index("np_reactions_site_idx").on(table.siteId),
|
|
218
|
+
unique("np_reactions_unique").on(table.targetType, table.targetId, table.memberId, table.kind)
|
|
219
|
+
]
|
|
220
|
+
);
|
|
221
|
+
var npFollows = pgTable(
|
|
222
|
+
"np_follows",
|
|
223
|
+
{
|
|
224
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
225
|
+
followerId: uuid("follower_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
226
|
+
targetType: text("target_type").notNull(),
|
|
227
|
+
targetId: text("target_id").notNull(),
|
|
228
|
+
/**
|
|
229
|
+
* Phase 18 — site the follow happened on. The same global
|
|
230
|
+
* member can follow on multiple sites and each row scopes
|
|
231
|
+
* to where the click happened (so site-scoped notifications
|
|
232
|
+
* + activity feeds don't leak cross-tenant). The unique
|
|
233
|
+
* key is widened to include site_id so the same follower
|
|
234
|
+
* can have parallel follow rows under different tenants.
|
|
235
|
+
*/
|
|
236
|
+
siteId: text("site_id").default("default").notNull(),
|
|
237
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
238
|
+
},
|
|
239
|
+
(table) => [
|
|
240
|
+
index("np_follows_target_idx").on(table.targetType, table.targetId),
|
|
241
|
+
index("np_follows_site_idx").on(table.siteId),
|
|
242
|
+
unique("np_follows_unique").on(
|
|
243
|
+
table.followerId,
|
|
244
|
+
table.targetType,
|
|
245
|
+
table.targetId,
|
|
246
|
+
table.siteId
|
|
247
|
+
)
|
|
248
|
+
]
|
|
249
|
+
);
|
|
250
|
+
var npMemberMutes = pgTable(
|
|
251
|
+
"np_member_mutes",
|
|
252
|
+
{
|
|
253
|
+
memberId: uuid("member_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
254
|
+
targetId: uuid("target_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
255
|
+
/**
|
|
256
|
+
* Phase 18 — site the mute applies to. A muter can choose
|
|
257
|
+
* to silence someone on one tenant without affecting their
|
|
258
|
+
* other tenants. PK is widened to include site_id so the
|
|
259
|
+
* same `(member, target)` pair can hold parallel rows per
|
|
260
|
+
* site.
|
|
261
|
+
*/
|
|
262
|
+
siteId: text("site_id").default("default").notNull(),
|
|
263
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
264
|
+
},
|
|
265
|
+
(table) => [
|
|
266
|
+
primaryKey({ columns: [table.memberId, table.targetId, table.siteId] }),
|
|
267
|
+
index("np_member_mutes_target_idx").on(table.targetId)
|
|
268
|
+
]
|
|
269
|
+
);
|
|
270
|
+
var npNotifications = pgTable(
|
|
271
|
+
"np_notifications",
|
|
272
|
+
{
|
|
273
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
274
|
+
memberId: uuid("member_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
275
|
+
kind: text("kind").notNull(),
|
|
276
|
+
payload: jsonb("payload").$type().default({}).notNull(),
|
|
277
|
+
readAt: timestamp("read_at", { withTimezone: true, mode: "date" }),
|
|
278
|
+
/**
|
|
279
|
+
* Phase 18 — site this notification belongs to. A member
|
|
280
|
+
* who's active on multiple tenants gets one inbox per site
|
|
281
|
+
* (the inbox API filters by current site) so cross-tenant
|
|
282
|
+
* activity doesn't bleed into the wrong site's UI.
|
|
283
|
+
*/
|
|
284
|
+
siteId: text("site_id").default("default").notNull(),
|
|
285
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
286
|
+
},
|
|
287
|
+
(table) => [
|
|
288
|
+
index("np_notifications_inbox_idx").on(table.memberId, table.readAt, table.createdAt),
|
|
289
|
+
index("np_notifications_site_inbox_idx").on(table.siteId, table.memberId, table.readAt)
|
|
290
|
+
]
|
|
291
|
+
);
|
|
292
|
+
var npReports = pgTable(
|
|
293
|
+
"np_reports",
|
|
294
|
+
{
|
|
295
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
296
|
+
reporterId: uuid("reporter_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
297
|
+
targetType: text("target_type").notNull(),
|
|
298
|
+
targetId: text("target_id").notNull(),
|
|
299
|
+
reason: text("reason").notNull(),
|
|
300
|
+
resolvedAt: timestamp("resolved_at", { withTimezone: true, mode: "date" }),
|
|
301
|
+
resolvedByUserId: uuid("resolved_by_user_id").references(() => npUsers.id),
|
|
302
|
+
resolvedByMemberId: uuid("resolved_by_member_id").references(() => npMembers.id),
|
|
303
|
+
resolution: text("resolution"),
|
|
304
|
+
/**
|
|
305
|
+
* Phase 18 — site this report belongs to. The mod queue
|
|
306
|
+
* is per-site so a category-mod on tenant A doesn't see
|
|
307
|
+
* tenant B's reports.
|
|
308
|
+
*/
|
|
309
|
+
siteId: text("site_id").default("default").notNull(),
|
|
310
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
311
|
+
},
|
|
312
|
+
(table) => [
|
|
313
|
+
index("np_reports_queue_idx").on(table.resolvedAt, table.createdAt),
|
|
314
|
+
index("np_reports_target_idx").on(table.targetType, table.targetId),
|
|
315
|
+
index("np_reports_site_queue_idx").on(table.siteId, table.resolvedAt)
|
|
316
|
+
]
|
|
317
|
+
);
|
|
318
|
+
var npAuditEvents = pgTable(
|
|
319
|
+
"np_audit_events",
|
|
320
|
+
{
|
|
321
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
322
|
+
actorKind: text("actor_kind").notNull(),
|
|
323
|
+
actorUserId: uuid("actor_user_id").references(() => npUsers.id),
|
|
324
|
+
actorMemberId: uuid("actor_member_id").references(() => npMembers.id),
|
|
325
|
+
action: text("action").notNull(),
|
|
326
|
+
targetType: text("target_type"),
|
|
327
|
+
targetId: text("target_id"),
|
|
328
|
+
payload: jsonb("payload").$type().default({}).notNull(),
|
|
329
|
+
/**
|
|
330
|
+
* Phase 17 — site-scoped audit. Filled by `recordAuditEvent`
|
|
331
|
+
* from the current request's site (the multi-site resolver).
|
|
332
|
+
* Nullable for events that don't belong to a single site
|
|
333
|
+
* (super-admin actions, background jobs, scripts).
|
|
334
|
+
*/
|
|
335
|
+
siteId: text("site_id"),
|
|
336
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
337
|
+
},
|
|
338
|
+
(table) => [
|
|
339
|
+
index("np_audit_target_idx").on(table.targetType, table.targetId, table.createdAt),
|
|
340
|
+
index("np_audit_actor_user_idx").on(table.actorUserId, table.createdAt),
|
|
341
|
+
index("np_audit_actor_member_idx").on(table.actorMemberId, table.createdAt),
|
|
342
|
+
index("np_audit_site_idx").on(table.siteId, table.createdAt)
|
|
343
|
+
]
|
|
344
|
+
);
|
|
345
|
+
var npBans = pgTable(
|
|
346
|
+
"np_bans",
|
|
347
|
+
{
|
|
348
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
349
|
+
memberId: uuid("member_id").notNull().references(() => npMembers.id, { onDelete: "cascade" }),
|
|
350
|
+
scopeType: npBanScopeEnum("scope_type").notNull(),
|
|
351
|
+
scopeId: text("scope_id"),
|
|
352
|
+
kind: npBanKindEnum("kind").notNull(),
|
|
353
|
+
expiresAt: timestamp("expires_at", { withTimezone: true, mode: "date" }),
|
|
354
|
+
reason: text("reason"),
|
|
355
|
+
byUserId: uuid("by_user_id").references(() => npUsers.id),
|
|
356
|
+
byMemberId: uuid("by_member_id").references(() => npMembers.id),
|
|
357
|
+
/**
|
|
358
|
+
* Phase 18 — the tenant this ban applies to. Pre-Phase 18
|
|
359
|
+
* `scope_type='site'` rows had `scope_id=null` because
|
|
360
|
+
* "site" was the singular root scope; with multi-tenancy
|
|
361
|
+
* the column tells `assertNotBanned` WHICH site the ban
|
|
362
|
+
* blocks writes on. Category / collection scopes resolve
|
|
363
|
+
* per-site too — the same `posts` collection slug exists
|
|
364
|
+
* on every tenant.
|
|
365
|
+
*/
|
|
366
|
+
siteId: text("site_id").default("default").notNull(),
|
|
367
|
+
createdAt: timestamp("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
368
|
+
},
|
|
369
|
+
(table) => [
|
|
370
|
+
index("np_bans_member_scope_idx").on(table.memberId, table.scopeType, table.scopeId),
|
|
371
|
+
index("np_bans_active_idx").on(table.memberId, table.expiresAt),
|
|
372
|
+
index("np_bans_site_idx").on(table.siteId, table.memberId)
|
|
373
|
+
]
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
// src/db/schema/media.ts
|
|
377
|
+
var npMediaStatusEnum = pgEnum2("np_media_status", [
|
|
378
|
+
"processing",
|
|
379
|
+
"ready",
|
|
380
|
+
"error"
|
|
381
|
+
]);
|
|
382
|
+
var npMediaFolders = pgTable2("np_media_folders", {
|
|
383
|
+
id: uuid2("id").defaultRandom().primaryKey(),
|
|
384
|
+
name: text2("name").notNull(),
|
|
385
|
+
parentId: uuid2("parent_id").references(() => npMediaFolders.id),
|
|
386
|
+
createdAt: timestamp2("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
387
|
+
});
|
|
388
|
+
var npMedia = pgTable2(
|
|
389
|
+
"np_media",
|
|
390
|
+
{
|
|
391
|
+
id: uuid2("id").defaultRandom().primaryKey(),
|
|
392
|
+
filename: text2("filename").notNull(),
|
|
393
|
+
originalFilename: text2("original_filename").notNull(),
|
|
394
|
+
mimeType: text2("mime_type").notNull(),
|
|
395
|
+
filesize: bigint("filesize", { mode: "number" }).notNull(),
|
|
396
|
+
width: integer2("width"),
|
|
397
|
+
height: integer2("height"),
|
|
398
|
+
alt: text2("alt"),
|
|
399
|
+
caption: jsonb2("caption").$type(),
|
|
400
|
+
focalPoint: jsonb2("focal_point").$type(),
|
|
401
|
+
sizes: jsonb2("sizes").$type(),
|
|
402
|
+
storageKey: text2("storage_key").notNull(),
|
|
403
|
+
hash: text2("hash").notNull(),
|
|
404
|
+
status: npMediaStatusEnum("status").notNull(),
|
|
405
|
+
folderId: uuid2("folder_id").references(() => npMediaFolders.id),
|
|
406
|
+
uploadedBy: uuid2("uploaded_by").references(() => npUsers.id),
|
|
407
|
+
/**
|
|
408
|
+
* Set when a member uploaded the row instead of a staff user
|
|
409
|
+
* (Phase 9.7j). Mutually exclusive with `uploadedBy`: a row
|
|
410
|
+
* has exactly one uploader. Member-side moderation tools key
|
|
411
|
+
* off this column to filter "uploads I should review."
|
|
412
|
+
* `ON DELETE SET NULL` so a member account deletion doesn't
|
|
413
|
+
* cascade-delete their uploads — staff still need them for
|
|
414
|
+
* the audit trail (just like `member_author_id` on
|
|
415
|
+
* collection tables).
|
|
416
|
+
*/
|
|
417
|
+
uploadedByMemberId: uuid2("uploaded_by_member_id").references(
|
|
418
|
+
() => npMembers.id,
|
|
419
|
+
{ onDelete: "set null" }
|
|
420
|
+
),
|
|
421
|
+
createdAt: timestamp2("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
422
|
+
updatedAt: timestamp2("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
423
|
+
deletedAt: timestamp2("deleted_at", { withTimezone: true, mode: "date" })
|
|
424
|
+
},
|
|
425
|
+
(table) => ({
|
|
426
|
+
hashIdx: index2("np_media_hash_idx").on(table.hash),
|
|
427
|
+
statusIdx: index2("np_media_status_idx").on(table.status),
|
|
428
|
+
uploadedByMemberIdx: index2("np_media_uploaded_by_member_idx").on(
|
|
429
|
+
table.uploadedByMemberId
|
|
430
|
+
)
|
|
431
|
+
})
|
|
432
|
+
);
|
|
433
|
+
var npMediaRefs = pgTable2(
|
|
434
|
+
"np_media_refs",
|
|
435
|
+
{
|
|
436
|
+
id: uuid2("id").defaultRandom().primaryKey(),
|
|
437
|
+
mediaId: uuid2("media_id").notNull().references(() => npMedia.id, { onDelete: "cascade" }),
|
|
438
|
+
collection: text2("collection").notNull(),
|
|
439
|
+
documentId: text2("document_id").notNull(),
|
|
440
|
+
field: text2("field").notNull()
|
|
441
|
+
},
|
|
442
|
+
(table) => ({
|
|
443
|
+
mediaIdIdx: index2("np_media_refs_media_id_idx").on(table.mediaId),
|
|
444
|
+
documentIdIdx: index2("np_media_refs_document_id_idx").on(table.documentId)
|
|
445
|
+
})
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
// src/db/schema/system.ts
|
|
449
|
+
var npUserRoleEnum = pgEnum3("np_user_role", [
|
|
450
|
+
"admin",
|
|
451
|
+
"editor",
|
|
452
|
+
// 9.5: community moderator. Sits OUTSIDE the linear content-edit
|
|
453
|
+
// hierarchy — a moderator handles community moderation (hide
|
|
454
|
+
// comments, resolve reports, issue bans) but cannot author or edit
|
|
455
|
+
// collection content. ROLE_HIERARCHY in config/types.ts intentionally
|
|
456
|
+
// does not list this role; community-moderation paths check the role
|
|
457
|
+
// explicitly via `principalCan()`.
|
|
458
|
+
"moderator",
|
|
459
|
+
"author",
|
|
460
|
+
"viewer"
|
|
461
|
+
]);
|
|
462
|
+
var npRevisionStatusEnum = pgEnum3("np_revision_status", [
|
|
463
|
+
"draft",
|
|
464
|
+
"published",
|
|
465
|
+
"autosave"
|
|
466
|
+
]);
|
|
467
|
+
var npPasswordResetPurposeEnum = pgEnum3("np_password_reset_purpose", ["invite", "reset"]);
|
|
468
|
+
var npUsers = pgTable3("np_users", {
|
|
469
|
+
id: uuid3("id").defaultRandom().primaryKey(),
|
|
470
|
+
email: text3("email").notNull().unique(),
|
|
471
|
+
password: text3("password").notNull(),
|
|
472
|
+
name: text3("name").notNull(),
|
|
473
|
+
role: npUserRoleEnum("role").notNull(),
|
|
474
|
+
/**
|
|
475
|
+
* Phase 15.5 — super-admin flag. Bypasses per-site membership
|
|
476
|
+
* checks; the super-admin can manage every site including
|
|
477
|
+
* creating / deleting tenants. The flag is independent of
|
|
478
|
+
* the per-site `role` enum (a super-admin still needs a
|
|
479
|
+
* `role` field for non-multi-site contexts; multi-site
|
|
480
|
+
* permissions check `is_super_admin OR site_membership`).
|
|
481
|
+
*/
|
|
482
|
+
isSuperAdmin: boolean2("is_super_admin").default(false).notNull(),
|
|
483
|
+
avatar: uuid3("avatar").references(() => npMedia.id),
|
|
484
|
+
loginAttempts: integer3("login_attempts").default(0).notNull(),
|
|
485
|
+
lockUntil: timestamp3("lock_until", { withTimezone: true, mode: "date" }),
|
|
486
|
+
tokenVersion: integer3("token_version").default(0).notNull(),
|
|
487
|
+
passwordResetTokenHash: text3("password_reset_token_hash"),
|
|
488
|
+
passwordResetExpiresAt: timestamp3("password_reset_expires_at", {
|
|
489
|
+
withTimezone: true,
|
|
490
|
+
mode: "date"
|
|
491
|
+
}),
|
|
492
|
+
passwordResetPurpose: npPasswordResetPurposeEnum("password_reset_purpose"),
|
|
493
|
+
createdAt: timestamp3("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
494
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
495
|
+
});
|
|
496
|
+
var npSiteMemberships = pgTable3(
|
|
497
|
+
"np_site_memberships",
|
|
498
|
+
{
|
|
499
|
+
siteId: text3("site_id").notNull(),
|
|
500
|
+
userId: uuid3("user_id").notNull().references(() => npUsers.id, { onDelete: "cascade" }),
|
|
501
|
+
role: npUserRoleEnum("role").notNull(),
|
|
502
|
+
createdAt: timestamp3("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
503
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
504
|
+
},
|
|
505
|
+
(table) => [primaryKey2({ columns: [table.siteId, table.userId] })]
|
|
506
|
+
);
|
|
507
|
+
var npUserOAuthIdentities = pgTable3(
|
|
508
|
+
"np_user_oauth_identities",
|
|
509
|
+
{
|
|
510
|
+
id: uuid3("id").defaultRandom().primaryKey(),
|
|
511
|
+
userId: uuid3("user_id").notNull().references(() => npUsers.id, { onDelete: "cascade" }),
|
|
512
|
+
provider: text3("provider").notNull(),
|
|
513
|
+
providerUserId: text3("provider_user_id").notNull(),
|
|
514
|
+
/** Free-form per-provider metadata (avatar URL, scopes granted, etc.). */
|
|
515
|
+
metadata: jsonb3("metadata").$type().default({}).notNull(),
|
|
516
|
+
createdAt: timestamp3("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
517
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
518
|
+
},
|
|
519
|
+
(table) => ({
|
|
520
|
+
providerSubjectUnique: unique2("np_user_oauth_identities_provider_subject_unique").on(
|
|
521
|
+
table.provider,
|
|
522
|
+
table.providerUserId
|
|
523
|
+
),
|
|
524
|
+
userProviderUnique: unique2("np_user_oauth_identities_user_provider_unique").on(
|
|
525
|
+
table.userId,
|
|
526
|
+
table.provider
|
|
527
|
+
),
|
|
528
|
+
userIdx: index3("np_user_oauth_identities_user_idx").on(table.userId)
|
|
529
|
+
})
|
|
530
|
+
);
|
|
531
|
+
var npSessions = pgTable3("np_sessions", {
|
|
532
|
+
id: uuid3("id").defaultRandom().primaryKey(),
|
|
533
|
+
userId: uuid3("user_id").notNull().references(() => npUsers.id, { onDelete: "cascade" }),
|
|
534
|
+
tokenHash: text3("token_hash").notNull(),
|
|
535
|
+
userAgent: text3("user_agent"),
|
|
536
|
+
ip: text3("ip"),
|
|
537
|
+
expiresAt: timestamp3("expires_at", { withTimezone: true, mode: "date" }).notNull(),
|
|
538
|
+
createdAt: timestamp3("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
539
|
+
});
|
|
540
|
+
var npRevisions = pgTable3(
|
|
541
|
+
"np_revisions",
|
|
542
|
+
{
|
|
543
|
+
id: uuid3("id").defaultRandom().primaryKey(),
|
|
544
|
+
collection: text3("collection").notNull(),
|
|
545
|
+
documentId: text3("document_id").notNull(),
|
|
546
|
+
version: integer3("version").notNull(),
|
|
547
|
+
status: npRevisionStatusEnum("status").notNull(),
|
|
548
|
+
snapshot: jsonb3("snapshot").$type().notNull(),
|
|
549
|
+
changedFields: text3("changed_fields").array().notNull(),
|
|
550
|
+
authorId: uuid3("author_id").references(() => npUsers.id),
|
|
551
|
+
createdAt: timestamp3("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
552
|
+
},
|
|
553
|
+
(table) => ({
|
|
554
|
+
documentVersionUnique: unique2("np_revisions_document_id_version_unique").on(
|
|
555
|
+
table.documentId,
|
|
556
|
+
table.version
|
|
557
|
+
),
|
|
558
|
+
collectionIdx: index3("np_revisions_collection_idx").on(table.collection),
|
|
559
|
+
documentIdIdx: index3("np_revisions_document_id_idx").on(table.documentId)
|
|
560
|
+
})
|
|
561
|
+
);
|
|
562
|
+
var npSettings = pgTable3(
|
|
563
|
+
"np_settings",
|
|
564
|
+
{
|
|
565
|
+
siteId: text3("site_id").default("default").notNull(),
|
|
566
|
+
key: text3("key").notNull(),
|
|
567
|
+
value: jsonb3("value").$type().notNull(),
|
|
568
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
569
|
+
updatedBy: uuid3("updated_by").references(() => npUsers.id)
|
|
570
|
+
},
|
|
571
|
+
(table) => [primaryKey2({ columns: [table.siteId, table.key] })]
|
|
572
|
+
);
|
|
573
|
+
var npSlugHistory = pgTable3(
|
|
574
|
+
"np_slug_history",
|
|
575
|
+
{
|
|
576
|
+
id: uuid3("id").defaultRandom().primaryKey(),
|
|
577
|
+
siteId: text3("site_id").default("default").notNull(),
|
|
578
|
+
collection: text3("collection").notNull(),
|
|
579
|
+
documentId: text3("document_id").notNull(),
|
|
580
|
+
oldSlug: text3("old_slug").notNull(),
|
|
581
|
+
newSlug: text3("new_slug").notNull(),
|
|
582
|
+
createdAt: timestamp3("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
583
|
+
},
|
|
584
|
+
(table) => [
|
|
585
|
+
index3("np_slug_history_lookup_idx").on(table.siteId, table.collection, table.oldSlug),
|
|
586
|
+
index3("np_slug_history_doc_idx").on(table.siteId, table.collection, table.documentId)
|
|
587
|
+
]
|
|
588
|
+
);
|
|
589
|
+
var npNavigation = pgTable3(
|
|
590
|
+
"np_navigation",
|
|
591
|
+
{
|
|
592
|
+
id: uuid3("id").defaultRandom().primaryKey(),
|
|
593
|
+
siteId: text3("site_id").default("default").notNull(),
|
|
594
|
+
location: text3("location").notNull(),
|
|
595
|
+
items: jsonb3("items").$type().notNull(),
|
|
596
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
597
|
+
updatedBy: uuid3("updated_by").references(() => npUsers.id)
|
|
598
|
+
},
|
|
599
|
+
(table) => [unique2("np_navigation_site_location_idx").on(table.siteId, table.location)]
|
|
600
|
+
);
|
|
601
|
+
var npStringOverrides = pgTable3(
|
|
602
|
+
"np_string_overrides",
|
|
603
|
+
{
|
|
604
|
+
siteId: text3("site_id").default("default").notNull(),
|
|
605
|
+
locale: text3("locale").notNull(),
|
|
606
|
+
key: text3("key").notNull(),
|
|
607
|
+
value: text3("value"),
|
|
608
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
609
|
+
updatedBy: uuid3("updated_by").references(() => npUsers.id)
|
|
610
|
+
},
|
|
611
|
+
(table) => [primaryKey2({ columns: [table.siteId, table.locale, table.key] })]
|
|
612
|
+
);
|
|
613
|
+
var npSites = pgTable3(
|
|
614
|
+
"np_sites",
|
|
615
|
+
{
|
|
616
|
+
id: text3("id").primaryKey(),
|
|
617
|
+
name: text3("name").notNull(),
|
|
618
|
+
hostname: text3("hostname"),
|
|
619
|
+
description: text3("description"),
|
|
620
|
+
settings: jsonb3("settings").$type().default({}).notNull(),
|
|
621
|
+
isDefault: boolean2("is_default").default(false).notNull(),
|
|
622
|
+
createdAt: timestamp3("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
623
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
624
|
+
},
|
|
625
|
+
(table) => [unique2("np_sites_hostname_idx").on(table.hostname)]
|
|
626
|
+
);
|
|
627
|
+
var npPlugins = pgTable3("np_plugins", {
|
|
628
|
+
id: text3("id").primaryKey(),
|
|
629
|
+
enabled: boolean2("enabled").default(true).notNull(),
|
|
630
|
+
installedAt: timestamp3("installed_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
631
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
632
|
+
});
|
|
633
|
+
var NP_GLOBAL_PLUGIN_SITE_ID = "_global_";
|
|
634
|
+
var npPluginStorage = pgTable3(
|
|
635
|
+
"np_plugin_storage",
|
|
636
|
+
{
|
|
637
|
+
pluginId: text3("plugin_id").notNull(),
|
|
638
|
+
siteId: text3("site_id").default(NP_GLOBAL_PLUGIN_SITE_ID).notNull(),
|
|
639
|
+
key: text3("key").notNull(),
|
|
640
|
+
value: jsonb3("value").$type().notNull(),
|
|
641
|
+
expiresAt: timestamp3("expires_at", { withTimezone: true, mode: "date" }),
|
|
642
|
+
updatedAt: timestamp3("updated_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
643
|
+
},
|
|
644
|
+
(table) => ({
|
|
645
|
+
pk: primaryKey2({ columns: [table.pluginId, table.siteId, table.key] }),
|
|
646
|
+
pluginIdx: index3("np_plugin_storage_plugin_id_idx").on(table.pluginId),
|
|
647
|
+
siteIdx: index3("np_plugin_storage_site_idx").on(table.siteId)
|
|
648
|
+
})
|
|
649
|
+
);
|
|
650
|
+
var npWorkerHeartbeats = pgTable3("np_worker_heartbeats", {
|
|
651
|
+
id: text3("id").primaryKey(),
|
|
652
|
+
status: text3("status").default("running").notNull(),
|
|
653
|
+
startedAt: timestamp3("started_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
654
|
+
lastSeenAt: timestamp3("last_seen_at", { withTimezone: true, mode: "date" }).defaultNow().notNull(),
|
|
655
|
+
/** Free-form metadata (worker version, hostname, env). */
|
|
656
|
+
meta: jsonb3("meta").$type().default({}).notNull()
|
|
657
|
+
});
|
|
658
|
+
var npJobLogs = pgTable3(
|
|
659
|
+
"np_job_logs",
|
|
660
|
+
{
|
|
661
|
+
id: uuid3("id").defaultRandom().primaryKey(),
|
|
662
|
+
jobId: text3("job_id").notNull(),
|
|
663
|
+
level: text3("level").notNull(),
|
|
664
|
+
message: text3("message").notNull(),
|
|
665
|
+
context: jsonb3("context").$type(),
|
|
666
|
+
createdAt: timestamp3("created_at", { withTimezone: true, mode: "date" }).defaultNow().notNull()
|
|
667
|
+
},
|
|
668
|
+
(table) => [
|
|
669
|
+
index3("np_job_logs_job_idx").on(table.jobId, table.createdAt),
|
|
670
|
+
index3("np_job_logs_created_idx").on(table.createdAt)
|
|
671
|
+
]
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
export {
|
|
675
|
+
npUserRoleEnum,
|
|
676
|
+
npRevisionStatusEnum,
|
|
677
|
+
npPasswordResetPurposeEnum,
|
|
678
|
+
npUsers,
|
|
679
|
+
npSiteMemberships,
|
|
680
|
+
npUserOAuthIdentities,
|
|
681
|
+
npSessions,
|
|
682
|
+
npRevisions,
|
|
683
|
+
npSettings,
|
|
684
|
+
npSlugHistory,
|
|
685
|
+
npNavigation,
|
|
686
|
+
npStringOverrides,
|
|
687
|
+
npSites,
|
|
688
|
+
npPlugins,
|
|
689
|
+
NP_GLOBAL_PLUGIN_SITE_ID,
|
|
690
|
+
npPluginStorage,
|
|
691
|
+
npWorkerHeartbeats,
|
|
692
|
+
npJobLogs,
|
|
693
|
+
npMediaStatusEnum,
|
|
694
|
+
npMediaFolders,
|
|
695
|
+
npMedia,
|
|
696
|
+
npMediaRefs,
|
|
697
|
+
npMemberStatusEnum,
|
|
698
|
+
npBanScopeEnum,
|
|
699
|
+
npBanKindEnum,
|
|
700
|
+
npCommentStatusEnum,
|
|
701
|
+
npMemberRoleScopeEnum,
|
|
702
|
+
npMembers,
|
|
703
|
+
npMemberSessions,
|
|
704
|
+
npMemberIdentities,
|
|
705
|
+
npMemberRoles,
|
|
706
|
+
npComments,
|
|
707
|
+
npReactions,
|
|
708
|
+
npFollows,
|
|
709
|
+
npMemberMutes,
|
|
710
|
+
npNotifications,
|
|
711
|
+
npReports,
|
|
712
|
+
npAuditEvents,
|
|
713
|
+
npBans
|
|
714
|
+
};
|
|
715
|
+
//# sourceMappingURL=chunk-M43PGOQY.js.map
|