@nixxie-cms/core 1.0.0 → 1.0.1
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/README.md +2 -2
- package/admin-ui/components/dist/nixxie-cms-core-admin-ui-components.cjs.js +4 -4
- package/admin-ui/components/dist/nixxie-cms-core-admin-ui-components.esm.js +4 -4
- package/admin-ui/context/dist/nixxie-cms-core-admin-ui-context.cjs.js +2 -2
- package/admin-ui/context/dist/nixxie-cms-core-admin-ui-context.esm.js +2 -2
- package/context/dist/nixxie-cms-core-context.cjs.js +2 -2
- package/context/dist/nixxie-cms-core-context.esm.js +2 -2
- package/dist/{CreateItemDialog-33335548.esm.js → CreateItemDialog-7008b050.esm.js} +1 -1
- package/dist/{CreateItemDialog-56cf59b7.cjs.js → CreateItemDialog-a0cab315.cjs.js} +1 -1
- package/dist/{PageContainer-7db73317.esm.js → PageContainer-5ae731cc.esm.js} +25 -18
- package/dist/{PageContainer-27c27f10.cjs.js → PageContainer-abd7159f.cjs.js} +25 -18
- package/dist/{admin-meta-graphql-6f7f5331.esm.js → admin-meta-graphql-0e6e606e.esm.js} +1 -1
- package/dist/{admin-meta-graphql-c8f926e9.cjs.js → admin-meta-graphql-306c224a.cjs.js} +1 -1
- package/dist/{context-3132c3ed.esm.js → context-af9957ed.esm.js} +2 -2
- package/dist/{context-e7a45152.cjs.js → context-b5204629.cjs.js} +2 -2
- package/dist/declarations/src/admin-ui/components/Navigation.d.ts.map +1 -1
- package/dist/declarations/src/admin-ui/components/PageContainer.d.ts.map +1 -1
- package/dist/declarations/src/helpers.d.ts.map +1 -1
- package/dist/declarations/src/index.d.ts +1 -0
- package/dist/declarations/src/index.d.ts.map +1 -1
- package/dist/declarations/src/internal-unstable/admin-ui/id-field-view.d.ts.map +1 -0
- package/dist/declarations/src/internal-unstable/admin-ui/pages/App/index.d.ts.map +1 -0
- package/dist/declarations/src/internal-unstable/admin-ui/pages/CreateItemPage/index.d.ts.map +1 -0
- package/dist/declarations/src/internal-unstable/admin-ui/pages/HomePage/index.d.ts.map +1 -0
- package/dist/declarations/src/internal-unstable/admin-ui/pages/ItemPage/index.d.ts.map +1 -0
- package/dist/declarations/src/internal-unstable/admin-ui/pages/ListPage/index.d.ts.map +1 -0
- package/dist/declarations/src/internal-unstable/admin-ui/pages/NoAccessPage/index.d.ts.map +1 -0
- package/dist/declarations/src/internal-unstable/artifacts.d.ts.map +1 -0
- package/dist/declarations/src/lib/core/initialise-lists.d.ts +1 -1
- package/dist/declarations/src/schema.d.ts.map +1 -1
- package/dist/declarations/src/types/config/index.d.ts +60 -1
- package/dist/declarations/src/types/config/index.d.ts.map +1 -1
- package/dist/declarations/src/types/config/lists.d.ts +4 -4
- package/dist/declarations/src/types/context.d.ts +150 -0
- package/dist/declarations/src/types/context.d.ts.map +1 -1
- package/dist/declarations/src/types/next-fields.d.ts +1 -1
- package/dist/{express-e9ed9a7d.cjs.js → express-455ae20c.cjs.js} +1 -1
- package/dist/{express-6743b918.esm.js → express-7559ca2d.esm.js} +1 -1
- package/dist/{index-ac01583b.cjs.js → index-89635494.cjs.js} +4 -4
- package/dist/{index-24b78415.esm.js → index-baa799e0.esm.js} +4 -4
- package/dist/nixxie-cms-core.cjs.js +104 -77
- package/dist/nixxie-cms-core.esm.js +104 -77
- package/dist/{non-null-graphql-5315718c.esm.js → non-null-graphql-a84ed64d.esm.js} +1 -1
- package/dist/{non-null-graphql-17b83ddc.cjs.js → non-null-graphql-add6bb3d.cjs.js} +1 -1
- package/dist/{resolve-hooks-66fe8a8e.cjs.js → resolve-hooks-165a9ce2.cjs.js} +1 -1
- package/dist/{resolve-hooks-17aafd37.esm.js → resolve-hooks-6813a045.esm.js} +2 -2
- package/dist/{system-dfec2f0a.esm.js → system-03e49e4f.esm.js} +8 -4
- package/dist/{system-48c5f6df.cjs.js → system-a321642d.cjs.js} +8 -4
- package/dist/{useFilter-0b5a1ee6.esm.js → useFilter-9b6db1f9.esm.js} +1 -1
- package/dist/{useFilter-1a4e6900.cjs.js → useFilter-acc9d413.cjs.js} +1 -1
- package/fields/dist/nixxie-cms-core-fields.cjs.js +16 -16
- package/fields/dist/nixxie-cms-core-fields.esm.js +17 -17
- package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.cjs.js +3 -3
- package/fields/types/bytes/dist/nixxie-cms-core-fields-types-bytes.esm.js +3 -3
- package/fields/types/bytes/views/dist/nixxie-cms-core-fields-types-bytes-views.cjs.js +1 -1
- package/fields/types/bytes/views/dist/nixxie-cms-core-fields-types-bytes-views.esm.js +1 -1
- package/fields/types/password/dist/nixxie-cms-core-fields-types-password.cjs.js +3 -3
- package/fields/types/password/dist/nixxie-cms-core-fields-types-password.esm.js +3 -3
- package/fields/types/relationship/views/dist/nixxie-cms-core-fields-types-relationship-views.cjs.js +4 -4
- package/fields/types/relationship/views/dist/nixxie-cms-core-fields-types-relationship-views.esm.js +4 -4
- package/fields/types/select/views/dist/nixxie-cms-core-fields-types-select-views.cjs.js +1 -1
- package/fields/types/select/views/dist/nixxie-cms-core-fields-types-select-views.esm.js +1 -1
- package/fields/types/text/views/dist/nixxie-cms-core-fields-types-text-views.cjs.js +1 -1
- package/fields/types/text/views/dist/nixxie-cms-core-fields-types-text-views.esm.js +1 -1
- package/internal-unstable/admin-ui/id-field-view/dist/nixxie-cms-core-internal-unstable-admin-ui-id-field-view.cjs.d.ts +2 -0
- package/internal-unstable/admin-ui/id-field-view/dist/nixxie-cms-core-internal-unstable-admin-ui-id-field-view.cjs.js +244 -0
- package/internal-unstable/admin-ui/id-field-view/dist/nixxie-cms-core-internal-unstable-admin-ui-id-field-view.esm.js +235 -0
- package/internal-unstable/admin-ui/id-field-view/package.json +4 -0
- package/internal-unstable/admin-ui/next-config/package.json +4 -0
- package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.cjs.d.ts +2 -0
- package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.cjs.js +59 -0
- package/internal-unstable/admin-ui/pages/App/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-App.esm.js +55 -0
- package/internal-unstable/admin-ui/pages/App/package.json +4 -0
- package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.cjs.d.ts +2 -0
- package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.cjs.js +116 -0
- package/internal-unstable/admin-ui/pages/CreateItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-CreateItemPage.esm.js +112 -0
- package/internal-unstable/admin-ui/pages/CreateItemPage/package.json +4 -0
- package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.cjs.d.ts +2 -0
- package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.cjs.js +336 -0
- package/internal-unstable/admin-ui/pages/HomePage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-HomePage.esm.js +332 -0
- package/internal-unstable/admin-ui/pages/HomePage/package.json +4 -0
- package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.cjs.d.ts +2 -0
- package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.cjs.js +463 -0
- package/internal-unstable/admin-ui/pages/ItemPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ItemPage.esm.js +455 -0
- package/internal-unstable/admin-ui/pages/ItemPage/package.json +4 -0
- package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.cjs.d.ts +2 -0
- package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.cjs.js +1195 -0
- package/internal-unstable/admin-ui/pages/ListPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-ListPage.esm.js +1187 -0
- package/internal-unstable/admin-ui/pages/ListPage/package.json +4 -0
- package/internal-unstable/admin-ui/pages/NoAccessPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-NoAccessPage.cjs.d.ts +2 -0
- package/internal-unstable/admin-ui/pages/NoAccessPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-NoAccessPage.cjs.js +40 -0
- package/internal-unstable/admin-ui/pages/NoAccessPage/dist/nixxie-cms-core-internal-unstable-admin-ui-pages-NoAccessPage.esm.js +35 -0
- package/internal-unstable/admin-ui/pages/NoAccessPage/package.json +4 -0
- package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.d.ts +2 -0
- package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.js +51 -0
- package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.esm.js +38 -0
- package/internal-unstable/artifacts/package.json +4 -0
- package/package.json +44 -44
- package/scripts/cli/dist/nixxie-cms-core-scripts-cli.cjs.js +15 -15
- package/scripts/cli/dist/nixxie-cms-core-scripts-cli.esm.js +15 -15
- package/scripts/dist/nixxie-cms-core-scripts.cjs.js +3 -3
- package/scripts/dist/nixxie-cms-core-scripts.esm.js +3 -3
- package/src/admin-ui/admin-meta-graphql.ts +168 -168
- package/src/admin-ui/components/CommandPalette.tsx +433 -431
- package/src/admin-ui/components/Navigation.tsx +389 -385
- package/src/admin-ui/components/PageContainer.tsx +311 -310
- package/src/admin-ui/components/WelcomeDialog.tsx +1 -1
- package/src/admin-ui/context.tsx +338 -338
- package/src/admin-ui/templates/app.ts +60 -60
- package/src/admin-ui/templates/create-item.ts +5 -5
- package/src/admin-ui/templates/home.ts +2 -2
- package/src/admin-ui/templates/item.tsx +5 -5
- package/src/admin-ui/templates/list.tsx +5 -5
- package/src/admin-ui/templates/no-access.ts +7 -7
- package/src/fields/types/bigInt/index.ts +181 -181
- package/src/fields/types/bytes/index.ts +275 -275
- package/src/fields/types/calendarDay/index.ts +194 -194
- package/src/fields/types/checkbox/index.ts +76 -76
- package/src/fields/types/decimal/index.ts +182 -182
- package/src/fields/types/file/index.ts +168 -168
- package/src/fields/types/float/index.ts +133 -133
- package/src/fields/types/image/index.ts +244 -244
- package/src/fields/types/integer/index.ts +156 -156
- package/src/fields/types/json/index.ts +77 -77
- package/src/fields/types/multiselect/index.ts +212 -212
- package/src/fields/types/password/index.ts +241 -241
- package/src/fields/types/relationship/index.ts +381 -381
- package/src/fields/types/relationship/views/RelationshipTable.tsx +190 -190
- package/src/fields/types/select/index.ts +226 -226
- package/src/fields/types/text/index.ts +207 -207
- package/src/fields/types/timestamp/index.ts +116 -116
- package/src/fields/types/virtual/index.ts +108 -108
- package/src/helpers.ts +342 -316
- package/src/index.ts +4 -0
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/id-field-view.tsx +167 -167
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/App/index.tsx +22 -22
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/CreateItemPage/index.tsx +71 -71
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/HomePage/index.tsx +333 -333
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ItemPage/common.tsx +358 -358
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ItemPage/index.tsx +483 -483
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/FilterAdd.tsx +221 -221
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/PaginationControls.tsx +170 -170
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/Tag.tsx +72 -72
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/index.tsx +1006 -1006
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/NoAccessPage/index.tsx +24 -24
- package/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/artifacts.ts +5 -5
- package/src/lib/context/createContext.ts +165 -161
- package/src/lib/core/initialise-lists.ts +1097 -1097
- package/src/lib/id-field.ts +214 -214
- package/src/lib/telemetry.ts +342 -342
- package/src/schema.ts +237 -233
- package/src/scripts/telemetry.ts +1 -1
- package/src/types/config/index.ts +400 -333
- package/src/types/config/lists.ts +4 -4
- package/src/types/context.ts +700 -530
- package/src/types/next-fields.ts +499 -499
- package/src/types/telemetry.ts +51 -51
- package/tests/telemetry.test.ts +361 -361
- package/CHANGELOG.md +0 -3158
- package/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view/package.json +0 -4
- package/___internal-do-not-use-will-break-in-patch/admin-ui/next-config/package.json +0 -4
- package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/package.json +0 -4
- package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/package.json +0 -4
- package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/package.json +0 -4
- package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/package.json +0 -4
- package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/package.json +0 -4
- package/___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage/package.json +0 -4
- package/___internal-do-not-use-will-break-in-patch/artifacts/package.json +0 -4
- package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/id-field-view.d.ts.map +0 -1
- package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/App/index.d.ts.map +0 -1
- package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/CreateItemPage/index.d.ts.map +0 -1
- package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/HomePage/index.d.ts.map +0 -1
- package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ItemPage/index.d.ts.map +0 -1
- package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/ListPage/index.d.ts.map +0 -1
- package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/admin-ui/pages/NoAccessPage/index.d.ts.map +0 -1
- package/dist/declarations/src/___internal-do-not-use-will-break-in-patch/artifacts.d.ts.map +0 -1
- /package/dist/{common-1a350e11.cjs.js → common-5933f758.cjs.js} +0 -0
- /package/dist/{common-29fc82e6.esm.js → common-ea5c441a.esm.js} +0 -0
- /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/id-field-view.d.ts +0 -0
- /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/App/index.d.ts +0 -0
- /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/CreateItemPage/index.d.ts +0 -0
- /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/HomePage/index.d.ts +0 -0
- /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ItemPage/index.d.ts +0 -0
- /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/ListPage/index.d.ts +0 -0
- /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/admin-ui/pages/NoAccessPage/index.d.ts +0 -0
- /package/dist/declarations/src/{___internal-do-not-use-will-break-in-patch → internal-unstable}/artifacts.d.ts +0 -0
|
@@ -1,241 +1,241 @@
|
|
|
1
|
-
import bcryptjs from 'bcryptjs'
|
|
2
|
-
// @ts-expect-error
|
|
3
|
-
import dumbPasswords from 'dumb-passwords'
|
|
4
|
-
import { userInputError } from '../../../lib/core/graphql-errors'
|
|
5
|
-
import {
|
|
6
|
-
type BaseListTypeInfo,
|
|
7
|
-
type CommonFieldConfig,
|
|
8
|
-
type FieldTypeFunc,
|
|
9
|
-
fieldType,
|
|
10
|
-
} from '../../../types'
|
|
11
|
-
import { g } from '../../..'
|
|
12
|
-
import { makeValidateHook, defaultIsRequired } from '../../non-null-graphql'
|
|
13
|
-
import { isObjectType, type GraphQLSchema } from 'graphql'
|
|
14
|
-
import type { InferValueFromInputType } from '@graphql-ts/schema'
|
|
15
|
-
import type { controller } from './views'
|
|
16
|
-
|
|
17
|
-
type FieldTypeInfo = {
|
|
18
|
-
item: string | null
|
|
19
|
-
inputs: {
|
|
20
|
-
where: InferValueFromInputType<typeof PasswordFilter> | undefined
|
|
21
|
-
create: string | null | undefined
|
|
22
|
-
update: string | null | undefined
|
|
23
|
-
uniqueWhere: never
|
|
24
|
-
orderBy: never
|
|
25
|
-
}
|
|
26
|
-
prisma: {
|
|
27
|
-
create: string | null | undefined
|
|
28
|
-
update: string | null | undefined
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export type PasswordFieldConfig<ListTypeInfo extends BaseListTypeInfo> = CommonFieldConfig<
|
|
33
|
-
ListTypeInfo,
|
|
34
|
-
FieldTypeInfo
|
|
35
|
-
> & {
|
|
36
|
-
validation?: {
|
|
37
|
-
isRequired?: boolean
|
|
38
|
-
rejectCommon?: boolean
|
|
39
|
-
match?: { regex: RegExp; explanation?: string }
|
|
40
|
-
length?: {
|
|
41
|
-
/** @default 8 */
|
|
42
|
-
min?: number
|
|
43
|
-
max?: number
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
db?: {
|
|
47
|
-
isNullable?: boolean
|
|
48
|
-
map?: string
|
|
49
|
-
extendPrismaSchema?: (field: string) => string
|
|
50
|
-
}
|
|
51
|
-
kdf?: KDF
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
type KDF = {
|
|
55
|
-
compare(preImage: string, hash: string): Promise<boolean>
|
|
56
|
-
hash(preImage: string): Promise<string>
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const PasswordState = g.object<{ isSet: boolean }>()({
|
|
60
|
-
name: 'PasswordState',
|
|
61
|
-
fields: {
|
|
62
|
-
isSet: g.field({ type: g.nonNull(g.Boolean) }),
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
const PasswordFilter = g.inputObject({
|
|
67
|
-
name: 'PasswordFilter',
|
|
68
|
-
fields: {
|
|
69
|
-
isSet: g.arg({ type: g.nonNull(g.Boolean) }),
|
|
70
|
-
},
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
export function password<ListTypeInfo extends BaseListTypeInfo>(
|
|
74
|
-
config: PasswordFieldConfig<ListTypeInfo> = {}
|
|
75
|
-
): FieldTypeFunc<ListTypeInfo> {
|
|
76
|
-
const {
|
|
77
|
-
kdf = {
|
|
78
|
-
hash: secret => {
|
|
79
|
-
// note this is slightly different to checking .length > 72 because
|
|
80
|
-
// bcrypt will truncate to 72 bytes after utf8 encoding
|
|
81
|
-
// not JS string length which may be different since that's the utf16 length
|
|
82
|
-
// (though using characters in the error message makes sense since users shouldn't have to think about bytes and it aligns with the validation messages below)
|
|
83
|
-
if (bcryptjs.truncates(secret))
|
|
84
|
-
throw new Error('value must be no longer than 72 characters')
|
|
85
|
-
return bcryptjs.hash(secret, 10)
|
|
86
|
-
},
|
|
87
|
-
compare: (secret, hash) => bcryptjs.compare(secret, hash),
|
|
88
|
-
},
|
|
89
|
-
validation = {},
|
|
90
|
-
} = config
|
|
91
|
-
const { isRequired = false, rejectCommon = false, match, length: { max } = {} } = validation
|
|
92
|
-
const min = isRequired ? (validation.length?.min ?? 8) : validation.length?.min
|
|
93
|
-
|
|
94
|
-
return meta => {
|
|
95
|
-
if ((config as any).isIndexed === 'unique') {
|
|
96
|
-
throw Error("isIndexed: 'unique' is not a supported option for field type password")
|
|
97
|
-
}
|
|
98
|
-
if (min !== undefined && (!Number.isInteger(min) || min < 0)) {
|
|
99
|
-
throw new Error(
|
|
100
|
-
`${meta.listKey}.${meta.fieldKey} specifies validation.length.min: ${min} but it must be a positive integer`
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
if (max !== undefined && (!Number.isInteger(max) || max < 0)) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
`${meta.listKey}.${meta.fieldKey} specifies validation.length.max: ${max} but it must be a positive integer`
|
|
106
|
-
)
|
|
107
|
-
}
|
|
108
|
-
if (isRequired && min !== undefined && min === 0) {
|
|
109
|
-
throw new Error(
|
|
110
|
-
`${meta.listKey}.${meta.fieldKey} specifies validation.isRequired: true and validation.length.min: 0, this is not allowed because validation.isRequired implies at least a min length of 1`
|
|
111
|
-
)
|
|
112
|
-
}
|
|
113
|
-
if (isRequired && max !== undefined && max === 0) {
|
|
114
|
-
throw new Error(
|
|
115
|
-
`${meta.listKey}.${meta.fieldKey} specifies validation.isRequired: true and validation.length.max: 0, this is not allowed because validation.isRequired implies at least a max length of 1`
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
if (min !== undefined && max !== undefined && min > max) {
|
|
119
|
-
throw new Error(
|
|
120
|
-
`${meta.listKey}.${meta.fieldKey} specifies a validation.length.max that is less than the validation.length.min, and therefore has no valid options`
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function inputResolver(val: string | null | undefined) {
|
|
125
|
-
if (val == null) return val
|
|
126
|
-
return kdf.hash(val)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const hasAdditionalValidation = match || rejectCommon || min !== undefined || max !== undefined
|
|
130
|
-
const { mode, validate } = makeValidateHook(
|
|
131
|
-
meta,
|
|
132
|
-
config,
|
|
133
|
-
hasAdditionalValidation
|
|
134
|
-
? ({ inputData, operation, addValidationError }) => {
|
|
135
|
-
if (operation === 'delete') return
|
|
136
|
-
|
|
137
|
-
const value = inputData[meta.fieldKey] // we use inputData, as resolveData is hashed
|
|
138
|
-
if (value != null) {
|
|
139
|
-
if (min !== undefined && value.length < min) {
|
|
140
|
-
if (min === 1) {
|
|
141
|
-
addValidationError(`value must not be empty`)
|
|
142
|
-
} else {
|
|
143
|
-
addValidationError(`value must be at least ${min} characters long`)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (max !== undefined && value.length > max) {
|
|
147
|
-
addValidationError(`value must be no longer than ${max} characters`)
|
|
148
|
-
}
|
|
149
|
-
if (match && !match.regex.test(value)) {
|
|
150
|
-
addValidationError(match.explanation ?? `value must match ${match.regex}`)
|
|
151
|
-
}
|
|
152
|
-
if (rejectCommon && dumbPasswords.check(value)) {
|
|
153
|
-
addValidationError(`value is too common and is not allowed`)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
: undefined
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
return fieldType({
|
|
161
|
-
kind: 'scalar',
|
|
162
|
-
scalar: 'String',
|
|
163
|
-
mode,
|
|
164
|
-
map: config.db?.map,
|
|
165
|
-
extendPrismaSchema: config.db?.extendPrismaSchema,
|
|
166
|
-
})({
|
|
167
|
-
...config,
|
|
168
|
-
...defaultIsRequired(config, isRequired),
|
|
169
|
-
hooks: {
|
|
170
|
-
...config.hooks,
|
|
171
|
-
validate,
|
|
172
|
-
},
|
|
173
|
-
input: {
|
|
174
|
-
where:
|
|
175
|
-
mode === 'required'
|
|
176
|
-
? undefined
|
|
177
|
-
: {
|
|
178
|
-
arg: g.arg({ type: PasswordFilter }),
|
|
179
|
-
resolve(val) {
|
|
180
|
-
if (val === null) throw userInputError('Password filters cannot be set to null')
|
|
181
|
-
if (val.isSet) return { not: null }
|
|
182
|
-
return null
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
create: {
|
|
186
|
-
arg: g.arg({ type: g.String }),
|
|
187
|
-
resolve(val) {
|
|
188
|
-
if (val === undefined) return null
|
|
189
|
-
return inputResolver(val)
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
update: {
|
|
193
|
-
arg: g.arg({ type: g.String }),
|
|
194
|
-
resolve: inputResolver,
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
|
|
198
|
-
views: '@nixxie-cms/core/fields/types/password/views',
|
|
199
|
-
getAdminMeta: (): Parameters<typeof controller>[0]['fieldMeta'] => ({
|
|
200
|
-
isNullable: mode === 'optional',
|
|
201
|
-
validation: {
|
|
202
|
-
rejectCommon,
|
|
203
|
-
match: match
|
|
204
|
-
? {
|
|
205
|
-
regex: {
|
|
206
|
-
source: match.regex.source,
|
|
207
|
-
flags: match.regex.flags,
|
|
208
|
-
},
|
|
209
|
-
explanation: match.explanation ?? `value must match ${match.regex}`,
|
|
210
|
-
}
|
|
211
|
-
: null,
|
|
212
|
-
length: {
|
|
213
|
-
max: max ?? null,
|
|
214
|
-
min: min ?? 8,
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
}),
|
|
218
|
-
output: g.field({
|
|
219
|
-
type: PasswordState,
|
|
220
|
-
resolve(val) {
|
|
221
|
-
return { isSet: val.value !== null }
|
|
222
|
-
},
|
|
223
|
-
extensions: {
|
|
224
|
-
nixxieKDF: kdf,
|
|
225
|
-
},
|
|
226
|
-
}),
|
|
227
|
-
})
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export function getPasswordFieldKDF(
|
|
232
|
-
schema: GraphQLSchema,
|
|
233
|
-
listKey: string,
|
|
234
|
-
fieldKey: string
|
|
235
|
-
): KDF | null {
|
|
236
|
-
const gqlOutputType = schema.getType(listKey)
|
|
237
|
-
if (!isObjectType(gqlOutputType)) return null
|
|
238
|
-
const passwordField = gqlOutputType.getFields()[fieldKey]
|
|
239
|
-
if (!passwordField?.extensions.nixxieKDF) return null
|
|
240
|
-
return passwordField.extensions.nixxieKDF as KDF
|
|
241
|
-
}
|
|
1
|
+
import bcryptjs from 'bcryptjs'
|
|
2
|
+
// @ts-expect-error
|
|
3
|
+
import dumbPasswords from 'dumb-passwords'
|
|
4
|
+
import { userInputError } from '../../../lib/core/graphql-errors'
|
|
5
|
+
import {
|
|
6
|
+
type BaseListTypeInfo,
|
|
7
|
+
type CommonFieldConfig,
|
|
8
|
+
type FieldTypeFunc,
|
|
9
|
+
fieldType,
|
|
10
|
+
} from '../../../types'
|
|
11
|
+
import { g } from '../../..'
|
|
12
|
+
import { makeValidateHook, defaultIsRequired } from '../../non-null-graphql'
|
|
13
|
+
import { isObjectType, type GraphQLSchema } from 'graphql'
|
|
14
|
+
import type { InferValueFromInputType } from '@graphql-ts/schema'
|
|
15
|
+
import type { controller } from './views'
|
|
16
|
+
|
|
17
|
+
type FieldTypeInfo = {
|
|
18
|
+
item: string | null
|
|
19
|
+
inputs: {
|
|
20
|
+
where: InferValueFromInputType<typeof PasswordFilter> | undefined
|
|
21
|
+
create: string | null | undefined
|
|
22
|
+
update: string | null | undefined
|
|
23
|
+
uniqueWhere: never
|
|
24
|
+
orderBy: never
|
|
25
|
+
}
|
|
26
|
+
prisma: {
|
|
27
|
+
create: string | null | undefined
|
|
28
|
+
update: string | null | undefined
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type PasswordFieldConfig<ListTypeInfo extends BaseListTypeInfo> = CommonFieldConfig<
|
|
33
|
+
ListTypeInfo,
|
|
34
|
+
FieldTypeInfo
|
|
35
|
+
> & {
|
|
36
|
+
validation?: {
|
|
37
|
+
isRequired?: boolean
|
|
38
|
+
rejectCommon?: boolean
|
|
39
|
+
match?: { regex: RegExp; explanation?: string }
|
|
40
|
+
length?: {
|
|
41
|
+
/** @default 8 */
|
|
42
|
+
min?: number
|
|
43
|
+
max?: number
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
db?: {
|
|
47
|
+
isNullable?: boolean
|
|
48
|
+
map?: string
|
|
49
|
+
extendPrismaSchema?: (field: string) => string
|
|
50
|
+
}
|
|
51
|
+
kdf?: KDF
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type KDF = {
|
|
55
|
+
compare(preImage: string, hash: string): Promise<boolean>
|
|
56
|
+
hash(preImage: string): Promise<string>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const PasswordState = g.object<{ isSet: boolean }>()({
|
|
60
|
+
name: 'PasswordState',
|
|
61
|
+
fields: {
|
|
62
|
+
isSet: g.field({ type: g.nonNull(g.Boolean) }),
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const PasswordFilter = g.inputObject({
|
|
67
|
+
name: 'PasswordFilter',
|
|
68
|
+
fields: {
|
|
69
|
+
isSet: g.arg({ type: g.nonNull(g.Boolean) }),
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
export function password<ListTypeInfo extends BaseListTypeInfo>(
|
|
74
|
+
config: PasswordFieldConfig<ListTypeInfo> = {}
|
|
75
|
+
): FieldTypeFunc<ListTypeInfo> {
|
|
76
|
+
const {
|
|
77
|
+
kdf = {
|
|
78
|
+
hash: secret => {
|
|
79
|
+
// note this is slightly different to checking .length > 72 because
|
|
80
|
+
// bcrypt will truncate to 72 bytes after utf8 encoding
|
|
81
|
+
// not JS string length which may be different since that's the utf16 length
|
|
82
|
+
// (though using characters in the error message makes sense since users shouldn't have to think about bytes and it aligns with the validation messages below)
|
|
83
|
+
if (bcryptjs.truncates(secret))
|
|
84
|
+
throw new Error('value must be no longer than 72 characters')
|
|
85
|
+
return bcryptjs.hash(secret, 10)
|
|
86
|
+
},
|
|
87
|
+
compare: (secret, hash) => bcryptjs.compare(secret, hash),
|
|
88
|
+
},
|
|
89
|
+
validation = {},
|
|
90
|
+
} = config
|
|
91
|
+
const { isRequired = false, rejectCommon = false, match, length: { max } = {} } = validation
|
|
92
|
+
const min = isRequired ? (validation.length?.min ?? 8) : validation.length?.min
|
|
93
|
+
|
|
94
|
+
return meta => {
|
|
95
|
+
if ((config as any).isIndexed === 'unique') {
|
|
96
|
+
throw Error("isIndexed: 'unique' is not a supported option for field type password")
|
|
97
|
+
}
|
|
98
|
+
if (min !== undefined && (!Number.isInteger(min) || min < 0)) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`${meta.listKey}.${meta.fieldKey} specifies validation.length.min: ${min} but it must be a positive integer`
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
if (max !== undefined && (!Number.isInteger(max) || max < 0)) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`${meta.listKey}.${meta.fieldKey} specifies validation.length.max: ${max} but it must be a positive integer`
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
if (isRequired && min !== undefined && min === 0) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`${meta.listKey}.${meta.fieldKey} specifies validation.isRequired: true and validation.length.min: 0, this is not allowed because validation.isRequired implies at least a min length of 1`
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
if (isRequired && max !== undefined && max === 0) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`${meta.listKey}.${meta.fieldKey} specifies validation.isRequired: true and validation.length.max: 0, this is not allowed because validation.isRequired implies at least a max length of 1`
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
if (min !== undefined && max !== undefined && min > max) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`${meta.listKey}.${meta.fieldKey} specifies a validation.length.max that is less than the validation.length.min, and therefore has no valid options`
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function inputResolver(val: string | null | undefined) {
|
|
125
|
+
if (val == null) return val
|
|
126
|
+
return kdf.hash(val)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const hasAdditionalValidation = match || rejectCommon || min !== undefined || max !== undefined
|
|
130
|
+
const { mode, validate } = makeValidateHook(
|
|
131
|
+
meta,
|
|
132
|
+
config,
|
|
133
|
+
hasAdditionalValidation
|
|
134
|
+
? ({ inputData, operation, addValidationError }) => {
|
|
135
|
+
if (operation === 'delete') return
|
|
136
|
+
|
|
137
|
+
const value = inputData[meta.fieldKey] // we use inputData, as resolveData is hashed
|
|
138
|
+
if (value != null) {
|
|
139
|
+
if (min !== undefined && value.length < min) {
|
|
140
|
+
if (min === 1) {
|
|
141
|
+
addValidationError(`value must not be empty`)
|
|
142
|
+
} else {
|
|
143
|
+
addValidationError(`value must be at least ${min} characters long`)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (max !== undefined && value.length > max) {
|
|
147
|
+
addValidationError(`value must be no longer than ${max} characters`)
|
|
148
|
+
}
|
|
149
|
+
if (match && !match.regex.test(value)) {
|
|
150
|
+
addValidationError(match.explanation ?? `value must match ${match.regex}`)
|
|
151
|
+
}
|
|
152
|
+
if (rejectCommon && dumbPasswords.check(value)) {
|
|
153
|
+
addValidationError(`value is too common and is not allowed`)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
: undefined
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
return fieldType({
|
|
161
|
+
kind: 'scalar',
|
|
162
|
+
scalar: 'String',
|
|
163
|
+
mode,
|
|
164
|
+
map: config.db?.map,
|
|
165
|
+
extendPrismaSchema: config.db?.extendPrismaSchema,
|
|
166
|
+
})({
|
|
167
|
+
...config,
|
|
168
|
+
...defaultIsRequired(config, isRequired),
|
|
169
|
+
hooks: {
|
|
170
|
+
...config.hooks,
|
|
171
|
+
validate,
|
|
172
|
+
},
|
|
173
|
+
input: {
|
|
174
|
+
where:
|
|
175
|
+
mode === 'required'
|
|
176
|
+
? undefined
|
|
177
|
+
: {
|
|
178
|
+
arg: g.arg({ type: PasswordFilter }),
|
|
179
|
+
resolve(val) {
|
|
180
|
+
if (val === null) throw userInputError('Password filters cannot be set to null')
|
|
181
|
+
if (val.isSet) return { not: null }
|
|
182
|
+
return null
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
create: {
|
|
186
|
+
arg: g.arg({ type: g.String }),
|
|
187
|
+
resolve(val) {
|
|
188
|
+
if (val === undefined) return null
|
|
189
|
+
return inputResolver(val)
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
update: {
|
|
193
|
+
arg: g.arg({ type: g.String }),
|
|
194
|
+
resolve: inputResolver,
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
__nxTelemetryFieldTypeName: '@nixxie-cms/password',
|
|
198
|
+
views: '@nixxie-cms/core/fields/types/password/views',
|
|
199
|
+
getAdminMeta: (): Parameters<typeof controller>[0]['fieldMeta'] => ({
|
|
200
|
+
isNullable: mode === 'optional',
|
|
201
|
+
validation: {
|
|
202
|
+
rejectCommon,
|
|
203
|
+
match: match
|
|
204
|
+
? {
|
|
205
|
+
regex: {
|
|
206
|
+
source: match.regex.source,
|
|
207
|
+
flags: match.regex.flags,
|
|
208
|
+
},
|
|
209
|
+
explanation: match.explanation ?? `value must match ${match.regex}`,
|
|
210
|
+
}
|
|
211
|
+
: null,
|
|
212
|
+
length: {
|
|
213
|
+
max: max ?? null,
|
|
214
|
+
min: min ?? 8,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
}),
|
|
218
|
+
output: g.field({
|
|
219
|
+
type: PasswordState,
|
|
220
|
+
resolve(val) {
|
|
221
|
+
return { isSet: val.value !== null }
|
|
222
|
+
},
|
|
223
|
+
extensions: {
|
|
224
|
+
nixxieKDF: kdf,
|
|
225
|
+
},
|
|
226
|
+
}),
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function getPasswordFieldKDF(
|
|
232
|
+
schema: GraphQLSchema,
|
|
233
|
+
listKey: string,
|
|
234
|
+
fieldKey: string
|
|
235
|
+
): KDF | null {
|
|
236
|
+
const gqlOutputType = schema.getType(listKey)
|
|
237
|
+
if (!isObjectType(gqlOutputType)) return null
|
|
238
|
+
const passwordField = gqlOutputType.getFields()[fieldKey]
|
|
239
|
+
if (!passwordField?.extensions.nixxieKDF) return null
|
|
240
|
+
return passwordField.extensions.nixxieKDF as KDF
|
|
241
|
+
}
|