Musselman
808f3f3772
This commit removes unnecessary endpoints from the HTTP server and deletes the tables.go file, consolidating the relevant code under main.go. The kanban and update-task-status handlers are no longer needed, as well as the createTableDB function. No major consequences or considerations arise from these changes.
356 lines
9.6 KiB
Go
356 lines
9.6 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
_ "github.com/lib/pq"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// Varibles, constants and basic structures
|
|
|
|
const (
|
|
hashCost = 16
|
|
sessionLength = 12000 * time.Second
|
|
)
|
|
|
|
var (
|
|
db *sql.DB
|
|
sessions = make(map[string]session)
|
|
fileUploadPath = "./uploads"
|
|
templateFilePath = "./editor_templates"
|
|
templates = template.Must(template.ParseGlob("templates/*.html"))
|
|
)
|
|
|
|
type session struct {
|
|
username string
|
|
expiry time.Time
|
|
sessionUUID string
|
|
}
|
|
|
|
type Credentials struct {
|
|
Password string `json:"password"`
|
|
Username string `json:"username"`
|
|
}
|
|
|
|
type File struct {
|
|
ID int
|
|
UserID uuid.UUID
|
|
Filename string
|
|
CreationTime int64
|
|
LastEdited int64
|
|
LastOpened int64
|
|
}
|
|
|
|
func main() {
|
|
// Retrieve Environment variables
|
|
dbName := "notatio"
|
|
dbHost := getEnvVariable("DB_HOST")
|
|
dbPort := getEnvVariable("PGPORT")
|
|
dbUser := getEnvVariable("DB_USER")
|
|
dbPassword := getEnvVariable("POSTGRES_PASSWORD")
|
|
dbSSLMode := getEnvVariable("DB_SSL_MODE")
|
|
|
|
db = connectToDatabase(dbHost, dbPort, dbUser, dbPassword, dbUser, dbSSLMode)
|
|
defer db.Close()
|
|
|
|
missingParam := ""
|
|
switch {
|
|
case dbHost == "":
|
|
missingParam = "DB_HOST"
|
|
case dbPort == "":
|
|
missingParam = "PGPORT"
|
|
case dbUser == "":
|
|
missingParam = "DB_USER"
|
|
case dbPassword == "":
|
|
missingParam = "POSTGRES_PASSWORD"
|
|
}
|
|
|
|
if missingParam != "" {
|
|
log.Printf("Error: Required PostgreSQL connection environment variable '%s' is not provided.\n", missingParam)
|
|
log.Println("Exiting...")
|
|
os.Exit(10)
|
|
}
|
|
|
|
exists := checkDatabaseExists(db, dbName)
|
|
|
|
if !exists {
|
|
createDatabase(db, dbName)
|
|
}
|
|
|
|
db = connectToNotatioDatabase(dbHost, dbPort, dbUser, dbPassword, dbName, dbSSLMode)
|
|
defer db.Close()
|
|
|
|
adminUsername := getEnvVariable("ADMIN_USER")
|
|
adminPassword := getEnvVariable("ADMIN_PASS")
|
|
adminName := getEnvVariable("ADMIN_NAME")
|
|
adminEmail := getEnvVariable("ADMIN_EMAIL")
|
|
|
|
createUserTable()
|
|
createFilesTable()
|
|
|
|
createUser(adminUsername, adminPassword, adminName, adminEmail)
|
|
log.Println("Done with database checks, starting webserver!")
|
|
|
|
// Start webserver
|
|
initHTTPServer()
|
|
|
|
}
|
|
|
|
// Web server initializer
|
|
func initHTTPServer() {
|
|
http.HandleFunc("/", AboutPage)
|
|
http.HandleFunc("/signup", Signup)
|
|
http.HandleFunc("/login", Login)
|
|
http.HandleFunc("/home", ListFiles)
|
|
http.HandleFunc("/refresh", Refresh)
|
|
http.HandleFunc("/logout", Logout)
|
|
http.HandleFunc("/upload", UploadFile)
|
|
http.HandleFunc("/edit", EditFile)
|
|
http.HandleFunc("/save", SaveFile)
|
|
http.HandleFunc("/create", createNewFile)
|
|
http.HandleFunc("/welcome", newUser)
|
|
http.HandleFunc("/delete", DeleteFiles)
|
|
http.HandleFunc("/export", exportFiles)
|
|
http.HandleFunc("/checkusername", handleUsernameCheck)
|
|
|
|
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
|
|
|
// Start the server on port 9991
|
|
fmt.Println("Starting HTTP server on port 9991...")
|
|
log.Fatal(http.ListenAndServe(":9991", nil))
|
|
}
|
|
|
|
func renderTemplate(w http.ResponseWriter, templateName string, data interface{}) {
|
|
err := templates.ExecuteTemplate(w, templateName, data)
|
|
if err != nil {
|
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
log.Printf("Error rendering template %s: %v", templateName, err)
|
|
}
|
|
}
|
|
|
|
func handleUsernameCheck(w http.ResponseWriter, r *http.Request) {
|
|
// Get the username from the query parameter
|
|
username := r.URL.Query().Get("username")
|
|
// Check if the username is taken (example function)
|
|
isTaken, err := isUsernameTaken(username)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Create a response map to store the availability status
|
|
response := map[string]bool{
|
|
"available": isTaken,
|
|
}
|
|
|
|
// Convert the response to JSON format
|
|
jsonResponse, err := json.Marshal(response)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Set the content type as JSON
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
// Write the JSON response to the response writer
|
|
w.Write(jsonResponse)
|
|
}
|
|
|
|
func handleInternalServerError(w http.ResponseWriter, err error) {
|
|
log.Printf("Error: %v", err)
|
|
}
|
|
|
|
func AboutPage(w http.ResponseWriter, r *http.Request) {
|
|
renderTemplate(w, "index.html", nil)
|
|
}
|
|
|
|
func newUser(w http.ResponseWriter, r *http.Request) {
|
|
renderTemplate(w, "newuser.html", nil)
|
|
}
|
|
|
|
func Refresh(w http.ResponseWriter, r *http.Request) {
|
|
userSession, err := validateSession(w, r)
|
|
if err != nil {
|
|
// Handle the error as needed
|
|
return
|
|
}
|
|
|
|
// Create a new session token for the current user
|
|
newSessionToken := uuid.NewString()
|
|
expiresAt := time.Now().Add(sessionLength)
|
|
|
|
// Store the token in the session map, along with the user whom it represents
|
|
sessions[newSessionToken] = session{
|
|
username: userSession.username,
|
|
expiry: expiresAt,
|
|
}
|
|
|
|
// Delete the older session token
|
|
delete(sessions, userSession.sessionUUID)
|
|
|
|
// Set the new token as the user's `session_token` cookie
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: "session_token",
|
|
Value: newSessionToken,
|
|
Expires: expiresAt,
|
|
})
|
|
|
|
log.Printf("Session refreshed for user: %s", userSession.username)
|
|
|
|
// Redirect to the home page
|
|
http.Redirect(w, r, "/home", http.StatusSeeOther)
|
|
}
|
|
|
|
func (s session) isSessionExpired() bool {
|
|
return s.expiry.Before(time.Now())
|
|
}
|
|
|
|
func validateSession(w http.ResponseWriter, r *http.Request) (session, error) {
|
|
sessionCookie, err := r.Cookie("session_token")
|
|
if err != nil {
|
|
log.Printf("Error getting session cookie: %v", err)
|
|
http.Redirect(w, r, "/?login", http.StatusSeeOther) // Redirect to the login page
|
|
return session{}, err
|
|
}
|
|
|
|
sessionToken := sessionCookie.Value
|
|
userSession, exists := sessions[sessionToken]
|
|
if !exists {
|
|
log.Printf("Session not found for token: %s", sessionToken)
|
|
http.Redirect(w, r, "/?login", http.StatusSeeOther) // Redirect to the login page
|
|
return session{}, fmt.Errorf("session not found")
|
|
}
|
|
|
|
if userSession.isSessionExpired() {
|
|
delete(sessions, sessionToken)
|
|
log.Printf("Session expired for user: %s", userSession.username)
|
|
http.Redirect(w, r, "/?login", http.StatusSeeOther) // Redirect to the login page
|
|
return session{}, fmt.Errorf("session expired")
|
|
}
|
|
|
|
return userSession, nil
|
|
}
|
|
|
|
func createUserTable() {
|
|
// Create User Table
|
|
createUserTable := `
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id SERIAL PRIMARY KEY,
|
|
username VARCHAR(255) UNIQUE NOT NULL,
|
|
password VARCHAR(255) NOT NULL,
|
|
accounttype VARCHAR(255) NOT NULL,
|
|
uuid VARCHAR(255) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
email VARCHAR(255) NOT NULL
|
|
);`
|
|
|
|
_, err := db.Exec(createUserTable)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
}
|
|
|
|
func createFilesTable() {
|
|
// Create File Table
|
|
createFileTable := `
|
|
CREATE TABLE IF NOT EXISTS files (
|
|
id SERIAL PRIMARY KEY,
|
|
user_id UUID NOT NULL,
|
|
filename VARCHAR(255) NOT NULL,
|
|
creation_time TIMESTAMP NOT NULL,
|
|
last_edited TIMESTAMP NOT NULL,
|
|
last_opened TIMESTAMP NOT NULL
|
|
);`
|
|
|
|
_, err := db.Exec(createFileTable)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func getEnvVariable(name string) string {
|
|
value := os.Getenv(name)
|
|
if value == "" {
|
|
log.Printf("Error: Required environment variable '%s' is not provided.\n", name)
|
|
log.Println("Exiting...")
|
|
os.Exit(10)
|
|
}
|
|
return value
|
|
}
|
|
|
|
func connectToDatabase(dbHost, dbPort, dbUser, dbPassword, dbName, dbSSLMode string) *sql.DB {
|
|
dbConnectionString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", dbHost, dbPort, dbUser, dbPassword, dbName, dbSSLMode)
|
|
db, err := sql.Open("postgres", dbConnectionString)
|
|
if err != nil {
|
|
// Handle the error appropriately
|
|
panic(err)
|
|
}
|
|
return db
|
|
}
|
|
|
|
func checkDatabaseExists(db *sql.DB, dbName string) bool {
|
|
var exists bool
|
|
row := db.QueryRow("SELECT EXISTS (SELECT 1 FROM pg_database WHERE datname = $1)", dbName)
|
|
if err := row.Scan(&exists); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return exists
|
|
}
|
|
|
|
func createDatabase(db *sql.DB, dbName string) {
|
|
log.Println("Notatio database does not exist. Creating Database...")
|
|
_, err := db.Exec("CREATE DATABASE notatio")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func connectToNotatioDatabase(dbHost, dbPort, dbUser, dbPassword, dbName, dbSSLMode string) *sql.DB {
|
|
dbConnectionString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", dbHost, dbPort, dbUser, dbPassword, dbName, dbSSLMode)
|
|
db, err := sql.Open("postgres", dbConnectionString)
|
|
if err != nil {
|
|
// Handle the error appropriately
|
|
panic(err)
|
|
}
|
|
return db
|
|
}
|
|
|
|
func createUser(adminUsername string, adminPassword string, adminName string, adminEmail string) {
|
|
adminUsername = strings.ToLower(adminUsername)
|
|
if adminUsername != "" && adminPassword != "" && adminName != "" && adminEmail != "" {
|
|
// Check if the admin user already exists in the database
|
|
_, adminExists := getUserFromDatabase(adminUsername)
|
|
if !adminExists {
|
|
// Admin user doesn't exist, create it
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(adminPassword), hashCost)
|
|
if err != nil {
|
|
log.Printf("Error hashing admin password: %v", err)
|
|
// Handle the error appropriately
|
|
} else {
|
|
// Generate a UUID for the admin user
|
|
adminUUID := uuid.New()
|
|
createUserFolder(adminUsername)
|
|
// Insert the admin user into the database with the hashed password and UUID
|
|
if err := insertUserIntoDatabase(adminUsername, string(hashedPassword), "admin", adminUUID.String(), adminName, adminEmail); err != nil {
|
|
log.Printf("Error inserting admin user into database: %v", err)
|
|
// Handle the error appropriately
|
|
} else {
|
|
log.Printf("Admin user created and added to the database: %s", adminUsername)
|
|
}
|
|
}
|
|
} else {
|
|
log.Printf("Admin user already exists in the database: %s", adminUsername)
|
|
}
|
|
}
|
|
}
|