mirror of
https://github.com/Forceu/Gokapi.git
synced 2026-01-10 02:49:34 -06:00
157 lines
8.7 KiB
Go
157 lines
8.7 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/jinzhu/copier"
|
|
"net/url"
|
|
)
|
|
|
|
// File is a struct used for saving information about an uploaded file
|
|
type File struct {
|
|
Id string `json:"Id" redis:"Id"` // The internal ID of the file
|
|
Name string `json:"Name" redis:"Name"` // The filename. Will be 'Encrypted file' for end-to-end encrypted files
|
|
Size string `json:"Size" redis:"Size"` // Filesize in a human-readable format
|
|
SHA1 string `json:"SHA1" redis:"SHA1"` // The hash of the file, used for deduplication
|
|
PasswordHash string `json:"PasswordHash" redis:"PasswordHash"` // The hash of the password (if the file is password protected)
|
|
HotlinkId string `json:"HotlinkId" redis:"HotlinkId"` // If file is a picture file and can be hotlinked, this is the ID for the hotlink
|
|
ContentType string `json:"ContentType" redis:"ContentType"` // The MIME type for the file
|
|
AwsBucket string `json:"AwsBucket" redis:"AwsBucket"` // If the file is stored in the cloud, this is the bucket that is being used
|
|
ExpireAtString string `json:"ExpireAtString" redis:"ExpireAtString"` // Time expiry in a human-readable format in local time
|
|
ExpireAt int64 `json:"ExpireAt" redis:"ExpireAt"` // UTC timestamp of file expiry
|
|
SizeBytes int64 `json:"SizeBytes" redis:"SizeBytes"` // Filesize in bytes
|
|
UploadDate int64 `json:"UploadDate" redis:"UploadDate"` // UTC timestamp of upload time
|
|
DownloadsRemaining int `json:"DownloadsRemaining" redis:"DownloadsRemaining"` // The remaining downloads for this file
|
|
DownloadCount int `json:"DownloadCount" redis:"DownloadCount"` // The amount of times the file has been downloaded
|
|
UserId int `json:"UserId" redis:"UserId"` // The user ID of the uploader
|
|
Encryption EncryptionInfo `json:"Encryption" redis:"-"` // If the file is encrypted, this stores all info for decrypting
|
|
UnlimitedDownloads bool `json:"UnlimitedDownloads" redis:"UnlimitedDownloads"` // True if the uploader did not limit the downloads
|
|
UnlimitedTime bool `json:"UnlimitedTime" redis:"UnlimitedTime"` // True if the uploader did not limit the time
|
|
InternalRedisEncryption []byte `redis:"EncryptionRedis"` // This field is an internal field, used to store the EncryptionInfo in a Redis Hashmap
|
|
}
|
|
|
|
// FileApiOutput will be displayed for public outputs from the ID, hiding sensitive information
|
|
type FileApiOutput struct {
|
|
Id string `json:"Id"` // The internal ID of the file
|
|
Name string `json:"Name"` // The filename. Will be 'Encrypted file' for end-to-end encrypted files
|
|
Size string `json:"Size"` // Filesize in a human-readable format
|
|
HotlinkId string `json:"HotlinkId"` // If file is a picture file and can be hotlinked, this is the ID for the hotlink
|
|
ContentType string `json:"ContentType"` // The MIME type for the file
|
|
ExpireAtString string `json:"ExpireAtString"` // Time expiry in a human-readable format in local time
|
|
UrlDownload string `json:"UrlDownload"` // The public download URL for the file
|
|
UrlHotlink string `json:"UrlHotlink"` // The public hotlink URL for the file
|
|
UploadDate int64 `json:"UploadDate"` // UTC timestamp of upload time
|
|
ExpireAt int64 `json:"ExpireAt"` // "UTC timestamp of file expiry
|
|
SizeBytes int64 `json:"SizeBytes"` // Filesize in bytes
|
|
DownloadsRemaining int `json:"DownloadsRemaining"` // The remaining downloads for this file
|
|
DownloadCount int `json:"DownloadCount"` // The amount of times the file has been downloaded
|
|
UnlimitedDownloads bool `json:"UnlimitedDownloads"` // True if the uploader did not limit the downloads
|
|
UnlimitedTime bool `json:"UnlimitedTime"` // True if the uploader did not limit the time
|
|
RequiresClientSideDecryption bool `json:"RequiresClientSideDecryption"` // True if the file has to be decrypted client-side
|
|
IsEncrypted bool `json:"IsEncrypted"` // True if the file is encrypted
|
|
IsEndToEndEncrypted bool `json:"IsEndToEndEncrypted"` // True if the file is end-to-end encrypted
|
|
IsPasswordProtected bool `json:"IsPasswordProtected"` // True if a password has to be entered before downloading the file
|
|
IsSavedOnLocalStorage bool `json:"IsSavedOnLocalStorage"` // True if the file does not use cloud storage
|
|
UploaderId int `json:"UploaderId"` // The user ID of the uploader
|
|
}
|
|
|
|
// EncryptionInfo holds information about the encryption used on the file
|
|
type EncryptionInfo struct {
|
|
IsEncrypted bool `json:"IsEncrypted" redis:"IsEncrypted"`
|
|
IsEndToEndEncrypted bool `json:"IsEndToEndEncrypted" redis:"IsEndToEndEncrypted"`
|
|
DecryptionKey []byte `json:"DecryptionKey" redis:"DecryptionKey"`
|
|
Nonce []byte `json:"Nonce" redis:"Nonce"`
|
|
}
|
|
|
|
// IsLocalStorage returns true if the file is not stored on a remote storage
|
|
func (f *File) IsLocalStorage() bool {
|
|
return f.AwsBucket == ""
|
|
}
|
|
|
|
// ToFileApiOutput returns a json object without sensitive information
|
|
func (f *File) ToFileApiOutput(serverUrl string, useFilenameInUrl bool) (FileApiOutput, error) {
|
|
var result FileApiOutput
|
|
err := copier.Copy(&result, &f)
|
|
if err != nil {
|
|
return FileApiOutput{}, err
|
|
}
|
|
result.IsPasswordProtected = f.PasswordHash != ""
|
|
result.IsEncrypted = f.Encryption.IsEncrypted
|
|
result.IsSavedOnLocalStorage = f.AwsBucket == ""
|
|
if f.Encryption.IsEndToEndEncrypted || f.RequiresClientDecryption() {
|
|
result.RequiresClientSideDecryption = true
|
|
}
|
|
result.IsEndToEndEncrypted = f.Encryption.IsEndToEndEncrypted
|
|
result.UrlHotlink = getHotlinkUrl(result, serverUrl, useFilenameInUrl)
|
|
result.UrlDownload = getDownloadUrl(result, serverUrl, useFilenameInUrl)
|
|
result.UploaderId = f.UserId
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func getDownloadUrl(input FileApiOutput, serverUrl string, useFilename bool) string {
|
|
if useFilename {
|
|
return serverUrl + "d/" + input.Id + "/" + url.PathEscape(input.Name)
|
|
}
|
|
return serverUrl + "d?id=" + input.Id
|
|
}
|
|
|
|
func getHotlinkUrl(input FileApiOutput, serverUrl string, useFilename bool) string {
|
|
if input.RequiresClientSideDecryption || input.IsPasswordProtected {
|
|
return ""
|
|
}
|
|
if input.HotlinkId != "" {
|
|
return serverUrl + "h/" + input.HotlinkId
|
|
}
|
|
if useFilename {
|
|
return serverUrl + "dh/" + input.Id + "/" + url.PathEscape(input.Name)
|
|
}
|
|
return serverUrl + "downloadFile?id=" + input.Id
|
|
}
|
|
|
|
// ToJsonResult converts the file info to a json String used for returning a result for an upload
|
|
func (f *File) ToJsonResult(serverUrl string, includeFilename bool) string {
|
|
info, err := f.ToFileApiOutput(serverUrl, includeFilename)
|
|
if err != nil {
|
|
return errorAsJson(err)
|
|
}
|
|
|
|
byteOutput, err := json.Marshal(Result{
|
|
Result: "OK",
|
|
IncludeFilename: includeFilename,
|
|
FileInfo: info,
|
|
})
|
|
if err != nil {
|
|
return errorAsJson(err)
|
|
}
|
|
return string(byteOutput)
|
|
}
|
|
|
|
// RequiresClientDecryption checks if the file needs to be decrypted by the client
|
|
// (if remote storage or end-to-end encryption)
|
|
func (f *File) RequiresClientDecryption() bool {
|
|
if !f.Encryption.IsEncrypted {
|
|
return false
|
|
}
|
|
return !f.IsLocalStorage() || f.Encryption.IsEndToEndEncrypted
|
|
}
|
|
func errorAsJson(err error) string {
|
|
fmt.Println(err)
|
|
return "{\"Result\":\"error\",\"ErrorMessage\":\"" + err.Error() + "\"}"
|
|
}
|
|
|
|
// Result is the struct used for the result after an upload
|
|
// swagger:model UploadResult
|
|
type Result struct {
|
|
Result string `json:"Result"`
|
|
FileInfo FileApiOutput `json:"FileInfo"`
|
|
IncludeFilename bool `json:"IncludeFilename"`
|
|
}
|
|
|
|
// DownloadStatus contains current downloads, so they do not get removed during cleanup
|
|
type DownloadStatus struct {
|
|
Id string
|
|
FileId string
|
|
ExpireAt int64
|
|
}
|