ikibooru/install.lua
2024-06-11 16:53:52 +03:00

305 lines
11 KiB
Lua
Executable File

#!/usr/bin/env lua5.3
print"Ikibooru Installer\n"
do
local st, res = pcall(io.popen, "uname -s", "r")
if not st or res:read"*l" ~= "Linux" then
print"Only Linux is supported by the installer at this moment."
return
end
end
if not os.getenv"SUDO_COMMAND" then
print"Please run under sudo or as root."
return
end
local baad = false
if not pcall(require, "lfs") then
print"LuaFileSystem not installed."
print"Install manually or using LuaRocks (package name 'luafilesystem')"
print""
baad = true
end
if not pcall(require, "zlib") or not require"zlib"._COPYRIGHT or not (require"zlib"._COPYRIGHT:match"Maher" or require"zlib"._COPYRIGHT:match"Dionizio") then
print"lua-zlib not installed."
print"Install manually or using LuaRocks (package name 'lua-zlib')"
print""
baad = true
end
if not pcall(require, "openssl") or require"openssl".VERSION_NUMBER == nil then
print"luaossl not installed."
print"Install manually or using LuaRocks (package name 'luaossl')"
print""
baad = true
end
if not pcall(require, "posix.fcntl") then
print"LuaPOSIX not installed."
print"Install manually or using LuaRocks (package name 'luaposix')"
print""
baad = true
end
if not pcall(require, "socket.url") then
print"LuaSocket not installed."
print"Install manually or using LuaRocks (package name 'luasocket')"
print""
baad = true
end
if not pcall(require, "luasql.mysql") or not require"luasql.mysql".mysql() then
print"luasql-mysql not installed."
print"Install manually or using LuaRocks (package name 'luasql-mysql')"
print""
baad = true
end
if not pcall(require, "mimetypes") then
print"mimetypes not installed."
print"Install manually or using LuaRocks (package name 'mimetypes')"
print""
baad = true
end
local magickprefix
if #io.popen"magick convert":read"*a" > 0 then
magickprefix = "magick "
elseif #io.popen"convert":read"*a" > 0 then
magickprefix = ""
else
print"ImageMagick not installed."
print"Install manually or via your system's package manager."
print""
baad = true
end
if baad then
return
end
if not os.execute"adduser --shell /bin/sh --disabled-password ikibooru" then
print"Failed to create user ikibooru."
return
end
io.stdout:write"MySQL host: "
local mysqladdr = io.read"*l"
io.stdout:write"MySQL user: "
local mysqluser = io.read"*l"
io.stdout:write"MySQL password: "
local mysqlpass = io.read"*l"
local SQLConn = require"luasql.mysql".mysql():connect("", mysqluser, mysqlpass, mysqladdr)
if not SQLConn then
print"Could not connect to MySQL database."
baad = true;
end
if baad then
return
end
print""
local port
repeat
io.stdout:write"Port to run on (external connections to which must be blocked): "
port = tonumber(io.read"*l")
until tonumber(port) ~= nil
port = tostring(port)
io.stdout:write"Public URI prefix (e.g. https://foo.bar): "
local domname = io.read"*l"
print""
print"The following parameters are required for the administrator account (user id 0):"
io.stdout:write"E-mail address (must be real): "
local adminusereml = io.read"*l"
io.stdout:write"Display name: "
local adminuserdispname = io.read"*l"
print""
print"Enter member exclusivity:"
print"1. Only admin can invite"
print"2. Mods can invite"
print"3. Any member can invite"
print"4. Public registration; mods must approve"
print"5. Public registration without approval (not recommended)"
local membexcl
while true do
membexcl = tonumber(io.read"*l")
if membexcl and membexcl % 1 == 0 then
if membexcl >= 1 and membexcl <= 5 then
break
end
end
print"Try again."
end
print""
io.stdout:write"Max number of files per object: "
local maxfilesperobj
while true do
maxfilesperobj = tonumber(io.read"*l")
if maxfilesperobj and maxfilesperobj % 1 == 0 and maxfilesperobj > 0 then
break
end
print"Try again."
end
io.stdout:write"Max total object size (in kB): "
local maxtotalobjsize
while true do
maxtotalobjsize = tonumber(io.read"*l")
if maxtotalobjsize and maxtotalobjsize % 1 == 0 and maxtotalobjsize > 0 then
break
end
print"Try again."
end
os.execute"mkdir /home/ikibooru/ikibooru"
os.execute"cp -r ./* /home/ikibooru/ikibooru"
os.execute"mkdir -p /home/ikibooru/ikibooru/objd"
os.execute"rm -r /home/ikibooru/ikibooru/objd/*"
os.execute"rm /home/ikibooru/ikibooru/install.lua"
-- Not /dev/urandom. System might be new and unseeded.
os.execute"dd if=/dev/random of=/home/ikibooru/ikibooru/objkey bs=16 count=1"
os.execute"chown -R ikibooru:ikibooru /home/ikibooru/*"
os.execute"find /home/ikibooru/ -type f -exec chmod 444 {} \\;"
os.execute"find /home/ikibooru/ -type d -exec chmod 555 {} \\;"
os.execute"chmod 440 /home/ikibooru/ikibooru/objkey"
-- These files need writability
os.execute"find /home/ikibooru/ikibooru/objd/ -type d -exec chmod 755 {} \\;"
os.execute"chmod 644 /home/ikibooru/ikibooru/static/tagcats.css"
local cfg = io.open("/home/ikibooru/ikibooru/cfg.lua", "wb")
cfg:write(string.format([[-- THIS FILE IS AUTO-GENERATED. EDIT WITH CAUTION.
return {["domain"]=%q,["sqlh"]=%q,["sqlu"]=%q,["sqlp"]=%q,["port"]=%q,["maxfilesperobj"]=%q,["membexcl"]=%q,["maxtotalobjsize"]=%q,["magickprefix"]=%q,["tc"]={[1]={["name"]="",["col"]=14096010,},[2]={["name"]="",["col"]=6616647,},[3]={["name"]="",["col"]=13138224,},[4]={["name"]="",["col"]=13395600,},[5]={["name"]="",["col"]=15294904,},[6]={["name"]="",["col"]=3314361,},[7]={["name"]="",["col"]=5624104,},[8]={["name"]="",["col"]=12888753,},[9]={["name"]="",["col"]=4660286,},[10]={["name"]="",["col"]=9294073,},[11]={["name"]="",["col"]=8009393,},[12]={["name"]="",["col"]=10550703,},[13]={["name"]="",["col"]=6120067,},[14]={["name"]="",["col"]=8613437,},[15]={["name"]="",["col"]=15975763,},[16]={["name"]="",["col"]=15371202,},[17]={["name"]="",["col"]=10665472,},[18]={["name"]="",["col"]=12034245,},[19]={["name"]="",["col"]=2375696,},[20]={["name"]="",["col"]=10183247,},[21]={["name"]="",["col"]=273478,},[22]={["name"]="",["col"]=4074963,},[23]={["name"]="",["col"]=2302363,},[24]={["name"]="",["col"]=13491847,},[25]={["name"]="",["col"]=2628638,},[26]={["name"]="",["col"]=6726730,},[27]={["name"]="",["col"]=2177522,},[28]={["name"]="",["col"]=1825508,},[29]={["name"]="",["col"]=16759172,},[30]={["name"]="",["col"]=3661743,},[31]={["name"]="",["col"]=8605577,},[32]={["name"]="",["col"]=14077967,},["n"]=32,},["smtpauth"]=true,["sitename"]="Ikibooru",["codever"]=0,["dbver"]=0,["ruleset"]="",["sendfile"]="",["sendfileprefix"]="",["enable18plus"]=false}]], domname, mysqladdr, mysqluser, mysqlpass, port, maxfilesperobj, membexcl, maxtotalobjsize, magickprefix))
cfg:close() cfg = nil
os.execute"chmod 640 /home/ikibooru/ikibooru/cfg.lua"
os.execute"chown root:root /home/ikibooru/ikibooru/uninstall.lua"
SQLConn:execute[[CREATE DATABASE ikibooru CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;]]
SQLConn:execute[[USE ikibooru;]]
SQLConn:execute[[CREATE TABLE `tags` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`category` tinyint(3) unsigned NOT NULL,
`adultonly` BIT NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;]]
SQLConn:execute[[CREATE TABLE `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`displayname` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`createtime` datetime NOT NULL,
`privs` tinyint(4) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;]]
SQLConn:execute[[CREATE TABLE `objects` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`owner` bigint(20) unsigned NULL,
`approved` bit(1) NOT NULL,
`published` bit(1) NOT NULL,
`createtime` datetime NOT NULL,
PRIMARY KEY (`id`),
FULLTEXT KEY `objects_name_IDX` (`name`),
KEY `objects_FK_owner` (`owner`),
CONSTRAINT `objects_FK_owner` FOREIGN KEY (`owner`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;]]
SQLConn:execute[[CREATE TABLE `comments` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`objid` bigint(20) unsigned NOT NULL,
`authorid` bigint(20) unsigned NOT NULL,
`createtime` datetime NOT NULL,
`content` text NOT NULL,
PRIMARY KEY (`id`),
KEY `comments_FK_objid` (`objid`),
KEY `comments_FK_authorid` (`authorid`),
CONSTRAINT `comments_FK_objid` FOREIGN KEY (`objid`) REFERENCES `objects` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `comments_FK_authorid` FOREIGN KEY (`authorid`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;]]
SQLConn:execute[[CREATE TABLE `objtag` (
`objid` bigint(20) unsigned NOT NULL,
`tagid` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`objid`,`tagid`),
KEY `objtag_FK_objid` (`objid`),
KEY `objtag_FK_tagid` (`tagid`),
CONSTRAINT `objtag_FK_objid` FOREIGN KEY (`objid`) REFERENCES `objects` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `objtag_FK_tagid` FOREIGN KEY (`tagid`) REFERENCES `tags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;]]
SQLConn:execute[[CREATE TABLE `reports` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`reporter` bigint(20) unsigned NOT NULL,
`reportee` bigint(20) unsigned NOT NULL,
`createtime` datetime NOT NULL,
`content` text DEFAULT NULL,
`status` tinyint(4) NOT NULL,
PRIMARY KEY (`id`),
KEY `reports_FK_reporter` (`reporter`),
KEY `reports_FK_reportee` (`reportee`),
CONSTRAINT `reports_FK_reportee` FOREIGN KEY (`reportee`) REFERENCES `users` (`id`),
CONSTRAINT `reports_FK_reporter` FOREIGN KEY (`reporter`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;]]
SQLConn:execute(string.format([[INSERT INTO ikibooru.users (displayname, email, createtime, privs) VALUES ("%s", "%s", "%s", 255);]], SQLConn:escape(adminuserdispname), SQLConn:escape(adminusereml), os.date("!%Y-%m-%d %H:%M:%S")))
if pcall(io.popen, "systemd --version") then
local systemd
while true do
io.stdout:write"Register systemd service (y/n): "
local o = io.read"*l":lower()
if o == "y" then
systemd = true
break
elseif o == "n" then
systemd = false
break
end
print"Try again."
end
if systemd then
local external = false
while true do
io.stdout:write"Is MySQL server external (y/n): "
local o = io.read"*l":lower()
if o == "y" then
external = true
break
elseif o == "n" then
external = false
break
end
print"Try again."
end
local service = io.open("/etc/systemd/system/ikibooru.service", "wb")
service:write(string.format([[[Unit]
Description=Ikibooru fileboard
%s
[Service]
Type=simple
Restart=on-failure
ExecStart=lua5.3 main.lua
WorkingDirectory=/home/ikibooru/ikibooru
User=ikibooru
Group=ikibooru]], external and "" or "After=mysql.target"))
service:close()
os.execute"systemctl daemon-reload"
print"Done. Service can be started via 'systemctl start ikibooru'."
end
end
print""
print"Installation complete. Remember: Ikibooru is only an HTTP server. It must be used together with a relay or reverse proxy."