diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md new file mode 100644 index 0000000..eca44b3 --- /dev/null +++ b/DOCUMENTATION.md @@ -0,0 +1,403 @@ +# ThreadR Rewritten - Technical Specification + +## Project Overview + +ThreadR Rewritten is a free and open-source forum engine, re-implemented in Go. It aims to provide a robust and extensible platform for users to host their own forum instances. The project, initially a PHP/MySQL school project, has been completely rewritten to leverage Go's performance and concurrency features. It supports traditional forum boards, real-time chat boards, user profiles, news announcements, and file uploads (specifically for profile pictures). + +## File-by-File Explanation + +This section details the purpose and functionality of each significant file and directory within the ThreadR project. +This, of course, assumes you have a decent understanding of Go. + +### Configuration Files + +* **config/config.json.sample**: + This file provides a template for the main application configuration. It defines critical parameters for the application to run, such as database credentials, domain, and file storage locations. + Example content: + { + "domain_name": "localhost", + "threadr_dir": "/threadr", + "db_username": "threadr_user", + "db_password": "threadr_password", + "db_database": "threadr_db", + "db_svr_host": "localhost:3306", + "file_storage_dir": "files" + } + +* **config/config.json**: + The active configuration file, copied from `config.json.sample` and modified for the specific deployment. Contains sensitive information like database passwords. + +* **config/about_page.htmlbody.sample**: + A template HTML snippet for the "About" page content. This allows administrators to customize the about page without modifying Go templates. + +* **config/about_page.htmlbody**: + The active HTML content for the "About" page, copied from `about_page.htmlbody.sample` and modified as needed. + +### Core Application Files + +* **main.go**: + The entry point of the ThreadR application. + - Parses command-line flags (e.g., `--initialize` for database setup). + - Loads the `config/config.json` file. + - Establishes a connection to the MariaDB database. + - If `--initialize` is set: + - Calls `createTablesIfNotExist` to set up all necessary database tables. + - Calls `ensureAdminUser` to guide the creation of an initial admin user. + - Initializes Gorilla Sessions for session management. + - Loads HTML templates for rendering pages. + - Sets up all HTTP routes and maps them to their respective handler functions, wrapped with session and login middleware as needed. + - Starts the HTTP server on port 8080. + +### Handlers Directory (`handlers/`) + +This directory contains the HTTP handler functions that process incoming requests, interact with models, and render responses. + +* **handlers/app.go**: + Defines common application-wide structures and middleware: + - `PageData`: A struct holding data passed to HTML templates for rendering common elements (title, navbar state, login status, cookie banner, base paths, current URL). + - `Config`: A struct to unmarshal application configuration from `config.json`. + Example JSON for `Config`: + { + "domain_name": "localhost", + "threadr_dir": "/threadr", + "db_username": "threadr_user", + "db_password": "threadr_password", + "db_database": "threadr_db", + "db_svr_host": "localhost:3306", + "file_storage_dir": "files" + } + - `App`: The main application context struct, holding pointers to the database connection, session store, configuration, and templates. + - `SessionMW`: Middleware to retrieve or create a new Gorilla session for each request, making the session available in the request context. + - `RequireLoginMW`: Middleware to enforce user authentication for specific routes, redirecting unauthenticated users to the login page. + +* **handlers/about.go**: + Handles requests for the `/about/` page. Reads the `config/about_page.htmlbody` file and renders it within the `about.html` template. + +* **handlers/accept_cookie.go**: + Handles the cookie banner acceptance. Sets a `threadr_cookie_banner` cookie in the user's browser for 30 days and redirects them back to their previous page or the home page. + +* **handlers/board.go**: + Handles requests for individual forum boards (`/board/?id=`). + - Fetches board details and threads within it. + - Enforces permissions for private boards (redirects if not logged in or unauthorized). + - Handles POST requests to create new threads within the board if the user is logged in and has permission. + - Renders the `board.html` template with board and thread data. + +* **handlers/boards.go**: + Handles requests for the `/boards/` page, listing all available public and private boards. + - Checks if the logged-in user has admin privileges (`PermCreateBoard`). + - Handles POST requests to create new boards (classic or chat type) if the user is an admin. + - Filters private boards based on user permissions. + - Renders the `boards.html` template with lists of public and accessible private boards. + +* **handlers/chat.go**: + Handles both rendering the chat interface and managing WebSocket connections for real-time chat. + - **HTTP Request (`/chat/?id=`):** + - Authenticates the user and fetches board details. + - Enforces permissions for private chat boards. + - Fetches recent chat messages for the specified board. + - Renders the `chat.html` template. + - **WebSocket Request (`/chat/?ws=true&id=`):** + - Upgrades the HTTP connection to a WebSocket. + - Manages client connections via a `ChatHub`. + - Receives JSON messages from clients, creates `models.ChatMessage`, saves them to the DB, and broadcasts them to all clients in the same board. + - `Client` struct: Represents an individual WebSocket connection with user and board ID. + - `ChatHub` struct: Manages active WebSocket clients, message broadcasting, and client registration/unregistration. + Example JSON message for broadcast: + { + "id": 123, + "boardId": 1, + "userId": 456, + "content": "Hello, world! @username", + "replyTo": -1, + "timestamp": "2024-07-30T10:30:00Z", + "username": "chatter1", + "pfpFileId": { + "Int64": 789, + "Valid": true + }, + "mentions": ["username"] + } + +* **handlers/file.go**: + Handles requests for serving uploaded files, primarily profile pictures (`/file?id=`). + - Retrieves file metadata from the database using `models.GetFileByID`. + - Constructs the file path and serves the file using `http.ServeFile`. + +* **handlers/home.go**: + Handles requests for the root path (`/`). Renders the `home.html` template, displaying a welcome message and the ThreadR logo. + +* **handlers/like.go**: + Handles POST requests for liking or disliking posts (`/like/`). + - Requires login. + - Checks for existing likes/dislikes and updates or deletes them based on user action. + - Interacts with `models.Like` for database operations. + +* **handlers/login.go**: + Handles user login (`/login/`). + - On GET: Renders the `login.html` template. + - On POST: Authenticates the user against the database, sets session values upon successful login, and redirects to user home. Shows an error if login fails. + +* **handlers/logout.go**: + Handles user logout (`/logout/`). Clears the user's session and redirects to the home page. + +* **handlers/news.go**: + Handles requests for the `/news/` page. + - Fetches and displays all news items from the database. + - If the user is an admin, it allows posting new news items via POST requests and deleting existing ones. + - Renders the `news.html` template. + +* **handlers/profile.go**: + Handles requests for the user's profile page (`/profile/`). + - Requires login. + - Fetches user details from the database using `models.GetUserByID`. + - Renders the `profile.html` template, displaying user information. + +* **handlers/profile_edit.go**: + Handles editing of user profiles, including display name, bio, and profile picture upload (`/profile/edit/`). + - Requires login. + - On GET: Fetches current user data and renders `profile_edit.html`. + - On POST: Processes form data, including file uploads. + - For profile pictures, it hashes the file, creates a `models.File` record, saves the file to disk, and updates the user's `pfp_file_id`. + - Updates the user's display name and bio in the database. + - Redirects to the profile page after successful update. + +* **handlers/signup.go**: + Handles new user registration (`/signup/`). + - On GET: Renders the `signup.html` template. + - On POST: Creates a new user in the database after hashing the password. Redirects to the login page on success. + +* **handlers/thread.go**: + Handles requests for individual discussion threads (`/thread/?id=`). + - Fetches thread and associated posts. + - Enforces permissions for private boards (if the thread belongs to one). + - Handles POST requests to create new posts within the thread if the user is logged in and has permission. + - Renders the `thread.html` template with thread and post data. + +* **handlers/userhome.go**: + Handles requests for the user's personal home page (`/userhome/`). + - Requires login. + - Fetches current user details. + - Renders the `userhome.html` template. + +### Models Directory (`models/`) + +This directory contains data structures and functions for interacting with the database. Each file typically corresponds to a database table or a logical data entity. + +* **models/board.go**: + - `Board` struct: Represents a forum board. + Example `Board` struct: + type Board struct { + ID int + Name string + Description string + Private bool + PublicVisible bool + PinnedThreads []int // Stored as JSON array in DB + CustomLandingPage string + ColorScheme string + Type string // "classic" or "chat" + } + + - `GetBoardByID`: Fetches a single board by its ID. + - `GetAllBoards`: Fetches all boards, optionally filtered by `private` status. + +* **models/board_permission.go**: + - `BoardPermission` struct: Represents user permissions for a specific board. + - Defines bitmask constants for different permissions (`PermPostInBoard`, `PermModerateBoard`, `PermViewBoard`). + - `GetBoardPermission`: Retrieves a user's permissions for a given board. + - `SetBoardPermission`: Inserts or updates a user's permissions for a board. + - `HasBoardPermission`: Checks if a user has a specific permission for a board. + +* **models/chat.go**: + - `ChatMessage` struct: Represents a single chat message in a chat board. Includes fields for user, content, reply, timestamp, and mentions. + Example `ChatMessage` struct: + type ChatMessage struct { + ID int `json:"id"` + BoardID int `json:"boardId"` + UserID int `json:"userId"` + Content string `json:"content"` + ReplyTo int `json:"replyTo"` + Timestamp time.Time `json:"timestamp"` + Username string `json:"username"` + PfpFileID sql.NullInt64 `json:"pfpFileId"` + Mentions []string `json:"mentions"` + } + Example JSON output (as seen in `handlers/chat.go` broadcast): + { + "id": 123, + "boardId": 1, + "userId": 456, + "content": "Hello, world! @username", + "replyTo": -1, + "timestamp": "2024-07-30T10:30:00Z", + "username": "chatter1", + "pfpFileId": { + "Int64": 789, + "Valid": true + }, + "mentions": ["username"] + } + - `CreateChatMessage`: Inserts a new chat message into the database. + - `GetRecentChatMessages`: Retrieves a limited number of the most recent messages for a board. + - `GetChatMessageByID`: Fetches a single chat message by its ID. + - `extractMentions`: Utility function to parse usernames mentioned in a message. + +* **models/file.go**: + - `File` struct: Represents metadata for an uploaded file (e.g., profile pictures). + Example `File` struct: + type File struct { + ID int + OriginalName string + Hash string + HashAlgorithm string + } + Hypothetical JSON representation: + { + "id": 789, + "originalName": "my_pfp.png", + "hash": "a1b2c3d4...", + "hashAlgorithm": "sha256" + } + - `GetFileByID`: Fetches file metadata by its ID. + - `CreateFile`: Creates a new file record in the database and returns its ID. + +* **models/like.go**: + - `Like` struct: Represents a user's "like" or "dislike" on a post. + - `GetLikesByPostID`: Retrieves all likes/dislikes for a specific post. + - `GetLikeByPostAndUser`: Retrieves a specific like/dislike by post and user. + - `CreateLike`: Adds a new like/dislike. + - `UpdateLikeType`: Changes an existing like to a dislike or vice-versa. + - `DeleteLike`: Removes a like/dislike. + +* **models/news.go**: + - `News` struct: Represents a news announcement. + - `GetAllNews`: Retrieves all news items, ordered by creation time. + - `CreateNews`: Adds a new news item. + - `DeleteNews`: Removes a news item. + +* **models/notification.go**: + - `Notification` struct: Represents a user notification (stubbed for future expansion). + - `GetNotificationsByUserID`: Retrieves notifications for a specific user. + - `CreateNotification`: Stub for creating a notification. + - `MarkNotificationAsRead`: Stub for marking a notification as read. + +* **models/post.go**: + - `Post` struct: Represents a forum post within a thread. + - `GetPostsByThreadID`: Retrieves all posts for a given thread. + - `CreatePost`: Adds a new post to a thread. + +* **models/reaction.go**: + - `Reaction` struct: Represents a user's emoji reaction to a post (stubbed for future expansion). + - `GetReactionsByPostID`: Retrieves all reactions for a post. + - `CreateReaction`: Stub for creating a reaction. + - `DeleteReaction`: Stub for deleting a reaction. + +* **models/repost.go**: + - `Repost` struct: Represents a re-post of a thread to another board (stubbed for future expansion). + - `GetRepostsByThreadID`: Retrieves all reposts for a thread. + - `CreateRepost`: Stub for creating a repost. + +* **models/thread.go**: + - `Thread` struct: Represents a discussion thread within a board. + - `GetThreadByID`: Fetches a single thread by its ID. + - `GetThreadsByBoardID`: Fetches all threads for a given board. + - `CreateThread`: Adds a new thread to a board. + +* **models/user.go**: + - `User` struct: Represents a user in the system, including authentication details, profile info, and global permissions. + Example `User` struct: + type User struct { + ID int + Username string + DisplayName string + PfpFileID sql.NullInt64 // Nullable foreign key to files.id + Bio string + AuthenticationString string + AuthenticationSalt string + AuthenticationAlgorithm string + CreatedAt time.Time + UpdatedAt time.Time + Verified bool + Permissions int64 // Bitmask for global permissions + } + Hypothetical JSON representation (sensitive fields omitted): + { + "id": 456, + "username": "testuser", + "displayName": "Test User", + "pfpFileId": { "Int64": 789, "Valid": true }, + "bio": "Just a test user.", + "createdAt": "2024-01-01T00:00:00Z", + "updatedAt": "2024-07-30T10:00:00Z", + "verified": false, + "permissions": 3 // Assuming PermCreateBoard | PermManageUsers + } + - Defines bitmask constants for global permissions (`PermCreateBoard`, `PermManageUsers`). + - `GetUserByID`: Fetches a user by their ID. + - `GetUserByUsername`: Fetches a user by their username. + - `CheckPassword`: Verifies a given password against the stored hash, salt, and algorithm. + - `HashPassword`: Hashes a password using a salt and specified algorithm (currently SHA256). + - `CreateUser`: Creates a new user with hashed password. + - `UpdateUserProfile`: Updates a user's display name and bio. + - `UpdateUserPfp`: Updates a user's profile picture file ID. + - `HasGlobalPermission`: Checks if a user has a specific global permission. + +### Static Directory (`static/`) + +* **static/style.css**: + The main stylesheet for the ThreadR application, defining the visual theme, layout, and responsive design elements. It includes light and dark mode support. + +* **static/img/ThreadR.png**: + The main logo or banner image for the ThreadR application, displayed on the home page. + +### Templates Directory (`templates/`) + +This directory holds HTML templates for rendering the user interface. It follows a structure of a base template, partials (reusable components), and page-specific templates. + +* **templates/base.html**: + The foundational HTML template. It defines the basic HTML structure, includes the stylesheet, and incorporates the `navbar` and `cookie_banner` partials. It also defines a `content` block where page-specific content will be inserted. + +* **templates/partials/cookie_banner.html**: + A reusable template snippet that renders a cookie consent banner at the bottom of the page if `ShowCookieBanner` is true in `PageData`. + +* **templates/partials/navbar.html**: + A reusable template snippet that renders the navigation bar at the top of the page. It dynamically highlights the active page and shows different links based on `LoggedIn` status (e.g., Login/Signup vs. User Home/Profile/Logout). + +* **templates/pages/about.html**: + Page-specific template for the "About" section. It's unique in that it directly renders `AboutContent` from the handler, allowing for fully custom HTML content without needing an additional `content` block. + +* **templates/pages/board.html**: + Page-specific template for displaying an individual forum board, listing its threads and providing a form to create new threads. + +* **templates/pages/boards.html**: + Page-specific template for listing all public and accessible private forum boards, and an admin form for creating new boards. + +* **templates/pages/chat.html**: + Page-specific template for a real-time chat board. It includes: + - A header with board name and description. + - A scrollable `div` for chat messages. + - An input area with a `textarea` for messages and a "Send" button. + - Client-side JavaScript for WebSocket communication, message appending, replying to messages, and username autocomplete. + - Extensive inline CSS for chat-specific styling. + +* **templates/pages/home.html**: + Page-specific template for the home page, displaying a welcome message and the `ThreadR.png` image. + +* **templates/pages/login.html**: + Page-specific template for the user login form. Displays error messages if authentication fails. + +* **templates/pages/news.html**: + Page-specific template for displaying news announcements. Includes forms for admins to post and delete news items. + +* **templates/pages/profile.html**: + Page-specific template for displaying a user's profile information. Shows username, display name, profile picture (if uploaded), bio, and account creation/update times. + +* **templates/pages/profile_edit.html**: + Page-specific template for editing a user's profile. Provides forms to update display name, bio, and upload a new profile picture. + +* **templates/pages/signup.html**: + Page-specific template for the new user registration form. + +* **templates/pages/thread.html**: + Page-specific template for displaying an individual discussion thread, listing its posts, and providing forms for users to post new messages or reply to existing ones.