Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a60c7c495b | |||
| 337c9f6040 | |||
| 27e7df7c3c | |||
| 463a42d80f | |||
| 1e58cc3d3a | |||
| c05f7fea76 | |||
| 7e0a3cbcbe | |||
| adbd2dec20 | |||
| 403b2f878c | |||
| 4d81678c1d |
5
.example.env
Normal file
5
.example.env
Normal file
@@ -0,0 +1,5 @@
|
||||
POSTGRES_USER=
|
||||
POSTGRES_PWD=
|
||||
POSTGRES_DB=
|
||||
POSTGRES_HOST=
|
||||
POSTGRES_PORT=
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
/tmp
|
||||
/uploads/*
|
||||
.env
|
||||
12
README.md
12
README.md
@@ -27,15 +27,15 @@ $(go env GOPATH)/bin/air -c .air.toml
|
||||
|
||||
# Tasks
|
||||
|
||||
- [<] Interface for adding new images
|
||||
- [x] Interface for adding new images
|
||||
- [x] ASCII ART support
|
||||
- [ ] some sort of auth
|
||||
- [ ] Change image system to database
|
||||
- [ ] Add support for user inputed webassembly
|
||||
- [>] Change image system to database
|
||||
- [ ] Add support for user inputed webassembly
|
||||
- [ ] ATB integration
|
||||
- [ ] Show more spicy images after kl 22:00
|
||||
- [ ] Hide mouse cursor (do this though linux)
|
||||
- [ ] More images and memes???
|
||||
- [ ] Show more spicy images after kl 22:00
|
||||
- [>] Hide mouse cursor (do this though linux)
|
||||
- [x] More images and memes???
|
||||
|
||||
# NB!!!!
|
||||
Changes in the static directory will not rebuild the project, simple workaround is to just make a small change in main.go and save, for example adding a space
|
||||
|
||||
17
api/FileHandlers.go
Normal file
17
api/FileHandlers.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"Advertisement_Panel/db"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// move FileData and AsciiEntry here if you want, or leave in main.go
|
||||
func FileHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var data []db.ImagesEntry
|
||||
|
||||
data = db.GetImageMetadata()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
52
api/MetadataSaving.go
Normal file
52
api/MetadataSaving.go
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
38
api/UploadHandlers.go
Normal file
38
api/UploadHandlers.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"Advertisement_Panel/db"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
file, header, err := r.FormFile("image") // "image" is the name of the file input
|
||||
if err != nil {
|
||||
http.Error(w, "Error retrieving file", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
dst, err := os.Create("./uploads/" + header.Filename)
|
||||
if err != nil {
|
||||
http.Error(w, "Error creating file on server", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
if _, err := io.Copy(dst, file); err != nil {
|
||||
http.Error(w, "Error saving file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
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
45
config/env.go
Normal 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
58
db/db.go
Normal 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
5
go.mod
@@ -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
8
go.sum
Normal 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=
|
||||
35
main.go
35
main.go
@@ -1,23 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"Advertisement_Panel/src"
|
||||
"Advertisement_Panel/api"
|
||||
"Advertisement_Panel/config"
|
||||
"Advertisement_Panel/db"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var templ *template.Template
|
||||
var templates *template.Template
|
||||
|
||||
func main() {
|
||||
templ, _ = template.ParseGlob("templates/*.html")
|
||||
templates, _ = template.ParseGlob("website/templates/*.html")
|
||||
|
||||
fmt.Print("Now running server!\n")
|
||||
|
||||
config.LoadConfig()
|
||||
db.Init()
|
||||
|
||||
// Serves index
|
||||
http.HandleFunc("/", index_handler)
|
||||
|
||||
// Serves json for html to find file names
|
||||
http.HandleFunc("/files", src.FileHandler)
|
||||
http.HandleFunc("/files", api.FileHandler)
|
||||
|
||||
// Serves ascii page
|
||||
http.HandleFunc("/ascii", ascii_handler)
|
||||
@@ -25,14 +31,31 @@ func main() {
|
||||
// Serves images
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
|
||||
|
||||
http.Handle("/uploads/", http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads"))))
|
||||
|
||||
// Serves administration page
|
||||
http.HandleFunc("/admin/", admin_handler)
|
||||
|
||||
// Handles image upload
|
||||
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")
|
||||
|
||||
// Serves what ever the user is requesting base on above urls
|
||||
http.ListenAndServe(":8080", nil)
|
||||
|
||||
}
|
||||
|
||||
func index_handler(w http.ResponseWriter, r *http.Request) {
|
||||
templ.ExecuteTemplate(w, "images.html", nil)
|
||||
templates.ExecuteTemplate(w, "images.html", nil)
|
||||
}
|
||||
|
||||
func ascii_handler(w http.ResponseWriter, r *http.Request) {
|
||||
templ.ExecuteTemplate(w, "ascii.html", nil)
|
||||
templates.ExecuteTemplate(w, "ascii.html", nil)
|
||||
}
|
||||
|
||||
func admin_handler(w http.ResponseWriter, r *http.Request) {
|
||||
templates.ExecuteTemplate(w, "admin.html", nil)
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package src
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const staticDir = "static"
|
||||
|
||||
type FileData struct {
|
||||
ImageNames []string
|
||||
SpicyImageNames []string
|
||||
AsciiFiles []AsciiEntry
|
||||
}
|
||||
|
||||
type AsciiEntry struct {
|
||||
Name string
|
||||
FontSize int
|
||||
}
|
||||
|
||||
// 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{},
|
||||
}
|
||||
|
||||
dirs, err := os.ReadDir(staticDir)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
for _, dir := range dirs {
|
||||
dirName := dir.Name()
|
||||
|
||||
if strings.EqualFold(dirName, "images") {
|
||||
|
||||
files, _ := os.ReadDir(filepath.Join(staticDir, dirName))
|
||||
for _, file := range files {
|
||||
fileName := file.Name()
|
||||
data.ImageNames = append(data.ImageNames, filepath.Join(staticDir, dirName, fileName))
|
||||
}
|
||||
} else if strings.EqualFold(dirName, "spicy") {
|
||||
|
||||
files, _ := os.ReadDir(filepath.Join(staticDir, dirName))
|
||||
for _, file := range files {
|
||||
fileName := file.Name()
|
||||
data.SpicyImageNames = append(data.SpicyImageNames, filepath.Join(staticDir, dirName, fileName))
|
||||
}
|
||||
} else if strings.EqualFold(dirName, "ascii_art") {
|
||||
|
||||
files, _ := os.ReadDir(filepath.Join(staticDir, dirName))
|
||||
for _, file := range files {
|
||||
fileName := file.Name()
|
||||
data.AsciiFiles = append(data.AsciiFiles,
|
||||
AsciiEntry{Name: filepath.Join(staticDir, dirName, fileName), FontSize: 12},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
91
static/css/admin.css
Normal file
91
static/css/admin.css
Normal file
@@ -0,0 +1,91 @@
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: #101010;
|
||||
color: #fff;
|
||||
}
|
||||
.container {
|
||||
background: #212121;
|
||||
padding: 24px 32px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||
}
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #121212;
|
||||
}
|
||||
nav {
|
||||
padding: 10px 18px;
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.08)
|
||||
}
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
color: #ccc;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
input[type="file"] {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
button[type="submit"] {
|
||||
background: #007bff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 10px 18px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
}
|
||||
button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
.preview {
|
||||
margin-top: 16px;
|
||||
max-width: 100%;
|
||||
max-height: 200px;
|
||||
display: none;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.tab-container {
|
||||
display: none;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
.tab-container.active {
|
||||
display: block;
|
||||
}
|
||||
.tab-button {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 10px 18px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.tab-button:hover {
|
||||
background: #555;
|
||||
}
|
||||
.tab-button.active {
|
||||
background: #007bff;
|
||||
}
|
||||
.gallery-image {
|
||||
max-width: 90px;
|
||||
max-height: 70px;
|
||||
margin: 10px;
|
||||
border: 2px solid #444;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
22
static/css/global.css
Normal file
22
static/css/global.css
Normal file
@@ -0,0 +1,22 @@
|
||||
body {
|
||||
margin: 0;
|
||||
background: black;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
height: 100vh;
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
11
static/js/admin.js
Normal file
11
static/js/admin.js
Normal file
@@ -0,0 +1,11 @@
|
||||
let tab_buttons = $( ".tab-button" )
|
||||
let tab_containers = $( ".tab-container" )
|
||||
|
||||
tab_buttons.on( "click", function() {
|
||||
let id = $( this ).attr( "id" )
|
||||
tab_buttons.removeClass( "active" )
|
||||
tab_containers.removeClass( "active" )
|
||||
$( this ).addClass( "active" )
|
||||
console.log( id )
|
||||
$( "#tab-" + id ).addClass( "active" )
|
||||
})
|
||||
49
static/js/image_upload.js
Normal file
49
static/js/image_upload.js
Normal file
@@ -0,0 +1,49 @@
|
||||
let imageInput = $("#imageInput")
|
||||
let imagePreview = $("#preview")
|
||||
|
||||
let uploadForm = $("#imageForm")
|
||||
|
||||
let currentFile = null;
|
||||
|
||||
imageInput.on("change", function() {
|
||||
currentFile = this.files[0];
|
||||
if (currentFile) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
preview.src = e.target.result;
|
||||
preview.style.display = 'block';
|
||||
}
|
||||
reader.readAsDataURL(currentFile);
|
||||
} else {
|
||||
preview.style.display = 'none';
|
||||
}
|
||||
})
|
||||
|
||||
uploadForm.on("submit", function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!currentFile) {
|
||||
alert("Please select a file first.");
|
||||
return;
|
||||
}
|
||||
console.log(currentFile);
|
||||
|
||||
let formData = new FormData(this);
|
||||
formData.append("image", currentFile);
|
||||
|
||||
$.ajax({
|
||||
url: "/api/upload",
|
||||
type: "POST",
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
alert("Image uploaded successfully!");
|
||||
preview.style.display = 'none';
|
||||
currentFile = null;
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
alert("Error uploading image: " + xhr.responseText);
|
||||
}
|
||||
});
|
||||
})
|
||||
64
static/js/images.js
Normal file
64
static/js/images.js
Normal file
@@ -0,0 +1,64 @@
|
||||
let galleryTable = $("#imagesGallery")
|
||||
let refreshButton = $("#refresh-gallery")
|
||||
let saveButton = $("#save-gallery")
|
||||
|
||||
let fileMetaData = []
|
||||
|
||||
async function fetchImages() {
|
||||
await $.ajax({
|
||||
url: '/files',
|
||||
type: 'GET',
|
||||
success: function(response) {
|
||||
fileMetaData = response;
|
||||
console.log(fileMetaData);
|
||||
},
|
||||
error: function() {
|
||||
alert('Error fetching images.');
|
||||
}
|
||||
});
|
||||
|
||||
fileMetaData.forEach((file, i) => {
|
||||
let newRow = $(`
|
||||
<tr>
|
||||
<td>`+ i +`</td>
|
||||
<td>
|
||||
<img src="` + file.path + `" class="gallery-image" />
|
||||
</td>
|
||||
<td class="spice-slider-td">
|
||||
<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.forEach(file => {
|
||||
$("#spice-slider-" + file.id).on("input", function() {
|
||||
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();
|
||||
});
|
||||
|
||||
$( document ).ready(function() {
|
||||
fetchImages();
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" type="image/x-icon" href="/static/images/pvv_logo.png">
|
||||
<title>IMAGES</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background: black;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<img id="images" src="/static/images/pvv_logo.png" alt="images">
|
||||
<script>
|
||||
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);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
BIN
uploads/DSC_0094.jpg
Normal file
BIN
uploads/DSC_0094.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 MiB |
1
website/Admin.go
Normal file
1
website/Admin.go
Normal file
@@ -0,0 +1 @@
|
||||
package admin
|
||||
53
website/templates/admin.html
Normal file
53
website/templates/admin.html
Normal file
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Admin</title>
|
||||
<link rel="stylesheet" href="/static/css/admin.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<nav>
|
||||
<button class="tab-button" id="1">Image Upload</button>
|
||||
<button class="tab-button" id="2">Images</button>
|
||||
</nav>
|
||||
|
||||
<div class="tab-container active" id="tab-1">
|
||||
<div class="container" style="max-width: 400px;">
|
||||
<h2>Add New Image</h2>
|
||||
<form id="imageForm" enctype="multipart/form-data">
|
||||
<label for="imageInput">Select Image:</label>
|
||||
<input type="file" id="imageInput" name="image" accept="image/*" required><br>
|
||||
<button type="submit" style="width: 100%;">Upload</button>
|
||||
</form>
|
||||
<img id="preview" class="preview" alt="Image Preview">
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-container" id="tab-2">
|
||||
<h1>
|
||||
Images
|
||||
<button id="refresh-gallery">Refresh</button>
|
||||
<button id="save-gallery">Save</button>
|
||||
</h1>
|
||||
<div>
|
||||
<table id="imagesGallery">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Image</th>
|
||||
<th>Spice lvl</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="/static/js/admin.js"></script>
|
||||
<script src="/static/js/image_upload.js"></script>
|
||||
<script src="/static/js/images.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -5,24 +5,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" type="image/x-icon" href="/static/images/pvv_logo.png">
|
||||
<title>ASCII</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background: black;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: white;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/static/css/global.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
49
website/templates/images.html
Normal file
49
website/templates/images.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" type="image/x-icon" href="/static/images/pvv_logo.png">
|
||||
<link rel="stylesheet" href="/static/css/global.css">
|
||||
<title>IMAGES</title>
|
||||
</head>
|
||||
|
||||
<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 => {
|
||||
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();
|
||||
}, images.length*2*1000);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user