migration: migrate NodeJS code base to Golang

This commit is contained in:
Mickael Kerjean
2018-07-30 13:34:44 +10:00
parent c5f2839fd7
commit 04c97e34fb
68 changed files with 3837 additions and 2217 deletions
+39
View File
@@ -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,
}
+50
View File
@@ -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
}
+188
View File
@@ -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)
}
+109
View File
@@ -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"
}
}
+8
View File
@@ -0,0 +1,8 @@
package common
func IsDirectory(path string) bool {
if string(path[len(path)-1]) != "/" {
return false
}
return true
}
+39
View File
@@ -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
}
}
+29
View File
@@ -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))
}
+60
View File
@@ -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:"-"`
}
+19
View File
@@ -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
}