readysite / website / controllers / site.go
2.5 KB
site.go
package controllers

import (
	"net/http"
	"os"

	"github.com/readysite/readysite/pkg/application"
	"github.com/readysite/readysite/website/internal/access"
	"github.com/readysite/readysite/website/internal/content"
	"github.com/readysite/readysite/website/internal/helpers"
	"github.com/readysite/readysite/website/models"
)

// Site returns the site controller.
// NOTE: This controller must be registered LAST as it uses a catch-all route.
func Site() (string, *SiteController) {
	return "site", &SiteController{}
}

// SiteController handles public site rendering.
type SiteController struct {
	application.BaseController
}

// Setup registers routes.
func (c *SiteController) Setup(app *application.App) {
	c.BaseController.Setup(app)

	// Catch-all route for public pages - must be registered LAST
	http.Handle("GET /{path...}", app.Method(c, "Render", nil))
}

// Handle implements Controller interface with value receiver for request isolation.
func (c SiteController) Handle(r *http.Request) application.Controller {
	c.Request = r
	return &c
}

// Render renders a public page.
func (c *SiteController) Render(w http.ResponseWriter, r *http.Request) {
	path := r.PathValue("path")

	// Check for setup redirect
	if helpers.GetSetting(models.SettingSetupComplete) != "true" {
		if hostingURL := os.Getenv("HOSTING_URL"); hostingURL != "" {
			http.Redirect(w, r, hostingURL, http.StatusSeeOther)
			return
		}
		http.Redirect(w, r, "/setup", http.StatusSeeOther)
		return
	}

	// Default to "home" for root path
	pageID := path
	if pageID == "" {
		pageID = "home"
	}

	// Try to find the page
	page, err := models.Pages.Get(pageID)
	if err != nil {
		page = content.FindByPath(path)
	}

	// Check access: published pages are public, otherwise check ACL
	user := access.GetUserFromJWT(r)
	if page == nil || (!page.IsPublished() && !access.CheckAccess(user, access.ResourcePage, page.ID, access.PermRead)) {
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(content.NotFound404HTML()))
		return
	}

	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write([]byte(content.RenderPage(page, r)))
}

// PublishedPages returns published root pages for navigation.
func (c *SiteController) PublishedPages() []*models.Page {
	allPages, _ := models.Pages.Search("WHERE ParentID = '' ORDER BY Position, CreatedAt")
	var published []*models.Page
	for _, p := range allPages {
		if p.IsPublished() {
			published = append(published, p)
		}
	}
	return published
}
← Back