@cmscraft/schema 0.3.6 → 0.3.17

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/schema.gql CHANGED
@@ -2,6 +2,11 @@
2
2
  # THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
3
3
  # ------------------------------------------------------
4
4
 
5
+ input AddCustomerEmailInput {
6
+ """The address to add or change to; it gets a 6-digit code"""
7
+ email: String!
8
+ }
9
+
5
10
  enum AdoptionRequestStatus {
6
11
  APPROVED
7
12
  CANCELLED
@@ -223,6 +228,78 @@ type CategoryTreeNode {
223
228
  slug: String!
224
229
  }
225
230
 
231
+ type ClientZoneAttributeDefinition {
232
+ description: [LocalizedText!]
233
+
234
+ """True: the Customer edits it (updateMyAttributes); false: shown only"""
235
+ editable: Boolean!
236
+
237
+ """
238
+ The one long free-text field: show the "no health, ID or bank details" warning with it
239
+ """
240
+ isNote: Boolean!
241
+ key: String!
242
+ label: [LocalizedText!]!
243
+ namespace: String!
244
+ options: CustomerAttributeOptions!
245
+ position: Int!
246
+
247
+ """A form prefill only, never stored as a value"""
248
+ prefill: JSON
249
+
250
+ """
251
+ A profile save (updateMyAttributes) must fill it when the Customer may write it
252
+ """
253
+ required: Boolean!
254
+ type: CustomerAttributeType!
255
+ }
256
+
257
+ """
258
+ A record type the signed-in Customer may read: how to render its values (no personal data)
259
+ """
260
+ type ClientZoneRecordType {
261
+ audience: CustomerRecordAudience!
262
+ cardinality: CustomerRecordCardinality!
263
+ fields: [CustomerRecordField!]!
264
+ label: [LocalizedText!]!
265
+ namespace: String!
266
+ position: Int!
267
+
268
+ """
269
+ True: myRecords needs a second factor proved in the last 15 minutes (STEP_UP_REQUIRED)
270
+ """
271
+ sensitive: Boolean!
272
+ type: String!
273
+ }
274
+
275
+ """
276
+ How people sign in on this site, for rendering its sign-in page. No personal data; tier NONE when never set
277
+ """
278
+ type ClientZoneSignIn {
279
+ """False: no e-mail sign-in links, show social sign-in only"""
280
+ emailSignIn: Boolean!
281
+ providers: [CustomerProvider!]!
282
+ signupMode: ClientZoneSignupMode!
283
+ tier: ClientZoneTier!
284
+ }
285
+
286
+ """
287
+ self = people create their own Customer; invite = staff invitation only
288
+ """
289
+ enum ClientZoneSignupMode {
290
+ INVITE
291
+ SELF
292
+ }
293
+
294
+ """
295
+ What the site offers its Customers: none (no login), basic, or secure (sensitive-data D15)
296
+ """
297
+ enum ClientZoneTier {
298
+ BASIC
299
+ NONE
300
+ SECURE
301
+ }
302
+
226
303
  type Content {
227
304
  author: Author
228
305
  category: Category
@@ -369,6 +446,219 @@ type CurrencyType {
369
446
  updatedAt: DateTime
370
447
  }
371
448
 
449
+ """The signed-in client-zone Customer of this site (never an admin user)"""
450
+ type Customer {
451
+ """
452
+ The Customer's attribute values they may read (customerAccess READ or READ_WRITE). Personal data: never cache it
453
+ """
454
+ attributes: [CustomerAttributeValue!]!
455
+ avatarUrl: String
456
+ createdAt: DateTime!
457
+ displayName: String!
458
+ email: String
459
+
460
+ """False until the Customer proves the address (magic link or code)"""
461
+ emailVerified: Boolean!
462
+ id: Int!
463
+ identities: [CustomerIdentity!]!
464
+ locale: String
465
+ }
466
+
467
+ """Limits on the values; which apply depends on the type"""
468
+ type CustomerAttributeOptions {
469
+ """SELECT / MULTI_SELECT: the allowed values (1..100)"""
470
+ choices: [String!]
471
+
472
+ """NUMBER: whole numbers only"""
473
+ integer: Boolean
474
+
475
+ """NUMBER: highest allowed"""
476
+ max: Float
477
+
478
+ """TEXT: at most this many characters (1..1000, default 1000)"""
479
+ maxLength: Int
480
+
481
+ """NUMBER: lowest allowed"""
482
+ min: Float
483
+
484
+ """
485
+ TEXT: a PostgreSQL regular expression the value must match; anchor it (^…$) to match the whole value. The server decides: a check in the browser (JavaScript) is a hint only, its syntax differs
486
+ """
487
+ pattern: String
488
+ }
489
+
490
+ """More types may come: treat a type you do not know as read-only"""
491
+ enum CustomerAttributeType {
492
+ BOOLEAN
493
+ DATE
494
+ JSON
495
+ MULTI_SELECT
496
+ NUMBER
497
+ SELECT
498
+ TEXT
499
+ }
500
+
501
+ type CustomerAttributeValue {
502
+ key: String!
503
+ namespace: String!
504
+ value: JSON!
505
+ }
506
+
507
+ input CustomerAttributeValueInput {
508
+ key: String!
509
+ namespace: String!
510
+
511
+ """null removes the value"""
512
+ value: JSON
513
+ }
514
+
515
+ """
516
+ Keep both tokens server-side (httpOnly cookies on the site domain); send the access token as Bearer
517
+ """
518
+ type CustomerAuthPayload {
519
+ accessToken: String!
520
+ accessTokenExpiresAt: DateTime!
521
+
522
+ """
523
+ Secure tier, first sign-in only: the 10 single-use backup codes, shown this once. Null on every other sign-in
524
+ """
525
+ backupCodes: [String!]
526
+ customer: Customer!
527
+
528
+ """
529
+ Null only when a parallel refresh already rotated the token: keep the refresh token you have
530
+ """
531
+ refreshToken: String
532
+ }
533
+
534
+ """A social login linked to the signed-in Customer"""
535
+ type CustomerIdentity {
536
+ createdAt: DateTime!
537
+ lastUsedAt: DateTime
538
+ provider: CustomerProvider!
539
+ }
540
+
541
+ """Basic tier: the session. Secure tier: the second-factor challenge"""
542
+ union CustomerLoginStep = CustomerAuthPayload | SecondFactorChallenge
543
+
544
+ enum CustomerProvider {
545
+ DISCORD
546
+ GOOGLE
547
+ STEAM
548
+ TWITCH
549
+ }
550
+
551
+ """One of the signed-in Customer's records (or a shared one)"""
552
+ type CustomerRecord {
553
+ """main for a ONE type"""
554
+ handle: String!
555
+ id: ID!
556
+ namespace: String!
557
+ position: Int!
558
+ type: String!
559
+ updatedAt: DateTime!
560
+
561
+ """
562
+ Keyed by field. Decimals are strings: parse them, never through a float for money
563
+ """
564
+ values: JSON!
565
+ }
566
+
567
+ enum CustomerRecordAudience {
568
+ CUSTOMER
569
+ SHARED
570
+ }
571
+
572
+ enum CustomerRecordCardinality {
573
+ MANY
574
+ ONE
575
+ }
576
+
577
+ type CustomerRecordChoice {
578
+ label: [LocalizedText!]!
579
+ value: String!
580
+ }
581
+
582
+ type CustomerRecordField {
583
+ description: [LocalizedText!]
584
+
585
+ """The key in a record's values (camelCase allowed); never renamed"""
586
+ key: String!
587
+ label: [LocalizedText!]!
588
+ options: CustomerRecordFieldOptions!
589
+ required: Boolean!
590
+
591
+ """What the field means to renderers and agents"""
592
+ role: CustomerRecordFieldRole
593
+ type: CustomerRecordFieldType!
594
+ }
595
+
596
+ """Limits on the values; which apply depends on the field type"""
597
+ type CustomerRecordFieldOptions {
598
+ """SELECT: the allowed values (1..100)"""
599
+ choices: [CustomerRecordChoice!]
600
+
601
+ """MONEY: the allowed ISO 4217 currencies (any when absent)"""
602
+ currencies: [String!]
603
+
604
+ """NUMBER / PERCENT: highest allowed, a decimal string"""
605
+ max: String
606
+
607
+ """TEXT: at most this many characters (1..1000; sensitive types 200)"""
608
+ maxLength: Int
609
+
610
+ """SERIES: at most this many points (1..5000, default 5000)"""
611
+ maxPoints: Int
612
+
613
+ """NUMBER / PERCENT: lowest allowed, a decimal string"""
614
+ min: String
615
+
616
+ """
617
+ TEXT: a PostgreSQL regular expression the value must match; anchor it (^…$). The server decides
618
+ """
619
+ pattern: String
620
+
621
+ """NUMBER: at most this many decimal places (0..6)"""
622
+ scale: Int
623
+
624
+ """SERIES: what the values are: MONEY:<ISO 4217>, PERCENT or NUMBER"""
625
+ unit: String
626
+ }
627
+
628
+ enum CustomerRecordFieldRole {
629
+ AMOUNT
630
+ AS_OF
631
+ HISTORY
632
+ LINK_KEY
633
+ RETURN
634
+ SHARE
635
+ TITLE
636
+ }
637
+
638
+ """More types may come: treat a type you do not know as read-only"""
639
+ enum CustomerRecordFieldType {
640
+ DATE
641
+ MONEY
642
+ NUMBER
643
+ PERCENT
644
+ SELECT
645
+ SERIES
646
+ TEXT
647
+ }
648
+
649
+ """A device the Customer is signed in on"""
650
+ type CustomerSession {
651
+ createdAt: DateTime!
652
+
653
+ """The session this request is made with"""
654
+ current: Boolean!
655
+ expiresAt: DateTime!
656
+ id: Int!
657
+ ip: String
658
+ lastSeenAt: DateTime
659
+ userAgent: String
660
+ }
661
+
372
662
  """
373
663
  A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format.
374
664
  """
@@ -566,9 +856,15 @@ enum MainMailDtoTemplate {
566
856
  creator_shop_ready_for_pickup
567
857
  creator_shop_withdrawal_received
568
858
  creator_shop_withdrawal_request
859
+ customer_email_code
860
+ customer_inactivity_warning
861
+ customer_invite
862
+ customer_login_link
863
+ customer_second_factor_reset
569
864
  domain_ready
570
865
  email_verification
571
866
  newsletter
867
+ newsletter_confirm
572
868
  newsletter_welcome
573
869
  password_reset
574
870
  project_invitation
@@ -610,6 +906,7 @@ enum ModuleType {
610
906
  BLOG
611
907
  BOOKINGS
612
908
  CATEGORIES
909
+ CLIENT_ZONE
613
910
  CONNECTIONS
614
911
  CONTENT
615
912
  DONATIONS
@@ -622,23 +919,93 @@ enum ModuleType {
622
919
  }
623
920
 
624
921
  type Mutation {
922
+ """
923
+ Mail a 6-digit code (valid 15 min) to an address to add or change to. Needs a session signed in within the last 30 min (REAUTH_REQUIRED). 5 per hour (TOO_MANY_EMAIL_CODES); only the newest code works. Nothing changes until verifyCustomerEmail
924
+ """
925
+ addCustomerEmail(input: AddCustomerEmailInput!): Boolean!
625
926
  cancelRegistration(token: String!): Boolean!
626
927
 
928
+ """
929
+ The challenge's second factor → the session. Exactly one of code (6 digits from the authenticator app) and backupCode. The first good code enrols the Customer and returns backupCodes once. INVALID_SECOND_FACTOR_CODE (wrong or reused code), CHALLENGE_EXPIRED (unknown, used, older than 5 min, or its 5 tries spent), SECOND_FACTOR_LOCKED (10 wrong codes in an hour)
930
+ """
931
+ completeCustomerSecondFactor(backupCode: String, challengeToken: String!, code: String): CustomerAuthPayload!
932
+
627
933
  """Create a guest booking"""
628
934
  createPublicBooking(input: CreatePublicBookingInput!): PublicBookingResult!
629
935
 
630
936
  """Place a shop order; returns the order link token once"""
631
937
  createShopOrder(input: CreateShopOrderInput!): CreateShopOrderResult!
632
938
 
939
+ """Delete your account and everything linked to it, at once"""
940
+ deleteMe: Boolean!
941
+
942
+ """
943
+ Trade the sign-in link's token for a session (single use). Basic tier only: a secure site answers SECOND_FACTOR_REQUIRED, use startCustomerLogin
944
+ """
945
+ exchangeCustomerLoginToken(token: String!): CustomerAuthPayload!
946
+
947
+ """End the session this request is signed in with"""
948
+ logoutCustomer: Boolean!
949
+
950
+ """End every session of the signed-in Customer, this one included"""
951
+ logoutCustomerEverywhere: Boolean!
952
+
953
+ """
954
+ Trade the social callback's one-time code plus your verifier for a session (single use, 60 s)
955
+ """
956
+ redeemCustomerSocialCode(code: String!, verifier: String!): CustomerAuthPayload!
957
+
958
+ """New access token; the refresh token rotates"""
959
+ refreshCustomerSession(refreshToken: String!): CustomerAuthPayload!
960
+
961
+ """
962
+ Secure tier: a good authenticator code replaces all backup codes with 10 new ones, shown this once
963
+ """
964
+ regenerateCustomerBackupCodes(code: String!): [String!]!
965
+
966
+ """
967
+ Email a sign-in link. Always true: the answer never says whether the address has an account, nor whether the site sends e-mail links at all (clientZoneSignIn.emailSignIn)
968
+ """
969
+ requestCustomerLogin(input: RequestCustomerLoginInput!): Boolean!
970
+
633
971
  """
634
972
  Record the consumer's withdrawal request; by token, or by order number + e-mail
635
973
  """
636
974
  requestShopOrderWithdrawal(input: ShopWithdrawalInput!): Boolean!
975
+
976
+ """Sign out one of your own devices"""
977
+ revokeSession(id: Int!): Boolean!
637
978
  sendContactRequest(mailInput: MailInputType!): Boolean!
979
+
980
+ """
981
+ Trade the sign-in link's token (single use) for the next step: the session on a basic site, a SecondFactorChallenge on a secure one (then completeCustomerSecondFactor). Same refusals as exchangeCustomerLoginToken
982
+ """
983
+ startCustomerLogin(token: String!): CustomerLoginStep!
984
+
985
+ """
986
+ Start a social sign-in: returns the URL to send the browser to (valid 5 min). The code comes back to the client zone's callback path
987
+ """
988
+ startCustomerSocialLogin(input: StartCustomerSocialLoginInput!): String!
989
+
990
+ """
991
+ Secure tier: a good authenticator code marks this session's second factor fresh now (before sensitive downloads). Same wrong-code counting and lock as sign-in
992
+ """
993
+ stepUpCustomer(code: String!): Boolean!
638
994
  submitAdoptionRequest(input: PublicCreateAdoptionRequestInput!): PublicAdoptionRequestResult!
639
995
  submitRegistration(input: SubmitRegistrationInput!): RegistrationResult!
640
996
  subscribeNewsletter(input: SubscribeNewsletterInput!): PublicNewsletterSubscription!
641
997
  unsubscribeNewsletter(token: String!): Boolean!
998
+ updateMe(input: UpdateMeInput!): Customer!
999
+
1000
+ """
1001
+ Save the signed-in Customer's own attributes: only the keys given change, null removes one. Only READ_WRITE keys (ATTRIBUTE_NOT_WRITABLE; a key they cannot see is UNKNOWN_ATTRIBUTE). INVALID_ATTRIBUTE_VALUE and ATTRIBUTE_REQUIRED name the key in extensions; ATTRIBUTES_TOO_LARGE past 16 KB. A value the Customer writes is theirs to forge: anything the site computes belongs in a READ attribute
1002
+ """
1003
+ updateMyAttributes(input: UpdateMyAttributesInput!): Customer!
1004
+
1005
+ """
1006
+ Prove the address with its code; it becomes the verified e-mail. INVALID_EMAIL_CODE (wrong, expired, or 5 wrong guesses), EMAIL_IN_USE (another Customer of this site has it). 10 wrong guesses at one address in 24 h lock it for that Customer, 100 from all Customers together lock it for everyone, until they age out
1007
+ """
1008
+ verifyCustomerEmail(input: VerifyCustomerEmailInput!): Customer!
642
1009
  }
643
1010
 
644
1011
  type NewsletterType {
@@ -1158,7 +1525,13 @@ type PublicDeliveryMethod {
1158
1525
 
1159
1526
  type PublicNewsletterSubscription {
1160
1527
  email: String!
1161
- id: Int!
1528
+
1529
+ """Always 0"""
1530
+ id: Int! @deprecated(reason: "Always 0: the answer must not tell whether the address was already on the list")
1531
+
1532
+ """
1533
+ Always PENDING: the address gets a confirmation e-mail unless it is already subscribed, and the answer does not say which
1534
+ """
1162
1535
  status: SubscriberStatus!
1163
1536
  }
1164
1537
 
@@ -1351,6 +1724,24 @@ enum QrFormat {
1351
1724
  type Query {
1352
1725
  """Get blog authors"""
1353
1726
  authors: [Author!]!
1727
+
1728
+ """
1729
+ The Customer attributes a Customer may see on this site, for its profile form (no personal data). Cache it with the site's revalidation tag client-zone-attributes
1730
+ """
1731
+ clientZoneAttributeDefinitions: [ClientZoneAttributeDefinition!]!
1732
+
1733
+ """
1734
+ The record types a Customer may read on this site, to render myRecords (no personal data). Cache it with the site's revalidation tag client-zone-record-types
1735
+ """
1736
+ clientZoneRecordTypes: [ClientZoneRecordType!]!
1737
+
1738
+ """
1739
+ How people sign in on this site (tier, signup mode, e-mail links, social providers). No personal data
1740
+ """
1741
+ clientZoneSignIn: ClientZoneSignIn!
1742
+
1743
+ """Download my data: everything held about the signed-in Customer (JSON)"""
1744
+ exportMe: JSON!
1354
1745
  getPublicContentById(contentId: Int!): Content!
1355
1746
  getPublicContentBySeoUrl(seoUrl: String!): Content!
1356
1747
  getPublicContentsByTags(getContentByTagsInput: GetContentByTagsInput!, pageOptionsDto: PageOptionsInput!): ContentsPage!
@@ -1369,6 +1760,17 @@ type Query {
1369
1760
  getPublicSchedule(from: DateTime, locale: String, slug: String!, to: DateTime): PublicSchedule
1370
1761
  isPetAvailableForAdoption(petId: Int!): Boolean!
1371
1762
 
1763
+ """The signed-in Customer"""
1764
+ me: Customer!
1765
+
1766
+ """
1767
+ The signed-in Customer's records of one namespace (optionally some types), plus shared ones, in display order. A sensitive type needs a second factor proved in the last 15 minutes: STEP_UP_REQUIRED, then stepUpCustomer and ask again. Never cache the answer (fetch with no-store, never inside a shared cache)
1768
+ """
1769
+ myRecords(namespace: String!, types: [String!]): [CustomerRecord!]!
1770
+
1771
+ """The signed-in Customer's devices"""
1772
+ mySessions: [CustomerSession!]!
1773
+
1372
1774
  """Get post (blog) categories"""
1373
1775
  postCategories: [Category!]!
1374
1776
  projectContents(pageOptionInput: PageOptionsInput!): ContentsPage!
@@ -1486,6 +1888,19 @@ type RentalRate {
1486
1888
  updatedAt: DateTime
1487
1889
  }
1488
1890
 
1891
+ input RequestCustomerLoginInput {
1892
+ """
1893
+ The "I am 15 or older" box. Required to create an account: without it an unknown address gets no link
1894
+ """
1895
+ ageConfirmed: Boolean! = false
1896
+ email: String!
1897
+
1898
+ """
1899
+ The site's verify page; must be on one of the project's domains. The link adds ?token=
1900
+ """
1901
+ verifyUrl: String!
1902
+ }
1903
+
1489
1904
  type SalePriceType {
1490
1905
  amount: Float!
1491
1906
  compareAt: Float
@@ -1498,6 +1913,29 @@ type SalePriceType {
1498
1913
  updatedAt: DateTime
1499
1914
  }
1500
1915
 
1916
+ """
1917
+ Secure tier: the sign-in link was good, now the second factor. Send challengeToken with a code to completeCustomerSecondFactor within 5 minutes; 5 wrong codes end it
1918
+ """
1919
+ type SecondFactorChallenge {
1920
+ """Single use; keep it server-side"""
1921
+ challengeToken: String!
1922
+
1923
+ """
1924
+ False: the Customer sets up an authenticator app first (otpauthUri / manualKey / QR code)
1925
+ """
1926
+ enrolled: Boolean!
1927
+ expiresAt: DateTime!
1928
+
1929
+ """The new secret (base32) to type in by hand; only when not enrolled"""
1930
+ manualKey: String
1931
+
1932
+ """otpauth:// URI of the new secret; only when not enrolled"""
1933
+ otpauthUri: String
1934
+
1935
+ """PNG data URL of the otpauth QR code; only when not enrolled"""
1936
+ qrCodeDataUrl: String
1937
+ }
1938
+
1501
1939
  type ShopAddress {
1502
1940
  city: String!
1503
1941
  country: String!
@@ -1666,6 +2104,24 @@ enum Species {
1666
2104
  SMALL_ANIMAL
1667
2105
  }
1668
2106
 
2107
+ input StartCustomerSocialLoginInput {
2108
+ """The "I am 15 or older" box. Required to create an account"""
2109
+ ageConfirmed: Boolean! = false
2110
+
2111
+ """
2112
+ base64url(sha256(verifier)); the server keeps the verifier and redeems the code with it
2113
+ """
2114
+ challenge: String!
2115
+
2116
+ """TWITCH only for now"""
2117
+ provider: CustomerProvider!
2118
+
2119
+ """
2120
+ Where the one-time code comes back: the client zone's callback path on one of the site's domains
2121
+ """
2122
+ returnUrl: String!
2123
+ }
2124
+
1669
2125
  enum StatisticsSection {
1670
2126
  AUDIENCE
1671
2127
  EVENTS
@@ -1715,12 +2171,29 @@ type Tag {
1715
2171
  updatedAt: DateTime
1716
2172
  }
1717
2173
 
2174
+ input UpdateMeInput {
2175
+ displayName: String
2176
+
2177
+ """e.g. en, cs, en-GB"""
2178
+ locale: String
2179
+ }
2180
+
2181
+ input UpdateMyAttributesInput {
2182
+ """Only these keys change; at most 50"""
2183
+ values: [CustomerAttributeValueInput!]!
2184
+ }
2185
+
1718
2186
  type VatLine {
1719
2187
  baseMinor: Int!
1720
2188
  rate: Int!
1721
2189
  vatMinor: Int!
1722
2190
  }
1723
2191
 
2192
+ input VerifyCustomerEmailInput {
2193
+ """The 6-digit code from the e-mail"""
2194
+ code: String!
2195
+ }
2196
+
1724
2197
  """Supported SEO verification providers"""
1725
2198
  enum projectEntityEnum {
1726
2199
  Google