@schemavaults/trpc-backend-init 0.8.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 +52 -0
- package/dist/context/context.d.ts +23 -0
- package/dist/context/context.js +94 -0
- package/dist/context/context.js.map +1 -0
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.js +2 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/load_auth_context.d.ts +5 -0
- package/dist/context/load_auth_context.js +60 -0
- package/dist/context/load_auth_context.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/procedures/create_procedures.d.ts +44 -0
- package/dist/procedures/create_procedures.js +44 -0
- package/dist/procedures/create_procedures.js.map +1 -0
- package/dist/procedures/index.d.ts +2 -0
- package/dist/procedures/index.js +2 -0
- package/dist/procedures/index.js.map +1 -0
- package/dist/procedures/schemavaults-trpc-procedures.d.ts +3 -0
- package/dist/procedures/schemavaults-trpc-procedures.js +2 -0
- package/dist/procedures/schemavaults-trpc-procedures.js.map +1 -0
- package/dist/schemavaults-trpc-backend.d.ts +22 -0
- package/dist/schemavaults-trpc-backend.js +31 -0
- package/dist/schemavaults-trpc-backend.js.map +1 -0
- package/dist/schemavaults-trpc-runtime.d.ts +5 -0
- package/dist/schemavaults-trpc-runtime.js +2 -0
- package/dist/schemavaults-trpc-runtime.js.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @schemavaults/trpc-backend-init
|
|
2
|
+
|
|
3
|
+
Repository for easily initializing a tRPC server with SchemaVaults Auth access token validation.
|
|
4
|
+
|
|
5
|
+
## What This Package Does
|
|
6
|
+
|
|
7
|
+
`@schemavaults/trpc-backend-init` is a tRPC server-side router factory for the SchemaVaults ecosystem. It provides an abstract base class that microservices extend to get type-safe tRPC routers with built-in JWT authentication middleware. The "virtual backend" pattern lets client SDKs target a single router interface that routes to the correct microservice (registry server, regional vault filesystem servers).
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
- **Build**: `bun run build` (runs `tsc` then `tsc-alias` to resolve `@/*` path aliases, then removes test files from dist)
|
|
12
|
+
- **Test**: `bun run test` (sets `SCHEMAVAULTS_APP_ENVIRONMENT=test NODE_ENV=test`)
|
|
13
|
+
- **Run single test**: `SCHEMAVAULTS_APP_ENVIRONMENT=test NODE_ENV=test bun test src/context/load_auth_context.test.ts`
|
|
14
|
+
|
|
15
|
+
## Architecture
|
|
16
|
+
|
|
17
|
+
### Core Components
|
|
18
|
+
|
|
19
|
+
- **`SchemaVaults_tRPC_Backend<R>`** (`src/schemavaults-trpc-backend.ts`) — Abstract class that consuming services extend. Generic over `R extends Base_SchemaVaults_tRPC_Resources` to allow each service to inject its own resources into the tRPC context. Initializes tRPC once via `initTRPC` and exposes `router` and `lazy` builders.
|
|
20
|
+
|
|
21
|
+
- **Context** (`src/context/`) — `createContext()` factory builds the tRPC context per-request. Calls `loadAuthContext()` which extracts the Bearer token, validates the JWT via `RemoteJwtKeyManager` from `@schemavaults/auth-server-sdk`, and populates `user` and `user_organizations` (or null if unauthenticated).
|
|
22
|
+
|
|
23
|
+
- **Procedures** (`src/procedures/`) — `createProcedures()` factory builds three procedure levels using tRPC middleware:
|
|
24
|
+
- `publicProcedure` — no auth required
|
|
25
|
+
- `authorizedProcedure` — requires authenticated user (throws UNAUTHORIZED)
|
|
26
|
+
- `adminProcedure` — requires authenticated admin user (throws FORBIDDEN)
|
|
27
|
+
|
|
28
|
+
### Key Types (exported from `src/index.ts`)
|
|
29
|
+
|
|
30
|
+
- `SchemaVaults_tRPC_Context<R>` — The context shape including `user`, `user_organizations`, `connected_server_resources`, `jwt_audience`, `environment`, `debug`
|
|
31
|
+
- `SchemaVaults_tRPC_Runtime` — The initialized tRPC instance type
|
|
32
|
+
- `SchemaVaults_tRPC_Procedures` — The procedure set type
|
|
33
|
+
|
|
34
|
+
### Dependencies
|
|
35
|
+
|
|
36
|
+
- `@trpc/server` (v11) — tRPC framework
|
|
37
|
+
- `@schemavaults/auth-server-sdk` — JWT validation and remote key management
|
|
38
|
+
- `@schemavaults/app-definitions` — Environment types, auth server URIs, app config
|
|
39
|
+
|
|
40
|
+
## Environment Variables
|
|
41
|
+
|
|
42
|
+
- `SCHEMAVAULTS_APP_ENVIRONMENT` — App environment (development/test/staging/production)
|
|
43
|
+
- `SCHEMAVAULTS_API_SERVER_ID` — JWT audience identifier (required for auth context)
|
|
44
|
+
- `SCHEMAVAULTS_GITHUB_PACKAGE_REGISTRY_TOKEN` — For installing/publishing to GitHub Packages
|
|
45
|
+
|
|
46
|
+
## Conventions
|
|
47
|
+
|
|
48
|
+
- Path alias `@/*` maps to `./src/*` — resolved at build time by `tsc-alias`
|
|
49
|
+
- Tests use Bun's built-in test runner (`import { describe, expect, test } from "bun:test"`)
|
|
50
|
+
- Test files (`*.test.ts`) live alongside source files and are excluded from dist by the postbuild cleanup
|
|
51
|
+
- Published to GitHub Packages NPM registry (`@schemavaults` scope)
|
|
52
|
+
- CI runs on push to main: build → test → publish via GitHub Actions
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type AuthContext } from "./load_auth_context";
|
|
2
|
+
import { type SchemaVaultsAppEnvironment } from "@schemavaults/app-definitions";
|
|
3
|
+
import { type OrganizationID, type UserData, type OrganizationMembershipRoleType } from "@schemavaults/auth-server-sdk";
|
|
4
|
+
export interface Base_SchemaVaults_tRPC_Resources {
|
|
5
|
+
}
|
|
6
|
+
export interface SchemaVaults_tRPC_Context<R extends Base_SchemaVaults_tRPC_Resources> {
|
|
7
|
+
user: AuthContext["user"] | null;
|
|
8
|
+
isUserInOrganization: (user: UserData, org_id: OrganizationID) => Promise<OrganizationMembershipRoleType | false>;
|
|
9
|
+
connected_server_resources: R;
|
|
10
|
+
jwt_audience: string;
|
|
11
|
+
environment: SchemaVaultsAppEnvironment;
|
|
12
|
+
debug: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface ICreateContextFnOptions<R extends Base_SchemaVaults_tRPC_Resources> {
|
|
15
|
+
getAuthHeader(): string | null;
|
|
16
|
+
connected_server_resources: R;
|
|
17
|
+
environment?: SchemaVaultsAppEnvironment;
|
|
18
|
+
debug?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export type CreateContextFn<R extends Base_SchemaVaults_tRPC_Resources> = ({ getAuthHeader, connected_server_resources, }: ICreateContextFnOptions<R>) => Promise<SchemaVaults_tRPC_Context<R>>;
|
|
21
|
+
export declare function createContext<R extends Base_SchemaVaults_tRPC_Resources = Base_SchemaVaults_tRPC_Resources>({ getAuthHeader, connected_server_resources, ...opts }: ICreateContextFnOptions<R>): Promise<SchemaVaults_tRPC_Context<R>>;
|
|
22
|
+
declare const _default: typeof createContext;
|
|
23
|
+
export default _default;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { loadAuthContext } from "./load_auth_context";
|
|
2
|
+
import { TRPCError } from "@trpc/server";
|
|
3
|
+
import { getAppEnvironment, getAuthServerUri, schemaVaultsAppEnvironmentSchema, } from "@schemavaults/app-definitions";
|
|
4
|
+
import { getSchemavaultsApiServerId, isUserInOrganization, loadJwksAccessPrivateKey, } from "@schemavaults/auth-server-sdk";
|
|
5
|
+
export async function createContext({ getAuthHeader, connected_server_resources, ...opts }) {
|
|
6
|
+
let environment;
|
|
7
|
+
if (typeof opts.environment === "string") {
|
|
8
|
+
environment = opts.environment;
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
environment = getAppEnvironment();
|
|
12
|
+
}
|
|
13
|
+
if (!schemaVaultsAppEnvironmentSchema.safeParse(environment).success) {
|
|
14
|
+
throw new Error("Invalid app environment!");
|
|
15
|
+
}
|
|
16
|
+
let debug;
|
|
17
|
+
if (typeof opts.debug === "boolean") {
|
|
18
|
+
debug = opts.debug;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
debug =
|
|
22
|
+
environment === "development" ||
|
|
23
|
+
environment === "test" ||
|
|
24
|
+
environment === "staging";
|
|
25
|
+
}
|
|
26
|
+
if (debug) {
|
|
27
|
+
console.log("[createContext] Creating tRPC context...");
|
|
28
|
+
}
|
|
29
|
+
let authHeader;
|
|
30
|
+
try {
|
|
31
|
+
authHeader = getAuthHeader();
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
throw new TRPCError({
|
|
35
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
36
|
+
message: "There was an error parsing authentication details from request headers",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
let jwt_audience;
|
|
40
|
+
try {
|
|
41
|
+
jwt_audience = getSchemavaultsApiServerId();
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
console.error("[createContext] Failed to load API server ID from environment variables: ", e);
|
|
45
|
+
throw new TRPCError({
|
|
46
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
47
|
+
message: "Internal server error",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
let jwks_access_private_key;
|
|
51
|
+
try {
|
|
52
|
+
jwks_access_private_key = await loadJwksAccessPrivateKey(process.env);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
console.error("[createContext] Failed to load JWKS access private key from environment variables: ", e);
|
|
56
|
+
throw new TRPCError({
|
|
57
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
58
|
+
message: "Internal server error",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
if (debug) {
|
|
63
|
+
console.log("[createContext] Attempting to load auth context...");
|
|
64
|
+
}
|
|
65
|
+
const authContextPromise = loadAuthContext(authHeader);
|
|
66
|
+
if (debug) {
|
|
67
|
+
console.log("[createContext] Attempting to load DB context...");
|
|
68
|
+
}
|
|
69
|
+
const authContext = await authContextPromise;
|
|
70
|
+
if (debug) {
|
|
71
|
+
console.log("[createContext] Loaded auth context: ", authContext);
|
|
72
|
+
}
|
|
73
|
+
const finalContext = {
|
|
74
|
+
user: authContext.user,
|
|
75
|
+
isUserInOrganization: async (user, org_id) => {
|
|
76
|
+
return await isUserInOrganization(getAuthServerUri(environment), jwt_audience, jwks_access_private_key, user["uid"], org_id);
|
|
77
|
+
},
|
|
78
|
+
connected_server_resources,
|
|
79
|
+
jwt_audience,
|
|
80
|
+
environment,
|
|
81
|
+
debug,
|
|
82
|
+
};
|
|
83
|
+
return finalContext;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error("[createContext] Error creating tRPC context", error);
|
|
87
|
+
throw new TRPCError({
|
|
88
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
89
|
+
message: "Failed to create tRPC context",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export default createContext;
|
|
94
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/context/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { decodeJWTsWithKeyManager } from "@schemavaults/auth-server-sdk";
|
|
2
|
+
export type AuthContext = Awaited<ReturnType<typeof decodeJWTsWithKeyManager>>;
|
|
3
|
+
import { type SchemaVaultsAppEnvironment } from "@schemavaults/app-definitions";
|
|
4
|
+
export declare function loadAuthContext(authHeader: string | null, environment?: SchemaVaultsAppEnvironment): Promise<AuthContext>;
|
|
5
|
+
export default loadAuthContext;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// load_auth_context.ts
|
|
2
|
+
import { getSchemavaultsApiServerId, RemoteJwtKeyManager, decodeJWTsWithKeyManager, } from "@schemavaults/auth-server-sdk";
|
|
3
|
+
import { getAppEnvironment, getAuthServerUri, } from "@schemavaults/app-definitions";
|
|
4
|
+
const bearerPrefix = "Bearer ";
|
|
5
|
+
export async function loadAuthContext(authHeader, environment = getAppEnvironment()) {
|
|
6
|
+
if (!authHeader) {
|
|
7
|
+
if (environment === "development") {
|
|
8
|
+
console.log("[loadAuthContext] No authorization header found");
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
user: null,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const token = authHeader.startsWith(bearerPrefix)
|
|
15
|
+
? authHeader.slice(bearerPrefix.length)
|
|
16
|
+
: authHeader;
|
|
17
|
+
// token parsed from header now
|
|
18
|
+
if (!token) {
|
|
19
|
+
if (environment === "development") {
|
|
20
|
+
console.log("[loadAuthContext] No auth token found");
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
user: null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const audience = getSchemavaultsApiServerId();
|
|
27
|
+
if (typeof audience !== "string")
|
|
28
|
+
throw new Error("Environment variable 'SCHEMAVAULTS_API_SERVER_ID' not set or passed to auth context loader");
|
|
29
|
+
const remote_jwt_key_manager = new RemoteJwtKeyManager({
|
|
30
|
+
auth_server_uri: getAuthServerUri(),
|
|
31
|
+
debug: environment === "development",
|
|
32
|
+
});
|
|
33
|
+
// Proceed to validate the token
|
|
34
|
+
try {
|
|
35
|
+
if (environment === "development") {
|
|
36
|
+
console.log(`[loadAuthContext] Attempting to verify auth token '${token}'...`);
|
|
37
|
+
}
|
|
38
|
+
const result = await decodeJWTsWithKeyManager(remote_jwt_key_manager, [
|
|
39
|
+
{
|
|
40
|
+
token,
|
|
41
|
+
sourceHint: "tRPC Authorization Bearer Header",
|
|
42
|
+
type: "access",
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
if (environment === "development" && result.user) {
|
|
46
|
+
console.log(`[loadAuthContext] Successfully verified auth token for user '${result.user.email}'`);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (environment === "development") {
|
|
52
|
+
console.warn("[loadAuthContext] Could not verify the token: ", error);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
user: null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export default loadAuthContext;
|
|
60
|
+
//# sourceMappingURL=load_auth_context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load_auth_context.js","sourceRoot":"","sources":["../../src/context/load_auth_context.ts"],"names":[],"mappings":"AAAA,uBAAuB;AAEvB,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,wBAAwB,GAEzB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EAEL,iBAAiB,EACjB,gBAAgB,GAEjB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,YAAY,GAAc,SAAkB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,UAAyB,EACzB,cAA0C,iBAAiB,EAAE;IAE7D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QACjE,CAAC;QACD,OAAO;YACL,IAAI,EAAE,IAAI;SACX,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAuB,UAAU,CAAC,UAAU,CAAC,YAAY,CAAC;QACnE,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC;QACvC,CAAC,CAAC,UAAU,CAAC;IACf,+BAA+B;IAE/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QACvD,CAAC;QACD,OAAO;YACL,IAAI,EAAE,IAAI;SACX,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAgB,0BAA0B,EAAE,CAAC;IAC3D,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAC9B,MAAM,IAAI,KAAK,CACb,4FAA4F,CAC7F,CAAC;IAEJ,MAAM,sBAAsB,GAAmB,IAAI,mBAAmB,CAAC;QACrE,eAAe,EAAE,gBAAgB,EAAE;QACnC,KAAK,EAAE,WAAW,KAAK,aAAa;KACrC,CAAC,CAAC;IAEH,gCAAgC;IAChC,IAAI,CAAC;QACH,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CACT,sDAAsD,KAAK,MAAM,CAClE,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,sBAAsB,EAAE;YACpE;gBACE,KAAK;gBACL,UAAU,EAAE,kCAAkC;gBAC9C,IAAI,EAAE,QAAQ;aACf;SACF,CAAC,CAAC;QAEH,IAAI,WAAW,KAAK,aAAa,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YACjD,OAAO,CAAC,GAAG,CACT,gEAAgE,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CACrF,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,gDAAgD,EAAE,KAAK,CAAC,CAAC;QACxE,CAAC;QACD,OAAO;YACL,IAAI,EAAE,IAAI;SACX,CAAC;IACJ,CAAC;AACH,CAAC;AAED,eAAe,eAAe,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SchemaVaultsAppEnvironment } from "@schemavaults/app-definitions";
|
|
2
|
+
declare global {
|
|
3
|
+
namespace NodeJS {
|
|
4
|
+
interface ProcessEnv {
|
|
5
|
+
NODE_ENV: SchemaVaultsAppEnvironment;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export { SchemaVaults_tRPC_Backend } from "./schemavaults-trpc-backend";
|
|
10
|
+
export type { SchemaVaults_tRPC_Context, ICreateContextFnOptions, CreateContextFn, Base_SchemaVaults_tRPC_Resources, } from "./context";
|
|
11
|
+
export type { SchemaVaults_tRPC_Runtime } from "./schemavaults-trpc-runtime";
|
|
12
|
+
export type { SchemaVaults_tRPC_Procedures } from "./procedures";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Base_SchemaVaults_tRPC_Resources } from "../context";
|
|
2
|
+
import type { SchemaVaults_tRPC_Runtime } from "../schemavaults-trpc-runtime";
|
|
3
|
+
import type { UserData } from "@schemavaults/auth-server-sdk";
|
|
4
|
+
export declare function createProcedures<R extends Base_SchemaVaults_tRPC_Resources>(middleware: SchemaVaults_tRPC_Runtime<R>["middleware"], procedure: SchemaVaults_tRPC_Runtime<R>["procedure"]): {
|
|
5
|
+
readonly public: import("@trpc/server").TRPCProcedureBuilder<import("../context").SchemaVaults_tRPC_Context<R>, object, object, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
6
|
+
readonly authorized: import("@trpc/server").TRPCProcedureBuilder<import("../context").SchemaVaults_tRPC_Context<R>, object, {
|
|
7
|
+
user: {
|
|
8
|
+
uid: string;
|
|
9
|
+
sub: string;
|
|
10
|
+
email: string;
|
|
11
|
+
created_at: number;
|
|
12
|
+
email_verified?: boolean | undefined;
|
|
13
|
+
admin?: boolean | undefined;
|
|
14
|
+
phone_number?: string | undefined;
|
|
15
|
+
phone_verified?: boolean | undefined;
|
|
16
|
+
disabled?: boolean | undefined;
|
|
17
|
+
invite_code?: string | undefined;
|
|
18
|
+
};
|
|
19
|
+
connected_server_resources: R;
|
|
20
|
+
environment: "development" | "staging" | "test" | "production";
|
|
21
|
+
debug: boolean;
|
|
22
|
+
isUserInOrganization: (user: UserData, org_id: import("@schemavaults/auth-common").OrganizationID) => Promise<import("@schemavaults/auth-common").OrganizationMembershipRoleType | false>;
|
|
23
|
+
jwt_audience: string;
|
|
24
|
+
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
25
|
+
readonly admin: import("@trpc/server").TRPCProcedureBuilder<import("../context").SchemaVaults_tRPC_Context<R>, object, {
|
|
26
|
+
user: {
|
|
27
|
+
uid: string;
|
|
28
|
+
sub: string;
|
|
29
|
+
email: string;
|
|
30
|
+
created_at: number;
|
|
31
|
+
email_verified?: boolean | undefined;
|
|
32
|
+
admin?: boolean | undefined;
|
|
33
|
+
phone_number?: string | undefined;
|
|
34
|
+
phone_verified?: boolean | undefined;
|
|
35
|
+
disabled?: boolean | undefined;
|
|
36
|
+
invite_code?: string | undefined;
|
|
37
|
+
} | null;
|
|
38
|
+
connected_server_resources: R;
|
|
39
|
+
environment: "development" | "staging" | "test" | "production";
|
|
40
|
+
debug: boolean;
|
|
41
|
+
isUserInOrganization: (user: UserData, org_id: import("@schemavaults/auth-common").OrganizationID) => Promise<import("@schemavaults/auth-common").OrganizationMembershipRoleType | false>;
|
|
42
|
+
jwt_audience: string;
|
|
43
|
+
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
44
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TRPCError } from "@trpc/server";
|
|
2
|
+
export function createProcedures(middleware, procedure) {
|
|
3
|
+
// "Base Procedure" -- most procedures should be behind authentication middleware (instead of importing here)
|
|
4
|
+
const publicProcedure = procedure;
|
|
5
|
+
const isAuthed = middleware((opts) => {
|
|
6
|
+
const { ctx } = opts;
|
|
7
|
+
if (!ctx) {
|
|
8
|
+
throw new TRPCError({
|
|
9
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
10
|
+
message: "No context found",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
if (!ctx.user) {
|
|
14
|
+
throw new TRPCError({
|
|
15
|
+
code: "UNAUTHORIZED",
|
|
16
|
+
message: "You must be logged in to perform this action",
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const user = ctx.user;
|
|
20
|
+
return opts.next({
|
|
21
|
+
ctx: {
|
|
22
|
+
...ctx,
|
|
23
|
+
user,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
const authorizedProcedure = publicProcedure.use(isAuthed);
|
|
28
|
+
const isAdmin = middleware((opts) => {
|
|
29
|
+
if (!opts.ctx.user?.admin) {
|
|
30
|
+
throw new TRPCError({
|
|
31
|
+
code: "FORBIDDEN",
|
|
32
|
+
message: "You must be an admin user to perform this action",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return opts.next(opts);
|
|
36
|
+
});
|
|
37
|
+
const adminProcedure = authorizedProcedure.use(isAdmin);
|
|
38
|
+
return {
|
|
39
|
+
public: publicProcedure,
|
|
40
|
+
authorized: authorizedProcedure,
|
|
41
|
+
admin: adminProcedure,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=create_procedures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create_procedures.js","sourceRoot":"","sources":["../../src/procedures/create_procedures.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,UAAU,gBAAgB,CAC9B,UAAsD,EACtD,SAAoD;IAEpD,6GAA6G;IAC7G,MAAM,eAAe,GAA8C,SAAS,CAAC;IAE7E,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE;QACnC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,uBAAuB;gBAC7B,OAAO,EAAE,kBAAkB;aAC5B,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACd,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,8CAA8C;aACxD,CAAC,CAAC;QACL,CAAC;QAED,MAAM,IAAI,GAAa,GAAG,CAAC,IAAI,CAAC;QAEhC,OAAO,IAAI,CAAC,IAAI,CAAC;YACf,GAAG,EAAE;gBACH,GAAG,GAAG;gBACN,IAAI;aACL;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,mBAAmB,GAAG,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAE1D,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE;QAClC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;YAC1B,MAAM,IAAI,SAAS,CAAC;gBAClB,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,kDAAkD;aAC5D,CAAC,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAExD,OAAO;QACL,MAAM,EAAE,eAAe;QACvB,UAAU,EAAE,mBAAmB;QAC/B,KAAK,EAAE,cAAc;KACb,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/procedures/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemavaults-trpc-procedures.js","sourceRoot":"","sources":["../../src/procedures/schemavaults-trpc-procedures.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { lazy } from "@trpc/server";
|
|
2
|
+
import { type ICreateContextFnOptions, type Base_SchemaVaults_tRPC_Resources, type SchemaVaults_tRPC_Context } from "./context";
|
|
3
|
+
import type { SchemaVaults_tRPC_Runtime } from "./schemavaults-trpc-runtime";
|
|
4
|
+
import { createProcedures, type SchemaVaults_tRPC_Procedures } from "./procedures";
|
|
5
|
+
export declare abstract class SchemaVaults_tRPC_Backend<R extends Base_SchemaVaults_tRPC_Resources> {
|
|
6
|
+
protected _trpc: SchemaVaults_tRPC_Runtime<R>;
|
|
7
|
+
protected _trpc_procedures: ReturnType<typeof createProcedures<R>>;
|
|
8
|
+
constructor();
|
|
9
|
+
abstract get trpc(): SchemaVaults_tRPC_Runtime<R>;
|
|
10
|
+
abstract get procedures(): SchemaVaults_tRPC_Procedures<R>;
|
|
11
|
+
get router(): SchemaVaults_tRPC_Runtime<R>["router"];
|
|
12
|
+
/**
|
|
13
|
+
* @name lazy
|
|
14
|
+
* @description Lazy load a tRPC backend router
|
|
15
|
+
*/
|
|
16
|
+
get lazy(): typeof lazy;
|
|
17
|
+
/**
|
|
18
|
+
* @name createContext
|
|
19
|
+
* @description Initialize tRPC context accessible to procedures
|
|
20
|
+
*/
|
|
21
|
+
createContext(opts: ICreateContextFnOptions<R>): Promise<SchemaVaults_tRPC_Context<R>>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// schemavaults-trpc-backend.ts
|
|
2
|
+
import { initTRPC, lazy } from "@trpc/server";
|
|
3
|
+
import { createContext, } from "./context";
|
|
4
|
+
import { createProcedures, } from "./procedures";
|
|
5
|
+
export class SchemaVaults_tRPC_Backend {
|
|
6
|
+
_trpc;
|
|
7
|
+
_trpc_procedures;
|
|
8
|
+
constructor() {
|
|
9
|
+
// tRPC should only be initialized ONCE per server, so we initialize it here.
|
|
10
|
+
this._trpc = initTRPC.context().create();
|
|
11
|
+
this._trpc_procedures = createProcedures(this._trpc.middleware, this._trpc.procedure);
|
|
12
|
+
}
|
|
13
|
+
get router() {
|
|
14
|
+
return this.trpc.router;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* @name lazy
|
|
18
|
+
* @description Lazy load a tRPC backend router
|
|
19
|
+
*/
|
|
20
|
+
get lazy() {
|
|
21
|
+
return lazy;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @name createContext
|
|
25
|
+
* @description Initialize tRPC context accessible to procedures
|
|
26
|
+
*/
|
|
27
|
+
async createContext(opts) {
|
|
28
|
+
return await createContext(opts);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=schemavaults-trpc-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemavaults-trpc-backend.js","sourceRoot":"","sources":["../src/schemavaults-trpc-backend.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EACL,aAAa,GAId,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,gBAAgB,GAEjB,MAAM,cAAc,CAAC;AAEtB,MAAM,OAAgB,yBAAyB;IAGnC,KAAK,CAA+B;IACpC,gBAAgB,CAAyC;IAEnE;QACE,6EAA6E;QAC7E,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAgC,CAAC,MAAM,EAAE,CAAC;QACvE,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CACtC,IAAI,CAAC,KAAK,CAAC,UAAU,EACrB,IAAI,CAAC,KAAK,CAAC,SAAS,CACrB,CAAC;IACJ,CAAC;IAMD,IAAW,MAAM;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACH,IAAW,IAAI;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,aAAa,CACxB,IAAgC;QAEhC,OAAO,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { initTRPC } from "@trpc/server";
|
|
2
|
+
import type { Base_SchemaVaults_tRPC_Resources, SchemaVaults_tRPC_Context } from "./context";
|
|
3
|
+
type BuilderContext<R extends Base_SchemaVaults_tRPC_Resources> = ReturnType<typeof initTRPC.context<SchemaVaults_tRPC_Context<R>>>;
|
|
4
|
+
export type SchemaVaults_tRPC_Runtime<R extends Base_SchemaVaults_tRPC_Resources> = ReturnType<BuilderContext<R>["create"]>;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemavaults-trpc-runtime.js","sourceRoot":"","sources":["../src/schemavaults-trpc-runtime.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@schemavaults/trpc-backend-init",
|
|
3
|
+
"description": "tRPC Server-side Router Factory",
|
|
4
|
+
"version": "0.8.1",
|
|
5
|
+
"private": false,
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/schemavaults/trpc-backend-init.git"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"typescript": "5.9.3",
|
|
14
|
+
"bun-types": "1.3.11",
|
|
15
|
+
"tsc-alias": "1.8.16"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@trpc/server": "11.16.0",
|
|
19
|
+
"@schemavaults/auth-server-sdk": "0.22.18",
|
|
20
|
+
"@schemavaults/app-definitions": "0.6.23"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc --project ./tsconfig.json && tsc-alias --project ./tsconfig.json",
|
|
24
|
+
"postbuild": "bun run cleanup",
|
|
25
|
+
"cleanup:compiled_tests_in_dist_directory": "find ./dist -type f \\( -name \"*.test.js\" -o -name \"*.test.js.map\" -o -name \"*.test.d.ts\" \\) -delete",
|
|
26
|
+
"cleanup": "bun run cleanup:compiled_tests_in_dist_directory",
|
|
27
|
+
"test": "SCHEMAVAULTS_APP_ENVIRONMENT=test NODE_ENV=test bun test"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"main": "dist/index.js",
|
|
33
|
+
"types": "dist/index.d.ts",
|
|
34
|
+
"module": "dist/index.js",
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"packageManager": "bun@1.3.11"
|
|
39
|
+
}
|