mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2024-04-21 12:32:08 +00:00
migration: migrate NodeJS code base to Golang
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
Config *Config
|
||||
Helpers *Helpers
|
||||
Backend IBackend
|
||||
Body map[string]string
|
||||
Session map[string]string
|
||||
}
|
||||
|
||||
func GetCurrentDir() string {
|
||||
ex, _ := os.Executable()
|
||||
return filepath.Dir(ex)
|
||||
}
|
||||
|
||||
var HTTPClient = http.Client{
|
||||
Timeout: 5 * time.Hour,
|
||||
Transport: &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 10 * time.Second,
|
||||
KeepAlive: 10 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
IdleConnTimeout: 60 * time.Second,
|
||||
ResponseHeaderTimeout: 60 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
var HTTP = http.Client{
|
||||
Timeout: 800 * time.Millisecond,
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/hashstructure"
|
||||
"github.com/patrickmn/go-cache"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AppCache struct {
|
||||
Cache *cache.Cache
|
||||
}
|
||||
|
||||
func (a *AppCache) Get(key interface{}) interface{} {
|
||||
hash, err := hashstructure.Hash(key, nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
value, found := a.Cache.Get(fmt.Sprint(hash))
|
||||
if found == false {
|
||||
return nil
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func (a *AppCache) Set(key map[string]string, value interface{}) {
|
||||
hash, err := hashstructure.Hash(key, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
a.Cache.Set(fmt.Sprint(hash), value, cache.DefaultExpiration)
|
||||
}
|
||||
|
||||
func (a *AppCache) OnEvict(fn func(string, interface{})) {
|
||||
a.Cache.OnEvicted(fn)
|
||||
}
|
||||
|
||||
func NewAppCache(arg ...time.Duration) AppCache {
|
||||
var retention time.Duration = 5
|
||||
var cleanup time.Duration = 10
|
||||
if len(arg) > 0 {
|
||||
retention = arg[0]
|
||||
if len(arg) > 1 {
|
||||
cleanup = arg[1]
|
||||
}
|
||||
}
|
||||
c := AppCache{}
|
||||
c.Cache = cache.New(retention*time.Minute, cleanup*time.Minute)
|
||||
return c
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
CONFIG_PATH = "data/config/"
|
||||
APP_VERSION = "v0.3"
|
||||
)
|
||||
|
||||
func NewConfig() *Config {
|
||||
c := Config{}
|
||||
c.Initialise()
|
||||
return &c
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
General struct {
|
||||
Port int `json:"port"`
|
||||
Host string `json:"host"`
|
||||
SecretKey string `json:"secret_key"`
|
||||
Editor string `json:"editor"`
|
||||
ForkButton bool `json:"fork_button"`
|
||||
DisplayHidden bool `json:"display_hidden"`
|
||||
} `json:"general"`
|
||||
Log struct {
|
||||
Enable bool `json:"enable"`
|
||||
Level string `json:"level"`
|
||||
Telemetry bool `json:"telemetry"`
|
||||
} `json:"log"`
|
||||
OAuthProvider struct {
|
||||
Dropbox struct {
|
||||
ClientID string `json:"client_id"`
|
||||
} `json:"dropbox"`
|
||||
GoogleDrive struct {
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
} `json:"gdrive"`
|
||||
} `json:"oauth"`
|
||||
Connections []struct {
|
||||
Type string `json:"type"`
|
||||
Label string `json:"label"`
|
||||
Hostname *string `json:"hostname,omitempty"`
|
||||
Username *string `json:"username,omitempty"`
|
||||
Password *string `json:"password,omitempty"`
|
||||
Url *string `json:"url,omitempty"`
|
||||
Advanced *bool `json:"advanced,omitempty"`
|
||||
Port *uint `json:"port,omitempty"`
|
||||
Path *string `json:"path,omitempty"`
|
||||
Passphrase *string `json:"passphrase,omitempty"`
|
||||
SecretAccessKey *string `json:"secret_access_key,omitempty"`
|
||||
AccessKeyId *string `json:"access_key_id,omitempty"`
|
||||
Endpoint *string `json:"endpoint,omitempty"`
|
||||
Commit *string `json:"commit,omitempty"`
|
||||
Branch *string `json:"branch,omitempty"`
|
||||
AuthorEmail *string `json:"author_email,omitempty"`
|
||||
AuthorName *string `json:"author_name,omitempty"`
|
||||
CommitterEmail *string `json:"committer_email,omitempty"`
|
||||
CommitterName *string `json:"committter_name,omitempty"`
|
||||
} `json:"connections"`
|
||||
Runtime struct {
|
||||
Dirname string
|
||||
ConfigPath string
|
||||
FirstSetup bool
|
||||
} `-`
|
||||
MimeTypes map[string]string `json:"mimetypes"`
|
||||
}
|
||||
|
||||
func (c *Config) Initialise() {
|
||||
c.Runtime.Dirname = GetCurrentDir()
|
||||
c.Runtime.ConfigPath = filepath.Join(c.Runtime.Dirname, CONFIG_PATH)
|
||||
os.MkdirAll(c.Runtime.ConfigPath, os.ModePerm)
|
||||
if err := c.loadConfig(filepath.Join(c.Runtime.ConfigPath, "config.json")); err != nil {
|
||||
log.Println("> Can't load configuration file")
|
||||
}
|
||||
if err := c.loadMimeType(filepath.Join(c.Runtime.ConfigPath, "mime.json")); err != nil {
|
||||
log.Println("> Can't load mimetype config")
|
||||
}
|
||||
go c.ChangeListener()
|
||||
}
|
||||
|
||||
func (c *Config) loadConfig(path string) error {
|
||||
file, err := os.Open(path)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
c = &Config{}
|
||||
log.Println("can't load config file")
|
||||
return err
|
||||
}
|
||||
decoder := json.NewDecoder(file)
|
||||
err = decoder.Decode(&c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.populateDefault(path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) ChangeListener() {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer watcher.Close()
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event := <-watcher.Events:
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
config_path := filepath.Join(c.Runtime.ConfigPath, "config.json")
|
||||
if err = c.loadConfig(config_path); err != nil {
|
||||
log.Println("can't load config file")
|
||||
} else {
|
||||
c.populateDefault(config_path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
_ = watcher.Add(c.Runtime.ConfigPath)
|
||||
<-done
|
||||
}
|
||||
|
||||
func (c *Config) populateDefault(path string) {
|
||||
if c.General.Port == 0 {
|
||||
c.General.Port = 8334
|
||||
}
|
||||
if c.General.SecretKey == "" {
|
||||
c.General.SecretKey = RandomString(16)
|
||||
j, err := json.Marshal(c)
|
||||
if err == nil {
|
||||
f, err := os.OpenFile(path, os.O_WRONLY, os.ModePerm)
|
||||
if err == nil {
|
||||
f.Write(j)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.OAuthProvider.Dropbox.ClientID == "" {
|
||||
c.OAuthProvider.Dropbox.ClientID = os.Getenv("DROPBOX_CLIENT_ID")
|
||||
}
|
||||
if c.OAuthProvider.GoogleDrive.ClientID == "" {
|
||||
c.OAuthProvider.GoogleDrive.ClientID = os.Getenv("GDRIVE_CLIENT_ID")
|
||||
}
|
||||
if c.OAuthProvider.GoogleDrive.ClientSecret == "" {
|
||||
c.OAuthProvider.GoogleDrive.ClientSecret = os.Getenv("GDRIVE_CLIENT_SECRET")
|
||||
}
|
||||
if c.General.Host == "" {
|
||||
c.General.Host = os.Getenv("APPLICATION_URL")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) Export() (string, error) {
|
||||
publicConf := struct {
|
||||
Editor string `json:"editor"`
|
||||
ForkButton bool `json:"fork_button"`
|
||||
DisplayHidden bool `json:"display_hidden"`
|
||||
Connections interface{} `json:"connections"`
|
||||
MimeTypes map[string]string `json:"mime"`
|
||||
}{
|
||||
Editor: c.General.Editor,
|
||||
ForkButton: c.General.ForkButton,
|
||||
DisplayHidden: c.General.DisplayHidden,
|
||||
Connections: c.Connections,
|
||||
MimeTypes: c.MimeTypes,
|
||||
}
|
||||
j, err := json.Marshal(publicConf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(j), nil
|
||||
}
|
||||
|
||||
func (c *Config) loadMimeType(path string) error {
|
||||
file, err := os.Open(path)
|
||||
defer file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoder := json.NewDecoder(file)
|
||||
return decoder.Decode(&c.MimeTypes)
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func NewError(message string, status int) error {
|
||||
return AppError{message, status}
|
||||
}
|
||||
|
||||
type AppError struct {
|
||||
message string
|
||||
status int
|
||||
}
|
||||
|
||||
func (e AppError) Error() string {
|
||||
return fmt.Sprintf("%s", e.message)
|
||||
}
|
||||
func (e AppError) Status() int {
|
||||
return e.status
|
||||
}
|
||||
|
||||
func HTTPFriendlyStatus(n int) string {
|
||||
if n < 400 && n > 600 {
|
||||
return "Humm"
|
||||
}
|
||||
switch n {
|
||||
case 400:
|
||||
return "Bad Request"
|
||||
case 401:
|
||||
return "Unauthorized"
|
||||
case 402:
|
||||
return "Payment Required"
|
||||
case 403:
|
||||
return "Forbidden"
|
||||
case 404:
|
||||
return "Not Found"
|
||||
case 405:
|
||||
return "Not Allowed"
|
||||
case 406:
|
||||
return "Not Acceptable"
|
||||
case 407:
|
||||
return "Authentication Required"
|
||||
case 408:
|
||||
return "Timeout"
|
||||
case 409:
|
||||
return "Conflict"
|
||||
case 410:
|
||||
return "Gone"
|
||||
case 411:
|
||||
return "Length Required"
|
||||
case 412:
|
||||
return "Failed"
|
||||
case 413:
|
||||
return "Too Large"
|
||||
case 414:
|
||||
return "URI Too Long"
|
||||
case 415:
|
||||
return "Unsupported Media"
|
||||
case 416:
|
||||
return "Not Like This"
|
||||
case 417:
|
||||
return "Unexpected"
|
||||
case 418:
|
||||
return "I'm a teapot"
|
||||
case 421:
|
||||
return "Redirection Problem"
|
||||
case 422:
|
||||
return "Unprocessable"
|
||||
case 423:
|
||||
return "Locked"
|
||||
case 424:
|
||||
return "Failed Dependency"
|
||||
case 426:
|
||||
return "Upgrade Required"
|
||||
case 428:
|
||||
return "Need Something"
|
||||
case 429:
|
||||
return "Too Many Requests"
|
||||
case 431:
|
||||
return "Request Too Large"
|
||||
case 451:
|
||||
return "Not Available"
|
||||
case 500:
|
||||
return "Internal Server Error"
|
||||
case 501:
|
||||
return "Not Implemented"
|
||||
case 502:
|
||||
return "Bad Gateway"
|
||||
case 503:
|
||||
return "Service Unavailable"
|
||||
case 504:
|
||||
return "Gateway Timeout"
|
||||
case 505:
|
||||
return "Unsupported HTTP Version"
|
||||
case 506:
|
||||
return "Need To Negotiate"
|
||||
case 507:
|
||||
return "Insufficient Storage"
|
||||
case 508:
|
||||
return "Loop Detected"
|
||||
case 510:
|
||||
return "Not Extended"
|
||||
case 511:
|
||||
return "Authentication Required"
|
||||
default:
|
||||
return "Oops"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package common
|
||||
|
||||
func IsDirectory(path string) bool {
|
||||
if string(path[len(path)-1]) != "/" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Helpers struct {
|
||||
AbsolutePath func(p string) string
|
||||
MimeType func(p string) string
|
||||
}
|
||||
|
||||
func NewHelpers(config *Config) *Helpers {
|
||||
return &Helpers{
|
||||
MimeType: mimeType(config),
|
||||
AbsolutePath: absolutePath(config),
|
||||
}
|
||||
}
|
||||
|
||||
func absolutePath(c *Config) func(p string) string {
|
||||
return func(p string) string {
|
||||
return filepath.Join(c.Runtime.Dirname, p)
|
||||
}
|
||||
}
|
||||
|
||||
func mimeType(c *Config) func(p string) string {
|
||||
return func(p string) string {
|
||||
ext := filepath.Ext(p)
|
||||
if ext != "" {
|
||||
ext = ext[1:]
|
||||
}
|
||||
ext = strings.ToLower(ext)
|
||||
mType := c.MimeTypes[ext]
|
||||
if mType == "" {
|
||||
return "application/octet-stream"
|
||||
}
|
||||
return mType
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LogEntry struct {
|
||||
Host string `json:"host"`
|
||||
Method string `json:"method"`
|
||||
RequestURI string `json:"pathname"`
|
||||
Proto string `json:"proto"`
|
||||
Status int `json:"status"`
|
||||
Scheme string `json:"scheme"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
Ip string `json:"ip"`
|
||||
Referer string `json:"referer"`
|
||||
Timestamp time.Time `json:"_id"`
|
||||
Duration int64 `json:"responseTime"`
|
||||
Version string `json:"version"`
|
||||
Backend string `json:"backend"`
|
||||
}
|
||||
|
||||
func Debug_reader(r io.Reader) {
|
||||
a, _ := ioutil.ReadAll(r)
|
||||
fmt.Println("> DEBUG:", string(a))
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IBackend interface {
|
||||
Ls(path string) ([]os.FileInfo, error)
|
||||
Cat(path string) (io.Reader, error)
|
||||
Mkdir(path string) error
|
||||
Rm(path string) error
|
||||
Mv(from string, to string) error
|
||||
Save(path string, file io.Reader) error
|
||||
Touch(path string) error
|
||||
Info() string
|
||||
}
|
||||
|
||||
type File struct {
|
||||
FName string `json:"name"`
|
||||
FType string `json:"type"`
|
||||
FTime int64 `json:"time"`
|
||||
FSize int64 `json:"size"`
|
||||
CanRename *bool `json:"can_rename,omitempty"`
|
||||
CanMove *bool `json:"can_move_directory,omitempty"`
|
||||
CanDelete *bool `json:"can_delete,omitempty"`
|
||||
}
|
||||
|
||||
func (f File) Name() string {
|
||||
return f.FName
|
||||
}
|
||||
func (f File) Size() int64 {
|
||||
return f.FSize
|
||||
}
|
||||
func (f File) Mode() os.FileMode {
|
||||
return 0
|
||||
}
|
||||
func (f File) ModTime() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
func (f File) IsDir() bool {
|
||||
if f.FType != "directory" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (f File) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
CanSee *bool `json:"can_read,omitempty"`
|
||||
CanCreateFile *bool `json:"can_create_file,omitempty"`
|
||||
CanCreateDirectory *bool `json:"can_create_directory,omitempty"`
|
||||
CanRename *bool `json:"can_rename,omitempty"`
|
||||
CanMove *bool `json:"can_move,omitempty"`
|
||||
CanUpload *bool `json:"can_upload,omitempty"`
|
||||
Expire *time.Time `json:"-"`
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
var Letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
|
||||
func RandomString(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = Letters[rand.Intn(len(Letters))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func NewBool(t bool) *bool {
|
||||
return &t
|
||||
}
|
||||
Reference in New Issue
Block a user