notatio/main.go
Musselman 808f3f3772 Remove kanban links from main.go, delete tables.go
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.
2023-11-29 17:07:36 -06:00

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)
}
}
}