screenshots.go
go
package controllers
import (
	"fmt"
	"log"
	"net/http"
	"strings"
	"sync"
	"theskyscape.com/repo/skykit"
	"theskyscape.com/repo/skyshot/models"
)
func Screenshots(defaultImg []byte) (string, skykit.Handler) {
	return "screenshots", &ScreenshotsController{
		defaultImg: defaultImg,
		mutex:      &sync.Mutex{},
		capturing:  make(map[string]bool),
	}
}
type ScreenshotsController struct {
	skykit.Controller
	defaultImg []byte
	mutex      *sync.Mutex
	capturing  map[string]bool
}
func (c *ScreenshotsController) Setup(app *skykit.Application) {
	c.Controller.Setup(app)
	http.Handle("GET /", c.Serve("home.html", nil))
	http.Handle("GET /{app}", c.Protect(c.handleScreenshot, nil))
	http.Handle("POST /capture", c.Protect(c.handleCapture, nil))
}
func (c ScreenshotsController) Handle(r *http.Request) skykit.Handler {
	c.Request = r
	return &c
}
func (c *ScreenshotsController) RecentScreenshots() []*models.Screenshot {
	screenshots, _ := models.Screenshots.Search("ORDER BY CreatedAt DESC")
	return screenshots
}
func (c *ScreenshotsController) handleScreenshot(w http.ResponseWriter, r *http.Request) {
	appName := strings.ToLower(r.PathValue("app"))
	if appName == "" {
		log.Printf("Screenshot request missing app name")
		http.Error(w, "App name required", http.StatusBadRequest)
		return
	}
	screenshot, err := models.Screenshots.Get(appName)
	if err == nil && screenshot != nil {
		log.Printf("Serving cached screenshot for app: %s", appName)
		w.Header().Set("Content-Type", "image/png")
		w.Header().Set("Cache-Control", "public, max-age=86400") // Cache for 24 hours
		w.Write(screenshot.ImageData)
		return
	}
	go c.captureScreenshot(appName)
	w.Write(c.defaultImg)
}
func (c *ScreenshotsController) handleCapture(w http.ResponseWriter, r *http.Request) {
	appName := strings.ToLower(r.FormValue("app"))
	if appName == "" {
		log.Printf("Capture request missing app name")
		c.Render(w, r, "error.html", "App name is required")
		return
	}
	c.mutex.Lock()
	if _, ok := c.capturing[appName]; ok {
		c.mutex.Unlock()
		log.Printf("Capture: Already capturing for app: %s", appName)
		c.Render(w, r, "warning.html", fmt.Sprintf("Screenshot for %s is being captured", appName))
		return
	}
	c.mutex.Unlock()
	go c.captureScreenshot(appName)
	c.Render(w, r, "success.html", fmt.Sprintf("Screenshot for %s is being captured", appName))
}
func (c *ScreenshotsController) captureScreenshot(app string) {
	c.mutex.Lock()
	if _, ok := c.capturing[app]; ok {
		c.mutex.Unlock()
		log.Printf("Capture: Already capturing for app: %s", app)
		return
	}
	c.capturing[app] = true
	c.mutex.Unlock()
	log.Printf("Background capture starting for app: %s", app)
	imageData, err := models.CaptureScreenshot(app)
	if err != nil {
		log.Printf("Background screenshot capture failed for %s: %v", app, err)
		c.mutex.Lock()
		delete(c.capturing, app)
		c.mutex.Unlock()
		return
	}
	log.Printf("Successfully captured and stored screenshot for %s", imageData.ID)
	c.mutex.Lock()
	delete(c.capturing, app)
	c.mutex.Unlock()
}
bfb9971

updating claude file

Connor McCutcheon
@connor
1 stars

Screenshotting service for The Skyscape

Sign in to comment Sign In
ok can we study chromedp and use skykit to make an app like SkyLinks (maybe called SkyShot) to take pictures of the apps we are running on the network, and that we are reverse proxying with the web-server. I want to build this and launch it as a microservice itself that the web-server will later use once we have finished this exercise. It should have a simple homepage like Lorum Picsum to inform users about the usage and then all other routes will be parsed as URL, we will then take a screenshot and store it as []byte into a model that we can display to the users. If an error occures paring the url, taking the screenshot, or if we take longer than 400ms lets serve a default image, we can use the @web-server/views/public/background.png as a default for now
Connor McCutcheon
@connor
1 month ago