@webstudio-is/trpc-interface 0.68.0 → 0.70.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.
- package/lib/authorize/project.server.js +1 -1
- package/lib/cjs/authorize/project.server.js +1 -1
- package/lib/cjs/shared/deployment.js +48 -0
- package/lib/cjs/shared/domain.js +98 -0
- package/lib/cjs/shared/shared-router.js +5 -1
- package/lib/shared/deployment.js +28 -0
- package/lib/shared/domain.js +78 -0
- package/lib/shared/shared-router.js +5 -1
- package/lib/types/context/context.server.d.ts +14 -2
- package/lib/types/shared/deployment.d.ts +45 -0
- package/lib/types/shared/domain.d.ts +119 -0
- package/lib/types/shared/shared-router.d.ts +150 -0
- package/package.json +3 -3
- package/src/authorize/project.server.ts +1 -1
- package/src/context/context.server.ts +16 -2
- package/src/shared/deployment.ts +35 -0
- package/src/shared/domain.ts +117 -0
- package/src/shared/shared-router.ts +4 -0
|
@@ -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.
|
|
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.
|
|
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
|
-
*
|
|
15
|
+
* Allow service 2 service communications to skip authorization for view calls
|
|
16
16
|
*/
|
|
17
|
-
|
|
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.
|
|
3
|
+
"version": "0.70.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.
|
|
15
|
-
"@webstudio-is/prisma-client": "^0.
|
|
14
|
+
"zod": "^3.21.4",
|
|
15
|
+
"@webstudio-is/prisma-client": "^0.70.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.
|
|
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
|
-
*
|
|
18
|
+
* Allow service 2 service communications to skip authorization for view calls
|
|
19
19
|
*/
|
|
20
|
-
|
|
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;
|