aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorXe Iaso <me@xeiaso.net>2024-06-08 13:39:52 -0700
committerXe Iaso <me@xeiaso.net>2024-06-08 13:41:05 -0700
commitbb8a5a886c993fc943277e56b4c9065c1f3a82b2 (patch)
tree14de5be85b83b17553092f1f1c78b2b0d0b4cba3 /cmd
parent2f9125d0cbe232dbb0dcd64eab33af356d5d615c (diff)
downloadx-bb8a5a886c993fc943277e56b4c9065c1f3a82b2.tar.xz
x-bb8a5a886c993fc943277e56b4c9065c1f3a82b2.zip
cmd: add command future-sight for preview deployments
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/future-sight/.gitignore1
-rw-r--r--cmd/future-sight/main.go325
-rw-r--r--cmd/future-sight/manifest.dev.yaml195
-rw-r--r--cmd/future-sight/manifest.yaml226
-rwxr-xr-xcmd/future-sight/port-forward.sh10
-rw-r--r--cmd/future-sight/var/.gitignore2
-rw-r--r--cmd/future-sight/yeetfile.js5
-rw-r--r--cmd/mi/services/posse/posse.go3
-rw-r--r--cmd/xedn/internal/xesite/xesite.go210
-rw-r--r--cmd/xedn/main.go2
10 files changed, 767 insertions, 212 deletions
diff --git a/cmd/future-sight/.gitignore b/cmd/future-sight/.gitignore
new file mode 100644
index 0000000..abe61a7
--- /dev/null
+++ b/cmd/future-sight/.gitignore
@@ -0,0 +1 @@
+.env.prod \ No newline at end of file
diff --git a/cmd/future-sight/main.go b/cmd/future-sight/main.go
new file mode 100644
index 0000000..4948324
--- /dev/null
+++ b/cmd/future-sight/main.go
@@ -0,0 +1,325 @@
+package main
+
+import (
+ "context"
+ "crypto/sha256"
+ "encoding/base64"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "log/slog"
+ "net/http"
+ "os"
+ "path/filepath"
+ "runtime"
+
+ "github.com/aws/aws-sdk-go-v2/aws"
+ "github.com/aws/aws-sdk-go-v2/credentials"
+ "github.com/aws/aws-sdk-go-v2/service/s3"
+ "github.com/nats-io/nats.go"
+ "github.com/redis/go-redis/v9"
+ "golang.org/x/sync/errgroup"
+ "google.golang.org/protobuf/proto"
+ "within.website/x/internal"
+ "within.website/x/internal/xesite"
+ pb "within.website/x/proto/future-sight"
+ "within.website/x/web/useragent"
+)
+
+var (
+ apiBind = flag.String("api-bind", ":8080", "address to bind API to")
+ bind = flag.String("bind", ":8081", "address to bind zipserver to")
+
+ awsAccessKeyID = flag.String("aws-access-key-id", "", "AWS access key ID")
+ awsSecretKey = flag.String("aws-secret-access-key", "", "AWS secret access key")
+ awsEndpointS3 = flag.String("aws-endpoint-url-s3", "http://localhost:9000", "AWS S3 endpoint")
+ awsRegion = flag.String("aws-region", "auto", "AWS region")
+ bucketName = flag.String("bucket-name", "xesite-preview-versions", "bucket to fetch previews from")
+ dataDir = flag.String("data-dir", "./var", "directory to store data in (not permanent)")
+ natsURL = flag.String("nats-url", "nats://localhost:4222", "nats url")
+ usePathStyle = flag.Bool("use-path-style", false, "use path style for S3")
+ valkeyHost = flag.String("valkey-host", "localhost:6379", "host:port for valkey")
+ valkeyPassword = flag.String("valkey-password", "", "password for valkey")
+)
+
+func main() {
+ internal.HandleStartup()
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ creds := credentials.NewStaticCredentialsProvider(*awsAccessKeyID, *awsSecretKey, "")
+
+ s3c := s3.New(s3.Options{
+ AppID: useragent.GenUserAgent("future-sight-push", "https://xeiaso.net"),
+ BaseEndpoint: awsEndpointS3,
+ ClientLogMode: aws.LogRetries | aws.LogRequest | aws.LogResponse,
+ Credentials: creds,
+ EndpointResolver: s3.EndpointResolverFromURL(*awsEndpointS3),
+ //Logger: logging.NewStandardLogger(os.Stderr),
+ UsePathStyle: *usePathStyle,
+ Region: *awsRegion,
+ })
+
+ slog.Debug("details",
+ "awsAccessKeyID", *awsAccessKeyID,
+ "awsSecretKey", *awsSecretKey,
+ "awsEndpointS3", *awsEndpointS3,
+ "awsRegion", *awsRegion,
+ "bucketName", *bucketName,
+ "natsURL", *natsURL,
+ "usePathStyle", *usePathStyle,
+ "valkeyHost", *valkeyHost,
+ "valkeyPassword", *valkeyPassword,
+ )
+
+ vk := redis.NewClient(&redis.Options{
+ Addr: *valkeyHost,
+ Password: *valkeyPassword,
+ DB: 0,
+ })
+ defer vk.Close()
+
+ if _, err := vk.Ping(context.Background()).Result(); err != nil {
+ log.Fatal(err)
+ }
+
+ nc, err := nats.Connect(*natsURL)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer nc.Close()
+
+ zs, err := xesite.NewZipServer(filepath.Join(*dataDir, "current.zip"), *dataDir)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ s := &Server{
+ s3c: s3c,
+ vk: vk,
+ nc: nc,
+ zs: zs,
+ dir: *dataDir,
+ }
+
+ currentVersion, err := vk.Get(ctx, "future-sight:current").Result()
+ if err != nil && err != redis.Nil {
+ log.Fatal(err)
+ }
+
+ if currentVersion != "" {
+ nv := pb.NewVersion{
+ Slug: currentVersion,
+ }
+
+ if err := s.fetchVersion(ctx, &nv); err != nil {
+ slog.Error("can't fetch current version", "err", err)
+ }
+ }
+
+ if _, err := nc.Subscribe("future-sight-push", s.HandleFutureSightPushMsg); err != nil {
+ log.Fatal(err)
+ }
+
+ apiMux := http.NewServeMux()
+ apiMux.HandleFunc("/upload", s.UploadVersion)
+ apiMux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintln(w, "OK")
+ })
+
+ g, _ := errgroup.WithContext(context.Background())
+
+ g.Go(func() error {
+ slog.Info("listening", "for", "api", "addr", *apiBind)
+ return http.ListenAndServe(*apiBind, apiMux)
+ })
+
+ g.Go(func() error {
+ slog.Info("listening", "for", "zipserver", "addr", *bind)
+ return http.ListenAndServe(*bind, zs)
+ })
+
+ if err := g.Wait(); err != nil {
+ slog.Error("error doing work", "err", err)
+ os.Exit(1)
+ }
+}
+
+type Server struct {
+ s3c *s3.Client
+ vk *redis.Client
+ nc *nats.Conn
+ zs *xesite.ZipServer
+ dir string
+}
+
+func (s *Server) UploadVersion(w http.ResponseWriter, r *http.Request) {
+ ctx, cancel := context.WithCancel(r.Context())
+ defer cancel()
+
+ slog.Info("uploading version")
+
+ if err := r.ParseMultipartForm(10 << 24); err != nil {
+ slog.Error("failed to parse form", "err", err)
+ http.Error(w, "failed to parse form", http.StatusBadRequest)
+ return
+ }
+
+ f, header, err := r.FormFile("file")
+ if err != nil {
+ slog.Error("failed to get file", "err", err)
+ http.Error(w, "failed to get file", http.StatusBadRequest)
+ return
+ }
+ defer f.Close()
+
+ slog.Info("got file", "filename", header.Filename)
+
+ fout, err := os.CreateTemp(s.dir, "future-sight-upload-*")
+ if err != nil {
+ slog.Error("failed to create temp file", "err", err)
+ http.Error(w, "failed to create temp file", http.StatusInternalServerError)
+ return
+ }
+ defer fout.Close()
+ defer os.Remove(fout.Name())
+
+ if _, err := io.Copy(fout, f); err != nil {
+ slog.Error("failed to copy file", "err", err)
+ http.Error(w, "failed to copy file", http.StatusInternalServerError)
+ return
+ }
+
+ hash, err := hashFileSha256(fout)
+ if err != nil {
+ slog.Error("failed to hash file", "err", err)
+ http.Error(w, "failed to hash file", http.StatusInternalServerError)
+ return
+ }
+
+ st, err := fout.Stat()
+ if err != nil {
+ slog.Error("failed to stat file", "err", err)
+ http.Error(w, "failed to stat file", http.StatusInternalServerError)
+ return
+ }
+
+ slog.Info("hashed file", "hash", hash)
+
+ if _, err := s.s3c.PutObject(ctx, &s3.PutObjectInput{
+ Bucket: bucketName,
+ Key: aws.String(hash),
+ Body: fout,
+ ContentType: aws.String("application/zip"),
+ ContentLength: aws.Int64(st.Size()),
+ Metadata: map[string]string{
+ "host_os": runtime.GOOS,
+ },
+ }); err != nil {
+ slog.Error("failed to push file", "bucketName", *bucketName, "hash", hash, "err", err)
+ http.Error(w, "failed to push file", http.StatusInternalServerError)
+ return
+ }
+
+ nv := &pb.NewVersion{
+ Slug: hash,
+ }
+
+ if err := s.PushVersion(ctx, nv); err != nil {
+ slog.Error("failed to push version", "slug", nv.Slug, "err", err)
+ http.Error(w, "failed to push version", http.StatusInternalServerError)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+}
+
+func (s *Server) PushVersion(ctx context.Context, nv *pb.NewVersion) error {
+ slog.Info("got new version", "version", nv)
+
+ msg, err := proto.Marshal(nv)
+ if err != nil {
+ slog.Error("failed to marshal message", "slug", nv.Slug, "err", err)
+ return err
+ }
+
+ if err := s.nc.Publish("future-sight-push", msg); err != nil {
+ slog.Error("failed to publish message", "slug", nv.Slug, "err", err)
+ return err
+ }
+
+ if _, err := s.vk.Set(ctx, "future-sight:current", nv.Slug, 0).Result(); err != nil {
+ slog.Error("failed to set current version", "slug", nv.Slug, "err", err)
+ return err
+ }
+
+ return nil
+}
+
+func (s *Server) HandleFutureSightPushMsg(msg *nats.Msg) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ nv := new(pb.NewVersion)
+ if err := proto.Unmarshal(msg.Data, nv); err != nil {
+ slog.Error("failed to unmarshal message", "err", err)
+ return
+ }
+
+ if err := s.fetchVersion(ctx, nv); err != nil {
+ slog.Error("failed to handle message", "slug", nv.Slug, "err", err)
+ return
+ }
+
+ slog.Info("handled message", "slug", nv.Slug)
+}
+
+func (s *Server) fetchVersion(ctx context.Context, nv *pb.NewVersion) error {
+ os.Remove(filepath.Join(s.dir, "current.zip"))
+
+ fout, err := os.Create(filepath.Join(s.dir, "current.zip"))
+ if err != nil {
+ return err
+ }
+ defer fout.Close()
+
+ obj, err := s.s3c.GetObject(ctx, &s3.GetObjectInput{
+ Bucket: bucketName,
+ Key: aws.String(nv.Slug),
+ })
+ if err != nil {
+ os.Remove(filepath.Join(s.dir, "current.zip"))
+ slog.Error("failed to get object", "slug", nv.Slug, "err", err)
+ return err
+ }
+ defer obj.Body.Close()
+
+ if _, err := io.Copy(fout, obj.Body); err != nil {
+ os.Remove(filepath.Join(s.dir, "current.zip"))
+ slog.Error("failed to copy object", "slug", nv.Slug, "err", err)
+ return err
+ }
+
+ if err := s.zs.Update(filepath.Join(s.dir, "current.zip")); err != nil {
+ slog.Error("failed to update zipserver", "slug", nv.Slug, "err", err)
+ return err
+ }
+
+ return nil
+}
+
+// hashFileSha256 hashes a file with Sha256 and returns the hash as a base64 encoded string.
+func hashFileSha256(fin *os.File) (string, error) {
+ h := sha256.New()
+ if _, err := io.Copy(h, fin); err != nil {
+ return "", err
+ }
+
+ // rewind the file
+ if _, err := fin.Seek(0, io.SeekStart); err != nil {
+ return "", err
+ }
+
+ return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
+}
diff --git a/cmd/future-sight/manifest.dev.yaml b/cmd/future-sight/manifest.dev.yaml
new file mode 100644
index 0000000..49a71f6
--- /dev/null
+++ b/cmd/future-sight/manifest.dev.yaml
@@ -0,0 +1,195 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: future-sight
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: nats
+ namespace: future-sight
+spec:
+ replicas: 1
+ strategy: {}
+ selector:
+ matchLabels:
+ app: nats
+ template:
+ metadata:
+ labels:
+ app: nats
+ spec:
+ containers:
+ - name: nats
+ image: nats:2-alpine
+ ports:
+ - containerPort: 4222
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: nats
+ namespace: future-sight
+spec:
+ selector:
+ app: nats
+ ports:
+ - port: 4222
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: valkey-secret
+ namespace: future-sight
+ labels:
+ app: valkey
+data:
+ VALKEY_PASSWORD: hunter2
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: valkey
+ namespace: future-sight
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: valkey
+ template:
+ metadata:
+ labels:
+ app: valkey
+ spec:
+ containers:
+ - name: valkey
+ image: 'docker.io/bitnami/valkey:latest'
+ imagePullPolicy: Always
+ ports:
+ - containerPort: 6379
+ envFrom:
+ - configMapRef:
+ name: valkey-secret
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: valkey
+ namespace: future-sight
+ labels:
+ app: valkey
+spec:
+ type: ClusterIP
+ ports:
+ - port: 6379
+ selector:
+ app: valkey
+---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: minio
+ namespace: future-sight
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 10Gi
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: minio
+ namespace: future-sight
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: minio
+ template:
+ metadata:
+ labels:
+ app: minio
+ spec:
+ volumes:
+ - name: data
+ persistentVolumeClaim:
+ claimName: minio
+ containers:
+ - name: minio
+ volumeMounts:
+ - name: data
+ mountPath: /data
+ image: minio/minio
+ args:
+ - server
+ - /data
+ - --console-address=:9001
+ env:
+ - name: MINIO_ACCESS_KEY
+ value: "minio"
+ - name: MINIO_SECRET_KEY
+ value: "minio123"
+ - name: MINIO_ROOT_USER
+ value: root
+ - name: MINIO_ROOT_PASSWORD
+ value: hunter22
+ ports:
+ - containerPort: 9000
+ name: http
+ - containerPort: 9001
+ name: webui
+---
+apiVersion: batch/v1
+kind: Job
+metadata:
+ name: create-bucket
+ namespace: future-sight
+spec:
+ template:
+ spec:
+ containers:
+ - name: create-bucket
+ image: minio/mc
+ command: ["/bin/sh"]
+ args:
+ - "-c"
+ - |
+ /usr/bin/mc config host add k8s http://minio:9000 minio minio123;
+ /usr/bin/mc rm -r --force myminio/xesite-preview-versions;
+ /usr/bin/mc mb myminio/xesite-preview-versions;
+ /usr/bin/mc policy download myminio/xesite-preview-versions;
+ exit 0;
+ restartPolicy: Never
+ backoffLimit: 4
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: minio
+ namespace: future-sight
+spec:
+ type: ClusterIP
+ ports:
+ - name: http
+ port: 80
+ targetPort: 9000
+ protocol: TCP
+ selector:
+ app: minio
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: minio-webui
+ namespace: future-sight
+spec:
+ type: ClusterIP
+ ports:
+ - name: webui
+ port: 80
+ targetPort: 9001
+ protocol: TCP
+ selector:
+ app: minio \ No newline at end of file
diff --git a/cmd/future-sight/manifest.yaml b/cmd/future-sight/manifest.yaml
new file mode 100644
index 0000000..3260bd5
--- /dev/null
+++ b/cmd/future-sight/manifest.yaml
@@ -0,0 +1,226 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: future-sight
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: nats
+ namespace: future-sight
+spec:
+ replicas: 1
+ strategy: {}
+ selector:
+ matchLabels:
+ app: nats
+ template:
+ metadata:
+ labels:
+ app: nats
+ spec:
+ containers:
+ - name: nats
+ image: nats:2-alpine
+ ports:
+ - containerPort: 4222
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: nats
+ namespace: future-sight
+spec:
+ selector:
+ app: nats
+ ports:
+ - port: 4222
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: valkey-secret
+ namespace: future-sight
+ labels:
+ app: valkey
+data:
+ VALKEY_PASSWORD: hunter2
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: valkey
+ namespace: future-sight
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: valkey
+ template:
+ metadata:
+ labels:
+ app: valkey
+ spec:
+ containers:
+ - name: valkey
+ image: 'docker.io/bitnami/valkey:latest'
+ imagePullPolicy: Always
+ ports:
+ - containerPort: 6379
+ envFrom:
+ - configMapRef:
+ name: valkey-secret
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: valkey
+ namespace: future-sight
+ labels:
+ app: valkey
+spec:
+ type: ClusterIP
+ ports:
+ - port: 6379
+ selector:
+ app: valkey
+---
+apiVersion: onepassword.com/v1
+kind: OnePasswordItem
+metadata:
+ name: tigris-creds
+ namespace: future-sight
+ labels:
+ app.kubernetes.io/name: future-sight
+spec:
+ itemPath: "vaults/Kubernetes/items/Tigris creds"
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: future-sight
+ namespace: future-sight
+ labels:
+ app.kubernetes.io/name: future-sight
+data:
+ BUCKET_NAME: xesite-preview-versions
+ DATA_DIR: /cache
+ NATS_URL: nats://nats:4222
+ VALKEY_HOST: valkey:6379
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: future-sight
+ namespace: future-sight
+ labels:
+ app.kubernetes.io/name: future-sight
+ annotations:
+ operator.1password.io/auto-restart: "true"
+spec:
+ replicas: 3
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: future-sight
+ template:
+ metadata:
+ namespace: future-sight
+ labels:
+ app.kubernetes.io/name: future-sight
+ spec:
+ volumes:
+ - name: tigris
+ secret:
+ secretName: tigris-creds
+ - name: cache
+ emptyDir: {}
+ securityContext:
+ fsGroup: 1000
+ containers:
+ - name: main
+ image: ghcr.io/xe/x/future-sight:latest
+ imagePullPolicy: Always
+ resources:
+ limits:
+ cpu: "250m"
+ memory: "512Mi"
+ requests:
+ cpu: "100m"
+ memory: "256Mi"
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 1000
+ runAsNonRoot: true
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ seccompProfile:
+ type: RuntimeDefault
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8080
+ httpHeaders:
+ - name: X-Kubernetes
+ value: "is kinda okay"
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ volumeMounts:
+ - name: tigris
+ mountPath: /run/secrets/tigris
+ - name: cache
+ mountPath: /cache
+ envFrom:
+ - configMapRef:
+ name: valkey-secret
+ - configMapRef:
+ name: future-sight
+---
+apiVersion: v1
+kind: Service
+metadata:
+ name: future-sight
+ namespace: future-sight
+ labels:
+ app.kubernetes.io/name: future-sight
+spec:
+ selector:
+ app.kubernetes.io/name: future-sight
+ ports:
+ - protocol: TCP
+ port: 80
+ targetPort: 8081
+ name: web
+ - protocol: TCP
+ port: 8080
+ targetPort: 8080
+ name: api
+ type: ClusterIP
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: future-sight
+ namespace: future-sight
+ labels:
+ app.kubernetes.io/name: future-sight
+ annotations:
+ cert-manager.io/cluster-issuer: "letsencrypt-prod"
+spec:
+ ingressClassName: nginx
+ tls:
+ - hosts:
+ - preview.xeiaso.net
+ secretName: preview-xeiaso-net-tls
+ rules:
+ - host: preview.xeiaso.net
+ http:
+ paths:
+ - pathType: Prefix
+ path: "/"
+ backend:
+ service:
+ name: future-sight
+ port:
+ name: http \ No newline at end of file
diff --git a/cmd/future-sight/port-forward.sh b/cmd/future-sight/port-forward.sh
new file mode 100755
index 0000000..984ebb7
--- /dev/null
+++ b/cmd/future-sight/port-forward.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+kubectl apply -f manifest.dev.yaml
+
+kubectl port-forward -n future-sight svc/nats 4222:4222 &
+kubectl port-forward -n future-sight deploy/minio 9000:9000 &
+kubectl port-forward -n future-sight deploy/minio 9001:9001 &
+kubectl port-forward -n future-sight svc/valkey 6379:6379 &
+
+wait \ No newline at end of file
diff --git a/cmd/future-sight/var/.gitignore b/cmd/future-sight/var/.gitignore
new file mode 100644
index 0000000..c96a04f
--- /dev/null
+++ b/cmd/future-sight/var/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore \ No newline at end of file
diff --git a/cmd/future-sight/yeetfile.js b/cmd/future-sight/yeetfile.js
new file mode 100644
index 0000000..1b31c6c
--- /dev/null
+++ b/cmd/future-sight/yeetfile.js
@@ -0,0 +1,5 @@
+nix.build(".#docker.future-sight");
+docker.load("./result");
+docker.push(`ghcr.io/xe/x/future-sight`);
+yeet.run("kubectl", "apply", "-f=manifest.yaml");
+yeet.run("sh", "-c", "kubectl rollout restart -n future-sight deployments/future-sight");
diff --git a/cmd/mi/services/posse/posse.go b/cmd/mi/services/posse/posse.go
index 7645470..6c6702b 100644
--- a/cmd/mi/services/posse/posse.go
+++ b/cmd/mi/services/posse/posse.go
@@ -65,7 +65,8 @@ func New(ctx context.Context, dao *models.DAO, cfg Config) (*Announcer, error) {
func (a *Announcer) Announce(ctx context.Context, it *jsonfeed.Item) (*emptypb.Empty, error) {
switch {
case strings.Contains(it.GetUrl(), "svc.alrest.xeserv.us"),
- strings.Contains(it.GetUrl(), "shark-harmonic.ts.net"):
+ strings.Contains(it.GetUrl(), "shark-harmonic.ts.net"),
+ strings.Contains(it.GetUrl(), "preview.xeiaso.net"):
slog.Info("skipping announcement", "url", it.GetUrl(), "reason", "staging URLs")
return &emptypb.Empty{}, nil
}
diff --git a/cmd/xedn/internal/xesite/xesite.go b/cmd/xedn/internal/xesite/xesite.go
deleted file mode 100644
index 7ef525b..0000000
--- a/cmd/xedn/internal/xesite/xesite.go
+++ /dev/null
@@ -1,210 +0,0 @@
-package xesite
-
-import (
- "archive/zip"
- "compress/gzip"
- "encoding/json"
- "fmt"
- "io"
- "log/slog"
- "net/http"
- "os"
- "path/filepath"
- "sync"
- "time"
-)
-
-const (
- compressionGZIP = 0x69
-)
-
-func init() {
- zip.RegisterCompressor(compressionGZIP, func(w io.Writer) (io.WriteCloser, error) {
- return gzip.NewWriterLevel(w, gzip.BestCompression)
- })
- zip.RegisterDecompressor(compressionGZIP, func(r io.Reader) io.ReadCloser {
- rdr, err := gzip.NewReader(r)
- if err != nil {
- slog.Error("can't read from gzip stream", "err", err)
- panic(err)
- }
- return rdr
- })
-}
-
-type ZipServer struct {
- dir string
- lock sync.RWMutex
- zip *zip.ReadCloser
-}
-
-func NewZipServer(zipPath, dir string) (*ZipServer, error) {
- result := &ZipServer{dir: dir}
-
- if _, err := os.Stat(zipPath); !os.IsNotExist(err) {
- file, err := zip.OpenReader(zipPath)
- if err != nil {
- return nil, err
- }
-
- result.zip = file
- }
-
- return result, nil
-}
-
-func (zs *ZipServer) NukeGeneration(w http.ResponseWriter, r *http.Request) {
- gen := r.URL.Query().Get("gen")
-
- os.Remove(filepath.Join(zs.dir, "xesite", gen))
-
- fmt.Fprintln(w, "removed", gen)
-}
-
-func (zs *ZipServer) ListGenerations(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
-
- // list files in zs.dir/xesite
- var files []string
-
- dirEntries, err := os.ReadDir(filepath.Join(zs.dir, "xesite"))
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- slog.Error("can't read dir", "err", err, "dir", filepath.Join(zs.dir, "xesite"))
- return
- }
-
- for _, dirEntry := range dirEntries {
- if dirEntry.IsDir() {
- continue
- }
-
- if dirEntry.Name() == "latest.zip" {
- continue
- }
-
- files = append(files, dirEntry.Name())
- }
-
- json.NewEncoder(w).Encode(files)
-}
-
-func (zs *ZipServer) UploadNewZip(w http.ResponseWriter, r *http.Request) {
- fname := fmt.Sprintf("xesite-%s.zip", time.Now().Format("2006-01-02T15-04-05"))
- fpath := filepath.Join(zs.dir, "xesite", fname)
- fout, err := os.Create(fpath)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- slog.Error("can't create file", "err", err, "fpath", fpath)
- return
- }
- defer fout.Close()
-
- if _, err := io.Copy(fout, r.Body); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- slog.Error("can't write file", "err", err, "fpath", fpath)
- return
- }
-
- if err := fout.Close(); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- slog.Error("can't close file", "err", err, "fpath", fpath)
- return
- }
-
- if err := zs.Update(fpath); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- slog.Error("can't update zip", "err", err, "fpath", fpath)
- return
- }
-
- os.Link(fpath, filepath.Join(zs.dir, "xesite", "latest.zip"))
- if err := deleteOldestFileInFolder(filepath.Join(zs.dir, "xesite")); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- slog.Error("can't delete oldest file", "err", err)
- return
- }
-}
-
-func (zs *ZipServer) Update(fname string) error {
- zs.lock.Lock()
- defer zs.lock.Unlock()
-
- old := zs.zip
-
- file, err := zip.OpenReader(fname)
- if err != nil {
- return err
- }
-
- zs.zip = file
-
- if old != nil {
- old.Close()
- }
-
- slog.Info("activated new generation", "fname", fname)
- return nil
-}
-
-func (zs *ZipServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- zs.lock.RLock()
- defer zs.lock.RUnlock()
-
- if zs.zip == nil {
- http.Error(w, "no zip file", http.StatusNotFound)
- return
- }
-
- http.FileServer(http.FS(zs.zip)).ServeHTTP(w, r)
-}
-
-func deleteOldestFileInFolder(folderPath string) error {
- // Read the files in the folder
- files, err := os.ReadDir(folderPath)
- if err != nil {
- return err
- }
-
- if len(files) == 0 {
- slog.Debug("no files in the folder")
- return nil
- }
-
- if len(files) < 5 {
- slog.Debug("less than 5 files in the folder")
- return nil
- }
-
- // Initialize variables to keep track of the oldest file
- var oldestFile os.DirEntry
- var oldestTime time.Time
-
- // Find the oldest file in the folder
- for _, file := range files {
- fileInfo, err := file.Info()
- if err != nil {
- return err
- }
-
- if fileInfo.Name() == "latest.zip" {
- continue
- }
-
- if oldestFile == nil || fileInfo.ModTime().Before(oldestTime) {
- oldestFile = file
- oldestTime = fileInfo.ModTime()
- }
- }
-
- // Delete the oldest file
- oldestFilePath := filepath.Join(folderPath, oldestFile.Name())
- err = os.Remove(oldestFilePath)
- if err != nil {
- return err
- }
-
- slog.Info("deleted oldest generation", "path", oldestFilePath)
-
- return nil
-}
diff --git a/cmd/xedn/main.go b/cmd/xedn/main.go
index 5ede100..1b545d4 100644
--- a/cmd/xedn/main.go
+++ b/cmd/xedn/main.go
@@ -26,8 +26,8 @@ import (
"tailscale.com/metrics"
"tailscale.com/tsnet"
"tailscale.com/tsweb"
- "within.website/x/cmd/xedn/internal/xesite"
"within.website/x/internal"
+ "within.website/x/internal/xesite"
"within.website/x/web/fly/flymachines"
"within.website/x/web/stablediffusion"
)