package template import ( "database/sql" "encoding/json" "errors" "github.com/labstack/echo/v4" "io" "net/http" "tesseract/internal/apierror" "tesseract/internal/service" ) type createTemplateRequestBody struct { Description string `json:"description"` Content string `json:"content"` Documentation string `json:"documentation"` BaseTemplate string `json:"baseTemplate"` } type postTemplateRequestBody struct { Name *string `json:"name"` Description *string `json:"description"` Files []templateFile `json:"files"` ImageTag *string `json:"imageTag"` BuildArgs map[string]*string `json:"buildArgs"` } func fetchAllTemplates(c echo.Context) error { mgr := templateManagerFrom(c) templates, err := mgr.findAllTemplates(c.Request().Context()) if err != nil { return err } return c.JSON(http.StatusOK, templates) } func fetchBaseTemplates(c echo.Context) error { mgr := templateManagerFrom(c) templates, err := mgr.findBaseTemplates(c.Request().Context()) if err != nil { return err } return c.JSON(http.StatusOK, templates) } func fetchTemplate(c echo.Context) error { mgr := templateManagerFrom(c) template, err := mgr.findTemplate(c.Request().Context(), c.Param("templateName")) if err != nil { if errors.Is(err, errTemplateNotFound) { return echo.NewHTTPError(http.StatusNotFound) } return err } return c.JSON(http.StatusOK, template) } func createTemplate(c echo.Context) error { name := c.Param("templateName") mgr := templateManagerFrom(c) exists, err := mgr.hasTemplate(c.Request().Context(), name) if err != nil { return err } if exists { return echo.NewHTTPError(http.StatusConflict) } var body createTemplateRequestBody if err = json.NewDecoder(c.Request().Body).Decode(&body); err != nil { return err } createdTemplate, err := mgr.createTemplate(c.Request().Context(), createTemplateOptions{ name: name, description: body.Description, baseTemplate: body.BaseTemplate, }) if err != nil { return err } return c.JSON(http.StatusOK, createdTemplate) } func updateOrBuildTemplate(c echo.Context) error { mgr := templateManagerFrom(c) exists, err := mgr.hasTemplate(c.Request().Context(), c.Param("templateName")) if err != nil { return err } if !exists { return echo.NewHTTPError(http.StatusNotFound) } var body postTemplateRequestBody err = json.NewDecoder(c.Request().Body).Decode(&body) if err != nil { return err } if body.ImageTag != nil || body.BuildArgs != nil { return buildTemplate(c, body) } return updateTemplate(c, body) } func updateTemplate(c echo.Context, body postTemplateRequestBody) error { name := c.Param("templateName") mgr := templateManagerFrom(c) ctx := c.Request().Context() var opts updateTemplateOptions if body.Name != nil { opts.name = *body.Name } if body.Description != nil { opts.description = *body.Description } updatedTemplate, err := mgr.updateTemplate(ctx, name, opts) if err != nil { if errors.Is(err, errTemplateNotFound) { return echo.NewHTTPError(http.StatusNotFound) } if errors.Is(err, errTemplateExists) { return echo.NewHTTPError(http.StatusConflict) } return err } return c.JSON(http.StatusOK, &updatedTemplate) } func buildTemplate(c echo.Context, body postTemplateRequestBody) error { mgr := templateManagerFrom(c) name := c.Param("templateName") ctx := c.Request().Context() template, err := mgr.findTemplate(ctx, name) if err != nil { if errors.Is(err, errTemplateNotFound) { return echo.NewHTTPError(http.StatusNotFound) } return err } outputChan, err := mgr.buildTemplate(ctx, template, buildTemplateOptions{ imageTag: *body.ImageTag, buildArgs: body.BuildArgs, }) if err != nil { var errBadTemplate *errBadTemplate if errors.As(err, &errBadTemplate) { return apierror.New(http.StatusBadRequest, "BAD_TEMPLATE", errBadTemplate.message) } return err } w := c.Response() w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") for o := range outputChan { switch o := o.(type) { case error: return err case string: if _, err = w.Write([]byte(o)); err != nil { return err } w.Flush() } } return nil } func deleteTemplate(c echo.Context) error { mgr := templateManagerFrom(c) name := c.Param("templateName") err := mgr.deleteTemplate(c.Request().Context(), name) if err != nil { if errors.Is(err, errTemplateNotFound) { return echo.NewHTTPError(http.StatusNotFound) } return err } return c.NoContent(http.StatusOK) } func fetchTemplateFile(c echo.Context) error { mgr := templateManagerFrom(c) templateName := c.Param("templateName") filePath := c.Param("filePath") file, err := mgr.findTemplateFile(c.Request().Context(), templateName, filePath) if err != nil { if errors.Is(err, errTemplateNotFound) || errors.Is(err, errTemplateFileNotFound) { return echo.NewHTTPError(http.StatusNotFound) } return err } return c.Blob(http.StatusOK, "application/octet-stream", file.Content) } func updateTemplateFile(c echo.Context) error { mgr := templateManagerFrom(c) templateName := c.Param("templateName") filePath := c.Param("filePath") newContent, err := io.ReadAll(c.Request().Body) if err != nil { return err } err = mgr.updateTemplateFile(c.Request().Context(), templateName, filePath, newContent) if err != nil { if errors.Is(err, errTemplateNotFound) || errors.Is(err, errTemplateFileNotFound) { return echo.NewHTTPError(http.StatusNotFound) } return err } return c.NoContent(http.StatusOK) } func fetchAllTemplateImages(c echo.Context) error { db := service.Database(c) var images []Image err := db.NewSelect().Model(&images).Scan(c.Request().Context()) if err != nil { if errors.Is(err, sql.ErrNoRows) { return c.JSON(http.StatusOK, make([]Image, 0)) } return err } if len(images) == 0 { return c.JSON(http.StatusOK, make([]Image, 0)) } return c.JSON(http.StatusOK, images) }