initial commit
This commit is contained in:
commit
c21f569144
37 changed files with 3956 additions and 0 deletions
204
routes/file.go
Normal file
204
routes/file.go
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"foobar/database"
|
||||
"foobar/model"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
var maxFileSize int64 = 32 * 1000 * 1000
|
||||
|
||||
func fileUpload(w http.ResponseWriter, r *http.Request) (redirectURL, error) {
|
||||
|
||||
boxURL, err := GetBoxURL(r)
|
||||
if err != nil {
|
||||
return noRedirect, err
|
||||
}
|
||||
|
||||
tx := database.MustBeginTx()
|
||||
defer tx.Rollback()
|
||||
|
||||
box, err := database.SelectBox(boxURL)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) { // this is a new URL the user wants to claim for themselves
|
||||
editCode := r.FormValue("edit_code")
|
||||
if editCode == "" {
|
||||
return noRedirect, newUserError(
|
||||
"Missing edit code. Box can't be created without it",
|
||||
)
|
||||
}
|
||||
var (
|
||||
id = uuid.New()
|
||||
createdAt = time.Now()
|
||||
lastUpdatedAt = createdAt
|
||||
header = readmeTemplate
|
||||
)
|
||||
newBox := model.NewBox(
|
||||
id, boxURL, editCode, header, false, false, createdAt, lastUpdatedAt,
|
||||
)
|
||||
err = database.InsertBox(tx, newBox, editCode, header)
|
||||
if err != nil {
|
||||
return noRedirect, err
|
||||
}
|
||||
box = newBox
|
||||
} else {
|
||||
return noRedirect, err
|
||||
}
|
||||
}
|
||||
|
||||
filesCtx, err := GetFilesFromUploadRequest(r)
|
||||
if err != nil {
|
||||
return noRedirect, err
|
||||
}
|
||||
|
||||
var files []model.File
|
||||
|
||||
for _, file := range filesCtx {
|
||||
|
||||
bytes, err := readFileBytes(file.FileHeader())
|
||||
if err != nil {
|
||||
return noRedirect, err
|
||||
}
|
||||
|
||||
mime := model.FileMime(http.DetectContentType(bytes))
|
||||
|
||||
if file.Filename() == "README.md" {
|
||||
|
||||
editCode := r.FormValue("edit_code")
|
||||
if editCode == "" {
|
||||
return noRedirect, newUserError(
|
||||
"Missing edit code. README can't be updated. " +
|
||||
"If you're not attempting to update the header, we'd " +
|
||||
"suggest changing \"README.md\" to a new filename.",
|
||||
)
|
||||
}
|
||||
|
||||
if editCode != box.EditCode() {
|
||||
return noRedirect, newUserError(
|
||||
"Wrong edit code.",
|
||||
)
|
||||
}
|
||||
|
||||
md := mdToHTML(bytes)
|
||||
|
||||
err := database.UpdateHeader(tx, box.ID(), md)
|
||||
if err != nil {
|
||||
return noRedirect, err
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return noRedirect, err
|
||||
}
|
||||
|
||||
// FIXME: we're ignoring all other uploaded files
|
||||
return redirectURL(fmt.Sprintf("/box/%s", box.Url())), nil
|
||||
|
||||
} else {
|
||||
|
||||
err = copyBytesToDisk(file.ID(), bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
checksum := calculateMD5(bytes)
|
||||
|
||||
newFile := model.NewFile(
|
||||
file.ID(), file.Filename(), file.Size(),
|
||||
file.UploadedDate(), mime, checksum,
|
||||
)
|
||||
|
||||
files = append(files, newFile)
|
||||
}
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
err = database.InsertFile(tx, box.ID(), file)
|
||||
if err != nil {
|
||||
return noRedirect, err
|
||||
}
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return noRedirect, err
|
||||
}
|
||||
|
||||
return redirectURL(fmt.Sprintf("/box/%s/inside", box.Url())), nil
|
||||
}
|
||||
|
||||
func calculateMD5(fileBytes []byte) model.MD5Checksum {
|
||||
return md5.Sum(fileBytes)
|
||||
}
|
||||
|
||||
func readFileBytes(fileHeader *multipart.FileHeader) ([]byte, error) {
|
||||
fileReader, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer fileReader.Close()
|
||||
|
||||
fileBytes, err := io.ReadAll(fileReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileBytes, nil
|
||||
}
|
||||
|
||||
func copyBytesToDisk(fileId uuid.UUID, fileBytes []byte) error {
|
||||
fileWriter, err := os.Create(filepath.Join(
|
||||
staticDir, "box", fmt.Sprintf(fileId.String())),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fileWriter.Close()
|
||||
|
||||
_, err = io.Copy(fileWriter, bytes.NewReader(fileBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetFilesFromUploadRequest(r *http.Request) ([]model.FileUploadContext, error) {
|
||||
|
||||
err := r.ParseMultipartForm(int64(maxFileSize))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
headers, ok := r.MultipartForm.File["file"]
|
||||
if !ok {
|
||||
return nil, newUserError("no file")
|
||||
}
|
||||
|
||||
var (
|
||||
files []model.FileUploadContext
|
||||
)
|
||||
|
||||
for _, header := range headers {
|
||||
var (
|
||||
id = uuid.New()
|
||||
uploadedDate = time.Now()
|
||||
size = header.Size
|
||||
filename = model.Filename(header.Filename)
|
||||
mime = model.MimeFromHeader(header.Header)
|
||||
)
|
||||
|
||||
fileUpload := model.NewFileUploadContext(id, uploadedDate, size, filename, mime, header)
|
||||
files = append(files, fileUpload)
|
||||
}
|
||||
return files, nil
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue