219 lines
5.4 KiB
Go
219 lines
5.4 KiB
Go
package sites
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"emperror.dev/errors"
|
|
"github.com/go-ozzo/ozzo-validation/v4"
|
|
"github.com/gofiber/fiber/v3"
|
|
"lmika.dev/lmika/weiro/models"
|
|
"lmika.dev/lmika/weiro/providers/db"
|
|
"lmika.dev/pkg/modash/moslice"
|
|
)
|
|
|
|
type Service struct {
|
|
db *db.Provider
|
|
}
|
|
|
|
func New(dbp *db.Provider) *Service {
|
|
return &Service{
|
|
db: dbp,
|
|
}
|
|
}
|
|
|
|
func (s *Service) HasUsersAsSites(ctx context.Context) (bool, error) {
|
|
return s.db.HasUsersAndSites(ctx)
|
|
}
|
|
|
|
func (s *Service) ListSites(ctx context.Context) ([]models.Site, error) {
|
|
user, ok := models.GetUser(ctx)
|
|
if !ok {
|
|
return nil, models.UserRequiredError
|
|
}
|
|
|
|
sites, err := s.db.SelectSitesOwnedByUser(ctx, user.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(sites) == 0 {
|
|
return nil, errors.New("no sites found")
|
|
}
|
|
|
|
return sites, nil
|
|
}
|
|
|
|
func (s *Service) BestSite(ctx context.Context, user models.User) (models.Site, error) {
|
|
sites, err := s.db.SelectSitesOwnedByUser(ctx, user.ID)
|
|
if err != nil {
|
|
return models.Site{}, err
|
|
} else if len(sites) == 0 {
|
|
return models.Site{}, errors.New("no sites found")
|
|
}
|
|
|
|
return sites[0], nil
|
|
}
|
|
|
|
type CreateSiteParams struct {
|
|
SiteName string `form:"siteName"`
|
|
SiteURL string `form:"siteUrl"`
|
|
NetlifySiteID string `form:"netlifySiteId"`
|
|
NetlifyAPIKey string `form:"netlifyAPIToken"`
|
|
}
|
|
|
|
type FirstRunRequest struct {
|
|
CreateSiteParams
|
|
Username string `form:"username"`
|
|
Password1 string `form:"password1"`
|
|
Password2 string `form:"password2"`
|
|
}
|
|
|
|
func (frr FirstRunRequest) Validate() error {
|
|
return validation.ValidateStruct(&frr,
|
|
validation.Field(&frr.Username, validation.Required, validation.Match(models.ValidUserName)),
|
|
validation.Field(&frr.Password1, validation.Required),
|
|
validation.Field(&frr.Password2, validation.Required, validation.By(stringEquals(frr.Password1))),
|
|
)
|
|
}
|
|
|
|
func (s *Service) FirstRun(ctx context.Context, req FirstRunRequest) (newUser models.User, newSite models.Site, _ error) {
|
|
if err := req.Validate(); err != nil {
|
|
return newUser, newSite, err
|
|
}
|
|
|
|
hasSite, err := s.db.HasUsersAndSites(ctx)
|
|
if err != nil {
|
|
return newUser, newSite, err
|
|
} else if hasSite {
|
|
return newUser, newSite, errors.New("user and site already exists")
|
|
}
|
|
|
|
newUser = models.User{
|
|
Username: req.Username,
|
|
TimeZone: "UTC",
|
|
Created: time.Now(),
|
|
}
|
|
newUser.SetPassword(req.Password1)
|
|
if err := s.db.SaveUser(ctx, &newUser); err != nil {
|
|
return newUser, newSite, err
|
|
}
|
|
|
|
ctx = models.WithUser(ctx, newUser)
|
|
newSite, err = s.CreateSite(ctx, req.CreateSiteParams)
|
|
if err != nil {
|
|
return newUser, newSite, err
|
|
}
|
|
|
|
return newUser, newSite, nil
|
|
}
|
|
|
|
func (s *Service) CreateSite(ctx context.Context, req CreateSiteParams) (newSite models.Site, _ error) {
|
|
user, ok := models.GetUser(ctx)
|
|
if !ok {
|
|
return newSite, models.UserRequiredError
|
|
}
|
|
|
|
newSite = models.Site{
|
|
Title: defaultIfEmpty(req.SiteName, "New Site"),
|
|
GUID: models.NewNanoID(),
|
|
OwnerID: user.ID,
|
|
Timezone: "UTC",
|
|
PostsPerPage: 10,
|
|
Created: time.Now(),
|
|
}
|
|
if err := s.db.SaveSite(ctx, &newSite); err != nil {
|
|
return newSite, err
|
|
}
|
|
|
|
hasNetlifyConfig := req.SiteURL != "" && req.NetlifySiteID != "" && req.NetlifyAPIKey != ""
|
|
if hasNetlifyConfig {
|
|
target := models.SitePublishTarget{
|
|
SiteID: newSite.ID,
|
|
Enabled: true,
|
|
GUID: models.NewNanoID(),
|
|
BaseURL: req.SiteURL,
|
|
TargetType: "netlify",
|
|
TargetRef: req.NetlifySiteID,
|
|
TargetKey: req.NetlifyAPIKey,
|
|
}
|
|
if err := s.db.SavePublishTarget(ctx, &target); err != nil {
|
|
return newSite, err
|
|
}
|
|
}
|
|
|
|
return newSite, nil
|
|
}
|
|
|
|
func (s *Service) GetSiteByID(ctx context.Context, siteID int64) (models.Site, error) {
|
|
user, ok := models.GetUser(ctx)
|
|
if !ok {
|
|
return models.Site{}, models.UserRequiredError
|
|
}
|
|
|
|
site, err := s.db.SelectSiteByID(ctx, siteID)
|
|
if err != nil {
|
|
return models.Site{}, err
|
|
}
|
|
|
|
if site.OwnerID != user.ID {
|
|
return models.Site{}, fiber.ErrForbidden
|
|
}
|
|
|
|
return site, nil
|
|
}
|
|
|
|
func (s *Service) ListAllSitesWithOwners(ctx context.Context) ([]db.SiteWithOwner, error) {
|
|
return s.db.SelectAllSitesWithOwners(ctx)
|
|
}
|
|
|
|
type UpdateSiteSettingsParams struct {
|
|
SiteID int64 `form:"siteID"`
|
|
Name string `form:"name"`
|
|
Tagline string `form:"tagline"`
|
|
Timezone string `form:"timezone"`
|
|
PostsPerPage int `form:"postsPerPage"`
|
|
}
|
|
|
|
func (s *Service) UpdateSiteSettings(ctx context.Context, params UpdateSiteSettingsParams) (models.Site, error) {
|
|
site, err := s.GetSiteByID(ctx, params.SiteID)
|
|
if err != nil {
|
|
return models.Site{}, err
|
|
}
|
|
|
|
_, err = time.LoadLocation(params.Timezone)
|
|
if err != nil {
|
|
return models.Site{}, errors.Wrap(err, "invalid timezone")
|
|
}
|
|
|
|
postsPerPage := params.PostsPerPage
|
|
if postsPerPage < 1 {
|
|
postsPerPage = 1
|
|
} else if postsPerPage > 100 {
|
|
postsPerPage = 100
|
|
}
|
|
|
|
site.Title = params.Name
|
|
site.Tagline = params.Tagline
|
|
site.Timezone = params.Timezone
|
|
site.PostsPerPage = postsPerPage
|
|
|
|
if err := s.db.SaveSite(ctx, &site); err != nil {
|
|
return models.Site{}, err
|
|
}
|
|
|
|
return site, nil
|
|
}
|
|
|
|
func (s *Service) BestPubTarget(ctx context.Context, site models.Site) (models.SitePublishTarget, error) {
|
|
pubTargets, err := s.db.SelectPublishTargetsOfSite(ctx, site.ID)
|
|
if err != nil {
|
|
return models.SitePublishTarget{}, err
|
|
}
|
|
|
|
enabledPubTargets := moslice.Filter(pubTargets, func(pubTarget models.SitePublishTarget) bool { return pubTarget.Enabled })
|
|
if len(enabledPubTargets) == 0 {
|
|
return models.SitePublishTarget{}, errors.New("no publish targets found")
|
|
}
|
|
|
|
return enabledPubTargets[0], nil
|
|
}
|