This commit is contained in:
Yann Stepienik 2023-02-11 16:00:16 +00:00
commit 9d1eb7ae6a
46 changed files with 856 additions and 0 deletions

1
.bin/acorn Symbolic link
View file

@ -0,0 +1 @@
../node_modules/acorn/bin/acorn

1
.bin/browserslist Symbolic link
View file

@ -0,0 +1 @@
../node_modules/browserslist/cli.js

1
.bin/browserslist-lint Symbolic link
View file

@ -0,0 +1 @@
../node_modules/update-browserslist-db/cli.js

1
.bin/envinfo Symbolic link
View file

@ -0,0 +1 @@
../node_modules/envinfo/dist/cli.js

1
.bin/esbuild Symbolic link
View file

@ -0,0 +1 @@
../node_modules/esbuild/bin/esbuild

1
.bin/import-local-fixture Symbolic link
View file

@ -0,0 +1 @@
../node_modules/import-local/fixtures/cli.js

1
.bin/jsesc Symbolic link
View file

@ -0,0 +1 @@
../node_modules/jsesc/bin/jsesc

1
.bin/json5 Symbolic link
View file

@ -0,0 +1 @@
../node_modules/json5/lib/cli.js

1
.bin/loose-envify Symbolic link
View file

@ -0,0 +1 @@
../node_modules/loose-envify/cli.js

1
.bin/nanoid Symbolic link
View file

@ -0,0 +1 @@
../node_modules/nanoid/bin/nanoid.cjs

1
.bin/node-which Symbolic link
View file

@ -0,0 +1 @@
../node_modules/which/bin/node-which

1
.bin/resolve Symbolic link
View file

@ -0,0 +1 @@
../node_modules/resolve/bin/resolve

1
.bin/rollup Symbolic link
View file

@ -0,0 +1 @@
../node_modules/rollup/dist/bin/rollup

1
.bin/semver Symbolic link
View file

@ -0,0 +1 @@
../node_modules/semver/bin/semver.js

1
.bin/terser Symbolic link
View file

@ -0,0 +1 @@
../node_modules/terser/bin/terser

1
.bin/tsc Symbolic link
View file

@ -0,0 +1 @@
../node_modules/typescript/bin/tsc

1
.bin/tsserver Symbolic link
View file

@ -0,0 +1 @@
../node_modules/typescript/bin/tsserver

1
.bin/vite Symbolic link
View file

@ -0,0 +1 @@
../node_modules/vite/bin/vite.js

1
.bin/webpack Symbolic link
View file

@ -0,0 +1 @@
../node_modules/webpack/bin/webpack.js

1
.bin/webpack-cli Symbolic link
View file

@ -0,0 +1 @@
../node_modules/webpack-cli/bin/cli.js

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
go_modules
gupm_modules
node_modules
build
dev.gs
localcert.csr
localcert.crt
localcert.key
.vite
dev.json

7
.gupm_rc.gs Normal file
View file

@ -0,0 +1,7 @@
// look up dependencies in local go_modules and gupm_modules directories
env("GOPATH", run("go", ["env", "GOROOT"]) + ":" + pwd() + "/go_modules" + ":" + pwd() + "/gupm_modules")
// dev mode
env("MONGODB", readJsonFile("dev.json").MONGODB)
env("HTTP_PORT", 8080)
env("HTTPS_PORT", 8443)

5
build.gs Normal file
View file

@ -0,0 +1,5 @@
removeFiles(["build"]);
var goArgs = ["build", "-o", "build/bin"]
goArgs = goArgs.concat(dir("src/*.go"))
exec("go", goArgs);
copyFiles("client/dist/", "build/static/")

40
client/dist/assets/index-fb8d864c.js vendored Normal file

File diff suppressed because one or more lines are too long

14
client/dist/index.html vendored Normal file
View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<script type="module" crossorigin src="/assets/index-fb8d864c.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>

13
client/index.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

0
client/src/main.css Normal file
View file

9
client/src/main.tsx Normal file
View file

@ -0,0 +1,9 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import './main.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<div>Hello</div>
</React.StrictMode>
)

11
generate-certificate.sh Normal file
View file

@ -0,0 +1,11 @@
#!/bin/ash
FILE_CERT_NAME=localcert
if [ -f "certificates/$FILE_CERT_NAME.crt" ] && [ -f "$FILE_CERT_NAME.key" ] ; then
echo "Cert and Key already exist"
else
echo "Cert and Key does not exist, trying to create new ones..."
apk update && apk add openssl && rm -rf /var/cache/apk/*
openssl req -new -subj "/C=US/ST=California/CN=localhost" \
-newkey rsa:2048 -nodes -keyout "$FILE_CERT_NAME.key" -out "$FILE_CERT_NAME.csr"
openssl x509 -req -days 365 -in "$FILE_CERT_NAME.csr" -signkey "$FILE_CERT_NAME.key" -out "$FILE_CERT_NAME.crt" -extfile "self-signed-cert.ext"
fi

32
gupm.json Normal file
View file

@ -0,0 +1,32 @@
{
"author": "Yann Stepienik",
"cli": {
"aliases": {
"start": "build/bin",
"certificate": "sh generate-certificate.sh"
}
},
"dependencies": {
"default": {
"go://github.com/go-playground/validator/v10": "master",
"go://github.com/gorilla/mux": "master",
"go://github.com/imdario/mergo": "master",
"go://github.com/joho/godotenv": "master",
"go://github.com/lib/pq": "master",
"go://github.com/pquerna/ffjson": "master",
"go://go.mongodb.org/mongo-driver": "master",
"go://gopkg.in/ffmt.v1": "v1.5.6",
"npm://@esbuild/linux-x64": "0.16.17",
"npm://@vitejs/plugin-react": "3.1.0",
"npm://react": "18.2.0",
"npm://react-dom": "18.2.0",
"npm://typescript": "4.9.5",
"npm://vite": "4.1.1"
},
"defaultProvider": "go"
},
"description": "MyData file server",
"license": "ISC",
"name": "myFileServer",
"wrapInstallFolder": "src"
}

30
readme.md Normal file
View file

@ -0,0 +1,30 @@
# GUCO Server
blablabla ...
# Installation
blablabala ...
# Build locally
You need [GuPM](https://github.com/azukaar/GuPM) with the [provider-go](https://github.com/azukaar/GuPM-official#provider-go) plugin to run this project.
```
g make
```
# Run locally
First create a file called dev.json with:
```json
{
"MONGODB": "your mongodb connection string"
}
```
```
g build
g start
```

4
self-signed-cert.ext Normal file
View file

@ -0,0 +1,4 @@
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = https-server

59
src/file/copy.go Normal file
View file

@ -0,0 +1,59 @@
package file
import (
"log"
"net/http"
"io"
"encoding/json"
"os"
"../utils"
)
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return out.Close()
}
func FileCopy(w http.ResponseWriter, req *http.Request) {
utils.SetHeaders(w)
fullPath := req.URL.Query().Get("path")
if fullPath == "" {
log.Println("No path specified")
}
filePath := utils.GetRealPath(fullPath)
fullDestination := req.URL.Query().Get("destination")
if fullDestination == "" {
log.Println("No destination specified")
}
destination := utils.GetRealPath(fullDestination)
// copy file to destination
err := copyFile(filePath, destination)
if err != nil {
log.Fatal(err)
}
// return json object
json.NewEncoder(w).Encode(map[string]interface{}{
"Status": "OK",
})
}

31
src/file/delete.go Normal file
View file

@ -0,0 +1,31 @@
package file
import (
"log"
"net/http"
"encoding/json"
"os"
"../utils"
)
func FileDelete(w http.ResponseWriter, req *http.Request) {
utils.SetHeaders(w)
fullPath := req.URL.Query().Get("path")
if fullPath == "" {
log.Println("No path specified")
}
filePath := utils.GetRealPath(fullPath)
// delete file
err := os.Remove(filePath)
if err != nil {
log.Fatal(err)
}
// return json object
json.NewEncoder(w).Encode(map[string]interface{}{
"Status": "OK",
})
}

112
src/file/get.go Normal file
View file

@ -0,0 +1,112 @@
package file
import (
"log"
"net/http"
"io"
"strings"
"os"
// "bytes"
// "mime/multipart"
"bufio"
"strconv"
"../utils"
)
func getExtension(path string) string {
return strings.Split(path, ".")[len(strings.Split(path, ".")) - 1]
}
func getContentType(path string) string {
switch getExtension(path) {
case "html":
return "text/html"
case "css":
return "text/css"
case "js":
return "application/javascript"
case "png":
return "image/png"
case "jpg":
return "image/jpeg"
case "jpeg":
return "image/jpeg"
case "gif":
return "image/gif"
case "svg":
return "image/svg+xml"
case "mp4":
return "video/mp4"
case "mkv":
return "video/x-matroska"
case "webm":
return "video/webm"
case "mp3":
return "audio/mpeg"
case "wav":
return "audio/wav"
case "ogg":
return "audio/ogg"
default:
return "text/plain"
}
}
func FileGet(w http.ResponseWriter, req *http.Request) {
utils.SetHeaders(w)
fullPath := req.URL.Query().Get("path")
if fullPath == "" {
log.Println("No path specified")
}
filePath := utils.GetRealPath(fullPath)
file, err := os.Open(filePath)
if err != nil {
log.Fatal(err)
}
defer file.Close()
/*fileBytes, err := ioutil.ReadAll(file)
if err != nil {
log.Fatal(err)
}*/
// set header content type depending on file type
w.Header().Set("Content-Type", getContentType(filePath))
// multipart file send
if getExtension(filePath) == "mp4" || getExtension(filePath) == "mkv" || getExtension(filePath) == "webm" {
w.Header().Set("Content-Disposition", "attachment; filename=" + filePath)
}
// open stat file
stat, err := os.Stat(filePath)
buffer := bufio.NewReader(file)
// set content-length
w.Header().Set("Content-Length", strconv.FormatInt(stat.Size(), 10))
//copy buffer to client
io.Copy(w, buffer)
/*
// get file stats
fileStats, err := os.Stat(filePath)
// return json object with metadata FileStat and content
json.NewEncoder(w).Encode(map[string]interface{}{
"Metadata": FileStats{
Name: fileStats.Name(),
Path: filePath,
Size: fileStats.Size(),
Mode: fileStats.Mode(),
ModTime: fileStats.ModTime(),
IsDir: fileStats.IsDir(),
},
"Content": string(file),
})*/
}

55
src/file/list.go Normal file
View file

@ -0,0 +1,55 @@
package file
import (
"log"
"net/http"
"io/ioutil"
"os"
"encoding/json"
"../utils"
)
func FileList(w http.ResponseWriter, req *http.Request) {
utils.SetHeaders(w)
fullPath := req.URL.Query().Get("path")
if fullPath == "" {
log.Println("No path specified")
}
filePath := utils.GetRealPath(fullPath)
files, err := ioutil.ReadDir(filePath)
if err != nil {
log.Fatal(err)
}
// get folder stats
folderStats, err := os.Stat(filePath)
// add file FileStats to json object
var fileStats [](utils.FileStats)
for _, file := range files {
fileStats = append(fileStats, utils.FileStats{
Name: file.Name(),
Path: fullPath + "/" + file.Name(),
Size: file.Size(),
Mode: file.Mode(),
ModTime: file.ModTime(),
IsDir: file.IsDir(),
})
}
// return json object
// return json object with metadata FileStat and content
json.NewEncoder(w).Encode(map[string]interface{}{
"Metadata": utils.FileStats{
Name: folderStats.Name(),
Size: folderStats.Size(),
Mode: folderStats.Mode(),
ModTime: folderStats.ModTime(),
IsDir: folderStats.IsDir(),
},
"Content": fileStats,
})
}

39
src/file/move.go Normal file
View file

@ -0,0 +1,39 @@
package file
import (
"log"
"net/http"
"encoding/json"
"os"
"../utils"
)
func FileMove(w http.ResponseWriter, req *http.Request) {
utils.SetHeaders(w)
fullPath := req.URL.Query().Get("path")
if fullPath == "" {
log.Println("No path specified")
}
filePath := utils.GetRealPath(fullPath)
fullDestination := req.URL.Query().Get("destination")
if fullDestination == "" {
log.Println("No destination specified")
}
destination := utils.GetRealPath(fullDestination)
// copy file to destination
err := os.Rename(filePath, destination)
if err != nil {
log.Fatal(err)
}
// return json object
json.NewEncoder(w).Encode(map[string]interface{}{
"Status": "OK",
})
}

106
src/index.go Normal file
View file

@ -0,0 +1,106 @@
package main
import (
"net/http"
"./utils"
"./file"
"./user"
"github.com/gorilla/mux"
"log"
"os"
"strings"
)
var tlsCert = "localcert.crt"
var tlsKey= "localcert.key"
var serverPortHTTP = os.Getenv("HTTP_PORT")
var serverPortHTTPS = os.Getenv("HTTPS_PORT")
func startHTTPServer(router *mux.Router) {
log.Println("Listening to HTTP on :" + serverPortHTTP)
err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, router)
if err != nil {
log.Fatal(err)
}
}
func startHTTPSServer(router *mux.Router) {
// redirect http to https
go (func () {
err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// change port in host
if strings.HasSuffix(r.Host, ":" + serverPortHTTP) {
if serverPortHTTPS != "443" {
r.Host = r.Host[:len(r.Host)-len(":" + serverPortHTTP)] + ":" + serverPortHTTPS
} else {
r.Host = r.Host[:len(r.Host)-len(":" + serverPortHTTP)]
}
}
http.Redirect(w, r, "https://"+r.Host+r.URL.String(), http.StatusMovedPermanently)
}))
if err != nil {
log.Fatal(err)
}
})()
log.Println("Listening to HTTP on :" + serverPortHTTP)
log.Println("Listening to HTTPS on :" + serverPortHTTPS)
// start https server
err := http.ListenAndServeTLS("0.0.0.0:" + serverPortHTTPS, tlsCert, tlsKey, router)
if err != nil {
log.Fatal(err)
}
}
func main() {
log.Println("Starting...")
if serverPortHTTP == "" {
serverPortHTTP = "80"
}
if serverPortHTTPS == "" {
serverPortHTTPS = "443"
}
router := mux.NewRouter().StrictSlash(true)
utils.DB()
defer utils.Disconnect()
router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
router.HandleFunc("/file/list", file.FileList)
router.HandleFunc("/file/get", file.FileGet)
router.HandleFunc("/file/delete", file.FileDelete)
router.HandleFunc("/file/copy", file.FileCopy)
router.HandleFunc("/file/move", file.FileMove)
router.HandleFunc("/user/login", user.UserLogin)
// router.HandleFunc("/user/register", user.UserRegister)
// router.HandleFunc("/user/edit", )
// router.HandleFunc("/user/delete", )
// router.HandleFunc("/config/get", )
// router.HandleFunc("/config/set", )
// router.HandleFunc("/db", )
if utils.FileExists(tlsCert) && utils.FileExists(tlsKey) {
log.Println("TLS certificate found, starting HTTPS servers and redirecting HTTP to HTTPS")
startHTTPSServer(router)
} else {
log.Println("No TLS certificate found, starting HTTP server only")
startHTTPServer(router)
}
}

28
src/user/login.go Normal file
View file

@ -0,0 +1,28 @@
package user
import (
"net/http"
"log"
"encoding/json"
"../utils"
)
func UserLogin(w http.ResponseWriter, req *http.Request) {
utils.SetHeaders(w)
if(req.Method == "POST") {
nickname := req.FormValue("nickname")
password := req.FormValue("password")
log.Println("UserLogin: nickname: " + nickname)
log.Println("UserLogin: password: " + password) // im just testing ok dont panic
json.NewEncoder(w).Encode(map[string]interface{}{
"Status": "OK",
})
} else {
log.Println("UserLogin: Method not allowed" + req.Method)
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
}

43
src/user/register.go Normal file
View file

@ -0,0 +1,43 @@
package user
import (
"net/http"
"log"
// "io"
// "os"
"encoding/json"
"../utils"
)
func UserRegister(w http.ResponseWriter, req *http.Request) {
utils.SetHeaders(w)
if(req.Method == "GET") {
// sedn form
}
if(req.Method == "POST") {
// check origin
// check password strength
user := &utils.User{
Nickname: req.FormValue("nickname"),
Password: req.FormValue("password"),
Role: (utils.Role)(req.FormValue("role")),
}
err := utils.Validate.Struct(user)
if(err != nil) {
log.Fatal(err)
}
req.ParseForm()
}
// return json object
json.NewEncoder(w).Encode(map[string]interface{}{
"Status": "OK",
})
}

53
src/utils/config.go Normal file
View file

@ -0,0 +1,53 @@
package utils
import (
"context"
"log"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/imdario/mergo"
)
type GucoConfiguration struct {
Test string
Caca string
}
var defaultConfig = GucoConfiguration{
Test: "test",
Caca: "prout",
}
func GetConfigs() GucoConfiguration {
c := GetCollection("GUCO", "Configurations")
config := GucoConfiguration{}
err := c.FindOne(context.TODO(), bson.M{"_id": "GUCO"}).Decode(&config)
if err == mongo.ErrNoDocuments {
log.Println("Record does not exist")
} else if err != nil {
log.Fatal(err)
}
mergo.Merge(&config, defaultConfig)
return config;
}
func SetConfig(config GucoConfiguration) {
currentConfig := GetConfigs()
mergo.Merge(&config, currentConfig)
c := GetCollection("GUCO", "Configurations")
opts := options.Update().SetUpsert(true)
filter := bson.D{{"_id", "GUCO"}}
update := bson.D{{"$set", config}}
_, err := c.UpdateOne(context.Background(), filter, update, opts)
if err != nil {
log.Fatal(err)
}
}

54
src/utils/db.go Normal file
View file

@ -0,0 +1,54 @@
package utils
import (
"context"
"log"
"os"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
var client *mongo.Client
func DB() {
log.Println("Connecting to the database...")
uri := os.Getenv("MONGODB") + "/?retryWrites=true&w=majority"
var err error
client, err = mongo.Connect(context.TODO(), options.Client().ApplyURI(uri))
if err != nil {
log.Fatal(err)
}
defer func() {
}()
// Ping the primary
if err := client.Ping(context.TODO(), readpref.Primary()); err != nil {
log.Fatal(err)
}
log.Println("Successfully connected to the database.")
}
func Disconnect() {
if err := client.Disconnect(context.TODO()); err != nil {
log.Fatal(err)
}
}
func GetCollection(applicationId string, collection string) *mongo.Collection {
name := os.Getenv("MONGODB_NAME"); if name == "" {
name = "GUCO"
}
log.Println("Getting collection " + applicationId + "_" + collection + " from database " + name)
c := client.Database(name).Collection(applicationId + "_" + collection)
return c
}
// func query(q string) (*sql.Rows, error) {
// return db.Query(q)
// }

29
src/utils/types.go Normal file
View file

@ -0,0 +1,29 @@
package utils
import (
"os"
"time"
)
type Role string
const (
GUEST string = "GUEST"
USER = "USER"
ADMIN = "ADMIN"
)
type FileStats struct {
Name string
Path string
Size int64
Mode os.FileMode
ModTime time.Time
IsDir bool
}
type User struct {
Nickname string `validate:"required"`
Password string `validate:"required"`
Role Role `validate:"required"`
}

38
src/utils/utils.go Normal file
View file

@ -0,0 +1,38 @@
package utils
import (
"strings"
"log"
"os"
"net/http"
)
func GetRealPath(fullPath string) string {
var dataPathsObject = map[string]string{
"data": "/mnt/d/",
"diskE": "/mnt/e/",
}
path := dataPathsObject[strings.Split(fullPath, "/")[0]]
if path == "" {
log.Println("No path specified")
}
return path + strings.Join(strings.Split(fullPath, "/")[1:], "/")
}
func SetHeaders(w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
func FileExists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
log.Println(err)
return false
}

5
src/utils/validator.go Normal file
View file

@ -0,0 +1,5 @@
package utils
import "github.com/go-playground/validator/v10"
var Validate = validator.New()

9
vite.config.js Normal file
View file

@ -0,0 +1,9 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
root: 'client',
outDir: 'static',
})