Compare commits

..

1 Commits

Author SHA1 Message Date
ca23415288 feat: allow containerized deployments
At the moment, it seems like it should be working, but I get:
```
lua5.1: error loading module 'bcrypt' from file '/usr/local/openresty/luajit/lib/lua/5.1/bcrypt.so':
	Error relocating /usr/local/openresty/luajit/lib/lua/5.1/bcrypt.so: luaL_setfuncs: symbol not found
```
2025-05-22 11:25:21 +02:00
18 changed files with 87 additions and 105 deletions

View File

@ -1,8 +0,0 @@
logs/
nginx.conf.compiled
.vscode/
.local/
data/db/*
secrets
secrets/.touched*
sass

11
.gitignore vendored
View File

@ -1,9 +1,10 @@
logs/ logs/
nginx.conf.compiled nginx.conf.compiled
db.*.sqlite
.vscode/ .vscode/
.local/ .local/
data/db/* static/avatars/*
secrets/secrets.lua !static/avatars/default.webp
secrets/.touched* secrets.lua
data/static/avatars/*
!data/static/avatars/default.webp .first_launch.*

View File

@ -1,16 +0,0 @@
# HOW TO:
#
# docker compose up
#
# it exposes the data/ and secrets/ volumes in app root
#
FROM openresty/openresty:alpine-fat
RUN apk add --no-cache git make gcc g++ musl-dev libffi-dev openssl-dev sqlite-dev libsodium libsodium-dev imagemagick-dev openssl
WORKDIR /app
COPY . .
RUN eval "$(luarocks --lua-version=5.1 path)"
RUN luarocks --lua-version=5.1 build --only-deps
EXPOSE 8080
RUN chmod +x /app/start.sh
ENTRYPOINT ["/app/start.sh", "production"]

View File

@ -6,43 +6,34 @@ Released under [CNPLv7+](https://thufie.lain.haus/NPL.html).
Please read the [full terms](./LICENSE.md) for proper wording. Please read the [full terms](./LICENSE.md) for proper wording.
# installing & first time setup # installing & first time setup
## docker 1. first, install OpenResty. instructions for linux can be found [here](https://openresty.org/en/linux-packages.html).
```bash 2. then, install LuaJIT and Lua 5.1 (usually called `lua5.1` in package managers)
$ docker compose up 3. then, install [LuaRocks](https://luarocks.org) (prefer your package manager instead of a local install recommended by the guide)
``` 4. add luarocks search dirs to path:
- opens port 8080
- exposes `data/db` and `data/avatars` as volumes for data backup and persistence
- exposes `secrets/` as a volume so that the script won't try to perform first time setup again
## manual
1. install:
- OpenResty. instructions for linux can be found [here](https://openresty.org/en/linux-packages.html)
- LuaJIT and Lua 5.1 (usually called `lua5.1` in package managers)
- openssl (-dev)
- sqlite (-dev)
- libsodium (-dev)
- imagemagick (-dev)
- [LuaRocks](https://luarocks.org) (either through the guide's instructions or your package manager, whichever is newer)
2. add luarocks search dirs to path:
```bash ```bash
# in .bashrc (or other shell equivalent) # in .bashrc (or other shell equivalent)
eval "$(luarocks --lua-version 5.1 path)" eval "$(luarocks --lua-version 5.1 path)"
``` ```
3. clone repo 5. clone repo
4. install the lua dependencies: 6. install the dependencies:
```bash ```bash
$ luarocks --local --lua-version 5.1 build --only-deps $ luarocks --local --lua-version 5.1 build --only-deps
``` ```
5. run: 7. create a file named `secrets.lua` in the project directory.
use the `secrets.lua.example` file as reference, and generate a cryptographically secure random key, for example, with:
```bash ```bash
$ start.sh production # or 'development' or empty string $ openssl rand -hex 32
```
8. run:
```bash
$ start.sh production
``` ```
the script will perform some necessary first time setup (and create a hidden file in the folder to ensure it won't do so again). it will create an administrator account and print the credentials to the console; **this will only happen once**. make sure you save them somewhere. the administrator account is the only one that can promote other users to moderator. the script will perform some necessary first time setup (and create a hidden file in the folder to ensure it won't do so again). it will create an administrator account and print the credentials to the console; **this will only happen once**. make sure you save them somewhere. the administrator account is the only one that can promote other users to moderator.
(note the `production` argument. if called with no arguments, `start.sh` will run in a development environment, which uses a separate database and shows more debug information.) (note the `production` argument. if called with no arguments, `start.sh` will run in a development environment, which uses a separate database.)
this app is made with the assumption that it is being reverse-proxied. as such, you may want to change the port to something other than the default `8080`. you can do that in [`config.lua`]([./config.lua]). this app is made with the assumption that it is being reverse-proxied. as such, you may want to change the port to something other than the default `8080`. you can do that in [`config.lua`]([./config.lua]).

View File

@ -5,7 +5,7 @@ local constants = require("constants")
local util = require("util") local util = require("util")
local auth = require("lib.auth") local bcrypt = require("bcrypt")
local rand = require("openssl.rand") local rand = require("openssl.rand")
local models = require("models") local models = require("models")
@ -14,7 +14,7 @@ local Sessions = models.Sessions
local Avatars = models.Avatars local Avatars = models.Avatars
local function authenticate_user(user, password) local function authenticate_user(user, password)
return auth.verify(password, user.password_hash) return bcrypt.verify(password, user.password_hash)
end end
local function create_session_key() local function create_session_key()
@ -321,7 +321,7 @@ app:post("user_signup", "/signup", function(self)
local new_user = Users:create({ local new_user = Users:create({
username = username, username = username,
password_hash = auth.digest(password), password_hash = bcrypt.digest(password, constants.BCRYPT_ROUNDS),
permission = constants.PermissionLevel.GUEST, permission = constants.PermissionLevel.GUEST,
}) })

View File

@ -1,5 +1,5 @@
local config = require("lapis.config") local config = require("lapis.config")
local secrets = require("secrets.secrets") local secrets = require("secrets")
config({"development", "production"}, { config({"development", "production"}, {
port = 8080, port = 8080,
@ -7,7 +7,7 @@ config({"development", "production"}, {
code_cache = "off", code_cache = "off",
num_workers = "1", num_workers = "1",
sqlite = { sqlite = {
database = "data/db/db.dev.sqlite" database = "db.dev.sqlite"
}, },
secret = "SUPER SECRET", secret = "SUPER SECRET",
session_name = "porom_session", session_name = "porom_session",
@ -20,7 +20,7 @@ config("production", {
}, },
secret = secrets.key, secret = secrets.key,
sqlite = { sqlite = {
database = "data/db/db.prod.sqlite" database = "db.prod.sqlite"
}, },
session_name = "porom_session_s" session_name = "porom_session_s"
}) })

View File

@ -1,4 +1,4 @@
local auth = require("lib.auth") local bcrypt = require("bcrypt")
local models = require("models") local models = require("models")
local constants = require("constants") local constants = require("constants")
@ -23,14 +23,13 @@ local function create_admin()
return return
end end
math.randomseed(os.time())
local password = "" local password = ""
for _ = 1, 16 do for _ = 1, 16 do
local randi = math.random(#alphabet) local randi = math.random(#alphabet)
password = password .. alphabet:sub(randi, randi) password = password .. alphabet:sub(randi, randi)
end end
local hash = auth.digest(password) local hash = bcrypt.digest(password, constants.BCRYPT_ROUNDS)
models.Users:create({ models.Users:create({
username = username, username = username,

13
docker-compose.yaml Normal file
View File

@ -0,0 +1,13 @@
# Generate a random secret key
# export PROD_SECRET_KEY=$(openssl rand -hex 32)
# Start the container
# docker-compose up
version: "3"
services:
porom:
build:
context: .
args:
- PROD_SECRET_KEY=${PROD_SECRET_KEY}
ports:
- "8080:8080"

View File

@ -1,10 +0,0 @@
services:
porom:
build:
context: .
ports:
- "8080:8080"
volumes:
- ./data/static:/app/data/static
- ./data/db:/app/data/db
- ./secrets:/app/secrets

36
dockerfile Normal file
View File

@ -0,0 +1,36 @@
# HOW TO:
#
# Generate a random secret key & build the Docker image
# ```sh
# SECRET_KEY=$(openssl rand -hex 32) docker build --build-arg PROD_SECRET_KEY="$SECRET_KEY" -t porom:latest .
# ```
#
# Then run the container
# ```sh
# docker run -d -p 8080:8080 --name porom porom:latest
# ```
#
FROM openresty/openresty:alpine-fat
COPY ./nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
COPY . /usr/local/openresty/nginx/html
WORKDIR /usr/local/openresty/nginx/html
RUN apk add --no-cache \
make \
git \
make \
gcc \
g++ \
musl-dev \
libffi-dev \
openssl-dev \
sqlite-dev \
imagemagick-dev \
lua5.1 \
lua5.1-dev
RUN eval "$(luarocks --lua-version 5.1 path)"
RUN luarocks --lua-version 5.1 build --only-deps
ARG PROD_SECRET_KEY
RUN echo "return { key = \"${PROD_SECRET_KEY}\",}" > /usr/local/openresty/nginx/html/secrets.lua
EXPOSE 8080
RUN chmod +x /usr/local/openresty/nginx/html/start.sh
ENTRYPOINT ["/usr/local/openresty/nginx/html/start.sh", "production"]

View File

@ -1,16 +0,0 @@
local auth = {}
local ls = require "luasodium"
function auth.digest(password)
return ls.crypto_pwhash_str(
password,
ls.crypto_pwhash_OPSLIMIT_INTERACTIVE,
ls.crypto_pwhash_MEMLIMIT_INTERACTIVE)
end
function auth.verify(password, hash)
return ls.crypto_pwhash_str_verify(hash, password)
end
return auth

View File

@ -26,15 +26,15 @@ http {
} }
location /static/ { location /static/ {
alias data/static/; alias static/;
} }
location /favicon.ico { location /favicon.ico {
alias data/static/favicon.ico; alias static/favicon.ico;
} }
location /avatars { location /avatars {
alias data/static/avatars; alias static/avatars;
expires 1y; expires 1y;
} }
} }

View File

@ -16,7 +16,7 @@ dependencies = {
"lapis == 1.16.0", "lapis == 1.16.0",
"lsqlite3", "lsqlite3",
"magick", "magick",
"luasodium", "bcrypt",
"luaossl", "luaossl",
} }

View File

@ -1,23 +1,15 @@
#!/bin/bash #!/bin/bash
set -e
start() { start() {
lapis serve lapis serve
} }
first_launch() { first_launch() {
echo "Setting up for the first time" echo "Setting up for the first time"
mkdir -p secrets touch ".first_launch.$LAPIS_ENVIRONMENT"
local SECRET lua5.1 schema.lua
SECRET="$(openssl rand -hex 32)"
echo "return { key = \"${SECRET}\",}" > secrets/secrets.lua
touch "secrets/.touched.$LAPIS_ENVIRONMENT"
mkdir -p data/db
luajit schema.lua
chmod -R a+rw data
lapis migrate lapis migrate
luajit create_default_accounts.lua lua5.1 create_default_accounts.lua
} }
if [[ $# -ne 1 ]]; then if [[ $# -ne 1 ]]; then
@ -29,7 +21,7 @@ fi
echo "Starting in $LAPIS_ENVIRONMENT" echo "Starting in $LAPIS_ENVIRONMENT"
if ! [ -f "secrets/.touched.$LAPIS_ENVIRONMENT" ]; then if ! [ -f ".first_launch.$LAPIS_ENVIRONMENT" ]; then
first_launch first_launch
fi fi

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -1,5 +1,5 @@
<% if infobox then %> <% if infobox then %>
<% render("views.common.infobox", infobox) %> <% render("views.common.infobox", pop_infobox) %>
<% end %> <% end %>
<div class="darkbg"> <div class="darkbg">
<h1 class="thread-title">Latest posts by <i><%= user.username %></i></h1> <h1 class="thread-title">Latest posts by <i><%= user.username %></i></h1>