@goscribe/server 1.0.1 → 1.0.3

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/dist/context.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { CreateExpressContextOptions } from "@trpc/server/adapters/express";
2
2
  export declare function createContext({ req, res }: CreateExpressContextOptions): Promise<{
3
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
3
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
4
4
  session: any;
5
5
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
6
6
  res: import("express").Response<any, Record<string, any>>;
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/lib/file.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ "use strict";
@@ -1,3 +1,3 @@
1
- import { PrismaClient } from "../generated/prisma";
2
- export declare const prisma: PrismaClient<import("../generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
1
+ import { PrismaClient } from "@prisma/client";
2
+ export declare const prisma: PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
3
3
  //# sourceMappingURL=prisma.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/lib/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAInD,eAAO,MAAM,MAAM,2IAIf,CAAC"}
1
+ {"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/lib/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI9C,eAAO,MAAM,MAAM,gIAIf,CAAC"}
@@ -1,10 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.prisma = void 0;
4
- const prisma_1 = require("../generated/prisma");
4
+ const client_1 = require("@prisma/client");
5
5
  const globalForPrisma = globalThis;
6
6
  exports.prisma = globalForPrisma.prisma ??
7
- new prisma_1.PrismaClient({
7
+ new client_1.PrismaClient({
8
8
  // log: ["query"], // enable if you want
9
9
  });
10
10
  if (process.env.NODE_ENV !== "production")
@@ -0,0 +1,4 @@
1
+ import { Storage } from "@google-cloud/storage";
2
+ export declare const storage: Storage;
3
+ export declare const bucket: import("@google-cloud/storage").Bucket;
4
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/lib/storage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,eAAO,MAAM,OAAO,SAMlB,CAAC;AAEH,eAAO,MAAM,MAAM,wCAA0C,CAAC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.bucket = exports.storage = void 0;
4
+ // src/server/lib/gcs.ts
5
+ const storage_1 = require("@google-cloud/storage");
6
+ exports.storage = new storage_1.Storage({
7
+ projectId: process.env.GCP_PROJECT_ID,
8
+ credentials: {
9
+ client_email: process.env.GCP_CLIENT_EMAIL,
10
+ private_key: process.env.GCP_PRIVATE_KEY?.replace(/\\n/g, "\n"),
11
+ },
12
+ });
13
+ exports.bucket = exports.storage.bucket(process.env.GCP_BUCKET);
@@ -1,7 +1,7 @@
1
1
  import { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
2
2
  export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
3
3
  ctx: {
4
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
4
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
5
5
  session: any;
6
6
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
7
7
  res: import("express").Response<any, Record<string, any>>;
@@ -12,7 +12,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
12
12
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
13
13
  auth: import("@trpc/server").TRPCBuiltRouter<{
14
14
  ctx: {
15
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
15
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
16
16
  session: any;
17
17
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
18
18
  res: import("express").Response<any, Record<string, any>>;
@@ -37,7 +37,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
37
37
  }>>;
38
38
  workspace: import("@trpc/server").TRPCBuiltRouter<{
39
39
  ctx: {
40
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
40
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
41
41
  session: any;
42
42
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
43
43
  res: import("express").Response<any, Record<string, any>>;
@@ -48,47 +48,93 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
48
48
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
49
49
  list: import("@trpc/server").TRPCQueryProcedure<{
50
50
  input: void;
51
- output: void;
51
+ output: {
52
+ id: string;
53
+ createdAt: Date;
54
+ updatedAt: Date;
55
+ ownerId: string;
56
+ title: string;
57
+ description: string | null;
58
+ folderId: string | null;
59
+ }[];
52
60
  meta: object;
53
61
  }>;
54
62
  create: import("@trpc/server").TRPCMutationProcedure<{
55
- input: Record<string, never>;
56
- output: void;
63
+ input: {
64
+ name: string;
65
+ description?: string | undefined;
66
+ };
67
+ output: {
68
+ id: string;
69
+ createdAt: Date;
70
+ updatedAt: Date;
71
+ ownerId: string;
72
+ title: string;
73
+ description: string | null;
74
+ folderId: string | null;
75
+ };
57
76
  meta: object;
58
77
  }>;
59
78
  get: import("@trpc/server").TRPCQueryProcedure<{
60
79
  input: {
61
80
  id: string;
62
81
  };
63
- output: void;
82
+ output: {
83
+ id: string;
84
+ createdAt: Date;
85
+ updatedAt: Date;
86
+ ownerId: string;
87
+ title: string;
88
+ description: string | null;
89
+ folderId: string | null;
90
+ } | null;
64
91
  meta: object;
65
92
  }>;
66
93
  update: import("@trpc/server").TRPCMutationProcedure<{
67
94
  input: {
68
95
  id: string;
96
+ name?: string | undefined;
97
+ description?: string | undefined;
98
+ };
99
+ output: {
100
+ id: string;
101
+ createdAt: Date;
102
+ updatedAt: Date;
103
+ ownerId: string;
104
+ title: string;
105
+ description: string | null;
106
+ folderId: string | null;
69
107
  };
70
- output: void;
71
108
  meta: object;
72
109
  }>;
73
110
  delete: import("@trpc/server").TRPCMutationProcedure<{
74
111
  input: {
75
112
  id: string;
76
113
  };
77
- output: void;
114
+ output: boolean;
78
115
  meta: object;
79
116
  }>;
80
- upload: import("@trpc/server").TRPCMutationProcedure<{
117
+ uploadFiles: import("@trpc/server").TRPCMutationProcedure<{
81
118
  input: {
82
- file: string;
119
+ id: string;
120
+ files: {
121
+ filename: string;
122
+ contentType: string;
123
+ size: number;
124
+ }[];
83
125
  };
84
- output: void;
126
+ output: {
127
+ fileId: string;
128
+ uploadUrl: string;
129
+ }[];
85
130
  meta: object;
86
131
  }>;
87
- deleteFile: import("@trpc/server").TRPCMutationProcedure<{
132
+ deleteFiles: import("@trpc/server").TRPCMutationProcedure<{
88
133
  input: {
89
- fileId: string;
134
+ fileId: string[];
135
+ id: string;
90
136
  };
91
- output: void;
137
+ output: import(".prisma/client").Prisma.BatchPayload;
92
138
  meta: object;
93
139
  }>;
94
140
  }>>;
@@ -1 +1 @@
1
- {"version":3,"file":"_app.d.ts","sourceRoot":"","sources":["../../src/routers/_app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAKrE,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAGpB,CAAC;AAGH,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC;AACzC,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"_app.d.ts","sourceRoot":"","sources":["../../src/routers/_app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAKrE,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAGpB,CAAC;AAGH,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC;AACzC,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  export declare const auth: import("@trpc/server").TRPCBuiltRouter<{
2
2
  ctx: {
3
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
3
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
4
4
  session: any;
5
5
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
6
6
  res: import("express").Response<any, Record<string, any>>;
@@ -1,6 +1,6 @@
1
1
  export declare const workspace: import("@trpc/server").TRPCBuiltRouter<{
2
2
  ctx: {
3
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
3
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
4
4
  session: any;
5
5
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
6
6
  res: import("express").Response<any, Record<string, any>>;
@@ -11,47 +11,93 @@ export declare const workspace: import("@trpc/server").TRPCBuiltRouter<{
11
11
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
12
12
  list: import("@trpc/server").TRPCQueryProcedure<{
13
13
  input: void;
14
- output: void;
14
+ output: {
15
+ id: string;
16
+ createdAt: Date;
17
+ updatedAt: Date;
18
+ ownerId: string;
19
+ title: string;
20
+ description: string | null;
21
+ folderId: string | null;
22
+ }[];
15
23
  meta: object;
16
24
  }>;
17
25
  create: import("@trpc/server").TRPCMutationProcedure<{
18
- input: Record<string, never>;
19
- output: void;
26
+ input: {
27
+ name: string;
28
+ description?: string | undefined;
29
+ };
30
+ output: {
31
+ id: string;
32
+ createdAt: Date;
33
+ updatedAt: Date;
34
+ ownerId: string;
35
+ title: string;
36
+ description: string | null;
37
+ folderId: string | null;
38
+ };
20
39
  meta: object;
21
40
  }>;
22
41
  get: import("@trpc/server").TRPCQueryProcedure<{
23
42
  input: {
24
43
  id: string;
25
44
  };
26
- output: void;
45
+ output: {
46
+ id: string;
47
+ createdAt: Date;
48
+ updatedAt: Date;
49
+ ownerId: string;
50
+ title: string;
51
+ description: string | null;
52
+ folderId: string | null;
53
+ } | null;
27
54
  meta: object;
28
55
  }>;
29
56
  update: import("@trpc/server").TRPCMutationProcedure<{
30
57
  input: {
31
58
  id: string;
59
+ name?: string | undefined;
60
+ description?: string | undefined;
61
+ };
62
+ output: {
63
+ id: string;
64
+ createdAt: Date;
65
+ updatedAt: Date;
66
+ ownerId: string;
67
+ title: string;
68
+ description: string | null;
69
+ folderId: string | null;
32
70
  };
33
- output: void;
34
71
  meta: object;
35
72
  }>;
36
73
  delete: import("@trpc/server").TRPCMutationProcedure<{
37
74
  input: {
38
75
  id: string;
39
76
  };
40
- output: void;
77
+ output: boolean;
41
78
  meta: object;
42
79
  }>;
43
- upload: import("@trpc/server").TRPCMutationProcedure<{
80
+ uploadFiles: import("@trpc/server").TRPCMutationProcedure<{
44
81
  input: {
45
- file: string;
82
+ id: string;
83
+ files: {
84
+ filename: string;
85
+ contentType: string;
86
+ size: number;
87
+ }[];
46
88
  };
47
- output: void;
89
+ output: {
90
+ fileId: string;
91
+ uploadUrl: string;
92
+ }[];
48
93
  meta: object;
49
94
  }>;
50
- deleteFile: import("@trpc/server").TRPCMutationProcedure<{
95
+ deleteFiles: import("@trpc/server").TRPCMutationProcedure<{
51
96
  input: {
52
- fileId: string;
97
+ fileId: string[];
98
+ id: string;
53
99
  };
54
- output: void;
100
+ output: import(".prisma/client").Prisma.BatchPayload;
55
101
  meta: object;
56
102
  }>;
57
103
  }>>;
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/routers/workspace.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CpB,CAAC"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/routers/workspace.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuJpB,CAAC"}
@@ -3,43 +3,145 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.workspace = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const trpc_1 = require("../trpc");
6
+ const storage_1 = require("src/lib/storage");
6
7
  exports.workspace = (0, trpc_1.router)({
7
8
  // Mutation with Zod input
8
9
  list: trpc_1.publicProcedure
9
10
  .query(async ({ ctx, input }) => {
11
+ const workspaces = await ctx.db.workspace.findMany({
12
+ where: {
13
+ ownerId: ctx.session?.user.id,
14
+ },
15
+ });
16
+ return workspaces;
10
17
  }),
11
18
  create: trpc_1.publicProcedure
12
- .input(zod_1.z.object({}))
13
- .mutation(({ input }) => {
19
+ .input(zod_1.z.object({
20
+ name: zod_1.z.string().min(1).max(100),
21
+ description: zod_1.z.string().max(500).optional(),
22
+ }))
23
+ .mutation(({ ctx, input }) => {
24
+ return ctx.db.workspace.create({
25
+ data: {
26
+ title: input.name,
27
+ description: input.description,
28
+ ownerId: ctx.session?.user.id,
29
+ },
30
+ });
14
31
  }),
15
32
  get: trpc_1.publicProcedure
16
33
  .input(zod_1.z.object({
17
34
  id: zod_1.z.string().uuid(),
18
35
  }))
19
- .query(({ input }) => {
36
+ .query(({ ctx, input }) => {
37
+ return ctx.db.workspace.findUnique({
38
+ where: {
39
+ id: input.id,
40
+ },
41
+ });
20
42
  }),
21
43
  update: trpc_1.publicProcedure
22
44
  .input(zod_1.z.object({
23
45
  id: zod_1.z.string().uuid(),
46
+ name: zod_1.z.string().min(1).max(100).optional(),
47
+ description: zod_1.z.string().max(500).optional(),
24
48
  }))
25
- .mutation(({ input }) => {
49
+ .mutation(({ ctx, input }) => {
50
+ return ctx.db.workspace.update({
51
+ where: {
52
+ id: input.id,
53
+ },
54
+ data: {
55
+ title: input.name,
56
+ description: input.description,
57
+ },
58
+ });
26
59
  }),
27
60
  delete: trpc_1.publicProcedure
28
61
  .input(zod_1.z.object({
29
62
  id: zod_1.z.string().uuid(),
30
63
  }))
31
- .mutation(({ input }) => {
64
+ .mutation(({ ctx, input }) => {
65
+ ctx.db.workspace.delete({
66
+ where: {
67
+ id: input.id,
68
+ },
69
+ });
70
+ return true;
32
71
  }),
33
- upload: trpc_1.publicProcedure
72
+ uploadFiles: trpc_1.publicProcedure
34
73
  .input(zod_1.z.object({
35
- file: zod_1.z.string(),
74
+ id: zod_1.z.string().uuid(),
75
+ files: zod_1.z.array(zod_1.z.object({
76
+ filename: zod_1.z.string().min(1).max(255),
77
+ contentType: zod_1.z.string().min(1).max(100),
78
+ size: zod_1.z.number().min(1), // size in bytes
79
+ })),
36
80
  }))
37
- .mutation(({ input }) => {
81
+ .mutation(async ({ ctx, input }) => {
82
+ const results = [];
83
+ for (const file of input.files) {
84
+ // 1. Insert into DB
85
+ const record = await ctx.db.fileAsset.create({
86
+ data: {
87
+ userId: ctx.session.user.id,
88
+ name: file.filename,
89
+ mimeType: file.contentType,
90
+ size: file.size,
91
+ workspaceId: input.id,
92
+ },
93
+ });
94
+ // 2. Generate signed URL for direct upload
95
+ const [url] = await storage_1.bucket
96
+ .file(`${ctx.session.user.id}/${record.id}-${file.filename}`)
97
+ .getSignedUrl({
98
+ action: "write",
99
+ expires: Date.now() + 5 * 60 * 1000, // 5 min
100
+ contentType: file.contentType,
101
+ });
102
+ // 3. Update record with bucket info
103
+ await ctx.db.fileAsset.update({
104
+ where: { id: record.id },
105
+ data: {
106
+ bucket: storage_1.bucket.name,
107
+ objectKey: `${ctx.session.user.id}/${record.id}-${file.filename}`,
108
+ },
109
+ });
110
+ results.push({
111
+ fileId: record.id,
112
+ uploadUrl: url,
113
+ });
114
+ }
115
+ return results;
38
116
  }),
39
- deleteFile: trpc_1.publicProcedure
117
+ deleteFiles: trpc_1.publicProcedure
40
118
  .input(zod_1.z.object({
41
- fileId: zod_1.z.string().uuid(),
119
+ fileId: zod_1.z.array(zod_1.z.string().uuid()),
120
+ id: zod_1.z.string().uuid(),
42
121
  }))
43
- .mutation(({ input }) => {
122
+ .mutation(({ ctx, input }) => {
123
+ const files = ctx.db.fileAsset.findMany({
124
+ where: {
125
+ id: { in: input.fileId },
126
+ workspaceId: input.id,
127
+ },
128
+ });
129
+ // Delete from GCS
130
+ files.then((fileRecords) => {
131
+ fileRecords.forEach((file) => {
132
+ if (file.bucket && file.objectKey) {
133
+ const gcsFile = storage_1.bucket.file(file.objectKey);
134
+ gcsFile.delete({ ignoreNotFound: true }).catch((err) => {
135
+ console.error(`Error deleting file ${file.objectKey} from bucket ${file.bucket}:`, err);
136
+ });
137
+ }
138
+ });
139
+ });
140
+ return ctx.db.fileAsset.deleteMany({
141
+ where: {
142
+ id: { in: input.fileId },
143
+ workspaceId: input.id,
144
+ },
145
+ });
44
146
  }),
45
147
  });
package/dist/server.js CHANGED
@@ -50,7 +50,10 @@ async function main() {
50
50
  const app = (0, express_1.default)();
51
51
  // Middlewares
52
52
  app.use((0, helmet_1.default)());
53
- app.use((0, cors_1.default)({ origin: true, credentials: true }));
53
+ app.use((0, cors_1.default)({
54
+ origin: "http://localhost:3000", // your Next.js dev URL
55
+ credentials: true, // allow cookies
56
+ }));
54
57
  app.use((0, morgan_1.default)('dev'));
55
58
  app.use((0, compression_1.default)());
56
59
  app.use(express_1.default.json());
package/dist/trpc.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export declare const router: import("@trpc/server").TRPCRouterBuilder<{
2
2
  ctx: {
3
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
3
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
4
4
  session: any;
5
5
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
6
6
  res: import("express").Response<any, Record<string, any>>;
@@ -10,32 +10,32 @@ export declare const router: import("@trpc/server").TRPCRouterBuilder<{
10
10
  transformer: true;
11
11
  }>;
12
12
  export declare const middleware: <$ContextOverrides>(fn: import("@trpc/server").TRPCMiddlewareFunction<{
13
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
13
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
14
14
  session: any;
15
15
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
16
16
  res: import("express").Response<any, Record<string, any>>;
17
17
  }, object, object, $ContextOverrides, unknown>) => import("@trpc/server").TRPCMiddlewareBuilder<{
18
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
18
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
19
19
  session: any;
20
20
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
21
21
  res: import("express").Response<any, Record<string, any>>;
22
22
  }, object, $ContextOverrides, unknown>;
23
23
  export declare const publicProcedure: import("@trpc/server").TRPCProcedureBuilder<{
24
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
24
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
25
25
  session: any;
26
26
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
27
27
  res: import("express").Response<any, Record<string, any>>;
28
28
  }, object, object, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
29
29
  /** Exported authed procedure */
30
30
  export declare const authedProcedure: import("@trpc/server").TRPCProcedureBuilder<{
31
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
31
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
32
32
  session: any;
33
33
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
34
34
  res: import("express").Response<any, Record<string, any>>;
35
35
  }, object, {
36
36
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
37
37
  res: import("express").Response<any, Record<string, any>>;
38
- db: import("src/generated/prisma").PrismaClient<import("src/generated/prisma").Prisma.PrismaClientOptions, never, import("src/generated/prisma/runtime/library").DefaultArgs>;
38
+ db: import(".prisma/client").PrismaClient<import(".prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
39
39
  session: any;
40
40
  }, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
41
41
  //# sourceMappingURL=trpc.d.ts.map
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@goscribe/server",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "type": "module",
7
8
  "exports": {
8
9
  ".": {
9
10
  "types": "./dist/index.d.ts",
@@ -12,14 +13,15 @@
12
13
  },
13
14
  "scripts": {
14
15
  "dev": "ts-node-dev --respawn --transpile-only src/server.ts",
15
- "build": "tsc -p .",
16
- "start": "node dist/server.js"
16
+ "build": "npx prisma generate && tsc -p .",
17
+ "start": "node --experimental-specifier-resolution=node dist/server.js"
17
18
  },
18
19
  "author": "",
19
20
  "license": "MIT",
20
21
  "dependencies": {
21
22
  "@auth/express": "^0.11.0",
22
23
  "@auth/prisma-adapter": "^2.10.0",
24
+ "@google-cloud/storage": "^7.17.0",
23
25
  "@prisma/client": "^6.14.0",
24
26
  "@trpc/server": "^11.5.0",
25
27
  "bcryptjs": "^3.0.2",
@@ -40,6 +42,9 @@
40
42
  "@types/node": "^24.3.0",
41
43
  "ts-node": "^10.9.2",
42
44
  "ts-node-dev": "^2.0.0",
43
- "typescript": "^5.9.2"
45
+ "tsc-alias": "^1.8.16",
46
+ "tsc-esm-fix": "^3.1.2",
47
+ "typescript": "^5.9.2",
48
+ "typescript-transform-paths": "^3.5.5"
44
49
  }
45
50
  }
@@ -1,7 +1,6 @@
1
1
 
2
2
  generator client {
3
3
  provider = "prisma-client-js"
4
- output = "../src/generated/prisma"
5
4
  }
6
5
 
7
6
  datasource db {
@@ -140,8 +139,8 @@ model FileAsset {
140
139
  name String
141
140
  mimeType String
142
141
  size Int
143
- bucket String
144
- objectKey String
142
+ bucket String?
143
+ objectKey String?
145
144
  url String? // optional if serving via signed GET per-view
146
145
  checksum String? // optional server-side integrity
147
146
 
package/src/context.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/server/trpc/context.ts
2
2
  import type { CreateExpressContextOptions } from "@trpc/server/adapters/express";
3
- import { prisma } from "./lib/prisma";
3
+ import { prisma } from "./lib/prisma.js";
4
4
 
5
5
  export async function createContext({ req, res }: CreateExpressContextOptions) {
6
6
  const session = (req as any).auth ?? null;
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export type { AppRouter } from "./routers/_app";
2
- export type { RouterInputs } from "./routers/_app";
3
- export type { RouterOutputs } from "./routers/_app";
1
+ export type { AppRouter } from "./routers/_app.js";
2
+ export type { RouterInputs } from "./routers/_app.js";
3
+ export type { RouterOutputs } from "./routers/_app.js";
package/src/lib/auth.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/server/auth.ts
2
2
  import { ExpressAuth } from "@auth/express";
3
3
  import { PrismaAdapter } from "@auth/prisma-adapter";
4
- import { prisma } from "../lib/prisma";
4
+ import { prisma } from "../lib/prisma.js";
5
5
  import Google from "@auth/core/providers/google";
6
6
  import Credentials from "@auth/core/providers/credentials";
7
7
 
File without changes
package/src/lib/prisma.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { PrismaClient } from "../generated/prisma";
1
+ import { PrismaClient } from "@prisma/client";
2
2
 
3
3
  const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
4
4
 
@@ -0,0 +1,13 @@
1
+ // src/server/lib/gcs.ts
2
+ import { Storage } from "@google-cloud/storage";
3
+
4
+ export const storage = new Storage({
5
+ projectId: process.env.GCP_PROJECT_ID,
6
+ credentials: {
7
+ client_email: process.env.GCP_CLIENT_EMAIL,
8
+ private_key: process.env.GCP_PRIVATE_KEY?.replace(/\\n/g, "\n"),
9
+ },
10
+ });
11
+
12
+ export const bucket = storage.bucket(process.env.GCP_BUCKET!);
13
+
@@ -1,7 +1,7 @@
1
1
  import { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
2
- import { router } from '../trpc';
3
- import { auth } from './auth';
4
- import { workspace } from './workspace';
2
+ import { router } from '../trpc.js';
3
+ import { auth } from './auth.js';
4
+ import { workspace } from './workspace.js';
5
5
 
6
6
  export const appRouter = router({
7
7
  auth,
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { router, publicProcedure, authedProcedure } from '../trpc';
2
+ import { router, publicProcedure, authedProcedure } from '../trpc.js';
3
3
  import bcrypt from 'bcryptjs';
4
4
 
5
5
  export const auth = router({
@@ -1,51 +1,157 @@
1
1
  import { z } from 'zod';
2
- import { router, publicProcedure, authedProcedure } from '../trpc';
2
+ import { router, publicProcedure, authedProcedure } from '../trpc.js';
3
+ import { bucket } from '../lib/storage.js';
4
+ import { FileAsset } from '@prisma/client';
3
5
 
4
6
  export const workspace = router({
5
7
  // Mutation with Zod input
6
8
  list: publicProcedure
7
9
  .query(async ({ ctx, input }) => {
10
+ const workspaces = await ctx.db.workspace.findMany({
11
+ where: {
12
+ ownerId: ctx.session?.user.id,
13
+ },
14
+ });
15
+ return workspaces;
8
16
  }),
9
17
 
10
18
  create: publicProcedure
11
19
  .input(z.object({
12
-
20
+ name: z.string().min(1).max(100),
21
+ description: z.string().max(500).optional(),
13
22
  }))
14
- .mutation(({ input }) => {
15
-
23
+ .mutation(({ ctx, input}) => {
24
+ return ctx.db.workspace.create({
25
+ data: {
26
+ title: input.name,
27
+ description: input.description,
28
+ ownerId: ctx.session?.user.id,
29
+ },
30
+ });
16
31
  }),
17
32
  get: publicProcedure
18
33
  .input(z.object({
19
34
  id: z.string().uuid(),
20
35
  }))
21
- .query(({ input }) => {
36
+ .query(({ ctx, input }) => {
37
+ return ctx.db.workspace.findUnique({
38
+ where: {
39
+ id: input.id,
40
+ },
41
+ });
22
42
  }),
23
43
  update: publicProcedure
24
44
  .input(z.object({
25
45
  id: z.string().uuid(),
46
+ name: z.string().min(1).max(100).optional(),
47
+ description: z.string().max(500).optional(),
26
48
  }))
27
- .mutation(({ input }) => {
28
-
49
+ .mutation(({ ctx, input }) => {
50
+ return ctx.db.workspace.update({
51
+ where: {
52
+ id: input.id,
53
+ },
54
+ data: {
55
+ title: input.name,
56
+ description: input.description,
57
+ },
58
+ });
29
59
  }),
30
60
  delete: publicProcedure
31
61
  .input(z.object({
32
62
  id: z.string().uuid(),
33
63
  }))
34
- .mutation(({ input }) => {
35
-
64
+ .mutation(({ ctx, input }) => {
65
+ ctx.db.workspace.delete({
66
+ where: {
67
+ id: input.id,
68
+ },
69
+ });
70
+ return true;
36
71
  }),
37
- upload: publicProcedure
72
+ uploadFiles: publicProcedure
38
73
  .input(z.object({
39
- file: z.string(),
74
+ id: z.string().uuid(),
75
+ files: z.array(
76
+ z.object({
77
+ filename: z.string().min(1).max(255),
78
+ contentType: z.string().min(1).max(100),
79
+ size: z.number().min(1), // size in bytes
80
+ })
81
+ ),
40
82
  }))
41
- .mutation(({ input }) => {
42
-
83
+ .mutation(async ({ ctx, input }) => {
84
+ const results = [];
85
+
86
+ for (const file of input.files) {
87
+ // 1. Insert into DB
88
+ const record = await ctx.db.fileAsset.create({
89
+ data: {
90
+ userId: ctx.session.user.id,
91
+ name: file.filename,
92
+ mimeType: file.contentType,
93
+ size: file.size,
94
+ workspaceId: input.id,
95
+ },
96
+ });
97
+
98
+ // 2. Generate signed URL for direct upload
99
+ const [url] = await bucket
100
+ .file(`${ctx.session.user.id}/${record.id}-${file.filename}`)
101
+ .getSignedUrl({
102
+ action: "write",
103
+ expires: Date.now() + 5 * 60 * 1000, // 5 min
104
+ contentType: file.contentType,
105
+ });
106
+
107
+ // 3. Update record with bucket info
108
+ await ctx.db.fileAsset.update({
109
+ where: { id: record.id },
110
+ data: {
111
+ bucket: bucket.name,
112
+ objectKey: `${ctx.session.user.id}/${record.id}-${file.filename}`,
113
+ },
114
+ });
115
+
116
+ results.push({
117
+ fileId: record.id,
118
+ uploadUrl: url,
119
+ });
120
+ }
121
+
122
+ return results;
123
+
43
124
  }),
44
- deleteFile: publicProcedure
125
+ deleteFiles: publicProcedure
45
126
  .input(z.object({
46
- fileId: z.string().uuid(),
127
+ fileId: z.array(z.string().uuid()),
128
+ id: z.string().uuid(),
47
129
  }))
48
- .mutation(({ input }) => {
49
-
130
+ .mutation(({ ctx, input }) => {
131
+ const files = ctx.db.fileAsset.findMany({
132
+ where: {
133
+ id: { in: input.fileId },
134
+ workspaceId: input.id,
135
+ },
136
+ });
137
+
138
+ // Delete from GCS
139
+ files.then((fileRecords: FileAsset[]) => {
140
+ fileRecords.forEach((file: FileAsset) => {
141
+ if (file.bucket && file.objectKey) {
142
+ const gcsFile: import('@google-cloud/storage').File = bucket.file(file.objectKey);
143
+ gcsFile.delete({ ignoreNotFound: true }).catch((err: unknown) => {
144
+ console.error(`Error deleting file ${file.objectKey} from bucket ${file.bucket}:`, err);
145
+ });
146
+ }
147
+ });
148
+ });
149
+
150
+ return ctx.db.fileAsset.deleteMany({
151
+ where: {
152
+ id: { in: input.fileId },
153
+ workspaceId: input.id,
154
+ },
155
+ });
50
156
  }),
51
157
  });
package/src/server.ts CHANGED
@@ -4,10 +4,10 @@ import helmet from 'helmet';
4
4
  import morgan from 'morgan';
5
5
  import compression from 'compression';
6
6
  import * as trpcExpress from '@trpc/server/adapters/express';
7
- import { authRouter } from './lib/auth';
7
+ import { authRouter } from './lib/auth.js';
8
8
 
9
- import { appRouter } from './routers/_app';
10
- import { createContext } from './context';
9
+ import { appRouter } from './routers/_app.js';
10
+ import { createContext } from './context.js';
11
11
 
12
12
  const PORT = process.env.PORT ? Number(process.env.PORT) : 3001;
13
13
 
@@ -16,7 +16,11 @@ async function main() {
16
16
 
17
17
  // Middlewares
18
18
  app.use(helmet());
19
- app.use(cors({ origin: true, credentials: true }));
19
+ app.use(cors({
20
+ origin: "http://localhost:3000", // your Next.js dev URL
21
+ credentials: true, // allow cookies
22
+ }));
23
+
20
24
  app.use(morgan('dev'));
21
25
  app.use(compression());
22
26
  app.use(express.json());
package/src/trpc.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { initTRPC, TRPCError } from "@trpc/server";
2
2
  import superjson from "superjson";
3
- import type { Context } from "./context";
3
+ import type { Context } from "./context.js";
4
4
 
5
5
  const t = initTRPC.context<Context>().create({
6
6
  transformer: superjson,
package/tsconfig.json CHANGED
@@ -1,17 +1,24 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2020",
4
- "module": "CommonJS",
5
- "moduleResolution": "node",
4
+ "module": "ES2020", // <-- this ensures ESM output
5
+ "moduleResolution": "bundler",
6
+ "plugins": [
7
+ { "transform": "typescript-transform-paths" },
8
+ { "transform": "typescript-transform-paths", "afterDeclarations": true }
9
+
10
+ ],
11
+ "baseUrl": "./",
12
+ "declaration": true,
13
+ "declarationDir": "dist", // optional, but keeps .d.ts in dist
14
+ "emitDeclarationOnly": false, // unless you only want .d.ts
15
+ "noEmit": false, // must not be true
16
+
6
17
  "esModuleInterop": true,
7
18
  "strict": true,
8
19
  "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "outDir": "dist",
11
- "baseUrl": ".",
12
- "declaration": true,
13
- "declarationMap": true
20
+ "outDir": "dist"
14
21
  },
15
- "include": ["src/**/*"],
16
- "exclude": ["node_modules", "dist"]
17
- }
22
+ "include": ["src"],
23
+ "exclude": ["node_modules", "dist" ]
24
+ }