Files
WingetRepoServer/main.go
2024-06-30 15:36:33 +03:00

429 lines
14 KiB
Go

// Package main defines the entry point of the Go application.
package main
// Importing necessary packages for database interaction, web server functionality, logging, and more.
import (
"database/sql"
"fmt"
"html/template"
"io"
"log/slog"
"net/http"
"os"
"regexp"
"strings"
"time"
_ "modernc.org/sqlite" // Importing SQLite driver for database interaction.
)
var c chan bool
// readHtml function reads an HTML file and applies a CSS template to it before sending it to the client.
func readHtml(res http.ResponseWriter, _ *http.Request) {
g := template.CSS("./html/public/mod.css") // Define the CSS path.
f, err := template.ParseFiles("./html/wsb.html") // Parse the HTML file.
if err != nil {
slog.Error(err.Error())
// Log any errors encountered.
}
err = f.Execute(res, g) // Execute the template with the CSS and send it to the client.
if err != nil {
slog.Error(err.Error())
// Log any errors encountered.
}
}
// simpRequestResp sends a simple response to the client.
func simpRequestResp(res http.ResponseWriter, _ *http.Request) {
fmt.Fprint(res, "Hello, M'Lord", 153*4, " I'm here to serveè") // Print a formatted string to the client.
}
// getMsgFromDB retrieves a message from the database based on the provided ID and language.
func getMsgFromDB(sqldb *sql.DB, id string, lang string) string {
var lngs []string
lngs = append(lngs, lang) // Append the language to the slice.
query := generateSQL("msgs", lngs, [2]string{"content", id}) // Generate the SQL query.
res := sqldb.QueryRow(query) // Execute the query.
lng := ""
dt := res.Scan(&lng) // Scan the result into the lng variable.
if dt != nil {
return "" // Return an empty string if there's an error.
}
return lng // Return the retrieved message.
}
// queryPacks retrieves a list of packages from the database.
func queryPacks(sqldb *sql.DB, element *elements) []packages {
pkg_list := getPkgIdFromDB(sqldb) // Get the list of package IDs from the database.
pkgs := make([]packages, 0) // Initialize a slice to hold the packages.
for _, p := range pkg_list {
query := generateSQL("packs_all", []string{"*"}, [2]string{"id", p}) // Generate the SQL query for each package.
res, err := sqldb.Query(query) // Execute the query.
defer res.Close() // Ensure the result set is closed after the function returns.
err = res.Err() // Check for any errors during query execution.
if err != nil {
slog.Error(err.Error()) // Log and panic on any errors.
}
for res.Next() {
npkg := packages{} // Create a new package instance.
err := res.Scan(&npkg.Id, &npkg.Name, &npkg.Version, &npkg.ExtraCommand, &npkg.Tag, &npkg.Warning) // Scan the result into the package instance.
if err != nil {
slog.Error(err.Error()) // Log and exit on any errors.
}
npkg.Element = element // Associate the element with the package.
pkgs = append(pkgs, npkg) // Append the package to the slice.
}
}
return pkgs // Return the slice of packages.
}
// queryDatabase executes a SQL query expecting a single row and returns the result as a slice of strings.
// sqldb: The database connection object.
// queryRow: The SQL query string to be executed.
// Returns: A slice of strings representing the query result.
func queryDatabase(sqldb *sql.DB, queryRow string) []string {
res := sqldb.QueryRow(queryRow)
var values []string
err := res.Scan(&values)
if err != nil {
slog.Error(err.Error())
}
return values
}
// generateSQL constructs a SQL SELECT statement with optional WHERE clauses.
// table: The name of the database table.
// columns: A slice of strings representing the column names to be selected.
// arg: A variadic slice of string pairs representing WHERE clause conditions.
// Returns: A string representing the complete SQL SELECT statement.
func generateSQL(table string, columns []string, arg ...[2]string) string {
cols := strings.Join(columns, ",")
table = "SELECT " + cols + " FROM " + table
if len(arg) > 0 {
table += " WHERE "
for i, a := range arg {
table += a[0] + " like '" + a[1] + "'"
if i+1 < len(arg) {
table += " and "
}
}
}
return table
}
// searchDownloads performs a database query to retrieve download statistics.
// sqldb: The database connection object.
// table: The name of the database table.
// columns: A variadic number of strings representing the column names to be selected.
// Returns: A slice of DownloadElements structs containing the query results.
func searchDownloads(sqldb *sql.DB, table string, columns ...string) []DownloadElements {
query := generateSQL(table, columns)
runquery, err := sqldb.Query(query)
defer runquery.Close()
err = runquery.Err()
if err != nil {
slog.Error(err.Error())
}
var elems []DownloadElements
var e DownloadElements
for runquery.Next() {
if len(columns) > 2 {
err = runquery.Scan(&e.ID, &e.Version, &e.Counter)
} else {
err = runquery.Scan(&e.ID, &e.Counter)
}
elems = append(elems, e)
}
return elems
}
// stats creates an HTTP handler that serves a statistics page with data from the database.
// sqldb: The database connection object.
// lang: The language code to localize the content.
// Returns: An http.Handler that serves the statistics page.
func stats(sqldb *sql.DB, lang string) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
Counter := struct{
ElementsData elements
VersionStats []DownloadElements
TotalStats []DownloadElements
BodyClass string
Lang string
}{
ElementsData: elements{
Title: getMsgFromDB(sqldb, "title", lang),
Header: getMsgFromDB(sqldb, "header", lang),
Help: getMsgFromDB(sqldb, "help", lang),
Packagename: getMsgFromDB(sqldb, "packagename", lang),
Packageversion: getMsgFromDB(sqldb, "packageversion", lang),
Allow: getMsgFromDB(sqldb, "allow", lang),
Warning: getMsgFromDB(sqldb, "warning", lang),
Explain: getMsgFromDB(sqldb, "explain", lang),
},
BodyClass: "m",
VersionStats: searchDownloads(sqldb, "counterd", "program", "version","download_count"),
TotalStats: searchDownloads(sqldb, "count_all", "program","download_count"),
Lang: lang,
}
templates, err := template.ParseFiles("html/master.html", "html/statsitics.html")
if err != nil {
slog.Error(err.Error())
}
err = templates.ExecuteTemplate(res, "master", Counter)
if err != nil {
slog.Error(err.Error())
}
res.WriteHeader(http.StatusOK)
})
}
// getPkgIdFromDB retrieves a list of package IDs from the database.
// sqldb: The database connection object.
// Returns: A slice of strings containing package IDs.
func getPkgIdFromDB(sqldb *sql.DB) []string {
query, err := sqldb.Query("SELECT id FROM packs_all")
defer query.Close()
err = query.Err()
if err != nil {
slog.Error(err.Error())
}
var pkgs []string
var tmpstr string
for query.Next() {
err = query.Scan(&tmpstr)
if err != nil {
slog.Error(err.Error())
}
pkgs = append(pkgs, tmpstr)
}
// query.Scan(&pkgs)
if err != nil {
slog.Error(err.Error())
}
return pkgs
}
// fullSearchPackagesDB performs a full search of packages in the database.
// sqldb: The database connection object.
// Returns: A slice of packages structs containing the full package details.
func fullSearchPackagesDB(sqldb *sql.DB) []packages {
pkg_list := getPkgIdFromDB(sqldb)
var pkgs []packages
for _, p := range pkg_list {
query := generateSQL("packs_all", []string{"*"}, [2]string{"id", p})
res := queryDatabase(sqldb, query)
var pkg packages
pkg, _ = ConvertStringsToPackage(res)
pkgs = append(pkgs, pkg)
}
return pkgs
}
func uploadFileForServer(sqldb *sql.DB, lang string) http.Handler{
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
pageDataFromDatabase := elements{
Title: getMsgFromDB(sqldb, "title", lang),
Header: getMsgFromDB(sqldb, "header", lang),
Help: getMsgFromDB(sqldb, "help", lang),
Packagename: getMsgFromDB(sqldb, "packagename", lang),
Packageversion: getMsgFromDB(sqldb, "packageversion", lang),
Allow: getMsgFromDB(sqldb, "allow", lang),
Warning: getMsgFromDB(sqldb, "warning", lang),
Explain: getMsgFromDB(sqldb, "explain", lang),
}
data := struct {
BodyClass string
ElementsData elements
Lang string
}{
ElementsData: pageDataFromDatabase,
BodyClass: "",
Lang: lang,
}
templates, err := template.ParseFiles("html/master.html", "html/uploadfile.html")
if err != nil {
slog.Error(err.Error())
}
err = templates.ExecuteTemplate(res, "master", data)
if err != nil {
slog.Error(err.Error())
}
// upload(res, req)
res.WriteHeader(http.StatusOK)
})
}
func upload() http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request){
file, header, err := req.FormFile("myFile")
if err != nil {
http.Error(res, "Failed to read file", http.StatusBadRequest)
return
}
defer file.Close()
req.ParseMultipartForm(10000000<<20)
filename := header.Filename
tempFile, err := os.CreateTemp("Data", header.Filename)
if err != nil {
http.Error(res, "Failed to create temp file", http.StatusInternalServerError)
return
}
defer tempFile.Close()
fileBytes, err := io.ReadAll(file)
if err != nil {
http.Error(res, "Failed to read file bytes", http.StatusInternalServerError)
return
}
_, err = tempFile.Write(fileBytes)
if err != nil {
http.Error(res, "Failed to write to temp file", http.StatusInternalServerError)
return
}
fn, err := os.OpenFile("Data/"+filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
http.Error(res, "Failed to open file", http.StatusInternalServerError)
return
}
defer fn.Close()
_, err = fn.Write(fileBytes)
if err != nil {
http.Error(res, "Failed to write to file", http.StatusInternalServerError)
return
}
slog.Info(fmt.Sprint(getDataFromFile("Data/" + filename)))
})
}
func createPackageAndRename() http.Handler{
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request){
slog.Debug(req.Host)
})
}
// index creates an HTTP handler that serves the main page with data from the database.
// sqldb: The database connection object.
// lang: The language code to localize the content.
// Returns: An http.Handler that serves the main page.
func index(sqldb *sql.DB, lang string) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
pageDataFromDatabase := elements{
Title: getMsgFromDB(sqldb, "title", lang),
Header: getMsgFromDB(sqldb, "header", lang),
Help: getMsgFromDB(sqldb, "help", lang),
Packagename: getMsgFromDB(sqldb, "packagename", lang),
Packageversion: getMsgFromDB(sqldb, "packageversion", lang),
Allow: getMsgFromDB(sqldb, "allow", lang),
Warning: getMsgFromDB(sqldb, "warning", lang),
Explain: getMsgFromDB(sqldb, "explain", lang),
}
data := struct {
BodyClass string
ElementsData elements
PackagesData []packages
Lang string
}{
ElementsData: pageDataFromDatabase,
BodyClass: "",
PackagesData: queryPacks(sqldb, &pageDataFromDatabase),
Lang: lang,
}
templates, err := template.ParseFiles("html/master.html", "html/main.html")
if err != nil {
slog.Error(err.Error())
}
err = templates.ExecuteTemplate(res, "master", data)
if err != nil {
slog.Error(err.Error())
}
res.WriteHeader(http.StatusOK)
})
}
func renameForFile() http.Handler{
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
init:=req.FormValue("Vendor")
init=string(init[0])
regexext:= regexp.MustCompile(`\.[\u\l]{1,5}$`)
extention:=regexext.FindString(req.FormValue("InstallerUrl"))
yamlmodel:=yaml{
PUB: req.FormValue("Vendor"),
HASH: req.FormValue("InstallerSha256"),
URL: req.FormValue("InstallerUrl"),
NAME: req.FormValue("Package"),
PID: req.FormValue("Vendor")+"."+req.FormValue("Package"),
VER: req.FormValue("PackageVersion"),
INIT: init,
LOCATION: os.Getenv("packagesDirectory")+"/"+init+"/"+req.FormValue("Vendor")+"/"+req.FormValue("Package")+"/"+req.FormValue("PackageVersion"),
FILELOCATION: os.Getenv("installationDirectory")+"/"+req.FormValue("Vendor")+"."+req.FormValue("Package")+".v."+req.FormValue("PackageVersion")+extention ,
}
CreateDirectoryInPackageDir(yamlmodel)
CreateYaml(yamlmodel)
// fur:=
// slog.Debug(fur)
})
}
// public serves static files from the "public" directory.
// Returns: An http.Handler that serves static files.
func public() http.Handler {
return http.StripPrefix("/public/", http.FileServer(http.Dir("./public")))
}
// main initializes the application, sets up logging, database connection, HTTP routing, and starts the server.
func main() {
// Check if the directory exists
if _, err := os.Stat("Logs/"); os.IsNotExist(err) {
// Create your directory if it does not exist
err = os.Mkdir("Logs/", 0755)
if err != nil {
fmt.Println(err)
}
}
handlerOpts := &slog.HandlerOptions{
Level: slog.LevelDebug,
}
logfilename := "Logs/" + time.Now().Format("2006-01-02--15-04-05") + ".log"
file, err := os.OpenFile(logfilename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0777)
defer file.Close()
log:=slog.New(slog.NewTextHandler(file,handlerOpts ))
slog.SetDefault(log)
if err != nil {
panic(err)
}
// Write a log entry
slog.Info("This is a test log entry")
db, _ := sql.Open("sqlite", "file:progs.db")
mux := http.NewServeMux()
mux.Handle("/public/", public())
mux.Handle("/", index(db, "heb"))
mux.Handle("/en", index(db, "eng"))
mux.Handle("/stats", stats(db,"eng"))
mux.Handle("/up", uploadFileForServer(db,"eng"))
mux.Handle("/upload",upload())
mux.Handle("/pkg",getJson(db))
mux.Handle("/ren",renameForFile())
addr := fmt.Sprintf(":%s", "12312")
server := http.Server{
Addr: addr,
Handler: mux,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 15 * time.Second,
}
if err := server.ListenAndServe(); err != nil {
log.Error("main: couldn't start simple server: %v\n", err)
}
lang := "heb"
log.Info(getMsgFromDB(db, "title", lang))
}