summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlaster4385 <blaster4385@tablaster.dev>2024-04-24 23:30:06 +0530
committerBlaster4385 <venkatesh@tablaster.dev>2024-07-23 01:19:30 +0530
commitaa9165a879b111df471affadcbc0f85573ea8e78 (patch)
tree86ef853a9b31a830cd89ef5c0f1006ba26a465e3
Initial backend
-rw-r--r--server/go.mod9
-rw-r--r--server/go.sum6
-rw-r--r--server/main.go220
3 files changed, 235 insertions, 0 deletions
diff --git a/server/go.mod b/server/go.mod
new file mode 100644
index 0000000..db064f2
--- /dev/null
+++ b/server/go.mod
@@ -0,0 +1,9 @@
+module fileshare
+
+go 1.22.2
+
+require (
+ github.com/gorilla/mux v1.8.1 // indirect
+ github.com/lib/pq v1.10.9 // indirect
+ github.com/rs/cors v1.10.1 // indirect
+)
diff --git a/server/go.sum b/server/go.sum
new file mode 100644
index 0000000..74cea27
--- /dev/null
+++ b/server/go.sum
@@ -0,0 +1,6 @@
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
+github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
+github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
+github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
diff --git a/server/main.go b/server/main.go
new file mode 100644
index 0000000..a4520b9
--- /dev/null
+++ b/server/main.go
@@ -0,0 +1,220 @@
+package main
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "database/sql"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/gorilla/mux"
+ _ "github.com/lib/pq"
+ "github.com/rs/cors"
+)
+
+const (
+ maxUploadSize = 10 * 1024 * 1024 // 10 MB
+ keySize = 32
+ nonceSize = 12
+)
+
+var db *sql.DB
+
+func main() {
+ db = initDB()
+ defer db.Close()
+
+ router := mux.NewRouter()
+ router.HandleFunc("/upload", handleUpload).Methods("POST")
+ router.HandleFunc("/download/{id}", handleDownload).Methods("GET")
+
+ handler := cors.New(cors.Options{
+ AllowedOrigins: []string{"*"},
+ AllowedMethods: []string{"GET", "POST"},
+ AllowedHeaders: []string{"*"},
+ }).Handler(router)
+
+ http.ListenAndServe(":8080", handler)
+}
+
+func initDB() *sql.DB {
+ db, err := sql.Open("postgres", "postgres://file:password@localhost/filedb?sslmode=disable")
+ if err != nil {
+ panic(err)
+ }
+ if err := createFilesTable(db); err != nil {
+ panic(err)
+ }
+ return db
+}
+
+func createFilesTable(db *sql.DB) error {
+ _, err := db.Exec(`
+ CREATE TABLE IF NOT EXISTS files (
+ id TEXT PRIMARY KEY,
+ name TEXT,
+ data BYTEA
+ );
+ `)
+ return err
+}
+
+func handleUpload(w http.ResponseWriter, r *http.Request) {
+ r.ParseMultipartForm(maxUploadSize)
+
+ key, err := generateRandomKey()
+ if err != nil {
+ handleError(w, err, http.StatusInternalServerError)
+ return
+ }
+
+ file, handler, err := r.FormFile("file")
+ if err != nil {
+ handleError(w, errors.New("error parsing uploaded file"), http.StatusBadRequest)
+ return
+ }
+ defer file.Close()
+
+ id := generateID()
+
+ encryptedData, err := encryptFile(file, key)
+ if err != nil {
+ handleError(w, errors.New("error encrypting file"), http.StatusInternalServerError)
+ return
+ }
+
+ err = storeFileInDB(id, handler.Filename, encryptedData)
+ if err != nil {
+ handleError(w, errors.New("error storing file in database"), http.StatusInternalServerError)
+ return
+ }
+
+ encodedKey := hex.EncodeToString(key)
+ url := fmt.Sprintf("/download/%s?key=%s", id, encodedKey)
+ fmt.Fprintf(w, "File uploaded successfully. Download URL: %s", url)
+}
+
+func handleDownload(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ id := vars["id"]
+
+ keyHex := r.URL.Query().Get("key")
+ key, err := hex.DecodeString(keyHex)
+ if err != nil {
+ handleError(w, errors.New("invalid key"), http.StatusBadRequest)
+ return
+ }
+
+ fileName, encryptedData, err := getFileFromDB(id)
+ if err != nil {
+ handleError(w, err, http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName))
+
+ plaintext, err := decryptFile(encryptedData, key)
+ if err != nil {
+ handleError(w, errors.New("error decrypting file"), http.StatusInternalServerError)
+ return
+ }
+
+ if _, err := w.Write(plaintext); err != nil {
+ handleError(w, errors.New("error writing response"), http.StatusInternalServerError)
+ return
+ }
+}
+
+func storeFileInDB(id, fileName string, encryptedData []byte) error {
+ tx, err := db.Begin()
+ if err != nil {
+ return err
+ }
+ defer tx.Rollback()
+
+ _, err = tx.Exec("INSERT INTO files (id, name, data) VALUES ($1, $2, $3)", id, fileName, encryptedData)
+ if err != nil {
+ return err
+ }
+
+ return tx.Commit()
+}
+
+func getFileFromDB(id string) (fileName string, encryptedData []byte, err error) {
+ err = db.QueryRow("SELECT name, data FROM files WHERE id = $1", id).Scan(&fileName, &encryptedData)
+ if err == sql.ErrNoRows {
+ return "", nil, errors.New("file not found")
+ }
+ return fileName, encryptedData, err
+}
+
+func handleError(w http.ResponseWriter, err error, code int) {
+ http.Error(w, err.Error(), code)
+}
+
+func encryptFile(in io.Reader, key []byte) ([]byte, error) {
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ aesgcm, err := cipher.NewGCM(block)
+ if err != nil {
+ return nil, err
+ }
+
+ nonce := make([]byte, nonceSize)
+ if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
+ return nil, err
+ }
+
+ plaintext, err := io.ReadAll(in)
+ if err != nil {
+ return nil, err
+ }
+
+ ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)
+ return append(nonce, ciphertext...), nil
+}
+
+func decryptFile(encryptedData []byte, key []byte) ([]byte, error) {
+ block, err := aes.NewCipher(key)
+ if err != nil {
+ return nil, err
+ }
+
+ aesgcm, err := cipher.NewGCM(block)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(encryptedData) < nonceSize {
+ return nil, errors.New("ciphertext too short")
+ }
+
+ nonce, ciphertext := encryptedData[:nonceSize], encryptedData[nonceSize:]
+ plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ return plaintext, nil
+}
+
+func generateRandomKey() ([]byte, error) {
+ key := make([]byte, keySize)
+ _, err := rand.Read(key)
+ return key, err
+}
+
+func generateID() string {
+ b := make([]byte, 16)
+ if _, err := rand.Read(b); err != nil {
+ panic(err)
+ }
+ return hex.EncodeToString(b)
+}