Files
yoink-go/comic/archive.go
Bryan Bailey 89a5013fb2 fix(web): add comic delete UI and fix container Cloudflare bypass for #6
- Add delete button (SVG X, hover-reveal) and confirmation modal to comic cards
- Add DELETE /api/comics/delete endpoint with path traversal protection
- Fix container downloads: delegate Cloudflare-blocked requests to FlareSolverr
  (headless Chrome sidecar) instead of retrying with Go HTTP client, whose Linux
  TCP fingerprint is flagged by Cloudflare even with network_mode: host
- Add FlareSolverr service to docker-compose; inject FLARESOLVERR_URL env var
- Add diagnostic logging to BatcaveBizMarkup request flow
- Trim URL whitespace before storing in download job
- Guard Archive() against empty filelist; fix runJob error-check ordering
2026-03-12 09:41:03 -04:00

116 lines
2.0 KiB
Go

package comic
import (
"archive/zip"
"io"
"log"
"os"
"path/filepath"
"strings"
)
type ArchiveError struct {
Message string
Code int
}
func (a ArchiveError) Error() string {
return a.Message
}
// Archive creates a zip archive of the comic files.
//
// It takes no parameters.
// Returns an error if the operation fails.
func (c *Comic) Archive() error {
if len(c.Filelist) == 0 {
return nil
}
outputPath := filepath.Join(c.LibraryPath, c.Title, c.Title+".cbz")
err := os.MkdirAll(filepath.Dir(outputPath), os.ModePerm)
if err != nil {
return ArchiveError{
Message: "error creating directory",
Code: 1,
}
}
zipFile, err := os.Create(outputPath)
if err != nil {
return err
}
defer zipFile.Close()
zwriter := zip.NewWriter(zipFile)
defer zwriter.Close()
sourcePath := filepath.Join(c.LibraryPath, c.Title)
err = filepath.Walk(
sourcePath,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return ArchiveError{
Message: "error walking archive",
Code: 1,
}
}
if info.IsDir() {
return nil
}
ext := strings.ToLower(filepath.Ext(path))
if ext != ".jpg" && ext != ".jpeg" && ext != ".png" {
return nil
}
relPath, err := filepath.Rel(sourcePath, path)
if err != nil {
return ArchiveError{
Message: "error walking archive",
Code: 1,
}
}
file, err := os.Open(path)
if err != nil {
return ArchiveError{
Message: "error walking archive",
Code: 1,
}
}
defer file.Close()
zipEntry, err := zwriter.Create(relPath)
if err != nil {
return ArchiveError{
Message: "error walking archive",
Code: 1,
}
}
_, err = io.Copy(zipEntry, file)
if err != nil {
return ArchiveError{
Message: "error walking archive",
Code: 1,
}
}
return nil
},
)
if err != nil {
return ArchiveError{
Message: "error writing files to archive",
Code: 1,
}
}
log.Printf("Created archive\n: %s", outputPath)
return nil
}