2018-10-25 13:51:47 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/knadh/listmonk/subimporter"
|
|
|
|
"github.com/labstack/echo"
|
|
|
|
)
|
|
|
|
|
|
|
|
// reqImport represents file upload import params.
|
|
|
|
type reqImport struct {
|
|
|
|
Delim string `json:"delim"`
|
|
|
|
OverrideStatus bool `json:"override_status"`
|
|
|
|
ListIDs []int `json:"lists"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleImportSubscribers handles the uploading and bulk importing of
|
|
|
|
// a ZIP file of one or more CSV files.
|
|
|
|
func handleImportSubscribers(c echo.Context) error {
|
|
|
|
app := c.Get("app").(*App)
|
|
|
|
|
|
|
|
// Is an import already running?
|
|
|
|
if app.Importer.GetStats().Status == subimporter.StatusImporting {
|
|
|
|
return echo.NewHTTPError(http.StatusBadRequest,
|
|
|
|
"An import is already running. Wait for it to finish or stop it before trying again.")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarsal the JSON params.
|
|
|
|
var r reqImport
|
|
|
|
if err := json.Unmarshal([]byte(c.FormValue("params")), &r); err != nil {
|
|
|
|
return echo.NewHTTPError(http.StatusBadRequest,
|
|
|
|
fmt.Sprintf("Invalid `params` field: %v", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(r.Delim) != 1 {
|
|
|
|
return echo.NewHTTPError(http.StatusBadRequest,
|
|
|
|
"`delim` should be a single character")
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := c.FormFile("file")
|
|
|
|
if err != nil {
|
|
|
|
return echo.NewHTTPError(http.StatusBadRequest,
|
|
|
|
fmt.Sprintf("Invalid `file`: %v", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
src, err := file.Open()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer src.Close()
|
|
|
|
|
|
|
|
out, err := ioutil.TempFile("", "listmonk")
|
|
|
|
if err != nil {
|
|
|
|
return echo.NewHTTPError(http.StatusInternalServerError,
|
|
|
|
fmt.Sprintf("Error copying uploaded file: %v", err))
|
|
|
|
}
|
|
|
|
defer out.Close()
|
|
|
|
|
|
|
|
if _, err = io.Copy(out, src); err != nil {
|
|
|
|
return echo.NewHTTPError(http.StatusInternalServerError,
|
|
|
|
fmt.Sprintf("Error copying uploaded file: %v", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the importer session.
|
|
|
|
impSess, err := app.Importer.NewSession(file.Filename,
|
|
|
|
r.OverrideStatus,
|
|
|
|
r.ListIDs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
go impSess.Start()
|
|
|
|
|
|
|
|
// For now, we only extract 1 CSV from the ZIP. Handling async CSV
|
|
|
|
// imports is more trouble than it's worth.
|
|
|
|
dir, files, err := impSess.ExtractZIP(out.Name(), 1)
|
|
|
|
if err != nil {
|
|
|
|
return echo.NewHTTPError(http.StatusInternalServerError,
|
|
|
|
fmt.Sprintf("Error extracting ZIP file: %v", err))
|
2018-10-30 13:14:51 +00:00
|
|
|
} else if len(files) == 0 {
|
|
|
|
return echo.NewHTTPError(http.StatusBadRequest, "No CSV files found to import.")
|
2018-10-25 13:51:47 +00:00
|
|
|
}
|
|
|
|
go impSess.LoadCSV(dir+"/"+files[0], rune(r.Delim[0]))
|
|
|
|
|
|
|
|
return c.JSON(http.StatusOK, okResp{app.Importer.GetStats()})
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleGetImportSubscribers returns import statistics.
|
|
|
|
func handleGetImportSubscribers(c echo.Context) error {
|
|
|
|
var (
|
|
|
|
app = c.Get("app").(*App)
|
|
|
|
s = app.Importer.GetStats()
|
|
|
|
)
|
|
|
|
|
|
|
|
return c.JSON(http.StatusOK, okResp{s})
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleGetImportSubscriberLogs returns import statistics.
|
|
|
|
func handleGetImportSubscriberLogs(c echo.Context) error {
|
|
|
|
app := c.Get("app").(*App)
|
|
|
|
return c.JSON(http.StatusOK, okResp{string(app.Importer.GetLogs())})
|
|
|
|
}
|
|
|
|
|
|
|
|
// handleStopImportSubscribers sends a stop signal to the importer.
|
|
|
|
// If there's an ongoing import, it'll be stopped, and if an import
|
|
|
|
// is finished, it's state is cleared.
|
|
|
|
func handleStopImportSubscribers(c echo.Context) error {
|
|
|
|
app := c.Get("app").(*App)
|
|
|
|
app.Importer.Stop()
|
|
|
|
|
|
|
|
return c.JSON(http.StatusOK, okResp{app.Importer.GetStats()})
|
|
|
|
}
|