@mohasinac/appkit 3.8.0 → 3.8.2
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/dist/features/admin/components/AdminCartsView.js +3 -6
- package/dist/features/seller/components/SellerArtView.js +1 -1
- package/dist/features/seller/components/SellerAuctionsView.js +3 -3
- package/dist/features/seller/components/SellerClassifiedView.js +1 -1
- package/dist/features/seller/components/SellerDigitalCodesView.js +1 -1
- package/dist/features/seller/components/SellerLiveView.js +1 -1
- package/dist/features/seller/components/SellerPreOrdersView.js +3 -3
- package/dist/features/seller/components/SellerPrizeDrawsView.js +3 -3
- package/dist/features/seller/components/SellerStickersView.js +1 -1
- package/dist/index.d.ts +1 -27
- package/dist/index.js +2 -81
- package/dist/security/index.d.ts +2 -1
- package/dist/security/index.js +5 -1
- package/dist/security/pii-encrypt.d.ts +2 -53
- package/dist/security/pii-encrypt.js +19 -100
- package/dist/security/pii-mask.d.ts +67 -0
- package/dist/security/pii-mask.js +100 -0
- package/dist/server.d.ts +26 -0
- package/dist/server.js +27 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +9 -0
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
4
3
|
import { sortBy } from "@mohasinac/appkit";
|
|
5
4
|
import React from "react";
|
|
6
|
-
import {
|
|
5
|
+
import { ListingLayout } from "../../../ui";
|
|
7
6
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
-
import { ADMIN_CART_OWNERSHIP_TABS } from "../constants/filter-tabs";
|
|
9
7
|
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
10
8
|
import { DataListingView } from "./DataListingView";
|
|
11
9
|
const ADMIN_CARTS_CONFIG = {
|
|
@@ -13,7 +11,7 @@ const ADMIN_CARTS_CONFIG = {
|
|
|
13
11
|
title: "Carts",
|
|
14
12
|
searchPlaceholder: "Search by user ID or session",
|
|
15
13
|
emptyLabel: "No carts found",
|
|
16
|
-
filterKeys: [
|
|
14
|
+
filterKeys: [],
|
|
17
15
|
defaultSort: sortBy("updatedAt", "DESC"),
|
|
18
16
|
queryKey: ["admin", "carts", "listing"],
|
|
19
17
|
endpoint: ADMIN_ENDPOINTS.ADMIN_CARTS,
|
|
@@ -36,8 +34,7 @@ const ADMIN_CARTS_CONFIG = {
|
|
|
36
34
|
};
|
|
37
35
|
}),
|
|
38
36
|
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
39
|
-
buildFilters: (
|
|
40
|
-
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Type", tabs: ADMIN_CART_OWNERSHIP_TABS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) })),
|
|
37
|
+
buildFilters: () => undefined,
|
|
41
38
|
};
|
|
42
39
|
export function AdminCartsView({ children, ...props }) {
|
|
43
40
|
if (React.Children.count(children) > 0) {
|
|
@@ -69,7 +69,7 @@ export function SellerArtView({ onCreateClick, onEditClick, onDelete, onBulkDele
|
|
|
69
69
|
sortOptions: [
|
|
70
70
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
71
71
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
72
|
-
{ value: "
|
|
72
|
+
{ value: "title", label: "Name A–Z" },
|
|
73
73
|
{ value: sortBy("price", "ASC"), label: "Price: Low–High" },
|
|
74
74
|
{ value: sortBy("price", "DESC"), label: "Price: High–Low" },
|
|
75
75
|
],
|
|
@@ -39,12 +39,12 @@ export function SellerAuctionsView({ children, onEditClick, onDelete, onBulkDele
|
|
|
39
39
|
searchPlaceholder: "Search auctions by product name",
|
|
40
40
|
emptyLabel: "No auctions found",
|
|
41
41
|
filterKeys: ["status"],
|
|
42
|
-
defaultSort: sortBy("
|
|
42
|
+
defaultSort: sortBy("auctionEndDate", "ASC"),
|
|
43
43
|
queryKey: ["seller", "auctions", "listing"],
|
|
44
44
|
endpoint: SELLER_ENDPOINTS.AUCTIONS,
|
|
45
45
|
sortOptions: [
|
|
46
|
-
{ value: "
|
|
47
|
-
{ value: sortBy("
|
|
46
|
+
{ value: "auctionEndDate", label: "Ending soon" },
|
|
47
|
+
{ value: sortBy("auctionEndDate", "DESC"), label: "Ending latest" },
|
|
48
48
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
49
49
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
50
50
|
],
|
|
@@ -70,7 +70,7 @@ export function SellerClassifiedView({ onCreateClick, onEditClick, onDelete, onB
|
|
|
70
70
|
sortOptions: [
|
|
71
71
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
72
72
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
73
|
-
{ value: "
|
|
73
|
+
{ value: "title", label: "Name A–Z" },
|
|
74
74
|
{ value: sortBy("price", "ASC"), label: "Price: Low–High" },
|
|
75
75
|
{ value: sortBy("price", "DESC"), label: "Price: High–Low" },
|
|
76
76
|
],
|
|
@@ -75,7 +75,7 @@ export function SellerDigitalCodesView({ onCreateClick, onEditClick, onDelete, o
|
|
|
75
75
|
sortOptions: [
|
|
76
76
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
77
77
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
78
|
-
{ value: "
|
|
78
|
+
{ value: "title", label: "Name A–Z" },
|
|
79
79
|
{ value: sortBy("price", "ASC"), label: "Price: Low–High" },
|
|
80
80
|
],
|
|
81
81
|
columns: COLUMNS,
|
|
@@ -74,7 +74,7 @@ export function SellerLiveView({ onCreateClick, onEditClick, onDelete, onBulkDel
|
|
|
74
74
|
sortOptions: [
|
|
75
75
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
76
76
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
77
|
-
{ value: "
|
|
77
|
+
{ value: "title", label: "Name A–Z" },
|
|
78
78
|
{ value: sortBy("price", "ASC"), label: "Price: Low–High" },
|
|
79
79
|
{ value: sortBy("price", "DESC"), label: "Price: High–Low" },
|
|
80
80
|
],
|
|
@@ -88,7 +88,7 @@ export function SellerPreOrdersView({ children, onDelete, ...props }) {
|
|
|
88
88
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
89
89
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
90
90
|
{ value: "title", label: "Title A–Z" },
|
|
91
|
-
{ value: "
|
|
91
|
+
{ value: "preOrderDeliveryDate", label: "Delivery Soon" },
|
|
92
92
|
],
|
|
93
93
|
columns: PRE_ORDER_COLUMNS,
|
|
94
94
|
mapRows: (response) => toRecordArray(response.products).map((item, index) => {
|
|
@@ -99,8 +99,8 @@ export function SellerPreOrdersView({ children, onDelete, ...props }) {
|
|
|
99
99
|
secondary: toStringValue(item.condition, ""),
|
|
100
100
|
status: toStringValue(item.status, "draft"),
|
|
101
101
|
price: priceRaw ? toRupees(priceRaw) : "—",
|
|
102
|
-
deliveryDate: item.
|
|
103
|
-
? toRelativeDate(item.
|
|
102
|
+
deliveryDate: item.preOrderDeliveryDate
|
|
103
|
+
? toRelativeDate(item.preOrderDeliveryDate)
|
|
104
104
|
: "TBA",
|
|
105
105
|
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
106
106
|
imageUrl: toStringValue(item.mainImage ?? item.images?.[0], undefined),
|
|
@@ -85,7 +85,7 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
|
|
|
85
85
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
86
86
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
87
87
|
{ value: "title", label: "Title A–Z" },
|
|
88
|
-
{ value: "
|
|
88
|
+
{ value: "prizeRevealWindowEnd", label: "Draw Date Soon" },
|
|
89
89
|
],
|
|
90
90
|
columns: PRIZE_DRAW_COLUMNS,
|
|
91
91
|
mapRows: (response) => toRecordArray(response.products).map((item, index) => {
|
|
@@ -96,8 +96,8 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
|
|
|
96
96
|
secondary: toStringValue(item.condition, ""),
|
|
97
97
|
status: toStringValue(item.status, "draft"),
|
|
98
98
|
entryFee: priceRaw ? toRupees(priceRaw) : "Free",
|
|
99
|
-
drawDate: item.
|
|
100
|
-
? toRelativeDate(item.
|
|
99
|
+
drawDate: item.prizeRevealWindowEnd
|
|
100
|
+
? toRelativeDate(item.prizeRevealWindowEnd)
|
|
101
101
|
: "TBA",
|
|
102
102
|
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
103
103
|
imageUrl: toStringValue(item.mainImage ?? item.images?.[0], undefined),
|
|
@@ -69,7 +69,7 @@ export function SellerStickersView({ onCreateClick, onEditClick, onDelete, onBul
|
|
|
69
69
|
sortOptions: [
|
|
70
70
|
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
71
71
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
72
|
-
{ value: "
|
|
72
|
+
{ value: "title", label: "Name A–Z" },
|
|
73
73
|
{ value: sortBy("price", "ASC"), label: "Price: Low–High" },
|
|
74
74
|
{ value: sortBy("price", "DESC"), label: "Price: High–Low" },
|
|
75
75
|
],
|
package/dist/index.d.ts
CHANGED
|
@@ -444,10 +444,8 @@ export { BID_PII_FIELDS } from "./security/index";
|
|
|
444
444
|
export { CHAT_PII_FIELDS } from "./security/index";
|
|
445
445
|
export { Can } from "./security/index";
|
|
446
446
|
export { DEFAULT_ROLES } from "./security/index";
|
|
447
|
-
export { ENC_PREFIX } from "./security/index";
|
|
448
447
|
export { EVENT_ENTRY_PII_FIELDS } from "./security/index";
|
|
449
448
|
export { LOTTERY_ENTRY_PII_FIELDS } from "./security/index";
|
|
450
|
-
export { HMAC_PREFIX } from "./security/index";
|
|
451
449
|
export { NEWSLETTER_PII_FIELDS } from "./security/index";
|
|
452
450
|
export { NEWSLETTER_PII_INDEX_MAP } from "./security/index";
|
|
453
451
|
export { OFFER_PII_FIELDS } from "./security/index";
|
|
@@ -461,7 +459,6 @@ export { TOKEN_PII_FIELDS } from "./security/index";
|
|
|
461
459
|
export { TOKEN_PII_INDEX_MAP } from "./security/index";
|
|
462
460
|
export { USER_PII_FIELDS } from "./security/index";
|
|
463
461
|
export { USER_PII_INDEX_MAP } from "./security/index";
|
|
464
|
-
export { addPiiIndices } from "./security/index";
|
|
465
462
|
export { applyRateLimit } from "./security/index";
|
|
466
463
|
export { buildCSP } from "./security/index";
|
|
467
464
|
export { canChangeRole } from "./security/index";
|
|
@@ -470,42 +467,19 @@ export { createRbacHook } from "./security/index";
|
|
|
470
467
|
export { createRbacMiddleware } from "./security/index";
|
|
471
468
|
export { createRequirePermission } from "./security/index";
|
|
472
469
|
export { createRequirePermissionSync } from "./security/index";
|
|
473
|
-
export { decryptPayoutBankAccount } from "./security/index";
|
|
474
|
-
export { decryptPayoutDetails } from "./security/index";
|
|
475
|
-
export { decryptPii } from "./security/index";
|
|
476
|
-
export { decryptPiiFields } from "./security/index";
|
|
477
470
|
export { decryptSecret } from "./security/index";
|
|
478
|
-
export { decryptShippingAddress } from "./security/index";
|
|
479
|
-
export { decryptShippingConfig } from "./security/index";
|
|
480
|
-
export { decryptValue } from "./security/index";
|
|
481
|
-
export { encryptPayoutBankAccount } from "./security/index";
|
|
482
|
-
export { encryptPayoutDetails } from "./security/index";
|
|
483
|
-
export { encryptPii } from "./security/index";
|
|
484
|
-
export { encryptPiiFields } from "./security/index";
|
|
485
471
|
export { encryptSecret } from "./security/index";
|
|
486
|
-
export { encryptShippingAddress } from "./security/index";
|
|
487
|
-
export { encryptShippingConfig } from "./security/index";
|
|
488
|
-
export { encryptValue } from "./security/index";
|
|
489
472
|
export { generateNonce } from "./security/index";
|
|
490
|
-
export { getPiiConfigError } from "./security/index";
|
|
491
473
|
export { getRoleLevel } from "./security/index";
|
|
492
474
|
export { hasAllPermissions } from "./security/index";
|
|
493
475
|
export { hasAnyPermission } from "./security/index";
|
|
494
476
|
export { hasPermission } from "./security/index";
|
|
495
|
-
export { hmacBlindIndex } from "./security/index";
|
|
496
|
-
export { isPiiEncrypted } from "./security/index";
|
|
497
477
|
export { isSecretEncrypted } from "./security/index";
|
|
498
|
-
export { maskEmail } from "./security/index";
|
|
499
|
-
export { maskName } from "./security/index";
|
|
500
|
-
export { maskOfferForSeller } from "./security/index";
|
|
501
|
-
export { maskPublicBid } from "./security/index";
|
|
502
|
-
export { maskPublicEventEntry } from "./security/index";
|
|
503
|
-
export { maskPublicReview } from "./security/index";
|
|
504
478
|
export { maskSecret } from "./security/index";
|
|
505
|
-
export { piiBlindIndex } from "./security/index";
|
|
506
479
|
export { rateLimit } from "./security/index";
|
|
507
480
|
export { rateLimitByIdentifier } from "./security/index";
|
|
508
481
|
export { redactPii, safeDisplayName, safeDisplayEmail } from "./security/index";
|
|
482
|
+
export { isPiiEncrypted, maskName, maskEmail, maskPublicReview, maskPublicBid, maskPublicEventEntry, maskOfferForSeller, ENC_PREFIX, HMAC_PREFIX, } from "./security/index";
|
|
509
483
|
export { requireActiveAccount } from "./security/index";
|
|
510
484
|
export { requireAuth } from "./security/index";
|
|
511
485
|
export { requireEmailVerified } from "./security/index";
|
package/dist/index.js
CHANGED
|
@@ -782,17 +782,11 @@ export { Can } from "./security/index";
|
|
|
782
782
|
// DEFAULT_ROLES - Constant used across modules.
|
|
783
783
|
export { DEFAULT_ROLES } from "./security/index";
|
|
784
784
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
785
|
-
// ENC_PREFIX - Constant used across modules.
|
|
786
|
-
export { ENC_PREFIX } from "./security/index";
|
|
787
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
788
785
|
// EVENT_ENTRY_PII_FIELDS - Constant used across modules.
|
|
789
786
|
export { EVENT_ENTRY_PII_FIELDS } from "./security/index";
|
|
790
787
|
// LOTTERY_ENTRY_PII_FIELDS - PII fields for lottery entries.
|
|
791
788
|
export { LOTTERY_ENTRY_PII_FIELDS } from "./security/index";
|
|
792
789
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
793
|
-
// HMAC_PREFIX - Constant used across modules.
|
|
794
|
-
export { HMAC_PREFIX } from "./security/index";
|
|
795
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
796
790
|
// NEWSLETTER_PII_FIELDS - Constant used across modules.
|
|
797
791
|
export { NEWSLETTER_PII_FIELDS } from "./security/index";
|
|
798
792
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
@@ -832,9 +826,6 @@ export { USER_PII_FIELDS } from "./security/index";
|
|
|
832
826
|
// USER_PII_INDEX_MAP - Constant used across modules.
|
|
833
827
|
export { USER_PII_INDEX_MAP } from "./security/index";
|
|
834
828
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
835
|
-
// addPiiIndices - Helper for add pii indices.
|
|
836
|
-
export { addPiiIndices } from "./security/index";
|
|
837
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
838
829
|
// applyRateLimit - Helper for apply rate limit.
|
|
839
830
|
export { applyRateLimit } from "./security/index";
|
|
840
831
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
@@ -859,60 +850,15 @@ export { createRequirePermission } from "./security/index";
|
|
|
859
850
|
// createRequirePermissionSync - Helper for create require permission sync.
|
|
860
851
|
export { createRequirePermissionSync } from "./security/index";
|
|
861
852
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
862
|
-
// decryptPayoutBankAccount - Helper for decrypt payout bank account.
|
|
863
|
-
export { decryptPayoutBankAccount } from "./security/index";
|
|
864
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
865
|
-
// decryptPayoutDetails - Helper for decrypt payout details.
|
|
866
|
-
export { decryptPayoutDetails } from "./security/index";
|
|
867
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
868
|
-
// decryptPii - Helper for decrypt pii.
|
|
869
|
-
export { decryptPii } from "./security/index";
|
|
870
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
871
|
-
// decryptPiiFields - Helper for decrypt pii fields.
|
|
872
|
-
export { decryptPiiFields } from "./security/index";
|
|
873
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
874
853
|
// decryptSecret - Helper for decrypt secret.
|
|
875
854
|
export { decryptSecret } from "./security/index";
|
|
876
855
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
877
|
-
// decryptShippingAddress - Helper for decrypt shipping address.
|
|
878
|
-
export { decryptShippingAddress } from "./security/index";
|
|
879
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
880
|
-
// decryptShippingConfig - Helper for decrypt shipping config.
|
|
881
|
-
export { decryptShippingConfig } from "./security/index";
|
|
882
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
883
|
-
// decryptValue - Helper for decrypt value.
|
|
884
|
-
export { decryptValue } from "./security/index";
|
|
885
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
886
|
-
// encryptPayoutBankAccount - Helper for encrypt payout bank account.
|
|
887
|
-
export { encryptPayoutBankAccount } from "./security/index";
|
|
888
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
889
|
-
// encryptPayoutDetails - Helper for encrypt payout details.
|
|
890
|
-
export { encryptPayoutDetails } from "./security/index";
|
|
891
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
892
|
-
// encryptPii - Helper for encrypt pii.
|
|
893
|
-
export { encryptPii } from "./security/index";
|
|
894
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
895
|
-
// encryptPiiFields - Helper for encrypt pii fields.
|
|
896
|
-
export { encryptPiiFields } from "./security/index";
|
|
897
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
898
856
|
// encryptSecret - Helper for encrypt secret.
|
|
899
857
|
export { encryptSecret } from "./security/index";
|
|
900
858
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
901
|
-
// encryptShippingAddress - Helper for encrypt shipping address.
|
|
902
|
-
export { encryptShippingAddress } from "./security/index";
|
|
903
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
904
|
-
// encryptShippingConfig - Helper for encrypt shipping config.
|
|
905
|
-
export { encryptShippingConfig } from "./security/index";
|
|
906
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
907
|
-
// encryptValue - Helper for encrypt value.
|
|
908
|
-
export { encryptValue } from "./security/index";
|
|
909
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
910
859
|
// generateNonce - Helper for generate nonce.
|
|
911
860
|
export { generateNonce } from "./security/index";
|
|
912
861
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
913
|
-
// getPiiConfigError - Helper for get pii config error.
|
|
914
|
-
export { getPiiConfigError } from "./security/index";
|
|
915
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
916
862
|
// getRoleLevel - Helper for get role level.
|
|
917
863
|
export { getRoleLevel } from "./security/index";
|
|
918
864
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
@@ -925,39 +871,12 @@ export { hasAnyPermission } from "./security/index";
|
|
|
925
871
|
// hasPermission - Model for has permission.
|
|
926
872
|
export { hasPermission } from "./security/index";
|
|
927
873
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
928
|
-
// hmacBlindIndex - Shared export for hmac blind index.
|
|
929
|
-
export { hmacBlindIndex } from "./security/index";
|
|
930
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
931
|
-
// isPiiEncrypted - Shared export for is pii encrypted.
|
|
932
|
-
export { isPiiEncrypted } from "./security/index";
|
|
933
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
934
874
|
// isSecretEncrypted - Shared export for is secret encrypted.
|
|
935
875
|
export { isSecretEncrypted } from "./security/index";
|
|
936
876
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
937
|
-
// maskEmail - Helper for mask email.
|
|
938
|
-
export { maskEmail } from "./security/index";
|
|
939
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
940
|
-
// maskName - Helper for mask name.
|
|
941
|
-
export { maskName } from "./security/index";
|
|
942
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
943
|
-
// maskOfferForSeller - Helper for mask offer for seller.
|
|
944
|
-
export { maskOfferForSeller } from "./security/index";
|
|
945
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
946
|
-
// maskPublicBid - Helper for mask public bid.
|
|
947
|
-
export { maskPublicBid } from "./security/index";
|
|
948
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
949
|
-
// maskPublicEventEntry - Helper for mask public event entry.
|
|
950
|
-
export { maskPublicEventEntry } from "./security/index";
|
|
951
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
952
|
-
// maskPublicReview - Helper for mask public review.
|
|
953
|
-
export { maskPublicReview } from "./security/index";
|
|
954
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
955
877
|
// maskSecret - Helper for mask secret.
|
|
956
878
|
export { maskSecret } from "./security/index";
|
|
957
879
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
958
|
-
// piiBlindIndex - Shared export for pii blind index.
|
|
959
|
-
export { piiBlindIndex } from "./security/index";
|
|
960
|
-
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
961
880
|
// rateLimit - Shared export for rate limit.
|
|
962
881
|
export { rateLimit } from "./security/index";
|
|
963
882
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
@@ -966,6 +885,8 @@ export { rateLimitByIdentifier } from "./security/index";
|
|
|
966
885
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
967
886
|
// redactPii - Shared export for redact pii.
|
|
968
887
|
export { redactPii, safeDisplayName, safeDisplayEmail } from "./security/index";
|
|
888
|
+
// [CLIENT-SSR]-Runs in both SSR and browser â€" pure display-masking helpers, no crypto dependency.
|
|
889
|
+
export { isPiiEncrypted, maskName, maskEmail, maskPublicReview, maskPublicBid, maskPublicEventEntry, maskOfferForSeller, ENC_PREFIX, HMAC_PREFIX, } from "./security/index";
|
|
969
890
|
// [CLIENT-SSR]-Runs in both SSR and browser â€" React component or hook that does not depend on browser-only APIs.
|
|
970
891
|
// requireActiveAccount - Shared export for require active account.
|
|
971
892
|
export { requireActiveAccount } from "./security/index";
|
package/dist/security/index.d.ts
CHANGED
|
@@ -4,7 +4,8 @@ export type { RateLimitConfig, RateLimitResult } from "./rate-limit";
|
|
|
4
4
|
export { requireAuth, requireRole, requireOwnership, requireEmailVerified, requireActiveAccount, canChangeRole, getRoleLevel, } from "./authorization";
|
|
5
5
|
export type { UserRole } from "./authorization";
|
|
6
6
|
export { redactPii, safeDisplayName, safeDisplayEmail, maskIp } from "./pii-redact";
|
|
7
|
-
export { encryptValue, decryptValue, hmacBlindIndex, encryptPiiFields, decryptPiiFields, encryptPii, decryptPii,
|
|
7
|
+
export { encryptValue, decryptValue, hmacBlindIndex, encryptPiiFields, decryptPiiFields, encryptPii, decryptPii, piiBlindIndex, addPiiIndices, getPiiConfigError, encryptShippingAddress, decryptShippingAddress, encryptPayoutDetails, decryptPayoutDetails, encryptShippingConfig, decryptShippingConfig, encryptPayoutBankAccount, decryptPayoutBankAccount, } from "./pii-encrypt";
|
|
8
|
+
export { isPiiEncrypted, maskName, maskEmail, maskPublicReview, maskPublicBid, maskPublicEventEntry, maskOfferForSeller, ENC_PREFIX, HMAC_PREFIX, } from "./pii-mask";
|
|
8
9
|
export { USER_PII_FIELDS, USER_PII_INDEX_MAP, ADDRESS_PII_FIELDS, ORDER_PII_FIELDS, PAYOUT_PII_FIELDS, PAYOUT_PII_INDEX_MAP, BID_PII_FIELDS, NEWSLETTER_PII_FIELDS, NEWSLETTER_PII_INDEX_MAP, TOKEN_PII_FIELDS, TOKEN_PII_INDEX_MAP, REVIEW_PII_FIELDS, REVIEW_PII_INDEX_MAP, OFFER_PII_FIELDS, CHAT_PII_FIELDS, EVENT_ENTRY_PII_FIELDS, LOTTERY_ENTRY_PII_FIELDS, PAYMENT_METHOD_PII_FIELDS, } from "./pii-schemas";
|
|
9
10
|
export { encryptSecret, decryptSecret, isSecretEncrypted, maskSecret, } from "./settings-encryption";
|
|
10
11
|
export type { Permission, RoleDefinition, RbacConfig, ResolvedUser, RbacHookReturn, } from "./rbac";
|
package/dist/security/index.js
CHANGED
|
@@ -2,7 +2,11 @@ export { generateNonce, buildCSP } from "./csp";
|
|
|
2
2
|
export { rateLimit, applyRateLimit, rateLimitByIdentifier, RateLimitPresets, clearRateLimitStore, } from "./rate-limit";
|
|
3
3
|
export { requireAuth, requireRole, requireOwnership, requireEmailVerified, requireActiveAccount, canChangeRole, getRoleLevel, } from "./authorization";
|
|
4
4
|
export { redactPii, safeDisplayName, safeDisplayEmail, maskIp } from "./pii-redact";
|
|
5
|
-
export { encryptValue, decryptValue, hmacBlindIndex, encryptPiiFields, decryptPiiFields, encryptPii, decryptPii,
|
|
5
|
+
export { encryptValue, decryptValue, hmacBlindIndex, encryptPiiFields, decryptPiiFields, encryptPii, decryptPii, piiBlindIndex, addPiiIndices, getPiiConfigError, encryptShippingAddress, decryptShippingAddress, encryptPayoutDetails, decryptPayoutDetails, encryptShippingConfig, decryptShippingConfig, encryptPayoutBankAccount, decryptPayoutBankAccount, } from "./pii-encrypt";
|
|
6
|
+
// Crypto-free display-masking helpers -- sourced from pii-mask.ts (not
|
|
7
|
+
// pii-encrypt.ts) so this barrel stays safe to re-export from the
|
|
8
|
+
// universal client+server index.ts. See pii-mask.ts's header comment.
|
|
9
|
+
export { isPiiEncrypted, maskName, maskEmail, maskPublicReview, maskPublicBid, maskPublicEventEntry, maskOfferForSeller, ENC_PREFIX, HMAC_PREFIX, } from "./pii-mask";
|
|
6
10
|
export { USER_PII_FIELDS, USER_PII_INDEX_MAP, ADDRESS_PII_FIELDS, ORDER_PII_FIELDS, PAYOUT_PII_FIELDS, PAYOUT_PII_INDEX_MAP, BID_PII_FIELDS, NEWSLETTER_PII_FIELDS, NEWSLETTER_PII_INDEX_MAP, TOKEN_PII_FIELDS, TOKEN_PII_INDEX_MAP, REVIEW_PII_FIELDS, REVIEW_PII_INDEX_MAP, OFFER_PII_FIELDS, CHAT_PII_FIELDS, EVENT_ENTRY_PII_FIELDS, LOTTERY_ENTRY_PII_FIELDS, PAYMENT_METHOD_PII_FIELDS, } from "./pii-schemas";
|
|
7
11
|
export { encryptSecret, decryptSecret, isSecretEncrypted, maskSecret, } from "./settings-encryption";
|
|
8
12
|
export { DEFAULT_ROLES, resolvePermissions, hasPermission, hasAllPermissions, hasAnyPermission, createRbacHook, Can, createRequirePermission, createRequirePermissionSync, createRbacMiddleware, } from "./rbac";
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* Ciphertext format: "enc:v1:<iv_b64>:<ciphertext_b64>:<authtag_b64>"
|
|
10
10
|
* Blind index format: "hmac-sha256:<sha256_hex>"
|
|
11
11
|
*/
|
|
12
|
-
|
|
13
|
-
export
|
|
12
|
+
import { ENC_PREFIX, HMAC_PREFIX, isPiiEncrypted } from "./pii-mask";
|
|
13
|
+
export { ENC_PREFIX, HMAC_PREFIX, isPiiEncrypted };
|
|
14
14
|
export declare function getPiiConfigError(): string | null;
|
|
15
15
|
/** Encrypt a plaintext string with AES-256-GCM. */
|
|
16
16
|
export declare function encryptValue(plaintext: string): string;
|
|
@@ -36,8 +36,6 @@ export declare function decryptPiiFields<T extends object>(doc: T, piiFields: st
|
|
|
36
36
|
export declare function encryptPii(plaintext: string | null | undefined): string | null | undefined;
|
|
37
37
|
/** Alias for decryptValue — decrypts a PII ciphertext. */
|
|
38
38
|
export declare function decryptPii(ciphertext: string | null | undefined): string | null | undefined;
|
|
39
|
-
/** Check if a value is encrypted by our PII system. */
|
|
40
|
-
export declare function isPiiEncrypted(value: string): boolean;
|
|
41
39
|
/** Alias for hmacBlindIndex. */
|
|
42
40
|
export declare function piiBlindIndex(plaintext: string): string;
|
|
43
41
|
/**
|
|
@@ -80,52 +78,3 @@ export declare function encryptPayoutBankAccount<T extends object>(bank: T | und
|
|
|
80
78
|
* Decrypt PII on a payout bankAccount sub-object from the payouts collection.
|
|
81
79
|
*/
|
|
82
80
|
export declare function decryptPayoutBankAccount<T extends object>(bank: T | undefined | null): T | undefined | null;
|
|
83
|
-
/**
|
|
84
|
-
* Mask a person's name for public display.
|
|
85
|
-
* Each word is reduced to its first letter followed by "***".
|
|
86
|
-
*
|
|
87
|
-
* "John Doe" → "J*** D***"
|
|
88
|
-
* "Alice" → "A***"
|
|
89
|
-
*
|
|
90
|
-
* If the value is still an encrypted blob (e.g. PII_ENCRYPTION_KEY not set),
|
|
91
|
-
* returns "Anonymous" so encrypted ciphertext never leaks to the UI.
|
|
92
|
-
*/
|
|
93
|
-
export declare function maskName(name: string | null | undefined): string;
|
|
94
|
-
/**
|
|
95
|
-
* Mask an email address for public display.
|
|
96
|
-
* "john.doe@example.com" → "j***@***.com"
|
|
97
|
-
*/
|
|
98
|
-
export declare function maskEmail(email: string | null | undefined): string;
|
|
99
|
-
/**
|
|
100
|
-
* Return a copy of a review document with PII fields masked for public display.
|
|
101
|
-
* Admin/owner endpoints should NOT call this — use the raw document instead.
|
|
102
|
-
*/
|
|
103
|
-
export declare function maskPublicReview<T extends {
|
|
104
|
-
userName: string;
|
|
105
|
-
}>(review: T): T;
|
|
106
|
-
/**
|
|
107
|
-
* Return a copy of a bid document with PII masked for public display
|
|
108
|
-
* (auction product page).
|
|
109
|
-
*/
|
|
110
|
-
export declare function maskPublicBid<T extends {
|
|
111
|
-
userName: string;
|
|
112
|
-
}>(bid: T): T;
|
|
113
|
-
/**
|
|
114
|
-
* Return a copy of an event-entry document with PII masked for the leaderboard.
|
|
115
|
-
* ipAddress and userEmail are never sent to the client;
|
|
116
|
-
* only userDisplayName needs masking for a public leaderboard view.
|
|
117
|
-
*/
|
|
118
|
-
export declare function maskPublicEventEntry<T extends {
|
|
119
|
-
userDisplayName?: string;
|
|
120
|
-
userEmail?: string;
|
|
121
|
-
ipAddress?: string;
|
|
122
|
-
}>(entry: T): Omit<T, "userEmail" | "ipAddress">;
|
|
123
|
-
/**
|
|
124
|
-
* Return a copy of an offer document with buyer PII masked for the seller view.
|
|
125
|
-
* The seller needs to know an offer was made and its amount, but the buyer's
|
|
126
|
-
* full name and email should remain private until the order is confirmed.
|
|
127
|
-
*/
|
|
128
|
-
export declare function maskOfferForSeller<T extends {
|
|
129
|
-
buyerName: string;
|
|
130
|
-
buyerEmail: string;
|
|
131
|
-
}>(offer: T): T;
|
|
@@ -9,22 +9,26 @@
|
|
|
9
9
|
* Ciphertext format: "enc:v1:<iv_b64>:<ciphertext_b64>:<authtag_b64>"
|
|
10
10
|
* Blind index format: "hmac-sha256:<sha256_hex>"
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
import { ENC_PREFIX, HMAC_PREFIX, isPiiEncrypted } from "./pii-mask";
|
|
13
|
+
// Re-exported for backward-compat with existing direct imports of these
|
|
14
|
+
// symbols from this file — the actual crypto-free definitions live in
|
|
15
|
+
// pii-mask.ts (see that file's header comment for why the split exists).
|
|
16
|
+
export { ENC_PREFIX, HMAC_PREFIX, isPiiEncrypted };
|
|
17
|
+
// crypto is a Node.js built-in. A bare, non-static `require(...)` call
|
|
18
|
+
// (never a top-level `import ... from "node:module"`/"crypto") is
|
|
19
|
+
// deliberate: bundlers targeting the browser (Turbopack in particular)
|
|
20
|
+
// statically resolve every top-level import in a module's chain before any
|
|
21
|
+
// tree-shaking pass, and hard-fail immediately on an unresolvable Node
|
|
22
|
+
// builtin even if the symbol using it is never actually consumed
|
|
23
|
+
// client-side. A bare `require` reference inside a function body is a
|
|
24
|
+
// runtime lookup, not a static import, so it's invisible to that pass.
|
|
25
|
+
// This only works where an ambient `require` global exists: Next.js's own
|
|
26
|
+
// server bundle (webpack/Turbopack) provides one automatically; the one
|
|
27
|
+
// place that doesn't is a standalone pure-ESM script importing
|
|
28
|
+
// @mohasinac/appkit directly (appkit/scripts/seed-cli.mjs), which sets
|
|
29
|
+
// `globalThis.require` itself before importing appkit — see that file.
|
|
30
|
+
function nodeCrypto() { return require("crypto"); }
|
|
25
31
|
const ALGO = "aes-256-gcm";
|
|
26
|
-
export const ENC_PREFIX = "enc:v1:";
|
|
27
|
-
export const HMAC_PREFIX = "hmac-sha256:";
|
|
28
32
|
function normalizePiiSecretValue(raw) {
|
|
29
33
|
return (raw ?? "").replace(/(?:\\r|\\n|\r|\n)+$/g, "").trim();
|
|
30
34
|
}
|
|
@@ -139,10 +143,6 @@ export function decryptPii(ciphertext) {
|
|
|
139
143
|
return ciphertext;
|
|
140
144
|
return decryptValue(ciphertext);
|
|
141
145
|
}
|
|
142
|
-
/** Check if a value is encrypted by our PII system. */
|
|
143
|
-
export function isPiiEncrypted(value) {
|
|
144
|
-
return typeof value === "string" && value.startsWith(ENC_PREFIX);
|
|
145
|
-
}
|
|
146
146
|
/** Alias for hmacBlindIndex. */
|
|
147
147
|
export function piiBlindIndex(plaintext) {
|
|
148
148
|
return hmacBlindIndex(plaintext);
|
|
@@ -278,84 +278,3 @@ export function decryptPayoutBankAccount(bank) {
|
|
|
278
278
|
return bank;
|
|
279
279
|
return decryptPiiFields({ ...bank }, []);
|
|
280
280
|
}
|
|
281
|
-
// --- Public display masking -------------------------------------------------
|
|
282
|
-
/**
|
|
283
|
-
* Mask a person's name for public display.
|
|
284
|
-
* Each word is reduced to its first letter followed by "***".
|
|
285
|
-
*
|
|
286
|
-
* "John Doe" → "J*** D***"
|
|
287
|
-
* "Alice" → "A***"
|
|
288
|
-
*
|
|
289
|
-
* If the value is still an encrypted blob (e.g. PII_ENCRYPTION_KEY not set),
|
|
290
|
-
* returns "Anonymous" so encrypted ciphertext never leaks to the UI.
|
|
291
|
-
*/
|
|
292
|
-
export function maskName(name) {
|
|
293
|
-
if (!name || typeof name !== "string")
|
|
294
|
-
return "Anonymous";
|
|
295
|
-
if (isPiiEncrypted(name))
|
|
296
|
-
return "Anonymous";
|
|
297
|
-
return name
|
|
298
|
-
.trim()
|
|
299
|
-
.split(/\s+/)
|
|
300
|
-
.filter(Boolean)
|
|
301
|
-
.map((word) => word[0] + "***")
|
|
302
|
-
.join(" ");
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Mask an email address for public display.
|
|
306
|
-
* "john.doe@example.com" → "j***@***.com"
|
|
307
|
-
*/
|
|
308
|
-
export function maskEmail(email) {
|
|
309
|
-
if (!email || typeof email !== "string")
|
|
310
|
-
return "***@***.***";
|
|
311
|
-
if (isPiiEncrypted(email))
|
|
312
|
-
return "***@***.***";
|
|
313
|
-
const atIdx = email.indexOf("@");
|
|
314
|
-
if (atIdx < 0)
|
|
315
|
-
return "***";
|
|
316
|
-
const local = email.slice(0, atIdx);
|
|
317
|
-
const domain = email.slice(atIdx + 1);
|
|
318
|
-
const dotIdx = domain.lastIndexOf(".");
|
|
319
|
-
const domainMasked = dotIdx > 0 ? "***" + domain.slice(dotIdx) : "***";
|
|
320
|
-
return (local[0] ?? "*") + "***@" + domainMasked;
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Return a copy of a review document with PII fields masked for public display.
|
|
324
|
-
* Admin/owner endpoints should NOT call this — use the raw document instead.
|
|
325
|
-
*/
|
|
326
|
-
export function maskPublicReview(review) {
|
|
327
|
-
return { ...review, userName: maskName(review.userName) };
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Return a copy of a bid document with PII masked for public display
|
|
331
|
-
* (auction product page).
|
|
332
|
-
*/
|
|
333
|
-
export function maskPublicBid(bid) {
|
|
334
|
-
return { ...bid };
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Return a copy of an event-entry document with PII masked for the leaderboard.
|
|
338
|
-
* ipAddress and userEmail are never sent to the client;
|
|
339
|
-
* only userDisplayName needs masking for a public leaderboard view.
|
|
340
|
-
*/
|
|
341
|
-
export function maskPublicEventEntry(entry) {
|
|
342
|
-
const { userEmail: _e, ipAddress: _ip, ...rest } = entry;
|
|
343
|
-
return {
|
|
344
|
-
...rest,
|
|
345
|
-
...(rest.userDisplayName !== undefined
|
|
346
|
-
? { userDisplayName: maskName(rest.userDisplayName) }
|
|
347
|
-
: {}),
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Return a copy of an offer document with buyer PII masked for the seller view.
|
|
352
|
-
* The seller needs to know an offer was made and its amount, but the buyer's
|
|
353
|
-
* full name and email should remain private until the order is confirmed.
|
|
354
|
-
*/
|
|
355
|
-
export function maskOfferForSeller(offer) {
|
|
356
|
-
return {
|
|
357
|
-
...offer,
|
|
358
|
-
buyerName: maskName(offer.buyerName),
|
|
359
|
-
buyerEmail: maskEmail(offer.buyerEmail),
|
|
360
|
-
};
|
|
361
|
-
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PII display-masking helpers — pure string manipulation, zero runtime
|
|
3
|
+
* dependencies. Split out from pii-encrypt.ts so these can be exported
|
|
4
|
+
* from the universal client+server barrel (index.ts): pii-encrypt.ts has
|
|
5
|
+
* a top-level `import { createRequire } from "node:module"` (needed for
|
|
6
|
+
* the AES-256-GCM/HMAC functions to work under both Next.js's bundler and
|
|
7
|
+
* a pure-ESM standalone script context) that poisons the whole module for
|
|
8
|
+
* client bundling — Turbopack statically resolves every export in a file
|
|
9
|
+
* before tree-shaking, so re-exporting even one crypto-free symbol from
|
|
10
|
+
* pii-encrypt.ts still pulls `node:module` into the client graph and hard
|
|
11
|
+
* fails the browser build. This file has no such import and is safe
|
|
12
|
+
* anywhere `ReviewModal`/`ReviewDetailShell`/`ReviewsList` (client
|
|
13
|
+
* components displaying masked reviewer names) can reach it.
|
|
14
|
+
*/
|
|
15
|
+
export declare const ENC_PREFIX = "enc:v1:";
|
|
16
|
+
export declare const HMAC_PREFIX = "hmac-sha256:";
|
|
17
|
+
/** Check if a value is encrypted by our PII system. */
|
|
18
|
+
export declare function isPiiEncrypted(value: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Mask a person's name for public display.
|
|
21
|
+
* Each word is reduced to its first letter followed by "***".
|
|
22
|
+
*
|
|
23
|
+
* "John Doe" → "J*** D***"
|
|
24
|
+
* "Alice" → "A***"
|
|
25
|
+
*
|
|
26
|
+
* If the value is still an encrypted blob (e.g. PII_ENCRYPTION_KEY not set),
|
|
27
|
+
* returns "Anonymous" so encrypted ciphertext never leaks to the UI.
|
|
28
|
+
*/
|
|
29
|
+
export declare function maskName(name: string | null | undefined): string;
|
|
30
|
+
/**
|
|
31
|
+
* Mask an email address for public display.
|
|
32
|
+
* "john.doe@example.com" → "j***@***.com"
|
|
33
|
+
*/
|
|
34
|
+
export declare function maskEmail(email: string | null | undefined): string;
|
|
35
|
+
/**
|
|
36
|
+
* Return a copy of a review document with PII fields masked for public display.
|
|
37
|
+
* Admin/owner endpoints should NOT call this — use the raw document instead.
|
|
38
|
+
*/
|
|
39
|
+
export declare function maskPublicReview<T extends {
|
|
40
|
+
userName: string;
|
|
41
|
+
}>(review: T): T;
|
|
42
|
+
/**
|
|
43
|
+
* Return a copy of a bid document with PII masked for public display
|
|
44
|
+
* (auction product page).
|
|
45
|
+
*/
|
|
46
|
+
export declare function maskPublicBid<T extends {
|
|
47
|
+
userName: string;
|
|
48
|
+
}>(bid: T): T;
|
|
49
|
+
/**
|
|
50
|
+
* Return a copy of an event-entry document with PII masked for the leaderboard.
|
|
51
|
+
* ipAddress and userEmail are never sent to the client;
|
|
52
|
+
* only userDisplayName needs masking for a public leaderboard view.
|
|
53
|
+
*/
|
|
54
|
+
export declare function maskPublicEventEntry<T extends {
|
|
55
|
+
userDisplayName?: string;
|
|
56
|
+
userEmail?: string;
|
|
57
|
+
ipAddress?: string;
|
|
58
|
+
}>(entry: T): Omit<T, "userEmail" | "ipAddress">;
|
|
59
|
+
/**
|
|
60
|
+
* Return a copy of an offer document with buyer PII masked for the seller view.
|
|
61
|
+
* The seller needs to know an offer was made and its amount, but the buyer's
|
|
62
|
+
* full name and email should remain private until the order is confirmed.
|
|
63
|
+
*/
|
|
64
|
+
export declare function maskOfferForSeller<T extends {
|
|
65
|
+
buyerName: string;
|
|
66
|
+
buyerEmail: string;
|
|
67
|
+
}>(offer: T): T;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PII display-masking helpers — pure string manipulation, zero runtime
|
|
3
|
+
* dependencies. Split out from pii-encrypt.ts so these can be exported
|
|
4
|
+
* from the universal client+server barrel (index.ts): pii-encrypt.ts has
|
|
5
|
+
* a top-level `import { createRequire } from "node:module"` (needed for
|
|
6
|
+
* the AES-256-GCM/HMAC functions to work under both Next.js's bundler and
|
|
7
|
+
* a pure-ESM standalone script context) that poisons the whole module for
|
|
8
|
+
* client bundling — Turbopack statically resolves every export in a file
|
|
9
|
+
* before tree-shaking, so re-exporting even one crypto-free symbol from
|
|
10
|
+
* pii-encrypt.ts still pulls `node:module` into the client graph and hard
|
|
11
|
+
* fails the browser build. This file has no such import and is safe
|
|
12
|
+
* anywhere `ReviewModal`/`ReviewDetailShell`/`ReviewsList` (client
|
|
13
|
+
* components displaying masked reviewer names) can reach it.
|
|
14
|
+
*/
|
|
15
|
+
export const ENC_PREFIX = "enc:v1:";
|
|
16
|
+
export const HMAC_PREFIX = "hmac-sha256:";
|
|
17
|
+
/** Check if a value is encrypted by our PII system. */
|
|
18
|
+
export function isPiiEncrypted(value) {
|
|
19
|
+
return typeof value === "string" && value.startsWith(ENC_PREFIX);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Mask a person's name for public display.
|
|
23
|
+
* Each word is reduced to its first letter followed by "***".
|
|
24
|
+
*
|
|
25
|
+
* "John Doe" → "J*** D***"
|
|
26
|
+
* "Alice" → "A***"
|
|
27
|
+
*
|
|
28
|
+
* If the value is still an encrypted blob (e.g. PII_ENCRYPTION_KEY not set),
|
|
29
|
+
* returns "Anonymous" so encrypted ciphertext never leaks to the UI.
|
|
30
|
+
*/
|
|
31
|
+
export function maskName(name) {
|
|
32
|
+
if (!name || typeof name !== "string")
|
|
33
|
+
return "Anonymous";
|
|
34
|
+
if (isPiiEncrypted(name))
|
|
35
|
+
return "Anonymous";
|
|
36
|
+
return name
|
|
37
|
+
.trim()
|
|
38
|
+
.split(/\s+/)
|
|
39
|
+
.filter(Boolean)
|
|
40
|
+
.map((word) => word[0] + "***")
|
|
41
|
+
.join(" ");
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Mask an email address for public display.
|
|
45
|
+
* "john.doe@example.com" → "j***@***.com"
|
|
46
|
+
*/
|
|
47
|
+
export function maskEmail(email) {
|
|
48
|
+
if (!email || typeof email !== "string")
|
|
49
|
+
return "***@***.***";
|
|
50
|
+
if (isPiiEncrypted(email))
|
|
51
|
+
return "***@***.***";
|
|
52
|
+
const atIdx = email.indexOf("@");
|
|
53
|
+
if (atIdx < 0)
|
|
54
|
+
return "***";
|
|
55
|
+
const local = email.slice(0, atIdx);
|
|
56
|
+
const domain = email.slice(atIdx + 1);
|
|
57
|
+
const dotIdx = domain.lastIndexOf(".");
|
|
58
|
+
const domainMasked = dotIdx > 0 ? "***" + domain.slice(dotIdx) : "***";
|
|
59
|
+
return (local[0] ?? "*") + "***@" + domainMasked;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Return a copy of a review document with PII fields masked for public display.
|
|
63
|
+
* Admin/owner endpoints should NOT call this — use the raw document instead.
|
|
64
|
+
*/
|
|
65
|
+
export function maskPublicReview(review) {
|
|
66
|
+
return { ...review, userName: maskName(review.userName) };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Return a copy of a bid document with PII masked for public display
|
|
70
|
+
* (auction product page).
|
|
71
|
+
*/
|
|
72
|
+
export function maskPublicBid(bid) {
|
|
73
|
+
return { ...bid };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Return a copy of an event-entry document with PII masked for the leaderboard.
|
|
77
|
+
* ipAddress and userEmail are never sent to the client;
|
|
78
|
+
* only userDisplayName needs masking for a public leaderboard view.
|
|
79
|
+
*/
|
|
80
|
+
export function maskPublicEventEntry(entry) {
|
|
81
|
+
const { userEmail: _e, ipAddress: _ip, ...rest } = entry;
|
|
82
|
+
return {
|
|
83
|
+
...rest,
|
|
84
|
+
...(rest.userDisplayName !== undefined
|
|
85
|
+
? { userDisplayName: maskName(rest.userDisplayName) }
|
|
86
|
+
: {}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Return a copy of an offer document with buyer PII masked for the seller view.
|
|
91
|
+
* The seller needs to know an offer was made and its amount, but the buyer's
|
|
92
|
+
* full name and email should remain private until the order is confirmed.
|
|
93
|
+
*/
|
|
94
|
+
export function maskOfferForSeller(offer) {
|
|
95
|
+
return {
|
|
96
|
+
...offer,
|
|
97
|
+
buyerName: maskName(offer.buyerName),
|
|
98
|
+
buyerEmail: maskEmail(offer.buyerEmail),
|
|
99
|
+
};
|
|
100
|
+
}
|
package/dist/server.d.ts
CHANGED
|
@@ -454,6 +454,32 @@ export { applyRateLimit } from "./security/index";
|
|
|
454
454
|
export { rateLimitByIdentifier } from "./security/index";
|
|
455
455
|
export { RateLimitPresets } from "./security/index";
|
|
456
456
|
export { maskPublicEventEntry } from "./security/index";
|
|
457
|
+
export { encryptValue } from "./security/index";
|
|
458
|
+
export { decryptValue } from "./security/index";
|
|
459
|
+
export { hmacBlindIndex } from "./security/index";
|
|
460
|
+
export { encryptPiiFields } from "./security/index";
|
|
461
|
+
export { decryptPiiFields } from "./security/index";
|
|
462
|
+
export { encryptPii } from "./security/index";
|
|
463
|
+
export { decryptPii } from "./security/index";
|
|
464
|
+
export { isPiiEncrypted } from "./security/index";
|
|
465
|
+
export { piiBlindIndex } from "./security/index";
|
|
466
|
+
export { addPiiIndices } from "./security/index";
|
|
467
|
+
export { getPiiConfigError } from "./security/index";
|
|
468
|
+
export { encryptShippingAddress } from "./security/index";
|
|
469
|
+
export { decryptShippingAddress } from "./security/index";
|
|
470
|
+
export { encryptPayoutDetails } from "./security/index";
|
|
471
|
+
export { decryptPayoutDetails } from "./security/index";
|
|
472
|
+
export { encryptShippingConfig } from "./security/index";
|
|
473
|
+
export { decryptShippingConfig } from "./security/index";
|
|
474
|
+
export { encryptPayoutBankAccount } from "./security/index";
|
|
475
|
+
export { decryptPayoutBankAccount } from "./security/index";
|
|
476
|
+
export { maskName } from "./security/index";
|
|
477
|
+
export { maskEmail } from "./security/index";
|
|
478
|
+
export { maskPublicReview } from "./security/index";
|
|
479
|
+
export { maskPublicBid } from "./security/index";
|
|
480
|
+
export { maskOfferForSeller } from "./security/index";
|
|
481
|
+
export { ENC_PREFIX } from "./security/index";
|
|
482
|
+
export { HMAC_PREFIX } from "./security/index";
|
|
457
483
|
export { createInstrumentation } from "./instrumentation/index";
|
|
458
484
|
export { SEO_CONFIG } from "./seo/index";
|
|
459
485
|
export { createSeoConfig } from "./seo/index";
|
package/dist/server.js
CHANGED
|
@@ -1312,6 +1312,33 @@ export { rateLimitByIdentifier } from "./security/index";
|
|
|
1312
1312
|
export { RateLimitPresets } from "./security/index";
|
|
1313
1313
|
// maskPublicEventEntry - Redact PII from event entries before public exposure.
|
|
1314
1314
|
export { maskPublicEventEntry } from "./security/index";
|
|
1315
|
+
// PII encryption/masking (server-only -- crypto module via node:module, must never leak into a client bundle; see appkit Export Rules in CLAUDE.md)
|
|
1316
|
+
export { encryptValue } from "./security/index";
|
|
1317
|
+
export { decryptValue } from "./security/index";
|
|
1318
|
+
export { hmacBlindIndex } from "./security/index";
|
|
1319
|
+
export { encryptPiiFields } from "./security/index";
|
|
1320
|
+
export { decryptPiiFields } from "./security/index";
|
|
1321
|
+
export { encryptPii } from "./security/index";
|
|
1322
|
+
export { decryptPii } from "./security/index";
|
|
1323
|
+
export { isPiiEncrypted } from "./security/index";
|
|
1324
|
+
export { piiBlindIndex } from "./security/index";
|
|
1325
|
+
export { addPiiIndices } from "./security/index";
|
|
1326
|
+
export { getPiiConfigError } from "./security/index";
|
|
1327
|
+
export { encryptShippingAddress } from "./security/index";
|
|
1328
|
+
export { decryptShippingAddress } from "./security/index";
|
|
1329
|
+
export { encryptPayoutDetails } from "./security/index";
|
|
1330
|
+
export { decryptPayoutDetails } from "./security/index";
|
|
1331
|
+
export { encryptShippingConfig } from "./security/index";
|
|
1332
|
+
export { decryptShippingConfig } from "./security/index";
|
|
1333
|
+
export { encryptPayoutBankAccount } from "./security/index";
|
|
1334
|
+
export { decryptPayoutBankAccount } from "./security/index";
|
|
1335
|
+
export { maskName } from "./security/index";
|
|
1336
|
+
export { maskEmail } from "./security/index";
|
|
1337
|
+
export { maskPublicReview } from "./security/index";
|
|
1338
|
+
export { maskPublicBid } from "./security/index";
|
|
1339
|
+
export { maskOfferForSeller } from "./security/index";
|
|
1340
|
+
export { ENC_PREFIX } from "./security/index";
|
|
1341
|
+
export { HMAC_PREFIX } from "./security/index";
|
|
1315
1342
|
// -- Instrumentation ---------------------------------------------------------
|
|
1316
1343
|
// createInstrumentation - Factory for Next.js instrumentation hooks.
|
|
1317
1344
|
export { createInstrumentation } from "./instrumentation/index";
|
package/package.json
CHANGED
package/scripts/seed-cli.mjs
CHANGED
|
@@ -117,6 +117,15 @@ if (ACTION === "load" && !PII_KEY_PRESENT) {
|
|
|
117
117
|
// firebase-admin init
|
|
118
118
|
// ---------------------------------------------------------------------------
|
|
119
119
|
const require = createRequire(import.meta.url);
|
|
120
|
+
// appkit's pii-encrypt.ts calls a bare `require("crypto")` at runtime (not a
|
|
121
|
+
// static import — deliberately, so bundlers like Turbopack never see a
|
|
122
|
+
// node:module dependency and refuse to build the client bundle). A bare
|
|
123
|
+
// `require` identifier resolves via the global scope in Node ESM, so it must
|
|
124
|
+
// exist as a global before the dynamic `import("@mohasinac/appkit")` below
|
|
125
|
+
// ever triggers that code path. Only this standalone-script environment
|
|
126
|
+
// needs it — Next.js's own server bundle (webpack/Turbopack) provides an
|
|
127
|
+
// ambient `require` automatically.
|
|
128
|
+
globalThis.require = require;
|
|
120
129
|
const admin = require("firebase-admin");
|
|
121
130
|
|
|
122
131
|
function parsePrivateKey(raw) {
|