diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 1eecd60..0000000 --- a/.editorconfig +++ /dev/null @@ -1,3 +0,0 @@ -[{Makefile,go.mod,go.sum,*.go,.gitmodules}] -indent_style = tab -indent_size = 4 diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7a655a4..21273ee 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -21,7 +21,9 @@ jobs: run: go build -v ./.. - name: Upload a Build Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3.1.3 with: + # Artifact name name: "catApi" + # A file, directory or wildcard pattern that describes what to upload path: "catApi" diff --git a/README.md b/README.md index 3e42cd8..2a2866b 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,39 @@ # 🐾 catApi -catApi is a minimal, self-hostable API (and frontend) for serving pictures of... -you guessed, it cats! Who doesn't like cats? +> **catApi** is a minimal, self-hostable API endpoint for serving pictures of, you guessed it, cats! +> but it could be used to serve anything, really -## API Documentation +## Usage -There are several API endpoints that you may query. +There are two ways to "use" **catApi** - you can either serve it, blessing the world with pictures of your cat +or be served. Below is the API documentation for visiting an existing instance of catApi. -### `/api/id` +### API Documentation + +**catApi** exposes several endpoints. + +#### ID `/api/id` will return the image with the associated ID. -For example **`http://localhost:3000/api/id?id=3`** will return the image with -the ID of "3". +For example **`http://localhost:3000/api/id?id=3`** will return the image with the ID of "3". -### `/api/list` +#### List -`/api/list` will return return a JSON object containing data about the images -within the /images directory +`/api/list` will return eturn a JSON object containing data about the images within the /images directory -For example, querying **`http://localhost:3000/api/random`** will return a JSON -object that might be as follows +For example, **`http://localhost:3000/api/random`** will return a JSON object that might be as follows -```json -[ - { "filename": "0.jpg", "id": "0", "url": "/api/id?id=0" }, - { "filename": "1.jpg", "id": "1", "url": "/api/id?id=1" }, - { "filename": "10.jpg", "id": "2", "url": "/api/id?id=2" }, - { "filename": "11.jpg", "id": "3", "url": "/api/id?id=3" } -] -``` +> `[{"id":"0","url":"/api/id?id=0"},{"id":"1","url":"/api/id?id=1"},{"id":"2","url":"/api/id?id=2"}]` -### `/api/random` +#### Random `/api/random` will return a random image from the list of available images +### Self-hosting + +TODO + ## License -**catApi** is licensed under the [MIT License](./LICENSE) +> **catApi** is licensed under the [MIT](https://github.com/NotAShelf/catApi/blob/v2/LICENSE) license. diff --git a/main.go b/main.go index 592de8f..0496b63 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,8 @@ package main import ( "encoding/json" + "flag" "html/template" - "image" "log" "math/rand" "net/http" @@ -20,13 +20,6 @@ var images []string var logger = logrus.New() var port string -// Cache for image list, it should expire every 10 minutes -// but until it does, images should load faster in the frontend. -var cachedImages struct { - images []string - expiry time.Time -} - // Okay I admit, this is bad. Just a workaround for now, until I figure out a clean // way of displaying all images in a grid. var tmpl = template.Must(template.New("index").Parse(` @@ -85,12 +78,13 @@ func main() { viper.SetConfigName("config") // name of config file (without extension) viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name viper.AddConfigPath(".") // path to look for the config file in - if err := viper.ReadInConfig(); err != nil { log.Fatalf("Error reading configuration file: %v", err) } port = viper.GetString("server.port") + + flag.Parse() images = getImages() // Add request logging middleware @@ -110,14 +104,6 @@ func main() { log.Fatal(http.ListenAndServe(":"+port, nil)) } -func getCachedImages() []string { - if time.Now().After(cachedImages.expiry) { - cachedImages.images = getImages() - cachedImages.expiry = time.Now().Add(10 * time.Minute) - } - return cachedImages.images -} - func getImages() []string { files, err := os.ReadDir("images/") if err != nil { @@ -140,12 +126,11 @@ func homeHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") tmpl.Execute(w, struct { Images []string - }{Images: getCachedImages()}) + }{Images: images}) } func idHandler(w http.ResponseWriter, r *http.Request) { id := r.URL.Query().Get("id") - if id == "" { http.Error(w, "Missing id", http.StatusBadRequest) return @@ -174,13 +159,11 @@ func isValidImagePath(path string) bool { } func listHandler(w http.ResponseWriter, r *http.Request) { - imageList := []map[string]interface{}{} - for i := range getCachedImages() { - imageInfo := map[string]interface{}{ - "id": strconv.Itoa(i), - "url": "/api/id?id=" + strconv.Itoa(i), - "filename": images[i], - "size": getImageSize("images/" + images[i]), + imageList := []map[string]string{} + for i := range images { + imageInfo := map[string]string{ + "id": strconv.Itoa(i), + "url": "/api/id?id=" + strconv.Itoa(i), } imageList = append(imageList, imageInfo) } @@ -195,35 +178,6 @@ func listHandler(w http.ResponseWriter, r *http.Request) { w.Write(jsonData) } -func getImageSize(path string) map[string]interface{} { - file, err := os.Open(path) - if err != nil { - logger.WithError(err).Error("Error opening file for size") - return nil - } - defer file.Close() - - // Decode image to get dimensions (JPEG/PNG only) - img, _, err := image.Decode(file) - if err != nil { - logger.WithError(err).Error("Error decoding image") - return nil - } - - // Get file info for size - fileInfo, err := file.Stat() - if err != nil { - logger.WithError(err).Error("Error getting file info") - return nil - } - - return map[string]interface{}{ - "width": img.Bounds().Dx(), - "height": img.Bounds().Dy(), - "size": fileInfo.Size(), - } -} - func logRequest(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() @@ -235,7 +189,8 @@ func logRequest(next http.Handler) http.Handler { } func randomHandler(w http.ResponseWriter, r *http.Request) { - rand.New(rand.NewSource(time.Now().UnixNano())) + source := rand.NewSource(time.Now().UnixNano()) + rand.New(source) i := rand.Intn(len(images)) http.Redirect(w, r, "/api/id?id="+strconv.Itoa(i), http.StatusSeeOther)