From c07ca9c9a54944544017b31156dabc5a9115461a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andrej=20Rama=C5=A1euski?= <andrej@x2.cz>
Date: Sat, 30 Nov 2024 02:08:35 +0100
Subject: [PATCH] Golang version

---
 .dockerignore      |  4 ----
 .env               |  2 ++
 .gitlab-ci.yml     | 10 ++++----
 Dockerfile         | 16 ++++++-------
 VERSION            |  1 +
 compose.yaml       |  7 ++++++
 go.mod             | 14 +++++++++++
 go.sum             |  8 +++++++
 main.go            | 40 +++++++++++++++++++++++++++++++
 octopus/go.mod     |  3 +++
 octopus/octopus.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++
 piratar            | 41 -------------------------------
 12 files changed, 148 insertions(+), 58 deletions(-)
 delete mode 100644 .dockerignore
 create mode 100644 .env
 create mode 100644 VERSION
 create mode 100644 compose.yaml
 create mode 100644 go.mod
 create mode 100644 go.sum
 create mode 100644 main.go
 create mode 100644 octopus/go.mod
 create mode 100644 octopus/octopus.go
 delete mode 100755 piratar

diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 381df27..0000000
--- a/.dockerignore
+++ /dev/null
@@ -1,4 +0,0 @@
-**
-!piratar
-!piratar.conf
-!default.jpg
diff --git a/.env b/.env
new file mode 100644
index 0000000..c012b30
--- /dev/null
+++ b/.env
@@ -0,0 +1,2 @@
+export OCTOPUS=https://chobotnice.pirati.cz/graphql/
+export OCTPUS_DEBUG=1
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5ac9641..10d3766 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,11 +1,10 @@
-image: docker:20.10.9
+image: docker:latest
 
 variables:
   DOCKER_TLS_CERTDIR: "/certs"
-  IMAGE_VER: 2.1.0
 
 services:
-  - docker:20.10.9-dind
+  - docker:dind
 
 before_script:
   - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
@@ -13,7 +12,8 @@ before_script:
 build:
   stage: build
   script:
+    - VERSION=`cat VERSION`
     - docker pull $CI_REGISTRY_IMAGE:latest || true
-    - docker build --cache-from $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:$IMAGE_VER --tag $CI_REGISTRY_IMAGE:latest .
-    - docker push $CI_REGISTRY_IMAGE:$IMAGE_VER
+    - docker build --cache-from $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:$VERSION --tag $CI_REGISTRY_IMAGE:latest .
+    - docker push $CI_REGISTRY_IMAGE:$VERSION
     - docker push $CI_REGISTRY_IMAGE:latest
diff --git a/Dockerfile b/Dockerfile
index 3f4c2f3..0de64e5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,10 @@
-FROM alpine:latest
-RUN apk update && apk add perl-mojolicious perl-io-socket-ssl perl-app-cpanminus make
-RUN cpanm GraphQL::Client
+FROM golang:alpine AS build-stage
+ADD . ./
+RUN CGO_ENABLED=0 GOOS=linux go build -o /piratar
 
-ADD . /opt/piratar
-WORKDIR /opt/piratar
-
-USER nobody
+FROM alpine
+WORKDIR /app
+COPY --from=build-stage /piratar /app
+COPY default.jpg /app
 EXPOSE 3000
-CMD /opt/piratar/piratar daemon -c 3000
+ENTRYPOINT ["/app/piratar"]
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..4a36342
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+3.0.0
diff --git a/compose.yaml b/compose.yaml
new file mode 100644
index 0000000..6e0f8b7
--- /dev/null
+++ b/compose.yaml
@@ -0,0 +1,7 @@
+services:
+  app:
+    image: piratar
+    environment:
+        OCTOPUS: https://chobotnice.pirati.cz/graphql/
+    ports:
+      - "3000:3000"
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..68b7c45
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,14 @@
+module pirates/piratar
+
+go 1.22.2
+
+require pirates/piratar/octopus v0.0.0-00010101000000-000000000000
+
+require (
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/machinebox/graphql v0.2.2 // indirect
+	github.com/matryer/is v1.4.1 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+)
+
+replace pirates/piratar/octopus => ./octopus
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..2fe3d62
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,8 @@
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/machinebox/graphql v0.2.2 h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkBZfo=
+github.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=
+github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
+github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..8635994
--- /dev/null
+++ b/main.go
@@ -0,0 +1,40 @@
+package main
+
+import (
+    "fmt"
+    "log"
+    "path"
+    "strings"
+    "net/http"
+    "pirates/piratar/octopus"
+)
+
+func main() {
+	http.HandleFunc("/", handler)
+
+	port := 3000 // TODO: ENV
+	fmt.Printf("Piratar service listening on port %d...\n", port)
+
+	if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
+		log.Fatalf("Server failed: %v", err)
+	}
+}
+
+func handler(w http.ResponseWriter, r *http.Request) {
+
+    // Extrakce username nebo UUID
+    id := strings.ToLower(path.Base(r.URL.Path))
+    if dotIndex := strings.LastIndex(id, "."); dotIndex != -1 {
+		id = id[:dotIndex]
+	}
+
+    // Profilova fotka nebo default
+    if photo, err := octopus.ProfilePhoto(id); err == nil {
+	    fmt.Printf("Piratar for %s: %s\n", id, photo)
+	    http.Redirect(w, r, photo, http.StatusFound)
+    } else {
+	    fmt.Printf("Piratar for %s not found\n", id)
+   	    w.Header().Set("Content-Type", "image/jpeg")
+        http.ServeFile(w, r, "default.jpg")
+    }
+}
diff --git a/octopus/go.mod b/octopus/go.mod
new file mode 100644
index 0000000..17405e1
--- /dev/null
+++ b/octopus/go.mod
@@ -0,0 +1,3 @@
+module pirates/piratar/octopus
+
+go 1.22.2
diff --git a/octopus/octopus.go b/octopus/octopus.go
new file mode 100644
index 0000000..f7ef860
--- /dev/null
+++ b/octopus/octopus.go
@@ -0,0 +1,60 @@
+package octopus
+
+import (
+    "os"
+    "fmt"
+    "log"
+    "errors"
+    "context"
+    "github.com/google/uuid"
+    "github.com/machinebox/graphql"
+)
+
+func ProfilePhoto (id string) (string, error) {
+    var keyname string
+    var response struct {
+       AllPeople struct {
+           Edges []struct {
+               Node struct {
+                   ProfilePhoto string `json:"profilePhoto"`
+               }                       `json:"node"`
+           }                           `json:"edges"`
+       }                               `json:"allPeople"`
+    }
+
+    if _, err := uuid.Parse(id); err == nil {
+        keyname = "keycloakId"
+    } else {
+        keyname = "username"
+    }
+
+    // GraphAPI klient
+    octopus := graphql.NewClient(os.Getenv("OCTOPUS"))
+    if os.Getenv("DEBUG") != "" {
+        octopus.Log = func(s string) { log.Println(s) }
+    }
+
+    // Request
+    req := graphql.NewRequest(fmt.Sprintf(
+    `query profilePhoto($username: String!) {
+        allPeople(filters: {%s: {iExact: $username}}) {
+            edges { node { profilePhoto } }
+        }}
+    `, keyname ))
+
+    req.Var("username", id)
+
+// define a Context for the request
+    ctx := context.Background()
+
+// run it and capture the response
+    if err := octopus.Run(ctx, req, &response); err != nil {
+        log.Fatal(err)
+    }
+
+    if people := response.AllPeople.Edges; len(people) == 1 {
+        return people[0].Node.ProfilePhoto, nil
+    } else {
+        return "", errors.New("User not found")
+    }
+}
diff --git a/piratar b/piratar
deleted file mode 100755
index e1d838b..0000000
--- a/piratar
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env perl
-use Mojolicious::Lite -signatures;
-use Mojo::UserAgent;
-use Mojolicious::Static;
-use GraphQL::Client;
-use Mojo::Util qw(dumper);
-
-get '/*url' => sub ($c) {
-
-    my $username = lc($c->param('url'));
-    $username =~ s/^.*\///;
-    $username =~ s/\.(png|jpg|gif)$//;
-    $c->app->log->debug("Username: $username");
-
-    my $gq = GraphQL::Client->new(url => $ENV{OCTOPUS});
-    my $rc = $gq->execute(qq[ query MyQuery {
-        allPeople(filters: {username: {iExact: "$username"}}) {
-            edges { node { profilePhoto } }
-        }}],
-    );
-
-    if ($rc->{errors}) {
-        $c->app->log->warn("Octopus error: " . dumper $rc->{errors});
-    }
-    elsif ( $rc->{data} ) {
-        $c->app->log->debug("Octopus response: " . dumper $rc->{data});
-
-        my $user  = $rc->{data}{allPeople}{edges}[0]{node};
-
-        if ( my $photo = $user->{profilePhoto}) {
-            $c->app->log->info("Avatar for user $username: $photo");
-            $c->redirect_to($photo);
-            return;
-        }
-    }
-
-    $c->app->log->info("Avatar for user $username: FALLBACK");
-    $c->reply->file('default.jpg');
-};
-
-app->start;
-- 
GitLab