| 
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -3,10 +3,15 @@ package services | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import ( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"context" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"errors" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"git.aiterp.net/rpdata/api/internal/generate" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"git.aiterp.net/rpdata/api/models" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"git.aiterp.net/rpdata/api/models/changekeys" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"git.aiterp.net/rpdata/api/repositories" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"git.aiterp.net/rpdata/api/space" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"github.com/h2non/filetype" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"io" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"log" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						"time" | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					var ErrPrivateNoAuthor = errors.New("cannot search for private files without an author") | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -18,8 +23,10 @@ var ErrCouldNotUploadFile = errors.New("could not upload file") | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					// FileService is a service for files.
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					type FileService struct { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						files       repositories.FileRepository | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						authService *AuthService | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						files         repositories.FileRepository | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						authService   *AuthService | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						changeService *ChangeService | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						space         *space.Client | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					func (s *FileService) Find(ctx context.Context, id string) (*models.File, error) { | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -56,13 +63,18 @@ func (s *FileService) List(ctx context.Context, filter models.FileFilter) ([]*mo | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						return s.files.List(ctx, filter) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					func (s *FileService) Upload(ctx context.Context, reader io.Reader, name string, size int64) (*models.File, error) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					func (s *FileService) Upload(ctx context.Context, reader io.Reader, name string, public bool, size int64) (*models.File, error) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						if name == "" { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							return nil, ErrInvalidName | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						} else if size < 320 || size > 16777216 { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							return nil, ErrInvalidFileSize | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						token := s.authService.TokenFromContext(ctx) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						if token == nil || !token.Permitted("file.upload", "member") { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							return nil, ErrUnauthorized | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						head := make([]byte, 320) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						n, err := reader.Read(head) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						if err != nil || n < 320 { | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -74,7 +86,35 @@ func (s *FileService) Upload(ctx context.Context, reader io.Reader, name string, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							return nil, ErrInvalidFileType | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						panic("implement rest of me") | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						reader2 := &concatReader{head: head, body: reader} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						path := generate.FileUploadID() + "." + fileType.Extension | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						err = s.space.UploadFile(ctx, path, fileType.MIME.Value, reader2, size) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						if err != nil || !allowedMimeTypes[fileType.MIME.Value] { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							log.Println("File upload failed:", err) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							return nil, ErrCouldNotUploadFile | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						file := &models.File{ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							Size:     size, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							Name:     name, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							URL:      s.space.URLFromPath(path), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							Time:     time.Now(), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							Author:   token.UserID, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							Kind:     "upload", | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							MimeType: fileType.MIME.Value, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							Public:   public, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						file, err = s.files.Insert(ctx, *file) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						if err != nil { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							return nil, err | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						s.changeService.Submit(ctx, models.ChangeModelFile, "upload", file.Public, changekeys.Listed(file), file) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						return file, nil | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					func (s *FileService) Edit(ctx context.Context, id string, name *string, public *bool) (*models.File, error) { | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -122,7 +162,7 @@ func (r *concatReader) Read(p []byte) (n int, err error) { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							r.headPos = len(r.head) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							copy(p, remainder) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							return len(p), nil | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
							return len(remainder), nil | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
						return r.body.Read(p) | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
				 | 
				
					
  |