Create admin user, admin can edit news blotter

master
Joca 2025-04-15 21:40:01 -03:00
parent 3c438b0ee2
commit f7206db295
Signed by: jocadbz
GPG Key ID: B1836DCE2F50BDF7
6 changed files with 206 additions and 14 deletions

View File

@ -4,5 +4,7 @@
"db_username": "threadr_user", "db_username": "threadr_user",
"db_password": "threadr_password", "db_password": "threadr_password",
"db_database": "threadr_db", "db_database": "threadr_db",
"db_svr_host": "localhost:3306" "db_svr_host": "localhost:3306",
"admin_username": "admin",
"admin_password": "adminpassword"
} }

View File

@ -25,6 +25,8 @@ type Config struct {
DBPassword string `json:"db_password"` DBPassword string `json:"db_password"`
DBDatabase string `json:"db_database"` DBDatabase string `json:"db_database"`
DBServerHost string `json:"db_svr_host"` DBServerHost string `json:"db_svr_host"`
AdminUsername string `json:"admin_username"`
AdminPassword string `json:"admin_password"`
} }
type App struct { type App struct {

View File

@ -3,6 +3,8 @@ package handlers
import ( import (
"log" "log"
"net/http" "net/http"
"strconv"
"threadr/models"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
) )
@ -11,13 +13,69 @@ func NewsHandler(app *App) http.HandlerFunc {
session := r.Context().Value("session").(*sessions.Session) session := r.Context().Value("session").(*sessions.Session)
loggedIn := session.Values["user_id"] != nil loggedIn := session.Values["user_id"] != nil
cookie, _ := r.Cookie("threadr_cookie_banner") cookie, _ := r.Cookie("threadr_cookie_banner")
newsItems := []string{ userID, _ := session.Values["user_id"].(int)
"2020-02-21 Whole Website updated: Homepage, News, Boards, About, Log In, Userhome, Log Out", isAdmin := false
"2020-01-06 First Steps done",
if loggedIn {
user, err := models.GetUserByID(app.DB, userID)
if err != nil {
log.Printf("Error fetching user: %v", err)
} else if user != nil {
isAdmin = models.HasGlobalPermission(user, models.PermManageUsers)
} }
}
if r.Method == http.MethodPost && loggedIn && isAdmin {
if action := r.URL.Query().Get("action"); action == "delete" {
newsIDStr := r.URL.Query().Get("id")
newsID, err := strconv.Atoi(newsIDStr)
if err != nil {
http.Error(w, "Invalid news ID", http.StatusBadRequest)
return
}
err = models.DeleteNews(app.DB, newsID)
if err != nil {
log.Printf("Error deleting news item: %v", err)
http.Error(w, "Failed to delete news item", http.StatusInternalServerError)
return
}
http.Redirect(w, r, app.Config.ThreadrDir+"/news/", http.StatusFound)
return
} else {
title := r.FormValue("title")
content := r.FormValue("content")
if title != "" && content != "" {
news := models.News{
Title: title,
Content: content,
PostedBy: userID,
}
err := models.CreateNews(app.DB, news)
if err != nil {
log.Printf("Error creating news item: %v", err)
http.Error(w, "Failed to create news item", http.StatusInternalServerError)
return
}
http.Redirect(w, r, app.Config.ThreadrDir+"/news/", http.StatusFound)
return
} else {
http.Error(w, "Title and content are required", http.StatusBadRequest)
return
}
}
}
newsItems, err := models.GetAllNews(app.DB)
if err != nil {
log.Printf("Error fetching news items: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
data := struct { data := struct {
PageData PageData
News []string News []models.News
IsAdmin bool
}{ }{
PageData: PageData{ PageData: PageData{
Title: "ThreadR - News", Title: "ThreadR - News",
@ -29,6 +87,7 @@ func NewsHandler(app *App) http.HandlerFunc {
CurrentURL: r.URL.Path, CurrentURL: r.URL.Path,
}, },
News: newsItems, News: newsItems,
IsAdmin: isAdmin,
} }
if err := app.Tmpl.ExecuteTemplate(w, "news", data); err != nil { if err := app.Tmpl.ExecuteTemplate(w, "news", data); err != nil {
log.Printf("Error executing template in NewsHandler: %v", err) log.Printf("Error executing template in NewsHandler: %v", err)

53
main.go
View File

@ -10,6 +10,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"threadr/handlers" "threadr/handlers"
"threadr/models"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
@ -168,10 +169,56 @@ func createTablesIfNotExist(db *sql.DB) error {
return fmt.Errorf("error creating reposts table: %v", err) return fmt.Errorf("error creating reposts table: %v", err)
} }
// Create news table
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS news (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
posted_by INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`)
if err != nil {
return fmt.Errorf("error creating news table: %v", err)
}
log.Println("Database tables created or already exist") log.Println("Database tables created or already exist")
return nil return nil
} }
func ensureAdminUser(db *sql.DB, config *handlers.Config) error {
adminUser, err := models.GetUserByUsername(db, config.AdminUsername)
if err != nil && err != sql.ErrNoRows {
return fmt.Errorf("error checking for admin user: %v", err)
}
// If admin user doesn't exist, create one
if adminUser == nil {
log.Printf("Creating admin user: %s", config.AdminUsername)
err = models.CreateUser(db, config.AdminUsername, config.AdminPassword)
if err != nil {
return fmt.Errorf("error creating admin user: %v", err)
}
// Get the newly created admin user to update permissions
adminUser, err = models.GetUserByUsername(db, config.AdminUsername)
if err != nil {
return fmt.Errorf("error fetching new admin user: %v", err)
}
// Set admin permissions (all permissions)
_, err = db.Exec("UPDATE users SET permissions = ? WHERE id = ?",
models.PermCreateBoard|models.PermManageUsers, adminUser.ID)
if err != nil {
return fmt.Errorf("error setting admin permissions: %v", err)
}
log.Println("Admin user created successfully with full permissions")
} else {
log.Println("Admin user already exists")
}
return nil
}
func main() { func main() {
config, err := loadConfig("config/config.json") config, err := loadConfig("config/config.json")
if err != nil { if err != nil {
@ -191,6 +238,12 @@ func main() {
log.Fatal("Error creating database tables:", err) log.Fatal("Error creating database tables:", err)
} }
// Ensure admin user exists
err = ensureAdminUser(db, config)
if err != nil {
log.Fatal("Error ensuring admin user:", err)
}
dir, err := os.Getwd() dir, err := os.Getwd()
if err != nil { if err != nil {
log.Fatal("Error getting working directory:", err) log.Fatal("Error getting working directory:", err)

53
models/news.go Normal file
View File

@ -0,0 +1,53 @@
package models
import (
"database/sql"
"time"
)
type News struct {
ID int
Title string
Content string
PostedBy int
CreatedAt time.Time
}
func GetAllNews(db *sql.DB) ([]News, error) {
query := "SELECT id, title, content, posted_by, created_at FROM news ORDER BY created_at DESC"
rows, err := db.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
var newsItems []News
for rows.Next() {
news := News{}
var createdAtStr string
err := rows.Scan(&news.ID, &news.Title, &news.Content, &news.PostedBy, &createdAtStr)
if err != nil {
return nil, err
}
// Parse the timestamp string into time.Time
news.CreatedAt, err = time.Parse("2006-01-02 15:04:05", createdAtStr)
if err != nil {
// Fallback to a default time if parsing fails
news.CreatedAt = time.Time{}
}
newsItems = append(newsItems, news)
}
return newsItems, nil
}
func CreateNews(db *sql.DB, news News) error {
query := "INSERT INTO news (title, content, posted_by, created_at) VALUES (?, ?, ?, NOW())"
_, err := db.Exec(query, news.Title, news.Content, news.PostedBy)
return err
}
func DeleteNews(db *sql.DB, id int) error {
query := "DELETE FROM news WHERE id = ?"
_, err := db.Exec(query, id)
return err
}

View File

@ -12,12 +12,35 @@
<h2>News</h2> <h2>News</h2>
</header> </header>
<section> <section>
{{if .News}}
<ul> <ul>
{{range .News}} {{range .News}}
<li>{{.}}</li> <li><strong>{{.Title}}</strong> - Posted on {{.CreatedAt.Format "02/01/2006 - 15:04"}}
<p>{{.Content}}</p>
{{if $.IsAdmin}}
<form method="post" action="{{$.BasePath}}/news/?action=delete&id={{.ID}}" style="display:inline;">
<button type="submit" onclick="return confirm('Are you sure you want to delete this news item?')">Delete</button>
</form>
{{end}}
</li>
{{end}} {{end}}
</ul> </ul>
{{else}}
<p>No news items available at the moment.</p>
{{end}}
</section> </section>
{{if .IsAdmin}}
<section>
<h3>Post New Announcement</h3>
<form method="post" action="{{.BasePath}}/news/">
<label for="title">Title:</label>
<input type="text" id="title" name="title" required><br>
<label for="content">Content:</label>
<textarea id="content" name="content" required></textarea><br>
<input type="submit" value="Post News">
</form>
</section>
{{end}}
</main> </main>
{{template "cookie_banner" .}} {{template "cookie_banner" .}}
</body> </body>