@7365admin1/core 3.48.1-staging.205 → 3.48.1-staging.206

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.
@@ -0,0 +1,63 @@
1
+ ---
2
+ "@7365admin1/core": minor
3
+ ---
4
+
5
+ Make revoking somebody's access actually end their session.
6
+
7
+ A session in this product is an opaque `sid` whose only record of truth is a
8
+ Redis key `sid:<sid>` holding a copy of the user document. `requireAuth`
9
+ authenticates a request by finding that key and nothing else — it never reads
10
+ the database — and it re-`EXPIRE`s the key to four hours on every request, so a
11
+ session that is in use never ages out on its own.
12
+
13
+ **Signing out was never the problem.** `logout()` deletes the key, so a
14
+ signed-out session is refused; that half already worked. Every other way access
15
+ is taken away did not.
16
+
17
+ **Suspending somebody changed a column and nothing else.**
18
+ `suspendResidentById` writes `users.status = "suspended"` through
19
+ `updateUserFieldById`, which cleared the user-document cache and left the
20
+ `sid:` key untouched. The person carried on working, and every request they made
21
+ pushed the four-hour expiry out another four hours. On top of that,
22
+ `refreshToken()` minted a **brand new** session through `issueSession` with no
23
+ account-status check at all — the mobile refresh token is seven days and
24
+ rotating, so a suspended account's phone could keep issuing itself fresh
25
+ sessions indefinitely. Suspension was not a delay, it was a no-op for anyone
26
+ already signed in.
27
+
28
+ **Two of the statuses the product writes were never refused at sign-in either.**
29
+ Both `login()`s checked `suspended` and `pending` only, so `rejected` (written by
30
+ `reviewPerson`, whose email is the one that hides the login link) and `deleted`
31
+ (`UserStatus.DELETED`, the soft-delete marker) could sign in fresh with a
32
+ password.
33
+
34
+ The fix is the two halves of that, at the two choke points that already exist:
35
+
36
+ - **Minting refuses a non-live account** — `assertAccountLive` in `issueSession`
37
+ in both auth services, which is the one place every sign-in path in this
38
+ package goes through (login and mobile refresh, v1 and v2), for the same
39
+ reason `assertOrganizationNotSuspended` lives there.
40
+ - **Revoking destroys what already exists** — `revokeAccountAccess` from
41
+ `updateUserFieldById`, the only path in either user repository that writes
42
+ `users.status`. It deletes every `sid:` key the person holds and every refresh
43
+ token in `tokens`, so nothing can mint a replacement.
44
+
45
+ Finding every session a person holds needs an index: `users.sid` only ever
46
+ remembers the most recent sign-in, v2 never writes it, and nothing reads it — a
47
+ fix built on that column would revoke the phone and leave the browser signed in.
48
+ The index is a Redis set (`user-sessions:<userId>`), so there is no schema
49
+ change and no migration; `users.sid` stays as the fallback that catches v1
50
+ sessions minted before the set existed.
51
+
52
+ The blocking statuses are `suspended`, `pending`, `rejected` and `deleted`, as a
53
+ deny-list — `status` is optional on the model and legacy rows carry `""` or no
54
+ field at all, and an allow-list would lock every one of them out. `resubmit` is
55
+ deliberately live: that email keeps the login link, because resubmitting the
56
+ application is done signed in.
57
+
58
+ Nothing is logged out by deploying this — no existing session is touched, and
59
+ the two sign-in messages for `suspended` and `pending` are unchanged, because
60
+ `login()` still checks those two itself before minting. What changes for a
61
+ signed-in user is only that a `rejected` or soft-deleted account can no longer
62
+ obtain a session, and that a suspension now takes effect at once rather than
63
+ never.
package/dist/index.d.ts CHANGED
@@ -67,6 +67,7 @@ declare function useSessionRepo(): {
67
67
  token: string;
68
68
  }>;
69
69
  revokeFamily: (familyId: string, session?: ClientSession) => Promise<void>;
70
+ revokeAllForUser: (userId: string | ObjectId, session?: ClientSession) => Promise<void>;
70
71
  };
71
72
 
72
73
  declare function useAuthService(): {
@@ -993,8 +994,8 @@ declare const TPrice: z.ZodObject<{
993
994
  updatedAt: z.ZodOptional<z.ZodDate>;
994
995
  deletedAt: z.ZodOptional<z.ZodDate>;
995
996
  }, "strip", z.ZodTypeAny, {
996
- name: string;
997
997
  type: "other" | "one-time-payment" | "monthly-subscription" | "yearly-subscription";
998
+ name: string;
998
999
  value: number;
999
1000
  _id?: ObjectId | undefined;
1000
1001
  createdAt?: Date | undefined;
@@ -1016,8 +1017,8 @@ declare const TPrice: z.ZodObject<{
1016
1017
  type TPrice = z.infer<typeof TPrice>;
1017
1018
  declare function usePriceModel(db: Db): {
1018
1019
  createPrice: (value: Pick<TPrice, "type" | "name" | "value">) => {
1019
- name: string;
1020
1020
  type: "other" | "one-time-payment" | "monthly-subscription" | "yearly-subscription";
1021
+ name: string;
1021
1022
  value: number;
1022
1023
  _id?: ObjectId | undefined;
1023
1024
  createdAt?: Date | undefined;
@@ -1037,8 +1038,8 @@ declare function usePriceModel(db: Db): {
1037
1038
  saleValue?: number | undefined;
1038
1039
  saleExpiry?: Date | undefined;
1039
1040
  }, {
1040
- name: string;
1041
1041
  type: "other" | "one-time-payment" | "monthly-subscription" | "yearly-subscription";
1042
+ name: string;
1042
1043
  value: number;
1043
1044
  _id?: ObjectId | undefined;
1044
1045
  createdAt?: Date | undefined;
@@ -1048,8 +1049,8 @@ declare function usePriceModel(db: Db): {
1048
1049
  saleExpiry?: Date | undefined;
1049
1050
  }>;
1050
1051
  collection: Collection<{
1051
- name: string;
1052
1052
  type: "other" | "one-time-payment" | "monthly-subscription" | "yearly-subscription";
1053
+ name: string;
1053
1054
  value: number;
1054
1055
  _id?: ObjectId | undefined;
1055
1056
  createdAt?: Date | undefined;
@@ -1226,8 +1227,8 @@ declare const TInvoice: z.ZodObject<{
1226
1227
  createdAt: z.ZodOptional<z.ZodDefault<z.ZodDate>>;
1227
1228
  updatedAt: z.ZodOptional<z.ZodDate>;
1228
1229
  }, "strip", z.ZodTypeAny, {
1229
- status: "pending" | "cancelled" | "paid" | "overdue";
1230
1230
  type: "other" | "organization-subscription" | "affiliate-subscription" | "one-time-payment";
1231
+ status: "pending" | "cancelled" | "paid" | "overdue";
1231
1232
  items: {
1232
1233
  description: string;
1233
1234
  total: number;
@@ -1263,8 +1264,8 @@ declare const TInvoice: z.ZodObject<{
1263
1264
  dueDate: Date;
1264
1265
  _id?: string | ObjectId | undefined;
1265
1266
  createdAt?: Date | undefined;
1266
- status?: "pending" | "cancelled" | "paid" | "overdue" | undefined;
1267
1267
  type?: "other" | "organization-subscription" | "affiliate-subscription" | "one-time-payment" | undefined;
1268
+ status?: "pending" | "cancelled" | "paid" | "overdue" | undefined;
1268
1269
  updatedAt?: Date | undefined;
1269
1270
  metadata?: {
1270
1271
  description?: string | undefined;
@@ -1280,8 +1281,8 @@ type TInvoice = z.infer<typeof TInvoice>;
1280
1281
  declare function useInvoiceModel(db: Db): {
1281
1282
  createInvoice: (data: TInvoice) => TInvoice;
1282
1283
  collection: Collection<{
1283
- status: "pending" | "cancelled" | "paid" | "overdue";
1284
1284
  type: "other" | "organization-subscription" | "affiliate-subscription" | "one-time-payment";
1285
+ status: "pending" | "cancelled" | "paid" | "overdue";
1285
1286
  items: {
1286
1287
  description: string;
1287
1288
  total: number;
@@ -1312,8 +1313,8 @@ declare function useInvoiceRepo(): {
1312
1313
  createUniqueIndex: () => Promise<void>;
1313
1314
  add: (value: TInvoice, session?: ClientSession) => Promise<void>;
1314
1315
  getByDueDate: (dueDate: Date, status?: TInvoice["status"]) => Promise<{
1315
- status: "pending" | "cancelled" | "paid" | "overdue";
1316
1316
  type: "other" | "organization-subscription" | "affiliate-subscription" | "one-time-payment";
1317
+ status: "pending" | "cancelled" | "paid" | "overdue";
1317
1318
  items: {
1318
1319
  description: string;
1319
1320
  total: number;
@@ -1348,8 +1349,8 @@ declare function useInvoiceRepo(): {
1348
1349
  pages: number;
1349
1350
  pageRange: string;
1350
1351
  } | {
1351
- status: "pending" | "cancelled" | "paid" | "overdue";
1352
1352
  type: "other" | "organization-subscription" | "affiliate-subscription" | "one-time-payment";
1353
+ status: "pending" | "cancelled" | "paid" | "overdue";
1353
1354
  items: {
1354
1355
  description: string;
1355
1356
  total: number;
@@ -1374,8 +1375,8 @@ declare function useInvoiceRepo(): {
1374
1375
  descriptions?: string | undefined;
1375
1376
  }>;
1376
1377
  getOverdueInvoices: (BATCH_SIZE?: number) => Promise<{
1377
- status: "pending" | "cancelled" | "paid" | "overdue";
1378
1378
  type: "other" | "organization-subscription" | "affiliate-subscription" | "one-time-payment";
1379
+ status: "pending" | "cancelled" | "paid" | "overdue";
1379
1380
  items: {
1380
1381
  description: string;
1381
1382
  total: number;
@@ -9860,8 +9861,8 @@ declare function useUserRepoV2(): {
9860
9861
  createTextIndex: () => Promise<void>;
9861
9862
  createUniqueIndex: () => Promise<void>;
9862
9863
  createUser: (value: TUser, session?: ClientSession) => Promise<ObjectId>;
9863
- getUserByEmail: (email: string) => Promise<TUser | mongodb.WithId<bson.Document> | null>;
9864
- getUserByEmailStatus: (email: string) => Promise<TUser | mongodb.WithId<bson.Document> | null>;
9864
+ getUserByEmail: (email: string) => Promise<mongodb.WithId<bson.Document> | TUser | null>;
9865
+ getUserByEmailStatus: (email: string) => Promise<mongodb.WithId<bson.Document> | TUser | null>;
9865
9866
  getUserById: (id: string | ObjectId) => Promise<TUser>;
9866
9867
  getUsers: ({ search, page, limit, sort, type, status, }: {
9867
9868
  search?: string | undefined;