@authhero/drizzle 0.56.3 → 0.57.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.
@@ -136,9 +136,6 @@ CREATE TABLE `users` (
136
136
  `created_at` text(35) NOT NULL,
137
137
  `updated_at` text(35) NOT NULL,
138
138
  `linked_to` text(255),
139
- `last_ip` text(255),
140
- `login_count` integer NOT NULL,
141
- `last_login` text(255),
142
139
  `registration_completed_at` text(35),
143
140
  `provider` text(255) NOT NULL,
144
141
  `connection` text(255),
@@ -167,6 +164,18 @@ CREATE INDEX `users_email_index` ON `users` (`email`);--> statement-breakpoint
167
164
  CREATE INDEX `users_linked_to_index` ON `users` (`linked_to`);--> statement-breakpoint
168
165
  CREATE INDEX `users_name_index` ON `users` (`name`);--> statement-breakpoint
169
166
  CREATE INDEX `users_phone_tenant_provider_index` ON `users` (`tenant_id`,`phone_number`,`provider`);--> statement-breakpoint
167
+ CREATE TABLE `user_activity` (
168
+ `tenant_id` text(255) NOT NULL,
169
+ `user_id` text(255) NOT NULL,
170
+ `last_login` text(35),
171
+ `last_ip` text(45),
172
+ `login_count` integer DEFAULT 0 NOT NULL,
173
+ `failed_logins` text,
174
+ `last_password_reset` text(35),
175
+ PRIMARY KEY(`tenant_id`, `user_id`),
176
+ FOREIGN KEY (`user_id`,`tenant_id`) REFERENCES `users`(`user_id`,`tenant_id`) ON UPDATE no action ON DELETE cascade
177
+ );
178
+ --> statement-breakpoint
170
179
  CREATE TABLE `authentication_codes` (
171
180
  `tenant_id` text(191) NOT NULL,
172
181
  `code` text(255) PRIMARY KEY NOT NULL,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "6",
3
3
  "dialect": "sqlite",
4
- "id": "aa362ef7-2aa5-43c2-822c-5e9ffd5ec7e5",
4
+ "id": "f5a5b35e-cf8e-4fe4-afe9-ec2d33b03c33",
5
5
  "prevId": "00000000-0000-0000-0000-000000000000",
6
6
  "tables": {
7
7
  "tenants": {
@@ -952,27 +952,6 @@
952
952
  "notNull": false,
953
953
  "autoincrement": false
954
954
  },
955
- "last_ip": {
956
- "name": "last_ip",
957
- "type": "text(255)",
958
- "primaryKey": false,
959
- "notNull": false,
960
- "autoincrement": false
961
- },
962
- "login_count": {
963
- "name": "login_count",
964
- "type": "integer",
965
- "primaryKey": false,
966
- "notNull": true,
967
- "autoincrement": false
968
- },
969
- "last_login": {
970
- "name": "last_login",
971
- "type": "text(255)",
972
- "primaryKey": false,
973
- "notNull": false,
974
- "autoincrement": false
975
- },
976
955
  "registration_completed_at": {
977
956
  "name": "registration_completed_at",
978
957
  "type": "text(35)",
@@ -1181,6 +1160,90 @@
1181
1160
  "uniqueConstraints": {},
1182
1161
  "checkConstraints": {}
1183
1162
  },
1163
+ "user_activity": {
1164
+ "name": "user_activity",
1165
+ "columns": {
1166
+ "tenant_id": {
1167
+ "name": "tenant_id",
1168
+ "type": "text(255)",
1169
+ "primaryKey": false,
1170
+ "notNull": true,
1171
+ "autoincrement": false
1172
+ },
1173
+ "user_id": {
1174
+ "name": "user_id",
1175
+ "type": "text(255)",
1176
+ "primaryKey": false,
1177
+ "notNull": true,
1178
+ "autoincrement": false
1179
+ },
1180
+ "last_login": {
1181
+ "name": "last_login",
1182
+ "type": "text(35)",
1183
+ "primaryKey": false,
1184
+ "notNull": false,
1185
+ "autoincrement": false
1186
+ },
1187
+ "last_ip": {
1188
+ "name": "last_ip",
1189
+ "type": "text(45)",
1190
+ "primaryKey": false,
1191
+ "notNull": false,
1192
+ "autoincrement": false
1193
+ },
1194
+ "login_count": {
1195
+ "name": "login_count",
1196
+ "type": "integer",
1197
+ "primaryKey": false,
1198
+ "notNull": true,
1199
+ "autoincrement": false,
1200
+ "default": 0
1201
+ },
1202
+ "failed_logins": {
1203
+ "name": "failed_logins",
1204
+ "type": "text",
1205
+ "primaryKey": false,
1206
+ "notNull": false,
1207
+ "autoincrement": false
1208
+ },
1209
+ "last_password_reset": {
1210
+ "name": "last_password_reset",
1211
+ "type": "text(35)",
1212
+ "primaryKey": false,
1213
+ "notNull": false,
1214
+ "autoincrement": false
1215
+ }
1216
+ },
1217
+ "indexes": {},
1218
+ "foreignKeys": {
1219
+ "user_activity_user_id_constraint": {
1220
+ "name": "user_activity_user_id_constraint",
1221
+ "tableFrom": "user_activity",
1222
+ "tableTo": "users",
1223
+ "columnsFrom": [
1224
+ "user_id",
1225
+ "tenant_id"
1226
+ ],
1227
+ "columnsTo": [
1228
+ "user_id",
1229
+ "tenant_id"
1230
+ ],
1231
+ "onDelete": "cascade",
1232
+ "onUpdate": "no action"
1233
+ }
1234
+ },
1235
+ "compositePrimaryKeys": {
1236
+ "user_activity_pkey": {
1237
+ "columns": [
1238
+ "tenant_id",
1239
+ "user_id"
1240
+ ],
1241
+ "name": "user_activity_pkey"
1242
+ }
1243
+ },
1244
+ "uniqueConstraints": {},
1245
+ "checkConstraints": {}
1246
+ },
1184
1247
  "authentication_codes": {
1185
1248
  "name": "authentication_codes",
1186
1249
  "columns": {
@@ -5,7 +5,7 @@
5
5
  {
6
6
  "idx": 0,
7
7
  "version": "6",
8
- "when": 1781946733503,
8
+ "when": 1783004771436,
9
9
  "tag": "0000_init",
10
10
  "breakpoints": true
11
11
  }
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "type": "git",
12
12
  "url": "https://github.com/markusahlstrand/authhero"
13
13
  },
14
- "version": "0.56.3",
14
+ "version": "0.57.0",
15
15
  "files": [
16
16
  "dist",
17
17
  "src/schema",
@@ -16,6 +16,7 @@ export * from "./actions";
16
16
 
17
17
  // Users & Authentication
18
18
  export * from "./users";
19
+ export * from "./userActivity";
19
20
  export * from "./sessions";
20
21
 
21
22
  // Clients & Grants
@@ -0,0 +1,38 @@
1
+ import {
2
+ sqliteTable,
3
+ text,
4
+ integer,
5
+ primaryKey,
6
+ foreignKey,
7
+ } from "drizzle-orm/sqlite-core";
8
+ import { users } from "./users";
9
+
10
+ /**
11
+ * Write-often per-user counters split out of the `users` row (issue #1003) so
12
+ * the profile row isn't rewritten on every login / failed password attempt.
13
+ * 1:1 with a user; a missing row means the user never logged in. Mirrors the
14
+ * kysely adapter's `user_activity` table.
15
+ */
16
+ export const userActivity = sqliteTable(
17
+ "user_activity",
18
+ {
19
+ tenant_id: text("tenant_id", { length: 255 }).notNull(),
20
+ user_id: text("user_id", { length: 255 }).notNull(),
21
+ last_login: text("last_login", { length: 35 }),
22
+ last_ip: text("last_ip", { length: 45 }), // right-sized for IPv6
23
+ login_count: integer("login_count").notNull().default(0),
24
+ failed_logins: text("failed_logins"), // JSON array of lockout timestamps
25
+ last_password_reset: text("last_password_reset", { length: 35 }),
26
+ },
27
+ (table) => [
28
+ primaryKey({
29
+ columns: [table.tenant_id, table.user_id],
30
+ name: "user_activity_pkey",
31
+ }),
32
+ foreignKey({
33
+ columns: [table.user_id, table.tenant_id],
34
+ foreignColumns: [users.user_id, users.tenant_id],
35
+ name: "user_activity_user_id_constraint",
36
+ }).onDelete("cascade"),
37
+ ],
38
+ );
@@ -28,9 +28,8 @@ export const users = sqliteTable(
28
28
  created_at: text("created_at", { length: 35 }).notNull(),
29
29
  updated_at: text("updated_at", { length: 35 }).notNull(),
30
30
  linked_to: text("linked_to", { length: 255 }),
31
- last_ip: text("last_ip", { length: 255 }),
32
- login_count: integer("login_count").notNull(),
33
- last_login: text("last_login", { length: 255 }),
31
+ // last_login/last_ip/login_count live in the user_activity table
32
+ // (issue #1003), not on the users row.
34
33
  registration_completed_at: text("registration_completed_at", {
35
34
  length: 35,
36
35
  }),