Create admin user, admin can edit news blotter
parent
3c438b0ee2
commit
f7206db295
|
@ -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"
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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
53
main.go
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue