completed update to SetAttachment (not yet tested)

This commit is contained in:
2026-01-20 17:22:40 -07:00
parent 31b4a5bbd2
commit 664525ea36
2 changed files with 86 additions and 3 deletions
+55
View File
@@ -12,9 +12,12 @@ package config
import ( import (
_ "embed" _ "embed"
"errors"
"fmt" "fmt"
"maps" "maps"
"os" "os"
"regexp"
"strconv"
argparse "github.com/alexflint/go-arg" argparse "github.com/alexflint/go-arg"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@@ -95,6 +98,11 @@ type AmConfig struct {
} `yaml:"posting"` } `yaml:"posting"`
} }
type AmConfigComputed struct {
UploadMaxSize int32
UploadNoCompress map[string]bool
}
//go:embed default.yaml //go:embed default.yaml
var defaultConfigData []byte var defaultConfigData []byte
@@ -104,6 +112,9 @@ var defaultConfig AmConfig
// GlobalConfig holds the global configuration. // GlobalConfig holds the global configuration.
var GlobalConfig AmConfig var GlobalConfig AmConfig
// GlobalComputedConfig holds the computed values based on GlobalConfig.
var GlobalComputedConfig AmConfigComputed
// init prepares the default configuration for the application. // init prepares the default configuration for the application.
func init() { func init() {
if err := yaml.Unmarshal(defaultConfigData, &defaultConfig); err != nil { if err := yaml.Unmarshal(defaultConfigData, &defaultConfig); err != nil {
@@ -125,6 +136,14 @@ func overlayString(loaded string, defaulted string) string {
return loaded return loaded
} }
/* overlayString is a helper that takes a loaded or defaulted string array and returns it. (It merges the two
* if two different arrays are specified.)
* Parameters:
* loaded - The array loaded from a configuration file.
* defaulted - The default value of this array.
* Returns:
* Merged version of the two arrays.
*/
func overlayStringArray(loaded, defaulted []string) []string { func overlayStringArray(loaded, defaulted []string) []string {
m := make(map[string]bool) m := make(map[string]bool)
for _, s := range defaulted { for _, s := range defaulted {
@@ -191,6 +210,31 @@ func overlayConfig(dest *AmConfig, loaded *AmConfig, defaults *AmConfig) {
dest.Posting.Uploads.NoCompressTypes = overlayStringArray(loaded.Posting.Uploads.NoCompressTypes, defaults.Posting.Uploads.NoCompressTypes) dest.Posting.Uploads.NoCompressTypes = overlayStringArray(loaded.Posting.Uploads.NoCompressTypes, defaults.Posting.Uploads.NoCompressTypes)
} }
// parseDataSize converts the data size in bytes, kilobytes, megabytes, or gigabytes to a number value.
func parseDataSize(s string) (int32, error) {
re, err := regexp.Compile(`^\s*(\d+)\s*([KkMmGg]?)[Bb]?`)
if err != nil {
return -1, err
}
m := re.FindStringSubmatch(s)
if m == nil {
return -1, errors.New("invalid value spacified")
}
rc, err := strconv.Atoi(m[1])
if err != nil {
return -1, err
}
switch m[2] {
case "k", "K":
rc *= 1024
case "m", "M":
rc *= (1024 * 1024)
case "g", "G":
rc *= (1024 * 1024 * 1024)
}
return int32(rc), nil
}
// SetupConfig loads the command line arguments, loads the config file, and prepares GlobalConfig. // SetupConfig loads the command line arguments, loads the config file, and prepares GlobalConfig.
func SetupConfig() { func SetupConfig() {
argparse.MustParse(&CommandLine) argparse.MustParse(&CommandLine)
@@ -209,4 +253,15 @@ func SetupConfig() {
} else { } else {
GlobalConfig = defaultConfig // just copy over the defaults GlobalConfig = defaultConfig // just copy over the defaults
} }
// Compute additional values.
tmp, err := parseDataSize(GlobalConfig.Posting.Uploads.MaxSize)
if err != nil {
panic(err.Error())
}
GlobalComputedConfig.UploadMaxSize = tmp
GlobalComputedConfig.UploadNoCompress = make(map[string]bool)
for _, s := range GlobalConfig.Posting.Uploads.NoCompressTypes {
GlobalComputedConfig.UploadNoCompress[s] = true
}
} }
+31 -3
View File
@@ -10,10 +10,14 @@
package database package database
import ( import (
"bytes"
"compress/gzip"
"context" "context"
"errors" "errors"
"fmt" "fmt"
"time" "time"
"git.erbosoft.com/amy/amsterdam/config"
) )
// PostHeader represents the "header" of a post, everything except for its text and attachment. // PostHeader represents the "header" of a post, everything except for its text and attachment.
@@ -115,9 +119,33 @@ func (p *PostHeader) SetAttachment(ctx context.Context, u *User, fileName string
if ai != nil { if ai != nil {
return errors.New("attachment already present for this post") return errors.New("attachment already present for this post")
} }
// TODO if length > config.GlobalComputedConfig.UploadMaxSize {
_, err = amdb.ExecContext(ctx, "INSERT INTO postattach (postid, datalen, filename, mimetype, data) VALUES (?, ?, ?, ?, ?)", return fmt.Errorf("file too large to be attached; maximum size is %s", config.GlobalConfig.Posting.Uploads.MaxSize)
p.PostId, length, fileName, mimeType, data) }
// Compress the data with GZIP if we need to.
var stgmethod int16
var realData []byte
if _, ok := config.GlobalComputedConfig.UploadNoCompress[mimeType]; ok {
realData = data
stgmethod = stgMethodPlain
} else {
buf := new(bytes.Buffer)
w := gzip.NewWriter(buf)
_, err := w.Write(data)
if err == nil {
err = w.Close()
}
if err != nil {
return err
}
realData = buf.Bytes()
stgmethod = stgMethodGZIP
}
// Write to the database.
_, err = amdb.ExecContext(ctx, "INSERT INTO postattach (postid, datalen, filename, mimetype, stgmethod, data) VALUES (?, ?, ?, ?, ?, ?)",
p.PostId, length, fileName, mimeType, stgmethod, realData)
return err return err
} }