Added Image metadata and made spice lvl editable

Also added db support
This commit is contained in:
2025-08-28 13:26:16 +02:00
parent 27e7df7c3c
commit 337c9f6040
14 changed files with 243 additions and 75 deletions

5
.example.env Normal file
View File

@@ -0,0 +1,5 @@
POSTGRES_USER=
POSTGRES_PWD=
POSTGRES_DB=
POSTGRES_HOST=
POSTGRES_PORT=

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/tmp
/uploads/*
/uploads/*
.env

View File

@@ -1,57 +1,16 @@
package api
import (
"Advertisement_Panel/db"
"encoding/json"
"net/http"
"os"
"path/filepath"
)
type FileData struct {
ImageNames []string
SpicyImageNames []string
AsciiFiles []AsciiEntry
}
type AsciiEntry struct {
Name string
FontSize int
}
const staticDir = "static"
// move FileData and AsciiEntry here if you want, or leave in main.go
func FileHandler(w http.ResponseWriter, r *http.Request) {
data := FileData{
ImageNames: []string{},
SpicyImageNames: []string{},
AsciiFiles: []AsciiEntry{},
}
var data []db.ImagesEntry
files, _ := os.ReadDir(filepath.Join(staticDir, "images"))
for _, file := range files {
fileName := file.Name()
data.ImageNames = append(data.ImageNames, filepath.Join("/", staticDir, "images", fileName))
}
files, _ = os.ReadDir(filepath.Join("uploads"))
for _, file := range files {
fileName := file.Name()
data.ImageNames = append(data.ImageNames, filepath.Join("/uploads", fileName))
}
files, _ = os.ReadDir(filepath.Join(staticDir, "spicy"))
for _, file := range files {
fileName := file.Name()
data.ImageNames = append(data.ImageNames, filepath.Join("/", staticDir, "spicy", fileName))
}
files, _ = os.ReadDir(filepath.Join(staticDir, "ascii_art"))
for _, file := range files {
fileName := file.Name()
data.AsciiFiles = append(data.AsciiFiles,
AsciiEntry{Name: filepath.Join("/", staticDir, "ascii_art", fileName), FontSize: 12},
)
}
data = db.GetImageMetadata()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)

52
api/MetadataSaving.go Normal file
View File

@@ -0,0 +1,52 @@
package api
import (
"Advertisement_Panel/db"
"encoding/json"
"io"
"log"
"net/http"
)
func SaveMetadataHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request body", http.StatusBadRequest)
return
}
defer r.Body.Close()
var newMetadata []db.ImagesEntry
log.Println(string(body))
err = json.Unmarshal(body, &newMetadata)
if err != nil {
log.Println("Error unmarshalling JSON:", err)
http.Error(w, "Failed to parse JSON", http.StatusBadRequest)
return
}
var currentMetadata []db.ImagesEntry
currentMetadata = db.GetImageMetadata()
for _, newEntry := range newMetadata {
for _, currentEntry := range currentMetadata {
if newEntry.ID == currentEntry.ID {
if newEntry.SpiceLevel != currentEntry.SpiceLevel {
_, err := db.DB.Exec("UPDATE images SET spice_level = $1 WHERE id = $2", newEntry.SpiceLevel, newEntry.ID)
if err != nil {
http.Error(w, "Failed to update database", http.StatusInternalServerError)
return
}
}
break
}
}
}
}

View File

@@ -1,6 +1,7 @@
package api
import (
"Advertisement_Panel/db"
"fmt"
"io"
"net/http"
@@ -33,4 +34,5 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
}
fmt.Fprint(w, "<script>location.href = '/admin/'</script>")
fmt.Fprintf(w, "Image uploaded successfully: %s", header.Filename)
db.DB.Exec("INSERT INTO images (path, spice_level) VALUES ($1, $2)", "/uploads/"+header.Filename, 0)
}

45
config/env.go Normal file
View File

@@ -0,0 +1,45 @@
package config
import (
"os"
// this will automatically load your .env file:
_ "github.com/joho/godotenv/autoload"
)
type Config struct {
Logs LogConfig
DB PostgresConfig
Port string
}
type LogConfig struct {
Style string
Level string
}
type PostgresConfig struct {
Username string
Password string
Db string
Host string
Port string
}
var Cfg *Config
func LoadConfig() {
Cfg = &Config{
Port: os.Getenv("PORT"),
Logs: LogConfig{
Style: os.Getenv("LOG_STYLE"),
Level: os.Getenv("LOG_LEVEL"),
},
DB: PostgresConfig{
Username: os.Getenv("POSTGRES_USER"),
Password: os.Getenv("POSTGRES_PWD"),
Db: os.Getenv("POSTGRES_DB"),
Host: os.Getenv("POSTGRES_HOST"),
Port: os.Getenv("POSTGRES_PORT"),
},
}
}

58
db/db.go Normal file
View File

@@ -0,0 +1,58 @@
package db
import (
"Advertisement_Panel/config"
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
)
type ImagesEntry struct {
ID int `json:"id"`
Path string `json:"path"`
SpiceLevel int `json:"spice_level"`
}
var DB *sql.DB
func Init() {
log.Println("Initializing database connection...")
connStr := fmt.Sprintf("host=%s port=%s user=%s "+
"password=%s dbname=%s sslmode=disable",
config.Cfg.DB.Host, config.Cfg.DB.Port, config.Cfg.DB.Username, config.Cfg.DB.Password, config.Cfg.DB.Db)
log.Println(connStr)
var err error
DB, err = sql.Open("postgres", connStr)
if err != nil {
log.Println("Error connecting to the database: ", err)
}
}
func GetImageMetadata() []ImagesEntry {
rows, err := DB.Query("SELECT id, path, spice_level FROM images")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
var data []ImagesEntry
for rows.Next() {
var id int
var path string
var spiceLevel int
if err := rows.Scan(&id, &path, &spiceLevel); err != nil {
log.Fatal(err)
}
data = append(data, ImagesEntry{ID: id, Path: path, SpiceLevel: spiceLevel})
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
return data
}

5
go.mod
View File

@@ -1,3 +1,8 @@
module Advertisement_Panel
go 1.24
require (
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9 // indirect
)

8
go.sum Normal file
View File

@@ -0,0 +1,8 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=

View File

@@ -2,6 +2,8 @@ package main
import (
"Advertisement_Panel/api"
"Advertisement_Panel/config"
"Advertisement_Panel/db"
"fmt"
"html/template"
"net/http"
@@ -14,6 +16,9 @@ func main() {
fmt.Print("Now running server!\n")
config.LoadConfig()
db.Init()
// Serves index
http.HandleFunc("/", index_handler)
@@ -32,7 +37,9 @@ func main() {
http.HandleFunc("/admin/", admin_handler)
// Handles image upload
http.HandleFunc("/upload", api.UploadHandler)
http.HandleFunc("/api/upload", api.UploadHandler)
// Handles metadata saving
http.HandleFunc("/api/save-metadata", api.SaveMetadataHandler)
fmt.Print("Webserver running on http://localhost:8080\n")

View File

@@ -32,7 +32,7 @@ uploadForm.on("submit", function(e) {
formData.append("image", currentFile);
$.ajax({
url: "/upload",
url: "/api/upload",
type: "POST",
data: formData,
processData: false,

View File

@@ -1,7 +1,6 @@
let galleryTable = $("#imagesGallery")
let refreshButton = $("#refresh-gallery")
let filesList = []
let saveButton = $("#save-gallery")
let fileMetaData = []
@@ -10,44 +9,51 @@ async function fetchImages() {
url: '/files',
type: 'GET',
success: function(response) {
filesList = response;
console.log(filesList);
fileMetaData = response;
console.log(fileMetaData);
},
error: function() {
alert('Error fetching images.');
}
});
filesList.ImageNames.forEach((filePath, i) => {
fileMetaData.forEach((file, i) => {
let newRow = $(`
<tr>
<td>`+ i +`</td>
<td>
<img src="` + filePath + `" class="gallery-image" />
<img src="` + file.path + `" class="gallery-image" />
</td>
<td class="spice-slider-td">
<output id="spice-output-` + i + `">0</output>
<input type="range" min="0" max="10" value="` + 0 + `" class="spice-slider" id="spice-slider-` + i + `" />
<output id="spice-output-` + file.id + `">` + file.spice_level +`</output>
<input type="range" min="0" max="10" value="` + file.spice_level + `" class="spice-slider" id="spice-slider-` + file.id + `" />
</td>
</tr>
`);
galleryTable.append(newRow);
fileMetaData.push({
"id": i,
"spice_level": 0,
"path": filePath
});
});
fileMetaData.forEach(file => {
$("#spice-slider-" + file.id).on("input", function() {
file.spice_level = this.value;
file.spice_level = Number(this.value);
$("#spice-output-" + file.id).text(this.value);
console.log(fileMetaData);
})
});
}
saveButton.on("click", function() {
$.ajax({
url: '/api/save-metadata',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(fileMetaData),
success: function() {
alert('Metadata saved successfully.');
},
error: function() {
alert('Error saving metadata.');
}
});
})
refreshButton.on("click", function() {
galleryTable.empty();
fetchImages();

View File

@@ -26,7 +26,9 @@
</div>
<div class="tab-container" id="tab-2">
<h1>
Images <button id="refresh-gallery">Refresh</button>
Images
<button id="refresh-gallery">Refresh</button>
<button id="save-gallery">Save</button>
</h1>
<div>
<table id="imagesGallery">

View File

@@ -11,20 +11,38 @@
<body>
<img id="images" src="/static/images/pvv_logo.png" alt="images">
<script>
function setImage(path) {
document.getElementById("images").src = path;
}
let imageMetadata = [];
fetch("/files")
.then(res => res.json())
.then(files => {
const images = files.ImageNames
console.log(images)
let index = 0;
setInterval(() => {
index = (index + 1) % images.length;
document.getElementById("images").src = images[index];
}, 1000);
.then(res => res.json())
.then(files => {
imageMetadata = files
let index = 1;
setInterval(() => {
if(index > imageMetadata.length) index = 0;
if(index == 0) {
setImage("/static/images/pvv_logo.png");
} else {
setImage(imageMetadata[index-1].path);
}
index++;
}, 4000);
if(images.length < 1) {
setImage("/static/images/pvv_logo.png");
return
} else {
setTimeout(() => {
window.location.reload();
//window.location.reload();
}, images.length*2*1000);
});
}
});
</script>
</body>