add leftside/rightside template and database singleton
This commit is contained in:
parent
0534da09a1
commit
69e0ab5645
@ -11,13 +11,19 @@ import (
|
|||||||
|
|
||||||
const DefaultPath = "dk.db"
|
const DefaultPath = "dk.db"
|
||||||
|
|
||||||
// DB wraps a SQLite connection pool with simplified methods
|
// database wraps a SQLite connection pool with simplified methods
|
||||||
type DB struct {
|
type database struct {
|
||||||
pool *sqlitex.Pool
|
pool *sqlitex.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DB is a backward-compatible type alias
|
||||||
|
type DB = database
|
||||||
|
|
||||||
|
// instance is the global singleton instance
|
||||||
|
var instance *database
|
||||||
|
|
||||||
// Open creates a new database connection pool
|
// Open creates a new database connection pool
|
||||||
func Open(path string) (*DB, error) {
|
func Open(path string) (*database, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = DefaultPath
|
path = DefaultPath
|
||||||
}
|
}
|
||||||
@ -49,26 +55,26 @@ func Open(path string) (*DB, error) {
|
|||||||
return nil, fmt.Errorf("failed to set synchronous mode: %w", err)
|
return nil, fmt.Errorf("failed to set synchronous mode: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DB{pool: pool}, nil
|
return &database{pool: pool}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the database connection pool
|
// Close closes the database connection pool
|
||||||
func (db *DB) Close() error {
|
func (db *database) Close() error {
|
||||||
return db.pool.Close()
|
return db.pool.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConn gets a connection from the pool - caller must call Put when done
|
// GetConn gets a connection from the pool - caller must call Put when done
|
||||||
func (db *DB) GetConn(ctx context.Context) (*sqlite.Conn, error) {
|
func (db *database) GetConn(ctx context.Context) (*sqlite.Conn, error) {
|
||||||
return db.pool.Take(ctx)
|
return db.pool.Take(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutConn returns a connection to the pool
|
// PutConn returns a connection to the pool
|
||||||
func (db *DB) PutConn(conn *sqlite.Conn) {
|
func (db *database) PutConn(conn *sqlite.Conn) {
|
||||||
db.pool.Put(conn)
|
db.pool.Put(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exec executes a SQL statement without returning results
|
// Exec executes a SQL statement without returning results
|
||||||
func (db *DB) Exec(query string, args ...any) error {
|
func (db *database) Exec(query string, args ...any) error {
|
||||||
conn, err := db.pool.Take(context.Background())
|
conn, err := db.pool.Take(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get connection from pool: %w", err)
|
return fmt.Errorf("failed to get connection from pool: %w", err)
|
||||||
@ -85,7 +91,7 @@ func (db *DB) Exec(query string, args ...any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Query executes a SQL query and calls fn for each row
|
// Query executes a SQL query and calls fn for each row
|
||||||
func (db *DB) Query(query string, fn func(*sqlite.Stmt) error, args ...any) error {
|
func (db *database) Query(query string, fn func(*sqlite.Stmt) error, args ...any) error {
|
||||||
conn, err := db.pool.Take(context.Background())
|
conn, err := db.pool.Take(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get connection from pool: %w", err)
|
return fmt.Errorf("failed to get connection from pool: %w", err)
|
||||||
@ -105,7 +111,7 @@ func (db *DB) Query(query string, fn func(*sqlite.Stmt) error, args ...any) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Begin starts a new transaction
|
// Begin starts a new transaction
|
||||||
func (db *DB) Begin() (*Tx, error) {
|
func (db *database) Begin() (*Tx, error) {
|
||||||
conn, err := db.pool.Take(context.Background())
|
conn, err := db.pool.Take(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get connection from pool: %w", err)
|
return nil, fmt.Errorf("failed to get connection from pool: %w", err)
|
||||||
@ -120,7 +126,7 @@ func (db *DB) Begin() (*Tx, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transaction runs a function within a transaction
|
// Transaction runs a function within a transaction
|
||||||
func (db *DB) Transaction(fn func(*Tx) error) error {
|
func (db *database) Transaction(fn func(*Tx) error) error {
|
||||||
tx, err := db.Begin()
|
tx, err := db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -176,3 +182,75 @@ func (tx *Tx) Rollback() error {
|
|||||||
defer tx.pool.Put(tx.conn)
|
defer tx.pool.Put(tx.conn)
|
||||||
return sqlitex.ExecuteTransient(tx.conn, "ROLLBACK", nil)
|
return sqlitex.ExecuteTransient(tx.conn, "ROLLBACK", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitializeDB initializes the global DB singleton
|
||||||
|
func InitializeDB(path string) error {
|
||||||
|
db, err := Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
instance = db
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDB returns the global database instance
|
||||||
|
func GetDB() *DB {
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global convenience functions that use the singleton
|
||||||
|
|
||||||
|
// Exec executes a SQL statement without returning results using the global DB
|
||||||
|
func Exec(query string, args ...any) error {
|
||||||
|
if instance == nil {
|
||||||
|
return fmt.Errorf("database not initialized")
|
||||||
|
}
|
||||||
|
return instance.Exec(query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query executes a SQL query and calls fn for each row using the global DB
|
||||||
|
func Query(query string, fn func(*sqlite.Stmt) error, args ...any) error {
|
||||||
|
if instance == nil {
|
||||||
|
return fmt.Errorf("database not initialized")
|
||||||
|
}
|
||||||
|
return instance.Query(query, fn, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin starts a new transaction using the global DB
|
||||||
|
func Begin() (*Tx, error) {
|
||||||
|
if instance == nil {
|
||||||
|
return nil, fmt.Errorf("database not initialized")
|
||||||
|
}
|
||||||
|
return instance.Begin()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction runs a function within a transaction using the global DB
|
||||||
|
func Transaction(fn func(*Tx) error) error {
|
||||||
|
if instance == nil {
|
||||||
|
return fmt.Errorf("database not initialized")
|
||||||
|
}
|
||||||
|
return instance.Transaction(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConn gets a connection from the pool using the global DB
|
||||||
|
func GetConn(ctx context.Context) (*sqlite.Conn, error) {
|
||||||
|
if instance == nil {
|
||||||
|
return nil, fmt.Errorf("database not initialized")
|
||||||
|
}
|
||||||
|
return instance.GetConn(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutConn returns a connection to the pool using the global DB
|
||||||
|
func PutConn(conn *sqlite.Conn) {
|
||||||
|
if instance != nil {
|
||||||
|
instance.PutConn(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the global database connection pool
|
||||||
|
func Close() error {
|
||||||
|
if instance == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return instance.Close()
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"dk/internal/auth"
|
"dk/internal/auth"
|
||||||
"dk/internal/csrf"
|
"dk/internal/csrf"
|
||||||
|
"dk/internal/database"
|
||||||
"dk/internal/middleware"
|
"dk/internal/middleware"
|
||||||
"dk/internal/password"
|
"dk/internal/password"
|
||||||
"dk/internal/router"
|
"dk/internal/router"
|
||||||
@ -155,13 +156,13 @@ func processRegister() router.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if username already exists
|
// Check if username already exists
|
||||||
if _, err := users.GetByUsername(auth.Manager.DB(), username); err == nil {
|
if _, err := users.GetByUsername(database.GetDB(), username); err == nil {
|
||||||
showRegisterError(ctx, "Username already exists", username, email)
|
showRegisterError(ctx, "Username already exists", username, email)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if email already exists
|
// Check if email already exists
|
||||||
if _, err := users.GetByEmail(auth.Manager.DB(), email); err == nil {
|
if _, err := users.GetByEmail(database.GetDB(), email); err == nil {
|
||||||
showRegisterError(ctx, "Email already registered", username, email)
|
showRegisterError(ctx, "Email already registered", username, email)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -315,17 +316,15 @@ func validateRegistration(username, email, password, confirmPassword string) err
|
|||||||
// createUser inserts a new user into the database
|
// createUser inserts a new user into the database
|
||||||
// This is a simplified version - in a real app you'd have a proper users.Create function
|
// This is a simplified version - in a real app you'd have a proper users.Create function
|
||||||
func createUser(user *users.User) error {
|
func createUser(user *users.User) error {
|
||||||
db := auth.Manager.DB()
|
|
||||||
|
|
||||||
query := `INSERT INTO users (username, password, email, verified, auth) VALUES (?, ?, ?, ?, ?)`
|
query := `INSERT INTO users (username, password, email, verified, auth) VALUES (?, ?, ?, ?, ?)`
|
||||||
|
|
||||||
err := db.Exec(query, user.Username, user.Password, user.Email, user.Verified, user.Auth)
|
err := database.Exec(query, user.Username, user.Password, user.Email, user.Verified, user.Auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to insert user: %w", err)
|
return fmt.Errorf("failed to insert user: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the user ID (simplified - in real app you'd return it from insert)
|
// Get the user ID (simplified - in real app you'd return it from insert)
|
||||||
createdUser, err := users.GetByUsername(db, user.Username)
|
createdUser, err := users.GetByUsername(database.GetDB(), user.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get created user: %w", err)
|
return fmt.Errorf("failed to get created user: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -27,14 +27,14 @@ func Start(port string) error {
|
|||||||
// Initialize template singleton
|
// Initialize template singleton
|
||||||
template.InitializeCache(cwd)
|
template.InitializeCache(cwd)
|
||||||
|
|
||||||
db, err := database.Open("dk.db")
|
// Initialize database singleton
|
||||||
if err != nil {
|
if err := database.InitializeDB("dk.db"); err != nil {
|
||||||
return fmt.Errorf("failed to open database: %w", err)
|
return fmt.Errorf("failed to initialize database: %w", err)
|
||||||
}
|
}
|
||||||
defer db.Close()
|
defer database.Close()
|
||||||
|
|
||||||
// Initialize auth singleton
|
// Initialize auth singleton
|
||||||
auth.InitializeManager(db, "sessions.json")
|
auth.InitializeManager(database.GetDB(), "sessions.json")
|
||||||
|
|
||||||
// Initialize router
|
// Initialize router
|
||||||
r := router.New()
|
r := router.New()
|
||||||
|
@ -6,9 +6,11 @@ import (
|
|||||||
|
|
||||||
"dk/internal/auth"
|
"dk/internal/auth"
|
||||||
"dk/internal/csrf"
|
"dk/internal/csrf"
|
||||||
|
"dk/internal/database"
|
||||||
"dk/internal/middleware"
|
"dk/internal/middleware"
|
||||||
"dk/internal/router"
|
"dk/internal/router"
|
||||||
"dk/internal/template"
|
"dk/internal/template"
|
||||||
|
"dk/internal/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateTopNav generates the top navigation HTML based on authentication status
|
// GenerateTopNav generates the top navigation HTML based on authentication status
|
||||||
@ -27,6 +29,80 @@ func GenerateTopNav(ctx router.Ctx) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateLeftSide generates the left sidebar content for authenticated users
|
||||||
|
func GenerateLeftSide(ctx router.Ctx) string {
|
||||||
|
if !middleware.IsAuthenticated(ctx) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and render the leftside template with user data
|
||||||
|
leftSideTmpl, err := template.Cache.Load("leftside.html")
|
||||||
|
if err != nil {
|
||||||
|
return "" // Silently fail - sidebar is optional
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current user from session
|
||||||
|
currentUser := middleware.GetCurrentUser(ctx)
|
||||||
|
if currentUser == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the full user object from database
|
||||||
|
db := database.GetDB()
|
||||||
|
if db == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := users.Find(db, currentUser.ID)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the user object directly to the template
|
||||||
|
leftSideData := map[string]any{
|
||||||
|
"user": user,
|
||||||
|
}
|
||||||
|
|
||||||
|
return leftSideTmpl.RenderNamed(leftSideData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateRightSide generates the right sidebar content for authenticated users
|
||||||
|
func GenerateRightSide(ctx router.Ctx) string {
|
||||||
|
if !middleware.IsAuthenticated(ctx) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and render the rightside template with user data
|
||||||
|
rightSideTmpl, err := template.Cache.Load("rightside.html")
|
||||||
|
if err != nil {
|
||||||
|
return "" // Silently fail - sidebar is optional
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current user from session
|
||||||
|
currentUser := middleware.GetCurrentUser(ctx)
|
||||||
|
if currentUser == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the full user object from database
|
||||||
|
db := database.GetDB()
|
||||||
|
if db == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := users.Find(db, currentUser.ID)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the user object directly to the template
|
||||||
|
rightSideData := map[string]any{
|
||||||
|
"user": user,
|
||||||
|
}
|
||||||
|
|
||||||
|
return rightSideTmpl.RenderNamed(rightSideData)
|
||||||
|
}
|
||||||
|
|
||||||
// PageData holds common page template data
|
// PageData holds common page template data
|
||||||
type PageData struct {
|
type PageData struct {
|
||||||
Title string
|
Title string
|
||||||
@ -69,10 +145,10 @@ func RenderPage(ctx router.Ctx, pageData PageData, additionalData map[string]any
|
|||||||
|
|
||||||
// Set defaults for empty fields
|
// Set defaults for empty fields
|
||||||
if data["leftside"] == "" {
|
if data["leftside"] == "" {
|
||||||
data["leftside"] = ""
|
data["leftside"] = GenerateLeftSide(ctx)
|
||||||
}
|
}
|
||||||
if data["rightside"] == "" {
|
if data["rightside"] == "" {
|
||||||
data["rightside"] = ""
|
data["rightside"] = GenerateRightSide(ctx)
|
||||||
}
|
}
|
||||||
if data["numqueries"] == "" {
|
if data["numqueries"] == "" {
|
||||||
data["numqueries"] = "0"
|
data["numqueries"] = "0"
|
||||||
|
46
templates/leftside.html
Normal file
46
templates/leftside.html
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<td class="title"><img src="/assets/images/button_location.gif" alt="Location" title="Location"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
Currently: {user.currentaction}<br>
|
||||||
|
Longitude: {user.longitude}<br>
|
||||||
|
Latitude: {user.latitude}<br>
|
||||||
|
<a href="javascript:openmappopup()">View Map</a>
|
||||||
|
<br><br>
|
||||||
|
<form action="/move" method="post">
|
||||||
|
<center>
|
||||||
|
<input name="north" type="submit" value="North"><br>
|
||||||
|
<input name="west" type="submit" value="West"><input name="east" type="submit" value="East"><br>
|
||||||
|
<input name="south" type="submit" value="South">
|
||||||
|
</center>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table width="100%">
|
||||||
|
<tr><td class="title"><img src="/assets/images/button_towns.gif" alt="Towns" title="Towns"></td></tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{townname}
|
||||||
|
{townlist}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table width="100%">
|
||||||
|
<tr><td class="title"><img src="/assets/images/button_functions.gif" alt="Functions" title="Functions"></td></tr>
|
||||||
|
<tr><td>
|
||||||
|
<a href="/">Home</a><br>
|
||||||
|
<a href="/forum">Forum</a>
|
||||||
|
<a href="/change-password">Change Password</a><br>
|
||||||
|
<a href="#">Log Out</a><br>
|
||||||
|
<a href="/help">Help</a>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
58
templates/rightside.html
Normal file
58
templates/rightside.html
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<td class="title"><img src="/assets/images/button_character.gif" alt="Character" title="Character"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<b>{user.username}</b><br>
|
||||||
|
Level: {user.level}<br>
|
||||||
|
Exp: {user.experience}<br>
|
||||||
|
Gold: {user.gold}<br>
|
||||||
|
HP: {user.currenthp}<br>
|
||||||
|
MP: {user.currentmp}<br>
|
||||||
|
TP: {user.currenttp}<br>
|
||||||
|
{statbars}<br>
|
||||||
|
<a href="javascript:opencharpopup()">Extended Stats</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<td class="title"><img src="/assets/images/button_inventory.gif" alt="Inventory" title="Inventory"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<td><img src="/assets/images/icon_weapon.gif" alt="Weapon" title="Weapon"></td>
|
||||||
|
<td width="100%">{weaponname}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="/assets/images/icon_armor.gif" alt="Armor" title="Armor"></td>
|
||||||
|
<td width="100%">{armorname}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><img src="/assets/images/icon_shield.gif" alt="Shield" title="Shield"></td>
|
||||||
|
<td width="100%">{shieldname}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
{slot1name}<br>
|
||||||
|
{slot2name}<br>
|
||||||
|
{slot3name}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
<td class="title"><img src="/assets/images/button_fastspells.gif" alt="Fast Spells" title="Fast Spells"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{magiclist}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
Loading…
x
Reference in New Issue
Block a user