@webstudio-is/trpc-interface 0.68.0 → 0.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,7 +21,7 @@ const hasProjectPermit = async (props, context) => {
21
21
  const { authorizeTrpc } = authorization;
22
22
  const checks = [];
23
23
  const namespace = "Project";
24
- if (props.permit === "view" && context.authorization.buildEnv === "prod") {
24
+ if (props.permit === "view" && context.authorization.isServiceCall) {
25
25
  return true;
26
26
  }
27
27
  if (props.permit === "view" && props.projectId === "62154aaef0cb0860ccf85d6e") {
@@ -46,7 +46,7 @@ const hasProjectPermit = async (props, context) => {
46
46
  const { authorizeTrpc } = authorization;
47
47
  const checks = [];
48
48
  const namespace = "Project";
49
- if (props.permit === "view" && context.authorization.buildEnv === "prod") {
49
+ if (props.permit === "view" && context.authorization.isServiceCall) {
50
50
  return true;
51
51
  }
52
52
  if (props.permit === "view" && props.projectId === "62154aaef0cb0860ccf85d6e") {
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var deployment_exports = {};
20
+ __export(deployment_exports, {
21
+ deploymentRouter: () => deploymentRouter
22
+ });
23
+ module.exports = __toCommonJS(deployment_exports);
24
+ var import_zod = require("zod");
25
+ var import_trpc = require("./trpc");
26
+ const PublishInput = import_zod.z.object({
27
+ // used to load build data from the builder see routes/rest.build.$buildId.ts
28
+ buildId: import_zod.z.string(),
29
+ builderApiOrigin: import_zod.z.string(),
30
+ // preview support
31
+ branchName: import_zod.z.string(),
32
+ // action log helper (not used for deployment, but for action logs readablity)
33
+ projectDomainName: import_zod.z.string()
34
+ });
35
+ const Output = import_zod.z.discriminatedUnion("success", [
36
+ import_zod.z.object({
37
+ success: import_zod.z.literal(true)
38
+ }),
39
+ import_zod.z.object({
40
+ success: import_zod.z.literal(false),
41
+ error: import_zod.z.string()
42
+ })
43
+ ]);
44
+ const deploymentRouter = (0, import_trpc.router)({
45
+ publish: import_trpc.procedure.input(PublishInput).output(Output).mutation(async ({ input, ctx }) => {
46
+ return { success: false, error: "Not implemented" };
47
+ })
48
+ });
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var domain_exports = {};
20
+ __export(domain_exports, {
21
+ domainRouter: () => domainRouter
22
+ });
23
+ module.exports = __toCommonJS(domain_exports);
24
+ var import_zod = require("zod");
25
+ var import_trpc = require("./trpc");
26
+ const CreateInput = import_zod.z.object({ domain: import_zod.z.string(), txtRecord: import_zod.z.string() });
27
+ const Input = import_zod.z.object({ domain: import_zod.z.string() });
28
+ const createOutput = (data) => import_zod.z.discriminatedUnion("success", [
29
+ import_zod.z.object({ success: import_zod.z.literal(true), data }),
30
+ import_zod.z.object({ success: import_zod.z.literal(false), error: import_zod.z.string() })
31
+ ]);
32
+ globalThis.dnsTxtEntries = globalThis.dnsTxtEntries ?? /* @__PURE__ */ new Map();
33
+ globalThis.domainStates = globalThis.domainStates ?? /* @__PURE__ */ new Map();
34
+ const domainRouter = (0, import_trpc.router)({
35
+ /**
36
+ * Verify TXT record and add custom domain entry to DNS
37
+ */
38
+ create: import_trpc.procedure.input(CreateInput).output(createOutput(import_zod.z.optional(import_zod.z.undefined()))).mutation(async ({ input, ctx }) => {
39
+ const record = dnsTxtEntries.get(input.domain);
40
+ if (record !== input.txtRecord) {
41
+ dnsTxtEntries.set(input.domain, input.txtRecord);
42
+ return {
43
+ success: false,
44
+ error: `TXT record does not match, expected "${input.txtRecord}" but got "${record ?? "undefined"}"`
45
+ };
46
+ }
47
+ domainStates.set(input.domain, "pending");
48
+ return { success: true };
49
+ }),
50
+ refresh: import_trpc.procedure.input(Input).output(createOutput(import_zod.z.optional(import_zod.z.undefined()))).mutation(async ({ input, ctx }) => {
51
+ return { success: true };
52
+ }),
53
+ /**
54
+ * Get status of verified domain
55
+ */
56
+ getStatus: import_trpc.procedure.input(Input).output(
57
+ createOutput(
58
+ import_zod.z.discriminatedUnion("status", [
59
+ import_zod.z.object({ status: import_zod.z.enum(["active", "pending"]) }),
60
+ import_zod.z.object({ status: import_zod.z.enum(["error"]), error: import_zod.z.string() })
61
+ ])
62
+ )
63
+ ).query(async ({ input, ctx }) => {
64
+ const domainState = domainStates.get(input.domain);
65
+ if (domainState === void 0) {
66
+ domainStates.set(input.domain, "pending");
67
+ return {
68
+ success: false,
69
+ error: `Domain ${input.domain} is not active`
70
+ };
71
+ }
72
+ if (domainState === "active") {
73
+ setTimeout(() => {
74
+ domainStates.set(input.domain, "error");
75
+ });
76
+ }
77
+ if (domainState === "pending" || domainState === "error") {
78
+ setTimeout(() => {
79
+ domainStates.set(input.domain, "active");
80
+ }, 5e3);
81
+ }
82
+ if (domainState === "error") {
83
+ return {
84
+ success: true,
85
+ data: {
86
+ status: "error",
87
+ error: "Domain cname verification failed"
88
+ }
89
+ };
90
+ }
91
+ return {
92
+ success: true,
93
+ data: {
94
+ status: domainState
95
+ }
96
+ };
97
+ })
98
+ });
@@ -23,6 +23,10 @@ __export(shared_router_exports, {
23
23
  module.exports = __toCommonJS(shared_router_exports);
24
24
  var import_trpc = require("./trpc");
25
25
  var import_authorization_router = require("./authorization-router");
26
+ var import_domain = require("./domain");
27
+ var import_deployment = require("./deployment");
26
28
  const sharedRouter = (0, import_trpc.router)({
27
- authorize: import_authorization_router.authorizationRouter
29
+ authorize: import_authorization_router.authorizationRouter,
30
+ domain: import_domain.domainRouter,
31
+ deployment: import_deployment.deploymentRouter
28
32
  });
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ import { router, procedure } from "./trpc";
3
+ const PublishInput = z.object({
4
+ // used to load build data from the builder see routes/rest.build.$buildId.ts
5
+ buildId: z.string(),
6
+ builderApiOrigin: z.string(),
7
+ // preview support
8
+ branchName: z.string(),
9
+ // action log helper (not used for deployment, but for action logs readablity)
10
+ projectDomainName: z.string()
11
+ });
12
+ const Output = z.discriminatedUnion("success", [
13
+ z.object({
14
+ success: z.literal(true)
15
+ }),
16
+ z.object({
17
+ success: z.literal(false),
18
+ error: z.string()
19
+ })
20
+ ]);
21
+ const deploymentRouter = router({
22
+ publish: procedure.input(PublishInput).output(Output).mutation(async ({ input, ctx }) => {
23
+ return { success: false, error: "Not implemented" };
24
+ })
25
+ });
26
+ export {
27
+ deploymentRouter
28
+ };
@@ -0,0 +1,78 @@
1
+ import { z } from "zod";
2
+ import { router, procedure } from "./trpc";
3
+ const CreateInput = z.object({ domain: z.string(), txtRecord: z.string() });
4
+ const Input = z.object({ domain: z.string() });
5
+ const createOutput = (data) => z.discriminatedUnion("success", [
6
+ z.object({ success: z.literal(true), data }),
7
+ z.object({ success: z.literal(false), error: z.string() })
8
+ ]);
9
+ globalThis.dnsTxtEntries = globalThis.dnsTxtEntries ?? /* @__PURE__ */ new Map();
10
+ globalThis.domainStates = globalThis.domainStates ?? /* @__PURE__ */ new Map();
11
+ const domainRouter = router({
12
+ /**
13
+ * Verify TXT record and add custom domain entry to DNS
14
+ */
15
+ create: procedure.input(CreateInput).output(createOutput(z.optional(z.undefined()))).mutation(async ({ input, ctx }) => {
16
+ const record = dnsTxtEntries.get(input.domain);
17
+ if (record !== input.txtRecord) {
18
+ dnsTxtEntries.set(input.domain, input.txtRecord);
19
+ return {
20
+ success: false,
21
+ error: `TXT record does not match, expected "${input.txtRecord}" but got "${record ?? "undefined"}"`
22
+ };
23
+ }
24
+ domainStates.set(input.domain, "pending");
25
+ return { success: true };
26
+ }),
27
+ refresh: procedure.input(Input).output(createOutput(z.optional(z.undefined()))).mutation(async ({ input, ctx }) => {
28
+ return { success: true };
29
+ }),
30
+ /**
31
+ * Get status of verified domain
32
+ */
33
+ getStatus: procedure.input(Input).output(
34
+ createOutput(
35
+ z.discriminatedUnion("status", [
36
+ z.object({ status: z.enum(["active", "pending"]) }),
37
+ z.object({ status: z.enum(["error"]), error: z.string() })
38
+ ])
39
+ )
40
+ ).query(async ({ input, ctx }) => {
41
+ const domainState = domainStates.get(input.domain);
42
+ if (domainState === void 0) {
43
+ domainStates.set(input.domain, "pending");
44
+ return {
45
+ success: false,
46
+ error: `Domain ${input.domain} is not active`
47
+ };
48
+ }
49
+ if (domainState === "active") {
50
+ setTimeout(() => {
51
+ domainStates.set(input.domain, "error");
52
+ });
53
+ }
54
+ if (domainState === "pending" || domainState === "error") {
55
+ setTimeout(() => {
56
+ domainStates.set(input.domain, "active");
57
+ }, 5e3);
58
+ }
59
+ if (domainState === "error") {
60
+ return {
61
+ success: true,
62
+ data: {
63
+ status: "error",
64
+ error: "Domain cname verification failed"
65
+ }
66
+ };
67
+ }
68
+ return {
69
+ success: true,
70
+ data: {
71
+ status: domainState
72
+ }
73
+ };
74
+ })
75
+ });
76
+ export {
77
+ domainRouter
78
+ };
@@ -1,7 +1,11 @@
1
1
  import { router } from "./trpc";
2
2
  import { authorizationRouter } from "./authorization-router";
3
+ import { domainRouter } from "./domain";
4
+ import { deploymentRouter } from "./deployment";
3
5
  const sharedRouter = router({
4
- authorize: authorizationRouter
6
+ authorize: authorizationRouter,
7
+ domain: domainRouter,
8
+ deployment: deploymentRouter
5
9
  });
6
10
  export {
7
11
  sharedRouter
@@ -12,11 +12,21 @@ type AuthorizationContext = {
12
12
  */
13
13
  authToken: string | undefined;
14
14
  /**
15
- * buildEnv==="prod" only if we are loading project with production build
15
+ * Allow service 2 service communications to skip authorization for view calls
16
16
  */
17
- buildEnv: "dev" | "prod";
17
+ isServiceCall: boolean;
18
18
  authorizeTrpc: TrpcInterfaceClient["authorize"];
19
19
  };
20
+ type DomainContext = {
21
+ domainTrpc: TrpcInterfaceClient["domain"];
22
+ };
23
+ type DeploymentContext = {
24
+ deploymentTrpc: TrpcInterfaceClient["deployment"];
25
+ env: {
26
+ BUILDER_ORIGIN: string;
27
+ BRANCH_NAME: string;
28
+ };
29
+ };
20
30
  /**
21
31
  * AppContext is a global context that is passed to all trpc/api queries/mutations
22
32
  * "authorization" is made inside the namespace because eventually there will be
@@ -24,5 +34,7 @@ type AuthorizationContext = {
24
34
  */
25
35
  export type AppContext = {
26
36
  authorization: AuthorizationContext;
37
+ domain: DomainContext;
38
+ deployment: DeploymentContext;
27
39
  };
28
40
  export {};
@@ -0,0 +1,45 @@
1
+ /**
2
+ * This is the ContentManagementService. It is currently used to publish content to a custom domain.
3
+ * In the future, additional methods, such as a 'preview' function, could be added.
4
+ **/
5
+ export declare const deploymentRouter: import("@trpc/server").CreateRouterInner<import("@trpc/server").RootConfig<{
6
+ ctx: {};
7
+ meta: object;
8
+ errorShape: import("@trpc/server").DefaultErrorShape;
9
+ transformer: import("@trpc/server").DefaultDataTransformer;
10
+ }>, {
11
+ publish: import("@trpc/server").BuildProcedure<"mutation", {
12
+ _config: import("@trpc/server").RootConfig<{
13
+ ctx: {};
14
+ meta: object;
15
+ errorShape: import("@trpc/server").DefaultErrorShape;
16
+ transformer: import("@trpc/server").DefaultDataTransformer;
17
+ }>;
18
+ _meta: object;
19
+ _ctx_out: {};
20
+ _input_in: {
21
+ buildId: string;
22
+ builderApiOrigin: string;
23
+ branchName: string;
24
+ projectDomainName: string;
25
+ };
26
+ _input_out: {
27
+ buildId: string;
28
+ builderApiOrigin: string;
29
+ branchName: string;
30
+ projectDomainName: string;
31
+ };
32
+ _output_in: {
33
+ success: true;
34
+ } | {
35
+ error: string;
36
+ success: false;
37
+ };
38
+ _output_out: {
39
+ success: true;
40
+ } | {
41
+ error: string;
42
+ success: false;
43
+ };
44
+ }, unknown>;
45
+ }>;
@@ -0,0 +1,119 @@
1
+ declare global {
2
+ var dnsTxtEntries: Map<string, string>;
3
+ var domainStates: Map<string, "active" | "pending" | "error">;
4
+ }
5
+ export declare const domainRouter: import("@trpc/server").CreateRouterInner<import("@trpc/server").RootConfig<{
6
+ ctx: {};
7
+ meta: object;
8
+ errorShape: import("@trpc/server").DefaultErrorShape;
9
+ transformer: import("@trpc/server").DefaultDataTransformer;
10
+ }>, {
11
+ /**
12
+ * Verify TXT record and add custom domain entry to DNS
13
+ */
14
+ create: import("@trpc/server").BuildProcedure<"mutation", {
15
+ _config: import("@trpc/server").RootConfig<{
16
+ ctx: {};
17
+ meta: object;
18
+ errorShape: import("@trpc/server").DefaultErrorShape;
19
+ transformer: import("@trpc/server").DefaultDataTransformer;
20
+ }>;
21
+ _meta: object;
22
+ _ctx_out: {};
23
+ _input_in: {
24
+ domain: string;
25
+ txtRecord: string;
26
+ };
27
+ _input_out: {
28
+ domain: string;
29
+ txtRecord: string;
30
+ };
31
+ _output_in: {
32
+ success: true;
33
+ data?: undefined;
34
+ } | {
35
+ error: string;
36
+ success: false;
37
+ };
38
+ _output_out: {
39
+ success: true;
40
+ data?: undefined;
41
+ } | {
42
+ error: string;
43
+ success: false;
44
+ };
45
+ }, unknown>;
46
+ refresh: import("@trpc/server").BuildProcedure<"mutation", {
47
+ _config: import("@trpc/server").RootConfig<{
48
+ ctx: {};
49
+ meta: object;
50
+ errorShape: import("@trpc/server").DefaultErrorShape;
51
+ transformer: import("@trpc/server").DefaultDataTransformer;
52
+ }>;
53
+ _meta: object;
54
+ _ctx_out: {};
55
+ _input_in: {
56
+ domain: string;
57
+ };
58
+ _input_out: {
59
+ domain: string;
60
+ };
61
+ _output_in: {
62
+ success: true;
63
+ data?: undefined;
64
+ } | {
65
+ error: string;
66
+ success: false;
67
+ };
68
+ _output_out: {
69
+ success: true;
70
+ data?: undefined;
71
+ } | {
72
+ error: string;
73
+ success: false;
74
+ };
75
+ }, unknown>;
76
+ /**
77
+ * Get status of verified domain
78
+ */
79
+ getStatus: import("@trpc/server").BuildProcedure<"query", {
80
+ _config: import("@trpc/server").RootConfig<{
81
+ ctx: {};
82
+ meta: object;
83
+ errorShape: import("@trpc/server").DefaultErrorShape;
84
+ transformer: import("@trpc/server").DefaultDataTransformer;
85
+ }>;
86
+ _meta: object;
87
+ _ctx_out: {};
88
+ _input_in: {
89
+ domain: string;
90
+ };
91
+ _input_out: {
92
+ domain: string;
93
+ };
94
+ _output_in: {
95
+ data: {
96
+ status: "active" | "pending";
97
+ } | {
98
+ error: string;
99
+ status: "error";
100
+ };
101
+ success: true;
102
+ } | {
103
+ error: string;
104
+ success: false;
105
+ };
106
+ _output_out: {
107
+ data: {
108
+ status: "active" | "pending";
109
+ } | {
110
+ error: string;
111
+ status: "error";
112
+ };
113
+ success: true;
114
+ } | {
115
+ error: string;
116
+ success: false;
117
+ };
118
+ }, unknown>;
119
+ }>;
@@ -260,6 +260,156 @@ export declare const sharedRouter: import("@trpc/server").CreateRouterInner<impo
260
260
  _output_out: typeof import("@trpc/server").unsetMarker;
261
261
  }, void>;
262
262
  }>;
263
+ domain: import("@trpc/server").CreateRouterInner<import("@trpc/server").RootConfig<{
264
+ ctx: {};
265
+ meta: object;
266
+ errorShape: import("@trpc/server").DefaultErrorShape;
267
+ transformer: import("@trpc/server").DefaultDataTransformer;
268
+ }>, {
269
+ create: import("@trpc/server").BuildProcedure<"mutation", {
270
+ _config: import("@trpc/server").RootConfig<{
271
+ ctx: {};
272
+ meta: object;
273
+ errorShape: import("@trpc/server").DefaultErrorShape;
274
+ transformer: import("@trpc/server").DefaultDataTransformer;
275
+ }>;
276
+ _meta: object;
277
+ _ctx_out: {};
278
+ _input_in: {
279
+ domain: string;
280
+ txtRecord: string;
281
+ };
282
+ _input_out: {
283
+ domain: string;
284
+ txtRecord: string;
285
+ };
286
+ _output_in: {
287
+ success: true;
288
+ data?: undefined;
289
+ } | {
290
+ error: string;
291
+ success: false;
292
+ };
293
+ _output_out: {
294
+ success: true;
295
+ data?: undefined;
296
+ } | {
297
+ error: string;
298
+ success: false;
299
+ };
300
+ }, unknown>;
301
+ refresh: import("@trpc/server").BuildProcedure<"mutation", {
302
+ _config: import("@trpc/server").RootConfig<{
303
+ ctx: {};
304
+ meta: object;
305
+ errorShape: import("@trpc/server").DefaultErrorShape;
306
+ transformer: import("@trpc/server").DefaultDataTransformer;
307
+ }>;
308
+ _meta: object;
309
+ _ctx_out: {};
310
+ _input_in: {
311
+ domain: string;
312
+ };
313
+ _input_out: {
314
+ domain: string;
315
+ };
316
+ _output_in: {
317
+ success: true;
318
+ data?: undefined;
319
+ } | {
320
+ error: string;
321
+ success: false;
322
+ };
323
+ _output_out: {
324
+ success: true;
325
+ data?: undefined;
326
+ } | {
327
+ error: string;
328
+ success: false;
329
+ };
330
+ }, unknown>;
331
+ getStatus: import("@trpc/server").BuildProcedure<"query", {
332
+ _config: import("@trpc/server").RootConfig<{
333
+ ctx: {};
334
+ meta: object;
335
+ errorShape: import("@trpc/server").DefaultErrorShape;
336
+ transformer: import("@trpc/server").DefaultDataTransformer;
337
+ }>;
338
+ _meta: object;
339
+ _ctx_out: {};
340
+ _input_in: {
341
+ domain: string;
342
+ };
343
+ _input_out: {
344
+ domain: string;
345
+ };
346
+ _output_in: {
347
+ data: {
348
+ status: "active" | "pending";
349
+ } | {
350
+ error: string;
351
+ status: "error";
352
+ };
353
+ success: true;
354
+ } | {
355
+ error: string;
356
+ success: false;
357
+ };
358
+ _output_out: {
359
+ data: {
360
+ status: "active" | "pending";
361
+ } | {
362
+ error: string;
363
+ status: "error";
364
+ };
365
+ success: true;
366
+ } | {
367
+ error: string;
368
+ success: false;
369
+ };
370
+ }, unknown>;
371
+ }>;
372
+ deployment: import("@trpc/server").CreateRouterInner<import("@trpc/server").RootConfig<{
373
+ ctx: {};
374
+ meta: object;
375
+ errorShape: import("@trpc/server").DefaultErrorShape;
376
+ transformer: import("@trpc/server").DefaultDataTransformer;
377
+ }>, {
378
+ publish: import("@trpc/server").BuildProcedure<"mutation", {
379
+ _config: import("@trpc/server").RootConfig<{
380
+ ctx: {};
381
+ meta: object;
382
+ errorShape: import("@trpc/server").DefaultErrorShape;
383
+ transformer: import("@trpc/server").DefaultDataTransformer;
384
+ }>;
385
+ _meta: object;
386
+ _ctx_out: {};
387
+ _input_in: {
388
+ buildId: string;
389
+ builderApiOrigin: string;
390
+ branchName: string;
391
+ projectDomainName: string;
392
+ };
393
+ _input_out: {
394
+ buildId: string;
395
+ builderApiOrigin: string;
396
+ branchName: string;
397
+ projectDomainName: string;
398
+ };
399
+ _output_in: {
400
+ success: true;
401
+ } | {
402
+ error: string;
403
+ success: false;
404
+ };
405
+ _output_out: {
406
+ success: true;
407
+ } | {
408
+ error: string;
409
+ success: false;
410
+ };
411
+ }, unknown>;
412
+ }>;
263
413
  }>;
264
414
  export type SharedRouter = typeof sharedRouter;
265
415
  export type TrpcInterfaceClient = ReturnType<typeof createTRPCProxyClient<SharedRouter>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webstudio-is/trpc-interface",
3
- "version": "0.68.0",
3
+ "version": "0.69.0",
4
4
  "description": "Webstudio TRPC Interface",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -11,8 +11,8 @@
11
11
  "node-fetch": "2",
12
12
  "ts-custom-error": "^3.3.1",
13
13
  "uuid": "^9.0.0",
14
- "zod": "^3.19.1",
15
- "@webstudio-is/prisma-client": "^0.68.0"
14
+ "zod": "^3.21.4",
15
+ "@webstudio-is/prisma-client": "^0.69.0"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/node": "^18.11.18",
@@ -47,7 +47,7 @@ export const hasProjectPermit = async (
47
47
  const namespace = "Project";
48
48
 
49
49
  // Allow load production build env i.e. "published" site
50
- if (props.permit === "view" && context.authorization.buildEnv === "prod") {
50
+ if (props.permit === "view" && context.authorization.isServiceCall) {
51
51
  return true;
52
52
  }
53
53
 
@@ -15,14 +15,26 @@ type AuthorizationContext = {
15
15
  authToken: string | undefined;
16
16
 
17
17
  /**
18
- * buildEnv==="prod" only if we are loading project with production build
18
+ * Allow service 2 service communications to skip authorization for view calls
19
19
  */
20
- buildEnv: "dev" | "prod";
20
+ isServiceCall: boolean;
21
21
 
22
22
  // Pass trpcClient through context as only main app can initialize it
23
23
  authorizeTrpc: TrpcInterfaceClient["authorize"];
24
24
  };
25
25
 
26
+ type DomainContext = {
27
+ domainTrpc: TrpcInterfaceClient["domain"];
28
+ };
29
+
30
+ type DeploymentContext = {
31
+ deploymentTrpc: TrpcInterfaceClient["deployment"];
32
+ env: {
33
+ BUILDER_ORIGIN: string;
34
+ BRANCH_NAME: string;
35
+ };
36
+ };
37
+
26
38
  /**
27
39
  * AppContext is a global context that is passed to all trpc/api queries/mutations
28
40
  * "authorization" is made inside the namespace because eventually there will be
@@ -30,4 +42,6 @@ type AuthorizationContext = {
30
42
  */
31
43
  export type AppContext = {
32
44
  authorization: AuthorizationContext;
45
+ domain: DomainContext;
46
+ deployment: DeploymentContext;
33
47
  };
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ import { router, procedure } from "./trpc";
3
+
4
+ const PublishInput = z.object({
5
+ // used to load build data from the builder see routes/rest.build.$buildId.ts
6
+ buildId: z.string(),
7
+ builderApiOrigin: z.string(),
8
+ // preview support
9
+ branchName: z.string(),
10
+ // action log helper (not used for deployment, but for action logs readablity)
11
+ projectDomainName: z.string(),
12
+ });
13
+
14
+ const Output = z.discriminatedUnion("success", [
15
+ z.object({
16
+ success: z.literal(true),
17
+ }),
18
+ z.object({
19
+ success: z.literal(false),
20
+ error: z.string(),
21
+ }),
22
+ ]);
23
+
24
+ /**
25
+ * This is the ContentManagementService. It is currently used to publish content to a custom domain.
26
+ * In the future, additional methods, such as a 'preview' function, could be added.
27
+ **/
28
+ export const deploymentRouter = router({
29
+ publish: procedure
30
+ .input(PublishInput)
31
+ .output(Output)
32
+ .mutation(async ({ input, ctx }) => {
33
+ return { success: false, error: "Not implemented" };
34
+ }),
35
+ });
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Localhost implementation of the dashboard trpc interface
3
+ * It's playground, and just emulates real 3rd party apis
4
+ */
5
+ import { z } from "zod";
6
+ import { router, procedure } from "./trpc";
7
+
8
+ const CreateInput = z.object({ domain: z.string(), txtRecord: z.string() });
9
+ const Input = z.object({ domain: z.string() });
10
+
11
+ const createOutput = <T extends z.ZodType>(data: T) =>
12
+ z.discriminatedUnion("success", [
13
+ z.object({ success: z.literal(true), data }),
14
+ z.object({ success: z.literal(false), error: z.string() }),
15
+ ]);
16
+
17
+ declare global {
18
+ // eslint-disable-next-line no-var
19
+ var dnsTxtEntries: Map<string, string>;
20
+ // eslint-disable-next-line no-var
21
+ var domainStates: Map<string, "active" | "pending" | "error">;
22
+ }
23
+
24
+ // Remix purges require module cache on every request in development,
25
+ // this is the way to persist data between requests in development
26
+ globalThis.dnsTxtEntries =
27
+ globalThis.dnsTxtEntries ?? new Map<string, string>();
28
+ globalThis.domainStates =
29
+ globalThis.domainStates ?? new Map<string, "active" | "pending">();
30
+
31
+ export const domainRouter = router({
32
+ /**
33
+ * Verify TXT record and add custom domain entry to DNS
34
+ */
35
+ create: procedure
36
+ .input(CreateInput)
37
+ .output(createOutput(z.optional(z.undefined())))
38
+ .mutation(async ({ input, ctx }) => {
39
+ const record = dnsTxtEntries.get(input.domain);
40
+ if (record !== input.txtRecord) {
41
+ // Return an error once then update the record
42
+ dnsTxtEntries.set(input.domain, input.txtRecord);
43
+
44
+ return {
45
+ success: false,
46
+ error: `TXT record does not match, expected "${
47
+ input.txtRecord
48
+ }" but got "${record ?? "undefined"}"`,
49
+ };
50
+ }
51
+
52
+ domainStates.set(input.domain, "pending");
53
+
54
+ return { success: true };
55
+ }),
56
+
57
+ refresh: procedure
58
+ .input(Input)
59
+ .output(createOutput(z.optional(z.undefined())))
60
+ .mutation(async ({ input, ctx }) => {
61
+ return { success: true };
62
+ }),
63
+ /**
64
+ * Get status of verified domain
65
+ */
66
+ getStatus: procedure
67
+ .input(Input)
68
+ .output(
69
+ createOutput(
70
+ z.discriminatedUnion("status", [
71
+ z.object({ status: z.enum(["active", "pending"]) }),
72
+ z.object({ status: z.enum(["error"]), error: z.string() }),
73
+ ])
74
+ )
75
+ )
76
+ .query(async ({ input, ctx }) => {
77
+ const domainState = domainStates.get(input.domain);
78
+
79
+ if (domainState === undefined) {
80
+ domainStates.set(input.domain, "pending");
81
+
82
+ return {
83
+ success: false,
84
+ error: `Domain ${input.domain} is not active`,
85
+ };
86
+ }
87
+
88
+ if (domainState === "active") {
89
+ setTimeout(() => {
90
+ domainStates.set(input.domain, "error");
91
+ });
92
+ }
93
+
94
+ if (domainState === "pending" || domainState === "error") {
95
+ setTimeout(() => {
96
+ domainStates.set(input.domain, "active");
97
+ }, 5000);
98
+ }
99
+
100
+ if (domainState === "error") {
101
+ return {
102
+ success: true,
103
+ data: {
104
+ status: "error",
105
+ error: "Domain cname verification failed",
106
+ },
107
+ };
108
+ }
109
+
110
+ return {
111
+ success: true,
112
+ data: {
113
+ status: domainState,
114
+ },
115
+ };
116
+ }),
117
+ });
@@ -1,9 +1,13 @@
1
1
  import type { createTRPCProxyClient } from "@trpc/client";
2
2
  import { router } from "./trpc";
3
3
  import { authorizationRouter } from "./authorization-router";
4
+ import { domainRouter } from "./domain";
5
+ import { deploymentRouter } from "./deployment";
4
6
 
5
7
  export const sharedRouter = router({
6
8
  authorize: authorizationRouter,
9
+ domain: domainRouter,
10
+ deployment: deploymentRouter,
7
11
  });
8
12
 
9
13
  export type SharedRouter = typeof sharedRouter;