add threads n posts

This commit is contained in:
Lera Elvoé 2025-05-18 17:55:03 +03:00
parent f5485702a8
commit 4039d6d299
Signed by: yagich
SSH Key Fingerprint: SHA256:6xjGb6uA7lAVcULa7byPEN//rQ0wPoG+UzYVMfZnbvc
7 changed files with 182 additions and 2 deletions

View File

@ -1,7 +1,119 @@
local app = require("lapis").Application()
local lapis_util = require("lapis.util")
local db = require("lapis.db")
local util = require("util")
local models = require("models")
local Topics = models.Topics
local Threads = models.Threads
local Posts = models.Posts
app:get("thread_create", "/create", function(self)
local user = util.get_logged_in_user(self)
if not user then
self.session.flash = {error = "You must be logged in to perform this action."}
return {redirect_to = self:url_for("user_login")}
end
local all_topics = db.query("select * from topics limit 25;")
if #all_topics == 0 then
return "how did you get here?"
end
self.all_topics = all_topics
return {render = "threads.create"}
end)
app:post("thread_create", "/create", function(self)
local user = util.get_logged_in_user(self)
if not user then
self.session.flash = {error = "You must be logged in to perform this action."}
return {redirect_to = self:url_for("user_login")}
end
local topic = Topics:find(self.params.topic_id)
if not topic then
return {redirect_to = self:url_for("topics")}
end
local title = lapis_util.trim(self.params.title)
local time = os.time()
local slug = lapis_util.slugify(title) .. "-" .. time
local post_content = self.params.initial_post
local thread = Threads:create({
topic_id = topic.id,
user_id = user.id,
title = title,
slug = slug,
created_at = time,
})
local post = util.create_post(thread.id, user.id, post_content)
if not post then
return {redirect_to = self:url_for("topics")}
end
return {redirect_to = self:url_for("thread", {slug = slug})}
end)
app:get("thread", "/:slug", function(self)
local thread = Threads:find({
slug = self.params.slug
})
if not thread then
return {status = 404}
end
self.thread = thread
local posts = db.query([[
SELECT
posts.id, post_history.content, users.username, avatars.file_path AS avatar_path
FROM
posts
JOIN
post_history ON posts.current_revision_id = post_history.id
JOIN
users ON posts.user_id = users.id
LEFT JOIN
avatars ON users.avatar_id = avatars.id
WHERE
posts.thread_id = ? and posts.id > ?
ORDER BY
posts.created_at ASC
LIMIT 20
]], thread.id, tonumber(self.params.cursor or 0))
self.user = util.get_logged_in_user_or_transient(self)
self.posts = posts
self.next_cursor = #posts > 0 and posts[#posts].id or nil
return {render = "threads.thread"}
end)
app:post("thread", "/:slug", function(self)
local thread = Threads:find({
slug = self.params.slug
})
if not thread then
return {redirect_to = self:url_for("all_topics")}
end
local user = util.get_logged_in_user(self)
if not user then
return {redirect_to = self:url_for("all_topics")}
end
if user:is_guest() then
return {redirect_to = self:url_for("thread", {slug = thread.slug})}
end
if util.is_thread_locked(thread) and not user:is_admin() then
return {redirect_to = self:url_for("thread", {slug = thread.slug})}
end
local post_content = self.params.post_content
local post = util.create_post(thread.id, user.id, post_content)
if not post then
return {redirect_to = self:url_for("thread", {slug = thread.slug})}
end
return {redirect_to = self:url_for("thread", {slug = thread.slug})}
end)
return app

View File

@ -19,7 +19,7 @@ local ThreadCreateError = {
TOPIC_LOCKED = 3,
}
app:get("all_topics", "/", function(self)
app:get("all_topics", "", function(self)
self.topic_list = db.query("select * from topics limit 25;")
self.user = util.get_logged_in_user(self) or util.TransientUser
return {render = "topics.topics"}

View File

@ -41,4 +41,11 @@ return {
-- will appear on top of non-stickied threads in topic view
schema.add_column("threads", "is_stickied", "BOOLEAN DEFAULT FALSE")
end,
[5] = function ()
db.query("CREATE INDEX idx_posts_thread ON posts(thread_id, created_at, id)")
db.query("CREATE INDEX idx_users_avatar ON users(avatar_id)")
db.query("CREATE INDEX idx_topics_slug ON topics(slug)")
db.query("CREATE INDEX idx_threads_slug ON threads(slug)")
end,
}

View File

@ -4,6 +4,8 @@ local db = require("lapis.db")
local Avatars = require("models").Avatars
local Users = require("models").Users
local Posts = require("models").Posts
local PostHistory = require("models").PostHistory
util.TransientUser = {
is_admin = function (self)
@ -15,6 +17,7 @@ util.TransientUser = {
is_logged_in_guest = function (self)
return false
end,
username = "Deleted User",
}
function util.get_user_avatar_url(req, user)
@ -94,4 +97,29 @@ function util.form_bool_to_sqlite(s)
return util.bton(util.stob(s))
end
function util.is_thread_locked(thread)
return util.ntob(thread.is_locked)
end
function util.create_post(thread_id, user_id, content)
db.query("BEGIN")
local post = Posts:create({
thread_id = thread_id,
user_id = user_id,
current_revision_id = db.NULL,
})
local revision = PostHistory:create({
post_id = post.id,
user_id = user_id,
content = content,
is_initial_revision = true,
})
post:update({current_revision_id = revision.id})
db.query("COMMIT")
return post
end
return util

View File

@ -0,0 +1,13 @@
<h1>New thread</h1>
<form method="post">
<label for="topic_id">Topic:</label>
<select name="topic_id", id="topic_id" autocomplete="off">
<% for _, topic in ipairs(all_topics) do %>
<option value="<%= topic.id %>" <%- params.topic_id == tostring(topic.id) and "selected" or "" %>><%= topic.name %></value>
<% end %>
</select><br>
<label for="title">Thread title:</label>
<input type="text" id="title" name="title" required><br>
<textarea id="initial_post" name="initial_post" placeholder="Post body" required></textarea><br>
<input type="submit" value="Create thread">
</form>

View File

@ -0,0 +1,20 @@
<% for _, post in ipairs(posts) do %>
<div>
<img src="<%= post.avatar_path or "/avatars/default.webp" %>">
<div><%= post.username %></div>
<div><p><%- require("lapis.html").escape(post.content):gsub("\n", "<br>") %></p></div>
</div>
<% end %>
<% if not user:is_guest() then %>
<h1>Respond to "<%= thread.title %>"</h1>
<form method="post">
<textarea id="post_content" name="post_content" placeholder="Response body" required></textarea><br>
<input type="submit" value="Reply">
</form>
<% end %>
<% if next_cursor then %>
<a href="<%= url_for('thread', {slug = thread.slug}, {cursor = next_cursor}) %>">
Older posts →
</a>
<% end %>

View File

@ -6,7 +6,7 @@
<ul>
<% for i, v in ipairs(topic_list) do %>
<li>
<a href=<%= v.slug %>><%= v.name %></a> - <%= v.description %>
<a href=<%= url_for("topic", {slug = v.slug}) %>><%= v.name %></a> - <%= v.description %>
</li>
<% end %>
<% end %>