Add Go CDN and playground server
Build Core and Handles into in-memory CDN modules, add the marketing site and tutorial docs, and provide a SQLite-backed WebGPU playground with TypeScript tooling and profiling. Amp-Thread-ID: https://ampcode.com/threads/T-01a02485-5574-707c-bff4-5668d83bee8a Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
const (
|
||||
maximumCodeBytes = 200 * 1024
|
||||
maximumTitleRunes = 80
|
||||
)
|
||||
|
||||
var playgroundIDPattern = regexp.MustCompile(`^[a-z0-9]{6,24}$`)
|
||||
|
||||
type playground struct {
|
||||
ID string `json:"id"`
|
||||
Revision int `json:"revision"`
|
||||
Title string `json:"title"`
|
||||
Code string `json:"code"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
}
|
||||
|
||||
type playgroundStore struct {
|
||||
*sql.DB
|
||||
}
|
||||
|
||||
func openPlaygroundStore(path string) (*playgroundStore, error) {
|
||||
if path != ":memory:" {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
database, err := sql.Open("sqlite3", path+"?_foreign_keys=on&_busy_timeout=5000&_journal_mode=WAL")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
database.SetMaxOpenConns(1)
|
||||
store := &playgroundStore{DB: database}
|
||||
if err := store.initialize(); err != nil {
|
||||
database.Close()
|
||||
return nil, err
|
||||
}
|
||||
return store, nil
|
||||
}
|
||||
|
||||
func (store *playgroundStore) initialize() error {
|
||||
_, err := store.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS playgrounds (
|
||||
id TEXT NOT NULL,
|
||||
revision INTEGER NOT NULL CHECK (revision > 0),
|
||||
title TEXT NOT NULL,
|
||||
code TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
PRIMARY KEY (id, revision)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS playgrounds_created_at
|
||||
ON playgrounds (created_at DESC);
|
||||
`)
|
||||
return err
|
||||
}
|
||||
|
||||
func (store *playgroundStore) save(ctx context.Context, id, title, code string) (playground, error) {
|
||||
if id != "" && !playgroundIDPattern.MatchString(id) {
|
||||
return playground{}, errors.New("invalid playground id")
|
||||
}
|
||||
if title == "" || utf8.RuneCountInString(title) > maximumTitleRunes {
|
||||
return playground{}, fmt.Errorf("title must contain 1 to %d characters", maximumTitleRunes)
|
||||
}
|
||||
if code == "" || len(code) > maximumCodeBytes || !utf8.ValidString(code) {
|
||||
return playground{}, fmt.Errorf("code must contain 1 to %d bytes of UTF-8", maximumCodeBytes)
|
||||
}
|
||||
if id == "" {
|
||||
var err error
|
||||
id, err = randomPlaygroundID()
|
||||
if err != nil {
|
||||
return playground{}, err
|
||||
}
|
||||
}
|
||||
|
||||
transaction, err := store.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return playground{}, err
|
||||
}
|
||||
defer transaction.Rollback()
|
||||
|
||||
var revision int
|
||||
if err := transaction.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT COALESCE(MAX(revision), 0) + 1 FROM playgrounds WHERE id = ?`,
|
||||
id,
|
||||
).Scan(&revision); err != nil {
|
||||
return playground{}, err
|
||||
}
|
||||
createdAt := time.Now().UTC().Truncate(time.Millisecond)
|
||||
if _, err := transaction.ExecContext(
|
||||
ctx,
|
||||
`INSERT INTO playgrounds (id, revision, title, code, created_at) VALUES (?, ?, ?, ?, ?)`,
|
||||
id,
|
||||
revision,
|
||||
title,
|
||||
code,
|
||||
createdAt.Format(time.RFC3339Nano),
|
||||
); err != nil {
|
||||
return playground{}, err
|
||||
}
|
||||
if err := transaction.Commit(); err != nil {
|
||||
return playground{}, err
|
||||
}
|
||||
return playground{ID: id, Revision: revision, Title: title, Code: code, CreatedAt: createdAt}, nil
|
||||
}
|
||||
|
||||
func (store *playgroundStore) get(ctx context.Context, id string, revision int) (playground, error) {
|
||||
if !playgroundIDPattern.MatchString(id) || revision < 1 {
|
||||
return playground{}, sql.ErrNoRows
|
||||
}
|
||||
var result playground
|
||||
var createdAt string
|
||||
err := store.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT id, revision, title, code, created_at FROM playgrounds WHERE id = ? AND revision = ?`,
|
||||
id,
|
||||
revision,
|
||||
).Scan(&result.ID, &result.Revision, &result.Title, &result.Code, &createdAt)
|
||||
if err != nil {
|
||||
return playground{}, err
|
||||
}
|
||||
result.CreatedAt, err = time.Parse(time.RFC3339Nano, createdAt)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func randomPlaygroundID() (string, error) {
|
||||
const alphabet = "abcdefghijkmnopqrstuvwxyz23456789"
|
||||
result := make([]byte, 10)
|
||||
for index := range result {
|
||||
value, err := rand.Int(rand.Reader, big.NewInt(int64(len(alphabet))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result[index] = alphabet[value.Int64()]
|
||||
}
|
||||
return string(result), nil
|
||||
}
|
||||
Reference in New Issue
Block a user