mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
259 lines
7.0 KiB
Go
259 lines
7.0 KiB
Go
package resource
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/thecodingmachine/gotenberg/internal/pkg/normalize"
|
|
"github.com/thecodingmachine/gotenberg/internal/pkg/xassert"
|
|
"github.com/thecodingmachine/gotenberg/internal/pkg/xerror"
|
|
"github.com/thecodingmachine/gotenberg/internal/pkg/xlog"
|
|
)
|
|
|
|
/*
|
|
TemporaryDirectory is the directory
|
|
where all the resources directory
|
|
are located.
|
|
*/
|
|
const TemporaryDirectory string = "tmp"
|
|
|
|
// Resource helps managing
|
|
// arguments and files for a conversion.
|
|
type Resource struct {
|
|
logger xlog.Logger
|
|
dirPath string
|
|
customHeaders map[string]string
|
|
args map[ArgKey]string
|
|
files map[string]file
|
|
}
|
|
|
|
// New creates a Resource where its files will
|
|
// be located in the given directory name.
|
|
func New(logger xlog.Logger, directoryName string) (Resource, error) {
|
|
const op string = "resource.New"
|
|
resolver := func() (string, error) {
|
|
dirPath := fmt.Sprintf("%s/%s", TemporaryDirectory, directoryName)
|
|
if err := os.MkdirAll(dirPath, 0755); err != nil {
|
|
return "", err
|
|
}
|
|
absDirPath, err := filepath.Abs(dirPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return absDirPath, nil
|
|
}
|
|
dirPath, err := resolver()
|
|
if err != nil {
|
|
return Resource{}, xerror.New(op, err)
|
|
}
|
|
logger.DebugOpf(op, "resource directory '%s' created", directoryName)
|
|
return Resource{
|
|
logger: logger,
|
|
dirPath: dirPath,
|
|
customHeaders: make(map[string]string),
|
|
args: make(map[ArgKey]string),
|
|
files: make(map[string]file),
|
|
}, nil
|
|
}
|
|
|
|
// Close removes the working directory of the
|
|
// Resource if it exists.
|
|
func (r Resource) Close() error {
|
|
const op string = "resource.Resource.Close"
|
|
if _, err := os.Stat(r.dirPath); os.IsNotExist(err) {
|
|
r.logger.DebugOpf(op, "resource directory '%s' does not exist, nothing to remove", r.dirPath)
|
|
return nil
|
|
}
|
|
if err := os.RemoveAll(r.dirPath); err != nil {
|
|
return xerror.New(op, err)
|
|
}
|
|
r.logger.DebugOpf(op, "resource directory '%s' removed", r.dirPath)
|
|
return nil
|
|
}
|
|
|
|
// WithCustomHTTPHeader add a new custom header to the Resource.
|
|
// Given key should be in canonical format.
|
|
func (r *Resource) WithCustomHTTPHeader(key string, value string) {
|
|
const op string = "resource.Resource.WithCustomHTTPHeader"
|
|
// should already be in canonical format.
|
|
canonicalKey := http.CanonicalHeaderKey(key)
|
|
if strings.Contains(canonicalKey, RemoteURLCustomHTTPHeaderCanonicalBaseKey) ||
|
|
strings.Contains(canonicalKey, WebhookURLCustomHTTPHeaderCanonicalBaseKey) {
|
|
r.customHeaders[canonicalKey] = value
|
|
r.logger.DebugOpf(op, "added '%s' with value '%s' to resource custom HTTP headers", canonicalKey, value)
|
|
return
|
|
}
|
|
r.logger.DebugOpf(op, "skipping '%s' as it is not a custom HTTP header...", canonicalKey)
|
|
}
|
|
|
|
// WithArg add a new argument to the Resource.
|
|
func (r *Resource) WithArg(key ArgKey, value string) {
|
|
const op string = "resource.Resource.WithArg"
|
|
r.args[key] = value
|
|
r.logger.DebugOpf(op, "added '%s' with value '%s' to resource args", key, value)
|
|
}
|
|
|
|
// WithFile add a new file to the Resource.
|
|
func (r *Resource) WithFile(filename string, in io.Reader) error {
|
|
const op string = "resource.Resource.WithFile"
|
|
resolver := func() error {
|
|
// see https://github.com/thecodingmachine/gotenberg/issues/104.
|
|
normalized, err := normalize.String(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fpath := fmt.Sprintf("%s/%s", r.dirPath, normalized)
|
|
file := file{fpath: fpath}
|
|
if err := file.write(in); err != nil {
|
|
return err
|
|
}
|
|
r.files[filename] = file
|
|
r.logger.DebugOpf(op, "resource file '%s' created", filename)
|
|
return nil
|
|
}
|
|
if err := resolver(); err != nil {
|
|
return xerror.New(op, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DirPath returns the directory path
|
|
// of the Resource.
|
|
func (r Resource) DirPath() string {
|
|
return r.dirPath
|
|
}
|
|
|
|
// HasArg returns true if given key exists
|
|
// among the Resource and its value is not empty.
|
|
func (r Resource) HasArg(key ArgKey) bool {
|
|
if v, ok := r.args[key]; ok {
|
|
return v != ""
|
|
}
|
|
return false
|
|
}
|
|
|
|
/*
|
|
StringArg returns the value of the
|
|
argument identified by given key.
|
|
|
|
It works in the same manner as xassert.String.
|
|
*/
|
|
func (r Resource) StringArg(key ArgKey, defaultValue string, rules ...xassert.RuleString) (string, error) {
|
|
const op string = "resource.Resource.StringArg"
|
|
result, err := xassert.String(string(key), r.args[key], defaultValue, rules...)
|
|
if err != nil {
|
|
return result, xerror.New(op, err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
/*
|
|
Int64Arg returns the int64 representation of the
|
|
argument identified by given key.
|
|
|
|
It works in the same manner as xassert.Int64.
|
|
*/
|
|
func (r Resource) Int64Arg(key ArgKey, defaultValue int64, rules ...xassert.RuleInt64) (int64, error) {
|
|
const op string = "resource.Resource.Int64Arg"
|
|
result, err := xassert.Int64(string(key), r.args[key], defaultValue, rules...)
|
|
if err != nil {
|
|
return result, xerror.New(op, err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
/*
|
|
Float64Arg returns the float64 representation of the
|
|
argument identified by given key.
|
|
|
|
It works in the same manner as xassert.Float64.
|
|
*/
|
|
func (r Resource) Float64Arg(key ArgKey, defaultValue float64, rules ...xassert.RuleFloat64) (float64, error) {
|
|
const op string = "resource.Resource.Float64Arg"
|
|
result, err := xassert.Float64(string(key), r.args[key], defaultValue, rules...)
|
|
if err != nil {
|
|
return result, xerror.New(op, err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
/*
|
|
BoolArg returns the boolean representation of the
|
|
argument identified by given key.
|
|
|
|
It works in the same manner as xassert.Bool.
|
|
*/
|
|
func (r Resource) BoolArg(key ArgKey, defaultValue bool) (bool, error) {
|
|
const op string = "resource.Resource.BoolArg"
|
|
result, err := xassert.Bool(string(key), r.args[key], defaultValue)
|
|
if err != nil {
|
|
return result, xerror.New(op, err)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// Fpath returns the path of the given filename.
|
|
// This filename should exist whithin the Resource.
|
|
func (r Resource) Fpath(filename string) (string, error) {
|
|
const op string = "resource.Resource.Fpath"
|
|
file, ok := r.files[filename]
|
|
if !ok {
|
|
return "", xerror.Invalid(
|
|
op,
|
|
fmt.Sprintf("resource file '%s' does not exist", filename),
|
|
nil,
|
|
)
|
|
}
|
|
return file.fpath, nil
|
|
}
|
|
|
|
/*
|
|
Fpaths returns the paths of the files
|
|
having one of the given file extensions.
|
|
|
|
It should found at least one path.
|
|
*/
|
|
func (r Resource) Fpaths(exts ...string) ([]string, error) {
|
|
const op string = "resource.Resource.Fpaths"
|
|
var fpaths []string
|
|
for filename, file := range r.files {
|
|
for _, ext := range exts {
|
|
if filepath.Ext(filename) == ext {
|
|
fpaths = append(fpaths, file.fpath)
|
|
}
|
|
}
|
|
}
|
|
if len(fpaths) == 0 {
|
|
return nil, xerror.Invalid(
|
|
op,
|
|
fmt.Sprintf("no resource file found for extensions '%v'", exts),
|
|
nil,
|
|
)
|
|
}
|
|
return fpaths, nil
|
|
}
|
|
|
|
/*
|
|
Fcontent returns the string content of the
|
|
given filename.
|
|
|
|
If filename does not exist within the Resource,
|
|
returns the default value.
|
|
*/
|
|
func (r Resource) Fcontent(filename, defaultValue string) (string, error) {
|
|
const op string = "resource.Resource.Fcontent"
|
|
file, ok := r.files[filename]
|
|
if !ok {
|
|
return defaultValue, nil
|
|
}
|
|
content, err := file.content()
|
|
if err != nil {
|
|
return "", xerror.New(op, err)
|
|
}
|
|
return content, nil
|
|
}
|