diff --git a/.output-temp/cat.png b/.output-temp/cat.png
new file mode 100644
index 0000000..53a3c23
Binary files /dev/null and b/.output-temp/cat.png differ
diff --git a/.output-temp/cat.webp b/.output-temp/cat.webp
new file mode 100644
index 0000000..23961c4
Binary files /dev/null and b/.output-temp/cat.webp differ
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..c4dd945
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,27 @@
+FROM golang:1.25.3 AS build-stage
+
+WORKDIR /app
+
+COPY go.mod go.sum ./
+
+RUN go mod download
+
+COPY *.go ./
+
+RUN CGO_ENABLED=0 GOOS=linux go build -o /app.bin
+
+from build-stage AS run-tests-stage
+RUN go test -v ./
+
+FROM gcr.io/distroless/base-debian11 AS build-release-stage
+
+WORKDIR /
+
+COPY --from=build-stage /app.bin /app.bin
+
+EXPOSE ${LISTEN_PORT}/tcp
+
+USER nonroot:nonroot
+
+ENTRYPOINT ["/app.bin"]
+CMD ["-p", "${LISTEN_PORT}"]
\ No newline at end of file
diff --git a/cat.png b/cat.png
new file mode 100644
index 0000000..53a3c23
Binary files /dev/null and b/cat.png differ
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000..0251f18
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,28 @@
+services:
+ backend:
+ image: rither:dev
+ environment:
+ - LISTEN_PORT=${BACKEND_PORT}
+ ports:
+ - "13200:13200"
+ networks:
+ - shared
+
+ proxy:
+ environment:
+ - BACKEND_PORT=${BACKEND_PORT}
+ image: nginx
+ volumes:
+ - ./nginx/templates/:/etc/nginx/templates/
+ ports:
+ - "8080:80"
+ - "13201:8080"
+ restart: always
+ networks:
+ - shared
+ depends_on:
+ - backend
+
+networks:
+ shared:
+ driver: bridge
\ No newline at end of file
diff --git a/docker/backend.local.conf b/docker/backend.local.conf
new file mode 100644
index 0000000..ca4a052
--- /dev/null
+++ b/docker/backend.local.conf
@@ -0,0 +1,8 @@
+server {
+ listen 8080;
+
+ location / {
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_pass http://localhost:13200;
+ }
+}
\ No newline at end of file
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000..4932e46
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+ Dither
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/index.js b/frontend/index.js
new file mode 100644
index 0000000..e69de29
diff --git a/go.mod b/go.mod
index dcbb6f8..f6efa02 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.25.3
require (
github.com/gin-gonic/gin v1.11.0
- github.com/makeworld-the-better-one/dither v1.0.0
+ github.com/makeworld-the-better-one/dither/v2 v2.4.0
)
require (
diff --git a/go.sum b/go.sum
index 996326e..c045458 100644
--- a/go.sum
+++ b/go.sum
@@ -34,8 +34,8 @@ github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzh
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
-github.com/makeworld-the-better-one/dither v1.0.0 h1:sBZdGV4o6MG6UMMRJhzDhruwlt99yQe0ChwgL29LMWg=
-github.com/makeworld-the-better-one/dither v1.0.0/go.mod h1:iYNC2QRNGWaeJ7G6eiItq30v4ZRPHOb2Od6g7AFYehI=
+github.com/makeworld-the-better-one/dither/v2 v2.4.0 h1:Az/dYXiTcwcRSe59Hzw4RI1rSnAZns+1msaCXetrMFE=
+github.com/makeworld-the-better-one/dither/v2 v2.4.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
diff --git a/main.go b/main.go
index d135144..8638464 100644
--- a/main.go
+++ b/main.go
@@ -5,17 +5,16 @@ import (
"fmt"
"image"
"image/color"
+ "image/jpeg"
+ "image/png"
"net/http"
+ "os"
"strconv"
"github.com/gin-gonic/gin"
- "github.com/makeworld-the-better-one/dither"
+ "github.com/makeworld-the-better-one/dither/v2"
)
-func ditherImage(src image.Image) {
-
-}
-
func hw(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"message": 123})
}
@@ -24,6 +23,113 @@ func heart(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"emo": "<3"})
}
+func ditherImageHandler(ctx *gin.Context) {
+ fileHeader, fileErr := ctx.FormFile("source")
+
+ if fileErr != nil {
+ fmt.Printf("Could not fetch form file; %v", fileErr)
+ ctx.Status(http.StatusBadRequest)
+ return
+ }
+
+ sourceFile, sourceFileErr := fileHeader.Open()
+
+ if sourceFileErr != nil {
+
+ fmt.Printf("Could not open source file; %v", sourceFileErr)
+ ctx.Status(http.StatusBadRequest)
+ return
+ }
+
+ defer sourceFile.Close()
+
+ img, formatName, decodeErr := image.Decode(sourceFile)
+
+ fmt.Printf("Format: %v", formatName)
+
+ if decodeErr != nil {
+ fmt.Printf("Failed to decode image from stream: %v", decodeErr)
+ ctx.JSON(http.StatusBadRequest, gin.H{"error:": decodeErr.Error()})
+ return
+ }
+
+ palette := []color.Color{color.Black, color.White}
+
+ ditherer := dither.NewDitherer(palette)
+ ditherer.Matrix = dither.FalseFloydSteinberg
+
+ ditheredImage := ditherer.DitherCopy(img)
+
+ var encodeErr error
+
+ switch formatName {
+ case "png":
+ encodeErr = png.Encode(ctx.Writer, ditheredImage)
+ case "jpeg":
+ options := jpeg.Options{Quality: 100}
+ encodeErr = jpeg.Encode(ctx.Writer, ditheredImage, &options)
+ default:
+ fmt.Printf("Failed to encode dithered image: %v", decodeErr)
+ ctx.JSON(http.StatusBadRequest, gin.H{"error:": "format unsupported: " + formatName})
+ }
+
+ if encodeErr != nil {
+ fmt.Printf("Failed to encode dithered image: %v", decodeErr)
+ ctx.JSON(http.StatusBadRequest, gin.H{"error:": encodeErr.Error()})
+ return
+ }
+
+ if encodeErr != nil {
+ fmt.Printf("Failed to stream image: %v", encodeErr)
+
+ ctx.JSON(http.StatusBadRequest, gin.H{"error:": encodeErr.Error()})
+ return
+ }
+
+ ctx.Header("Content-Type", "image/png")
+ ctx.Status(http.StatusCreated)
+}
+
+func ditherCat(ctx *gin.Context) {
+ picStream, err := os.Open("./.output-temp/cat.png")
+
+ if err != nil {
+ fmt.Printf("Failed to stream image: %v", err)
+
+ ctx.JSON(http.StatusBadRequest, gin.H{"error:": err.Error()})
+ return
+ }
+
+ defer picStream.Close()
+
+ img, formatName, decodeErr := image.Decode(picStream)
+
+ fmt.Printf("Format: %v", formatName)
+
+ if decodeErr != nil {
+ fmt.Printf("Failed to decode image from stream: %v", decodeErr)
+ ctx.JSON(http.StatusBadRequest, gin.H{"error:": decodeErr.Error()})
+ return
+ }
+
+ palette := []color.Color{color.Black, color.White}
+
+ ditherer := dither.NewDitherer(palette)
+ ditherer.Matrix = dither.FalseFloydSteinberg
+
+ ditheredImage := ditherer.DitherCopy(img)
+
+ encodeErr := png.Encode(ctx.Writer, ditheredImage)
+
+ if encodeErr != nil {
+ fmt.Printf("Failed to encode dithered image: %v", decodeErr)
+ ctx.JSON(http.StatusBadRequest, gin.H{"error:": encodeErr.Error()})
+ return
+ }
+
+ ctx.Header("Content-Type", "image/png")
+}
+
func main() {
port := flag.Int("p", 8080, "port to listen on")
@@ -31,13 +137,10 @@ func main() {
router := gin.Default()
- palette := []color.Color{color.Black, color.White}
-
- ditherer := dither.NewDitherer(palette)
- ditherer.Matrix = dither.FalseFloydSteinberg
-
router.GET("/", hw)
router.GET("/heart", heart)
+ router.GET("/cat", ditherCat)
+ router.POST("/dither", ditherImageHandler)
//router.POST("dither",ditherImage())
diff --git a/nginx/sites-available/backend.local.conf b/nginx/sites-available/backend.local.conf
new file mode 100644
index 0000000..ca4a052
--- /dev/null
+++ b/nginx/sites-available/backend.local.conf
@@ -0,0 +1,8 @@
+server {
+ listen 8080;
+
+ location / {
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_pass http://localhost:13200;
+ }
+}
\ No newline at end of file
diff --git a/nginx/sites-enabled/backend.local.conf b/nginx/sites-enabled/backend.local.conf
new file mode 120000
index 0000000..4684b38
--- /dev/null
+++ b/nginx/sites-enabled/backend.local.conf
@@ -0,0 +1 @@
+/etc/nginx/sites-available/backend.local.conf
\ No newline at end of file
diff --git a/nginx/templates/default_cfg.conf.template b/nginx/templates/default_cfg.conf.template
new file mode 100644
index 0000000..7f97488
--- /dev/null
+++ b/nginx/templates/default_cfg.conf.template
@@ -0,0 +1,13 @@
+server {
+ listen 8080;
+
+ location / {
+ proxy_pass http://backend:${BACKEND_PORT};
+
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ proxy_set_header Testing-Header "$remote_addr $host";
+ }
+}
diff --git a/Dockerfile.multistage b/sxdgdxfgdsfg
similarity index 100%
rename from Dockerfile.multistage
rename to sxdgdxfgdsfg