phase3: HTTP server with converter, one-shot preview store, and middleware
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const janitorInterval = 5 * time.Minute
|
||||
|
||||
type PreviewStore struct {
|
||||
mu sync.Mutex
|
||||
items map[string]previewItem
|
||||
ttl time.Duration
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
type previewItem struct {
|
||||
html []byte
|
||||
mime string
|
||||
filename string
|
||||
expires time.Time
|
||||
}
|
||||
|
||||
func NewPreviewStore(ttl time.Duration) *PreviewStore {
|
||||
return &PreviewStore{
|
||||
items: make(map[string]previewItem),
|
||||
ttl: ttl,
|
||||
now: time.Now,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PreviewStore) Put(html []byte, mime, filename string) string {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
id := uuid.NewString()
|
||||
s.items[id] = previewItem{
|
||||
html: append([]byte(nil), html...),
|
||||
mime: mime,
|
||||
filename: filename,
|
||||
expires: s.now().Add(s.ttl),
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (s *PreviewStore) Take(id string) (previewItem, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
item, ok := s.items[id]
|
||||
if !ok {
|
||||
return previewItem{}, false
|
||||
}
|
||||
|
||||
delete(s.items, id)
|
||||
if s.now().After(item.expires) {
|
||||
return previewItem{}, false
|
||||
}
|
||||
|
||||
item.html = append([]byte(nil), item.html...)
|
||||
return item, true
|
||||
}
|
||||
|
||||
func (s *PreviewStore) janitor(ctx context.Context) {
|
||||
ticker := time.NewTicker(janitorInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case now := <-ticker.C:
|
||||
s.cleanupExpired(now)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PreviewStore) cleanupExpired(now time.Time) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
for id, item := range s.items {
|
||||
if now.After(item.expires) {
|
||||
delete(s.items, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user