319 lines
11 KiB
Lua
Executable File
319 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 select(3, os.execute"id ikibooru") == 0 then
|
|
while true do
|
|
io.stdout:write"User ikibooru already exists. Can use? (y/n): "
|
|
local o = io.read"*l":lower()
|
|
if o == "y" then
|
|
break
|
|
elseif o == "n" then
|
|
print"Exiting."
|
|
return
|
|
end
|
|
print"Try again."
|
|
end
|
|
else
|
|
if not os.execute"useradd -U -m -s /bin/sh ikibooru" then
|
|
print"Failed to create user ikibooru."
|
|
return
|
|
end
|
|
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. Reminder: Ikibooru is only an HTTP server. It must be used together with a relay or reverse proxy."
|