11import { TRPCError } from "@trpc/server" ;
22import { z } from "zod" ;
3- import { t } from "../../trpc.js" ;
3+ import { protectedProcedure , t } from "../../trpc.js" ;
4+ import type { FilesDomainError } from "./types.js" ;
45
56const pathSchema = z . string ( ) . min ( 1 ) ;
67
8+ function toTrpcError ( error : FilesDomainError ) : TRPCError {
9+ switch ( error . kind ) {
10+ case "Forbidden" :
11+ return new TRPCError ( { code : "FORBIDDEN" , message : error . reason } ) ;
12+ case "NotFound" :
13+ return new TRPCError ( { code : "NOT_FOUND" } ) ;
14+ case "Conflict" :
15+ return new TRPCError ( {
16+ code : "CONFLICT" ,
17+ message : "file changed on disk" ,
18+ cause : { currentMtimeMs : error . currentMtimeMs } ,
19+ } ) ;
20+ case "AlreadyExists" :
21+ return new TRPCError ( { code : "CONFLICT" , message : "path already exists" } ) ;
22+ case "PayloadTooLarge" :
23+ return new TRPCError ( { code : "PAYLOAD_TOO_LARGE" , message : error . detail } ) ;
24+ }
25+ }
26+
727export const filesRouter = t . router ( {
8- tree : t . procedure . query ( ( { ctx } ) => ( {
28+ tree : protectedProcedure . query ( ( { ctx } ) => ( {
929 entries : ctx . files . buildTree ( ) ,
1030 } ) ) ,
1131
12- read : t . procedure
32+ read : protectedProcedure
1333 . input ( z . object ( { path : pathSchema } ) )
1434 . query ( async ( { ctx, input } ) => {
1535 const result = await ctx . files . readFileSafe ( input . path ) ;
16- if ( ! result ) {
17- throw new TRPCError ( { code : "NOT_FOUND" } ) ;
18- }
19- return result ;
36+ if ( ! result . ok ) throw toTrpcError ( result . error ) ;
37+ return result . value ;
2038 } ) ,
2139
22- write : t . procedure
40+ write : protectedProcedure
2341 . input ( z . object ( {
2442 path : pathSchema ,
2543 content : z . string ( ) ,
2644 expectedMtimeMs : z . number ( ) . optional ( ) ,
2745 } ) )
2846 . mutation ( async ( { ctx, input } ) => {
29- try {
30- const result = await ctx . files . writeFileSafe (
31- input . path ,
32- input . content ,
33- input . expectedMtimeMs ,
34- ) ;
35- if ( "conflict" in result ) {
36- throw new TRPCError ( {
37- code : "CONFLICT" ,
38- message : "file changed on disk" ,
39- cause : { currentMtimeMs : result . currentMtimeMs } ,
40- } ) ;
41- }
42- return { mtimeMs : result . mtimeMs } ;
43- } catch ( err ) {
44- if ( err instanceof TRPCError ) throw err ;
45- throw new TRPCError ( { code : "FORBIDDEN" , message : ( err as Error ) . message } ) ;
46- }
47+ const result = await ctx . files . writeFileSafe (
48+ input . path ,
49+ input . content ,
50+ input . expectedMtimeMs ,
51+ ) ;
52+ if ( ! result . ok ) throw toTrpcError ( result . error ) ;
53+ return { mtimeMs : result . value . mtimeMs } ;
4754 } ) ,
4855
49- create : t . procedure
56+ create : protectedProcedure
5057 . input ( z . object ( { path : pathSchema , content : z . string ( ) } ) )
5158 . mutation ( async ( { ctx, input } ) => {
52- try {
53- const result = await ctx . files . createFileSafe ( input . path , input . content ) ;
54- if ( "exists" in result ) {
55- throw new TRPCError ( { code : "CONFLICT" , message : "path already exists" } ) ;
56- }
57- return { mtimeMs : result . mtimeMs } ;
58- } catch ( err ) {
59- if ( err instanceof TRPCError ) throw err ;
60- throw new TRPCError ( { code : "FORBIDDEN" , message : ( err as Error ) . message } ) ;
61- }
59+ const result = await ctx . files . createFileSafe ( input . path , input . content ) ;
60+ if ( ! result . ok ) throw toTrpcError ( result . error ) ;
61+ return { mtimeMs : result . value . mtimeMs } ;
6262 } ) ,
6363
64- mkdir : t . procedure
64+ mkdir : protectedProcedure
6565 . input ( z . object ( { path : pathSchema } ) )
6666 . mutation ( async ( { ctx, input } ) => {
67- try {
68- const result = await ctx . files . mkdirSafe ( input . path ) ;
69- if ( "exists" in result ) {
70- throw new TRPCError ( { code : "CONFLICT" , message : "path exists and is not a directory" } ) ;
71- }
72- return { ok : true as const } ;
73- } catch ( err ) {
74- if ( err instanceof TRPCError ) throw err ;
75- throw new TRPCError ( { code : "FORBIDDEN" , message : ( err as Error ) . message } ) ;
76- }
67+ const result = await ctx . files . mkdirSafe ( input . path ) ;
68+ if ( ! result . ok ) throw toTrpcError ( result . error ) ;
69+ return { ok : true as const } ;
7770 } ) ,
7871
79- rename : t . procedure
72+ rename : protectedProcedure
8073 . input ( z . object ( {
8174 from : pathSchema ,
8275 to : pathSchema ,
8376 overwrite : z . boolean ( ) . optional ( ) ,
8477 } ) )
8578 . mutation ( async ( { ctx, input } ) => {
86- try {
87- const result = await ctx . files . renameSafe ( input . from , input . to , input . overwrite ?? false ) ;
88- if ( "exists" in result ) {
89- throw new TRPCError ( { code : "CONFLICT" , message : "destination already exists" } ) ;
90- }
91- return { ok : true as const } ;
92- } catch ( err ) {
93- if ( err instanceof TRPCError ) throw err ;
94- throw new TRPCError ( { code : "FORBIDDEN" , message : ( err as Error ) . message } ) ;
95- }
79+ const result = await ctx . files . renameSafe ( input . from , input . to , input . overwrite ?? false ) ;
80+ if ( ! result . ok ) throw toTrpcError ( result . error ) ;
81+ return { ok : true as const } ;
9682 } ) ,
9783
98- remove : t . procedure
84+ remove : protectedProcedure
9985 . input ( z . object ( { path : pathSchema } ) )
10086 . mutation ( async ( { ctx, input } ) => {
101- try {
102- await ctx . files . deleteSafe ( input . path ) ;
103- return { ok : true as const } ;
104- } catch ( err ) {
105- if ( err instanceof TRPCError ) throw err ;
106- throw new TRPCError ( { code : "FORBIDDEN" , message : ( err as Error ) . message } ) ;
107- }
87+ const result = await ctx . files . deleteSafe ( input . path ) ;
88+ if ( ! result . ok ) throw toTrpcError ( result . error ) ;
89+ return { ok : true as const } ;
10890 } ) ,
10991
110- upload : t . procedure
92+ upload : protectedProcedure
11193 . input ( z . object ( {
11294 path : pathSchema ,
11395 contentBase64 : z . string ( ) ,
@@ -118,27 +100,16 @@ export const filesRouter = t.router({
118100 overwrite : z . boolean ( ) . optional ( ) ,
119101 } ) )
120102 . mutation ( async ( { ctx, input } ) => {
121- try {
122- const result = await ctx . files . uploadFileSafe (
123- input . path ,
124- input . contentBase64 ,
125- input . overwrite ?? false ,
126- ) ;
127- if ( "exists" in result ) {
128- throw new TRPCError ( { code : "CONFLICT" , message : "path already exists" } ) ;
129- }
130- return {
131- mtimeMs : result . mtimeMs ,
132- absolutePath : result . absolutePath ,
133- contentType : input . contentType ,
134- } ;
135- } catch ( err ) {
136- if ( err instanceof TRPCError ) throw err ;
137- const msg = ( err as Error ) . message ;
138- throw new TRPCError ( {
139- code : / t o o l a r g e / i. test ( msg ) ? "PAYLOAD_TOO_LARGE" : "FORBIDDEN" ,
140- message : msg ,
141- } ) ;
142- }
103+ const result = await ctx . files . uploadFileSafe (
104+ input . path ,
105+ input . contentBase64 ,
106+ input . overwrite ?? false ,
107+ ) ;
108+ if ( ! result . ok ) throw toTrpcError ( result . error ) ;
109+ return {
110+ mtimeMs : result . value . mtimeMs ,
111+ absolutePath : result . value . absolutePath ,
112+ contentType : input . contentType ,
113+ } ;
143114 } ) ,
144115} ) ;
0 commit comments