diff --git a/misc/website/backend/README.md b/misc/website/backend/README.md index a45fea5..cd99f79 100755 --- a/misc/website/backend/README.md +++ b/misc/website/backend/README.md @@ -1,5 +1,7 @@ # Lapis-chan +https://github.com/karai17/lapis-chan + Lapis-chan is a text and image board written in Lua using the Lapis web framework. # Features diff --git a/misc/website/backend/app/app.lua b/misc/website/backend/app/app.lua index aadc808..ab4bb88 100755 --- a/misc/website/backend/app/app.lua +++ b/misc/website/backend/app/app.lua @@ -41,6 +41,6 @@ end --]] app:include("apps.api") -app:include("apps.web") +-- app:include("apps.web") return app diff --git a/misc/website/backend/app/config.lua b/misc/website/backend/app/config.lua index ec66c20..0ba6567 100755 --- a/misc/website/backend/app/config.lua +++ b/misc/website/backend/app/config.lua @@ -6,63 +6,63 @@ local secret = assert(loadfile("../data/secrets/token.lua"))() local subdomains = false -- Maximum file size (update this in scripts.js too!) -local body_size = "15m" +local body_size = "15m" -- Maximum comment size (update this in scripts.js too!) -local text_size = 10000 +local text_size = 10000 -- Path to your lua libraries (LuaRocks and OpenResty) -local lua_path = "./src/?.lua;./src/?/init.lua" +local lua_path = "./src/?.lua;./src/?/init.lua" local lua_cpath = "" config("development", { - site_name = "[DEVEL] Lapis-chan", - port = 80, - secret = secret, - subdomains = subdomains, - body_size = body_size, - text_size = text_size, - lua_path = lua_path, - lua_cpath = lua_cpath, - postgres = { - host = "psql", - user = "postgres", - password = "", - database = "lapischan" - }, + site_name = "[DEVEL] Lapis-chan", + port = 80, + secret = secret, + subdomains = subdomains, + body_size = body_size, + text_size = text_size, + lua_path = lua_path, + lua_cpath = lua_cpath, + postgres = { + host = "psql", + user = "postgres", + password = "", + database = "lapischan", + }, }) config("production", { - code_cache = "on", - site_name = "Lapis-chan", - port = 80, - secret = secret, - subdomains = subdomains, - body_size = body_size, - text_size = text_size, - lua_path = lua_path, - lua_cpath = lua_cpath, - postgres = { - host = "psql", - user = "postgres", - password = "", - database = "lapischan" - }, + code_cache = "on", + site_name = "Lapis-chan", + port = 80, + secret = secret, + subdomains = subdomains, + body_size = body_size, + text_size = text_size, + lua_path = lua_path, + lua_cpath = lua_cpath, + postgres = { + host = "psql", + user = "postgres", + password = "", + database = "lapischan", + }, }) config("test", { - site_name = "[DEVEL] Lapis-chan", - port = 80, - secret = secret, - subdomains = subdomains, - body_size = body_size, - text_size = text_size, - lua_path = lua_path, - lua_cpath = lua_cpath, - postgres = { - host = "psql", - user = "postgres", - password = "", - database = "lapischan_test" - }, + site_name = "[DEVEL] Lapis-chan", + port = 80, + secret = secret, + subdomains = subdomains, + body_size = body_size, + text_size = text_size, + lua_path = lua_path, + lua_cpath = lua_cpath, + postgres = { + host = "psql", + user = "postgres", + password = "", + database = "lapischan_test", + }, }) diff --git a/misc/website/backend/app/migrations.lua b/misc/website/backend/app/migrations.lua index ec856a7..a5d43e5 100755 --- a/misc/website/backend/app/migrations.lua +++ b/misc/website/backend/app/migrations.lua @@ -21,106 +21,11 @@ return { { "time", types.integer }, { "duration", types.integer { default=259200 }}, -- 3 days }) - - schema.create_table("boards", { - { "id", types.serial { unique=true, primary_key=true }}, - { "short_name", types.varchar { unique=true }}, - { "name", types.varchar { unique=true }}, - { "subtext", types.varchar { null=true }}, - { "rules", types.text { null=true }}, - { "ban_message", types.varchar { default="USER WAS BANNED FOR THIS POST" }}, - { "anon_name", types.varchar { default="Anonymous" }}, - { "theme", types.varchar { default="yotsuba_b" }}, - { "posts", types.integer { default=0 }}, - { "pages", types.integer { default=10 }}, - { "threads_per_page", types.integer { default=10 }}, - { "text_only", types.boolean { default=false }}, - { "draw", types.boolean { default=false }}, - { "thread_file", types.boolean { default=true }}, - { "thread_comment", types.boolean { default=false }}, - { "thread_file_limit", types.integer { default=100 }}, - { "post_file", types.boolean { default=false }}, - { "post_comment", types.boolean { default=false }}, - { "post_limit", types.integer { default=250 }}, - { "archive", types.boolean { default=true }}, - { "archive_time", types.integer { default=2592000 }}, -- 30 days - { "group", types.integer { default=1 }} - }) - - schema.create_table("threads", { - { "id", types.serial { unique=true, primary_key=true }}, - { "board_id", types.integer }, - { "last_active", types.integer }, - { "sticky", types.boolean { default=false }}, - { "lock", types.boolean { default=false }}, - { "archive", types.boolean { default=false }}, - { "size_override", types.boolean { default=false }}, - { "save", types.boolean { default=false }} - }) - - schema.create_table("posts", { - { "id", types.serial { unique=true, primary_key=true }}, - { "post_id", types.integer }, - { "thread_id", types.integer }, - { "board_id", types.integer }, - { "timestamp", types.integer }, - { "ip", types.varchar }, - { "comment", types.text { null=true }}, - { "name", types.varchar { null=true }}, - { "trip", types.varchar { null=true }}, - { "subject", types.varchar { null=true }}, - { "password", types.varchar { null=true }}, - { "file_name", types.varchar { null=true }}, - { "file_path", types.varchar { null=true }}, - { "file_md5", types.varchar { null=true }}, - { "file_size", types.integer { null=true }}, - { "file_width", types.integer { null=true }}, - { "file_height", types.integer { null=true }}, - { "file_spoiler", types.boolean { null=true }}, - { "banned", types.boolean { default=false }} - }) - - schema.create_table("announcements", { - { "id", types.serial { unique=true, primary_key=true }}, - { "board_id", types.integer { null=true }}, - { "text", types.varchar } - }) - - schema.create_table("reports", { - { "id", types.serial { unique=true, primary_key=true }}, - { "board_id", types.integer }, - { "thread_id", types.integer }, - { "post_id", types.integer }, - { "timestamp", types.integer }, - { "num_reports", types.integer } - }) - - schema.create_table("pages", { - { "id", types.serial { unique=true, primary_key=true }}, - { "url", types.varchar { unique=true }}, - { "name", types.varchar }, - { "content", types.text } - }) end, [120] = function() - schema.add_column("boards", "filetype_image", types.boolean { default=true }) - schema.add_column("boards", "filetype_audio", types.boolean { default=false }) - schema.add_column("posts", "file_type", types.varchar { default="image" }) - schema.add_column("posts", "file_duration", types.varchar { null=true }) end, [200] = function() - schema.rename_column("boards", "posts", "total_posts") - schema.rename_column("boards", "name", "title") - schema.rename_column("boards", "short_name", "name") - schema.rename_column("pages", "url", "slug") - schema.rename_column("pages", "name", "title") - schema.rename_column("posts", "post_id", "post_number") - - db.query("ALTER TABLE posts ALTER COLUMN file_type DROP DEFAULT") - db.query("ALTER TABLE posts ALTER COLUMN file_type DROP NOT NULL") - schema.add_column("boards", "anon_only", types.boolean { default=false }) - local Users = require "src.models.users" local uuid = require "resty.jit-uuid" uuid.seed() @@ -140,9 +45,5 @@ return { schema.drop_column("users", "janitor") schema.drop_column("users", "mod") schema.drop_column("users", "admin") - - schema.add_column("posts", "reports", types.integer) - db.query("UPDATE posts SET reports=reports.num_reports FROM reports WHERE posts.id=reports.post_id") - schema.drop_table("reports") end } diff --git a/misc/website/backend/app/nginx.conf b/misc/website/backend/app/nginx.conf index 18767cb..2a3a88e 100755 --- a/misc/website/backend/app/nginx.conf +++ b/misc/website/backend/app/nginx.conf @@ -30,7 +30,7 @@ http { require "socket" } - resolver 127.0.0.11; + resolver 114.114.114.114; #域名解析地址 server { listen ${{PORT}}; @@ -42,7 +42,7 @@ http { } location /static/ { - alias static/; + alias ../data/static/; } location /files/ { diff --git a/misc/website/backend/app/spec/app_spec.lua b/misc/website/backend/app/spec/app_spec.lua index fdcf2c5..7acfec0 100755 --- a/misc/website/backend/app/spec/app_spec.lua +++ b/misc/website/backend/app/spec/app_spec.lua @@ -1,17 +1,16 @@ - local mock_request = require("lapis.spec.request").mock_request local app = require("app") describe("lapischan", function() - require("lapis.spec").use_test_env() + require("lapis.spec").use_test_env() - setup(function() - require("lapis.db.migrations").run_migrations(require("migrations")) - end) + setup(function() + require("lapis.db.migrations").run_migrations(require("migrations")) + end) - it("loads install page", function() - local status, body = mock_request(app, "/") - assert.same(200, status) - assert.truthy(body:find("Install Lapis-chan", 1, true)) - end) + it("loads install page", function() + local status, body = mock_request(app, "/") + assert.same(200, status) + assert.truthy(body:find("Install Lapis-chan", 1, true)) + end) end) diff --git a/misc/website/backend/app/src/apps/api.lua b/misc/website/backend/app/src/apps/api.lua index e989e68..3c31058 100755 --- a/misc/website/backend/app/src/apps/api.lua +++ b/misc/website/backend/app/src/apps/api.lua @@ -12,10 +12,7 @@ app:before_filter(capture({ on_error=handle, require "apps.api.internal.before_a app:before_filter(capture({ on_error=handle, require "apps.api.internal.before_locale" })) app:include("apps.api.core") -app:include("apps.api.announcements") app:include("apps.api.bans") -app:include("apps.api.boards") -app:include("apps.api.pages") app:include("apps.api.users") return app diff --git a/misc/website/backend/app/src/apps/api/announcements.lua b/misc/website/backend/app/src/apps/api/announcements.lua deleted file mode 100755 index 369532c..0000000 --- a/misc/website/backend/app/src/apps/api/announcements.lua +++ /dev/null @@ -1,14 +0,0 @@ -local lapis = require "lapis" -local capture = require("lapis.application").capture_errors_json -local r2 = require("lapis.application").respond_to -local handle = require("utils.error").handle -local app = lapis.Application() -app.__base = app -app.name = "api.announcements." -app.path = "/api/announcements" - -app:match("announcements", "", capture({ on_error=handle, r2(require "apps.api.announcements.announcements") })) -app:match("announcement", "/:uri_announcement[%d]", capture({ on_error=handle, r2(require "apps.api.announcements.announcement") })) -app:match("global", "/global", capture({ on_error=handle, r2(require "apps.api.announcements.global") })) - -return app diff --git a/misc/website/backend/app/src/apps/api/announcements/announcement.lua b/misc/website/backend/app/src/apps/api/announcements/announcement.lua deleted file mode 100755 index 8d2a35a..0000000 --- a/misc/website/backend/app/src/apps/api/announcements/announcement.lua +++ /dev/null @@ -1,64 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Announcements = models.announcements - -function action:GET() - - -- Get Announcement - local announcement = assert_error(Announcements:get(self.params.uri_announcement)) - Announcements:format_from_db(announcement) - - return { - status = ngx.HTTP_OK, - json = announcement - } -end - -function action:PUT() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Validate parameters - local params = { - id = self.params.uri_announcement, - board_id = tonumber(self.params.board_id), - text = self.params.text, - } - trim_filter(params) - Announcements:format_to_db(params) - assert_valid(params, Announcements.valid_record) - - -- Modify Announcement - local announcement = assert_error(Announcements:modify(params)) - Announcements:format_from_db(announcement) - - return { - status = ngx.HTTP_OK, - json = announcement - } -end - -function action:DELETE() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Delete Announcement - local announcement = assert_error(Announcements:delete(self.params.uri_announcement)) - - return { - status = ngx.HTTP_OK, - json = { - id = announcement.id, - text = announcement.text - } - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/announcements/announcements.lua b/misc/website/backend/app/src/apps/api/announcements/announcements.lua deleted file mode 100755 index 1b6c686..0000000 --- a/misc/website/backend/app/src/apps/api/announcements/announcements.lua +++ /dev/null @@ -1,51 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Announcements = models.announcements - -function action:GET() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Get all Announcements - local announcements = assert_error(Announcements:get_all()) - for _, announcement in ipairs(announcements) do - Announcements:format_from_db(announcement) - end - - return { - status = ngx.HTTP_OK, - json = announcements - } -end - -function action:POST() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Validate parameters - local params = { - board_id = tonumber(self.params.board_id), - text = self.params.text, - } - trim_filter(params) - Announcements:format_to_db(params) - assert_valid(params, Announcements.valid_record) - - -- Create Announcement - local announcement = assert_error(Announcements:new(params)) - Announcements:format_from_db(announcement) - - return { - status = ngx.HTTP_OK, - json = announcement - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/announcements/global.lua b/misc/website/backend/app/src/apps/api/announcements/global.lua deleted file mode 100755 index dde5dc6..0000000 --- a/misc/website/backend/app/src/apps/api/announcements/global.lua +++ /dev/null @@ -1,21 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local models = require "models" -local Announcements = models.announcements - -function action.GET() - - -- Get global Announcements - local announcements = assert_error(Announcements:get_global()) - for _, announcement in ipairs(announcements) do - Announcements:format_from_db(announcement) - end - - return { - status = ngx.HTTP_OK, - json = announcements - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/bans.lua b/misc/website/backend/app/src/apps/api/bans.lua deleted file mode 100755 index 082adc8..0000000 --- a/misc/website/backend/app/src/apps/api/bans.lua +++ /dev/null @@ -1,14 +0,0 @@ -local lapis = require "lapis" -local capture = require("lapis.application").capture_errors_json -local r2 = require("lapis.application").respond_to -local handle = require("utils.error").handle -local app = lapis.Application() -app.__base = app -app.name = "api.bans." -app.path = "/api/bans" - -app:match("bans", "", capture({ on_error=handle, r2(require "apps.api.bans.bans") })) -app:match("ban", "/:uri_ban[%d]", capture({ on_error=handle, r2(require "apps.api.bans.ban") })) -app:match("bans_ip", "/ip/:uri_ip", capture({ on_error=handle, r2(require "apps.api.bans.bans_ip") })) - -return app diff --git a/misc/website/backend/app/src/apps/api/bans/ban.lua b/misc/website/backend/app/src/apps/api/bans/ban.lua index fadc512..1e2272f 100755 --- a/misc/website/backend/app/src/apps/api/bans/ban.lua +++ b/misc/website/backend/app/src/apps/api/bans/ban.lua @@ -1,70 +1,70 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") +local ngx = _G.ngx +local action = setmetatable({}, require "apps.api.internal.action_base") local assert_error = require("lapis.application").assert_error local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Bans = models.bans +local trim_filter = require("lapis.util").trim_filter +local role = require "utils.role" +local models = require "models" +local Bans = models.bans function action:GET() - -- Verify the User's permissions - assert_error(role.mod(self.api_user)) + -- Verify the User's permissions + assert_error(role.mod(self.api_user)) - -- Get Ban - local ban = assert_error(Bans:get(self.params.uri_ban)) - Bans:format_from_db(ban) + -- Get Ban + local ban = assert_error(Bans:get(self.params.uri_ban)) + Bans:format_from_db(ban) - return { - status = ngx.HTTP_OK, - json = ban - } + return { + status = ngx.HTTP_OK, + json = ban, + } end function action:PUT() - -- Verify the User's permissions - assert_error(role.mod(self.api_user)) + -- Verify the User's permissions + assert_error(role.mod(self.api_user)) - -- Validate parameters - local params = { - id = self.params.uri_ban, - board_id = tonumber(self.params.board_id), - ip = self.params.ip, - reason = self.params.reason, - time = os.time(), - duration = tonumber(self.params.duration) - } - trim_filter(params) - Bans:format_to_db(params) - assert_valid(params, Bans.valid_record) + -- Validate parameters + local params = { + id = self.params.uri_ban, + board_id = tonumber(self.params.board_id), + ip = self.params.ip, + reason = self.params.reason, + time = os.time(), + duration = tonumber(self.params.duration), + } + trim_filter(params) + Bans:format_to_db(params) + assert_valid(params, Bans.valid_record) - -- Modify Ban - local ban = assert_error(Bans:modify(params)) - Bans:format_from_db(ban) + -- Modify Ban + local ban = assert_error(Bans:modify(params)) + Bans:format_from_db(ban) - return { - status = ngx.HTTP_OK, - json = ban - } + return { + status = ngx.HTTP_OK, + json = ban, + } end function action:DELETE() - -- Verify the User's permissions - assert_error(role.mod(self.api_user)) + -- Verify the User's permissions + assert_error(role.mod(self.api_user)) - -- Delete Ban - local ban = assert_error(Bans:delete(self.params.uri_ban)) + -- Delete Ban + local ban = assert_error(Bans:delete(self.params.uri_ban)) - return { - status = ngx.HTTP_OK, - json = { - id = ban.id, - ip = ban.ip, - } - } + return { + status = ngx.HTTP_OK, + json = { + id = ban.id, + ip = ban.ip, + }, + } end return action diff --git a/misc/website/backend/app/src/apps/api/bans/bans.lua b/misc/website/backend/app/src/apps/api/bans/bans.lua index e2bfe82..f9cc39d 100755 --- a/misc/website/backend/app/src/apps/api/bans/bans.lua +++ b/misc/website/backend/app/src/apps/api/bans/bans.lua @@ -1,54 +1,54 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") +local ngx = _G.ngx +local action = setmetatable({}, require "apps.api.internal.action_base") local assert_error = require("lapis.application").assert_error local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Bans = models.bans +local trim_filter = require("lapis.util").trim_filter +local role = require "utils.role" +local models = require "models" +local Bans = models.bans function action:GET() - -- Verify the User's permissions - assert_error(role.mod(self.api_user)) + -- Verify the User's permissions + assert_error(role.mod(self.api_user)) - -- Get all Bans - local bans = assert_error(Bans:get_all()) - for _, ban in ipairs(bans) do - Bans:format_from_db(ban) - end + -- Get all Bans + local bans = assert_error(Bans:get_all()) + for _, ban in ipairs(bans) do + Bans:format_from_db(ban) + end - return { - status = ngx.HTTP_OK, - json = bans - } + return { + status = ngx.HTTP_OK, + json = bans, + } end function action:POST() - -- Verify the User's permissions - assert_error(role.mod(self.api_user)) - - -- Validate parameters - local params = { - board_id = tonumber(self.params.board_id), - ip = self.params.ip, - reason = self.params.reason, - time = os.time(), - duration = tonumber(self.params.duration) - } - trim_filter(params) - Bans:format_to_db(params) - assert_valid(params, Bans.valid_record) - - -- Create Ban - local ban = assert_error(Bans:new(params)) - Bans:format_from_db(ban) - - return { - status = ngx.HTTP_OK, - json = ban - } + -- Verify the User's permissions + assert_error(role.mod(self.api_user)) + + -- Validate parameters + local params = { + board_id = tonumber(self.params.board_id), + ip = self.params.ip, + reason = self.params.reason, + time = os.time(), + duration = tonumber(self.params.duration), + } + trim_filter(params) + Bans:format_to_db(params) + assert_valid(params, Bans.valid_record) + + -- Create Ban + local ban = assert_error(Bans:new(params)) + Bans:format_from_db(ban) + + return { + status = ngx.HTTP_OK, + json = ban, + } end return action diff --git a/misc/website/backend/app/src/apps/api/bans/bans_ip.lua b/misc/website/backend/app/src/apps/api/bans/bans_ip.lua index f4ce35f..d6832c3 100755 --- a/misc/website/backend/app/src/apps/api/bans/bans_ip.lua +++ b/misc/website/backend/app/src/apps/api/bans/bans_ip.lua @@ -1,25 +1,25 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") +local ngx = _G.ngx +local action = setmetatable({}, require "apps.api.internal.action_base") local assert_error = require("lapis.application").assert_error -local role = require "utils.role" -local models = require "models" -local Bans = models.bans +local role = require "utils.role" +local models = require "models" +local Bans = models.bans function action:GET() - -- Verify the User's permissions - assert_error(role.mod(self.api_user)) + -- Verify the User's permissions + assert_error(role.mod(self.api_user)) - -- Get Bans - local bans = assert_error(Bans:get_ip(self.params.uri_ip)) - for _, ban in ipairs(bans) do - Bans:format_from_db(ban) - end + -- Get Bans + local bans = assert_error(Bans:get_ip(self.params.uri_ip)) + for _, ban in ipairs(bans) do + Bans:format_from_db(ban) + end - return { - status = ngx.HTTP_OK, - json = bans - } + return { + status = ngx.HTTP_OK, + json = bans, + } end return action diff --git a/misc/website/backend/app/src/apps/api/boards.lua b/misc/website/backend/app/src/apps/api/boards.lua deleted file mode 100755 index 3e493f0..0000000 --- a/misc/website/backend/app/src/apps/api/boards.lua +++ /dev/null @@ -1,23 +0,0 @@ -local lapis = require "lapis" -local capture = require("lapis.application").capture_errors_json -local r2 = require("lapis.application").respond_to -local handle = require("utils.error").handle -local app = lapis.Application() -app.__base = app -app.name = "api.boards." -app.path = "/api/boards" - -app:match("boards", "", capture({ on_error=handle, r2(require "apps.api.boards.boards") })) -app:match("board", "/:uri_board", capture({ on_error=handle, r2(require "apps.api.boards.board") })) -app:match("announcements", "/:uri_board/announcements", capture({ on_error=handle, r2(require "apps.api.boards.announcements") })) -app:match("bans", "/:uri_board/bans", capture({ on_error=handle, r2(require "apps.api.boards.bans") })) -app:match("reports", "/:uri_board/reports", capture({ on_error=handle, r2(require "apps.api.boards.reports") })) -app:match("threads", "/:uri_board/threads(/pages/:uri_page[%d])", capture({ on_error=handle, r2(require "apps.api.boards.threads") })) -app:match("archived", "/:uri_board/threads/archived", capture({ on_error=handle, r2(require "apps.api.boards.archived") })) -app:match("thread", "/:uri_board/threads/:uri_thread[%d]", capture({ on_error=handle, r2(require "apps.api.boards.thread") })) -app:match("thread.reports", "/:uri_board/threads/:uri_thread[%d]/reports", capture({ on_error=handle, r2(require "apps.api.boards.thread_reports") })) -app:match("posts", "/:uri_board(/threads/:uri_thread[%d])/posts", capture({ on_error=handle, r2(require "apps.api.boards.posts") })) -app:match("post", "/:uri_board/threads/:uri_thread[%d]/posts/:uri_post[%d]", capture({ on_error=handle, r2(require "apps.api.boards.post") })) -app:match("post.reports", "/:uri_board/threads/:uri_thread[%d]/posts/:uri_post[%d]/reports", capture({ on_error=handle, r2(require "apps.api.boards.post_reports") })) - -return app diff --git a/misc/website/backend/app/src/apps/api/boards/announcements.lua b/misc/website/backend/app/src/apps/api/boards/announcements.lua deleted file mode 100755 index ebeb3a3..0000000 --- a/misc/website/backend/app/src/apps/api/boards/announcements.lua +++ /dev/null @@ -1,25 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local models = require "models" -local Announcements = models.announcements -local Boards = models.boards - -function action:GET() - - -- Get Board - local board = assert_error(Boards:get(self.params.uri_board)) - - -- Get Announcements - local announcements = Announcements:get_board(board.id) - for _, announcement in ipairs(announcements) do - Announcements:format_from_db(announcement) - end - - return { - status = ngx.HTTP_OK, - json = announcements - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/archived.lua b/misc/website/backend/app/src/apps/api/boards/archived.lua deleted file mode 100755 index 965984e..0000000 --- a/misc/website/backend/app/src/apps/api/boards/archived.lua +++ /dev/null @@ -1,24 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local models = require "models" -local Boards = models.boards -local Threads = models.threads - -function action:GET() - - local board = assert_error(Boards:get(self.params.uri_name)) - - -- Get Threads - local threads = board:get_archived() - for _, thread in ipairs(threads) do - --Threads:format_from_db(thread) - end - - return { - status = ngx.HTTP_OK, - json = threads - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/bans.lua b/misc/website/backend/app/src/apps/api/boards/bans.lua deleted file mode 100755 index cd2dc92..0000000 --- a/misc/website/backend/app/src/apps/api/boards/bans.lua +++ /dev/null @@ -1,29 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local role = require "utils.role" -local models = require "models" -local Bans = models.bans -local Boards = models.boards - -function action:GET() - - -- Verify the User's permissions - assert_error(role.mod(self.api_user)) - - -- Get Board - local board = assert_error(Boards:get(self.params.uri_board)) - - -- Get Bans - local bans = Bans:get_board(board.id) - for _, ban in ipairs(bans) do - Bans:format_from_db(ban) - end - - return { - status = ngx.HTTP_OK, - json = bans - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/board.lua b/misc/website/backend/app/src/apps/api/boards/board.lua deleted file mode 100755 index 8d43c49..0000000 --- a/misc/website/backend/app/src/apps/api/boards/board.lua +++ /dev/null @@ -1,114 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local yield_error = require("lapis.application").yield_error -local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Boards = models.boards - -function action:GET() - - -- Get Board - local board = assert_error(Boards:get(self.params.uri_board)) - Boards:format_from_db(board) - - return { - status = ngx.HTTP_OK, - json = board - } -end - -function action:PUT() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Validate parameters - local params = { - name = self.params.name, - title = self.params.title, - subtext = self.params.subtext, - rules = self.params.rules, - ban_message = self.params.ban_message, - anon_name = self.params.anon_name, - theme = self.params.theme, - total_posts = tonumber(self.params.total_posts), - pages = tonumber(self.params.pages), - threads_per_page = tonumber(self.params.threads_per_page), - anon_only = self.params.anon_only, - text_only = self.params.text_only, - draw = self.params.draw, - thread_file = self.params.thread_file, - thread_comment = self.params.thread_comment, - thread_file_limit = self.params.thread_file_limit, - post_file = self.params.post_file, - post_comment = self.params.post_comment, - post_limit = tonumber(self.params.post_limit), - archive = self.params.archive, - archive_time = tonumber(self.params.archive_time), - group = self.params.group, - filetype_image = self.params.filetype_image, - filetype_audio = self.params.filetype_audio - } - trim_filter(params) - Boards:format_to_db(params) - assert_valid(params, Boards.valid_record) - - -- Check if board being modified is reusing name and title - local reuse_name = false - local reuse_title = false - - if params.name or params.title then - local board = Boards:get(self.params.uri_board) - reuse_name = board.name == params.name - reuse_title = board.title == params.title - end - - -- Verify unique or current name and title - if not (reuse_name and reuse_title) then - local boards = Boards:get_all() - for _, board in ipairs(boards) do - if not reuse_name and board.name == params.name then - yield_error("FIXME") - end - - if not reuse_title and board.title == params.title then - yield_error("FIXME") - end - end - end - - -- Modify board - local board = assert_error(Boards:modify(params, self.params.uri_board)) - Boards:format_from_db(board) - - return { - status = ngx.HTTP_OK, - json = board - } -end - -function action:DELETE() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Delete board - local board = assert_error(Boards:delete(self.params.uri_board)) - - -- TODO: delete adjacent data (announcements, bans, threads, posts, files) - -- probably best done by adding actual FK constraints with "ON DELETE CASCADE" - - return { - status = ngx.HTTP_OK, - json = { - id = board.id, - name = board.name, - title = board.title - } - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/boards.lua b/misc/website/backend/app/src/apps/api/boards/boards.lua deleted file mode 100755 index 146de1d..0000000 --- a/misc/website/backend/app/src/apps/api/boards/boards.lua +++ /dev/null @@ -1,79 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local yield_error = require("lapis.application").yield_error -local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Boards = models.boards - -function action.GET() - - -- Get Boards - local boards = assert_error(Boards:get_all()) - for _, board in ipairs(boards) do - Boards:format_from_db(board) - end - - return { - status = ngx.HTTP_OK, - json = boards - } -end - -function action:POST() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Validate parameters - local params = { - name = self.params.name, - title = self.params.title, - subtext = self.params.subtext, - rules = self.params.rules, - ban_message = self.params.ban_message, - anon_name = self.params.anon_name, - theme = self.params.theme, - total_posts = tonumber(self.params.total_posts), - pages = tonumber(self.params.pages), - threads_per_page = tonumber(self.params.threads_per_page), - anon_only = self.params.anon_only, - text_only = self.params.text_only, - draw = self.params.draw, - thread_file = self.params.thread_file, - thread_comment = self.params.thread_comment, - thread_file_limit = self.params.thread_file_limit, - post_file = self.params.post_file, - post_comment = self.params.post_comment, - post_limit = tonumber(self.params.post_limit), - archive = self.params.archive, - archive_time = tonumber(self.params.archive_time), - group = self.params.group, - filetype_image = self.params.filetype_image, - filetype_audio = self.params.filetype_audio - } - trim_filter(params) - Boards:format_to_db(params) - assert_valid(params, Boards.valid_record) - - -- Verify unique name and title - local boards = Boards:get_all() - for _, board in ipairs(boards) do - if board.name == params.name or board.title == params.title then - yield_error("FIXME") - end - end - - -- Create board - local board = assert_error(Boards:new(params)) - Boards:format_from_db(board) - - return { - status = ngx.HTTP_OK, - json = board - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/post.lua b/misc/website/backend/app/src/apps/api/boards/post.lua deleted file mode 100755 index bc4967c..0000000 --- a/misc/website/backend/app/src/apps/api/boards/post.lua +++ /dev/null @@ -1,92 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local models = require "models" -local Boards = models.boards -local Threads = models.threads -local Posts = models.posts - -function action:GET() - - local board = assert_error(Boards:get(self.params.uri_name)) - - -- Get Post - local post = assert_error(Posts:get(board.id, self.params.uri_id)) - --Posts:format_from_db(post) - - return { - status = ngx.HTTP_OK, - json = post - } -end - -function action:PUT() - - -- Validate parameters - local params = { - comment = self.params.comment, - subject = self.params.subject, - file_spoiler = self.params.file_spoiler - } - Posts:format_to_db(params) - trim_filter(params) - assert_valid(params, Posts.valid_record) - - -- Modify post - local post = assert_error(Posts:modify(params)) - Posts:format_from_db(post) - - return { - status = ngx.HTTP_OK, - json = post - } -end - -function action:DELETE() - - --[[ FIXME: needs proper auth! - -- MODS = FAGS - if type(session) == "table" and - (session.admin or session.mod or session.janitor) then - rm_post(board.name) - success = true - -- Override password - elseif type(session) == "string" and - session == "override" then - rm_post(board.name) - success = true - -- Password has to match! - elseif post and session.password and - post.password == session.password then - rm_post(board.name) - success = true - end - --]] - - -- Delete post - local post = assert_error(Posts:get_post_by_id(self.params.uri_id)) - local thread = post:get_thread() - local op = thread:get_op() - - if post.id == op.id then - assert_error(Threads:delete(thread.id)) - - local posts = thread:get_posts() - for _, p in ipairs(posts) do - assert_error(Posts:delete(p.id)) - end - else - assert_error(Posts:delete(post.id)) - end - - return { - status = ngx.HTTP_OK, - json = { - id = post.id - } - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/post_reports.lua b/misc/website/backend/app/src/apps/api/boards/post_reports.lua deleted file mode 100755 index 8213a3f..0000000 --- a/misc/website/backend/app/src/apps/api/boards/post_reports.lua +++ /dev/null @@ -1,21 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local models = require "models" -local Boards = models.boards - -function action:GET() - - -- Get Board - local board = assert_error(Boards:get(self.params.uri_name)) - - -- Get Reports - local reports = board:get_reports() - - return { - status = ngx.HTTP_OK, - json = reports - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/posts.lua b/misc/website/backend/app/src/apps/api/boards/posts.lua deleted file mode 100755 index 6e7e375..0000000 --- a/misc/website/backend/app/src/apps/api/boards/posts.lua +++ /dev/null @@ -1,105 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local models = require "models" -local Boards = models.boards -local Threads = models.threads -local Posts = models.posts - -function action:GET() - - local posts - - if self.params.uri_thread then - local thread = assert_error(Threads:get(self.params.uri_thread)) - posts = thread:get_posts() - else - local board = assert_error(Boards:get(self.params.uri_board)) - posts = board:get_posts() - end - - return { - status = ngx.HTTP_OK, - json = posts - } -end - -function action:POST() - local now = os.time() - local board = assert_error(Boards:get(self.params.uri_name)) - local thread = Threads:get(self.params.uri_id) - local op = thread and false or true - - -- Create a new thread if no thread exists - if not thread then - -- Validate parameters - local params = { - board_id = board.id, - last_active = now, - sticky = self.params.sticky, - lock = self.params.lock, - size_override = self.params.size_override, - save = self.params.save - } - - -- Only admins and mods can flag threads - -- FIXME: API has no session, need proper auth! - if not self.session.admin or self.session.mod then - params.sticky = nil - params.lock = nil - params.size_override = nil - params.save = nil - end - - --Threads:format_to_db(params) - trim_filter(params) - assert_valid(params, Threads.valid_record) - - -- Create thread - thread = assert_error(Threads:new(params)) - --Threads:format_from_db(thread) - end - - -- FIXME: there needs to be a better way to do this to avoid race conditions... - board.total_posts = board.total_posts + 1 - - -- Validate parameters - local params = { - post_id = board.total_posts, - thread_id = thread.id, - board_id = board.id, - timestamp = now, - ip = self.params.ip, - comment = self.params.comment, - name = self.params.name, - trip = self.params.trip, - subject = self.params.subject, - password = self.params.password, - file_name = self.params.file_name, - file_path = self.params.file_path, - file_type = self.params.file_type, - file_md5 = self.params.file_md5, - file_size = self.params.file_size, - file_width = self.params.file_width, - file_height = self.params.file_height, - file_duration = self.params.file_duration, - file_spoiler = self.params.file_spoiler, - file_content = self.params.file_content -- FIXME: we probably want to base64 decode this in format_to_db - } - Posts:format_to_db(params) - trim_filter(params) - assert_valid(params, Posts.valid_record) - - -- Create post - local post = assert_error(Posts:new(params, board, op)) - Posts:format_from_db(post) - - return { - status = ngx.HTTP_OK, - json = post - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/reports.lua b/misc/website/backend/app/src/apps/api/boards/reports.lua deleted file mode 100755 index 0ccfc08..0000000 --- a/misc/website/backend/app/src/apps/api/boards/reports.lua +++ /dev/null @@ -1,29 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local role = require "utils.role" -local models = require "models" -local Boards = models.boards -local Posts = models.posts - -function action:GET() - - -- Verify the User's permissions - assert_error(role.mod(self.api_user)) - - -- Get Board - local board = assert_error(Boards:get(self.params.uri_board)) - - -- Get Reported posts - local posts = Posts:get_board_reports(board.id) - for _, post in ipairs(posts) do - Posts:format_from_db(post) - end - - return { - status = ngx.HTTP_OK, - json = posts - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/thread.lua b/misc/website/backend/app/src/apps/api/boards/thread.lua deleted file mode 100755 index ddbedc8..0000000 --- a/misc/website/backend/app/src/apps/api/boards/thread.lua +++ /dev/null @@ -1,69 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local models = require "models" -local Threads = models.threads - -function action:GET() - - -- Get Thread - local thread = assert_error(Threads:get(self.params.uri_id)) - --Threads:format_from_db(thread) - - return { - status = ngx.HTTP_OK, - json = thread - } -end - -function action:PUT() - - local params = { id=self.params.uri_id } - - -- Extract flag from URI - if self.params.uri_value:lower() == "true" or - self.params.uri_value:lower() == "t" or - tonumber(self.params.uri_value) == 1 then - self.params.uri_value = true - elseif self.params.uri_value:lower() == "false" or - self.params.uri_value:lower() == "f" or - tonumber(self.params.uri_value) == 0 then - self.params.uri_value = false - else - return { - status = ngx.HTTP_BAD_REQUEST, - json = {} - } - end - - -- Extract variable from URI - if self.params.uri_action == "sticky" then - params.sticky = self.params.uri_value - elseif self.params.uri_action == "lock" then - params.lock = self.params.uri_value - elseif self.params.uri_action == "save" then - params.save = self.params.uri_value - elseif self.params.uri_action == "size_override" then - params.size_override = self.params.uri_value - else - return { - status = ngx.HTTP_BAD_REQUEST, - json = {} - } - end - - -- Modify thread - local thread = assert_error(Threads:modify(params)) - --Threads:format_from_db(thread) - - return { - status = ngx.HTTP_OK, - json = thread - } -end - -function action:DELETE() - -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/thread_reports.lua b/misc/website/backend/app/src/apps/api/boards/thread_reports.lua deleted file mode 100755 index 8213a3f..0000000 --- a/misc/website/backend/app/src/apps/api/boards/thread_reports.lua +++ /dev/null @@ -1,21 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local models = require "models" -local Boards = models.boards - -function action:GET() - - -- Get Board - local board = assert_error(Boards:get(self.params.uri_name)) - - -- Get Reports - local reports = board:get_reports() - - return { - status = ngx.HTTP_OK, - json = reports - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/boards/threads.lua b/misc/website/backend/app/src/apps/api/boards/threads.lua deleted file mode 100755 index 6ed4530..0000000 --- a/misc/website/backend/app/src/apps/api/boards/threads.lua +++ /dev/null @@ -1,35 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local models = require "models" -local Boards = models.boards -local Threads = models.threads - -function action:GET() - - local threads, pages - local board = assert_error(Boards:get(self.params.uri_name)) - - -- Get Threads - if self.params.uri_page then - local paginator = board:get_threads_paginated({ per_page=board.threads_per_page }) - threads = paginator:get_page(self.params.uri_page) - pages = paginator:num_pages() - else - threads = board:get_threads() - end - - for _, thread in ipairs(threads) do - --Threads:format_from_db(thread) - end - - return { - status = ngx.HTTP_OK, - json = { - threads = threads, - pages = pages - } - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/core.lua b/misc/website/backend/app/src/apps/api/core.lua index f005582..6a1d112 100755 --- a/misc/website/backend/app/src/apps/api/core.lua +++ b/misc/website/backend/app/src/apps/api/core.lua @@ -1,13 +1,19 @@ -local lapis = require "lapis" +local lapis = require "lapis" local capture = require("lapis.application").capture_errors_json -local r2 = require("lapis.application").respond_to -local handle = require("utils.error").handle -local app = lapis.Application() -app.__base = app -app.name = "api.core." -app.path = "/api" +local r2 = require("lapis.application").respond_to +local handle = require("utils.error").handle +local app = lapis.Application() +app.__base = app +app.name = "api.core." +app.path = "/api" -app:match("root", "", capture({ on_error=handle, r2(require "apps.api.core.root") })) -app:match("login", "/login", capture({ on_error=handle, r2(require "apps.api.core.login") })) +app:match("root", "", capture({ + on_error = handle, + r2(require "apps.api.core.root"), +})) +app:match("login", "/login", capture({ + on_error = handle, + r2(require "apps.api.core.login"), +})) return app diff --git a/misc/website/backend/app/src/apps/api/core/login.lua b/misc/website/backend/app/src/apps/api/core/login.lua index f39238d..4c3403b 100755 --- a/misc/website/backend/app/src/apps/api/core/login.lua +++ b/misc/website/backend/app/src/apps/api/core/login.lua @@ -1,35 +1,35 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") +local ngx = _G.ngx +local action = setmetatable({}, require "apps.api.internal.action_base") local assert_error = require("lapis.application").assert_error -local yield_error = require("lapis.application").yield_error -local models = require "models" -local Users = models.users +local yield_error = require("lapis.application").yield_error +local models = require "models" +local Users = models.users function action:POST() - -- Normally we'd process these inputs a bit but in the case of - -- authentication credentials, we want to use the raw user inputs. - local params = { - username = self.params.username, - password = self.params.password - } + -- Normally we'd process these inputs a bit but in the case of + -- authentication credentials, we want to use the raw user inputs. + local params = { + username = self.params.username, + password = self.params.password, + } - -- Early exit if credentials not sent - if not params.username or not params.password then - yield_error("FIXME") - end + -- Early exit if credentials not sent + if not params.username or not params.password then + yield_error("FIXME") + end - local user = assert_error(Users:login(params)) + local user = assert_error(Users:login(params)) - return { - status = ngx.HTTP_OK, - json = { - id = user.id, - username = user.username, - role = user.role, - api_key = user.api_key - } - } + return { + status = ngx.HTTP_OK, + json = { + id = user.id, + username = user.username, + role = user.role, + api_key = user.api_key, + }, + } end return action diff --git a/misc/website/backend/app/src/apps/api/core/root.lua b/misc/website/backend/app/src/apps/api/core/root.lua index aabefc0..6b35b2b 100755 --- a/misc/website/backend/app/src/apps/api/core/root.lua +++ b/misc/website/backend/app/src/apps/api/core/root.lua @@ -1,11 +1,11 @@ -local ngx = _G.ngx +local ngx = _G.ngx local action = setmetatable({}, require "apps.api.internal.action_base") function action.GET() - return { - status = ngx.HTTP_OK, - json = {} - } + return { + status = ngx.HTTP_OK, + json = {}, + } end return action diff --git a/misc/website/backend/app/src/apps/api/internal/action_base.lua b/misc/website/backend/app/src/apps/api/internal/action_base.lua index 06adb3c..1e03649 100755 --- a/misc/website/backend/app/src/apps/api/internal/action_base.lua +++ b/misc/website/backend/app/src/apps/api/internal/action_base.lua @@ -1,17 +1,17 @@ -local ngx = _G.ngx +local ngx = _G.ngx local action = {} local function errors() - return { - status = ngx.HTTP_NOT_ALLOWED, - json = {} - } + return { + status = ngx.HTTP_NOT_ALLOWED, + json = {}, + } end action.__index = action -action.GET = errors -action.POST = errors -action.PUT = errors -action.DELETE = errors +action.GET = errors +action.POST = errors +action.PUT = errors +action.DELETE = errors return action diff --git a/misc/website/backend/app/src/apps/api/internal/before_auth.lua b/misc/website/backend/app/src/apps/api/internal/before_auth.lua index 0aaee28..ac591b3 100755 --- a/misc/website/backend/app/src/apps/api/internal/before_auth.lua +++ b/misc/website/backend/app/src/apps/api/internal/before_auth.lua @@ -1,41 +1,41 @@ local assert_error = require("lapis.application").assert_error -local yield_error = require("lapis.application").yield_error -local mime = require "mime" -local models = require "models" -local Users = models.users +local yield_error = require("lapis.application").yield_error +local mime = require "mime" +local models = require "models" +local Users = models.users return function(self) - if self.req.headers["Authorization"] then - - -- Decode auth info - local auth = mime.unb64(self.req.headers["Authorization"]:sub(7)) - local username, api_key = auth:match("^(.+)%:(.+)$") - - -- DENY if Authorization is malformed - if not username or not api_key then - yield_error("FIXME: Corrupt auth!") - end - - -- DENY if a user's key isn't properly set - if api_key == Users.default_key then - yield_error("FIXME: Bad auth!") - end - - local params = { - username = username, - api_key = api_key - } - - -- Get User - self.api_user = assert_error(Users:get_api(params)) - Users:format_from_db(self.api_user) - return - end - - -- Set basic User - self.api_user = { - id = -1, - role = -1 - } + if self.req.headers["Authorization"] then + + -- Decode auth info + local auth = mime.unb64(self.req.headers["Authorization"]:sub(7)) + local username, api_key = auth:match("^(.+)%:(.+)$") + + -- DENY if Authorization is malformed + if not username or not api_key then + yield_error("FIXME: Corrupt auth!") + end + + -- DENY if a user's key isn't properly set + if api_key == Users.default_key then + yield_error("FIXME: Bad auth!") + end + + local params = { + username = username, + api_key = api_key, + } + + -- Get User + self.api_user = assert_error(Users:get_api(params)) + Users:format_from_db(self.api_user) + return + end + + -- Set basic User + self.api_user = { + id = -1, + role = -1, + } end diff --git a/misc/website/backend/app/src/apps/api/internal/before_locale.lua b/misc/website/backend/app/src/apps/api/internal/before_locale.lua index 8a14fb3..d7641dd 100755 --- a/misc/website/backend/app/src/apps/api/internal/before_locale.lua +++ b/misc/website/backend/app/src/apps/api/internal/before_locale.lua @@ -1,20 +1,20 @@ local i18n = require "i18n" -local lfs = require "lfs" +local lfs = require "lfs" return function(self) - -- Set locale - self.i18n = i18n - local locale = self.req.headers["Content-Language"] or "en" - i18n.setLocale(locale) - i18n.loadFile("src/locale/en.lua") + -- Set locale + self.i18n = i18n + local locale = self.req.headers["Content-Language"] or "en" + i18n.setLocale(locale) + i18n.loadFile("src/locale/en.lua") - -- Get locale file - local path = "src/locale" - for file in lfs.dir(path) do - local name, ext = string.match(file, "^(.+)%.(.+)$") - if name == locale and ext == "lua" then - i18n.loadFile(string.format("%s/%s.lua", path, name)) - end - end + -- Get locale file + local path = "src/locale" + for file in lfs.dir(path) do + local name, ext = string.match(file, "^(.+)%.(.+)$") + if name == locale and ext == "lua" then + i18n.loadFile(string.format("%s/%s.lua", path, name)) + end + end end diff --git a/misc/website/backend/app/src/apps/api/pages.lua b/misc/website/backend/app/src/apps/api/pages.lua deleted file mode 100755 index 86a02f4..0000000 --- a/misc/website/backend/app/src/apps/api/pages.lua +++ /dev/null @@ -1,13 +0,0 @@ -local lapis = require "lapis" -local capture = require("lapis.application").capture_errors_json -local r2 = require("lapis.application").respond_to -local handle = require("utils.error").handle -local app = lapis.Application() -app.__base = app -app.name = "api.pages." -app.path = "/api/pages" - -app:match("pages", "", capture({ on_error=handle, r2(require "apps.api.pages.pages") })) -app:match("page", "/:uri_page", capture({ on_error=handle, r2(require "apps.api.pages.page") })) - -return app diff --git a/misc/website/backend/app/src/apps/api/pages/page.lua b/misc/website/backend/app/src/apps/api/pages/page.lua deleted file mode 100755 index 694f7bc..0000000 --- a/misc/website/backend/app/src/apps/api/pages/page.lua +++ /dev/null @@ -1,60 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Pages = models.pages - -function action:GET() - local page = assert_error(Pages:get(self.params.uri_page)) - - return { - status = ngx.HTTP_OK, - json = page - } -end - -function action:PUT() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Validate parameters - local params = { - slug = self.params.slug, - title = self.params.title, - content = self.params.content - } - trim_filter(params) - Pages:format_to_db(params) - assert_valid(params, Pages.valid_record) - - -- Modify page - local page = assert_error(Pages:modify(params, self.params.uri_page)) - - return { - status = ngx.HTTP_OK, - json = page - } -end - -function action:DELETE() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Delete Page - local page = assert_error(Pages:delete(self.params.uri_page)) - - return { - status = ngx.HTTP_OK, - json = { - slug = page.slug, - title = page.title - } - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/pages/pages.lua b/misc/website/backend/app/src/apps/api/pages/pages.lua deleted file mode 100755 index 92d968b..0000000 --- a/misc/website/backend/app/src/apps/api/pages/pages.lua +++ /dev/null @@ -1,45 +0,0 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Pages = models.pages - -function action.GET() - - -- Get all Pages - local pages = assert_error(Pages:get_all()) - - return { - status = ngx.HTTP_OK, - json = pages - } -end - -function action:POST() - - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) - - -- Validate parameters - local params = { - slug = self.params.slug, - title = self.params.title, - content = self.params.content, - } - trim_filter(params) - Pages:format_to_db(params) - assert_valid(params, Pages.valid_record) - - -- Create Page - local page = assert_error(Pages:new(params)) - - return { - status = ngx.HTTP_OK, - json = page - } -end - -return action diff --git a/misc/website/backend/app/src/apps/api/users.lua b/misc/website/backend/app/src/apps/api/users.lua index cf5c86d..22285d0 100755 --- a/misc/website/backend/app/src/apps/api/users.lua +++ b/misc/website/backend/app/src/apps/api/users.lua @@ -1,13 +1,19 @@ -local lapis = require "lapis" +local lapis = require "lapis" local capture = require("lapis.application").capture_errors_json -local r2 = require("lapis.application").respond_to -local handle = require("utils.error").handle -local app = lapis.Application() -app.__base = app -app.name = "api.users." -app.path = "/api/users" +local r2 = require("lapis.application").respond_to +local handle = require("utils.error").handle +local app = lapis.Application() +app.__base = app +app.name = "api.users." +app.path = "/api/users" -app:match("users", "", capture({ on_error=handle, r2(require "apps.api.users.users") })) -app:match("user", "/:uri_user", capture({ on_error=handle, r2(require "apps.api.users.user") })) +app:match("users", "", capture({ + on_error = handle, + r2(require "apps.api.users.users"), +})) +app:match("user", "/:uri_user", capture({ + on_error = handle, + r2(require "apps.api.users.user"), +})) return app diff --git a/misc/website/backend/app/src/apps/api/users/user.lua b/misc/website/backend/app/src/apps/api/users/user.lua index fd31fba..abbfe4b 100755 --- a/misc/website/backend/app/src/apps/api/users/user.lua +++ b/misc/website/backend/app/src/apps/api/users/user.lua @@ -1,108 +1,108 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") +local ngx = _G.ngx +local action = setmetatable({}, require "apps.api.internal.action_base") local assert_error = require("lapis.application").assert_error -local yield_error = require("lapis.application").yield_error +local yield_error = require("lapis.application").yield_error local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Users = models.users +local trim_filter = require("lapis.util").trim_filter +local role = require "utils.role" +local models = require "models" +local Users = models.users function action:GET() - local user = assert_error(Users:get(self.params.uri_user)) - Users:format_from_db(user) + local user = assert_error(Users:get(self.params.uri_user)) + Users:format_from_db(user) - -- Verify the User's permissions - local is_admin = role.admin(self.api_user) - local is_user = self.api_user.id == user.id - if not is_admin and not is_user then - yield_error("FIXME") - end + -- Verify the User's permissions + local is_admin = role.admin(self.api_user) + local is_user = self.api_user.id == user.id + if not is_admin and not is_user then + yield_error("FIXME") + end - return { - status = ngx.HTTP_OK, - json = user - } + return { + status = ngx.HTTP_OK, + json = user, + } end function action:PUT() - local user = assert_error(Users:get(self.params.uri_user)) - - -- Verify the User's permissions - local is_admin = role.admin(self.api_user) - local is_user = self.api_user.id == user.id - local is_auth = self.api_user.role > user.role - if (not is_admin and not is_user) or not is_auth then - yield_error("FIXME") - end - - -- Validate parameters - local params = { - username = self.params.username, - password = self.params.password, - confirm = self.params.confirm, - role = tonumber(self.params.role), - api_key = self.params.api_key - } - trim_filter(params) - Users:format_to_db(params) - assert_valid(params, Users.valid_record) - - -- If no role was sent, don't update it - -- This is kind of dumb since we're just setting it from nil to -1 and back - -- to nil, but I want to keep the format_to_db in case of future formatting - -- concerns. - if params.role == Users.role.INVALID then - params.role = nil - end - - if params.role then - - -- Only admins can change a role - if not is_admin then - yield_error("FIXME") - end - - -- Cannot elevate to or above own role - if self.api_user.role <= params.role then - yield_error("FIXME") - end - end - - -- Modify User - user = assert_error(Users:modify(params, self.params.uri_user, self.params.password)) - Users:format_from_db(user) - - return { - status = ngx.HTTP_OK, - json = user - } + local user = assert_error(Users:get(self.params.uri_user)) + + -- Verify the User's permissions + local is_admin = role.admin(self.api_user) + local is_user = self.api_user.id == user.id + local is_auth = self.api_user.role > user.role + if (not is_admin and not is_user) or not is_auth then + yield_error("FIXME") + end + + -- Validate parameters + local params = { + username = self.params.username, + password = self.params.password, + confirm = self.params.confirm, + role = tonumber(self.params.role), + api_key = self.params.api_key, + } + trim_filter(params) + Users:format_to_db(params) + assert_valid(params, Users.valid_record) + + -- If no role was sent, don't update it + -- This is kind of dumb since we're just setting it from nil to -1 and back + -- to nil, but I want to keep the format_to_db in case of future formatting + -- concerns. + if params.role == Users.role.INVALID then + params.role = nil + end + + if params.role then + + -- Only admins can change a role + if not is_admin then + yield_error("FIXME") + end + + -- Cannot elevate to or above own role + if self.api_user.role <= params.role then + yield_error("FIXME") + end + end + + -- Modify User + user = assert_error(Users:modify(params, self.params.uri_user, self.params.password)) + Users:format_from_db(user) + + return { + status = ngx.HTTP_OK, + json = user, + } end function action:DELETE() - local user = assert_error(Users:get(self.params.uri_user)) - - -- Verify the User's permissions - local is_admin = role.admin(self.api_user) - local is_user = self.api_user.id == user.id - local is_auth = self.api_user.role > user.role - if not is_admin and not is_user and not is_auth then - yield_error("FIXME") - end - - -- Delete User - user = assert_error(Users:delete(self.params.uri_user)) - - return { - status = ngx.HTTP_OK, - json = { - id = user.id, - username = user.username - } - } + local user = assert_error(Users:get(self.params.uri_user)) + + -- Verify the User's permissions + local is_admin = role.admin(self.api_user) + local is_user = self.api_user.id == user.id + local is_auth = self.api_user.role > user.role + if not is_admin and not is_user and not is_auth then + yield_error("FIXME") + end + + -- Delete User + user = assert_error(Users:delete(self.params.uri_user)) + + return { + status = ngx.HTTP_OK, + json = { + id = user.id, + username = user.username, + }, + } end return action diff --git a/misc/website/backend/app/src/apps/api/users/users.lua b/misc/website/backend/app/src/apps/api/users/users.lua index fbb663e..51cf240 100755 --- a/misc/website/backend/app/src/apps/api/users/users.lua +++ b/misc/website/backend/app/src/apps/api/users/users.lua @@ -1,64 +1,64 @@ -local ngx = _G.ngx -local action = setmetatable({}, require "apps.api.internal.action_base") +local ngx = _G.ngx +local action = setmetatable({}, require "apps.api.internal.action_base") local assert_error = require("lapis.application").assert_error -local yield_error = require("lapis.application").yield_error +local yield_error = require("lapis.application").yield_error local assert_valid = require("lapis.validate").assert_valid -local trim_filter = require("lapis.util").trim_filter -local role = require "utils.role" -local models = require "models" -local Users = models.users +local trim_filter = require("lapis.util").trim_filter +local role = require "utils.role" +local models = require "models" +local Users = models.users function action:GET() - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) + -- Verify the User's permissions + assert_error(role.admin(self.api_user)) - -- Get all Users - local users = assert_error(Users:get_all()) - for _, user in ipairs(users) do - Users:format_from_db(user) - end + -- Get all Users + local users = assert_error(Users:get_all()) + for _, user in ipairs(users) do + Users:format_from_db(user) + end - return { - status = ngx.HTTP_OK, - json = users - } + return { + status = ngx.HTTP_OK, + json = users, + } end function action:POST() - -- Verify the User's permissions - assert_error(role.admin(self.api_user)) + -- Verify the User's permissions + assert_error(role.admin(self.api_user)) - -- Validate parameters - local params = { - username = self.params.username, - password = self.params.password, - confirm = self.params.confirm, - role = tonumber(self.params.role) - } - trim_filter(params) - Users:format_to_db(params) - assert_valid(params, Users.valid_record) + -- Validate parameters + local params = { + username = self.params.username, + password = self.params.password, + confirm = self.params.confirm, + role = tonumber(self.params.role), + } + trim_filter(params) + Users:format_to_db(params) + assert_valid(params, Users.valid_record) - -- DENY if no role was sent - if params.role == Users.role.INVALID then - yield_error("FIXME") - end + -- DENY if no role was sent + if params.role == Users.role.INVALID then + yield_error("FIXME") + end - -- Cannot elevate to or above own role - if self.api_user.role <= params.role then - yield_error("FIXME") - end + -- Cannot elevate to or above own role + if self.api_user.role <= params.role then + yield_error("FIXME") + end - -- Create user - local user = assert_error(Users:new(params, self.params.password)) - Users:format_from_db(user) + -- Create user + local user = assert_error(Users:new(params, self.params.password)) + Users:format_from_db(user) - return { - status = ngx.HTTP_OK, - json = user - } + return { + status = ngx.HTTP_OK, + json = user, + } end return action diff --git a/misc/website/backend/app/src/apps/web.lua b/misc/website/backend/app/src/apps/web.lua deleted file mode 100755 index 6acb1ef..0000000 --- a/misc/website/backend/app/src/apps/web.lua +++ /dev/null @@ -1,16 +0,0 @@ -local lapis = require "lapis" -local app = lapis.Application() -app.__base = app -app.include = function(self, a) - self.__class.include(self, a, nil, self) -end - -app:before_filter(require "apps.web.internal.config_site") -app:before_filter(require "apps.web.internal.check_auth") -app:before_filter(require "apps.web.internal.check_ban") - -app:include("apps.web.admin") -app:include("apps.web.pages") -app:include("apps.web.boards") - -return app diff --git a/misc/website/backend/app/src/apps/web/admin.lua b/misc/website/backend/app/src/apps/web/admin.lua deleted file mode 100755 index f37e73b..0000000 --- a/misc/website/backend/app/src/apps/web/admin.lua +++ /dev/null @@ -1,16 +0,0 @@ -local lapis = require "lapis" -local r2 = require("lapis.application").respond_to -local app = lapis.Application() -app.__base = app -app.name = "web.admin." -app.path = "/admin" -app.handle_404 = require "apps.web.internal.code_404" - -app:match("index", "", r2(require "apps.web.admin.index")) -app:match("users", "/:action/user(/:user)", r2(require "apps.web.admin.user")) -app:match("boards", "/:action/board(/:uri_name)", r2(require "apps.web.admin.board")) -app:match("announcements", "/:action/announcement(/:ann)", r2(require "apps.web.admin.announcement")) -app:match("pages", "/:action/page(/:page)", r2(require "apps.web.admin.page")) -app:match("reports", "/:action/report(/:report)", r2(require "apps.web.admin.report")) - -return app diff --git a/misc/website/backend/app/src/apps/web/admin/announcement.lua b/misc/website/backend/app/src/apps/web/admin/announcement.lua deleted file mode 100755 index 7c3bb25..0000000 --- a/misc/website/backend/app/src/apps/web/admin/announcement.lua +++ /dev/null @@ -1,128 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local csrf = require "lapis.csrf" -local capture = require "utils.capture" -local generate = require "utils.generate" - -return { - before = function(self) - -- Get announcements - self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcements"))) - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - -- Generate CSRF token - self.csrf_token = csrf.generate_token(self) - - -- Page title - self.page_title = self.i18n("admin_panel") - - -- Verify Authorization - if self.session.name then - if not self.session.admin then - assert_error(false, "err_not_admin") - end - else - return - end - - -- Display creation form - if self.params.action == "create" then - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("create_ann") - ) - self.announcement = self.params - return - end - - -- Display modification form - if self.params.action == "modify" then - self.announcement = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id=self.params.uri_id }))) - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("modify_ann") - ) - return - end - - -- Delete announcement - if self.params.action == "delete" then - self.announcement = assert_error(capture.delete(self:url_for("api.announcements.announcement", { uri_id=self.params.uri_id }))) - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("deleted_ann", { self.announcement.text }) - return - end - end, - - on_error = function(self) - self.errors = generate.errors(self.i18n, self.errors) - - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "create" then - return { render = "admin.announcement" } - elseif self.params.action == "modify" then - return { render = "admin.announcement" } - elseif self.params.action == "delete" then - return { render = "admin.admin" } - end - end, - - GET = function(self) - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "create" then - return { render = "admin.announcement" } - elseif self.params.action == "modify" then - return { render = "admin.announcement" } - elseif self.params.action == "delete" then - return { render = "admin.success" } - end - end, - - POST = function(self) - -- Validate CSRF token - csrf.assert_token(self) - - -- Validate user input - assert_valid(self.params, { - { "text", max_length=255, exists=true } - }) - - -- Create announcement - if self.params.create_announcement then - self.announcement = assert_error(capture.post(self:url_for("api.announcements.announcements"), self.params)) - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("created_ann", { self.announcement.text }) - - return { render = "admin.success" } - end - - -- Modify announcement - if self.params.modify_announcement then - self.announcement = assert_error(capture.put(self:url_for("api.announcements.announcement", { uri_id=self.params.uri_id }), self.params)) - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("modified_ann", { self.announcement.text }) - - return { render = "admin.success" } - end - - return { redirect_to = self:url_for("web.admin.index") } - end -} diff --git a/misc/website/backend/app/src/apps/web/admin/board.lua b/misc/website/backend/app/src/apps/web/admin/board.lua deleted file mode 100755 index fe05b61..0000000 --- a/misc/website/backend/app/src/apps/web/admin/board.lua +++ /dev/null @@ -1,136 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local csrf = require "lapis.csrf" -local lfs = require "lfs" -local capture = require "utils.capture" -local generate = require "utils.generate" - -return { - before = function(self) - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - -- Generate CSRF token - self.csrf_token = csrf.generate_token(self) - - -- Page title - self.page_title = self.i18n("admin_panel") - - -- Verify Authorization - if not self.session.name then return end - if not self.session.admin then - assert_error(false, "err_not_admin") - end - - -- Get list of themes - self.themes = {} - for file in lfs.dir("./static/css") do - local name, ext = string.match(file, "^(.+)%.(.+)$") - if name ~= "reset" and - name ~= "posts" and - name ~= "style" and - name ~= "tegaki" and - ext == "css" then - table.insert(self.themes, name) - end - end - - -- Display creation form - if self.params.action == "create" then - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("create_board") - ) - self.board = self.params - - if not self.board.theme then - self.board.theme = "yotsuba_b" - end - - return - end - - -- Display modification form - if self.params.action == "modify" then - self.board = assert_error(capture.get(self:url_for("api.boards.board", { uri_name=self.params.uri_name }))) - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("modify_board") - ) - return - end - - -- Delete board - if self.params.action == "delete" then - local board = assert_error(capture.delete(self:url_for("api.boards.board", { uri_name=self.params.uri_name }))) - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("deleted_board", { board.name, board.title }) - return - end - end, - - on_error = function(self) - self.errors = generate.errors(self.i18n, self.errors) - - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "create" then - return { render = "admin.board" } - elseif self.params.action == "modify" then - return { render = "admin.board" } - elseif self.params.action == "delete" then - return { render = "admin.admin" } - end - end, - - GET = function(self) - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "create" then - return { render = "admin.board" } - elseif self.params.action == "modify" then - return { render = "admin.board" } - elseif self.params.action == "delete" then - return { render = "admin.success" } - end - end, - - POST = function(self) - -- Validate CSRF token - csrf.assert_token(self) - - -- Create new board - if self.params.create_board then - local board = assert_error(capture.post(self:url_for("api.boards.boards"), self.params)) - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("created_board", { board.name, board.title }) - - return { render = "admin.success" } - end - - -- Modify board - if self.params.modify_board then - local board = assert_error(capture.put(self:url_for("api.boards.board", { uri_name=self.params.uri_name }), self.params)) - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("modified_board", { board.name, board.title }) - - return { render = "admin.success" } - end - - return { redirect_to = self:url_for("web.admin.index") } - end -} diff --git a/misc/website/backend/app/src/apps/web/admin/index.lua b/misc/website/backend/app/src/apps/web/admin/index.lua deleted file mode 100755 index df2b31d..0000000 --- a/misc/website/backend/app/src/apps/web/admin/index.lua +++ /dev/null @@ -1,138 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local csrf = require "lapis.csrf" -local capture = require "utils.capture" -local generate = require "utils.generate" -local Boards = require "models.boards" -local Pages = require "models.pages" -local Posts = require "models.posts" -local Reports = require "models.reports" -local Users = require "models.users" - -return { - before = function(self) - -- Get data - self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcements"))) - self.pages = Pages:get_pages() - self.reports = Reports:get_reports() - self.users = Users:get_users() - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - -- Page title - self.page_title = self.i18n("admin_panel") - - -- Generate CSRF token - self.csrf_token = csrf.generate_token(self) - - -- Verify Authorization - if self.session.name then - if not self.session.admin then - assert_error(false, "err_not_admin") - end - else - return - end - end, - - on_error = function(self) - self.errors = generate.errors(self.i18n, self.errors) - - if not self.session.name then - return { render = "admin.login" } - end - - return { render = "admin.admin" } - end, - - GET = function(self) - if not self.session.name then - return { render = "admin.login" } - end - - return { render = "admin.admin" } - end, - - POST = function(self) - -- Validate CSRF token - csrf.assert_token(self) - - -- Verify user credentials - if self.params.login then - -- Verify user - local user = assert_error(Users:verify_user(self.params)) - - -- Set username - self.session.name = user.username - - return { redirect_to = self.admin_url } - end - - -- Must be logged in as an admin! - if self.session.admin then - -- Redirect to modify user page - if self.params.modify_user then - return { redirect_to = self:url_for("web.admin.users", { action="modify", user=self.params.user }) } - end - - -- Redirect to delete user page - if self.params.delete_user then - return { redirect_to = self:url_for("web.admin.users", { action="delete", user=self.params.user }) } - end - - -- Redirect to modify board page - if self.params.modify_board then - return { redirect_to = self:url_for("web.admin.boards", { action="modify", uri_name=self.params.board }) } - end - - -- Redirect to delete board page - if self.params.delete_board then - return { redirect_to = self:url_for("web.admin.boards", { action="delete", uri_name=self.params.board }) } - end - - -- Redirect to modify announcement page - if self.params.modify_announcement then - return { redirect_to = self:url_for("web.admin.announcements", { action="modify", ann=self.params.ann }) } - end - - -- Redirect to delete announcement page - if self.params.delete_announcement then - return { redirect_to = self:url_for("web.admin.announcements", { action="delete", ann=self.params.ann }) } - end - - -- Redirect to modify page page - if self.params.modify_page then - return { redirect_to = self:url_for("web.admin.pages", { action="modify", page=self.params.page }) } - end - - -- Redirect to delete page page - if self.params.delete_page then - return { redirect_to = self:url_for("web.admin.pages", { action="delete", page=self.params.page }) } - end - - -- Redirect to reported post - if self.params.view_report then - local report = Reports:get_report_by_id(self.params.report) - local board = Boards:get_board(report.board_id) - local post = Posts:get_post(board.id, report.post_id) - local op = Posts:get_thread_op(report.thread_id) - - return { redirect_to = self:url_for("web.boards.thread", { board=board.name, thread=op.post_id, anchor="p", id=post.post_id }) } - end - - -- Redirect to delete report page - if self.params.delete_report then - return { redirect_to = self:url_for("web.admin.reports", { action="delete", report=self.params.report }) } - end - - -- Regenerate thumbnails - if self.params.regen_thumbs then - Boards:regen_thumbs() - end - - return { render = "admin.admin" } - end - - return { render = "admin.login" } - end -} diff --git a/misc/website/backend/app/src/apps/web/admin/page.lua b/misc/website/backend/app/src/apps/web/admin/page.lua deleted file mode 100755 index fce2138..0000000 --- a/misc/website/backend/app/src/apps/web/admin/page.lua +++ /dev/null @@ -1,164 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local csrf = require "lapis.csrf" -local generate = require "utils.generate" -local Boards = require "models.boards" -local Pages = require "models.pages" - -return { - before = function(self) - -- Get all board data - self.boards = Boards:get_boards() - - -- Get all page data - self.pages = Pages:get_pages() - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - -- Generate CSRF token - self.csrf_token = csrf.generate_token(self) - - -- Page title - self.page_title = self.i18n("admin_panel") - - -- Verify Authorization - if self.session.name then - if not self.session.admin then - assert_error(false, "err_not_admin") - end - else - return - end - - -- Display creation form - if self.params.action == "create" then - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("create_page") - ) - self.page = self.params - return - end - - -- Display modification form - if self.params.action == "modify" then - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("modify_page") - ) - self.page = Pages:get_page(self.params.page) - return - end - - -- Delete page - if self.params.action == "delete" then - local page = Pages:get_page(self.params.page) - assert_error(Pages:delete_page(page)) - - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("deleted_page", { page.slug, page.title }) - return - end - end, - on_error = function(self) - self.errors = generate.errors(self.i18n, self.errors) - - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "create" then - return { render = "admin.page" } - elseif self.params.action == "modify" then - return { render = "admin.page" } - elseif self.params.action == "delete" then - return { render = "admin.admin" } - end - end, - GET = function(self) - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "create" then - return { render = "admin.page" } - elseif self.params.action == "modify" then - return { render = "admin.page" } - elseif self.params.action == "delete" then - return { render = "admin.success" } - end - end, - POST = function(self) - -- Validate CSRF token - csrf.assert_token(self) - - -- Validate user input - assert_valid(self.params, { - { "slug", max_length=255, exists=true }, - { "title", max_length=255, exists=true } - }) - - -- Create new page - if self.params.create_page then - local sl = string.lower - -- Verify unique names - for _, page in ipairs(self.pages) do - if sl(page.slug) == sl(self.params.slug) then - assert_error(false, "err_slug_used") - end - end - - -- Create page - local page = assert_error(Pages:create_page(self.params)) - - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("created_page", { page.slug, page.title }) - - return { render = "admin.success" } - end - - -- Modify page - if self.params.modify_page then - local discard = { - "page", - "modify_page", - "ip", - "action", - "csrf_token", - "old" - } - - local page = Pages:get_page(self.params.old) - - -- Fill in board with new data - for k, param in pairs(self.params) do - page[k] = param - end - - -- Get rid of form trash - for _, param in ipairs(discard) do - page[param] = nil - end - - assert_error(Pages:modify_page(page)) - - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("modified_page", { page.slug, page.title }) - - return { render = "admin.success" } - end - - return { redirect_to = self:url_for("web.admin.index") } - end -} diff --git a/misc/website/backend/app/src/apps/web/admin/report.lua b/misc/website/backend/app/src/apps/web/admin/report.lua deleted file mode 100755 index 0ab4b87..0000000 --- a/misc/website/backend/app/src/apps/web/admin/report.lua +++ /dev/null @@ -1,64 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local csrf = require "lapis.csrf" -local generate = require "utils.generate" -local Boards = require "models.boards" -local Reports = require "models.reports" - -return { - before = function(self) - -- Get data - self.boards = Boards:get_boards() - self.reports = Reports:get_reports() - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - -- Generate CSRF token - self.csrf_token = csrf.generate_token(self) - - -- Page title - self.page_title = self.i18n("admin_panel") - - -- Verify Authorization - if self.session.name then - if not self.session.admin then - assert_error(false, "err_not_admin") - end - else - return - end - - -- Delete report - if self.params.action == "delete" then - local report = Reports:get_report_by_id(self.params.report) - assert_error(Reports:delete_report(report)) - - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("deleted_report", { report.id }) - return - end - end, - on_error = function(self) - self.errors = generate.errors(self.i18n, self.errors) - - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "delete" then - return { render = "admin.admin" } - end - end, - GET = function(self) - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "delete" then - return { render = "admin.success" } - end - end, - POST = function(self) - return { redirect_to = self:url_for("web.admin.index") } - end -} diff --git a/misc/website/backend/app/src/apps/web/admin/user.lua b/misc/website/backend/app/src/apps/web/admin/user.lua deleted file mode 100755 index b6ae735..0000000 --- a/misc/website/backend/app/src/apps/web/admin/user.lua +++ /dev/null @@ -1,190 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local csrf = require "lapis.csrf" -local generate = require "utils.generate" -local Boards = require "models.boards" -local Users = require "models.users" - -return { - before = function(self) - -- Get all board data - self.boards = Boards:get_boards() - - -- Get all user data - self.users = Users:get_users() - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - -- Generate CSRF token - self.csrf_token = csrf.generate_token(self) - - -- Page title - self.page_title = self.i18n("admin_panel") - - -- Verify Authorization - if self.session.name then - if not self.session.admin then - assert_error(false, "err_not_admin") - end - else - return - end - - -- Display creation form - if self.params.action == "create" then - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("create_user") - ) - self.user = self.params - return - end - - -- Display modification form - if self.params.action == "modify" then - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("modify_user") - ) - self.user = Users:get_user_by_id(self.params.user) - return - end - - -- Delete user - if self.params.action == "delete" then - local user = Users:get_user_by_id(self.params.user) - assert_error(Users:delete_user(user)) - - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("deleted_user", { user.username }) - return - end - end, - on_error = function(self) - self.errors = generate.errors(self.i18n, self.errors) - - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "create" then - return { render = "admin.user" } - elseif self.params.action == "modify" then - return { render = "admin.user" } - elseif self.params.action == "delete" then - return { render = "admin.admin" } - end - end, - GET = function(self) - if not self.session.name then - return { render = "admin.login" } - elseif self.params.action == "create" then - return { render = "admin.user" } - elseif self.params.action == "modify" then - return { render = "admin.user" } - elseif self.params.action == "delete" then - return { render = "admin.success" } - end - end, - POST = function(self) - -- Validate CSRF token - csrf.assert_token(self) - - -- Create new user - if self.params.create_user then - local sl = string.lower - - -- Validate user input - assert_valid(self.params, { - { "username", exists=true, max_length=255 }, - { "new_password", exists=true, equals=self.params.retype_password }, - { "retype_password", exists=true } - }) - - self.params.password = self.params.new_password - - -- Verify unique name - for _, user in ipairs(self.users) do - if sl(user.username) == sl(self.params.username) then - assert_error(false, "err_user_used") - end - end - - -- Create user - local user = assert_error(Users:create_user(self.params)) - - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("created_user", { user.username }) - - return { render = "admin.success" } - end - - -- Modify user - if self.params.modify_user then - -- Validate user input - assert_valid(self.params, { - { "username", exists=true, max_length=255 }, - { "new_password", equals=self.params.retype_password }, - { "retype_password", } - }) - - local discard = { - "user", - "modify_user", - "ip", - "action", - "csrf_token", - "old_password", - "new_password", - "retype_password", - } - - local user = Users:get_user(self.params.username) - - -- Validate user - if #self.params.old_password > 0 then - -- Validate user input - assert_valid(self.params, { - { "new_password", exists=true }, - { "retype_password", exists=true } - }) - - -- TODO: verify user's old password in non-admin setting - - self.params.password = self.params.new_password - end - - -- Fill in board with new data - for k, param in pairs(self.params) do - user[k] = param - end - - -- Get rid of form trash - for _, param in ipairs(discard) do - user[param] = nil - end - - assert_error(Users:modify_user(user)) - - self.page_title = string.format( - "%s - %s", - self.i18n("admin_panel"), - self.i18n("success") - ) - self.action = self.i18n("modified_user", { user.username }) - - return { render = "admin.success" } - end - - return { redirect_to = self:url_for("web.admin.index") } - end -} diff --git a/misc/website/backend/app/src/apps/web/boards.lua b/misc/website/backend/app/src/apps/web/boards.lua deleted file mode 100755 index fe8aad7..0000000 --- a/misc/website/backend/app/src/apps/web/boards.lua +++ /dev/null @@ -1,14 +0,0 @@ -local lapis = require "lapis" -local r2 = require("lapis.application").respond_to -local app = lapis.Application() -app.__base = app -app.name = "web.boards." -app.path = "/board" -app.handle_404 = require "apps.web.internal.code_404" - -app:match("board", "/:uri_name(/page/:page)", r2(require "apps.web.boards.board")) -app:match("catalog", "/:uri_name/catalog", r2(require "apps.web.boards.catalog")) -app:match("archive", "/:uri_name/archive", require "apps.web.boards.archive") -app:match("thread", "/:uri_name/thread/:thread(#:anchor:id)", r2(require "apps.web.boards.thread")) - -return app diff --git a/misc/website/backend/app/src/apps/web/boards/archive.lua b/misc/website/backend/app/src/apps/web/boards/archive.lua deleted file mode 100755 index ea86912..0000000 --- a/misc/website/backend/app/src/apps/web/boards/archive.lua +++ /dev/null @@ -1,76 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local capture = require "utils.capture" -local format = require "utils.text_formatter" -local Posts = require "models.posts" -local Threads = require "models.threads" - -return function(self) - - -- Get board - for _, board in ipairs(self.boards) do - if board.name == self.params.uri_name then - self.board = board - break - end - end - - -- Board not found - if not self.board then - return self:write({ redirect_to = self:url_for("web.pages.index") }) - end - - -- Get announcements - -- TODO: Consolidate these into a single call - self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id="global" }))) - local board_announcements = assert_error(capture.get(self:url_for("api.boards.announcements", { uri_name=self.params.uri_name }))) - for _, announcement in ipairs(board_announcements) do - table.insert(self.announcements, announcement) - end - - -- Page title - self.page_title = string.format( - "/%s/ - %s", - self.board.name, - self.board.title - ) - - -- Nav links link to sub page if available - self.sub_page = "archive" - - -- Get threads - self.threads = assert_error(capture.get(self:url_for("api.boards.archived", { uri_name=self.params.uri_name }))) - - -- Get time - self.days = math.floor(self.board.archive_time / 24 / 60 / 60) - - -- Get stats - for _, thread in ipairs(self.threads) do - thread.op = Posts:get_thread_op(thread.id) - thread.replies = Posts:count_posts(thread.id) - 1 - thread.url = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=thread.op.post_id }) - - -- Process name - thread.op.name = thread.op.name or self.board.anon_name - - -- Process tripcode - thread.op.trip = thread.op.trip or "" - - -- Process comment - if thread.op.comment then - local comment = thread.op.comment - comment = format.sanitize(comment) - comment = format.spoiler(comment) - - if #comment > 110 then - comment = comment:sub(1,100) - comment = comment .. "..." - end - - thread.op.comment = comment - else - thread.op.comment = "" - end - end - - return { render = "archive" } -end diff --git a/misc/website/backend/app/src/apps/web/boards/board.lua b/misc/website/backend/app/src/apps/web/boards/board.lua deleted file mode 100755 index e7d4afe..0000000 --- a/misc/website/backend/app/src/apps/web/boards/board.lua +++ /dev/null @@ -1,242 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local csrf = require "lapis.csrf" -local capture = require "utils.capture" -local format = require "utils.text_formatter" -local generate = require "utils.generate" -local process = require "utils.request_processor" -local Posts = require "models.posts" - -return { - before = function(self) - - -- Get board - for _, board in ipairs(self.boards) do - if board.name == self.params.uri_name then - self.board = board - break - end - end - - -- Board not found - if not self.board or self.params.page and not tonumber(self.params.page) then - return self:write({ redirect_to = self:url_for("web.pages.index") }) - end - - -- Get announcements - -- TODO: Consolidate these into a single call - self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id="global" }))) - local board_announcements = assert_error(capture.get(self:url_for("api.boards.announcements", { uri_name=self.params.uri_name }))) - for _, announcement in ipairs(board_announcements) do - table.insert(self.announcements, announcement) - end - - -- Page title - self.page_title = string.format( - "/%s/ - %s", - self.board.name, - self.board.title - ) - - -- Flag comments as required or not - self.comment_flag = self.board.thread_comment - - -- Generate CSRF token - self.csrf_token = csrf.generate_token(self) - - -- Current page - self.params.page = self.params.page or 1 - - -- Get threads - local response = assert_error(capture.get(self:url_for("api.boards.threads", { uri_name=self.params.uri_name, uri_page=self.params.page }))) - self.threads = response.threads - self.pages = response.pages - - -- Get posts - for _, thread in ipairs(self.threads) do - -- Get posts visible on the board index - thread.posts = Posts:get_index_posts(thread.id) - - -- Get hidden posts - thread.hidden = Posts:count_hidden_posts(thread.id) - - -- Get op - local op = thread.posts[#thread.posts] - if not op then - assert_error(false, { "err_orphaned", { thread.id } }) - end - - thread.url = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id }) - - -- Format comments - for _, post in ipairs(thread.posts) do - -- OP gets a thread tag - if post.post_id == op.post_id then - post.thread = post.post_id - end - - post.name = post.name or self.board.anon_name - post.reply = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="q", id=post.post_id }) - post.link = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="p", id=post.post_id }) - post.remix = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="r", id=post.post_id }) - post.timestamp = os.date("%Y-%m-%d (%a) %H:%M:%S", post.timestamp) - post.file_size = math.floor(post.file_size / 1024) - post.file_dimensions = "" - - if post.file_width > 0 and post.file_height > 0 then - post.file_dimensions = string.format(", %dx%d", post.file_width, post.file_height) - end - - if not post.file_duration or post.file_duration == "0" then - post.file_duration = "" - else - post.file_duration = string.format(", %s", post.file_duration) - end - - if post.file_path then - local name, ext = post.file_path:match("^(.+)(%..+)$") - ext = string.lower(ext) - - -- Get thumbnail URL - if post.file_type == "audio" then - if post == thread.posts[#thread.posts] then - post.thumb = self:format_url(self.static_url, "op_audio.png") - else - post.thumb = self:format_url(self.static_url, "post_audio.png") - end - elseif post.file_type == "image" then - if post.file_spoiler then - if post == thread.posts[#thread.posts] then - post.thumb = self:format_url(self.static_url, "op_spoiler.png") - else - post.thumb = self:format_url(self.static_url, "post_spoiler.png") - end - else - if ext == ".webm" or ext == ".svg" then - post.thumb = self:format_url(self.files_url, self.board.name, 's' .. name .. '.png') - else - post.thumb = self:format_url(self.files_url, self.board.name, 's' .. post.file_path) - end - end - end - - post.file_path = self:format_url(self.files_url, self.board.name, post.file_path) - end - - -- Process comment - if post.comment then - local comment = post.comment - comment = format.sanitize(comment) - comment = format.quote(comment, self, self.board, post) - comment = format.green_text(comment) - comment = format.blue_text(comment) - comment = format.spoiler(comment) - comment = format.new_lines(comment) - post.comment = comment - else - post.comment = "" - end - end - end - end, - on_error = function(self) - self.errors = generate.errors(self.i18n, self.errors) - return { render = "board"} - end, - GET = function() - return { render = "board" } - end, - POST = function(self) - -- Validate CSRF token - csrf.assert_token(self) - - local board_url = self:url_for("web.boards.board", { uri_name=self.board.name }) - - -- Submit new thread - if self.params.submit then - -- Validate user input - assert_valid(self.params, { - { "name", max_length=255 }, - { "subject", max_length=255 }, - { "options", max_length=255 }, - { "comment", max_length=self.text_size } - }) - - -- Validate post - local post = assert_error(process.create_thread(self.params, self.session, self.board)) - return { redirect_to = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=post.post_id, anchor="p", id=post.post_id }) } - end - - -- Delete thread - if self.params.delete and self.params.thread_id then - -- Validate user input - assert_valid(self.params, { - { "post_id", exists=true } - }) - - -- Validate deletion - assert_error(process.delete_thread(self.params, self.session, self.board)) - return { redirect_to = board_url } - end - - -- Delete post - if self.params.delete and not self.params.thread_id then - -- Validate user input - assert_valid(self.params, { - { "post_id", exists=true } - }) - - -- Validate deletion - assert_error(process.delete_post(self.params, self.session, self.board)) - return { redirect_to = board_url } - end - - -- Report post - if self.params.report then - -- Validate user input - assert_valid(self.params, { - { "board", exists=true }, - { "post_id", exists=true } - }) - - -- Validate report - assert_error(process.report_post(self.params, self.board)) - return { redirect_to = board_url } - end - - -- Admin commands - if self.session.admin or self.session.mod then - -- Sticky thread - if self.params.sticky then - assert_error(process.sticky_thread(self.params, self.board)) - return { redirect_to = board_url } - end - - -- Lock thread - if self.params.lock then - assert_error(process.lock_thread(self.params, self.board)) - return { redirect_to = board_url } - end - - -- Save thread - if self.params.save then - assert_error(process.save_thread(self.params, self.board)) - return { redirect_to = board_url } - end - - -- Override thread - if self.params.override then - assert_error(process.override_thread(self.params, self.board)) - return { redirect_to = board_url } - end - - -- Ban user - if self.params.ban then - assert_error(process.ban_user(self.params, self.board)) - return { redirect_to = board_url } - end - end - - return { redirect_to = board_url } - end -} diff --git a/misc/website/backend/app/src/apps/web/boards/catalog.lua b/misc/website/backend/app/src/apps/web/boards/catalog.lua deleted file mode 100755 index 1e2eee8..0000000 --- a/misc/website/backend/app/src/apps/web/boards/catalog.lua +++ /dev/null @@ -1,128 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local process = require "utils.request_processor" -local capture = require "utils.capture" -local csrf = require "lapis.csrf" -local format = require "utils.text_formatter" -local generate = require "utils.generate" -local Posts = require "models.posts" - -return { - before = function(self) - - -- Get board - for _, board in ipairs(self.boards) do - if board.name == self.params.uri_name then - self.board = board - break - end - end - - -- Board not found - if not self.board then - return self:write({ redirect_to = self:url_for("web.pages.index") }) - end - - -- Get announcements - -- TODO: Consolidate these into a single call - self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id="global" }))) - local board_announcements = assert_error(capture.get(self:url_for("api.boards.announcements", { uri_name=self.params.uri_name }))) - for _, announcement in ipairs(board_announcements) do - table.insert(self.announcements, announcement) - end - - -- Page title - self.page_title = string.format( - "/%s/ - %s", - self.board.name, - self.board.title - ) - - -- Nav links link to sub page if available - self.sub_page = "catalog" - - -- Flag comments as required or not - self.comment_flag = self.board.thread_comment - - -- Generate CSRF token - self.csrf_token = csrf.generate_token(self) - - -- Get threads - local response = assert_error(capture.get(self:url_for("api.boards.threads", { uri_name=self.params.uri_name }))) - self.threads = response.threads - - -- Get stats - for _, thread in ipairs(self.threads) do - thread.op = Posts:get_thread_op(thread.id) - thread.replies = Posts:count_posts(thread.id) - 1 - thread.files = Posts:count_files(thread.id) - thread.url = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=thread.op.post_id }) - - if thread.op.file_path then - local name, ext = thread.op.file_path:match("^(.+)(%..+)$") - ext = string.lower(ext) - - -- Get thumbnail URL - if thread.op.file_type == "audio" then - thread.op.thumb = self:format_url(self.static_url, "post_audio.png") - elseif thread.op.file_type == "image" then - if thread.op.file_spoiler then - thread.op.thumb = self:format_url(self.static_url, "post_spoiler.png") - else - if ext == ".webm" or ext == ".svg" then - thread.op.thumb = self:format_url(self.files_url, self.board.name, 's' .. name .. '.png') - else - thread.op.thumb = self:format_url(self.files_url, self.board.name, 's' .. thread.op.file_path) - end - end - end - - thread.op.file_path = self:format_url(self.files_url, self.board.name, thread.op.file_path) - end - - -- Process comment - if thread.op.comment then - local comment = thread.op.comment - comment = format.sanitize(comment) - comment = format.spoiler(comment) - comment = format.new_lines(comment) - - if #comment > 260 then - comment = comment:sub(1, 250) .. "..." - end - - thread.op.comment = comment - else - thread.op.comment = "" - end - end - end, - on_error = function(self) - self.errors = generate.errors(self.i18n, self.errors) - return { render = "catalog"} - end, - GET = function() - return { render = "catalog" } - end, - POST = function(self) - -- Validate CSRF token - csrf.assert_token(self) - - -- Submit new thread - if self.params.submit and not self.thread then - -- Validate user input - assert_valid(self.params, { - { "name", max_length=255 }, - { "subject", max_length=255 }, - { "options", max_length=255 }, - { "comment", max_length=self.text_size } - }) - - -- Validate post - local post = assert_error(process.create_thread(self.params, self.session, self.board)) - return { redirect_to = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=post.post_id, anchor="p", id=post.post_id }) } - end - - return { redirect_to = self:url_for("web.boards.catalog", { uri_name=self.board.name }) } - end -} diff --git a/misc/website/backend/app/src/apps/web/boards/thread.lua b/misc/website/backend/app/src/apps/web/boards/thread.lua deleted file mode 100755 index 395df43..0000000 --- a/misc/website/backend/app/src/apps/web/boards/thread.lua +++ /dev/null @@ -1,242 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local assert_valid = require("lapis.validate").assert_valid -local csrf = require "lapis.csrf" -local capture = require "utils.capture" -local format = require "utils.text_formatter" -local generate = require "utils.generate" -local process = require "utils.request_processor" -local Posts = require "models.posts" - -return { - before = function(self) - - -- Get board - for _, board in ipairs(self.boards) do - if board.name == self.params.uri_name then - self.board = board - break - end - end - - -- Board not found - if not self.board then - return self:write({ redirect_to = self:url_for("web.pages.index") }) - end - - -- Get current thread data - local post = Posts:get(self.board.id, self.params.thread) - if not post then - return self:write({ redirect_to = self:url_for("web.boards.board", { uri_name=self.board.name }) }) - end - - self.thread = post:get_thread() - if not self.thread then - return self:write({ redirect_to = self:url_for("web.boards.board", { uri_name=self.board.name }) }) - end - - local op = self.thread:get_op() - if post.post_id ~= op.post_id then - return self:write({ redirect_to = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=op.post_id, anchor="p", id=post.post_id }) }) - end - - -- Get announcements - -- TODO: Consolidate these into a single call - self.announcements = assert_error(capture.get(self:url_for("api.announcements.announcement", { uri_id="global" }))) - local board_announcements = assert_error(capture.get(self:url_for("api.boards.announcements", { uri_name=self.params.uri_name }))) - for _, announcement in ipairs(board_announcements) do - table.insert(self.announcements, announcement) - end - - -- Page title - self.page_title = string.format( - "/%s/ - %s", - self.board.name, - self.board.title - ) - - -- Flag comments as required or not - self.comment_flag = self.board.thread_comment - - -- Generate CSRF token - self.csrf_token = csrf.generate_token(self) - - -- Determine if we allow a user to upload a file - self.num_files = Posts:count_files(self.thread.id) - - -- Get posts - self.posts = self.thread:get_posts() - - -- Format comments - for i, post in ipairs(self.posts) do - -- OP gets a thread tag - if i == 1 then - post.thread = post.post_id - end - - post.name = post.name or self.board.anon_name - post.reply = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=self.posts[1].post_id, anchor="q", id=post.post_id }) - post.link = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=self.posts[1].post_id, anchor="p", id=post.post_id }) - post.timestamp = os.date("%Y-%m-%d (%a) %H:%M:%S", post.timestamp) - post.file_size = math.floor(post.file_size / 1024) - post.file_dimensions = "" - - if post.file_width > 0 and post.file_height > 0 then - post.file_dimensions = string.format(", %dx%d", post.file_width, post.file_height) - end - - if not post.file_duration or post.file_duration == "0" then - post.file_duration = "" - else - post.file_duration = string.format(", %s", post.file_duration) - end - - if post.file_path then - local name, ext = post.file_path:match("^(.+)(%..+)$") - ext = string.lower(ext) - - -- Get thumbnail URL - if post.file_type == "audio" then - if post == self.posts[1] then - post.thumb = self:format_url(self.static_url, "op_audio.png") - else - post.thumb = self:format_url(self.static_url, "post_audio.png") - end - elseif post.file_type == "image" then - if post.file_spoiler then - if post == self.posts[1] then - post.thumb = self:format_url(self.static_url, "op_spoiler.png") - else - post.thumb = self:format_url(self.static_url, "post_spoiler.png") - end - else - if ext == ".webm" or ext == ".svg" then - post.thumb = self:format_url(self.files_url, self.board.name, 's' .. name .. '.png') - else - post.thumb = self:format_url(self.files_url, self.board.name, 's' .. post.file_path) - end - end - end - - post.file_path = self:format_url(self.files_url, self.board.name, post.file_path) - end - - -- Process comment - if post.comment then - local comment = post.comment - comment = format.sanitize(comment) - comment = format.quote(comment, self, self.board, post) - comment = format.green_text(comment) - comment = format.blue_text(comment) - comment = format.spoiler(comment) - comment = format.new_lines(comment) - post.comment = comment - else - post.comment = "" - end - end - end, - on_error = function(self) - self.errors = generate.errors(self.i18n, self.errors) - return { render = "thread"} - end, - GET = function() - return { render = "thread" } - end, - POST = function(self) - -- Validate CSRF token - csrf.assert_token(self) - - local board_url = self:url_for("web.boards.board", { uri_name=self.board.name }) - local thread_url = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=self.posts[1].post_id }) - - -- Submit new post - if self.params.submit and self.thread then - -- Validate user input - assert_valid(self.params, { - { "thread", exists=true }, - { "name", max_length=255 }, - { "subject", max_length=255 }, - { "options", max_length=255 }, - { "comment", max_length=self.text_size } - }) - - -- Validate post - local post = assert_error(process.create_post(self.params, self.session, self.board, self.thread)) - return { redirect_to = self:url_for("web.boards.thread", { uri_name=self.board.name, thread=self.posts[1].post_id, anchor="p", id=post.post_id }) } - end - - -- Delete thread - if self.params.delete and self.params.thread_id then - -- Validate user input - assert_valid(self.params, { - { "post_id", exists=true } - }) - - -- Validate deletion - assert_error(process.delete_thread(self.params, self.session, self.board)) - return { redirect_to = board_url } - end - - -- Delete post - if self.params.delete and not self.params.thread_id then - -- Validate user input - assert_valid(self.params, { - { "post_id", exists=true } - }) - - -- Validate deletion - assert_error(process.delete_post(self.params, self.session, self.board)) - return { redirect_to = thread_url } - end - - -- Report post - if self.params.report then - -- Validate user input - assert_valid(self.params, { - { "board", exists=true }, - { "post_id", exists=true } - }) - - -- Validate report - assert_error(process.report_post(self.params, self.board)) - return { redirect_to = thread_url } - end - - -- Admin commands - if self.session.admin or self.session.mod then - -- Sticky thread - if self.params.sticky then - assert_error(process.sticky_thread(self.params, self.board)) - return { redirect_to = thread_url } - end - - -- Lock thread - if self.params.lock then - assert_error(process.lock_thread(self.params, self.board)) - return { redirect_to = thread_url } - end - - -- Save thread - if self.params.save then - assert_error(process.save_thread(self.params, self.board)) - return { redirect_to = thread_url } - end - - -- Override thread - if self.params.override then - assert_error(process.override_thread(self.params, self.board)) - return { redirect_to = thread_url } - end - - -- Ban user - if self.params.ban then - assert_error(process.ban_user(self.params, self.board)) - return { redirect_to = thread_url } - end - - return { redirect_to = thread_url } - end - - return { redirect_to = thread_url } - end -} diff --git a/misc/website/backend/app/src/apps/web/internal/check_auth.lua b/misc/website/backend/app/src/apps/web/internal/check_auth.lua deleted file mode 100755 index 8711f4b..0000000 --- a/misc/website/backend/app/src/apps/web/internal/check_auth.lua +++ /dev/null @@ -1,29 +0,0 @@ -local Users = require "models.users" - -return function(self) - -- Prepare session names - self.session.names = self.session.names or {} - - -- Verify Authorization - if self.session.name then - local user = Users:get_user(self.session.name) - - if user then - user.password = nil - self.session.admin = user.admin - self.session.mod = user.mod - self.session.janitor = user.janitor - else - self.session.admin = nil - self.session.mod = nil - self.session.janitor = nil - end - else - self.session.admin = nil - self.session.mod = nil - self.session.janitor = nil - end - - -- Get IP from ngx - self.params.ip = self.req.headers["X-Real-IP"] or self.req.remote_addr -end diff --git a/misc/website/backend/app/src/apps/web/internal/check_ban.lua b/misc/website/backend/app/src/apps/web/internal/check_ban.lua deleted file mode 100755 index bf69b7d..0000000 --- a/misc/website/backend/app/src/apps/web/internal/check_ban.lua +++ /dev/null @@ -1,41 +0,0 @@ -local assert_error = require("lapis.application").assert_error -local capture = require "utils.capture" - -return function(self) - -- MODS = FAGS - if self.session.admin or - self.session.mod or - self.session.janitor or - self.route_name == "admin" then - return - end - - -- Get list of bans by ip - local bans = assert_error(capture.get(self:url_for("api.bans.bans_ip", { uri_ip=self.params.ip }))) - - -- Get current board - local board = {} - if self.params.uri_name then - board = assert_error(capture.get(self:url_for("api.boards.board", { uri_name=self.params.uri_name }))) - end - - -- If you are banned, gtfo - for _, ban in ipairs(bans) do - if ban.board_id == 0 or - ban.board_id == board.id then - - -- Ban data - self.ip = ban.ip - self.reason = ban.reason or self.i18n("err_ban_reason") - self.expire = os.date("%Y-%m-%d (%a) %H:%M:%S", ban.time + ban.duration) - - -- Page title - self.page_title = self.i18n("ban_title") - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - return self:write({ render = "banned" }) - end - end -end diff --git a/misc/website/backend/app/src/apps/web/internal/code_404.lua b/misc/website/backend/app/src/apps/web/internal/code_404.lua deleted file mode 100755 index 5b545ad..0000000 --- a/misc/website/backend/app/src/apps/web/internal/code_404.lua +++ /dev/null @@ -1,14 +0,0 @@ -local Boards = require "models.boards" - -return function(self) - -- Get all board data - self.boards = Boards:get_boards() - - -- Page title - self.page_title = self.i18n("404") - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - return { render = "code_404", status = "404" } -end diff --git a/misc/website/backend/app/src/apps/web/internal/config_site.lua b/misc/website/backend/app/src/apps/web/internal/config_site.lua deleted file mode 100755 index 1e42595..0000000 --- a/misc/website/backend/app/src/apps/web/internal/config_site.lua +++ /dev/null @@ -1,45 +0,0 @@ -local config = require("lapis.config").get() -local assert_error = require("lapis.application").assert_error -local i18n = require "i18n" -local lfs = require "lfs" -local capture = require "utils.capture" - -return function(self) - -- Set basic information - self.software = "Lapis-chan" - self.version = "1.2.5" - self.site_name = config.site_name - self.text_size = _G.text_size - - -- Get localization files - self.locales = {} - for file in lfs.dir("src/locale") do - local name, ext = string.match(file, "^(.+)%.(.+)$") - if ext == "lua" then - table.insert(self.locales, name) - end - end - - -- Set localization - if self.params.locale then - self.session.locale = self.params.locale - end - - i18n.setLocale(self.session.locale or "en") - i18n.loadFile("src/locale/en.lua") - if i18n.getLocale() ~= "en" then - i18n.loadFile("locale/" .. i18n.getLocale() .. ".lua") - end - self.i18n = i18n - - -- Get all boards - self.boards = assert_error(capture.get(self:url_for("api.boards.boards"))) - - -- Static - self.static_url = "/static/%s" - self.files_url = "/files/%s/%s" - - function self.format_url(_, pattern, ...) - return self:build_url(string.format(pattern, ...)) - end -end diff --git a/misc/website/backend/app/src/apps/web/internal/install.lua b/misc/website/backend/app/src/apps/web/internal/install.lua deleted file mode 100755 index 002786e..0000000 --- a/misc/website/backend/app/src/apps/web/internal/install.lua +++ /dev/null @@ -1,333 +0,0 @@ -local Users = require "models.users" -local Boards = require "models.boards" -local Pages = require "models.pages" -local lfs = require "lfs" -local validate = require("lapis.validate").validate -local faq = [[ -
-
    -
  1. What is Lapis-chan?
  2. -
  3. What is Lapchan?
  4. -
  5. What should I know before posting?
  6. -
  7. What are the basics?
  8. -
  9. How do I post anonymously?
  10. -
  11. Do I have to post an image?
  12. -
  13. How do I quote a post?
  14. -
  15. What is a tripcode?
  16. -
  17. Can I mark an image as a spoiler?
  18. -
  19. What are post options?
  20. -
  21. How can I interact with posts?
  22. -
  23. What types of boards are supported?
  24. -
-
-
-
-

What is Lapis-chan?

-

- Lapis-chan is an open source imageboard web application written in Lua - using the Lapis web framework. -

-
-
-

What is Lapchan?

-

- Lapchan is a website that runs the latest version of Lapis-chan. It is - both used as a small community and a testing platform for new and - experimental features. -

-
-
-

What Should I Know Before Posting?

-

- Before posting, you should read the rules for whichever board you want to - post in. If you break the rules, your post may be deleted and you may - also earn yourself a temporary or permanent ban from the board or entire - website. Please read the rules! -

-
-
-

What are the Basics?

-

- In general, "blue" boards are considered safe for work (SFW) and "red" - boards are considered not safe for work (NSFW). The definition of - work-safety is often loose, but in general it means there shouldn't be - any direct pornographic material on a blue board. This may differ from - site to site, but Lapis-chan by default offers blue and red themes that - are nearly identical to 4chan's themes. New themes are expected in later - releases. -

-

- It is also worth noting that chan culture can be both friendly and - abrasive. More often than not, users will be posting anonymously and are - able to speak freely because if this. Be prepared for the best and worst - of society when interacting with others in an anonymous forum. -

-
-
-

How Do I Post Anonymously?

-

- By default, all users are anonymous to each other. By leaving the "Name" - field empty in a post, your name will simply be fille din with the - board's default name. Identifiable information such as your IP address is - recorded to the Lapis-chan database when you post for legal reasons, but - all posts are permanently purged after some time unless otherwise noted, - including your IP address and any other information attached to your - post. -

-
-
-

Do I Have to Post an Image?

-

- Some boards require you to post an image or a comment, others do not. By - default, Lapis-chan will place "(Required)" in or near a field that - requires data. Currently, Lapis-chan's only optionally required data - include a comment and an image. -

-
-
-

How Do I Quote a Post?

-

- To quote (and link to) another post, simply type ">>" followed by - the post number (e.g. >>2808). To quote a post that is on a - different board, You must type ">>>" follow by a slash, the - name of the board, another slash, and then the post number - (e.g. >>>/a/2808). -

-
-
-

What is a Tripcode?

-

- A tripcode is a uniquely identifiable hash attached to the end of your - name, or in lieu of a name. It is a completely optional feature that - allows users to de-anonymize if they so choose. Some boards benefit from - de-anonymization such as content-creation boards where being named can - help get your content seen and recognized. -

-

- Tripcodes come in two sizes: insecure and secure. Insecure tripcodes use - a weak hashing method that allows users to game the algorithm to generate - a hash that reads out something similar to what they want. A secure - tripcode uses a very secure hashing algorithm and a server-specific - secret token that is not gameable, but also significantly more difficult - to impersonate. -

-

- To use an insecure tripcode, place a hash ("#") sign at the end of your - name (or leave a name out entirely) and type your password after the - hash. To use a secure tripcode, simply use two hashes instead of one - (e.g. lapchan#insecure, lapchan##secure, #nameless, ##nameless). -

-
-
-

Can I Mark an Image as a Spoiler?

-

- Yes. When you upload an image, there should be a check box beside the - file input field. Checking that box will replace the thumbnail of your - image with a spoiler image. -

-

- You can also tag text within your post as a spoiler by writing your text - with [spoiler]a spoiler tag[/spoiler]. -

-
-
-

What Are Post Options?

-

- Post options are optional features you can use to modify how your post - affects the thread or board you are posting in. To apply an options, - simply type the option code into the options field in your post. - Currently, the options available include: -

- -
-
-

How Can I Interact With Posts?

-

- To interact with a post, click on the menu icon ("▶") on the left of the - post. The menu currently has the following interactions: -

- -
-
-

What Types of Boards are Supported?

-

- Lapis-chan has several different types of boards with more planned in the - future. Currently, Lapis-chan supports the following boards: -

- -
-
-]] - -local success = [[ -

- Congratulations! Lapis-chan is now installed! Please rename or delete the - `install.lua` file to see your new board. Visit "/admin" to get started! -

-

Thank you for installing Lapis-chan! <3

-]] - -return { - GET = function(self) - self.page_title = "Install Lapis-chan" - self.board = { theme = "yotsuba_b" } - - -- Do we already have data? - local users = Users:get_users() - local boards = Boards:get_boards() - local pages = Pages:get_pages() - - -- We did it! - if #users > 0 and #boards > 0 and #pages > 0 then - return success - end - - -- Get list of themes - self.themes = {} - for file in lfs.dir("."..self.styles_url) do - local name, ext = string.match(file, "^(.+)(%..+)$") - if name ~= "reset" and - name ~= "posts" and - name ~= "style" and - name ~= "tegaki" and - ext == ".css" then - table.insert(self.themes, name) - end - end - - return { render = "install" } - end, - POST = function(self) - self.page_title = "Install Lapis-chan" - self.board = { theme = "yotsuba_b" } - - -- Do we already have data? - local users = Users:get_users() - local boards = Boards:get_boards() - local pages = Pages:get_pages() - - -- We did it! - if #users > 0 and #boards > 0 and #pages > 0 then - return success - end - - local errs = validate(self.params, { - { "user_username", exists=true, max_length=255 }, - { "user_password", min_length=4, max_length=255 }, - { "name", exists=true, max_length=10 }, - { "title", min_length=2, max_length=255 }, - { "subtext", max_length=255 }, - { "rules" }, - { "ban_message", max_length=255 }, - { "anon_name", max_length=255 }, - { "theme", exists=true }, - { "pages", exists=true }, - { "threads_per_page", exists=true }, - { "thread_file_limit", exists=true }, - { "post_limit", exists=true }, - { "thread_file", exists=true }, - { "thread_comment", exists=true }, - { "post_file", exists=true }, - { "post_comment", exists=true }, - { "text_only", exists=true }, - { "filetype_image", exists=true }, - { "filetype_audio", exists=true }, - { "draw", exists=true }, - { "archive", exists=true }, - { "archive_time", exists=true }, - { "group", exists=true } - }) - - local out - if errs then - out = "
\n" - for _, err in ipairs(errs) do - out = out .. "

" .. err .. "

\n" - end - out = out .. [[ -
- -
-
- ]] - end - - if out then - return out - end - - -- Add new user - Users:create_user { - username = self.params.user_username, - password = self.params.user_password, - admin = true, - mod = false, - janitor = false - } - - -- Add new board - Boards:create_board { - name = self.params.name, - title = self.params.title, - subtext = self.params.subtext, - rules = self.params.rules, - anon_name = self.params.anon_name, - theme = self.params.theme, - posts = 0, - pages = self.params.pages, - threads_per_page = self.params.threads_per_page, - text_only = self.params.text_only, - filetype_image = self.params.filetype_image, - filetype_audio = self.params.filetype_audio, - draw = self.params.draw, - thread_file = self.params.thread_file, - thread_comment = self.params.thread_comment, - thread_file_limit = self.params.thread_file_limit, - post_file = self.params.post_file, - post_comment = self.params.post_comment, - post_limit = self.params.post_limit, - archive = self.params.archive, - archive_time = self.params.archive_time * 24 * 60 * 60, - group = self.params.group - } - - -- Add FAQ page - Pages:create_page { - title = "Frequently Asked Questions", - slug = "faq", - content = faq - } - end -} diff --git a/misc/website/backend/app/src/apps/web/pages.lua b/misc/website/backend/app/src/apps/web/pages.lua deleted file mode 100755 index 54d4547..0000000 --- a/misc/website/backend/app/src/apps/web/pages.lua +++ /dev/null @@ -1,14 +0,0 @@ -local lapis = require "lapis" -local app = lapis.Application() -app.__base = app -app.name = "web.pages." -app.handle_404 = require "apps.web.internal.code_404" - -app:match("index", "/", require "apps.web.pages.index") -app:match("c404", "/404", require "apps.web.internal.code_404") -- FIXME: remove this route -app:match("faq", "/faq", require "apps.web.pages.rules") -- FIXME: need a faq page -app:match("rules", "/rules", require "apps.web.pages.rules") -app:match("logout", "/logout", require "apps.web.pages.logout") -app:match("page", "/:page", require "apps.web.pages.page") - -return app diff --git a/misc/website/backend/app/src/apps/web/pages/index.lua b/misc/website/backend/app/src/apps/web/pages/index.lua deleted file mode 100755 index 64c32e4..0000000 --- a/misc/website/backend/app/src/apps/web/pages/index.lua +++ /dev/null @@ -1,10 +0,0 @@ -return function(self) - - -- Page title - self.page_title = self.i18n("index") - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - return { render = "index" } -end diff --git a/misc/website/backend/app/src/apps/web/pages/logout.lua b/misc/website/backend/app/src/apps/web/pages/logout.lua deleted file mode 100755 index b7d0ccd..0000000 --- a/misc/website/backend/app/src/apps/web/pages/logout.lua +++ /dev/null @@ -1,9 +0,0 @@ -return function(self) - -- Logout - self.session.name = nil - self.session.admin = nil - self.session.mod = nil - self.session.janitor = nil - - return { redirect_to = self:url_for("web.pages.index") } -end diff --git a/misc/website/backend/app/src/apps/web/pages/page.lua b/misc/website/backend/app/src/apps/web/pages/page.lua deleted file mode 100755 index 36add7f..0000000 --- a/misc/website/backend/app/src/apps/web/pages/page.lua +++ /dev/null @@ -1,28 +0,0 @@ -local Boards = require "models.boards" -local Pages = require "models.pages" -local markdown = require "markdown" - -return function(self) - -- Get all board data - self.boards = Boards:get_boards() - - -- Get page - self.page = Pages:get_page(self.params.page) - - if not self.page then - return self:write({ redirect_to = self:url_for("web.pages.c404") }) - end - - -- Page title - self.page_title = self.page.title - - -- Markdown - if self.page.content then - self.page.content = markdown(self.page.content) - end - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - return { render = "page" } -end diff --git a/misc/website/backend/app/src/apps/web/pages/rules.lua b/misc/website/backend/app/src/apps/web/pages/rules.lua deleted file mode 100755 index f45e50e..0000000 --- a/misc/website/backend/app/src/apps/web/pages/rules.lua +++ /dev/null @@ -1,19 +0,0 @@ -return function(self) - - -- Page title - self.page_title = self.i18n("rules") - - -- Display a theme - self.board = { theme = "yotsuba_b" } - - for _, board in ipairs(self.boards) do - board.url = self:url_for("web.boards.board", { board=board.name }) - if board.rules then - board.rules = _G.markdown(board.rules) - else - board.rules = "" - end - end - - return { render = "rules" } -end diff --git a/misc/website/backend/app/src/locale/fr.lua b/misc/website/backend/app/src/locale/fr.lua deleted file mode 100755 index 654f586..0000000 --- a/misc/website/backend/app/src/locale/fr.lua +++ /dev/null @@ -1,234 +0,0 @@ -return { fr = { - - --==[[ Navigation ]]==-- - - archive = "Archive", - bottom = "Bas", - catalog = "Catalogue", - index = "Index", - refresh = "Rafraîchir", - ["return"] = "Retourner", - return_board = "Retourner au babillard", - return_index = "Retourner à l'index", - return_thread = "Retourner au fil de discussion", - top = "Haut", - - --==[[ Error Messages ]]==-- - - -- Controller error messages - err_ban_reason = "Aucune Reçue.", - err_board_used = "Le nom du Babillard est déjà utilisé.", - err_not_admin = "Vous n'êtes pas un administrateur.", - err_orphaned = "Le Fil de Discussion nº%s est orphelin.", - err_slug_used = "Le slug de la page est déjà utilisée.", - err_user_used = "Le nom d'utilisateur est dèja utilisé.", - - -- Model error messages - err_contribute = "Vous devez poster un commentaire ou un fichier.", - err_locked_thread = "Le fil de discussion No.%s est verrouillé.", - err_no_files = "Les fichiers ne sont pas acceptés sur ce babillard.", - - err_comment_post = "Un commentaire est requis pour poster sur ce babillard.", - err_comment_thread = "Un commentaire est requis pour poster un fil de discussion sur ce babillard.", - - err_create_ann = "Impossible de créer un annoncement: %s.", - err_create_ban = "Impossible de bannir l'IP: %s.", - err_create_board = "Impossible de créer le babillard: /%s/ - %s.", - err_create_page = "Impossible de créer la page: /%s/ - %s.", - err_create_post = "Impossible de soumettre le poste.", - err_create_report = "Impossible de reporter le post No.%s.", - err_create_thread = "Impossible de créer un nouveau fil de discussion.", - - err_delete_board = "Impossible de supprimer le babillard: /%s/ - %s.", - err_delete_post = "Impossible de supprimer le poste No.%s.", - err_create_user = "Impossible de créer l'utilisateur: %s.", - - err_file_exists = "Le fichier existe déjà sur ce babillard.", - err_file_limit = "Le fil de discussion No.%s a atteint sa limite de fichier.", - err_file_post = "Un fichier est requis pour poster sur ce babillard.", - err_file_thread = "Un fichier est requis pour poster un fil de discussion sur ce babillard.", - - err_invalid_board = "Babillard invalide: /%s/.", - err_invalid_ext = "Type de fichier invalide: %s.", - err_invalid_image = "Données d'image invalides.", - err_invalid_post = "Le poste No.%s n'est pas valide.", - err_invalid_user = "Nom d'utilisateur ou mot de passe invalide.", - - --==[[ 404 ]]==-- - - ["404"] = "404 - Page Introuvable", - - --==[[ Administration ]]==-- - - -- General - admin_panel = "Panneau Administratif", - administrator = "Administrateur", - announcement = "Annoncement", - archive_days = "Nombre de jours à garder les Fils de Discussion dans l'Archive", - archive_pruned = "Archiver les Fils de Discussion Réduites", - board = "Babillard", - board_group = "Group de babillard", - board_title = "Nom de Babillard", - bump_limit = "Limite pour remonter un fil de discussion", - content_md = "Contenu (Markdown)", - default_name = "Nom par Défaut", - draw_board = "Babillard à Dessin", - file = "Fichier", - file_limit = "Limite de fichiers dans un Fil de Discussion", - global = "Global", - index_boards = "Babillards Présent", - janitor = "Concierge", - login = "Connexion", - logout = "Déconnexion", - moderator = "Modérateur", - num_pages = "Pages Actives", - num_threads = "Fils de Discussion par Page", - password = "Mot de Passe", - password_old = "Ancient Mot de Passe", - password_retype = "Confirmer le Mot de Passe", - post_comment_required = "Exiger un commentaire pour pouvoir poster", - post_file_required = "Exiger un Fichier pour pouvoir poster", - regen_thumb = "Régénérer les Miniatures", - reply = "Répondre", - rules = "Les Règles", - name = "Nom", - subtext = "Sous-texte", - success = "Succès", - text_board = "Babillard à Texte", - theme = "Thème", - thread_comment_required = "Exiger un Commentaire ", - thread_file_required = "Exiger un Fil de Discussion", - slug = "Slug", - username = "Nom d'utilisateur", - yes = "Oui", - no = "Non", - - -- Announcements - create_ann = "Créer un Annoncement", - modify_ann = "Modifier un Annoncement", - delete_ann = "Supprimer un Annoncement", - created_ann = "Création de l'annoncement: %s réussie.", - modified_ann = "Modification de l'annoncement: %s réussie.", - deleted_ann = "Suppression de l'annoncement: %s réussie.", - - -- Boards - create_board = "Créer un Babillard", - modify_board = "Modifier un Babillard", - delete_board = "Supprimer un Babillard", - created_board = "Création du Babillard: /%s/ - %s réussie.", - modified_board = "Modification du Babillard : /%s/ - %s réussie.", - deleted_board = "Suppression du Babillard : /%s/ - %s réussie.", - - -- Pages - create_page = "Créer une Page", - modify_page = "Modifier une Page", - delete_page = "Supprimer une Page", - created_page = "Création de la page : /%s/ - %s réussie.", - modified_page = "Modification de la page : /%s/ - %s réussie.", - deleted_page = "Suppression de la page : /%s/ - %s réussie.", - - -- Reports - view_report = "Afficher un Rapport", - delete_report = "Supprimer un Rapport", - deleted_report = "Suppression du Rapport: %s réussie.", - - -- Users - create_user = "Créer un Utilisateur", - modify_user = "Modifier un Utilisateur", - delete_user = "Supprimer un Utilisateur", - created_user = "Création de l'utilisateur: %s réussie.", - modified_user = "Modification de l'utilisateur: %s réussie.", - deleted_user = "Suppression de l'utilisateur: %s réussie.", - - --==[[ Archive ]]==-- - - arc_display = "Affichage de %{n_thread} %{p_thread} expirés depuis %{n_day} %{p_day} ", - arc_number = "nº", - arc_name = "Nom", - arc_excerpt = "Extrait", - arc_replies = "Réponses", - arc_view = "Afficher", - - --==[[ Ban ]]==-- - - ban_title = "Banni!", - ban_reason = "Vous avez été banni pour la raison suivante:", - ban_expire = "Votre ban expirera le %{expire}.", - ban_ip = "Selon avec notre serveur, votre IP est: %{ip}.", - - --==[[ Catalog ]]==-- - - cat_stats = "R: %{replies} / F: %{files}", - - --==[[ Copyright ]]==-- - - copy_software = "Réalisé avec %{software} %{version}", - copy_download = "Télécharger à partir de %{github}", - - --==[[ Forms ]]==-- - - form_ban = "Bannir l'Utilisateur", - form_ban_display = "Afficher le Ban", - form_ban_board = "Ban Local", - form_ban_reason = "Raison du ban", - form_ban_time = "Durée (en jours) à bannir l'utilisateur", - form_clear = "Effacer", - form_delete = "Supprimer le Poste", - form_draw = "Dessiner", - form_lock = "Verrouiller le Fil de Discussion", - form_override = "Fichiers Illimités", - form_readme = "Veuillez lire [%{rules}] et la [%{faq}] avant de poster.", - form_remix = "Remixer l'Image", - form_report = "Reporter le Poste", - form_required = "Champ Requis", - form_save = "Épargner le Fil de Discussion", - form_sticky = "Épingler le Fil de Discussion", - form_submit = "Soumettre le Poste", - form_submit_name = "Nom", - form_submit_name_help = "Donnez-vous un nom , un tripcode ou les deux (facultatif)", - form_submit_subject = "Sujet", - form_submit_subject_help = "Définir le sujet de la discussion (facultatif)", - form_submit_options = "Options", - form_submit_options_help = "Sage: poster sans faire monter le fil de discussion (À venir) (facultatif)", - form_submit_comment = "Commentaire", - form_submit_comment_help = "Contribuer à la discussion (ou non)", - form_submit_file = "Fichier", - form_submit_file_help = "Télécharger un fichier", - form_submit_draw = "Dessiner", - form_submit_draw_help = "Dessiner ou remixer une image", - form_submit_spoiler = "Spoiler", - form_submit_spoiler_help = "Replacer la miniature avec une image sans spoiler", - form_submit_mod = "Modérateur", - form_submit_mod_help = "Mettre un drapeau sur ce fil de discussion", - form_width = "Largeur", - form_height = "Hauteur", - - --==[[ Posts ]]==-- - - post_link = "Lier à ce poste", - post_lock = "Le fil de discussion est verrouillé", - post_hidden = "%{n_post} %{p_post} et %{n_file} %{p_file} omis. %{click} pour afficher.", - post_override = "Ce fil de discussion accepte un nombre illimité de fichiers", - post_reply = "Répondre à ce poste", - post_sticky = "Le fil de discussion est épinglé", - post_save = "Le fil de discussion ne sera pas enlevé automatiquement", - - --==[[ Plurals ]]==-- - - days = { - one = "jour", - other = "jours" - }, - files = { - one = "fichier", - other = "fichiers" - }, - posts = { - one = "poste", - other = "postes" - }, - threads = { - one = "fil de discussion", - other = "fils de discussion" - }, -}} diff --git a/misc/website/backend/app/src/locale/phpceo.lua b/misc/website/backend/app/src/locale/phpceo.lua deleted file mode 100755 index 8f399d9..0000000 --- a/misc/website/backend/app/src/locale/phpceo.lua +++ /dev/null @@ -1,234 +0,0 @@ -return { phpceo = { - - --==[[ Navigation ]]==-- - - archive = "Gallery Of Our Greatness", - bottom = "Best For Last", - catalog = "Products And Services", - index = "Home Page", - refresh = "Experience A Rejuvinating Meeting", - ["return"] = "Reconsider", - return_board = "Reconsider that product", - return_index = "Reconsider that home page", - return_thread = "Reconsider that email chain", - top = "I'm The Best", - - --==[[ Error Messages ]]==-- - - -- Controller error messages - err_ban_reason = "Hit 'enter' too early.", - err_board_used = "That product name is in use, and we don't want to spend money on lawyers.", - err_not_admin = "Call HR.", - err_orphaned = "I accidentally lost the email chain about %s, do you know where it went?", - err_slug_used = "SEO CONFLICT, GOOGLE GROWTHHACK ERROR.", - err_user_used = "You can't be Brandon, *I'm* Brandon!", - - -- Model error messages - err_contribute = "Your argument needs more pictures or more words. I can't decide which. I'm fickle like that.", - err_locked_thread = "Email chain about %s is locked, I blame Outlook.", - err_no_files = "The cloud is busy, it does not want your attachments.", - - err_comment_post = "Your pitch is great, except I don't know what you're talking about. Use your words.", - err_comment_thread = "Did you seriously just try to send an email without a body? At least put in your signature. Have some pride, Intern.", - - err_create_ann = "I couldn't stop yelling long enough to say: %s.", - err_create_ban = "These lottery numbers didn't work: %s.", - err_create_board = "I don't know how to make the product: /%s/ - %s.", - err_create_page = "Pages? No, we're paperless now, we don't need: /%s/ - %s.", - err_create_post = "I didn't read your email, let's set up a meeting in a few minutes where you read it to me.", - err_create_report = "I don't see anything wrong with an email about %s and I have no inclination to in my life.", - err_create_thread = "The reply button is broke! This is effecting production.", - - err_delete_board = "I tried to delete those emails about: /%s/ - but I just couldn't %s.", - err_delete_post = "I clicked the flag, did that delete %s? No? Well what else am I supposed to do?", - err_create_user = "I don't know who %s is and frankly I don't give a big enough damn to learn.", - - err_file_exists = "Johnson! I already have that spreadsheet! I think. Probably.", - err_file_limit = "THE CLOUD IS FULL, IF YOU SUBMIT ANYTHING ELSE TO %s THE INTERNET WILL GO DOWN.", - err_file_post = "If you want my attention you better use something other than words because I sure as hell ain't reading anything that comes across this desk today.", - err_file_thread = "This email chain said 'post your favorite confidential documents' but you forgot to attach any. Try that one again.", - - err_invalid_board = "CORRUPT EMAIL: /%s/.", - err_invalid_ext = "Do I look like I know what a %s is?", - err_invalid_image = "It looks like you sent a picture but what I got was a digital clown.", - err_invalid_post = "Whatever %s was about, I've decided to politely disregard it as 'wrong opinion'.", - err_invalid_user = "I don't know who you are or what you're doing. I'm calling the police.", - - --==[[ 404 ]]==-- - - ["404"] = "404 - That's 3 better than a 401k!", - - --==[[ Administration ]]==-- - - -- General - admin_panel = "CEO DASHBOARD", - administrator = "CEO", - announcement = "Important things that come out of my mouth", - archive_days = "Days to Archive Threads", - archive_pruned = "Gas these.", - board = "Chain", - board_group = "Chain Group", - board_title = "Email Subject", - bump_limit = "Burp Excusal Tolerance", - content_md = "Markers", - default_name = "What is this?", - draw_board = "MSPAINT but for the Internet", - file = "Datum gap", - file_limit = "Maximum Cloud Precipitation Ratio", - global = "Everyone Has To Deal With", - index_boards = "Product Selection", - janitor = "Unpaid Intern", - login = "Clock In", - logout = "Clock Out", - moderator = "Enforcer", - num_pages = "Reasons this company is great", - num_threads = "How much I can stand of this", - password = "Digital Hash Salt", - password_old = "That old thing.", - password_retype = "Do it again, it'll be funny.", - post_comment_required = "Need Context", - post_file_required = "Insert Meme", - regen_thumb = "Rectify Pixels", - reply = "Interject", - rules = "Things That Don't Apply To Me", - name = "Bob", - subtext = "sub-who?", - success = "Me, The Physical Embodiment of Greatness", - text_board = "Stuff I Won't Read", - theme = "Birthday Party", - thread_comment_required = "CONVERSE BEFORE CLOUD", - thread_file_required = "INSERT FILE FOR CLOUD", - slug = "CLOUD RESOURCE IDENTIFIER", - username = "User Identifcation String", - yes = "I Am Glad To Blindly Accept This", - no = "Not Exactly", - - -- Announcements - create_ann = "Open Mouth, Insert Foot", - modify_ann = "Damage Control", - delete_ann = "I never said that, you can't prove it.", - created_ann = "I believe %s is the lifeblood of this company.", - modified_ann = "What I meant to say was actually %s.", - deleted_ann = "What announcement? %s you say? Doesn't ring a bell.", - - -- Boards - create_board = "More Email!", - modify_board = "Different Email", - delete_board = "Less Email!", - created_board = "How do you feel about /%s/ - %s? How about agilefall?", - modified_board = "I hope we fixed the issue about /%s/ - %s because I was getting tired of covering for him.", - deleted_board = "I forgot I don't even like /%s/ - %s.", - - -- Pages - create_page = "Create new effigy to my greatness.", - modify_page = "Make this less bad.", - delete_page = "I ain't readin' this.", - created_page = "I heard about /%s/ - %s online, let me tell you how we can pivot this.", - modified_page = "I saw some stuff about /%s/ - %s so I fixed it.", - deleted_page = "Good news, I got rid of those books about: /%s/ - %s.", - - -- Reports - view_report = "I said *what*?", - delete_report = "Forget about that.", - deleted_report = "I changed my mind about %s and you can too.", - - -- Users - create_user = "Hire", - modify_user = "Rectify Person", - delete_user = "Get Rid Of", - created_user = "So %s is this bright young talent we've been hearing about.", - modified_user = "Whatever was wrong with %s we squared away.", - deleted_user = "I got rid of that %s character for you.", - - --==[[ Archive ]]==-- - - arc_display = "Displaying %{n_thread} expired %{p_thread} from the past %{n_day} %{p_day}", - arc_number = "In Britan they say 'pound'.", - arc_name = "Who?", - arc_excerpt = "TL;DR", - arc_replies = "Responses", - arc_view = "Investigate", - - --==[[ Ban ]]==-- - - ban_title = "Not allowed back!", - ban_reason = "I decided I don't like you anymore, because: ", - ban_expire = "I might forget about this on %{expire}.", - ban_ip = "And your stupid raffle ticket was: %{ip}.", - - --==[[ Catalog ]]==-- - - cat_stats = "R: %{replies} / F: %{files}", - - --==[[ Copyright ]]==-- - - copy_software = "You paid HOW MUCH for %{software}? And it's only version %{version}?! We should have used phpBB, it's way more mature.", - copy_download = "Negotiate a license from %{github}", - - --==[[ Forms ]]==-- - - form_ban = "Escort Off Property", - form_ban_display = "Not allowed in public", - form_ban_board = "Not allowed in my hosue", - form_ban_reason = "Why don't I like this person", - form_ban_time = "How long (in digital ages) to pretend I don't know this guy.", - form_clear = "Forget About It", - form_delete = "Hide Evidence", - form_draw = "Sketch Out", - form_lock = "END THIS", - form_override = "CLOUD IS LOOSE AND HUNGRY FOR DATA", - form_readme = "Frankly, I don't read the [%{rules}] and [%{faq}] so I won't ask you to, either. Hell, I don't even know what they say. And I wrote 'em!", - form_remix = "CLAIM AS YOUR OWN", - form_report = "I DON'T LIKE IT", - form_required = "MANDATORY IF YOU WANT TO KEEP WORKING HERE", - form_save = "Fwd to Offshore Account", - form_sticky = "IMMORTIALIZE SHAME", - form_submit = "HEMMORAGE BRILLIANCE", - form_submit_name = "EMPLOYEE ID NUMBER (PRE-ACQUISITION)", - form_submit_name_help = "Who are you again? A trip-what?", - form_submit_subject = "What Am I On About? Oh, Right.", - form_submit_subject_help = "What I've brought you all here today to discuss", - form_submit_options = "Stock Options", - form_submit_options_help = "sage: Don't let anyone know you're desperate for attention.", - form_submit_comment = "What you have to say.", - form_submit_comment_help = "Tell us about your great idea.", - form_submit_file = "DATUMS", - form_submit_file_help = "FOREFIT YOUR DATA TO THE CLOUD", - form_submit_draw = "MSPAINT", - form_submit_draw_help = "MSPAINT OR GIMPIFY", - form_submit_spoiler = "Paywalled Content", - form_submit_spoiler_help = "Increase viewer engagement 10%%", - form_submit_mod = "Enforcer of Marketability", - form_submit_mod_help = "Mark as landmine. Minesweeper humor. It means I think there's a bomb here.", - form_width = "Digital Horizon", - form_height = "Digital Embiggenment", - - --==[[ Posts ]]==-- - - post_link = "FORWARD THIS EMAIL", - post_lock = "EMAIL IS IN ARCHIVE MODE, STOP REPLYING TO THINGS I FORGOT ABOUT.", - post_hidden = "%{n_post} %{p_post} and %{n_file} %{p_file} omitted. %{click} to view.", - post_override = "CLOUD STORAGE ENABLED", - post_reply = "Reply to this email", - post_sticky = "reply-all'd, cc'd the company, 10/10", - post_save = "I refuse to cut down on the fat, on account of all the words I said being fluff meant to distract you.", - - --==[[ Plurals ]]==-- - - days = { - one = "digital age", - other = "digital aegis" - }, - files = { - one = "datum", - other = "datums" - }, - posts = { - one = "email", - other = "emailadoodles" - }, - threads = { - one = "email chain", - other = "clusterfuck" - }, -}} diff --git a/misc/website/backend/app/src/locale/pl.lua b/misc/website/backend/app/src/locale/pl.lua deleted file mode 100755 index bdadaf3..0000000 --- a/misc/website/backend/app/src/locale/pl.lua +++ /dev/null @@ -1,240 +0,0 @@ -return { pl = { - - --==[[ Navigation ]]==-- - - archive = "Archiwum", - bottom = "Przewiń na dół", - catalog = "Katalog", - index = "Spis treści", - refresh = "Odśwież", - ["return"] = "Powróć", - return_board = "Powróć do boardu", - return_index = "Powróć do spisu treści", - return_thread = "Powróć do wątku", - top = "Przewiń na górę", - - --==[[ Error Messages ]]==-- - - -- Controller error messages - err_ban_reason = "Brak powodu.", - err_board_used = "Nazwa boardu jest już w użyciu.", - err_not_admin = "Nie jesteś administratorem.", - err_orphaned = "Wątek %s został osierocony.", - err_slug_used = "Adres slug jest już w użyciu.", - err_user_used = "Nazwa użytkownika jest już w użyciu.", - - -- Model error messages - err_contribute = "Musisz załączyć treść posta lub załącznik.", - err_locked_thread = "Wątek %s jest zablokowany.", - err_no_files = "Dodawanie plików jest wyłączone na tym boardzie.", - - err_comment_post = "Na tym boardzie, by odpowiedzieć na wątek, musisz zamieścić komentarz.", - err_comment_thread = "Na tym boardzie, by stworzyć wątek, musisz zamieścić komentarz.", - - err_create_ann = "Nie można utworzyć ogłoszenia: %s.", - err_create_ban = "Nie można zbananować IP: %s.", - err_create_board = "Nie można utworzyć boarda: /%s/ - %s.", - err_create_page = "Nie można utworzyć strony: /%s/ - %s.", - err_create_post = "Nie można wysłać posta.", - err_create_report = "Nie można zgłosić posta %s.", - err_create_thread = "Nie można utworzyć wątku.", - - err_delete_board = "Nie można usunąć boarda: /%s/ - %s.", - err_delete_post = "Nie można usunąć posta %s.", - err_create_user = "Nie można utworzyć użytkownika: %s.", - - err_file_exists = "Ten plik już istnieje na tym boardzie.", - err_file_limit = "Wątek %s osiągnął już swój limit plików.", - err_file_post = "Na tym boardzie, by odpowiedzieć na wątek, musisz załączyć plik.", - err_file_thread = "Na tym boardzie, by stworzyć wątek, musisz załączyć plik.", - - err_invalid_board = "Nieznany board: /%s/.", - err_invalid_ext = "Nieznany typ pliku: %s.", - err_invalid_image = "Załączony plik nie jest prawidłowym obrazkiem.", - err_invalid_post = "Post %s nie jest poprawnym postem.", - err_invalid_user = "Niepoprawna nazwa użytkownika lub hasło.", - - --==[[ 404 ]]==-- - - ["404"] = "404 - Strona nie znaleziona", - - --==[[ Administration ]]==-- - - -- General - admin_panel = "Panel cwela", - administrator = "Cwel", - announcement = "Ogłoszenie", - archive_days = "Dni do archiwizowania wątków", - archive_pruned = "Archiwizuj wątki", - board = "Board", - board_group = "Grupa boarda", - board_title = "Nazwa boarda", - bump_limit = "Limit przyjebek", - content_md = "Opis (Markdown)", - default_name = "Domyślna nazwa", - draw_board = "Board do rysowania", - file = "Plik", - file_limit = "Limit plików w wątku", - global = "Globalnie", - index_boards = "Aktualne boardy", - janitor = "Woźny", - login = "Zaloguj", - logout = "Wyloguj", - moderator = "Moderator", - num_pages = "Aktywne strony", - num_threads = "Fredy na stronę", - password = "Hasło", - password_old = "Powtórz hasło", - password_retype = "Stare hasło", - post_comment_required = "Wymagany komentarz do posta", - post_file_required = "Wymagany plik do posta", - regen_thumb = "Odśwież miniaturki", - reply = "Odpowiedz", - rules = "Zasady", - name = "Nazwa", - subtext = "Podtekst", - success = "Sukces", - text_board = "Board tekstowy", - theme = "Motyw", - thread_comment_required = "Wymagany komentarz do freda", - thread_file_required = "Wymagany plik do freda", - slug = "Slug", - username = "Nazwa użytkownika", - yes = "Tak", - no = "Nie", - - -- Announcements - create_ann = "Utwórz ogłoszenie", - modify_ann = "Zmień ogłoszenie", - delete_ann = "Wyjeb ogłoszenie", - created_ann = "Utworzyłeś ogłoszenie %s.", - modified_ann = "Zmieniłeś ogłoszenie %s.", - deleted_ann = "Wyjebałeś ogłoszenie %s.", - - -- Boards - create_board = "Utwórz boarda", - modify_board = "Zmień boarda", - delete_board = "Wyjeb boarda", - created_board = "Utworzyłeś boarda /%s/ - %s.", - modified_board = "Zmieniłeś boarda /%s/ - %s.", - deleted_board = "Wyjebałeś boarda /%s/ - %s.", - - -- Pages - create_page = "Utwórz stronę", - modify_page = "Zmień stronę", - delete_page = "Wyjeb stronę", - created_page = "Utworzyłeś stronę /%s/ - %s.", - modified_page = "Zmieniłeś stronę /%s/ - %s.", - deleted_page = "Wyjebałeś stronę /%s/ - %s.", - - -- Reports - view_report = "Przejrzyj zgłoszenie", - delete_report = "Usuń zgłoszenie", - deleted_report = "Wyjebałeś zgłoszenie %s.", - - -- Users - create_user = "Utwórz użytkownika", - modify_user = "Zmień użytkownika", - delete_user = "Wyjeb użytkownika", - created_user = "Utworzyłeś użytkownika %s.", - modified_user = "Zmieniłeś użytkownika %s.", - deleted_user = "Wyjebałeś użytkownika %s.", - - --==[[ Archive ]]==-- - - arc_display = "Wyświetlanie %{n_thread} %{p_thread} (zarchiwizowane) z %{n_day} %{p_day}", - arc_number = "nr ", - arc_name = "Nazwa", - arc_excerpt = "Kawał freda", - arc_replies = "Odpowiedzi", - arc_view = "Wyślij", - - --==[[ Ban ]]==-- - - ban_title = "Zbanowany!", - ban_reason = "Zostałeś zbananowany z powodu o takiego:", - ban_expire = "Twój banan usunie się %{expire}.", - ban_ip = "Według naszych serwerów NASA, twoje IP to %{ip}.", - - --==[[ Catalog ]]==-- - - cat_stats = "Odp.: %{replies} / plików: %{files}", - - --==[[ Copyright ]]==-- - - copy_software = "Fredy napędzane przez %{software} %{version}", - copy_download = "Pobierz z %{github}", - - --==[[ Forms ]]==-- - - form_ban = "Zbanuj użytkownika", - form_ban_display = "Wyświetl bana", - form_ban_board = "Ban lokalny", - form_ban_reason = "Powód bana", - form_ban_time = "Długość bana (w minutach)", - form_clear = "Wyczyść", - form_delete = "Usuń posta", - form_draw = "Rysuj", - form_lock = "Zablokuj wątek", - form_override = "Nielimitowane pliki", - form_readme = "Przeczytaj [%{rules}] oraz [%{faq}] zanim zapostujesz.", - form_remix = "Przerób obrazek", - form_report = "Zgłoś posta", - form_required = "pole wymagane", - form_save = "Zapisz wątek", - form_sticky = "Przyklej freda", - form_submit = "Wyślij posta", - form_submit_name = "Pseudonim", - form_submit_name_help = "Nadaj sobie nazwę lub tripkod (opcjonalne)", - form_submit_subject = "Temat", - form_submit_subject_help = "Temat dyskusji (opcjonalne)", - form_submit_options = "Opcje", - form_submit_options_help = "sage: zapostuj bez podbijania freda (opcjonalne)", - form_submit_comment = "Komentarz", - form_submit_comment_help = "Dodaj coś do dyskusji", - form_submit_file = "Plik", - form_submit_file_help = "Załącz plik", - form_submit_draw = "Rysuj", - form_submit_draw_help = "Narysuj lub przerób obrazek", - form_submit_spoiler = "Spoiler", - form_submit_spoiler_help = "Podmień miniaturkę na spoiler", - form_submit_mod = "Moderator", - form_submit_mod_help = "Oznacz tego freda", - form_width = "Szerokość", - form_height = "Wysokość", - - --==[[ Posts ]]==-- - - post_link = "Link do tego posta", - post_lock = "Wątek jest zablokowany", - post_hidden = "%{n_post} %{p_post} i %{n_file} %{p_file} zostały pominięte. %{click} aby wyświetlić.", - post_override = "Wątek akceptuje nielimitowaną ilość plików", - post_reply = "Odpowiedz na ten post", - post_sticky = "Fred jest przyklejony", - post_save = "Wątek nie będzie archiwizowany", - - --==[[ Plurals ]]==-- - - days = { - one = "dzień", - other = "dni" - }, - files = { - one = "plik", - few = "pliki", - many = "plików", - other = "pliki" - }, - posts = { - one = "post", - few = "posty", - many = "postów", - other = "posty" - }, - threads = { - one = "wątek", - few = "wątki", - many = "wątków", - other = "wątki" - }, -}} diff --git a/misc/website/backend/app/src/models/announcements.lua b/misc/website/backend/app/src/models/announcements.lua deleted file mode 100755 index 35c9fbf..0000000 --- a/misc/website/backend/app/src/models/announcements.lua +++ /dev/null @@ -1,96 +0,0 @@ -local Model = require("lapis.db.model").Model -local Announcements = Model:extend("announcements", { - relations = { - { "board", belongs_to="Boards" } - } -}) - -Announcements.valid_record = { - { "board_id", is_integer=true }, - { "text", exists=true } -} - ---- Create an announcement --- @tparam table params Announcement parameters --- @treturn boolean success --- @treturn string error -function Announcements:new(params) - local announcement = self:create(params) - return announcement and announcement or nil, { "err_create_ann", { params.text } } -end - ---- Modify an announcement --- @tparam table params Announcement parameters --- @treturn boolean success --- @treturn string error -function Announcements:modify(params) - local announcement = self:get(params.id) - if not announcement then - return nil, { "err_create_ann", { params.text } } -- FIXME: wrong error - end - - local success, err = announcement:update(params) - return success and announcement or nil, "FIXME: " .. tostring(err) -end - ---- Delete an announcement --- @tparam number id Announcement ID --- @treturn boolean success --- @treturn string error -function Announcements:delete(id) - local announcement = self:get(id) - if not announcement then - return nil, "FIXME" - end - - local success = announcement:delete() - return success and announcement or nil, "FIXME" -end - ---- Get all announcements --- @treturn boolean success --- @treturn string error -function Announcements:get_all() - local announcements = self:select("order by board_id asc") - return announcements -end - ---- Get announcements --- @treturn boolean success --- @treturn string error -function Announcements:get_global() - local announcements = self:select("where board_id=0") - return announcements -end - ---- Get board announcements --- @tparam number board_id Board ID --- @treturn boolean success --- @treturn string error -function Announcements:get_board(board_id) - local announcements = self:select("where board_id=?", board_id) - return announcements -end - ---- Get announcement --- @tparam number id Announcement ID --- @treturn boolean success --- @treturn string error -function Announcements:get(id) - local announcement = self:find(id) - return announcement and announcement or nil, "FIXME" -end - -function Announcements.format_to_db(_, params) - if not params.board_id then - params.board_id = 0 - end -end - -function Announcements.format_from_db(_, params) - if params.board_id == 0 then - params.board_id = nil - end -end - -return Announcements diff --git a/misc/website/backend/app/src/models/bans.lua b/misc/website/backend/app/src/models/bans.lua index 66e6c8f..7d6cd68 100755 --- a/misc/website/backend/app/src/models/bans.lua +++ b/misc/website/backend/app/src/models/bans.lua @@ -1,151 +1,23 @@ -local Model = require("lapis.db.model").Model -local Bans = Model:extend("bans", { - relations = { - { "board", belongs_to="Boards" }, - } -}) - -Bans.valid_record = { - { "board_id", is_integer=true }, - { "ip", max_length=255, exists=true }, - { "time", exists=true } -} - ---- Create a ban --- @tparam table params Ban parameters --- @treturn boolean success --- @treturn string err -function Bans:new(params) - local ban = self:create(params) - return ban and ban or nil, { "err_create_ban", { params.ip } } -end - ---- Modify a ban --- @tparam table params Board parameters --- @treturn boolean success --- @treturn string error -function Bans:modify(params) - local ban = self:get(params.id) - if not ban then - return nil, { "err_create_board", { params.name, params.title } } -- FIXME: wrong error message - end - - local success, err = ban:update(params) - return success and ban or nil, "FIXME: " .. tostring(err) -end - ---- Delete a ban --- @tparam number id Ban's ID --- @treturn boolean success --- @treturn string err -function Bans:delete(id) - local ban = self:get(id) - if not ban then - return nil, "FIXME" - end - - local success = ban:delete() - return success and ban or nil, "FIXME" -end - ---- Get all bans --- @treturn table users List of bans -function Bans:get_all() - local bans = self:select("order by board_id asc, time + duration desc, ip asc") - - for i=#bans, 1, -1 do - local ban = bans[i] - if not self:validate(ban) then - table.remove(bans, i) - end - end - - return bans -end - ---- Get board bans --- @treturn table users List of bans -function Bans:get_board(board_id) - local bans = self:select("where board_id=? order by board_id asc, time + duration desc, ip asc", board_id) - - for i=#bans, 1, -1 do - local ban = bans[i] - if not self:validate(ban) then - table.remove(bans, i) - end - end - - return bans -end - ---- Get ban data --- @tparam number id Ban's ID --- @treturn table ban -function Bans:get(id) - local ban = self:find(id) - if not ban then - return nil, "FIXME: ALART!" - end - - local valid, err = self:validate(ban) - if not valid then - return nil, err - end - - return ban -end - ---- Get bans for specific ip --- @tparam string ip IP address --- @treturn table ban -function Bans:get_ip(ip) - local bans = self:select("where ip=?", ip) - - for i=#bans, 1, -1 do - local ban = bans[i] - if not self:validate(ban) then - table.remove(bans, i) - end - end - - return bans -end - ---- Validate ban --- @tparam table ban Ban data --- @treturn boolean valid -function Bans:validate(ban) - local time = os.time() - local finish = ban.time + ban.duration - - if time >= finish then - self:delete(ban) - return nil, "FIXME: ban has exired" - end - - return true -end - ---- Format ban paramaters for DB insertion --- @tparam table params Ban parameters -function Bans.format_to_db(_, params) - -- Convert duration from days to seconds - params.duration = (tonumber(params.duration) or 0) * 86400 - - if not params.board_id then - params.board_id = 0 - end -end - ---- Format ban parameters for User consumption --- @tparam table params Ban parameters -function Bans.format_from_db(_, params) - -- Convert duration from seconds to days - params.duration = tonumber(params.duration) / 86400 - - if params.board_id == 0 then - params.board_id = nil - end -end - -return Bans +local lapis = require "lapis" +local capture = require("lapis.application").capture_errors_json +local r2 = require("lapis.application").respond_to +local handle = require("utils.error").handle +local app = lapis.Application() +app.__base = app +app.name = "api.bans." +app.path = "/api/bans" + +app:match("bans", "", capture({ + on_error = handle, + r2(require "apps.api.bans.bans"), +})) +app:match("ban", "/:uri_ban[%d]", capture({ + on_error = handle, + r2(require "apps.api.bans.ban"), +})) +app:match("bans_ip", "/ip/:uri_ip", capture({ + on_error = handle, + r2(require "apps.api.bans.bans_ip"), +})) + +return app diff --git a/misc/website/backend/app/src/models/boards.lua b/misc/website/backend/app/src/models/boards.lua deleted file mode 100755 index 46ee3ce..0000000 --- a/misc/website/backend/app/src/models/boards.lua +++ /dev/null @@ -1,226 +0,0 @@ -local db = require "lapis.db" -local giflib = require "giflib" -local lfs = require "lfs" -local magick = require "magick" -local Model = require("lapis.db.model").Model -local Boards = Model:extend("boards", { - relations = { - { "announcements", has_many="Announcements" }, - { "bans", has_many="Bans" }, - { "posts", has_many="Posts" }, - { "reports", has_many="Reports" }, - { "threads", has_many="Threads", where={ archive=false }, order="sticky desc, last_active desc" }, - { "archived", has_many="Threads", where={ archive=true }, order="last_active desc" }, - } -}) - -Boards.valid_record = { - { "name", max_length=255, exists=true }, - { "title", max_length=255, exists=true }, - { "subtext", max_length=255 }, - { "ban_message", max_length=255 }, - { "anon_name", max_length=255 }, - { "theme", max_length=255 }, - { "pages", exists=true }, - { "threads_per_page", exists=true }, - { "thread_file_limit", exists=true }, - { "post_limit", exists=true }, - { "archive_time", exists=true }, - { "group", exists=true } -} - ---- Create a board --- @tparam table params Board parameters --- @treturn boolean success --- @treturn string error -function Boards:new(params) - local board = self:create(params) - if not board then - return false, { "err_create_board", { params.name, params.title } } - end - - lfs.mkdir(string.format("./static/%s/", board.name)) - return board -end - ---- Modify a board --- @tparam table params Board parameters --- @tparam old_name Board's current short name --- @treturn boolean success --- @treturn string error -function Boards:modify(params, old_name) - local board = self:get(old_name) - if not board then - return false, { "err_create_board", { params.name, params.title } } -- FIXME: wrong error message - end - - local success, err = board:update(params) - if not success then - return false, "FIXME: " .. tostring(err) - end - - if board.name ~= old_name then - local old = string.format("./static/%s/", old_name) - local new = string.format("./static/%s/", board.name) - os.rename(old, new) - end - - return board -end - ---- Delete a board --- @tparam string name Board's short name --- @treturn boolean success --- @treturn string error -function Boards:delete(name) - local board = self:get(name) - if not board then - return false, { "err_create_board", { name, name } } -- FIXME: wrong error message - end - - local announcements = board:get_announcements() - local bans = board:get_bans() - local posts = board:get_posts() - local reports = board:get_reports() - local threads = board:get_threads() - local dir = string.format("./static/%s/", board.name) - - -- Clear data - for _, announcement in ipairs(announcements) do announcement:delete() end - for _, ban in ipairs(bans) do ban:delete() end - for _, post in ipairs(posts) do post:delete() end - for _, report in ipairs(reports) do report:delete() end - for _, thread in ipairs(threads) do thread:delete() end - - -- Clear filesystem - if lfs.attributes(dir, "mode") == "directory" then - -- Delete files - for file in lfs.dir(dir) do - os.remove(dir .. file) - end - - -- Delete directory - lfs.rmdir(dir) - end - - -- Clear board - local success = board:delete() - return success and board or false, { "err_delete_board", { board.name, board.title } } -end - ---- Get all boards --- @treturn table boards -function Boards:get_all() - local boards = self:select("order by boards.group asc, name asc") - return boards and boards or false, "FIXME: ALART!" -end - ---- Get board data --- @tparam string name Board's short name --- @treturn table board -function Boards:get(name) - local board = self:find { name=name } - return board and board or false, "FIXME: ALART!" -end - ---- Format board paramaters for DB insertion --- @tparam table params Board parameters -function Boards.format_to_db(_, params) - -- Convert archive_time from days to seconds - params.archive_time = (tonumber(params.archive_time) or 0) * 86400 -end - ---- Format board parameters for User consumption --- @tparam table params Board parameters -function Boards.format_from_db(_, params) - -- Convert archive_time from seconds to days - params.archive_time = tonumber(params.archive_time) / 86400 -end - ---- Regenerate thumbnails for all posts --- @treturn none -function Boards.regen_thumbs(_) - local sql = [[ - select - boards.name as board, - posts.thread_id, - posts.file_path, - posts.file_width, - posts.file_height, - posts.file_type - from posts - left join boards on - board_id = boards.id - where - file_path is not null and - file_width is not null and - file_height is not null and - file_type = 'image' and - file_spoiler = false - order by - board_id asc, - thread_id asc, - post_id asc - ]] - local dir - local board - local thread = 0 - local results = db.query(sql) - - for _, result in ipairs(results) do - -- Change board, reset thread counter and image directory - if result.board ~= board then - board = result.board - thread = 0 - dir = string.format("./static/%s/", board) - end - - -- Filesystem paths - local name, ext = result.file_path:match("^(.+)(%..+)$") - ext = string.lower(ext) - - local full_path = dir .. result.file_path - local thumb_path = dir .. "s" .. result.file_path - - -- Generate a thumbnail - if ext == ".webm" then - thumb_path = dir .. "s" .. name .. ".png" - - -- Create screenshot of first frame - os.execute(string.format("ffmpeg -i %s -ss 00:00:01 -vframes 1 %s -y", full_path, thumb_path)) - end - - -- Save thumbnail - local w, h - if result.thread_id > thread then - thread = result.thread_id - w = result.file_width < 250 and result.file_width or 250 - h = result.file_height < 250 and result.file_height or 250 - else - w = result.file_width < 125 and result.file_width or 125 - h = result.file_height < 125 and result.file_height or 125 - end - - -- Grab first frame from video - if ext == ".webm" then - magick.thumb(thumb_path, string.format("%sx%s", w, h), thumb_path) - elseif ext == ".svg" then - thumb_path = dir .. "s" .. name .. ".png" - os.execute(string.format("convert -background none -resize %dx%d %s %s", w, h, full_path, thumb_path)) - elseif ext == ".gif" then - -- Grab first frame of a gif instead of the last - local gif, err = giflib.load_gif(full_path) - - if err then - magick.thumb(full_path, string.format("%sx%s", w, h), thumb_path) - else - gif:write_first_frame(thumb_path) - magick.thumb(thumb_path, string.format("%sx%s", w, h), thumb_path) - end - else - magick.thumb(full_path, string.format("%sx%s", w, h), thumb_path) - end - end -end - -return Boards diff --git a/misc/website/backend/app/src/models/pages.lua b/misc/website/backend/app/src/models/pages.lua deleted file mode 100755 index 82ce112..0000000 --- a/misc/website/backend/app/src/models/pages.lua +++ /dev/null @@ -1,90 +0,0 @@ -local Model = require("lapis.db.model").Model -local Pages = Model:extend("pages") - -Pages.valid_record = { - { "slug", exists=true, type="String" }, - { "title", exists=true, type="String" }, - { "content", exists=true, type="String" } -} - ---- Create a new page --- @tparam table page Page data --- @treturn boolean success --- @treturn string error -function Pages:new(params) - local unique, err = self:is_unique(params.slug, params.title) - if not unique then - return nil, err - end - - local page = self:create(params) - return page and page or nil, { "err_create_page", { page.slug, page.title } } -end - ---- Modify a page --- @tparam table page Page data --- @treturn boolean success --- @treturn string error -function Pages:modify(params, slug) - local page = self:get(slug) - if not page then - return nil, "FIXME" - end - - -- Check to see if the page we are modifying is the one that fails validation. - -- If it is, that's fine since we're updating the unique values with themselves. - -- If #pages > 1 then this will always fail since either the new slug or new - -- title is going to belong to some other page. - do - local unique, err, pages = self:is_unique(params.slug, params.title) - if not unique then - for _, p in ipairs(pages) do - if page.id ~= p.id then - return nil, err - end - end - end - end - - local success, err = page:update(params) - return success and page or nil, "FIXME: " .. tostring(err) -end - ---- Delete page --- @tparam table page Page data --- @treturn boolean success --- @treturn string error -function Pages:delete(slug) - local page = self:get(slug) - if not page then - return nil, "FIXME" - end - - local success = page:delete() - return success and page or nil, "FIXME" -end - ---- Get all pages --- @treturn table pages List of pages -function Pages:get_all() - return self:select("order by slug asc") -end - ---- Get page --- @tparam string slug Page slug --- @treturn table page -function Pages:get(slug) - local page = self:find { slug=slug:lower() } - return page and page or nil, "FIXME" -end - -function Pages:is_unique(slug, title) - local pages = self:select("where slug=? or lower(title)=?", slug, title:lower()) - return #pages == 0 and true or nil, "FIXME", pages -end - -function Pages.format_to_db(_, params) - params.slug = params.slug:lower() -end - -return Pages diff --git a/misc/website/backend/app/src/models/posts.lua b/misc/website/backend/app/src/models/posts.lua deleted file mode 100755 index e53d792..0000000 --- a/misc/website/backend/app/src/models/posts.lua +++ /dev/null @@ -1,387 +0,0 @@ -local encoding = require "lapis.util.encoding" -local Model = require("lapis.db.model").Model -local giflib = require "giflib" -local magick = require "magick" -local md5 = require "md5" -local filetypes = require "utils.file_whitelist" -local generate = require "utils.generate" -local Posts = Model:extend("posts", { - relations = { - { "board", belongs_to="Boards" }, - { "thread", belongs_to="Threads" }, - } -}) -local sf = string.format - -local function get_duration(path) - local cmd = sf("ffprobe -i %s -show_entries format=duration -v quiet -of csv=\"p=0\" -sexagesimal", path) - local f = io.popen(cmd, "r") - local s = f:read("*a") - f:close() - - local hr, mn, sc = string.match(s, "(%d+):(%d+):(%d+).%d+") - local d = mn..":"..sc - - if hr ~= "0" then - d = hr..":"..d - end - - return d -end - ---- Prepare post for insertion --- @tparam table params Input from the user --- @tparam table session User session --- @tparam table board Board data --- @tparam table thread Thread data --- @tparam number files Number of files in thread --- @treturn boolean success --- @treturn string error -function Posts:prepare_post(params, session, board, thread, files) - -- FIXME: this whole function should be web-side, not api-side - local time = os.time() - - -- Prepare session stuff - session.password = session.password or generate.password(time) - - -- Save names on individual boards - -- TODO: Put this code elsewhere - if params.name then - session.names[board.name] = params.name - end - - -- Files take presidence over drawings, but if there is no file, fill in - -- the file fields with draw data. This should avoid ugly code branching - -- later on in the file. - -- TODO: send file data through api! - params.file = params.file or { - filename = "", - content = "" - } - - if #params.file.content == 0 and params.draw then - local pattern = ".-,(.+)" - params.draw = params.draw:match(pattern) - params.file.filename = "tegaki.png" - params.file.content = encoding.decode_base64(params.draw) - end - - -- Check board flags - if thread then - if thread.lock and not session.admin and not session.mod then - return false, { "err_locked_thread", { thread.post_id } } - end - - if board.post_comment and not params.comment then - return false, { "err_comment_post" } - end - - if board.post_file and #params.file.content == 0 then - return false, { "err_file_post" } - end - else - if board.thread_comment and not params.comment then - return false, { "err_comment_thread" } - end - - if board.thread_file and #params.file.content == 0 then - return false, { "err_file_thread" } - end - end - - -- Parse name - if params.name then - params.name, params.trip = generate.tripcode(params.name) - end - - -- Set file - if #params.file.content > 0 then - - -- Reject files in text-only boards - if board.text_only then - return false, { "err_no_files" } - end - - -- Thread limit is already met. - if thread then - if files >= board.thread_file_limit and not thread.size_override then - return false, { "err_file_limit", { thread.post_id } } - end - end - - local name = sf("%s%s", time, generate.random()) - local ext = params.file.filename:match("^.+(%..+)$") - ext = string.lower(ext) - - -- Figure out how to deal with the file - if filetypes.image[ext] and board.filetype_image then - params.file_type = "image" - - if ext ~= ".webm" then - -- Check if valid image - local image = magick.load_image_from_blob(params.file.content) - - if not image then - return false, { "err_invalid_image" } - end - - params.file_width = image:get_width() - params.file_height = image:get_height() - end - elseif filetypes.audio[ext] and board.filetype_audio then - params.file_type = "audio" - else - return false, { "err_invalid_ext", { ext } } - end - - params.file_name = params.file.filename - params.file_path = name .. ext - params.file_md5 = md5.sumhexa(params.file.content) - params.file_size = #params.file.content - - if params.file_spoiler then - params.file_spoiler = true - else - params.file_spoiler = false - end - - -- Check if file already exists - local file = self:find_file(board.id, params.file_md5) - if file then - return false, { "err_file_exists" } - end - else - params.file_spoiler = false - end - - -- Check contributions - if not params.comment and not params.file_name then - return false, { "err_contribute" } - end - - return true -end - ---- Create a new post --- @tparam table params Post parameters --- @tparam table board Board data --- @tparam boolean op OP flag --- @treturn boolean success --- @treturn string error -function Posts:new(params, board, op) - - -- Create post - local post = self:create(params) - if not post then - return false, { "err_create_post" } - end - - -- Save file - if post.file_path then - local dir = sf("./static/%s/", board.name) - local name, ext = post.file_path:match("^(.+)%.(.+)$") - ext = string.lower(ext) - - -- Filesystem paths - local full_path = dir .. post.file_path - local thumb_path = dir .. "s" .. post.file_path - - -- Save file - local file = io.open(full_path, "w") - file:write(params.file_content) - file:close() - - -- Audio file - if post.file_type == "audio" then - post.file_duration = get_duration(full_path) - post:update("file_duration") - return post - end - - -- Image file - if post.file_type == "image" and not post.file_spoiler then - - -- Save thumbnail - local w, h - if op then - w = post.file_width < 250 and post.file_width or 250 - h = post.file_height < 250 and post.file_height or 250 - else - w = post.file_width < 125 and post.file_width or 125 - h = post.file_height < 125 and post.file_height or 125 - end - - -- Generate a thumbnail - if ext == "webm" then - thumb_path = dir .. "s" .. name .. ".png" - - -- Create screenshot of first frame - os.execute(sf("ffmpeg -i %s -ss 00:00:01 -vframes 1 %s -y", full_path, thumb_path)) - - -- Update post info - local image = magick.load_image(thumb_path) - post.file_width = image:get_width() - post.file_height = image:get_height() - post.file_duration = get_duration(full_path) - post:update("file_width", "file_height", "file_duration") - - -- Resize thumbnail - magick.thumb(thumb_path, sf("%sx%s", w, h), thumb_path) - - elseif ext == "svg" then - thumb_path = dir .. "s" .. name .. ".png" - os.execute(sf("convert -background none -resize %dx%d %s %s", w, h, full_path, thumb_path)) - - elseif ext == "gif" then - local gif, err = giflib.load_gif(full_path) - - if err then - -- Not animated I presume? TODO: check what err represents - magick.thumb(full_path, sf("%sx%s", w, h), thumb_path) - else - -- Grab first frame of a gif instead of the last - gif:write_first_frame(thumb_path) - - -- Update post info - local width, height = gif:dimensions() - post.file_width = width - post.file_height = height - post:update("file_width", "file_height") - - -- Resize thumbnail - magick.thumb(thumb_path, sf("%sx%s", w, h), thumb_path) - end - else - magick.thumb(full_path, sf("%sx%s", w, h), thumb_path) - end - end - end - - -- Update board - board:update("total_posts") - - return post -end - ---- Delete post data --- @tparam number id Post ID --- @treturn boolean success --- @treturn string error -function Posts:delete(id) - - -- Get post - local post, err = self:get_post_by_id(id) - if not post then - return false, err - end - - -- Delete post - local success, err = post:delete() - if not success then - return false, err--{ "err_delete_post", { post.post_id } } - end - - -- Delete files - if post.file_path then - local board = post:get_board() - local dir = sf("./static/%s/", board.name) - local name, ext = post.file_path:match("^(.+)%.(.+)$") - ext = string.lower(ext) - os.remove(dir .. post.file_path) - - -- Change thumbnail path to png - if ext == "webm" or ext == "svg" then - post.file_path = name .. ".png" - end - - os.remove(dir .. "s" .. post.file_path) - end - - return post -end - ---- Get op and last 5 posts of a thread to display on board index --- @tparam number thread_id Thread ID --- @treturn table posts -function Posts:get_index_posts(thread_id) - local sql = "where thread_id=? order by post_id desc limit 5" - local posts = self:select(sql, thread_id) - - if self:count_posts(thread_id) > 5 then - local thread = posts[1]:get_thread() - local op = thread:get_op() - table.insert(posts, op) - end - - return posts -end - ---- Get post data --- @tparam number board_id Board ID --- @tparam number post_id Local Post ID --- @treturn table post -function Posts:get(board_id, post_id) - local post = self:find { - board_id = board_id, - post_id = post_id - } - return post and post or false, "FIXME" -end - ---- Get post data --- @tparam number id Post ID --- @treturn table post -function Posts:get_post_by_id(id) - local post = self:find(id) - return post and post or false, "FIXME" -end - ---- Find file in active posts --- @tparam number board Board ID --- @tparam string file_md5 Unique hash of file --- @treturn boolean success --- @treturn string error -function Posts:find_file(board_id, file_md5) - local sql = "where board_id=? and file_md5=? limit 1" - return unpack(self:select(sql, board_id, file_md5)) -end - - ---- Count hidden posts in a thread --- @tparam number thread_id Thread ID --- @treturn table hidden -function Posts:count_hidden_posts(thread_id) - local posts = self:get_index_posts(thread_id) - local num_posts = self:count_posts(thread_id) - local num_files = self:count_files(thread_id) - - for _, post in ipairs(posts) do - -- Reduce number of posts hidden - num_posts = num_posts - 1 - - -- Reduce number of files hidden - if post.file_name then - num_files = num_files - 1 - end - end - - return { posts=num_posts, files=num_files } -end - ---- Count posts in a thread --- @tparam number thread_id Thread ID --- @treturn number posts -function Posts:count_posts(thread_id) - local sql = "thread_id=?" - return self:count(sql, thread_id) -end - ---- Count posts with images in a thread --- @tparam number thread_id Thread ID --- @treturn number files -function Posts:count_files(thread_id) - local sql = "thread_id=? and file_name is not null" - return self:count(sql, thread_id) -end - -return Posts diff --git a/misc/website/backend/app/src/models/reports.lua b/misc/website/backend/app/src/models/reports.lua deleted file mode 100755 index bbe1f9c..0000000 --- a/misc/website/backend/app/src/models/reports.lua +++ /dev/null @@ -1,76 +0,0 @@ -local trim = require("lapis.util").trim_filter -local Model = require("lapis.db.model").Model -local Reports = Model:extend("reports") - ---- Create a new report --- @tparam table report Report data --- @treturn boolean success --- @treturn string error -function Reports:create_report(report) - -- Trim white space - trim(report, { - "board_id", "thread_id", "post_id", - "timestamp", "num_reports" - }, nil) - - local r = self:create { - board_id = report.board_id, - thread_id = report.thread_id, - post_id = report.post_id, - timestamp = report.timestamp, - num_reports = report.num_reports - } - - if r then - return r - end - - return false, { "err_create_report" } -end - ---- Modify a report --- @tparam table report Report data --- @treturn boolean success --- @treturn string error -function Reports:modify_report(report) - local columns = {} - for col in pairs(report) do - table.insert(columns, col) - end - - return report:update(unpack(columns)) -end - ---- Delete report --- @tparam table report Report data --- @treturn boolean success --- @treturn string error -function Reports:delete_report(report) - return report:delete() -end - ---- Get all reports --- @treturn table reports List of reports -function Reports:get_reports() - return self:select("order by timestamp asc") -end - ---- Get report --- @tparam string board_id Board ID --- @tparam string post_id Post ID --- @treturn table report -function Reports:get_report(board_id, post_id) - return unpack(self:select( - "where board_id=? and post_id=? limit 1", - board_id, post_id - )) -end - ---- Get report --- @tparam string id Report ID --- @treturn table report -function Reports:get_report_by_id(id) - return unpack(self:select("where id=? limit 1", id)) -end - -return Reports diff --git a/misc/website/backend/app/src/models/threads.lua b/misc/website/backend/app/src/models/threads.lua deleted file mode 100755 index d33d00a..0000000 --- a/misc/website/backend/app/src/models/threads.lua +++ /dev/null @@ -1,106 +0,0 @@ -local Model = require("lapis.db.model").Model -local Threads = Model:extend("threads", { - relations = { - { "board", belongs_to="Boards" }, - { "posts", has_many="Posts" }, - { "op", has_one="Posts", order="post_id asc" }, - } -}) - -Threads.valid_record = { - { "board_id", exists=true } -} - ---- Create thread --- @tparam table params Thread parameters --- @treturn boolean success --- @treturn string error -function Threads:new(params) - local thread = self:create(params) - return thread and thread or false, { "err_create_thread" } -end - ---- Modify a thread --- @tparam table params Thread parameters --- @treturn boolean success --- @treturn string error -function Threads:modify(params) - local thread = self:get(params.id) - if not thread then - return false, { "err_create_board" } -- FIXME: wrong error message - end - - local success, err = thread:update(params) - return success and thread or false, "FIXME: " .. tostring(err) -end - ---- Delete entire thread --- @tparam number id Thread ID --- @treturn boolean success --- @treturn string error -function Threads:delete(id) - -- FIXME: API needs to create a user object for better auth checking - local thread, err = self:get(id) - if not thread then - return false, err - end - - local op = thread:get_op() - local success = thread:delete() - return success and thread or false, { "err_delete_thread", { op.post_id } } -end - ---- Get thread data --- @tparam number id Thread ID --- @treturn table thread -function Threads:get(id) - local thread = self:find(id) - return thread and thread or false, "FIXME" -end - ---- Get archived threads --- @tparam number board_id Board ID --- @treturn table threads -function Threads:get_archived(board_id) - local sql = "where board_id=? and archive=true order by last_active desc" - return self:select(sql, board_id) -end - ---- Bump threads to archive --- @tparam number board_id Board ID --- @tparam number max_threads Maximum number of threads on this board --- @treturn boolean success --- @treturn string error -function Threads:archive_threads(board_id, max_threads) - local threads = self:get_threads(board_id) - - if #threads > max_threads then - for i=max_threads+1, #threads do - local _, err = self:archive_thread(threads[i]) - - if err then - return false, err - end - end - end -end - ---- Archive a thread --- @tparam table thread Thread data --- @treturn boolean success --- @treturn string error -function Threads:archive_thread(thread) - thread.sticky = false - thread.lock = true - thread.archive = true - thread.last_active = os.time() - return thread:update("sticky", "lock", "archive", "last_active") -end - ---- Find threads with no posts --- @treturn table threads -function Threads:find_orphans() - return self:select("where id not in (select distinct thread_id from posts)") -end - -return Threads diff --git a/misc/website/backend/app/src/models/users.lua b/misc/website/backend/app/src/models/users.lua index 469d6f2..11d5168 100755 --- a/misc/website/backend/app/src/models/users.lua +++ b/misc/website/backend/app/src/models/users.lua @@ -1,30 +1,34 @@ local bcrypt = require "bcrypt" -local uuid = require "resty.jit-uuid" +local uuid = require "resty.jit-uuid" local config = require("lapis.config").get() -local Model = require("lapis.db.model").Model -local Users = Model:extend("users") -local token = config.secret +local Model = require("lapis.db.model").Model +local Users = Model:extend("users") +local token = config.secret Users.role = { - [-1] = "INVALID", - [1] = "USER", - [6] = "JANITOR", - [7] = "MOD", - [8] = "ADMIN", - [9] = "OWNER", - - INVALID = -1, - USER = 1, - JANITOR = 6, - MOD = 7, - ADMIN = 8, - OWNER = 9 + [-1] = "INVALID", + [1] = "USER", + [6] = "JANITOR", + [7] = "MOD", + [8] = "ADMIN", + [9] = "OWNER", + + INVALID = -1, + USER = 1, + JANITOR = 6, + MOD = 7, + ADMIN = 8, + OWNER = 9, } -Users.valid_record = { - { "username", exists=true }, - { "role", exists=true, is_integer=true } -} +Users.valid_record = {{ + "username", + exists = true, +}, { + "role", + exists = true, + is_integer = true, +}} Users.default_key = "00000000-0000-0000-0000-000000000000" @@ -34,31 +38,37 @@ Users.default_key = "00000000-0000-0000-0000-000000000000" -- @treturn string error function Users:new(params, raw_password) - -- Check if username is unique - do - local unique, err = self:is_unique(params.username) - if not unique then return nil, err end - end - - -- Verify password - do - local valid, err = self:validate_password(params.password, params.confirm, raw_password) - if not valid then return nil, err end - - params.confirm = nil - params.password = bcrypt.digest(params.username:lower() .. params.password .. token, 12) - end - - -- Generate unique API key - do - local api_key, err = self:generate_api_key() - if not api_key then return nil, err end - - params.api_key = api_key - end - - local user = self:create(params) - return user and user or nil, { "err_create_user", { params.username } } + -- Check if username is unique + do + local unique, err = self:is_unique(params.username) + if not unique then + return nil, err + end + end + + -- Verify password + do + local valid, err = self:validate_password(params.password, params.confirm, raw_password) + if not valid then + return nil, err + end + + params.confirm = nil + params.password = bcrypt.digest(params.username:lower() .. params.password .. token, 12) + end + + -- Generate unique API key + do + local api_key, err = self:generate_api_key() + if not api_key then + return nil, err + end + + params.api_key = api_key + end + + local user = self:create(params) + return user and user or nil, {"err_create_user", {params.username}} end --- Modify a user @@ -66,34 +76,42 @@ end -- @treturn boolean success -- @treturn string error function Users:modify(params, raw_username, raw_password) - local user = self:get(raw_username) - if not user then return nil, "FIXME" end - - -- Check if username is unique - do - local unique, err, u = self:is_unique(params.username) - if not unique and user.id ~= u.id then return nil, err end - end - - -- Verify password - if params.password then - local valid, err = self:validate_password(params.password, params.confirm, raw_password) - if not valid then return nil, err end - - params.confirm = nil - params.password = bcrypt.digest(params.username:lower() .. params.password .. token, 12) - end - - -- Generate unique API key - if params.api_key then - local api_key, err = self:generate_api_key() - if not api_key then return nil, err end - - params.api_key = api_key - end - - local success, err = user:update(params) - return success and user or nil, "FIXME: " .. tostring(err) + local user = self:get(raw_username) + if not user then + return nil, "FIXME" + end + + -- Check if username is unique + do + local unique, err, u = self:is_unique(params.username) + if not unique and user.id ~= u.id then + return nil, err + end + end + + -- Verify password + if params.password then + local valid, err = self:validate_password(params.password, params.confirm, raw_password) + if not valid then + return nil, err + end + + params.confirm = nil + params.password = bcrypt.digest(params.username:lower() .. params.password .. token, 12) + end + + -- Generate unique API key + if params.api_key then + local api_key, err = self:generate_api_key() + if not api_key then + return nil, err + end + + params.api_key = api_key + end + + local success, err = user:update(params) + return success and user or nil, "FIXME: " .. tostring(err) end --- Delete user @@ -101,13 +119,13 @@ end -- @treturn boolean success -- @treturn string error function Users:delete(username) - local user = self:get(username) - if not user then - return nil, "FIXME" - end + local user = self:get(username) + if not user then + return nil, "FIXME" + end - local success = user:delete() - return success and user or nil, "FIXME" + local success = user:delete() + return success and user or nil, "FIXME" end --- Verify user @@ -115,67 +133,73 @@ end -- @treturn boolean success -- @treturn string error function Users:login(params) - local user = self:get(params.username) - if not user then return nil, { "err_invalid_user" } end + local user = self:get(params.username) + if not user then + return nil, {"err_invalid_user"} + end - local password = user.username .. params.password .. token - local verified = bcrypt.verify(password, user.password) + local password = user.username .. params.password .. token + local verified = bcrypt.verify(password, user.password) - return verified and user or nil, { "err_invalid_user" } + return verified and user or nil, {"err_invalid_user"} end --- Get all users -- @treturn table users List of users function Users:get_all() - local users = self:select("order by username asc") - return users + local users = self:select("order by username asc") + return users end --- Get user -- @tparam string username Username -- @treturn table user function Users:get(username) - local users = self:select("where lower(username)=? limit 1", username:lower()) - return #users == 1 and users[1] or nil, "FIXME" + local users = self:select("where lower(username)=? limit 1", username:lower()) + return #users == 1 and users[1] or nil, "FIXME" end function Users:get_api(params) - local user = self:find(params) - return user and user or nil, "FIXME" + local user = self:find(params) + return user and user or nil, "FIXME" end function Users:format_to_db(params) - if not params.role then - params.role = self.role.INVALID - end + if not params.role then + params.role = self.role.INVALID + end end function Users.format_from_db(_, params) - params.password = nil - params.api_key = nil + params.password = nil + params.api_key = nil end function Users:is_unique(username) - local user = self:get(username) - return not user and true or nil, "FIXME", user + local user = self:get(username) + return not user and true or nil, "FIXME", user end function Users.validate_password(_, password, confirm, old_password) - if password ~= confirm or password ~= old_password then - return nil, "FIXME" - end + if password ~= confirm or password ~= old_password then + return nil, "FIXME" + end - return true + return true end function Users:generate_api_key() - for _ = 1, 10 do - local api_key = uuid() - local user = self:find { api_key=api_key } - if not user then return api_key end - end - - return nil, "FIXME" + for _ = 1, 10 do + local api_key = uuid() + local user = self:find{ + api_key = api_key, + } + if not user then + return api_key + end + end + + return nil, "FIXME" end return Users diff --git a/misc/website/backend/app/src/sass/posts.scss b/misc/website/backend/app/src/sass/posts.scss deleted file mode 100755 index a42c6d3..0000000 --- a/misc/website/backend/app/src/sass/posts.scss +++ /dev/null @@ -1,109 +0,0 @@ -$spacing: 10px; - -.thread_container, .post_container { - margin: $spacing 0 0 0; - padding: $spacing; -} - -.thread_container { - overflow: visible; - - .op, .post_container { - position: relative; - } - - .op { - .post_admin { - margin-left: $spacing * 35; - } - } - - .post_container { - display: table; - min-width: 300px; - max-width: 100%; - - .post_admin { - margin-left: $spacing * 2; - } - } - - .post_file { - margin: 0 0 3px 0; - - a { - text-decoration: underline; - } - } - - .post_image { - float: left; - margin: 0 $spacing * 2 0 $spacing; - - img { - max-width: 1000px; - max-height: 1000px; - } - } - - .post_subject, .post_name { - font-weight: bold; - } - - .post_comment { - padding: $spacing $spacing * 1.5; - - a { - text-decoration: underline; - } - - .broken_link { - text-decoration: line-through; - } - - .spoiler { - background-color: #000000; - color: #000000; - - &:hover { - color: #ffffff; - } - } - - .post_banned { - color: #ff0000; - font-weight: bold; - margin-top: $spacing * 2; - } - } - - .post_flag, .post_menu { - cursor: pointer; - } - - .post_menu { - position: relative; - display: inline-block; - - .menu { - display: none; - position: absolute; - z-index: 9; - - div { - padding: 3px; - - form { - margin: 0; - padding: 0; - - input[type="submit"], button { - background: none; - border: none; - padding: 0; - } - } - } - } - } -} diff --git a/misc/website/backend/app/src/sass/reset.scss b/misc/website/backend/app/src/sass/reset.scss deleted file mode 100755 index 887bf88..0000000 --- a/misc/website/backend/app/src/sass/reset.scss +++ /dev/null @@ -1,141 +0,0 @@ -/* - HTML5 Reset :: style.css - ---------------------------------------------------------- - We have learned much from/been inspired by/taken code where offered from: - - Eric Meyer :: http://meyerweb.com - HTML5 Doctor :: http://html5doctor.com - and the HTML5 Boilerplate :: http://html5boilerplate.com - --------------------------------------------------------------------------------*/ - -/* Let's default this puppy out --------------------------------------------------------------------------------*/ - -html, body, body div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, figure, footer, header, menu, nav, section, time, mark, audio, video, details, summary { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font-weight: normal; - vertical-align: baseline; - background: transparent; -} - -article, aside, figure, footer, header, nav, section, details, summary {display: block;} - -/* Handle box-sizing while better addressing child elements: - http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */ -html { - box-sizing: border-box; -} - -*, -*:before, -*:after { - box-sizing: inherit; -} - -/* consider resetting the default cursor: https://gist.github.com/murtaugh/5247154 */ - -/* Responsive images and other embedded objects */ -/* if you don't have full control over `img` tags (if you have to overcome attributes), consider adding height: auto */ -img, -object, -embed {max-width: 100%;} - -/* - Note: keeping IMG here will cause problems if you're using foreground images as sprites. - In fact, it *will* cause problems with Google Maps' controls at small size. - If this is the case for you, try uncommenting the following: - -#map img { - max-width: none; -} -*/ - -/* force a vertical scrollbar to prevent a jumpy page */ -html {overflow-y: scroll;} - -/* we use a lot of ULs that aren't bulleted. - you'll have to restore the bullets within content, - which is fine because they're probably customized anyway */ -ul {list-style: none;} - -blockquote, q {quotes: none;} - -blockquote:before, -blockquote:after, -q:before, -q:after {content: ''; content: none;} - -a {margin: 0; padding: 0; font-size: 100%; vertical-align: baseline; background: transparent;} - -del {text-decoration: line-through;} - -abbr[title], dfn[title] {border-bottom: 1px dotted #000; cursor: help;} - -/* tables still need cellspacing="0" in the markup */ -table {border-collapse: collapse; border-spacing: 0;} -th {font-weight: bold; vertical-align: bottom;} -td {font-weight: normal; vertical-align: top;} - -hr {display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0;} - -input, select {vertical-align: middle;} - -pre { - white-space: pre; /* CSS2 */ - white-space: pre-wrap; /* CSS 2.1 */ - white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */ - word-wrap: break-word; /* IE */ -} - -input[type="radio"] {vertical-align: text-bottom;} -.ie7 input[type="checkbox"] {vertical-align: baseline;} -.ie6 input {vertical-align: text-bottom;} - -select, input, textarea {font: 99% sans-serif;} - -table {font-size: inherit; font: 100%;} - -small {font-size: 85%;} - -strong {font-weight: bold;} - -td, td img {vertical-align: top;} - -/* Make sure sup and sub don't mess with your line-heights http://gist.github.com/413930 */ -sub, sup {font-size: 75%; line-height: 0; position: relative;} -sup {top: -0.5em;} -sub {bottom: -0.25em;} - -/* standardize any monospaced elements */ -pre, code, kbd, samp {font-family: monospace, sans-serif;} - -/* hand cursor on clickable elements */ -.clickable, -input[type=button], -input[type=submit], -input[type=file], -button {cursor: pointer;} - -/* Webkit browsers add a 2px margin outside the chrome of form elements */ -button, input, select, textarea {margin: 0;} - -/* make buttons play nice in IE */ -button, -input[type=button] {width: auto; overflow: visible;} - -/* scale images in IE7 more attractively */ -.ie7 img {-ms-interpolation-mode: bicubic;} - -/* prevent BG image flicker upon hover - (commented out as usage is rare, and the filter syntax messes with some pre-processors) -.ie6 html {filter: expression(document.execCommand("BackgroundImageCache", false, true));} -*/ - -/* let's clear some floats */ -.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; } -.clearfix:after { clear: both; } -.clearfix { zoom: 1; } diff --git a/misc/website/backend/app/src/sass/style.scss b/misc/website/backend/app/src/sass/style.scss deleted file mode 100755 index 83fae1f..0000000 --- a/misc/website/backend/app/src/sass/style.scss +++ /dev/null @@ -1,322 +0,0 @@ -@import "reset"; -@import "posts"; - -@mixin display-flex { - display: -webkit-box-flex; - display: -webkit-flexbox; - display: -ms-flexbox; - display: -webkit-box; - display: flex; -} - -@mixin flex($args) { - -webkit-flex: $args; - -ms-flex: $args; - flex: $args; -} - -@mixin flex-direction($dir) { - $alt: vertical !default; - @if $dir == column { - $alt: vertical; - } @else { - $alt: horizontal; - } - -webkit-flex-direction: column; - -webkit-box-orient: $alt; - -ms-flex-direction: column; - flex-direction: column; -} - -$spacing: 10px; - -body { - margin: $spacing; - font-size: 10pt; - font-family: Helvetica, "Noto Sans", "Roboto Sans", Arial, sans-serif; -} - -a { - text-decoration: none; -} - -input[type="text"], -input[type="password"], -textarea, -select, -.admin_main { - padding: 4px; - width: 280px; -} - -input[type="checkbox"] { - margin: 4px 0; -} - -input[type="file"] { - padding: 4px 0; -} - -h1 { - font-size: 240%; - font-weight: bolder; - margin-top: 10px; - text-align: center; -} - -h2 { - margin: $spacing 0; - text-align: center; -} - -hr { - clear: left; -} - -form { - display: inline-block; - margin: $spacing / 2 auto; - padding: $spacing; -} - -table { - border-collapse: separate; - border-spacing: 1px; - margin: 0 auto; - max-width: 80%; - - thead { - vertical-align: middle; - - td { - font-weight: bolder; - padding: $spacing / 2 $spacing; - } - } - - td { - padding: 3px $spacing / 2; - text-align: center; - } - - .rules { - text-align: left; - } -} - -ul { - list-style: disc; - - li { - margin-left: $spacing * 2; - } -} - -ol { - list-style: decimal; - - li { - margin-left: $spacing * 2; - } -} - -#error { - background-color: #ff0000; - color: #ffffff; - margin-bottom: $spacing; - padding: $spacing; - text-align: center; - - h3 { - font-size: 220%; - font-weight: bolder; - - } - - p { - font-weight: bold; - } -} - -#post_form, .admin_form { - text-align: center; - - form { - div { - @include display-flex; - - .title { - @include flex(0); - font-weight: bold; - text-align: right; - min-width: 120px; - margin: 1px; - padding: 4px; - - span { - cursor: pointer; - } - } - - .fields { - margin: 1px; - - textarea { - min-height: 100px !important; - } - } - } - - button { - margin: $spacing 0 0 0; - } - } -} - -.admin_form { - form { - margin: 0; - padding: 0; - - button { - margin: 0; - } - } -} - -.required { - color: #ff0000; -} - -.boards, .return, .copyright { - text-align: center; -} - -.archive_stats { - text-align: center; - margin-bottom: $spacing; -} - -.announcement { - text-align: center; - font-weight: bold; -} - -.banned { - border: 7px dashed #000000; - display: table; - font-size: 180%; - min-width: 200px; - max-width: 600px; - margin: $spacing * 2 auto; - padding: $spacing; - - p { - margin: $spacing; - } - - div { - text-align: center; - } -} - -.medium { - width: 120px !important; -} - -.short { - width: 40px !important; -} - -.catalog_container { - display: inline-block; - overflow: hidden; - text-align: center; - vertical-align: top; - width: 135px; - - .catalog_stats { - font-size: 70%; - } - - img { - box-shadow: 0 0 5px rgba(0, 0, 0, 0.25); - max-width: 125px; - max-height: 125px; - } -} - -.logout { - top: $spacing; - right: $spacing; - position: absolute; -} - -.copyright { - margin-top: $spacing; -} - -.front_page { - h2 { - font-size: 180%; - } - - h3 { - font-size: 140%; - margin-top: $spacing; - } - - .board { - display: inline-block; - max-width: 300px; - margin: $spacing * 2; - vertical-align: top; - - a { - font-size: 160%; - font-weight: bold; - } - - .subtext { - display: block; - font-size: 120%; - } - } -} - -.table_of_contents { - display: table; - margin: 0 auto; - max-width: 400px; -} - -.answers,{ - max-width: 600px; - margin: 0 auto; - - h2 { - font-size: 160%; - } -} - -.install { - max-width: 300px; - margin: 0 auto; - - h2 { - font-size: 160%; - margin-top: $spacing * 2; - } - - label { - font-weight: bold; - margin-top: $spacing * 2; - } - - button { - font-size: 120%; - font-weight: bold; - margin-top: $spacing * 2; - padding: $spacing / 2; - } -} diff --git a/misc/website/backend/app/src/sass/yotsuba.scss b/misc/website/backend/app/src/sass/yotsuba.scss deleted file mode 100755 index 5c6b253..0000000 --- a/misc/website/backend/app/src/sass/yotsuba.scss +++ /dev/null @@ -1,113 +0,0 @@ -$background: #ffffee; -$bg_light: #ffffff; -$border: #d9bfb7; -$subject: #cc1105; -$header: #800000; -$link: #0000ee; -$hover: #ff0000; -$quote: #000080; -$post: #f0e0d6; -$name: #117743; -$black: #000000; -$green: #789922; -$blue: #292299; -$active: #f0c0b0; -$active_border: #d99f91; - -body { - background-color: $background; -} - -body, h1, h2 { - color: $header; -} - -a, a:visited, .broken_link { - color: $link; -} - -a:hover, .quote_link:hover, .broken_link:hover { - color: $hover; -} - -.quote_link { - color: $quote; -} - -.quote_green { - color: $green; -} - -.quote_blue { - color: $blue; -} - -.announcement { - color: $header; -} - -#post_form, .admin_form { - form { - .title { - background-color: $active; - border: 1px solid $active_border; - } - } -} - -table { - thead { - td { - background-color: $post; - border: 1px solid $border; - } - } - - tr:nth-of-type(odd) { - background-color: $background; - } - - tr:nth-of-type(even) { - background-color: $bg_light; - } -} - -.post_container { - background-color: $post; - border-right: 1px solid $border; - border-bottom: 1px solid $border; - - &:target { - background-color: $active; - border-right: 1px solid $active_border; - border-bottom: 1px solid $active_border; - } -} - -.post_subject { - color: $subject; -} - -.post_name, .post_trip { - color: $name; -} - -.post_menu .menu { - background-color: $post; - border:1px solid $border; - - div { - border-right: 1px solid $border; - border-bottom: 1px solid $border; - - form { - button { - color: $header; - - &:hover { - color: $hover; - } - } - } - } -} diff --git a/misc/website/backend/app/src/sass/yotsuba_b.scss b/misc/website/backend/app/src/sass/yotsuba_b.scss deleted file mode 100755 index 732c1db..0000000 --- a/misc/website/backend/app/src/sass/yotsuba_b.scss +++ /dev/null @@ -1,109 +0,0 @@ -$background: #eef2ff; -$bg_light: #f7f9ff; -$border: #b7c5d9; -$subject: #0f0c5d; -$header: #af0a0f; -$link: #34345c; -$hover: #dd0000; -$post: #d6daf0; -$name: #117743; -$black: #000000; -$green: #789922; -$blue: #292299; -$active: #d6bad0; -$active_border: #ba9dbf; - -body { - background-color: $background; - color: $black; -} - -h1, h2 { - color: $header; -} - -a, a:visited, .broken_link { - color: $link; -} - -a:hover, .quote_link, .quote_link:visited, .broken_link:hover { - color: $hover; -} - -.quote_green { - color: $green; -} - -.quote_blue { - color: $blue; -} - -.announcement { - color: $header; -} - -#post_form, .admin_form { - form { - .title { - background-color: $active; - border: 1px solid $active_border; - } - } -} - -table { - thead { - td { - background-color: $post; - border: 1px solid $border; - } - } - - tr:nth-of-type(odd) { - background-color: $background; - } - - tr:nth-of-type(even) { - background-color: $bg_light; - } -} - -.post_container { - background-color: $post; - border-right: 1px solid $border; - border-bottom: 1px solid $border; - - &:target { - background-color: $active; - border-right: 1px solid $active_border; - border-bottom: 1px solid $active_border; - } -} - -.post_subject { - color: $subject; -} - -.post_name, .post_trip { - color: $name; -} - -.post_menu .menu { - background-color: $post; - border:1px solid $border; - - div { - border-right: 1px solid $border; - border-bottom: 1px solid $border; - - form { - button { - color: $link; - - &:hover { - color: $hover; - } - } - } - } -} diff --git a/misc/website/backend/app/src/utils/capture.lua b/misc/website/backend/app/src/utils/capture.lua index 2b5816b..fe2795f 100755 --- a/misc/website/backend/app/src/utils/capture.lua +++ b/misc/website/backend/app/src/utils/capture.lua @@ -1,35 +1,37 @@ -local ngx = _G.ngx -local json = require "cjson" +local ngx = _G.ngx +local json = require"utils.json" local function capture(method, uri, body) - local response = ngx.location.capture(uri, { - method = method, - body = json.encode(body) - }) + local response = ngx.location.capture(uri, { + method = method, + body = json.encode(body), + }) - if response.truncated then return end + if response.truncated then + return + end - if response.status ~= ngx.HTTP_OK then - return nil, json.decode(response.body) - end + if response.status ~= ngx.HTTP_OK then + return nil, json.decode(response.body) + end - return json.decode(response.body) + return json.decode(response.body) end return { - get = function(...) - return capture(ngx.HTTP_GET, ...) - end, + get = function(...) + return capture(ngx.HTTP_GET, ...) + end, - post = function(...) - return capture(ngx.HTTP_POST, ...) - end, + post = function(...) + return capture(ngx.HTTP_POST, ...) + end, - put = function(...) - return capture(ngx.HTTP_PUT, ...) - end, + put = function(...) + return capture(ngx.HTTP_PUT, ...) + end, - delete = function(...) - return capture(ngx.HTTP_DELETE, ...) - end, + delete = function(...) + return capture(ngx.HTTP_DELETE, ...) + end, } diff --git a/misc/website/backend/app/src/utils/error.lua b/misc/website/backend/app/src/utils/error.lua index c6644bc..c157d06 100755 --- a/misc/website/backend/app/src/utils/error.lua +++ b/misc/website/backend/app/src/utils/error.lua @@ -1,64 +1,94 @@ -local ngx = _G.ngx +local ngx = _G.ngx local get_error = {} -local status = {} +local status = {} ---[[ API Error Codes ]]-- +--[[ API Error Codes ]] -- -- Authorization -- email:api_key format in Authorization HTTP header is invalid function get_error.malformed_authorization() - return { code=100 } + return { + code = 100, + } end -- email:api_key in Authorization HTTP header does not match any user -- login credentials do not match any user function get_error.invalid_authorization() - return { code=101 } + return { + code = 101, + } end -- Attempting to access endpoint that requires higher priviliges function get_error.unauthorized_access() - return { code=102 } + return { + code = 102, + } end -- Data Validation function get_error.field_not_found(field) - return { code=200, field=field } + return { + code = 200, + field = field, + } end function get_error.field_invalid(field) - return { code=201, field=field } + return { + code = 201, + field = field, + } end function get_error.field_not_unique(field) - return { code=202, field=field } + return { + code = 202, + field = field, + } end function get_error.token_expired(field) - return { code=203, field=field } + return { + code = 203, + field = field, + } end function get_error.password_not_match() - return { code=204 } + return { + code = 204, + } end -- Database I/O function get_error.database_unresponsive() - return { code=300 } + return { + code = 300, + } end function get_error.database_create() - return { code=301 } + return { + code = 301, + } end function get_error.database_modify() - return { code=302 } + return { + code = 302, + } end function get_error.database_delete() - return { code=303 } + return { + code = 303, + } end function get_error.database_select() - return { code=304 } + return { + code = 304, + } end ---[[ API -> HTTP Code Map ]]-- +--[[ API -> HTTP Code Map ]] -- -- Authorization status[100] = ngx.HTTP_BAD_REQUEST @@ -79,26 +109,26 @@ status[303] = ngx.HTTP_INTERNAL_SERVER_ERROR status[304] = ngx.HTTP_INTERNAL_SERVER_ERROR return { - get_error = get_error, - handle = function(self) - - -- Inject localized error messages - for _, err in ipairs(self.errors) do - --err.message = self.i18n(err.code) - if type(err) == "table" then - for k, v in pairs(err) do - print(k, ": ", v) - end - else - print(err) - end - end - - print(#self.errors) - - return self:write { - status = 401,--status[self.errors[1].code], - json = self.errors - } - end + get_error = get_error, + handle = function(self) + + -- Inject localized error messages + for _, err in ipairs(self.errors) do + -- err.message = self.i18n(err.code) + if type(err) == "table" then + for k, v in pairs(err) do + print(k, ": ", v) + end + else + print(err) + end + end + + print(#self.errors) + + return self:write{ + status = 401, -- status[self.errors[1].code], + json = self.errors, + } + end, } diff --git a/misc/website/backend/app/src/utils/file_whitelist.lua b/misc/website/backend/app/src/utils/file_whitelist.lua index 78aa552..aefd14d 100755 --- a/misc/website/backend/app/src/utils/file_whitelist.lua +++ b/misc/website/backend/app/src/utils/file_whitelist.lua @@ -1,22 +1,22 @@ -- A whitelist of filetypes return { - -- Image formats - image = { - [".bmp"] = true, - [".png"] = true, - [".gif"] = true, - [".jpg"] = true, - [".jpeg"] = true, - [".webp"] = true, - [".webm"] = true, - [".svg"] = true - }, + -- Image formats + image = { + [".bmp"] = true, + [".png"] = true, + [".gif"] = true, + [".jpg"] = true, + [".jpeg"] = true, + [".webp"] = true, + [".webm"] = true, + [".svg"] = true, + }, - -- Audio formats - audio = { - [".wav"] = true, - [".flac"] = true, - [".mp3"] = true, - [".ogg"] = true - } + -- Audio formats + audio = { + [".wav"] = true, + [".flac"] = true, + [".mp3"] = true, + [".ogg"] = true, + }, } diff --git a/misc/website/backend/app/src/utils/generate.lua b/misc/website/backend/app/src/utils/generate.lua index 2dd32a4..d95d2f9 100755 --- a/misc/website/backend/app/src/utils/generate.lua +++ b/misc/website/backend/app/src/utils/generate.lua @@ -1,29 +1,28 @@ - -local config = require("lapis.config").get() +local config = require("lapis.config").get() local encoding = require "lapis.util.encoding" -local sha256 = require "resty.sha256" -local ffi = require "ffi" -local posix = require "posix" -local salt = loadfile("../data/secrets/salt.lua")() -local token = config.secret -local sf = string.format -local ss = string.sub +local sha256 = require "resty.sha256" +local ffi = require "ffi" +local posix = require "posix" +local salt = loadfile("../data/secrets/salt.lua")() +local token = config.secret +local sf = string.format +local ss = string.sub local function get_chunks(str) - -- Secure trip - local name, tripcode = str:match("(.-)(##.+)") + -- Secure trip + local name, tripcode = str:match("(.-)(##.+)") - -- Insecure trip - if not name then - name, tripcode = str:match("(.-)(#.+)") + -- Insecure trip + if not name then + name, tripcode = str:match("(.-)(#.+)") - -- Just a name - if not name then - return str:match("(.+)") - end - end + -- Just a name + if not name then + return str:match("(.+)") + end + end - return name, tripcode + return name, tripcode end local generate = {} @@ -31,23 +30,23 @@ local generate = {} -- math.random isn't reliable for this use case, so instead we're gonna snag -- some bytes from /dev/urandom, create a uint32, and grab the last 3 digits. function generate.random() - -- Read uint32_t from /dev/urandom - local r = io.open("/dev/urandom", "rb") - local bytes = r:read(4) - r:close() + -- Read uint32_t from /dev/urandom + local r = io.open("/dev/urandom", "rb") + local bytes = r:read(4) + r:close() - -- Build number - local num = ffi.new("unsigned int[1]") - ffi.copy(num, bytes, 4) + -- Build number + local num = ffi.new("unsigned int[1]") + ffi.copy(num, bytes, 4) - return sf("%03d", num[0] % 1000) + return sf("%03d", num[0] % 1000) end -- Generate an insecure password function generate.password(time) - local hasher = sha256:new() - hasher:update(sf("%s%s", time, generate.random())) - return encoding.encode_base64(hasher:final()) + local hasher = sha256:new() + hasher:update(sf("%s%s", time, generate.random())) + return encoding.encode_base64(hasher:final()) end -- Generate a secure or insecure tripcode based off the name a user supplies @@ -55,40 +54,40 @@ end -- Secure tripcodes use sha256 + the app's secret token. -- Insecure tripcodes use standard posix crypt + the app's secret salt. function generate.tripcode(raw_name) - local name, tripcode = get_chunks(raw_name) - - if tripcode then - local pattern = "^([^=]*)" - tripcode = tripcode:sub(2) -- remove leading '#' - - -- Secure tripcode - if tripcode:sub(1, 1) == "#" then - local hasher = sha256:new() - tripcode = token .. tripcode:sub(2) -- remove leading '#' - hasher:update(tripcode) - local hash = encoding.encode_base64(hasher:final()) - tripcode = "!!" .. ss(hash:match(pattern), -10) - -- Insecure tripcode - else - local hash = posix.crypt(tripcode, salt) - tripcode = "!" .. ss(hash, -10) - end - end - - return name, tripcode + local name, tripcode = get_chunks(raw_name) + + if tripcode then + local pattern = "^([^=]*)" + tripcode = tripcode:sub(2) -- remove leading '#' + + -- Secure tripcode + if tripcode:sub(1, 1) == "#" then + local hasher = sha256:new() + tripcode = token .. tripcode:sub(2) -- remove leading '#' + hasher:update(tripcode) + local hash = encoding.encode_base64(hasher:final()) + tripcode = "!!" .. ss(hash:match(pattern), -10) + -- Insecure tripcode + else + local hash = posix.crypt(tripcode, salt) + tripcode = "!" .. ss(hash, -10) + end + end + + return name, tripcode end function generate.errors(i18n, errors) - local err = {} + local err = {} - if #errors > 0 then - for _, error in ipairs(errors) do - local e = i18n(unpack(error)) - table.insert(err, e) - end - end + if #errors > 0 then + for _, error in ipairs(errors) do + local e = i18n(unpack(error)) + table.insert(err, e) + end + end - return err + return err end return generate diff --git a/misc/website/backend/app/src/utils/json.lua b/misc/website/backend/app/src/utils/json.lua new file mode 100644 index 0000000..27f4a21 --- /dev/null +++ b/misc/website/backend/app/src/utils/json.lua @@ -0,0 +1,42 @@ +local cjson = require("cjson") +local xpcall = xpcall + +-- 设置 处理稀疏数组 +-- https://www.kyne.com.au/~mark/software/lua-cjson-manual.html#encode_sparse_array +cjson.encode_sparse_array(true, 1, 1) + +-- https://github.com/cloudwu/lua-cjson/pull/8 +cjson.encode_empty_table_as_array("on") + +-- https://github.com/cloudwu/lua-cjson/pull/10 +cjson.encode_number_precision(16) + +local json = {} + +function json.encode(var) + local function _logExce(err) + print("json encode", err) + end + + local status, result = xpcall(cjson.encode, _logExce, var) + if status then + return result + end +end + +function json.decode(text) + if not text then + return + end + + local function _logExce(err) + print("json decode", text, err) + end + + local status, result = xpcall(cjson.decode, _logExce, text) + if status then + return result + end +end + +return json diff --git a/misc/website/backend/app/src/utils/request_processor.lua b/misc/website/backend/app/src/utils/request_processor.lua deleted file mode 100755 index 3e4cec7..0000000 --- a/misc/website/backend/app/src/utils/request_processor.lua +++ /dev/null @@ -1,298 +0,0 @@ -local Bans = require "models.bans" -local Threads = require "models.threads" -local Posts = require "models.posts" -local Reports = require "models.reports" -local process = {} - -function process.create_thread(params, session, board) - - -- Prepare data for entry - local _, err = Posts:prepare_post(params, session, board) - if err then - return false, err - end - - -- Archive old threads - local max_threads = board.threads_per_page * board.pages - Threads:archive_threads(board.id, max_threads) - - -- Delete old archived threads - local time = os.time() - local threads = Threads:get_archived_threads(board.id) - - for _, t in ipairs(threads) do - if time - t.last_active > board.archive_time and not t.save then - local posts = Posts:get_thread_posts(t.id) - Threads:delete_thread("override", t, posts[1]) - - -- Delete all associated posts - for _, post in ipairs(posts) do - Posts:delete_post("override", board, post) - - -- Delete associated report - local report = Reports:get_report(post.id) - if report then - Reports:delete_report(report) - end - end - end - end - - -- Insert post data into database - local post, err = Posts:create_post( - params, - session, - board, - thread, - true - ) - if err then - return false, err - end - - return post -end - -function process.create_post(params, session, board, thread) - local posts = Posts:count_posts(thread.id) - local files = Posts:count_files(thread.id) - - -- Prepare data for entry - local _, err = Posts:prepare_post( - params, session, board, thread, files - ) - if err then - return false, err - end - - -- Insert post data into database - local post, err = Posts:create_post( - params, - session, - board, - thread, - false - ) - if err then - return false, err - end - - posts = posts + 1 - - -- Check for [auto]sage - if params.options ~= "sage" and - posts <= board.post_limit then - -- Update thread - thread.last_active = os.time() - thread:update("last_active") - end - - return post -end - -function process.delete_thread(params, session, board) - -- Validate post - local post = Posts:get_post(board.id, params.thread_id) - if not post then - return false, { "err_invalid_post", { params.thread_id } } - end - - -- Validate thread - local thread = Threads:get_thread(post.thread_id) - if not thread then - return false, { "err_invalid_thread" } - end - - local posts = Posts:get_posts_by_thread(thread.id) - - -- Delete thread - local _, err = Threads:delete_thread(session, thread, posts[1]) - if err then - return false, err - end - - -- Delete all associated posts - for _, post in ipairs(posts) do - Posts:delete_post("override", board, post) - - -- Delete associated report - local report = Reports:get_report(board.id, post.id) - if report then - Reports:delete_report(report) - end - end - - return true -end - -function process.delete_post(params, session, board) - -- Validate post - local post = Posts:get_post(board.id, params.post_id) - if not post then - return false, { "err_invalid_post", { params.post_id } } - end - - -- Validate thread - local thread = Threads:get_thread(post.thread_id) - if not thread then - return false, { "err_invalid_thread" } - end - - -- Delete post - local _, err = Posts:delete_post(session, board, post) - if err then - return false, err - end - - -- Update thread - local posts = Posts:get_posts_by_thread(thread.id) - thread.last_active = posts[#posts].timestamp - thread:update("last_active") - - return true -end - -function process.report_post(params, board) - -- Validate post - local post = Posts:get_post(board.id, params.post_id) - if not post then - return false, { "err_invalid_post", { params.thread } } - end - - local report = Reports:get_report(board.id, post.post_id) - - -- If report exists, update it - if report then - report.num_reports = report.num_reports + 1 -- FIXME: race condition - local _, err = Reports:modify_report(report) - if err then - return false, err - end - -- If report is new, create it - else - local _, err = Reports:create_report { - board_id = board.id, - thread_id = post.thread_id, - post_id = post.post_id, - timestamp = os.time(), - num_reports = 1 - } - if err then - return false, err - end - end - - return post -end - --- Sticky thread -function process.sticky_thread(params, board) - -- Validate post - local post = Posts:get_post(board.id, params.post_id) - if not post then - return false, { "err_invalid_post", { params.post_id } } - end - - -- Validate thread - local thread = Threads:get_thread(post.thread_id) - if not thread then - return false, { "err_invalid_thread" } - end - - thread.sticky = not thread.sticky - thread:update("sticky") - - return true -end - --- Lock thread -function process.lock_thread(params, board) - -- Validate post - local post = Posts:get_post(board.id, params.post_id) - if not post then - return false, { "err_invalid_post", { params.post_id } } - end - - -- Validate thread - local thread = Threads:get_thread(post.thread_id) - if not thread then - return false, { "err_invalid_thread" } - end - - thread.lock = not thread.lock - thread:update("lock") - - return true -end - --- Save thread -function process.save_thread(params, board) - -- Validate post - local post = Posts:get_post(board.id, params.post_id) - if not post then - return false, { "err_invalid_post", { params.post_id } } - end - - -- Validate thread - local thread = Threads:get_thread(post.thread_id) - if not thread then - return false, { "err_invalid_thread" } - end - - thread.save = not thread.save - thread:update("save") - - return true -end - --- Override thread -function process.override_thread(params, board) - -- Validate post - local post = Posts:get_post(board.id, params.post_id) - if not post then - return false, { "err_invalid_post", { params.post_id } } - end - - -- Validate thread - local thread = Threads:get_thread(post.thread_id) - if not thread then - return false, { "err_invalid_thread" } - end - - thread.size_override = not thread.size_override - thread:update("size_override") - - return true -end - --- Ban user -function process.ban_user(params, board) - -- Validate post - local post = Posts:get_post(board.id, params.post_id) - if not post then - return false, { "err_invalid_post", { params.post_id } } - end - - params.ip = post.ip - - -- Convert board name to id if checkbox is set - if params.board_id then - params.board_id = board.id - end - - -- Ban user - local _, err = Bans:create_ban(params) - if err then - return false, err - end - - -- Flag post - if params.banned then - post.banned = true - post:update("banned") - end - - return true -end - -return process diff --git a/misc/website/backend/app/src/utils/role.lua b/misc/website/backend/app/src/utils/role.lua index 87062ce..f4c22a0 100755 --- a/misc/website/backend/app/src/utils/role.lua +++ b/misc/website/backend/app/src/utils/role.lua @@ -1,31 +1,31 @@ -local models = require "models" -local get_error = require "utils.error".get_error -local Users = models.users -local role = {} +local models = require "models" +local get_error = require"utils.error".get_error +local Users = models.users +local role = {} -- User must be the Owner function role.owner(user) - return user.role == Users.role.OWNER and true or nil, get_error.unauthorized_access() + return user.role == Users.role.OWNER and true or nil, get_error.unauthorized_access() end -- User must be an Admin or higher function role.admin(user) - return user.role >= Users.role.ADMIN and true or nil, get_error.unauthorized_access() + return user.role >= Users.role.ADMIN and true or nil, get_error.unauthorized_access() end -- User must be a Mod or higher function role.mod(user) - return user.role >= Users.role.MOD and true or nil, get_error.unauthorized_access() + return user.role >= Users.role.MOD and true or nil, get_error.unauthorized_access() end -- User must be a Janitor or higher function role.janitor(user) - return user.role >= Users.role.JANITOR and true or nil, get_error.unauthorized_access() + return user.role >= Users.role.JANITOR and true or nil, get_error.unauthorized_access() end -- User must be signed in function role.user(user) - return user.role >= Users.role.USER and true or nil, get_error.unauthorized_access() + return user.role >= Users.role.USER and true or nil, get_error.unauthorized_access() end return role diff --git a/misc/website/backend/app/src/utils/text_formatter.lua b/misc/website/backend/app/src/utils/text_formatter.lua index ae1f44a..f88e1ef 100755 --- a/misc/website/backend/app/src/utils/text_formatter.lua +++ b/misc/website/backend/app/src/utils/text_formatter.lua @@ -1,20 +1,20 @@ -local Posts = require "models.posts" -local escape = require("lapis.html").escape -local sf = string.format +local Posts = require "models.posts" +local escape = require("lapis.html").escape +local sf = string.format local formatter = {} --- Sanitize text for HTML safety -- @tparam string text Raw text -- @treturn string formatted function formatter.sanitize(text) - return escape(text) + return escape(text) end --- Format new lines to 'br' tags -- @tparam string text Raw text -- @treturn string formatted function formatter.new_lines(text) - return text:gsub("\n", "
\n") + return text:gsub("\n", "
\n") end --- Format words that begin with '>>' @@ -24,137 +24,150 @@ end -- @tparam table post Post data -- @treturn string formatted function formatter.quote(text, request, board, post) - local function get_url(board, post_id) - if tonumber(post_id) then - local p = Posts:get(board.id, post_id) - if not p then return false end - - local thread = p:get_thread() - if not thread then return false end - - local op = thread:get_op() - return - request:url_for("web.boards.thread", { board=board.name, thread=op.post_id }), - op - else - return request:url_for("web.boards.board", { board=board.name }) - end - end - - -- >>1234 ur a fag - -- >>(%d+) - local match_pattern = ">>(%d+)" - local sub_pattern = ">>%s" - - -- Get all the matches and store them in an ordered list - local posts = {} - for post_id in text:gmatch(match_pattern) do - table.insert(posts, { board=board, id=post_id }) - end - - -- Format each match - for i, p in ipairs(posts) do - local text = sf(sub_pattern, p.id) - local url, op = get_url(p.board, p.id) - if url then - if op.thread_id == post.thread_id then - posts[i] = sf("%s", url, p.id, text) - else - posts[i] = sf("%s→", url, p.id, text) - end - else - posts[i] = sf("%s", text) - end - end - - -- Substitute each match with the formatted match - local i = 0 - text = text:gsub(match_pattern, function() - i = i + 1 - return posts[i] - end) - - -- >>>/a/1234 check over here - -- >>>/(%w+)/(%d*) - match_pattern = ">>>/(%w+)/(%d*)" - sub_pattern = ">>>/%s/%s" - - -- Get all the matches and store them in an ordered list - posts = {} - for b, post_id in text:gmatch(match_pattern) do - local response = request.api.boards.GET(request) - b = response.json or b - table.insert(posts, { board=b, id=post_id }) - end - - -- Format each match - for i, p in ipairs(posts) do - if type(p.board) == "table" then - local text = sf(sub_pattern, p.board.name, p.id) - local url, op = get_url(p.board, p.id) - if op then - posts[i] = sf("%s", url, p.id, text) - else - posts[i] = sf("%s", url, text) - end - else - local text = sf(sub_pattern, p.board, p.id) - posts[i] = sf("%s", text) - end - end - - -- Substitute each match with the formatted match - i = 0 - text = text:gsub(match_pattern, function() - i = i + 1 - return posts[i] - end) - - return text + local function get_url(board, post_id) + if tonumber(post_id) then + local p = Posts:get(board.id, post_id) + if not p then + return false + end + + local thread = p:get_thread() + if not thread then + return false + end + + local op = thread:get_op() + return request:url_for("web.boards.thread", { + board = board.name, + thread = op.post_id, + }), op + else + return request:url_for("web.boards.board", { + board = board.name, + }) + end + end + + -- >>1234 ur a fag + -- >>(%d+) + local match_pattern = ">>(%d+)" + local sub_pattern = ">>%s" + + -- Get all the matches and store them in an ordered list + local posts = {} + for post_id in text:gmatch(match_pattern) do + table.insert(posts, { + board = board, + id = post_id, + }) + end + + -- Format each match + for i, p in ipairs(posts) do + local text = sf(sub_pattern, p.id) + local url, op = get_url(p.board, p.id) + if url then + if op.thread_id == post.thread_id then + posts[i] = sf("%s", url, p.id, text) + else + posts[i] = sf("%s→", url, p.id, text) + end + else + posts[i] = sf("%s", text) + end + end + + -- Substitute each match with the formatted match + local i = 0 + text = text:gsub(match_pattern, function() + i = i + 1 + return posts[i] + end) + + -- >>>/a/1234 check over here + -- >>>/(%w+)/(%d*) + match_pattern = ">>>/(%w+)/(%d*)" + sub_pattern = ">>>/%s/%s" + + -- Get all the matches and store them in an ordered list + posts = {} + for b, post_id in text:gmatch(match_pattern) do + local response = request.api.boards.GET(request) + b = response.json or b + table.insert(posts, { + board = b, + id = post_id, + }) + end + + -- Format each match + for i, p in ipairs(posts) do + if type(p.board) == "table" then + local text = sf(sub_pattern, p.board.name, p.id) + local url, op = get_url(p.board, p.id) + if op then + posts[i] = sf("%s", url, p.id, text) + else + posts[i] = sf("%s", url, text) + end + else + local text = sf(sub_pattern, p.board, p.id) + posts[i] = sf("%s", text) + end + end + + -- Substitute each match with the formatted match + i = 0 + text = text:gsub(match_pattern, function() + i = i + 1 + return posts[i] + end) + + return text end --- Format lines that begin with '>' -- @tparam string text Raw text -- @treturn string formatted function formatter.green_text(text) - local formatted = "" + local formatted = "" - for line in text:gmatch("[^\n]+") do - local first = line:sub(1, 4) + for line in text:gmatch("[^\n]+") do + local first = line:sub(1, 4) - -- >implying - if first == ">" then - line = sf("%s%s%s", "", line, "") - end + -- >implying + if first == ">" then + line = sf("%s%s%s", "", line, "") + end - formatted = sf("%s%s%s", formatted, line, "\n") - end + formatted = sf("%s%s%s", formatted, line, "\n") + end - return formatted + return formatted end --- Format lines that begin with '<' -- @tparam string text Raw text -- @treturn string formatted function formatter.blue_text(text) - local formatted = "" + local formatted = "" - for line in text:gmatch("[^\n]+") do - local first = line:sub(1, 4) + for line in text:gmatch("[^\n]+") do + local first = line:sub(1, 4) - -- ", line, "") - end + -- ", line, "") + end - formatted = sf("%s%s%s", formatted, line, "\n") - end + formatted = sf("%s%s%s", formatted, line, "\n") + end - return formatted + return formatted end function formatter.spoiler(text) - return text:gsub("(%[spoiler%])(.-)(%[/spoiler%])", "%2") + return text:gsub("(%[spoiler%])(.-)(%[/spoiler%])", "%2") end return formatter diff --git a/misc/website/backend/app/src/views/admin/admin.etlua b/misc/website/backend/app/src/views/admin/admin.etlua deleted file mode 100755 index 0619f36..0000000 --- a/misc/website/backend/app/src/views/admin/admin.etlua +++ /dev/null @@ -1,247 +0,0 @@ -

<%= page_title %>

- -
- - -
-
- - -
- - - - -
-
-
- -
-
- - -
- - - - -
-
-
- - -
-
-
- - - - -
-
-
- -
-
- - -
- - - - -
-
-
- -
-
- - -
- - - - -
-
-
- - -
-
-
- - - - -
-
-
- -
-
- - -
- - - - -
-
-
- -
-
- - -
- - - - -
-
-
- - -
-
-
- - - - -
-
-
- -
-
- - -
- - - - -
-
-
- -
-
- - -
- - - - -
-
-
- - -
-
-
- - - - -
-
-
- -
-
- - -
- - - - -
-
-
- -
-
- - -
- - - - -
-
-
- - -
-
- -
- - - - -
-
-
- -
diff --git a/misc/website/backend/app/src/views/admin/announcement.etlua b/misc/website/backend/app/src/views/admin/announcement.etlua deleted file mode 100755 index 780c76d..0000000 --- a/misc/website/backend/app/src/views/admin/announcement.etlua +++ /dev/null @@ -1,30 +0,0 @@ -
-
- - -
- - - - -
- -
- - - - -
- - <% if params.action == "create" then %> - - <% elseif params.action == "modify" then %> - - <% end %> -
-
diff --git a/misc/website/backend/app/src/views/admin/board.etlua b/misc/website/backend/app/src/views/admin/board.etlua deleted file mode 100755 index 9248868..0000000 --- a/misc/website/backend/app/src/views/admin/board.etlua +++ /dev/null @@ -1,189 +0,0 @@ -
-
- - -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- - <% if params.action == "create" then %> - - <% elseif params.action == "modify" then %> - - <% end %> -
-
diff --git a/misc/website/backend/app/src/views/admin/login.etlua b/misc/website/backend/app/src/views/admin/login.etlua deleted file mode 100755 index 7194224..0000000 --- a/misc/website/backend/app/src/views/admin/login.etlua +++ /dev/null @@ -1,21 +0,0 @@ -
-
- - -
- - - - -
- -
- - - - -
- - -
-
diff --git a/misc/website/backend/app/src/views/admin/page.etlua b/misc/website/backend/app/src/views/admin/page.etlua deleted file mode 100755 index 4ab4e75..0000000 --- a/misc/website/backend/app/src/views/admin/page.etlua +++ /dev/null @@ -1,33 +0,0 @@ -
-
- - - -
- - - - -
- -
- - - - -
- -
- - - - -
- - <% if params.action == "create" then %> - - <% elseif params.action == "modify" then %> - - <% end %> -
-
diff --git a/misc/website/backend/app/src/views/admin/success.etlua b/misc/website/backend/app/src/views/admin/success.etlua deleted file mode 100755 index 2e96583..0000000 --- a/misc/website/backend/app/src/views/admin/success.etlua +++ /dev/null @@ -1,3 +0,0 @@ -

Success

-

<%= action %>

-[<%= i18n("return") %>] diff --git a/misc/website/backend/app/src/views/admin/user.etlua b/misc/website/backend/app/src/views/admin/user.etlua deleted file mode 100755 index dd0a932..0000000 --- a/misc/website/backend/app/src/views/admin/user.etlua +++ /dev/null @@ -1,71 +0,0 @@ -
-
- - -
- - - - -
- - <% if params.action == "modify" then %> -
- - - - -
- <% end %> - -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- -
- - - - -
- - <% if params.action == "create" then %> - - <% elseif params.action == "modify" then %> - - <% end %> -
-
diff --git a/misc/website/backend/app/src/views/archive.etlua b/misc/website/backend/app/src/views/archive.etlua deleted file mode 100755 index a5d7a57..0000000 --- a/misc/website/backend/app/src/views/archive.etlua +++ /dev/null @@ -1,49 +0,0 @@ -<% render('views.fragments.board_title') %> -<% render('views.fragments.announcements') %> -
-[<%= i18n('return') %>] -[<%= i18n('catalog') %>] -[<%= i18n('bottom') %>] -
-

- - <%= i18n('arc_display', { - n_thread = #threads, - p_thread = i18n('threads', { count=#threads }), - n_day = days, - p_day = i18n('days', { count=days }) - }) %> - -

- - - - - - - - - - - - <% for _, thread in ipairs(threads) do %> - - - - - - - - <% end %> - -
<%= i18n('arc_number') %><%= i18n('arc_name') %><%= i18n('arc_excerpt') %><%= i18n('arc_replies') %>
<%= thread.op.post_id %><%= thread.op.name %> <%= thread.op.trip %> - <% if thread.op.subject then %> - <%= thread.op.subject %>: - <% end %> - <%- thread.op.comment %> - <%= thread.replies %>[<%= i18n('arc_view') %>]
-
-[<%= i18n('return') %>] -[<%= i18n('catalog') %>] -[<%= i18n('top') %>] -
diff --git a/misc/website/backend/app/src/views/banned.etlua b/misc/website/backend/app/src/views/banned.etlua deleted file mode 100755 index c94fc89..0000000 --- a/misc/website/backend/app/src/views/banned.etlua +++ /dev/null @@ -1,8 +0,0 @@ -

<%= page_title %>

-
-

<%= i18n('ban_reason') %>

-

<%= reason %>

-

<%- i18n('ban_expire', { expire=''..expire..'' }) %>

-

<%- i18n('ban_ip', { ip=''..ip..'' }) %>

-
Catfish.
-
diff --git a/misc/website/backend/app/src/views/board.etlua b/misc/website/backend/app/src/views/board.etlua deleted file mode 100755 index 8590492..0000000 --- a/misc/website/backend/app/src/views/board.etlua +++ /dev/null @@ -1,36 +0,0 @@ -<% render('views.fragments.board_title') %> -<% render('views.fragments.form_submit') %> -<% render('views.fragments.announcements') %> -
-[<%= i18n('catalog') %>] -[<%= i18n('archive') %>] -[<%= i18n('refresh') %>] -
-<% for _, thread in ipairs(threads) do %> -
-<% - local posts = thread.posts or {} - for i=#posts, 1, -1 do - local post = posts[i] - local op = posts[#posts] - if post.post_id == op.post_id then - render('views.fragments.op_content', { thread=thread, post=post, posts=posts, is_board=true }) - else - render('views.fragments.post_content', { thread=thread, post=post, posts=posts, is_board=true, op=op }) - end - end -%> -
-
-<% end %> -
- <% for i=1, pages do %> - <% if i == params.page then %> - [<%= i %>] - <% else %> - [<%= i %>] - <% end %> - <% end %> - [<%= i18n('catalog') %>] - [<%= i18n('archive') %>] -
diff --git a/misc/website/backend/app/src/views/catalog.etlua b/misc/website/backend/app/src/views/catalog.etlua deleted file mode 100755 index 108ea8d..0000000 --- a/misc/website/backend/app/src/views/catalog.etlua +++ /dev/null @@ -1,34 +0,0 @@ -<% render('views.fragments.board_title') %> -<% render('views.fragments.form_submit') %> -<% render('views.fragments.announcements') %> -
-[<%= i18n('return') %>] -[<%= i18n('archive') %>] -[<%= i18n('bottom') %>] -[<%= i18n('refresh') %>] -
-<% for t, thread in ipairs(threads) do %> - -<% end %> -
-[<%= i18n('return') %>] -[<%= i18n('archive') %>] -[<%= i18n('top') %>] -[<%= i18n('refresh') %>] -
diff --git a/misc/website/backend/app/src/views/code_404.etlua b/misc/website/backend/app/src/views/code_404.etlua deleted file mode 100755 index bd5f8f1..0000000 --- a/misc/website/backend/app/src/views/code_404.etlua +++ /dev/null @@ -1,2 +0,0 @@ -

<%= page_title %>

-

<%= i18n("return_index") %>

diff --git a/misc/website/backend/app/src/views/fragments/announcements.etlua b/misc/website/backend/app/src/views/fragments/announcements.etlua deleted file mode 100755 index 9a4f08c..0000000 --- a/misc/website/backend/app/src/views/fragments/announcements.etlua +++ /dev/null @@ -1,6 +0,0 @@ -<% if #announcements > 0 then %> -
-<% for _, announcement in ipairs(announcements) do %> -

<%= announcement.text %>

-<% end %> -<% end %> diff --git a/misc/website/backend/app/src/views/fragments/board_title.etlua b/misc/website/backend/app/src/views/fragments/board_title.etlua deleted file mode 100755 index 0c6358b..0000000 --- a/misc/website/backend/app/src/views/fragments/board_title.etlua +++ /dev/null @@ -1,6 +0,0 @@ -
-

<%= page_title %>

- <% if board.subtext then %> -

<%= board.subtext %>

- <% end %> -
diff --git a/misc/website/backend/app/src/views/fragments/copyright.etlua b/misc/website/backend/app/src/views/fragments/copyright.etlua deleted file mode 100755 index 2a06a9d..0000000 --- a/misc/website/backend/app/src/views/fragments/copyright.etlua +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/misc/website/backend/app/src/views/fragments/error.etlua b/misc/website/backend/app/src/views/fragments/error.etlua deleted file mode 100755 index cb0bdb3..0000000 --- a/misc/website/backend/app/src/views/fragments/error.etlua +++ /dev/null @@ -1,6 +0,0 @@ -
-

/!\ ALART /!\

-<% for _, err in ipairs(errors) do %> -

<%= err %>

-<% end %> -
diff --git a/misc/website/backend/app/src/views/fragments/form_ban.etlua b/misc/website/backend/app/src/views/fragments/form_ban.etlua deleted file mode 100755 index f4d72bf..0000000 --- a/misc/website/backend/app/src/views/fragments/form_ban.etlua +++ /dev/null @@ -1,15 +0,0 @@ -
- - - <% if post.thread then %> - - <% end %> - - <%= i18n("form_ban_display") %> -
- <%= i18n("form_ban_board") %> -
- " /> - " />
- -
diff --git a/misc/website/backend/app/src/views/fragments/form_delete.etlua b/misc/website/backend/app/src/views/fragments/form_delete.etlua deleted file mode 100755 index 2a3855c..0000000 --- a/misc/website/backend/app/src/views/fragments/form_delete.etlua +++ /dev/null @@ -1,9 +0,0 @@ -
- - - <% if post.thread then %> - - <% end %> - - -
diff --git a/misc/website/backend/app/src/views/fragments/form_locale.etlua b/misc/website/backend/app/src/views/fragments/form_locale.etlua deleted file mode 100755 index 456cbb4..0000000 --- a/misc/website/backend/app/src/views/fragments/form_locale.etlua +++ /dev/null @@ -1,10 +0,0 @@ -
-
- - -
-
diff --git a/misc/website/backend/app/src/views/fragments/form_lock.etlua b/misc/website/backend/app/src/views/fragments/form_lock.etlua deleted file mode 100755 index 29693d2..0000000 --- a/misc/website/backend/app/src/views/fragments/form_lock.etlua +++ /dev/null @@ -1,9 +0,0 @@ -
- - - <% if post.thread then %> - - <% end %> - - -
diff --git a/misc/website/backend/app/src/views/fragments/form_override.etlua b/misc/website/backend/app/src/views/fragments/form_override.etlua deleted file mode 100755 index 1d19005..0000000 --- a/misc/website/backend/app/src/views/fragments/form_override.etlua +++ /dev/null @@ -1,9 +0,0 @@ -
- - - <% if post.thread then %> - - <% end %> - - -
diff --git a/misc/website/backend/app/src/views/fragments/form_remix.etlua b/misc/website/backend/app/src/views/fragments/form_remix.etlua deleted file mode 100755 index 6bfae28..0000000 --- a/misc/website/backend/app/src/views/fragments/form_remix.etlua +++ /dev/null @@ -1,9 +0,0 @@ -<% if thread then %> -
- -
-<% else %> -
- -
-<% end %> diff --git a/misc/website/backend/app/src/views/fragments/form_report.etlua b/misc/website/backend/app/src/views/fragments/form_report.etlua deleted file mode 100755 index d1bc1a6..0000000 --- a/misc/website/backend/app/src/views/fragments/form_report.etlua +++ /dev/null @@ -1,6 +0,0 @@ -
- - - - -
diff --git a/misc/website/backend/app/src/views/fragments/form_save.etlua b/misc/website/backend/app/src/views/fragments/form_save.etlua deleted file mode 100755 index 734841f..0000000 --- a/misc/website/backend/app/src/views/fragments/form_save.etlua +++ /dev/null @@ -1,9 +0,0 @@ -
- - - <% if post.thread then %> - - <% end %> - - -
diff --git a/misc/website/backend/app/src/views/fragments/form_sticky.etlua b/misc/website/backend/app/src/views/fragments/form_sticky.etlua deleted file mode 100755 index 94e144b..0000000 --- a/misc/website/backend/app/src/views/fragments/form_sticky.etlua +++ /dev/null @@ -1,9 +0,0 @@ -
- - - <% if post.thread then %> - - <% end %> - - -
diff --git a/misc/website/backend/app/src/views/fragments/form_submit.etlua b/misc/website/backend/app/src/views/fragments/form_submit.etlua deleted file mode 100755 index 46ce1da..0000000 --- a/misc/website/backend/app/src/views/fragments/form_submit.etlua +++ /dev/null @@ -1,144 +0,0 @@ -
-
- - - <% if params.thread then %> - - <% end %> - -
- - - - -
- - <% if not params.thread then %> -
- - - - -
- <% end %> - -
- - - - -
- -
- - - - -
- - <% if not board.text_only and (not num_files or num_files < board.thread_file_limit) then %> -
- - - - -
- - <% if board.draw then %> -
- - - - <%= i18n("form_width") %>: - - <%= i18n("form_height") %>: - - - - -
- <% end %> - -
- - - [ - /> - ] - -
- <% end %> - - <% if not params.thread and (session.admin or session.mod) then %> -
- - - - [ - ">📌 - /> - ] - - - [ - ">🔒 - /> - ] - - - [ - ">💾 - /> - ] - - - [ - ">✔️ - /> - ] - - -
- <% end %> - - -
-

- * - <%= i18n("form_required") %> -

-

- <%- - i18n("form_readme", { -- FIXME - rules = 'Rules', - faq = 'FAQ' - }) - %> -

-
diff --git a/misc/website/backend/app/src/views/fragments/list_boards.etlua b/misc/website/backend/app/src/views/fragments/list_boards.etlua deleted file mode 100755 index d6ac290..0000000 --- a/misc/website/backend/app/src/views/fragments/list_boards.etlua +++ /dev/null @@ -1,16 +0,0 @@ -<% if boards then %> -
- [ <%= i18n('index') %> ] - <% local group = 1 %> - <% for i, board in ipairs(boards) do %> - <% local sub = sub_page or '' %> - <% if board.group ~= group then %> - <% group = board.group %> ] [ - <% else %> - <%= i == 1 and '[' or '/' %> - <% end %> - <%= board.name %> - <%= i == #boards and ']' or '' %> - <% end %> -
-<% end %> diff --git a/misc/website/backend/app/src/views/fragments/op_content.etlua b/misc/website/backend/app/src/views/fragments/op_content.etlua deleted file mode 100755 index 512dc90..0000000 --- a/misc/website/backend/app/src/views/fragments/op_content.etlua +++ /dev/null @@ -1,71 +0,0 @@ -
- <% if post.file_name then %> -
- <%= i18n("file") %>: - <% - if post.file_name:len() > 35 then - local short = string.sub(post.file_name, 1, 25) - local ext = post.file_name:match("^.+(%..+)$") - local name = string.format("%s(...)%s", short, ext) - %> - <%= name %> - <% else %> - <%= post.file_name %> - <% end%> - (<%= post.file_size %> KB<%= post.file_dimensions %><%= post.file_duration %>) -
-
- -
- <% end %> - -
- <%- post.comment %> - <% if post.banned then %> -

(<%= board.ban_message %>)

- <% end %> -
-
-<% if thread.hidden and thread.hidden.posts > 0 then %> -
- <%- - i18n("post_hidden", { - n_post = thread.hidden.posts, - p_post = i18n("posts", { count=thread.hidden.posts }), - n_file = thread.hidden.files, - p_file = i18n("files", { count=thread.hidden.files }), - click = "Click here" - }) - %> -
-<% end %> diff --git a/misc/website/backend/app/src/views/fragments/post_content.etlua b/misc/website/backend/app/src/views/fragments/post_content.etlua deleted file mode 100755 index 385bad2..0000000 --- a/misc/website/backend/app/src/views/fragments/post_content.etlua +++ /dev/null @@ -1,39 +0,0 @@ -
- - <% if post.file_name then %> -
- <%= i18n("file") %>: - <% - if post.file_name:len() > 35 then - local short = string.sub(post.file_name, 1, 25) - local ext = post.file_name:match("^.+(%..+)$") - local name = string.format("%s(...)%s", short, ext) - %> - <%= name %> - <% else %> - <%= post.file_name %> - <% end%> - (<%= post.file_size %> KB<%= post.file_dimensions %><%= post.file_duration %>) -
-
- -
- <% end %> -
- <%- post.comment %> - <% if post.banned then %> -

(<%= board.ban_message %>)

- <% end %> -
-
diff --git a/misc/website/backend/app/src/views/fragments/post_menu.etlua b/misc/website/backend/app/src/views/fragments/post_menu.etlua deleted file mode 100755 index acdbbee..0000000 --- a/misc/website/backend/app/src/views/fragments/post_menu.etlua +++ /dev/null @@ -1,21 +0,0 @@ -
- - -
diff --git a/misc/website/backend/app/src/views/fragments/return_board.etlua b/misc/website/backend/app/src/views/fragments/return_board.etlua deleted file mode 100755 index 87b193e..0000000 --- a/misc/website/backend/app/src/views/fragments/return_board.etlua +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/misc/website/backend/app/src/views/fragments/return_thread.etlua b/misc/website/backend/app/src/views/fragments/return_thread.etlua deleted file mode 100755 index 77e5193..0000000 --- a/misc/website/backend/app/src/views/fragments/return_thread.etlua +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/misc/website/backend/app/src/views/index.etlua b/misc/website/backend/app/src/views/index.etlua deleted file mode 100755 index a50ad8e..0000000 --- a/misc/website/backend/app/src/views/index.etlua +++ /dev/null @@ -1,16 +0,0 @@ -

<%= site_name %>

-
-

<%= i18n('index_boards') %>

- <% for _, board in ipairs(boards) do %> -
- /<%= board.name %>/ - <%= board.title %> - <% if board.subtext then %> - <%= board.subtext %> - <% end %> - <% if board.rules then %> -

<%= i18n('rules') %>

- <%- markdown(board.rules) %> - <% end %> -
- <% end %> -
diff --git a/misc/website/backend/app/src/views/install.etlua b/misc/website/backend/app/src/views/install.etlua deleted file mode 100755 index a049b6d..0000000 --- a/misc/website/backend/app/src/views/install.etlua +++ /dev/null @@ -1,132 +0,0 @@ -

Install Lapis-chan

- -

Welcome to Lapis-chan v1.0.0!

- -
-
-

Administrator Account Information

- - - - - - - -

First Board Information

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
diff --git a/misc/website/backend/app/src/views/layout.etlua b/misc/website/backend/app/src/views/layout.etlua deleted file mode 100755 index 3c8f594..0000000 --- a/misc/website/backend/app/src/views/layout.etlua +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - <% if board then %> - - <% if board.draw then %> - - - <% end %> - <% end %> - - <%= page_title %> - - - <% if errors then render('views.fragments.error') end %> - <% render('views.fragments.list_boards') %> - - <% content_for('inner') %> - <% render('views.fragments.list_boards') %> - <% render('views.fragments.copyright') %> - - diff --git a/misc/website/backend/app/src/views/page.etlua b/misc/website/backend/app/src/views/page.etlua deleted file mode 100755 index f4abf3a..0000000 --- a/misc/website/backend/app/src/views/page.etlua +++ /dev/null @@ -1,2 +0,0 @@ -

<%= page.name %>

-<%- page.content %> diff --git a/misc/website/backend/app/src/views/rules.etlua b/misc/website/backend/app/src/views/rules.etlua deleted file mode 100755 index 4c9ccc3..0000000 --- a/misc/website/backend/app/src/views/rules.etlua +++ /dev/null @@ -1,17 +0,0 @@ -

<%= i18n("rules") %>

- - - - - - - - - <% for _, board in ipairs(boards) do %> - - - - - <% end %> - -
<%= i18n("board") %><%= i18n("rules") %>
<%= board.title %><%- board.rules %>
diff --git a/misc/website/backend/app/src/views/thread.etlua b/misc/website/backend/app/src/views/thread.etlua deleted file mode 100755 index 2230e92..0000000 --- a/misc/website/backend/app/src/views/thread.etlua +++ /dev/null @@ -1,26 +0,0 @@ -<% render('views.fragments.board_title') %> -<% if not thread.lock or session.admin or session.mod then - render('views.fragments.form_submit') -end %> -<% render('views.fragments.announcements') %> -
-[<%= i18n('return') %>] -[<%= i18n('catalog') %>] -[<%= i18n('bottom') %>] -[<%= i18n('refresh') %>] -
-
-<% for i, post in ipairs(posts) do - if i == 1 then - render('views.fragments.op_content', { thread=thread, post=post }) - else - render('views.fragments.post_content', { thread=thread, post=post, op=posts[1] }) - end -end %> -
-
-[<%= i18n('return') %>] -[<%= i18n('catalog') %>] -[<%= i18n('top') %>] -[<%= i18n('refresh') %>] -
diff --git a/misc/website/backend/app/static/banned.jpg b/misc/website/backend/app/static/banned.jpg deleted file mode 100755 index 16579ef..0000000 Binary files a/misc/website/backend/app/static/banned.jpg and /dev/null differ diff --git a/misc/website/backend/app/static/css/posts.css b/misc/website/backend/app/static/css/posts.css deleted file mode 100755 index 33552b8..0000000 --- a/misc/website/backend/app/static/css/posts.css +++ /dev/null @@ -1,63 +0,0 @@ -.thread_container, .post_container { - margin: 10px 0 0 0; - padding: 10px; } - -.thread_container { - overflow: visible; } - .thread_container .op, .thread_container .post_container { - position: relative; } - .thread_container .op .post_admin { - margin-left: 350px; } - .thread_container .post_container { - display: table; - min-width: 300px; - max-width: 100%; } - .thread_container .post_container .post_admin { - margin-left: 20px; } - .thread_container .post_file { - margin: 0 0 3px 0; } - .thread_container .post_file a { - text-decoration: underline; } - .thread_container .post_image { - float: left; - margin: 0 20px 0 10px; } - .thread_container .post_image img { - max-width: 1000px; - max-height: 1000px; } - .thread_container .post_subject, .thread_container .post_name { - font-weight: bold; } - .thread_container .post_comment { - padding: 10px 15px; } - .thread_container .post_comment a { - text-decoration: underline; } - .thread_container .post_comment .broken_link { - text-decoration: line-through; } - .thread_container .post_comment .spoiler { - background-color: #000000; - color: #000000; } - .thread_container .post_comment .spoiler:hover { - color: #ffffff; } - .thread_container .post_comment .post_banned { - color: #ff0000; - font-weight: bold; - margin-top: 20px; } - .thread_container .post_flag, .thread_container .post_menu { - cursor: pointer; } - .thread_container .post_menu { - position: relative; - display: inline-block; } - .thread_container .post_menu .menu { - display: none; - position: absolute; - z-index: 9; } - .thread_container .post_menu .menu div { - padding: 3px; } - .thread_container .post_menu .menu div form { - margin: 0; - padding: 0; } - .thread_container .post_menu .menu div form input[type="submit"], .thread_container .post_menu .menu div form button { - background: none; - border: none; - padding: 0; } - -/*# sourceMappingURL=posts.css.map */ diff --git a/misc/website/backend/app/static/css/posts.css.map b/misc/website/backend/app/static/css/posts.css.map deleted file mode 100755 index c73ee42..0000000 --- a/misc/website/backend/app/static/css/posts.css.map +++ /dev/null @@ -1,7 +0,0 @@ -{ -"version": 3, -"mappings": "AAEA,kCAAmC;EAClC,MAAM,EAAE,UAAc;EACtB,OAAO,EAJE,IAAI;;AAOd,iBAAkB;EACjB,QAAQ,EAAE,OAAO;EAEjB,wDAAqB;IACpB,QAAQ,EAAE,QAAQ;EAIlB,iCAAY;IACX,WAAW,EAAE,KAAa;EAI5B,iCAAgB;IACf,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,KAAK;IAChB,SAAS,EAAE,IAAI;IAEf,6CAAY;MACX,WAAW,EAAE,IAAY;EAI3B,4BAAW;IACV,MAAM,EAAE,SAAS;IAEjB,8BAAE;MACD,eAAe,EAAE,SAAS;EAI5B,6BAAY;IACX,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,aAAyB;IAEjC,iCAAI;MACH,SAAS,EAAE,MAAM;MACjB,UAAU,EAAE,MAAM;EAIpB,6DAA0B;IACzB,WAAW,EAAE,IAAI;EAGlB,+BAAc;IACb,OAAO,EAAE,SAAuB;IAEhC,iCAAE;MACD,eAAe,EAAE,SAAS;IAG3B,4CAAa;MACZ,eAAe,EAAE,YAAY;IAG9B,wCAAS;MACR,gBAAgB,EAAE,OAAO;MACzB,KAAK,EAAE,OAAO;MAEd,8CAAQ;QACP,KAAK,EAAE,OAAO;IAIhB,4CAAa;MACZ,KAAK,EAAE,OAAO;MACd,WAAW,EAAE,IAAI;MACjB,UAAU,EAAE,IAAY;EAI1B,0DAAuB;IACtB,MAAM,EAAE,OAAO;EAGhB,4BAAW;IACV,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,YAAY;IAErB,kCAAM;MACL,OAAO,EAAE,IAAI;MACb,QAAQ,EAAE,QAAQ;MAClB,OAAO,EAAE,CAAC;MAEV,sCAAI;QACH,OAAO,EAAE,GAAG;QAEZ,2CAAK;UACJ,MAAM,EAAE,CAAC;UACT,OAAO,EAAE,CAAC;UAEV,oHAA6B;YAC5B,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,CAAC", -"sources": ["../sass/posts.scss"], -"names": [], -"file": "posts.css" -} \ No newline at end of file diff --git a/misc/website/backend/app/static/css/reset.css b/misc/website/backend/app/static/css/reset.css deleted file mode 100755 index 60c5732..0000000 --- a/misc/website/backend/app/static/css/reset.css +++ /dev/null @@ -1,200 +0,0 @@ -/* - HTML5 Reset :: style.css - ---------------------------------------------------------- - We have learned much from/been inspired by/taken code where offered from: - - Eric Meyer :: http://meyerweb.com - HTML5 Doctor :: http://html5doctor.com - and the HTML5 Boilerplate :: http://html5boilerplate.com - --------------------------------------------------------------------------------*/ -/* Let's default this puppy out --------------------------------------------------------------------------------*/ -html, body, body div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, figure, footer, header, menu, nav, section, time, mark, audio, video, details, summary { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font-weight: normal; - vertical-align: baseline; - background: transparent; } - -article, aside, figure, footer, header, nav, section, details, summary { - display: block; } - -/* Handle box-sizing while better addressing child elements: - http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */ -html { - box-sizing: border-box; } - -*, -*:before, -*:after { - box-sizing: inherit; } - -/* consider resetting the default cursor: https://gist.github.com/murtaugh/5247154 */ -/* Responsive images and other embedded objects */ -/* if you don't have full control over `img` tags (if you have to overcome attributes), consider adding height: auto */ -img, -object, -embed { - max-width: 100%; } - -/* - Note: keeping IMG here will cause problems if you're using foreground images as sprites. - In fact, it *will* cause problems with Google Maps' controls at small size. - If this is the case for you, try uncommenting the following: - -#map img { - max-width: none; -} -*/ -/* force a vertical scrollbar to prevent a jumpy page */ -html { - overflow-y: scroll; } - -/* we use a lot of ULs that aren't bulleted. - you'll have to restore the bullets within content, - which is fine because they're probably customized anyway */ -ul { - list-style: none; } - -blockquote, q { - quotes: none; } - -blockquote:before, -blockquote:after, -q:before, -q:after { - content: ''; - content: none; } - -a { - margin: 0; - padding: 0; - font-size: 100%; - vertical-align: baseline; - background: transparent; } - -del { - text-decoration: line-through; } - -abbr[title], dfn[title] { - border-bottom: 1px dotted #000; - cursor: help; } - -/* tables still need cellspacing="0" in the markup */ -table { - border-collapse: collapse; - border-spacing: 0; } - -th { - font-weight: bold; - vertical-align: bottom; } - -td { - font-weight: normal; - vertical-align: top; } - -hr { - display: block; - height: 1px; - border: 0; - border-top: 1px solid #ccc; - margin: 1em 0; - padding: 0; } - -input, select { - vertical-align: middle; } - -pre { - white-space: pre; - /* CSS2 */ - white-space: pre-wrap; - /* CSS 2.1 */ - white-space: pre-line; - /* CSS 3 (and 2.1 as well, actually) */ - word-wrap: break-word; - /* IE */ } - -input[type="radio"] { - vertical-align: text-bottom; } - -.ie7 input[type="checkbox"] { - vertical-align: baseline; } - -.ie6 input { - vertical-align: text-bottom; } - -select, input, textarea { - font: 99% sans-serif; } - -table { - font-size: inherit; - font: 100%; } - -small { - font-size: 85%; } - -strong { - font-weight: bold; } - -td, td img { - vertical-align: top; } - -/* Make sure sup and sub don't mess with your line-heights http://gist.github.com/413930 */ -sub, sup { - font-size: 75%; - line-height: 0; - position: relative; } - -sup { - top: -0.5em; } - -sub { - bottom: -0.25em; } - -/* standardize any monospaced elements */ -pre, code, kbd, samp { - font-family: monospace, sans-serif; } - -/* hand cursor on clickable elements */ -.clickable, -input[type=button], -input[type=submit], -input[type=file], -button { - cursor: pointer; } - -/* Webkit browsers add a 2px margin outside the chrome of form elements */ -button, input, select, textarea { - margin: 0; } - -/* make buttons play nice in IE */ -button, -input[type=button] { - width: auto; - overflow: visible; } - -/* scale images in IE7 more attractively */ -.ie7 img { - -ms-interpolation-mode: bicubic; } - -/* prevent BG image flicker upon hover - (commented out as usage is rare, and the filter syntax messes with some pre-processors) -.ie6 html {filter: expression(document.execCommand("BackgroundImageCache", false, true));} -*/ -/* let's clear some floats */ -.clearfix:before, .clearfix:after { - content: "\0020"; - display: block; - height: 0; - overflow: hidden; } - -.clearfix:after { - clear: both; } - -.clearfix { - zoom: 1; } - -/*# sourceMappingURL=reset.css.map */ diff --git a/misc/website/backend/app/static/css/reset.css.map b/misc/website/backend/app/static/css/reset.css.map deleted file mode 100755 index 9ff5fef..0000000 --- a/misc/website/backend/app/static/css/reset.css.map +++ /dev/null @@ -1,7 +0,0 @@ -{ -"version": 3, -"mappings": "AAAA;;;;;;;;;iFASiF;AAEjF;iFACiF;AAEjF,2YAA4Y;EAC3Y,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,MAAM,EAAE,CAAC;EACT,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,MAAM;EACnB,cAAc,EAAE,QAAQ;EACxB,UAAU,EAAE,WAAW;;AAGxB,sEAAuE;EAAC,OAAO,EAAE,KAAK;;AAEtF;yFACyF;AACzF,IAAK;EACJ,UAAU,EAAE,UAAU;;AAGvB;;OAEQ;EACP,UAAU,EAAE,OAAO;;AAGpB,qFAAqF;AAErF,kDAAkD;AAClD,uHAAuH;AACvH;;KAEM;EAAC,SAAS,EAAE,IAAI;;AAEtB;;;;;;;;EAQE;AAEF,wDAAwD;AACxD,IAAK;EAAC,UAAU,EAAE,MAAM;;AAExB;;4DAE4D;AAC5D,EAAG;EAAC,UAAU,EAAE,IAAI;;AAEpB,aAAc;EAAC,MAAM,EAAE,IAAI;;AAE3B;;;OAGQ;EAAC,OAAO,EAAE,EAAE;EAAE,OAAO,EAAE,IAAI;;AAEnC,CAAE;EAAC,MAAM,EAAE,CAAC;EAAE,OAAO,EAAE,CAAC;EAAE,SAAS,EAAE,IAAI;EAAE,cAAc,EAAE,QAAQ;EAAE,UAAU,EAAE,WAAW;;AAE5F,GAAI;EAAC,eAAe,EAAE,YAAY;;AAElC,uBAAwB;EAAC,aAAa,EAAE,eAAe;EAAE,MAAM,EAAE,IAAI;;AAErE,qDAAqD;AACrD,KAAM;EAAC,eAAe,EAAE,QAAQ;EAAE,cAAc,EAAE,CAAC;;AACnD,EAAG;EAAC,WAAW,EAAE,IAAI;EAAE,cAAc,EAAE,MAAM;;AAC7C,EAAG;EAAC,WAAW,EAAE,MAAM;EAAE,cAAc,EAAE,GAAG;;AAE5C,EAAG;EAAC,OAAO,EAAE,KAAK;EAAE,MAAM,EAAE,GAAG;EAAE,MAAM,EAAE,CAAC;EAAE,UAAU,EAAE,cAAc;EAAE,MAAM,EAAE,KAAK;EAAE,OAAO,EAAE,CAAC;;AAEjG,aAAc;EAAC,cAAc,EAAE,MAAM;;AAErC,GAAI;EACA,WAAW,EAAE,GAAG;EAAE,UAAU;EAC5B,WAAW,EAAE,QAAQ;EAAE,aAAa;EACpC,WAAW,EAAE,QAAQ;EAAE,uCAAuC;EAC9D,SAAS,EAAE,UAAU;EAAE,QAAQ;;AAGnC,mBAAoB;EAAC,cAAc,EAAE,WAAW;;AAChD,2BAA4B;EAAC,cAAc,EAAE,QAAQ;;AACrD,UAAW;EAAC,cAAc,EAAE,WAAW;;AAEvC,uBAAwB;EAAC,IAAI,EAAE,cAAc;;AAE7C,KAAM;EAAC,SAAS,EAAE,OAAO;EAAE,IAAI,EAAE,IAAI;;AAErC,KAAM;EAAC,SAAS,EAAE,GAAG;;AAErB,MAAO;EAAC,WAAW,EAAE,IAAI;;AAEzB,UAAW;EAAC,cAAc,EAAE,GAAG;;AAE/B,2FAA2F;AAC3F,QAAS;EAAC,SAAS,EAAE,GAAG;EAAE,WAAW,EAAE,CAAC;EAAE,QAAQ,EAAE,QAAQ;;AAC5D,GAAI;EAAC,GAAG,EAAE,MAAM;;AAChB,GAAI;EAAC,MAAM,EAAE,OAAO;;AAEpB,yCAAyC;AACzC,oBAAqB;EAAC,WAAW,EAAE,qBAAqB;;AAExD,uCAAuC;AACvC;;;;MAIO;EAAC,MAAM,EAAE,OAAO;;AAEvB,0EAA0E;AAC1E,+BAAgC;EAAC,MAAM,EAAE,CAAC;;AAE1C,kCAAkC;AAClC;kBACmB;EAAC,KAAK,EAAE,IAAI;EAAE,QAAQ,EAAE,OAAO;;AAElD,2CAA2C;AAC3C,QAAS;EAAC,sBAAsB,EAAE,OAAO;;AAEzC;;;EAGE;AAEF,6BAA6B;AAC7B,iCAAkC;EAAE,OAAO,EAAE,OAAO;EAAE,OAAO,EAAE,KAAK;EAAE,MAAM,EAAE,CAAC;EAAE,QAAQ,EAAE,MAAM;;AACjG,eAAgB;EAAE,KAAK,EAAE,IAAI;;AAC7B,SAAU;EAAE,IAAI,EAAE,CAAC", -"sources": ["../sass/reset.scss"], -"names": [], -"file": "reset.css" -} \ No newline at end of file diff --git a/misc/website/backend/app/static/css/style.css b/misc/website/backend/app/static/css/style.css deleted file mode 100755 index 4dc443d..0000000 --- a/misc/website/backend/app/static/css/style.css +++ /dev/null @@ -1,478 +0,0 @@ -/* - HTML5 Reset :: style.css - ---------------------------------------------------------- - We have learned much from/been inspired by/taken code where offered from: - - Eric Meyer :: http://meyerweb.com - HTML5 Doctor :: http://html5doctor.com - and the HTML5 Boilerplate :: http://html5boilerplate.com - --------------------------------------------------------------------------------*/ -/* Let's default this puppy out --------------------------------------------------------------------------------*/ -html, body, body div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, figure, footer, header, menu, nav, section, time, mark, audio, video, details, summary { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font-weight: normal; - vertical-align: baseline; - background: transparent; } - -article, aside, figure, footer, header, nav, section, details, summary { - display: block; } - -/* Handle box-sizing while better addressing child elements: - http://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ */ -html { - box-sizing: border-box; } - -*, -*:before, -*:after { - box-sizing: inherit; } - -/* consider resetting the default cursor: https://gist.github.com/murtaugh/5247154 */ -/* Responsive images and other embedded objects */ -/* if you don't have full control over `img` tags (if you have to overcome attributes), consider adding height: auto */ -img, -object, -embed { - max-width: 100%; } - -/* - Note: keeping IMG here will cause problems if you're using foreground images as sprites. - In fact, it *will* cause problems with Google Maps' controls at small size. - If this is the case for you, try uncommenting the following: - -#map img { - max-width: none; -} -*/ -/* force a vertical scrollbar to prevent a jumpy page */ -html { - overflow-y: scroll; } - -/* we use a lot of ULs that aren't bulleted. - you'll have to restore the bullets within content, - which is fine because they're probably customized anyway */ -ul { - list-style: none; } - -blockquote, q { - quotes: none; } - -blockquote:before, -blockquote:after, -q:before, -q:after { - content: ''; - content: none; } - -a { - margin: 0; - padding: 0; - font-size: 100%; - vertical-align: baseline; - background: transparent; } - -del { - text-decoration: line-through; } - -abbr[title], dfn[title] { - border-bottom: 1px dotted #000; - cursor: help; } - -/* tables still need cellspacing="0" in the markup */ -table { - border-collapse: collapse; - border-spacing: 0; } - -th { - font-weight: bold; - vertical-align: bottom; } - -td { - font-weight: normal; - vertical-align: top; } - -hr { - display: block; - height: 1px; - border: 0; - border-top: 1px solid #ccc; - margin: 1em 0; - padding: 0; } - -input, select { - vertical-align: middle; } - -pre { - white-space: pre; - /* CSS2 */ - white-space: pre-wrap; - /* CSS 2.1 */ - white-space: pre-line; - /* CSS 3 (and 2.1 as well, actually) */ - word-wrap: break-word; - /* IE */ } - -input[type="radio"] { - vertical-align: text-bottom; } - -.ie7 input[type="checkbox"] { - vertical-align: baseline; } - -.ie6 input { - vertical-align: text-bottom; } - -select, input, textarea { - font: 99% sans-serif; } - -table { - font-size: inherit; - font: 100%; } - -small { - font-size: 85%; } - -strong { - font-weight: bold; } - -td, td img { - vertical-align: top; } - -/* Make sure sup and sub don't mess with your line-heights http://gist.github.com/413930 */ -sub, sup { - font-size: 75%; - line-height: 0; - position: relative; } - -sup { - top: -0.5em; } - -sub { - bottom: -0.25em; } - -/* standardize any monospaced elements */ -pre, code, kbd, samp { - font-family: monospace, sans-serif; } - -/* hand cursor on clickable elements */ -.clickable, -input[type=button], -input[type=submit], -input[type=file], -button { - cursor: pointer; } - -/* Webkit browsers add a 2px margin outside the chrome of form elements */ -button, input, select, textarea { - margin: 0; } - -/* make buttons play nice in IE */ -button, -input[type=button] { - width: auto; - overflow: visible; } - -/* scale images in IE7 more attractively */ -.ie7 img { - -ms-interpolation-mode: bicubic; } - -/* prevent BG image flicker upon hover - (commented out as usage is rare, and the filter syntax messes with some pre-processors) -.ie6 html {filter: expression(document.execCommand("BackgroundImageCache", false, true));} -*/ -/* let's clear some floats */ -.clearfix:before, .clearfix:after { - content: "\0020"; - display: block; - height: 0; - overflow: hidden; } - -.clearfix:after { - clear: both; } - -.clearfix { - zoom: 1; } - -.thread_container, .post_container { - margin: 10px 0 0 0; - padding: 10px; } - -.thread_container { - overflow: visible; } - .thread_container .op, .thread_container .post_container { - position: relative; } - .thread_container .op .post_admin { - margin-left: 350px; } - .thread_container .post_container { - display: table; - min-width: 300px; - max-width: 100%; } - .thread_container .post_container .post_admin { - margin-left: 20px; } - .thread_container .post_file { - margin: 0 0 3px 0; } - .thread_container .post_file a { - text-decoration: underline; } - .thread_container .post_image { - float: left; - margin: 0 20px 0 10px; } - .thread_container .post_image img { - max-width: 1000px; - max-height: 1000px; } - .thread_container .post_subject, .thread_container .post_name { - font-weight: bold; } - .thread_container .post_comment { - padding: 10px 15px; } - .thread_container .post_comment a { - text-decoration: underline; } - .thread_container .post_comment .broken_link { - text-decoration: line-through; } - .thread_container .post_comment .spoiler { - background-color: #000000; - color: #000000; } - .thread_container .post_comment .spoiler:hover { - color: #ffffff; } - .thread_container .post_comment .post_banned { - color: #ff0000; - font-weight: bold; - margin-top: 20px; } - .thread_container .post_flag, .thread_container .post_menu { - cursor: pointer; } - .thread_container .post_menu { - position: relative; - display: inline-block; } - .thread_container .post_menu .menu { - display: none; - position: absolute; - z-index: 9; } - .thread_container .post_menu .menu div { - padding: 3px; } - .thread_container .post_menu .menu div form { - margin: 0; - padding: 0; } - .thread_container .post_menu .menu div form input[type="submit"], .thread_container .post_menu .menu div form button { - background: none; - border: none; - padding: 0; } - -body { - margin: 10px; - font-size: 10pt; - font-family: Helvetica, "Noto Sans", "Roboto Sans", Arial, sans-serif; } - -a { - text-decoration: none; } - -input[type="text"], -input[type="password"], -textarea, -select, -.admin_main { - padding: 4px; - width: 280px; } - -input[type="checkbox"] { - margin: 4px 0; } - -input[type="file"] { - padding: 4px 0; } - -h1 { - font-size: 240%; - font-weight: bolder; - margin-top: 10px; - text-align: center; } - -h2 { - margin: 10px 0; - text-align: center; } - -hr { - clear: left; } - -form { - display: inline-block; - margin: 5px auto; - padding: 10px; } - -table { - border-collapse: separate; - border-spacing: 1px; - margin: 0 auto; - max-width: 80%; } - table thead { - vertical-align: middle; } - table thead td { - font-weight: bolder; - padding: 5px 10px; } - table td { - padding: 3px 5px; - text-align: center; } - table .rules { - text-align: left; } - -ul { - list-style: disc; } - ul li { - margin-left: 20px; } - -ol { - list-style: decimal; } - ol li { - margin-left: 20px; } - -#error { - background-color: #ff0000; - color: #ffffff; - margin-bottom: 10px; - padding: 10px; - text-align: center; } - #error h3 { - font-size: 220%; - font-weight: bolder; } - #error p { - font-weight: bold; } - -#right { - top: 10px; - right: 10px; - position: absolute; - text-align: right; } - #right #locale form { - display: inline; - margin: 0; - padding: 0; } - #right #locale form select { - margin: 0; - padding: 0; - width: 75px; } - -#post_form, .admin_form { - text-align: center; } - #post_form form div, .admin_form form div { - display: -webkit-box-flex; - display: -webkit-flexbox; - display: -ms-flexbox; - display: -webkit-box; - display: flex; } - #post_form form div .title, .admin_form form div .title { - -webkit-flex: 0; - -ms-flex: 0; - flex: 0; - font-weight: bold; - text-align: right; - min-width: 120px; - margin: 1px; - padding: 4px; } - #post_form form div .title span, .admin_form form div .title span { - cursor: pointer; } - #post_form form div .fields, .admin_form form div .fields { - margin: 1px; } - #post_form form div .fields textarea, .admin_form form div .fields textarea { - min-height: 100px !important; } - #post_form form button, .admin_form form button { - margin: 10px 0 0 0; } - -.admin_form form { - margin: 0; - padding: 0; } - .admin_form form button { - margin: 0; } - -.required { - color: #ff0000; } - -.boards, .return, .copyright { - text-align: center; } - -.archive_stats { - text-align: center; - margin-bottom: 10px; } - -.announcement { - text-align: center; - font-weight: bold; } - -.banned { - border: 7px dashed #000000; - display: table; - font-size: 180%; - min-width: 200px; - max-width: 600px; - margin: 20px auto; - padding: 10px; } - .banned p { - margin: 10px; } - .banned div { - text-align: center; } - -.medium { - width: 120px !important; } - -.short { - width: 40px !important; } - -.catalog_container { - display: inline-block; - overflow: hidden; - text-align: center; - vertical-align: top; - width: 135px; } - .catalog_container .catalog_stats { - font-size: 70%; } - .catalog_container img { - box-shadow: 0 0 5px rgba(0, 0, 0, 0.25); - max-width: 125px; - max-height: 125px; } - -.copyright { - margin-top: 10px; } - -.front_page h2 { - font-size: 180%; } -.front_page h3 { - font-size: 140%; - margin-top: 10px; } -.front_page .board { - display: inline-block; - max-width: 300px; - margin: 20px; - vertical-align: top; } - .front_page .board a { - font-size: 160%; - font-weight: bold; } - .front_page .board .subtext { - display: block; - font-size: 120%; } - -.table_of_contents { - display: table; - margin: 0 auto; - max-width: 400px; } - -.answers { - max-width: 600px; - margin: 0 auto; } - .answers h2 { - font-size: 160%; } - -.install { - max-width: 300px; - margin: 0 auto; } - .install h2 { - font-size: 160%; - margin-top: 20px; } - .install label { - font-weight: bold; - margin-top: 20px; } - .install button { - font-size: 120%; - font-weight: bold; - margin-top: 20px; - padding: 5px; } - -/*# sourceMappingURL=style.css.map */ diff --git a/misc/website/backend/app/static/css/style.css.map b/misc/website/backend/app/static/css/style.css.map deleted file mode 100755 index 3e447a4..0000000 --- a/misc/website/backend/app/static/css/style.css.map +++ /dev/null @@ -1,7 +0,0 @@ -{ -"version": 3, -"mappings": "AAAA;;;;;;;;;iFASiF;AAEjF;iFACiF;AAEjF,2YAA4Y;EAC3Y,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EACV,MAAM,EAAE,CAAC;EACT,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,MAAM;EACnB,cAAc,EAAE,QAAQ;EACxB,UAAU,EAAE,WAAW;;AAGxB,sEAAuE;EAAC,OAAO,EAAE,KAAK;;AAEtF;yFACyF;AACzF,IAAK;EACJ,UAAU,EAAE,UAAU;;AAGvB;;OAEQ;EACP,UAAU,EAAE,OAAO;;AAGpB,qFAAqF;AAErF,kDAAkD;AAClD,uHAAuH;AACvH;;KAEM;EAAC,SAAS,EAAE,IAAI;;AAEtB;;;;;;;;EAQE;AAEF,wDAAwD;AACxD,IAAK;EAAC,UAAU,EAAE,MAAM;;AAExB;;4DAE4D;AAC5D,EAAG;EAAC,UAAU,EAAE,IAAI;;AAEpB,aAAc;EAAC,MAAM,EAAE,IAAI;;AAE3B;;;OAGQ;EAAC,OAAO,EAAE,EAAE;EAAE,OAAO,EAAE,IAAI;;AAEnC,CAAE;EAAC,MAAM,EAAE,CAAC;EAAE,OAAO,EAAE,CAAC;EAAE,SAAS,EAAE,IAAI;EAAE,cAAc,EAAE,QAAQ;EAAE,UAAU,EAAE,WAAW;;AAE5F,GAAI;EAAC,eAAe,EAAE,YAAY;;AAElC,uBAAwB;EAAC,aAAa,EAAE,eAAe;EAAE,MAAM,EAAE,IAAI;;AAErE,qDAAqD;AACrD,KAAM;EAAC,eAAe,EAAE,QAAQ;EAAE,cAAc,EAAE,CAAC;;AACnD,EAAG;EAAC,WAAW,EAAE,IAAI;EAAE,cAAc,EAAE,MAAM;;AAC7C,EAAG;EAAC,WAAW,EAAE,MAAM;EAAE,cAAc,EAAE,GAAG;;AAE5C,EAAG;EAAC,OAAO,EAAE,KAAK;EAAE,MAAM,EAAE,GAAG;EAAE,MAAM,EAAE,CAAC;EAAE,UAAU,EAAE,cAAc;EAAE,MAAM,EAAE,KAAK;EAAE,OAAO,EAAE,CAAC;;AAEjG,aAAc;EAAC,cAAc,EAAE,MAAM;;AAErC,GAAI;EACA,WAAW,EAAE,GAAG;EAAE,UAAU;EAC5B,WAAW,EAAE,QAAQ;EAAE,aAAa;EACpC,WAAW,EAAE,QAAQ;EAAE,uCAAuC;EAC9D,SAAS,EAAE,UAAU;EAAE,QAAQ;;AAGnC,mBAAoB;EAAC,cAAc,EAAE,WAAW;;AAChD,2BAA4B;EAAC,cAAc,EAAE,QAAQ;;AACrD,UAAW;EAAC,cAAc,EAAE,WAAW;;AAEvC,uBAAwB;EAAC,IAAI,EAAE,cAAc;;AAE7C,KAAM;EAAC,SAAS,EAAE,OAAO;EAAE,IAAI,EAAE,IAAI;;AAErC,KAAM;EAAC,SAAS,EAAE,GAAG;;AAErB,MAAO;EAAC,WAAW,EAAE,IAAI;;AAEzB,UAAW;EAAC,cAAc,EAAE,GAAG;;AAE/B,2FAA2F;AAC3F,QAAS;EAAC,SAAS,EAAE,GAAG;EAAE,WAAW,EAAE,CAAC;EAAE,QAAQ,EAAE,QAAQ;;AAC5D,GAAI;EAAC,GAAG,EAAE,MAAM;;AAChB,GAAI;EAAC,MAAM,EAAE,OAAO;;AAEpB,yCAAyC;AACzC,oBAAqB;EAAC,WAAW,EAAE,qBAAqB;;AAExD,uCAAuC;AACvC;;;;MAIO;EAAC,MAAM,EAAE,OAAO;;AAEvB,0EAA0E;AAC1E,+BAAgC;EAAC,MAAM,EAAE,CAAC;;AAE1C,kCAAkC;AAClC;kBACmB;EAAC,KAAK,EAAE,IAAI;EAAE,QAAQ,EAAE,OAAO;;AAElD,2CAA2C;AAC3C,QAAS;EAAC,sBAAsB,EAAE,OAAO;;AAEzC;;;EAGE;AAEF,6BAA6B;AAC7B,iCAAkC;EAAE,OAAO,EAAE,OAAO;EAAE,OAAO,EAAE,KAAK;EAAE,MAAM,EAAE,CAAC;EAAE,QAAQ,EAAE,MAAM;;AACjG,eAAgB;EAAE,KAAK,EAAE,IAAI;;AAC7B,SAAU;EAAE,IAAI,EAAE,CAAC;;AC1InB,kCAAmC;EAClC,MAAM,EAAE,UAAc;EACtB,OAAO,EAJE,IAAI;;AAOd,iBAAkB;EACjB,QAAQ,EAAE,OAAO;EAEjB,wDAAqB;IACpB,QAAQ,EAAE,QAAQ;EAIlB,iCAAY;IACX,WAAW,EAAE,KAAa;EAI5B,iCAAgB;IACf,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,KAAK;IAChB,SAAS,EAAE,IAAI;IAEf,6CAAY;MACX,WAAW,EAAE,IAAY;EAI3B,4BAAW;IACV,MAAM,EAAE,SAAS;IAEjB,8BAAE;MACD,eAAe,EAAE,SAAS;EAI5B,6BAAY;IACX,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,aAAyB;IAEjC,iCAAI;MACH,SAAS,EAAE,MAAM;MACjB,UAAU,EAAE,MAAM;EAIpB,6DAA0B;IACzB,WAAW,EAAE,IAAI;EAGlB,+BAAc;IACb,OAAO,EAAE,SAAuB;IAEhC,iCAAE;MACD,eAAe,EAAE,SAAS;IAG3B,4CAAa;MACZ,eAAe,EAAE,YAAY;IAG9B,wCAAS;MACR,gBAAgB,EAAE,OAAO;MACzB,KAAK,EAAE,OAAO;MAEd,8CAAQ;QACP,KAAK,EAAE,OAAO;IAIhB,4CAAa;MACZ,KAAK,EAAE,OAAO;MACd,WAAW,EAAE,IAAI;MACjB,UAAU,EAAE,IAAY;EAI1B,0DAAuB;IACtB,MAAM,EAAE,OAAO;EAGhB,4BAAW;IACV,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,YAAY;IAErB,kCAAM;MACL,OAAO,EAAE,IAAI;MACb,QAAQ,EAAE,QAAQ;MAClB,OAAO,EAAE,CAAC;MAEV,sCAAI;QACH,OAAO,EAAE,GAAG;QAEZ,2CAAK;UACJ,MAAM,EAAE,CAAC;UACT,OAAO,EAAE,CAAC;UAEV,oHAA6B;YAC5B,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,CAAC;;ACtEhB,IAAK;EACJ,MAAM,EAHG,IAAI;EAIb,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,wDAAwD;;AAGtE,CAAE;EACD,eAAe,EAAE,IAAI;;AAGtB;;;;WAIY;EACX,OAAO,EAAE,GAAG;EACZ,KAAK,EAAE,KAAK;;AAGb,sBAAuB;EACtB,MAAM,EAAE,KAAK;;AAGd,kBAAmB;EAClB,OAAO,EAAE,KAAK;;AAGf,EAAG;EACF,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,MAAM;EACnB,UAAU,EAAE,IAAI;EAChB,UAAU,EAAE,MAAM;;AAGnB,EAAG;EACF,MAAM,EAAE,MAAU;EAClB,UAAU,EAAE,MAAM;;AAGnB,EAAG;EACF,KAAK,EAAE,IAAI;;AAGZ,IAAK;EACJ,OAAO,EAAE,YAAY;EACrB,MAAM,EAAE,QAAiB;EACzB,OAAO,EAhDE,IAAI;;AAmDd,KAAM;EACL,eAAe,EAAE,QAAQ;EACzB,cAAc,EAAE,GAAG;EACnB,MAAM,EAAE,MAAM;EACd,SAAS,EAAE,GAAG;EAEd,WAAM;IACL,cAAc,EAAE,MAAM;IAEtB,cAAG;MACF,WAAW,EAAE,MAAM;MACnB,OAAO,EAAE,QAAqB;EAIhC,QAAG;IACF,OAAO,EAAE,OAAgB;IACzB,UAAU,EAAE,MAAM;EAGnB,YAAO;IACN,UAAU,EAAE,IAAI;;AAIlB,EAAG;EACF,UAAU,EAAE,IAAI;EAEhB,KAAG;IACF,WAAW,EAAE,IAAY;;AAI3B,EAAG;EACF,UAAU,EAAE,OAAO;EAEnB,KAAG;IACF,WAAW,EAAE,IAAY;;AAI3B,MAAO;EACN,gBAAgB,EAAE,OAAO;EACzB,KAAK,EAAE,OAAO;EACd,aAAa,EA/FJ,IAAI;EAgGb,OAAO,EAhGE,IAAI;EAiGb,UAAU,EAAE,MAAM;EAElB,SAAG;IACF,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,MAAM;EAIpB,QAAE;IACD,WAAW,EAAE,IAAI;;AAInB,MAAO;EACN,GAAG,EA/GM,IAAI;EAgHb,KAAK,EAhHI,IAAI;EAiHb,QAAQ,EAAE,QAAQ;EAClB,UAAU,EAAE,KAAK;EAGhB,mBAAK;IACJ,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;IAEV,0BAAO;MACN,MAAM,EAAE,CAAC;MACT,OAAO,EAAE,CAAC;MACV,KAAK,EAAE,IAAI;;AAUf,uBAAwB;EACvB,UAAU,EAAE,MAAM;EAGjB,yCAAI;IArKL,OAAO,EAAE,gBAAgB;IACzB,OAAO,EAAE,eAAe;IACxB,OAAO,EAAE,WAAW;IACpB,OAAO,EAAE,WAAW;IACpB,OAAO,EAAE,IAAI;IAoKX,uDAAO;MAhKT,YAAY,EAiKK,CAAC;MAhKlB,QAAQ,EAgKS,CAAC;MA/JlB,IAAI,EA+Ja,CAAC;MACf,WAAW,EAAE,IAAI;MACjB,UAAU,EAAE,KAAK;MACjB,SAAS,EAAE,KAAK;MAChB,MAAM,EAAE,GAAG;MACX,OAAO,EAAE,GAAG;MAEZ,iEAAK;QACJ,MAAM,EAAE,OAAO;IAIjB,yDAAQ;MACP,MAAM,EAAE,GAAG;MAEX,2EAAS;QACR,UAAU,EAAE,gBAAgB;EAK/B,+CAAO;IACN,MAAM,EAAE,UAAc;;AAMxB,gBAAK;EACJ,MAAM,EAAE,CAAC;EACT,OAAO,EAAE,CAAC;EAEV,uBAAO;IACN,MAAM,EAAE,CAAC;;AAKZ,SAAU;EACT,KAAK,EAAE,OAAO;;AAGf,4BAA6B;EAC5B,UAAU,EAAE,MAAM;;AAGnB,cAAe;EACd,UAAU,EAAE,MAAM;EAClB,aAAa,EA/LJ,IAAI;;AAkMd,aAAc;EACb,UAAU,EAAE,MAAM;EAClB,WAAW,EAAE,IAAI;;AAGlB,OAAQ;EACP,MAAM,EAAE,kBAAkB;EAC1B,OAAO,EAAE,KAAK;EACd,SAAS,EAAE,IAAI;EACf,SAAS,EAAE,KAAK;EAChB,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,SAAiB;EACzB,OAAO,EA9ME,IAAI;EAgNb,SAAE;IACD,MAAM,EAjNE,IAAI;EAoNb,WAAI;IACH,UAAU,EAAE,MAAM;;AAIpB,OAAQ;EACP,KAAK,EAAE,gBAAgB;;AAGxB,MAAO;EACN,KAAK,EAAE,eAAe;;AAGvB,kBAAmB;EAClB,OAAO,EAAE,YAAY;EACrB,QAAQ,EAAE,MAAM;EAChB,UAAU,EAAE,MAAM;EAClB,cAAc,EAAE,GAAG;EACnB,KAAK,EAAE,KAAK;EAEZ,iCAAe;IACd,SAAS,EAAE,GAAG;EAGf,sBAAI;IACH,UAAU,EAAE,2BAA2B;IACvC,SAAS,EAAE,KAAK;IAChB,UAAU,EAAE,KAAK;;AAInB,UAAW;EACV,UAAU,EApPD,IAAI;;AAwPb,cAAG;EACF,SAAS,EAAE,IAAI;AAGhB,cAAG;EACF,SAAS,EAAE,IAAI;EACf,UAAU,EA9PF,IAAI;AAiQb,kBAAO;EACN,OAAO,EAAE,YAAY;EACrB,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,IAAY;EACpB,cAAc,EAAE,GAAG;EAEnB,oBAAE;IACD,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,IAAI;EAGlB,2BAAS;IACR,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,IAAI;;AAKlB,kBAAmB;EAClB,OAAO,EAAE,KAAK;EACd,MAAM,EAAE,MAAM;EACd,SAAS,EAAE,KAAK;;AAGjB,QAAS;EACR,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,MAAM;EAEd,WAAG;IACF,SAAS,EAAE,IAAI;;AAIjB,QAAS;EACR,SAAS,EAAE,KAAK;EAChB,MAAM,EAAE,MAAM;EAEd,WAAG;IACF,SAAS,EAAE,IAAI;IACf,UAAU,EAAE,IAAY;EAGzB,cAAM;IACL,WAAW,EAAE,IAAI;IACjB,UAAU,EAAE,IAAY;EAGzB,eAAO;IACN,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,IAAI;IACjB,UAAU,EAAE,IAAY;IACxB,OAAO,EAAE,GAAY", -"sources": ["../sass/reset.scss","../sass/posts.scss","../sass/style.scss"], -"names": [], -"file": "style.css" -} \ No newline at end of file diff --git a/misc/website/backend/app/static/css/tegaki.css b/misc/website/backend/app/static/css/tegaki.css deleted file mode 100755 index d2e3591..0000000 --- a/misc/website/backend/app/static/css/tegaki.css +++ /dev/null @@ -1,187 +0,0 @@ -@font-face { - font-family: 'tegaki'; - src: url('data:application/octet-stream;base64,d09GRgABAAAAAAyIAA4AAAAAFVAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABRAAAAEQAAABWPeFIsGNtYXAAAAGIAAAAOgAAAUrQFxm3Y3Z0IAAAAcQAAAAKAAAACgAAAABmcGdtAAAB0AAABZQAAAtwiJCQWWdhc3AAAAdkAAAACAAAAAgAAAAQZ2x5ZgAAB2wAAAI+AAAC7u/G5z9oZWFkAAAJrAAAADYAAAA2BIBHAWhoZWEAAAnkAAAAHgAAACQHlwNRaG10eAAACgQAAAAWAAAAIBsOAABsb2NhAAAKHAAAABIAAAASA2cCrm1heHAAAAowAAAAIAAAACAAmwu2bmFtZQAAClAAAAF+AAACte3MYkJwb3N0AAAL0AAAAFAAAABnZ1gGo3ByZXAAAAwgAAAAZQAAAHvdawOFeJxjYGROYpzAwMrAwVTFtIeBgaEHQjM+YDBkZGJgYGJgZWbACgLSXFMYHF4wvGBjDvqfxRDFzM3gDxRmBMkBANw6Cw94nGNgYGBmgGAZBkYGEHAB8hjBfBYGDSDNBqQZGZgYGF6w/f8PUvCCAURLMELVAwEjG8OIBwBqdQa0AAAAAAAAAAAAAAAAAAB4nK1WaXMTRxCd1WHLNj6CDxI2gVnGcox2VpjLCBDG7EoW4BzylexCjl1Ldu6LT/wG/ZpekVSRb/y0vB4d2GAnVVQoSv2m9+1M9+ueXpPQksReWI+k3HwpprY2aWTnSUg3bFqO4kPZ2QspU0z+LoiCaLXUvu04JCISgap1hSWC2PfI0iTjQ48yWrYlvWpSbulJd9kaD+qt+vbT0FGO3QklNZuhQ+uRLanCqBJFMu2RkjYtw9VfSVrh5yvMfNUMJYLoJJLGm2EMj+Rn44xWGa3GdhxFkU2WG0WKRDM8iCKPslpin1wxQUD5oBlSXvk0onyEH5EVe5TTCnHJdprf9yU/6R3OvyTieouyJQf+QHZkB3unK/ki0toK46adbEehivB0fSfEI5uT6p/sUV7TaOB2RaYnzQiWyleQWPkJZfYPyWrhfMqXPBrVkoOcCFovc2Jf8g60HkdMiWsmyILujk6IoO6XnKHYY/q4+OO9XSwXIQTIOJb1jkq4EEYpYbOaJG0EOYiSskWV1HpHTJzyOi3iLWG/Tu3oS2e0Sag7MZ6th46tnKjkeDSp00ymTu2k5tGUBlFKOhM85tcBlB/RJK+2sZrEyqNpbDNjJJFQoIVzaSqIZSeWNAXRPJrRm7thmmvXokWaPFDPPXpPb26Fmzs9p+3AP2v8Z3UqpoO9MJ2eDshKfJp2uUnRun56hn8m8UPWAiqRLTbDlMVDtn4H5eVjS47CawNs957zK+h99kTIpIH4G/AeL9UpBUyFmFVQC9201rUsy9RqVotUZOq7IU0rX9ZpAk05Dn1jX8Y4/q+ZGUtMCd/vxOnZEZeeufYlyDSH3GZdj+Z1arFdgM5sz+k0y/Z9nebYfqDTPNvzOh1ha+t0lO2HOi2w/UinY2wvaEGT7jsEchGBXMAGEoGwdRAI20sIhK1CIGwXEQjbIgJhu4RA2H6MQNguIxC2l7Wsmn4qaRw7E8sARYgDoznuyGVuKldTyaUSrotGpzbkKXKrpKJ4Vv0rA/3ikTesgbVAukTW/IpJrnxUleOPrmh508S5Ao5Vf3tzXJ8TD2W/WPhT8L/amqqkV6x5ZHIVeSPQk+NE1yYVj67p8rmqR9f/i4oOa4F+A6UQC0VZlg2+mZDwUafTUA1c5RAzGzMP1/W6Zc3P4fybGCEL6H78NxQaC9yDTllJWe1gr9XXj2W5twflsCdYkmK+zOtb4YuMzEr7RWYpez7yecAVMCqVYasNXK3gzXsS85DpTfJMELcVZYOkjceZILGBYx4wb76TICRMXbWB2imcsIG8YMwp2O+EQ1RvlOVwe6F9Ho2Uf2tX7MgZFU0Q+G32Rtjrs1DyW6yBhCe/1NdAVSFNxbipgEsj5YZq8GFcrdtGMk6gr6jYDcuyig8fR9x3So5lIPlIEatHRz+tvUKd1Ln9yihu3zv9CIJBaWL+9r6Z4qCUd7WSZVZtA1O3GpVT15rDxasO3c2j7nvH2Sdy1jTddE/c9L6mVbeDg7lZEO3bHJSlTC6o68MOG6jLzaXQ6mVckt52DzAsMKDfoRUb/1f3cfg8V6oKo+NIvZ2oH6PPYgzyDzh/R/UF6OcxTLmGlOd7lxOfbtzD2TJdxV2sn+LfwKy15mbpGnBD0w2Yh6xaHbrKDXynBjo90tyO9BDwse4K8QBgE8Bi8InuWsbzKYDxfMYcH+Bz5jBoMofBFnMYbDNnDWCHOQx2mcNgjzkMvmDOOsCXzGEQModBxBwGT5gTADxlDoOvmMPga+Yw+IY59wG+ZQ6DmDkMEuYw2Nd0ayhzixd0F6htUBXowPQTFvewONRUGbK/44Vhf28Qs38wiKk/aro9pP7EC0P92SCm/mIQU3/VdGdI/Y0Xhvq7QUz9wyCmPtMvxnKZwV9GvkuFA8ouNp/z98T7B8IaQLYAAQAB//8AD3icZZI/bNNAFMbvnYOd3KW1kzhnqUQmdVo7FQWi/LGlMKDSUglRZesAylSKVCkMiB2UShUqE1LGSERCSlmYIFIr5q4MDFUpTN1IB8Rahjq8c9oy4OHzu3dPv+/u3iNAyOg3PaCvyAxRByIGN67Pmjqozi3QpLjVO+BJ8cvXIJAicNsS9EBfMeaNfh9lxZB/499a1/t9/ZmQwc6O/n+hflMWEOn9R0krnBTQeyqB3pA1Va+AohUcN6iheLWqH1RQbkNZWNlKWSjpvBjmRUvkYWjZgAvbamEwxMSezJ4IzGZPLrOynOAHUpQ0/CI6+iWVC7/pc5fpMfvsUUSl7y94Y1CeKNF5h/QFSRGHVAjbK3lXTZ0qyHE9gSjHrVUDVcNiH6qu5qhZ0wYf2ZWyf8XU1Fh+Bh8z8OchZgnl3Wrb6XztOO3VB8cQOw4/G3x53RDGUokb8J03wtPwR3ja4LwBcXAh3uBQ31qoL250OhuL9YWt59vbcB9L1+8lJ2malZaML5nMZre7mXHNdpf2XprRnUc/lV06R0y8M6N45wR214NxT60EjHuqfAjXmM3CNc6b3GZQhCLPJZsc3oSPOYe3mGtyHh5hGgty52+5S5cjri65szgwXgLGUxNIeMSVuPAoPIpwHHo8J6XVZAzmwm+MRXY9Jq1zeN7R2egjvUv3yRRyOUFuBvtipbDx47F0AxyFVEFGfhpeawxaOJKfuMMGkwlmtQZx9aHG6D6Lh3YxczgxcZgSJjxRn2riL3t/mWkAAAABAAAAAQAAO8vwqV8PPPUACwPoAAAAANC+FsgAAAAA0L3smP/9/7ED6AMLAAAACAACAAAAAAAAeJxjYGRgYA76n8UQxfyCgeH/NyAJFEEBHACQkgXuAAB4nGN+wcDALIiEXyAwkzUDAwBBEgQmAAAAAAAAAD4AdgCWAPABHAFIAXcAAAABAAAACAA0AAMAAAAAAAIAAAAQAHMAAAAcC3AAAAAAeJx1kM1Kw0AUhc/U/mArLiy4HjeiiOkPurBuxELrSsFFQVzI2E6T1DRTJlOhr+A7+BC+kM/iSTJIEcwwk++ee+7NnQA4wDcEyueSu2SBOqOSK2jg2vMO9VvPVfKd5xpauPdc53ry3MQZXjy30MYHO4jqLqMFPj0L7ImG5wr2RdvzDvUjz1XyuecaDsWV5zr1B89NTMSz5xaOxdfQrDY2DiMnT4anst/tXcjXjTSU4lQlUq1dZGwmb+TcpE4niQmmZul0qN7iRx2uE2XLoDwn2maxSWUv6JbCWKfaKqdnedfsPew7N5dza5Zy5PvJlTULPXVB5Nxq0OlsfwdDGKywgUWMEBEcJE6onvLdRxc9XJBe6ZB0lq4YKRQSKgprVkRFJmN8wz1nlFLVdCTkAFOey0IJWfHG+seC18wrVm5ntnlCzvvGRUfJWQJOtO0Yk9PCpQp99jtrhne6+lQdJ8qnssUUEqM/80neP88tqEypB8VfcFQH6HD9c58fnU58DwAAeJxjYGKAAC4G7ICDgYGRiZGZkYWRlZGNkZ2Rgy05MS85NYelIKe0mDU3M6+0mDm1MpUzJb88Tze/IDWPvbQATHPlpJal5uiCxBkYAP+wElx4nGPw3sFwIihiIyNjX+QGxp0cDBwMyQUbGVidNjIwaEFoDhR6JwMDAycyi5nBZaMKY0dgxAaHjoiNzCkuG9VAvF0cDQyMLA4dySERICWRQLCRgUdrB+P/1g0svRuZGFwAB9MiuAAAAA==') format('woff'); - font-weight: normal; - font-style: normal; -} - -.tegaki-icon:before { - font-size: 10px; - width: 10px; - font-family: 'tegaki'; - font-style: normal; - font-weight: normal; - speak: none; - display: inline-block; - text-align: center; - font-variant: normal; - text-transform: none; - line-height: 1em; -} - -.tegaki-cancel:before { content: '\e800'; } /* '' */ -.tegaki-plus:before { content: '\e801'; } /* '' */ -.tegaki-minus:before { content: '\e802'; } /* '' */ -.tegaki-eye:before { content: '\e803'; } /* '' */ -.tegaki-down-open:before { content: '\e804'; } /* '' */ -.tegaki-up-open:before { content: '\e805'; } /* '' */ -.tegaki-level-down:before { content: '\e806'; } /* '' */ - -#tegaki { - position: fixed; - width: 100%; - height: 100%; - top: 0; - left: 0; - background-color: #a3b1bf; - color: #000; - font-family: arial, sans-serif; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - overflow: auto; - z-index: 9999; - image-rendering: optimizeSpeed; - image-rendering: -webkit-optimize-contrast; - image-rendering: pixelated; - image-rendering: -moz-crisp-edges; -} - -#tegaki-debug { - position: absolute; - left: 0; - top: 0; -} - -#tegaki-debug canvas { - width: 75px; - height: 75px; - display: block; - border: 1px solid black; -} - -.tegaki-backdrop { - overflow: hidden; -} - -.tegaki-hidden { - display: none !important; -} - -.tegaki-strike { - text-decoration: line-through; -} - -#tegaki-cnt { - left: 50%; - top: 50%; - position: absolute; -} - -#tegaki-cnt.tegaki-overflow-x { - left: 10px; - margin-left: 0 !important; -} - -#tegaki-cnt.tegaki-overflow-y { - top: 10px; - margin-top: 0 !important; -} - -.tegaki-tb-btn { - margin-left: 10px; - cursor: pointer; - text-decoration: none; -} - -.tegaki-tb-btn:hover { - color: #007FFF; -} - -.tegaki-tb-btn:focus { - color: #007FFF; - outline: none; -} - -#tegaki-menu-bar { - font-size: 12px; - white-space: nowrap; - position: absolute; - right: 0; -} - -#tegaki-canvas { - -moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); - background: #FFFAFA; -} - -#tegaki-layers { - display: inline-block; - font-size: 0; -} - -#tegaki-finish-btn { - font-weight: bold; -} - -.tegaki-ctrlgrp { - margin-bottom: 5px; -} - -.tegaki-label { - font-size: 10px; -} - -.tegaki-label:after { - content: ' ' attr(data-value); -} - -#tegaki-ghost-layer, -.tegaki-layer { - position: absolute; - left: 0; -} - -#tegaki-ctrl { - position: absolute; - display: inline-block; - width: 80px; - padding-left: 5px; - font-size: 14px; -} - -#tegaki-color { - padding: 0; - border: 0; - display: block; - width: 25px; - height: 25px; - cursor: pointer; -} - -#tegaki-layer-grp span { - font-size: 12px; - margin-right: 3px; - cursor: pointer; -} - -#tegaki-layer-grp span:hover { - color: #007FFF; -} - -#tegaki-color::-moz-focus-inner { - border: none; - padding: 0; -} - -#tegaki-alpha, -#tegaki-size { - width: 90%; - margin: auto; -} - -#tegaki-ctrl select { - font-size: 11px; - width: 100%; -} diff --git a/misc/website/backend/app/static/css/yotsuba.css b/misc/website/backend/app/static/css/yotsuba.css deleted file mode 100755 index f00c023..0000000 --- a/misc/website/backend/app/static/css/yotsuba.css +++ /dev/null @@ -1,63 +0,0 @@ -body { - background-color: #ffffee; } - -body, h1, h2 { - color: #800000; } - -a, a:visited, .broken_link { - color: #0000ee; } - -a:hover, .quote_link:hover, .broken_link:hover { - color: #ff0000; } - -.quote_link { - color: #000080; } - -.quote_green { - color: #789922; } - -.quote_blue { - color: #292299; } - -.announcement { - color: #800000; } - -#post_form form .title, .admin_form form .title { - background-color: #f0c0b0; - border: 1px solid #d99f91; } - -table thead td { - background-color: #f0e0d6; - border: 1px solid #d9bfb7; } -table tr:nth-of-type(odd) { - background-color: #ffffee; } -table tr:nth-of-type(even) { - background-color: #ffffff; } - -.post_container { - background-color: #f0e0d6; - border-right: 1px solid #d9bfb7; - border-bottom: 1px solid #d9bfb7; } - .post_container:target { - background-color: #f0c0b0; - border-right: 1px solid #d99f91; - border-bottom: 1px solid #d99f91; } - -.post_subject { - color: #cc1105; } - -.post_name, .post_trip { - color: #117743; } - -.post_menu .menu { - background-color: #f0e0d6; - border: 1px solid #d9bfb7; } - .post_menu .menu div { - border-right: 1px solid #d9bfb7; - border-bottom: 1px solid #d9bfb7; } - .post_menu .menu div form button { - color: #800000; } - .post_menu .menu div form button:hover { - color: #ff0000; } - -/*# sourceMappingURL=yotsuba.css.map */ diff --git a/misc/website/backend/app/static/css/yotsuba.css.map b/misc/website/backend/app/static/css/yotsuba.css.map deleted file mode 100755 index d0b5431..0000000 --- a/misc/website/backend/app/static/css/yotsuba.css.map +++ /dev/null @@ -1,7 +0,0 @@ -{ -"version": 3, -"mappings": "AAgBA,IAAK;EACJ,gBAAgB,EAjBD,OAAO;;AAoBvB,YAAa;EACZ,KAAK,EAjBU,OAAO;;AAoBvB,0BAA2B;EAC1B,KAAK,EApBU,OAAO;;AAuBvB,8CAA+C;EAC9C,KAAK,EAvBU,OAAO;;AA0BvB,WAAY;EACX,KAAK,EA1BU,OAAO;;AA6BvB,YAAa;EACZ,KAAK,EA1BU,OAAO;;AA6BvB,WAAY;EACX,KAAK,EA7BU,OAAO;;AAgCvB,aAAc;EACb,KAAK,EAzCU,OAAO;;AA8CrB,+CAAO;EACN,gBAAgB,EAtCH,OAAO;EAuCpB,MAAM,EAAE,iBAAwB;;AAOjC,cAAG;EACF,gBAAgB,EApDH,OAAO;EAqDpB,MAAM,EAAE,iBAAiB;AAI3B,yBAAoB;EACnB,gBAAgB,EAlEF,OAAO;AAqEtB,0BAAqB;EACpB,gBAAgB,EArEF,OAAO;;AAyEvB,eAAgB;EACf,gBAAgB,EAnED,OAAO;EAoEtB,YAAY,EAAE,iBAAiB;EAC/B,aAAa,EAAE,iBAAiB;EAEhC,sBAAS;IACR,gBAAgB,EAnEF,OAAO;IAoErB,YAAY,EAAE,iBAAwB;IACtC,aAAa,EAAE,iBAAwB;;AAIzC,aAAc;EACb,KAAK,EApFU,OAAO;;AAuFvB,sBAAuB;EACtB,KAAK,EAlFU,OAAO;;AAqFvB,gBAAiB;EAChB,gBAAgB,EAvFD,OAAO;EAwFtB,MAAM,EAAC,iBAAiB;EAExB,oBAAI;IACH,YAAY,EAAE,iBAAiB;IAC/B,aAAa,EAAE,iBAAiB;IAG/B,gCAAO;MACN,KAAK,EApGO,OAAO;MAsGnB,sCAAQ;QACP,KAAK,EArGM,OAAO", -"sources": ["../sass/yotsuba.scss"], -"names": [], -"file": "yotsuba.css" -} \ No newline at end of file diff --git a/misc/website/backend/app/static/css/yotsuba_b.css b/misc/website/backend/app/static/css/yotsuba_b.css deleted file mode 100755 index 8e346b1..0000000 --- a/misc/website/backend/app/static/css/yotsuba_b.css +++ /dev/null @@ -1,61 +0,0 @@ -body { - background-color: #eef2ff; - color: #000000; } - -h1, h2 { - color: #af0a0f; } - -a, a:visited, .broken_link { - color: #34345c; } - -a:hover, .quote_link, .quote_link:visited, .broken_link:hover { - color: #dd0000; } - -.quote_green { - color: #789922; } - -.quote_blue { - color: #292299; } - -.announcement { - color: #af0a0f; } - -#post_form form .title, .admin_form form .title { - background-color: #d6bad0; - border: 1px solid #ba9dbf; } - -table thead td { - background-color: #d6daf0; - border: 1px solid #b7c5d9; } -table tr:nth-of-type(odd) { - background-color: #eef2ff; } -table tr:nth-of-type(even) { - background-color: #f7f9ff; } - -.post_container { - background-color: #d6daf0; - border-right: 1px solid #b7c5d9; - border-bottom: 1px solid #b7c5d9; } - .post_container:target { - background-color: #d6bad0; - border-right: 1px solid #ba9dbf; - border-bottom: 1px solid #ba9dbf; } - -.post_subject { - color: #0f0c5d; } - -.post_name, .post_trip { - color: #117743; } - -.post_menu .menu { - background-color: #d6daf0; - border: 1px solid #b7c5d9; } - .post_menu .menu div { - border-right: 1px solid #b7c5d9; - border-bottom: 1px solid #b7c5d9; } - .post_menu .menu div form button { - color: #34345c; } - .post_menu .menu div form button:hover { - color: #dd0000; } - -/*# sourceMappingURL=yotsuba_b.css.map */ diff --git a/misc/website/backend/app/static/css/yotsuba_b.css.map b/misc/website/backend/app/static/css/yotsuba_b.css.map deleted file mode 100755 index a5f9718..0000000 --- a/misc/website/backend/app/static/css/yotsuba_b.css.map +++ /dev/null @@ -1,7 +0,0 @@ -{ -"version": 3, -"mappings": "AAeA,IAAK;EACJ,gBAAgB,EAhBD,OAAO;EAiBtB,KAAK,EARU,OAAO;;AAWvB,MAAO;EACN,KAAK,EAjBU,OAAO;;AAoBvB,0BAA2B;EAC1B,KAAK,EApBU,OAAO;;AAuBvB,6DAA8D;EAC7D,KAAK,EAvBU,OAAO;;AA0BvB,YAAa;EACZ,KAAK,EAvBU,OAAO;;AA0BvB,WAAY;EACX,KAAK,EA1BU,OAAO;;AA6BvB,aAAc;EACb,KAAK,EArCU,OAAO;;AA0CrB,+CAAO;EACN,gBAAgB,EAnCH,OAAO;EAoCpB,MAAM,EAAE,iBAAwB;;AAOjC,cAAG;EACF,gBAAgB,EAjDH,OAAO;EAkDpB,MAAM,EAAE,iBAAiB;AAI3B,yBAAoB;EACnB,gBAAgB,EA9DF,OAAO;AAiEtB,0BAAqB;EACpB,gBAAgB,EAjEF,OAAO;;AAqEvB,eAAgB;EACf,gBAAgB,EAhED,OAAO;EAiEtB,YAAY,EAAE,iBAAiB;EAC/B,aAAa,EAAE,iBAAiB;EAEhC,sBAAS;IACR,gBAAgB,EAhEF,OAAO;IAiErB,YAAY,EAAE,iBAAwB;IACtC,aAAa,EAAE,iBAAwB;;AAIzC,aAAc;EACb,KAAK,EAhFU,OAAO;;AAmFvB,sBAAuB;EACtB,KAAK,EA/EU,OAAO;;AAkFvB,gBAAiB;EAChB,gBAAgB,EApFD,OAAO;EAqFtB,MAAM,EAAC,iBAAiB;EAExB,oBAAI;IACH,YAAY,EAAE,iBAAiB;IAC/B,aAAa,EAAE,iBAAiB;IAG/B,gCAAO;MACN,KAAK,EA/FO,OAAO;MAiGnB,sCAAQ;QACP,KAAK,EAjGM,OAAO", -"sources": ["../sass/yotsuba_b.scss"], -"names": [], -"file": "yotsuba_b.css" -} \ No newline at end of file diff --git a/misc/website/backend/app/static/js/script.js b/misc/website/backend/app/static/js/script.js deleted file mode 100755 index d45cda3..0000000 --- a/misc/website/backend/app/static/js/script.js +++ /dev/null @@ -1,249 +0,0 @@ -var body_size = 15; -var text_size = 10000; - -// https://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata -function dataURItoBlob(dataURI) { - // convert base64/URLEncoded data component to raw binary data held in a string - var byteString; - if (dataURI.split(',')[0].indexOf('base64') >= 0) - byteString = atob(dataURI.split(',')[1]); - else - byteString = unescape(dataURI.split(',')[1]); - - // separate out the mime component - var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; - - // write the bytes of the string to a typed array - var ia = new Uint8Array(byteString.length); - for (var i = 0; i < byteString.length; i++) { - ia[i] = byteString.charCodeAt(i); - } - - return new Blob([ia], {type:mimeString}); -} - -// Send quote text to submission form -// @param event A ClickEvent object -// @return none -function quote_post(event) { - var comment = document.getElementById("submit_comment"); - comment.value += ">>" + event.target.text + "\n"; - comment.focus(); -} - -// Send quote text to submission from when coming from board index -// @return none -function quote_thread() { - var hash = window.location.hash; - - if (hash[1] === "q") { - quote_post({ - target: { - text: hash.substring(2) - } - }); - } -} - -// Restrict the size of the comment block -// @return none -function restrict_comment_size() { - var comment = document.getElementById("submit_comment"); - - if (comment.value.length >= text_size) { - comment.value = comment.value.slice(0, text_size); - } -} - -// Cancel form submission if input is invalid -// @return valid -function validate_input() { - var file_input = document.getElementById("submit_file"); - - if (window.FileReader && file_input.files && file_input.files[0]) { - file = file_input.files[0]; - - if (file.size / 1024 / 1024 > body_size) { - alert("File size cannot exceed " + body_size + " MB."); - return false; - } - } -} - -// Toggle post menu visibility -// @return none -function toggle_menu() { - var child = this.parentNode.children[1]; - - if (child.style.display === "block") { - child.style.display = "none"; - } else { - child.style.display = "block"; - } -} - -// Add click events to all reply links -// @return none -function add_anchor_listeners() { - var elements = document.getElementsByClassName("link_reply"); - - for (var i=0; i < elements.length; i++) { - elements[i].onclick = quote_post; - } -} - -// Add change events to various form elements -// @return none -function add_change_listeners() { - var elements = document.getElementsByClassName("change_submit"); - - for (i=0; i < elements.length; i++) { - var element = elements[i]; - element.onchange = function(event) { - event.target.form.submit(); - }; - } - - var elements = document.getElementsByClassName("change_delete"); - - for (i=0; i < elements.length; i++) { - var element = elements[i]; - element.onchange = function(event) { - if (window.confirm("Are you sure you want to do this?")) { - event.target.form.submit(); - } - }; - } -} - -// Add click events to all menu buttons -// @return none -function add_menu_listeners() { - var buttons = document.getElementsByClassName("post_menu"); - - for (i=0; i < buttons.length; i++) { - var button = buttons[i]; - button.children[0].onclick = toggle_menu; - } -} - -// Add events to various user inputs -// @return none -function add_input_constraints() { - var comment = document.getElementById("submit_comment"); - if (comment) { - comment.onkeydown = restrict_comment_size; - comment.onkeyup = restrict_comment_size; - } - - var form = document.getElementById("submit_form"); - - if (form) { - form.onsubmit = validate_input; - } -} - -// Open a Tegaki canvas and add an image to it -// @param event A ClickEvent object -// @param path Path to image (optional) -function remix(event, path) { - var file = document.getElementById("draw"); - var clear = document.getElementById("tegaki_clear"); - - // get image - var image = new Image(); - image.src = path ? path : this.dataset.path; - - image.onload = function() { - Tegaki.open({ - onDone: function() { - var dataURL = Tegaki.flatten().toDataURL('image/png'); - //var blob = dataURItoBlob(dataURL); - - file.value = dataURL;//blob; - clear.disabled = false; - }, - onCancel: function() {}, - width: image.width, - height: image.height - }); - - // Force some data into Tegaki - Tegaki.layers[0].ctx.drawImage(image, 0, 0); - Tegaki.addLayer(); - Tegaki.setActiveLayer(Tegaki.layers.length); - } -} - -// Add ClickEvent listeners to Tegaki buttons -// @return none -function prepare_tegaki() { - var file = document.getElementById("draw"); - var width = document.getElementById("tegaki_width"); - var height = document.getElementById("tegaki_height"); - var draw = document.getElementById("tegaki_draw"); - var clear = document.getElementById("tegaki_clear"); - - // Standard draw button - if (draw) { - draw.onclick = function() { - Tegaki.open({ - onDone: function() { - var dataURL = Tegaki.flatten().toDataURL('image/png'); - //var blob = dataURItoBlob(dataURL); - - file.value = dataURL;//blob; - clear.disabled = false; - }, - onCancel: function() {}, - width: width.value > 2560 ? 2560 : width.value, - height: height.value > 2560 ? 2560 : height.value - }); - }; - } - - // Standard clear button - if (clear) { - clear.onclick = function() { - Tegaki.destroy(); - file.value = null; - clear.disabled = true; - }; - } - - // Remix buttons - var buttons = document.getElementsByClassName("remix"); - for (i=0; i < buttons.length; i++) { - var button = buttons[i]; - button.onclick = remix; - } - - // Come from board, auto-remix - var hash = window.location.hash; - if (hash[1] === "r") { - var button = document.getElementById(hash.slice(1)); - remix(null, button.dataset.path); - } -} - -// Check if document has loaded yet -// @return none -function ready() { - if (document.readyState != "loading") { - quote_thread(); - add_anchor_listeners(); - add_change_listeners() - add_menu_listeners(); - add_input_constraints(); - prepare_tegaki(); - } else { - document.addEventListener("DOMContentLoaded", quote_thread); - document.addEventListener("DOMContentLoaded", add_anchor_listeners); - document.addEventListener("DOMContentLoaded", add_change_listeners); - document.addEventListener("DOMContentLoaded", add_menu_listeners); - document.addEventListener("DOMContentLoaded", add_input_constraints); - document.addEventListener("DOMContentLoaded", prepare_tegaki); - } -} - -ready(); diff --git a/misc/website/backend/app/static/js/tegaki/LICENSE b/misc/website/backend/app/static/js/tegaki/LICENSE deleted file mode 100755 index 54d364f..0000000 --- a/misc/website/backend/app/static/js/tegaki/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2015 Maxime Youdine - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/misc/website/backend/app/static/js/tegaki/README.md b/misc/website/backend/app/static/js/tegaki/README.md deleted file mode 100755 index 6e3b4e7..0000000 --- a/misc/website/backend/app/static/js/tegaki/README.md +++ /dev/null @@ -1,10 +0,0 @@ -[Demo](https://desuwa.github.io/tegaki.html) - -```javascript -Tegaki.open({ - onDone: function() { window.open(Tegaki.flatten().toDataURL('image/png')); }, - onCancel: function() { console.log('Closing...')}, - width: 380, - height: 380 -}); -``` diff --git a/misc/website/backend/app/static/js/tegaki/Rakefile b/misc/website/backend/app/static/js/tegaki/Rakefile deleted file mode 100755 index 1b88c69..0000000 --- a/misc/website/backend/app/static/js/tegaki/Rakefile +++ /dev/null @@ -1,20 +0,0 @@ -Encoding.default_external = 'UTF-8' - -desc 'Run JShint' -task :jshint do |t| - require 'jshintrb' - - opts = { - laxbreak: true, - boss: true, - expr: true, - sub: true, - browser: true, - devel: true, - globalstrict: true, - unused: true, - '-W079' => true # no-native-reassign - } - - puts Jshintrb.report("'use strict';" + File.read('tegaki.js'), opts) -end diff --git a/misc/website/backend/app/static/js/tegaki/tegaki.js b/misc/website/backend/app/static/js/tegaki/tegaki.js deleted file mode 100755 index 775951d..0000000 --- a/misc/website/backend/app/static/js/tegaki/tegaki.js +++ /dev/null @@ -1,1925 +0,0 @@ -var TegakiBrush = { - brushFn: function(x, y) { - var i, ctx, dest, data, len, kernel; - - x = 0 | x; - y = 0 | y; - - ctx = Tegaki.ghostCtx; - dest = ctx.getImageData(x, y, this.brushSize, this.brushSize); - data = dest.data; - kernel = this.kernel; - len = kernel.length; - - i = 0; - while (i < len) { - data[i] = this.rgb[0]; ++i; - data[i] = this.rgb[1]; ++i; - data[i] = this.rgb[2]; ++i; - data[i] += kernel[i] * (1.0 - data[i] / 255); ++i; - } - - ctx.putImageData(dest, x, y); - }, - - commit: function() { - Tegaki.activeCtx.drawImage(Tegaki.ghostCanvas, 0, 0); - Tegaki.ghostCtx.clearRect(0, 0, - Tegaki.ghostCanvas.width, Tegaki.ghostCanvas.height - ); - }, - - draw: function(posX, posY, pt) { - var offset, mx, my, fromX, fromY, dx, dy, err, derr, step, stepAcc; - - offset = this.center; - step = this.stepSize; - stepAcc = this.stepAcc; - - if (pt === true) { - this.stepAcc = 0; - this.posX = posX; - this.posY = posY; - this.brushFn(posX - offset, posY - offset); - return; - } - - fromX = this.posX; - fromY = this.posY; - - if (fromX < posX) { dx = posX - fromX; mx = 1; } - else { dx = fromX - posX; mx = -1; } - if (fromY < posY) { dy = posY - fromY; my = 1; } - else { dy = fromY - posY; my = -1; } - - err = (dx > dy ? dx : -dy) / 2; - - dx = -dx; - - while (true) { - ++stepAcc; - if (stepAcc > step) { - this.brushFn(fromX - offset, fromY - offset); - stepAcc = 0; - } - if (fromX === posX && fromY === posY) { - break; - } - derr = err; - if (derr > dx) { err -= dy; fromX += mx; } - if (derr < dy) { err -= dx; fromY += my; } - } - - this.stepAcc = stepAcc; - this.posX = posX; - this.posY = posY; - }, - - generateBrush: function() { - var i, size, r, brush, ctx, dest, data, len, sqd, sqlen, hs, col, row, - ecol, erow, a; - - size = this.size * 2; - r = size / 2; - - brush = T$.el('canvas'); - brush.width = brush.height = size; - ctx = brush.getContext('2d'); - dest = ctx.getImageData(0, 0, size, size); - data = dest.data; - len = size * size * 4; - sqlen = Math.sqrt(r * r); - hs = Math.round(r); - col = row = -hs; - - i = 0; - while (i < len) { - if (col >= hs) { - col = -hs; - ++row; - continue; - } - - ecol = col; - erow = row; - - if (ecol < 0) { ecol = -ecol; } - if (erow < 0) { erow = -erow; } - - sqd = Math.sqrt(ecol * ecol + erow * erow); - - if (sqd > sqlen) { - a = 0; - } - else { - a = sqd / sqlen; - a = (Math.exp(1 - 1 / a) / a); - a = 255 - ((0 | (a * 100 + 0.5)) / 100) * 255; - } - - if (this.alphaDamp) { - a *= this.alpha * this.alphaDamp; - } - else { - a *= this.alpha; - } - - data[i + 3] = a; - - i += 4; - - ++col; - } - - ctx.putImageData(dest, 0, 0); - - this.center = r; - this.brushSize = size; - this.brush = brush; - this.kernel = data; - }, - - setSize: function(size, noBrush) { - this.size = size; - if (!noBrush) this.generateBrush(); - this.stepSize = Math.floor(this.size * this.step); - }, - - setAlpha: function(alpha, noBrush) { - this.alpha = alpha; - if (!noBrush) this.generateBrush(); - }, - - setColor: function(color, noBrush) { - this.rgb = Tegaki.hexToRgb(color); - if (!noBrush) this.generateBrush(); - }, - - set: function() { - this.setAlpha(this.alpha, true); - this.setSize(this.size, true); - this.setColor(Tegaki.toolColor, true); - this.generateBrush(); - } -}; - -var TegakiPen = { - init: function() { - this.size = 4; - this.alpha = 0.5; - this.step = 0.1; - this.stepAcc = 0; - }, - - draw: TegakiBrush.draw, - - commit: TegakiBrush.commit, - - brushFn: TegakiBrush.brushFn, - - generateBrush: function() { - var size, r, brush, ctx; - - size = this.size; - r = size / 2; - - brush = T$.el('canvas'); - brush.width = brush.height = size; - ctx = brush.getContext('2d'); - ctx.globalAlpha = this.alpha; - ctx.beginPath(); - ctx.arc(r, r, r, 0, Tegaki.TWOPI, false); - ctx.fillStyle = '#000000'; - ctx.fill(); - ctx.closePath(); - - this.center = r; - this.brushSize = size; - this.brush = brush; - this.kernel = ctx.getImageData(0, 0, this.brushSize, this.brushSize).data; - }, - - setSize: TegakiBrush.setSize, - - setAlpha: TegakiBrush.setAlpha, - - setColor: TegakiBrush.setColor, - - set: TegakiBrush.set -}; - -var TegakiPipette = { - size: 1, - alpha: 1, - noCursor: true, - - draw: function(posX, posY) { - var c, ctx; - - if (true) { - ctx = Tegaki.flatten().getContext('2d'); - } - else { - ctx = Tegaki.activeCtx; - } - - c = Tegaki.getColorAt(ctx, posX, posY); - - Tegaki.setToolColor(c); - Tegaki.updateUI('color'); - } -}; - -var TegakiAirbrush = { - init: function() { - this.size = 32; - this.alpha = 0.5; - this.alphaDamp = 0.2; - this.step = 0.25; - this.stepAcc = 0; - }, - - draw: TegakiBrush.draw, - - commit: TegakiBrush.commit, - - brushFn: TegakiBrush.brushFn, - - generateBrush: TegakiBrush.generateBrush, - - setSize: TegakiBrush.setSize, - - setAlpha: TegakiBrush.setAlpha, - - setColor: TegakiBrush.setColor, - - set: TegakiBrush.set -}; - -var TegakiPencil = { - init: function() { - this.size = 1; - this.alpha = 1.0; - this.step = 0.25; - this.stepAcc = 0; - }, - - draw: TegakiBrush.draw, - - commit: TegakiBrush.commit, - - brushFn: function(x, y) { - var i, ctx, dest, data, len, kernel, a; - - x = 0 | x; - y = 0 | y; - - ctx = Tegaki.ghostCtx; - dest = ctx.getImageData(x, y, this.brushSize, this.brushSize); - data = dest.data; - kernel = this.kernel; - len = kernel.length; - - a = this.alpha * 255; - - i = 0; - while (i < len) { - data[i] = this.rgb[0]; ++i; - data[i] = this.rgb[1]; ++i; - data[i] = this.rgb[2]; ++i; - if (kernel[i] > 0) { - data[i] = a; - } - ++i; - } - - ctx.putImageData(dest, x, y); - }, - - generateBrush: TegakiPen.generateBrush, - - setSize: TegakiBrush.setSize, - - setAlpha: TegakiBrush.setAlpha, - - setColor: TegakiBrush.setColor, - - set: TegakiBrush.set -}; - -var TegakiEraser = { - init: function() { - this.size = 8; - this.alpha = 1.0; - this.step = 0.25; - this.stepAcc = 0; - }, - - draw: TegakiBrush.draw, - - brushFn: function(x, y) { - var i, ctx, dest, data, len, kernel; - - x = 0 | x; - y = 0 | y; - - ctx = Tegaki.activeCtx; - dest = ctx.getImageData(x, y, this.brushSize, this.brushSize); - data = dest.data; - kernel = this.kernel; - len = kernel.length; - - for (i = 3; i < len; i += 4) { - if (kernel[i] > 0) { - data[i] = 0; - } - } - - ctx.putImageData(dest, x, y); - }, - - generateBrush: TegakiPen.generateBrush, - - setSize: TegakiBrush.setSize, - - setAlpha: TegakiBrush.setAlpha, - - setColor: TegakiBrush.setColor, - - set: TegakiBrush.set -}; - -var TegakiDodge = { - init: function() { - this.size = 24; - this.alpha = 0.25; - this.alphaDamp = 0.05; - this.step = 0.25; - this.stepAcc = 0; - }, - - brushFn: function(x, y) { - var i, a, aa, ctx, dest, data, len, kernel; - - x = 0 | x; - y = 0 | y; - - ctx = Tegaki.activeCtx; - dest = ctx.getImageData(x, y, this.brushSize, this.brushSize); - data = dest.data; - kernel = this.kernel; - len = kernel.length; - - i = 0; - while (i < len) { - aa = kernel[i + 3] * 0.3; - a = 1 + kernel[i + 3] / 255; - data[i] = data[i] * a + aa; ++i; - data[i] = data[i] * a + aa; ++i; - data[i] = data[i] * a + aa; ++i; - ++i; - } - - ctx.putImageData(dest, x, y); - }, - - draw: TegakiBrush.draw, - - generateBrush: TegakiBrush.generateBrush, - - setSize: TegakiBrush.setSize, - - setAlpha: TegakiBrush.setAlpha, - - setColor: TegakiBrush.setColor, - - set: TegakiBrush.set -}; - -var TegakiBurn = { - init: TegakiDodge.init, - - brushFn: function(x, y) { - var i, a, ctx, dest, data, len, kernel; - - x = 0 | x; - y = 0 | y; - - ctx = Tegaki.activeCtx; - dest = ctx.getImageData(x, y, this.brushSize, this.brushSize); - data = dest.data; - kernel = this.kernel; - len = kernel.length; - - i = 0; - while (i < len) { - a = 1 - kernel[i + 3] / 255; - data[i] = data[i] * a; ++i; - data[i] = data[i] * a; ++i; - data[i] = data[i] * a; ++i; - ++i; - } - - ctx.putImageData(dest, x, y); - }, - - draw: TegakiBrush.draw, - - generateBrush: TegakiDodge.generateBrush, - - setSize: TegakiBrush.setSize, - - setAlpha: TegakiBrush.setAlpha, - - setColor: TegakiBrush.setColor, - - set: TegakiBrush.set -}; - -var TegakiBlur = { - init: TegakiDodge.init, - - brushFn: function(x, y) { - var i, j, ctx, src, size, srcData, dest, destData, lim, kernel, - sx, sy, r, g, b, a, aa, acc, kx, ky; - - x = 0 | x; - y = 0 | y; - - size = this.brushSize; - ctx = Tegaki.activeCtx; - src = ctx.getImageData(x, y, size, size); - srcData = src.data; - dest = ctx.createImageData(size, size); - destData = dest.data; - kernel = this.kernel; - lim = size - 1; - - for (sx = 0; sx < size; ++sx) { - for (sy = 0; sy < size; ++sy) { - r = g = b = a = acc = 0; - i = (sy * size + sx) * 4; - if (kernel[(sy * size + sx) * 4 + 3] === 0 - || sx === 0 || sy === 0 || sx === lim || sy === lim) { - destData[i] = srcData[i]; ++i; - destData[i] = srcData[i]; ++i; - destData[i] = srcData[i]; ++i; - destData[i] = srcData[i]; - continue; - } - for (kx = -1; kx < 2; ++kx) { - for (ky = -1; ky < 2; ++ky) { - j = ((sy - ky) * size + (sx - kx)) * 4; - aa = srcData[j + 3]; - acc += aa; - r += srcData[j] * aa; ++j; - g += srcData[j] * aa; ++j; - b += srcData[j] * aa; ++j; - a += srcData[j]; - } - } - destData[i] = r / acc; ++i; - destData[i] = g / acc; ++i; - destData[i] = b / acc; ++i; - destData[i] = a / 9; - } - } - - ctx.putImageData(dest, x, y); - }, - - draw: TegakiBrush.draw, - - generateBrush: TegakiDodge.generateBrush, - - setSize: TegakiBrush.setSize, - - setAlpha: TegakiBrush.setAlpha, - - setColor: TegakiBrush.setColor, - - set: TegakiBrush.set -}; - -var TegakiHistory = { - maxSize: 10, - - undoStack: [], - redoStack: [], - - pendingAction: null, - - clear: function() { - this.undoStack = []; - this.redoStack = []; - this.pendingAction = null; - }, - - push: function(action) { - this.undoStack.push(action); - - if (this.undoStack.length > this.maxSize) { - this.undoStack.shift(); - } - - if (this.redoStack.length > 0) { - this.redoStack = []; - } - }, - - undo: function() { - var action; - - if (!this.undoStack.length) { - return; - } - - action = this.undoStack.pop(); - action.undo(); - - this.redoStack.push(action); - }, - - redo: function() { - var action; - - if (!this.redoStack.length) { - return; - } - - action = this.redoStack.pop(); - action.redo(); - - this.undoStack.push(action); - } -}; - -var TegakiHistoryActions = { - Draw: function(layerId) { - this.canvasBefore = null; - this.canvasAfter = null; - this.layerId = layerId; - }, - - DestroyLayers: function(indices, layers) { - this.indices = indices; - this.layers = layers; - this.canvasBefore = null; - this.canvasAfter = null; - this.layerId = null; - }, - - AddLayer: function(layerId) { - this.layerId = layerId; - }, - - MoveLayer: function(layerId, up) { - this.layerId = layerId; - this.up = up; - } -}; - -TegakiHistoryActions.Draw.prototype.addCanvasState = function(canvas, type) { - if (type) { - this.canvasAfter = T$.copyCanvas(canvas); - } - else { - this.canvasBefore = T$.copyCanvas(canvas); - } -}; - -TegakiHistoryActions.Draw.prototype.exec = function(type) { - var i, layer; - - for (i in Tegaki.layers) { - layer = Tegaki.layers[i]; - - if (layer.id === this.layerId) { - layer.ctx.clearRect(0, 0, layer.canvas.width, layer.canvas.height); - layer.ctx.drawImage(type ? this.canvasAfter: this.canvasBefore, 0, 0); - } - } -}; - -TegakiHistoryActions.Draw.prototype.undo = function() { - this.exec(0); -}; - -TegakiHistoryActions.Draw.prototype.redo = function() { - this.exec(1); -}; - -TegakiHistoryActions.DestroyLayers.prototype.undo = function() { - var i, ii, len, layers, idx, layer, frag; - - layers = new Array(len); - - for (i = 0; (idx = this.indices[i]) !== undefined; ++i) { - layers[idx] = this.layers[i]; - } - - i = ii = 0; - len = Tegaki.layers.length + this.layers.length; - frag = T$.frag(); - - while (i < len) { - if (!layers[i]) { - layer = layers[i] = Tegaki.layers[ii]; - Tegaki.layersCnt.removeChild(layer.canvas); - ++ii; - } - - if (this.layerId && layer.id === this.layerId) { - layer.ctx.clearRect(0, 0, layer.canvas.width, layer.canvas.height); - layer.ctx.drawImage(this.canvasBefore, 0, 0); - } - - frag.appendChild(layers[i].canvas); - - ++i; - } - - Tegaki.layersCnt.insertBefore(frag, Tegaki.canvas.nextElementSibling); - - Tegaki.layers = layers; - - Tegaki.setActiveLayer(); - - Tegaki.rebuildLayerCtrl(); -}; - -TegakiHistoryActions.DestroyLayers.prototype.redo = function() { - var i, layer, ids = []; - - for (i = 0; layer = this.layers[i]; ++i) { - ids.push(layer.id); - } - - if (this.layerId) { - ids.push(this.layerId); - Tegaki.mergeLayers(ids); - } - else { - Tegaki.deleteLayers(ids); - } -}; - -TegakiHistoryActions.MoveLayer.prototype.undo = function() { - Tegaki.setActiveLayer(this.layerId); - Tegaki.moveLayer(this.layerId, !this.up); -}; - -TegakiHistoryActions.MoveLayer.prototype.redo = function() { - Tegaki.setActiveLayer(this.layerId); - Tegaki.moveLayer(this.layerId, this.up); -}; - -TegakiHistoryActions.AddLayer.prototype.undo = function() { - Tegaki.deleteLayers([this.layerId]); - Tegaki.layerIndex--; -}; - -TegakiHistoryActions.AddLayer.prototype.redo = function() { - Tegaki.addLayer(); - Tegaki.setActiveLayer(); -}; - -var T$ = { - docEl: document.documentElement, - - id: function(id) { - return document.getElementById(id); - }, - - cls: function(klass, root) { - return (root || document).getElementsByClassName(klass); - }, - - on: function(o, e, h) { - o.addEventListener(e, h, false); - }, - - off: function(o, e, h) { - o.removeEventListener(e, h, false); - }, - - el: function(name) { - return document.createElement(name); - }, - - frag: function() { - return document.createDocumentFragment(); - }, - - extend: function(destination, source) { - for (var key in source) { - destination[key] = source[key]; - } - }, - - selectedOptions: function(el) { - var i, opt, sel; - - if (el.selectedOptions) { - return el.selectedOptions; - } - - sel = []; - - for (i = 0; opt = el.options[i]; ++i) { - if (opt.selected) { - sel.push(opt); - } - } - - return sel; - }, - - copyCanvas: function(source) { - var canvas = T$.el('canvas'); - canvas.width = source.width; - canvas.height = source.height; - canvas.getContext('2d').drawImage(source, 0, 0); - - return canvas; - } -}; - -var TegakiStrings = { - // Messages - badDimensions: 'Invalid dimensions.', - promptWidth: 'Canvas width in pixels', - promptHeight: 'Canvas height in pixels', - confirmDelLayers: 'Delete selected layers?', - errorMergeOneLayer: 'You need to select at least 2 layers.', - confirmMergeLayers: 'Merge selected layers?', - errorLoadImage: 'Could not load the image.', - noActiveLayer: 'No active layer.', - hiddenActiveLayer: 'The active layer is not visible.', - confirmCancel: 'Are you sure? Your work will be lost.', - confirmChangeCanvas: 'Changing the canvas will clear all layers and history.', - - // UI - color: 'Color', - size: 'Size', - alpha: 'Opacity', - layers: 'Layers', - addLayer: 'Add layer', - delLayers: 'Delete layers', - mergeLayers: 'Merge layers', - showHideLayer: 'Toggle visibility', - moveLayerUp: 'Move up', - moveLayerDown: 'Move down', - tool: 'Tool', - changeCanvas: 'Change canvas', - blank: 'Blank', - newCanvas: 'New', - undo: 'Undo', - redo: 'Redo', - close: 'Close', - finish: 'Finish', - - // Tools - pen: 'Pen', - pencil: 'Pencil', - airbrush: 'Airbrush', - pipette: 'Pipette', - dodge: 'Dodge', - burn: 'Burn', - blur: 'Blur', - eraser: 'Eraser' -}; - -var Tegaki = { - VERSION: '0.0.1', - - bg: null, - cnt: null, - canvas: null, - ctx: null, - layers: [], - layersCnt: null, - ghostCanvas: null, - ghostCtx: null, - activeCtx: null, - activeLayer: null, - layerIndex: null, - - isPainting: false, - isErasing: false, - isColorPicking: false, - - offsetX: 0, - offsetY: 0, - - TWOPI: 2 * Math.PI, - - tools: { - pencil: TegakiPencil, - pen: TegakiPen, - airbrush: TegakiAirbrush, - pipette: TegakiPipette, - dodge: TegakiDodge, - burn: TegakiBurn, - blur: TegakiBlur, - eraser: TegakiEraser - }, - - tool: null, - toolColor: '#000000', - - bgColor: '#ffffff', - maxSize: 32, - maxLayers: 25, - baseWidth: null, - baseHeight: null, - - onDoneCb: null, - onCancelCb: null, - - open: function(opts) { - var bg, cnt, el, el2, tool, lbl, btn, ctrl, canvas, grp, self = Tegaki; - - if (self.bg) { - self.resume(); - return; - } - - if (opts.bgColor) { - self.bgColor = opts.bgColor; - } - - self.onDoneCb = opts.onDone; - self.onCancelCb = opts.onCancel; - - cnt = T$.el('div'); - cnt.id = 'tegaki-cnt'; - - canvas = T$.el('canvas'); - canvas.id = 'tegaki-canvas'; - canvas.width = self.baseWidth = opts.width; - canvas.height = self.baseHeight = opts.height; - - el = T$.el('div'); - el.id = 'tegaki-layers'; - el.appendChild(canvas); - self.layersCnt = el; - - cnt.appendChild(el); - - ctrl = T$.el('div'); - ctrl.id = 'tegaki-ctrl'; - - // Colorpicker - grp = T$.el('div'); - grp.className = 'tegaki-ctrlgrp'; - el = T$.el('input'); - el.id = 'tegaki-color'; - el.value = self.toolColor; - try { - el.type = 'color'; - } catch(e) { - el.type = 'text'; - } - lbl = T$.el('div'); - lbl.className = 'tegaki-label'; - lbl.textContent = TegakiStrings.color; - grp.appendChild(lbl); - T$.on(el, 'change', self.onColorChange); - grp.appendChild(el); - ctrl.appendChild(grp); - - // Size control - grp = T$.el('div'); - grp.className = 'tegaki-ctrlgrp'; - el = T$.el('input'); - el.id = 'tegaki-size'; - el.min = 1; - el.max = self.maxSize; - el.type = 'range'; - lbl = T$.el('div'); - lbl.className = 'tegaki-label'; - lbl.textContent = TegakiStrings.size; - grp.appendChild(lbl); - T$.on(el, 'change', self.onSizeChange); - grp.appendChild(el); - ctrl.appendChild(grp); - - // Alpha control - grp = T$.el('div'); - grp.className = 'tegaki-ctrlgrp'; - el = T$.el('input'); - el.id = 'tegaki-alpha'; - el.min = 0; - el.max = 1; - el.step = 0.01; - el.type = 'range'; - lbl = T$.el('div'); - lbl.className = 'tegaki-label'; - lbl.textContent = TegakiStrings.alpha; - grp.appendChild(lbl); - T$.on(el, 'change', self.onAlphaChange); - grp.appendChild(el); - ctrl.appendChild(grp); - - // Layer control - grp = T$.el('div'); - grp.className = 'tegaki-ctrlgrp'; - grp.id = 'tegaki-layer-grp'; - el = T$.el('select'); - el.id = 'tegaki-layer'; - el.multiple = true; - el.size = 3; - lbl = T$.el('div'); - lbl.className = 'tegaki-label'; - lbl.textContent = TegakiStrings.layers; - grp.appendChild(lbl); - T$.on(el, 'change', self.onLayerChange); - grp.appendChild(el); - el = T$.el('span'); - el.title = TegakiStrings.addLayer; - el.className = 'tegaki-icon tegaki-plus'; - T$.on(el, 'click', self.onLayerAdd); - grp.appendChild(el); - el = T$.el('span'); - el.title = TegakiStrings.delLayers; - el.className = 'tegaki-icon tegaki-minus'; - T$.on(el, 'click', self.onLayerDelete); - grp.appendChild(el); - el = T$.el('span'); - el.id = 'tegaki-layer-visibility'; - el.title = TegakiStrings.showHideLayer; - el.className = 'tegaki-icon tegaki-eye'; - T$.on(el, 'click', self.onLayerVisibilityChange); - grp.appendChild(el); - el = T$.el('span'); - el.id = 'tegaki-layer-merge'; - el.title = TegakiStrings.mergeLayers; - el.className = 'tegaki-icon tegaki-level-down'; - T$.on(el, 'click', self.onMergeLayers); - grp.appendChild(el); - el = T$.el('span'); - el.id = 'tegaki-layer-up'; - el.title = TegakiStrings.moveLayerUp; - el.setAttribute('data-up', '1'); - el.className = 'tegaki-icon tegaki-up-open'; - T$.on(el, 'click', self.onMoveLayer); - grp.appendChild(el); - el = T$.el('span'); - el.id = 'tegaki-layer-down'; - el.title = TegakiStrings.moveLayerDown; - el.className = 'tegaki-icon tegaki-down-open'; - T$.on(el, 'click', self.onMoveLayer); - grp.appendChild(el); - ctrl.appendChild(grp); - - // Tool selector - grp = T$.el('div'); - grp.className = 'tegaki-ctrlgrp'; - el = T$.el('select'); - el.id = 'tegaki-tool'; - for (tool in Tegaki.tools) { - el2 = T$.el('option'); - el2.value = tool; - el2.textContent = TegakiStrings[tool]; - el.appendChild(el2); - } - lbl = T$.el('div'); - lbl.className = 'tegaki-label'; - lbl.textContent = TegakiStrings.tool; - grp.appendChild(lbl); - T$.on(el, 'change', self.onToolChange); - grp.appendChild(el); - ctrl.appendChild(grp); - - cnt.appendChild(ctrl); - - el = T$.el('div'); - el.id = 'tegaki-menu-bar'; - - if (opts.canvasOptions) { - btn = T$.el('select'); - btn.id = 'tegaki-canvas-select'; - btn.title = TegakiStrings.changeCanvas; - btn.innerHTML = ''; - opts.canvasOptions(btn); - T$.on(btn, 'change', Tegaki.onCanvasSelected); - T$.on(btn, 'focus', Tegaki.onCanvasSelectFocused); - el.appendChild(btn); - } - - btn = T$.el('span'); - btn.className = 'tegaki-tb-btn'; - btn.textContent = TegakiStrings.newCanvas; - T$.on(btn, 'click', Tegaki.onNewClick); - el.appendChild(btn); - - btn = T$.el('span'); - btn.className = 'tegaki-tb-btn'; - btn.textContent = TegakiStrings.undo; - T$.on(btn, 'click', Tegaki.onUndoClick); - el.appendChild(btn); - - btn = T$.el('span'); - btn.className = 'tegaki-tb-btn'; - btn.textContent = TegakiStrings.redo; - T$.on(btn, 'click', Tegaki.onRedoClick); - el.appendChild(btn); - - btn = T$.el('span'); - btn.className = 'tegaki-tb-btn'; - btn.textContent = TegakiStrings.close; - T$.on(btn, 'click', Tegaki.onCancelClick); - el.appendChild(btn); - - btn = T$.el('span'); - btn.id = 'tegaki-finish-btn'; - btn.className = 'tegaki-tb-btn'; - btn.textContent = TegakiStrings.finish; - T$.on(btn, 'click', Tegaki.onDoneClick); - el.appendChild(btn); - - cnt.appendChild(el); - - bg = T$.el('div'); - bg.id = 'tegaki'; - self.bg = bg; - bg.appendChild(cnt); - document.body.appendChild(bg); - document.body.classList.add('tegaki-backdrop'); - - el = T$.el('canvas'); - el.id = 'tegaki-ghost-layer'; - el.width = canvas.width; - el.height = canvas.height; - self.ghostCanvas = el; - self.ghostCtx = el.getContext('2d'); - - self.cnt = cnt; - self.centerCnt(); - - self.canvas = canvas; - - self.ctx = canvas.getContext('2d'); - self.ctx.fillStyle = self.bgColor; - self.ctx.fillRect(0, 0, opts.width, opts.height); - - self.addLayer(); - - self.setActiveLayer(); - - self.initTools(); - - self.setTool('pencil'); - - self.updateUI(); - - self.updateCursor(); - self.updatePosOffset(); - - T$.on(self.bg, 'mousemove', self.onMouseMove); - T$.on(self.bg, 'mousedown', self.onMouseDown); - T$.on(self.layersCnt, 'contextmenu', self.onDummy); - - T$.on(document, 'mouseup', self.onMouseUp); - T$.on(window, 'resize', self.updatePosOffset); - T$.on(window, 'scroll', self.updatePosOffset); - }, - - initTools: function() { - var tool; - - for (tool in Tegaki.tools) { - (tool = Tegaki.tools[tool]) && tool.init && tool.init(); - } - }, - - hexToRgb: function(hex) { - var c = hex.match(/^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i); - - if (c) { - return [ - parseInt(c[1], 16), - parseInt(c[2], 16), - parseInt(c[3], 16) - ]; - } - - return null; - }, - - centerCnt: function() { - var aabb, cnt; - - cnt = Tegaki.cnt; - aabb = cnt.getBoundingClientRect(); - - if (aabb.width > T$.docEl.clientWidth || aabb.height > T$.docEl.clientHeight) { - if (aabb.width > T$.docEl.clientWidth) { - cnt.classList.add('tegaki-overflow-x'); - } - if (aabb.height > T$.docEl.clientHeight) { - cnt.classList.add('tegaki-overflow-y'); - } - } - else { - cnt.classList.remove('tegaki-overflow-x'); - cnt.classList.remove('tegaki-overflow-y'); - } - - cnt.style.marginTop = -Math.round(aabb.height / 2) + 'px'; - cnt.style.marginLeft = -Math.round(aabb.width / 2) + 'px'; - }, - - getCursorPos: function(e, axis) { - if (axis === 0) { - return e.clientX + window.pageXOffset + Tegaki.bg.scrollLeft - Tegaki.offsetX; - } - else { - return e.clientY + window.pageYOffset + Tegaki.bg.scrollTop - Tegaki.offsetY; - } - }, - - resume: function() { - Tegaki.bg.classList.remove('tegaki-hidden'); - document.body.classList.add('tegaki-backdrop'); - - Tegaki.centerCnt(); - Tegaki.updatePosOffset(); - - T$.on(document, 'mouseup', Tegaki.onMouseUp); - T$.on(window, 'resize', Tegaki.updatePosOffset); - T$.on(window, 'scroll', Tegaki.updatePosOffset); - }, - - hide: function() { - Tegaki.bg.classList.add('tegaki-hidden'); - document.body.classList.remove('tegaki-backdrop'); - - T$.off(document, 'mouseup', Tegaki.onMouseUp); - T$.off(window, 'resize', Tegaki.updatePosOffset); - T$.off(window, 'scroll', Tegaki.updatePosOffset); - }, - - destroy: function() { - T$.off(Tegaki.bg, 'mousemove', Tegaki.onMouseMove); - T$.off(Tegaki.bg, 'mousedown', Tegaki.onMouseDown); - T$.off(Tegaki.layersCnt, 'contextmenu', Tegaki.onDummy); - - T$.off(document, 'mouseup', Tegaki.onMouseUp); - T$.off(window, 'resize', Tegaki.updatePosOffset); - T$.off(window, 'scroll', Tegaki.updatePosOffset); - - Tegaki.bg.parentNode.removeChild(Tegaki.bg); - - TegakiHistory.clear(); - - document.body.classList.remove('tegaki-backdrop'); - - Tegaki.bg = null; - Tegaki.cnt = null; - Tegaki.canvas = null; - Tegaki.ctx = null; - Tegaki.layers = []; - Tegaki.layerIndex = 0; - Tegaki.activeCtx = null; - }, - - flatten: function() { - var i, layer, canvas, ctx; - - canvas = T$.el('canvas'); - canvas.width = Tegaki.canvas.width; - canvas.height = Tegaki.canvas.height; - - ctx = canvas.getContext('2d'); - - ctx.drawImage(Tegaki.canvas, 0, 0); - - for (i = 0; layer = Tegaki.layers[i]; ++i) { - if (layer.canvas.classList.contains('tegaki-hidden')) { - continue; - } - ctx.drawImage(layer.canvas, 0, 0); - } - - return canvas; - }, - - updateUI: function(type) { - var i, ary, el, tool = Tegaki.tool; - - ary = type ? [type] : ['size', 'alpha', 'color']; - - for (i = 0; type = ary[i]; ++i) { - el = T$.id('tegaki-' + type); - el.value = type === 'color' ? Tegaki.toolColor : tool[type]; - - if (el.type === 'range') { - el.previousElementSibling.setAttribute('data-value', tool[type]); - } - } - }, - - rebuildLayerCtrl: function() { - var i, layer, sel, opt; - - sel = T$.id('tegaki-layer'); - - sel.textContent = ''; - - for (i = Tegaki.layers.length - 1; layer = Tegaki.layers[i]; i--) { - opt = T$.el('option'); - opt.value = layer.id; - opt.textContent = layer.name; - sel.appendChild(opt); - } - }, - - getColorAt: function(ctx, posX, posY) { - var rgba = ctx.getImageData(posX, posY, 1, 1).data; - - return '#' - + ('0' + rgba[0].toString(16)).slice(-2) - + ('0' + rgba[1].toString(16)).slice(-2) - + ('0' + rgba[2].toString(16)).slice(-2); - }, - - renderCircle: function(r) { - var i, canvas, ctx, d, e, x, y, dx, dy, idata, data, c, color; - - e = 1 - r; - dx = 0; - dy = -2 * r; - x = 0; - y = r; - d = 33; - c = 16; - - canvas = T$.el('canvas'); - canvas.width = canvas.height = d; - ctx = canvas.getContext('2d'); - idata = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height); - data = idata.data; - - color = 255; - - data[(c + (c + r) * d) * 4 + 3] = color; - data[(c + (c - r) * d) * 4 + 3] = color; - data[(c + r + c * d) * 4 + 3] = color; - data[(c - r + c * d) * 4 + 3] = color; - - while (x < y) { - if (e >= 0) { - y--; - dy += 2; - e += dy; - } - - ++x; - dx += 2; - e += dx; - - data[(c + x + (c + y) * d) * 4 + 3] = color; - data[(c - x + (c + y) * d) * 4 + 3] = color; - data[(c + x + (c - y) * d) * 4 + 3] = color; - data[(c - x + (c - y) * d) * 4 + 3] = color; - data[(c + y + (c + x) * d) * 4 + 3] = color; - data[(c - y + (c + x) * d) * 4 + 3] = color; - data[(c + y + (c - x) * d) * 4 + 3] = color; - data[(c - y + (c - x) * d) * 4 + 3] = color; - } - - if (r > 0) { - for (i = 0; i < 3; ++i) { - data[(c + c * d) * 4 + i] = 127; - } - data[(c + c * d) * 4 + i] = color; - } - - ctx.putImageData(idata, 0, 0); - - return canvas; - }, - - setToolSize: function(size) { - Tegaki.tool.setSize && Tegaki.tool.setSize(size); - Tegaki.updateCursor(); - }, - - setToolAlpha: function(alpha) { - Tegaki.tool.setAlpha && Tegaki.tool.setAlpha(alpha); - }, - - setToolColor: function(color) { - Tegaki.toolColor = color; - Tegaki.tool.setColor && Tegaki.tool.setColor(color); - Tegaki.updateCursor(); - }, - - setTool: function(tool) { - tool = Tegaki.tools[tool]; - Tegaki.tool = tool; - tool.set && tool.set(); - }, - - debugDumpPixelData: function(canvas) { - var i, idata, data, len, out, el; - - idata = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height); - data = idata.data; - len = data.length; - - out = ''; - - for (i = 0; i < len; i += 4) { - out += data[i] + ' ' + data[i+1] + ' ' + data[i+2] + ' ' + data[i+3] + '%0a'; - } - - el = document.createElement('a'); - el.href = 'data:,' + out; - el.download = 'dump.txt'; - document.body.appendChild(el); - el.click(); - document.body.removeChild(el); - }, - - debugDrawColors: function(sat) { - var i, ctx, grad, a; - - Tegaki.resizeCanvas(360, 360); - - ctx = Tegaki.activeCtx; - a = ctx.globalAlpha; - ctx.globalAlpha = 1; - - ctx.fillStyle = '#000000'; - ctx.fillRect(0, 0, 360, 360); - - for (i = 0; i < 360; ++i) { - if (sat) { - grad = ctx.createLinearGradient(0, 0, 10, 360); - grad.addColorStop(0, 'hsl(' + i + ', 0%, ' + '50%)'); - grad.addColorStop(1, 'hsl(' + i + ', 100%, ' + '50%)'); - ctx.strokeStyle = grad; - } - else { - ctx.strokeStyle = 'hsl(' + i + ', 100%, ' + '50%)'; - } - ctx.beginPath(); - ctx.moveTo(i, 0); - ctx.lineTo(i, 360); - ctx.stroke(); - ctx.closePath(); - } - - if (!sat) { - grad = ctx.createLinearGradient(0, 0, 10, 360); - grad.addColorStop(0, '#000000'); - grad.addColorStop(1, 'rgba(0,0,0,0)'); - ctx.fillStyle = grad; - ctx.fillRect(0, 0, 360, 360); - } - - ctx.globalAlpha = a; - }, - - onNewClick: function() { - var width, height, tmp; - - width = prompt(TegakiStrings.promptWidth, Tegaki.canvas.width); - if (!width) { return; } - - height = prompt(TegakiStrings.promptHeight, Tegaki.canvas.height); - if (!height) { return; } - - width = +width; - height = +height; - - if (width < 1 || height < 1) { - alert(TegakiStrings.badDimensions); - return; - } - - tmp = {}; - Tegaki.copyContextState(Tegaki.activeCtx, tmp); - Tegaki.resizeCanvas(width, height); - Tegaki.copyContextState(tmp, Tegaki.activeCtx); - - TegakiHistory.clear(); - Tegaki.centerCnt(); - Tegaki.updatePosOffset(); - }, - - onUndoClick: function() { - TegakiHistory.undo(); - }, - - onRedoClick: function() { - TegakiHistory.redo(); - }, - - onDoneClick: function() { - Tegaki.hide(); - Tegaki.onDoneCb(); - }, - - onCancelClick: function() { - if (!confirm(TegakiStrings.confirmCancel)) { - return; - } - - Tegaki.destroy(); - Tegaki.onCancelCb(); - }, - - onColorChange: function() { - Tegaki.setToolColor(this.value); - }, - - onSizeChange: function() { - this.previousElementSibling.setAttribute('data-value', this.value); - Tegaki.setToolSize(+this.value); - }, - - onAlphaChange: function() { - this.previousElementSibling.setAttribute('data-value', this.value); - Tegaki.setToolAlpha(+this.value); - }, - - onLayerChange: function() { - var selectedOptions = T$.selectedOptions(this); - - if (selectedOptions.length > 1) { - Tegaki.activeLayer = null; - } - else { - Tegaki.setActiveLayer(+this.value); - } - }, - - onLayerAdd: function() { - TegakiHistory.push(Tegaki.addLayer()); - Tegaki.setActiveLayer(); - }, - - onLayerDelete: function() { - var i, ary, sel, opt, selectedOptions, action; - - sel = T$.id('tegaki-layer'); - - selectedOptions = T$.selectedOptions(sel); - - if (Tegaki.layers.length === selectedOptions.length) { - return; - } - - if (!confirm(TegakiStrings.confirmDelLayers)) { - return; - } - - if (selectedOptions.length > 1) { - ary = []; - - for (i = 0; opt = selectedOptions[i]; ++i) { - ary.push(+opt.value); - } - } - else { - ary = [+sel.value]; - } - - action = Tegaki.deleteLayers(ary); - - TegakiHistory.push(action); - }, - - onLayerVisibilityChange: function() { - var i, ary, sel, opt, flag, selectedOptions; - - sel = T$.id('tegaki-layer'); - - selectedOptions = T$.selectedOptions(sel); - - if (selectedOptions.length > 1) { - ary = []; - - for (i = 0; opt = selectedOptions[i]; ++i) { - ary.push(+opt.value); - } - } - else { - ary = [+sel.value]; - } - - flag = !Tegaki.getLayerById(ary[0]).visible; - - Tegaki.setLayerVisibility(ary, flag); - }, - - onMergeLayers: function() { - var i, ary, sel, opt, selectedOptions, action; - - sel = T$.id('tegaki-layer'); - - selectedOptions = T$.selectedOptions(sel); - - if (selectedOptions.length > 1) { - ary = []; - - for (i = 0; opt = selectedOptions[i]; ++i) { - ary.push(+opt.value); - } - } - else { - ary = [+sel.value]; - } - - if (ary.length < 2) { - alert(TegakiStrings.errorMergeOneLayer); - return; - } - - if (!confirm(TegakiStrings.confirmMergeLayers)) { - return; - } - - action = Tegaki.mergeLayers(ary); - - TegakiHistory.push(action); - }, - - onMoveLayer: function(e) { - var id, action, sel; - - sel = T$.id('tegaki-layer'); - - id = +sel.options[sel.selectedIndex].value; - - if (action = Tegaki.moveLayer(id, e.target.hasAttribute('data-up'))) { - TegakiHistory.push(action); - } - }, - - onToolChange: function() { - Tegaki.setTool(this.value); - Tegaki.updateUI(); - Tegaki.updateCursor(); - }, - - onCanvasSelected: function() { - var img; - - if (!confirm(TegakiStrings.confirmChangeCanvas)) { - this.selectedIndex = +this.getAttribute('data-current'); - return; - } - - if (this.value === '0') { - Tegaki.ctx.fillStyle = Tegaki.bgColor; - Tegaki.ctx.fillRect(0, 0, Tegaki.baseWidth, Tegaki.baseHeight); - } - else { - img = T$.el('img'); - img.onload = Tegaki.onImageLoaded; - img.onerror = Tegaki.onImageError; - this.disabled = true; - img.src = this.value; - } - }, - - onImageLoaded: function() { - var el, tmp = {}; - - el = T$.id('tegaki-canvas-select'); - el.setAttribute('data-current', el.selectedIndex); - el.disabled = false; - - Tegaki.copyContextState(Tegaki.activeCtx, tmp); - Tegaki.resizeCanvas(this.naturalWidth, this.naturalHeight); - Tegaki.activeCtx.drawImage(this, 0, 0); - Tegaki.copyContextState(tmp, Tegaki.activeCtx); - - TegakiHistory.clear(); - Tegaki.centerCnt(); - Tegaki.updatePosOffset(); - }, - - onImageError: function() { - var el; - - el = T$.id('tegaki-canvas-select'); - el.selectedIndex = +el.getAttribute('data-current'); - el.disabled = false; - - alert(TegakiStrings.errorLoadImage); - }, - - resizeCanvas: function(width, height) { - var i, layer; - - Tegaki.canvas.width = width; - Tegaki.canvas.height = height; - Tegaki.ghostCanvas.width = width; - Tegaki.ghostCanvas.height = height; - - Tegaki.ctx.fillStyle = Tegaki.bgColor; - Tegaki.ctx.fillRect(0, 0, width, height); - - for (i = 0; layer = Tegaki.layers[i]; ++i) { - Tegaki.layersCnt.removeChild(layer.canvas); - } - - Tegaki.activeCtx = null; - Tegaki.layers = []; - Tegaki.layerIndex = 0; - T$.id('tegaki-layer').textContent = ''; - - Tegaki.addLayer(); - Tegaki.setActiveLayer(); - }, - - getLayerIndex: function(id) { - var i, layer, layers = Tegaki.layers; - - for (i = 0; layer = layers[i]; ++i) { - if (layer.id === id) { - return i; - } - } - - return -1; - }, - - getLayerById: function(id) { - return Tegaki.layers[Tegaki.getLayerIndex(id)]; - }, - - addLayer: function() { - var id, cnt, opt, canvas, layer, nodes, last; - - canvas = T$.el('canvas'); - canvas.className = 'tegaki-layer'; - canvas.width = Tegaki.canvas.width; - canvas.height = Tegaki.canvas.height; - - id = ++Tegaki.layerIndex; - - layer = { - id: id, - name: 'Layer ' + id, - canvas: canvas, - ctx: canvas.getContext('2d'), - visible: true, - empty: true, - opacity: 1.0 - }; - - Tegaki.layers.push(layer); - - cnt = T$.id('tegaki-layer'); - opt = T$.el('option'); - opt.value = layer.id; - opt.textContent = layer.name; - cnt.insertBefore(opt, cnt.firstElementChild); - - nodes = T$.cls('tegaki-layer', Tegaki.layersCnt); - - if (nodes.length) { - last = nodes[nodes.length - 1]; - } - else { - last = Tegaki.canvas; - } - - Tegaki.layersCnt.insertBefore(canvas, last.nextElementSibling); - - return new TegakiHistoryActions.AddLayer(id); - }, - - deleteLayers: function(ids) { - var i, id, len, sel, idx, indices, layers; - - sel = T$.id('tegaki-layer'); - - indices = []; - layers = []; - - for (i = 0, len = ids.length; i < len; ++i) { - id = ids[i]; - idx = Tegaki.getLayerIndex(id); - sel.removeChild(sel.options[Tegaki.layers.length - 1 - idx]); - Tegaki.layersCnt.removeChild(Tegaki.layers[idx].canvas); - - indices.push(idx); - layers.push(Tegaki.layers[idx]); - - Tegaki.layers.splice(idx, 1); - } - - Tegaki.setActiveLayer(); - - return new TegakiHistoryActions.DestroyLayers(indices, layers); - }, - - mergeLayers: function(ids) { - var i, id, sel, idx, canvasBefore, destId, dest, action; - - sel = T$.id('tegaki-layer'); - - destId = ids.pop(); - idx = Tegaki.getLayerIndex(destId); - dest = Tegaki.layers[idx].ctx; - - canvasBefore = T$.copyCanvas(Tegaki.layers[idx].canvas); - - for (i = ids.length - 1; i >= 0; i--) { - id = ids[i]; - idx = Tegaki.getLayerIndex(id); - dest.drawImage(Tegaki.layers[idx].canvas, 0, 0); - } - - action = Tegaki.deleteLayers(ids); - action.layerId = destId; - action.canvasBefore = canvasBefore; - action.canvasAfter = T$.copyCanvas(dest.canvas); - - Tegaki.setActiveLayer(destId); - - return action; - }, - - moveLayer: function(id, up) { - var idx, sel, opt, canvas, tmp, tmpId; - - sel = T$.id('tegaki-layer'); - idx = Tegaki.getLayerIndex(id); - - canvas = Tegaki.layers[idx].canvas; - opt = sel.options[Tegaki.layers.length - 1 - idx]; - - if (up) { - if (!Tegaki.ghostCanvas.nextElementSibling) { return false; } - canvas.parentNode.insertBefore(canvas, - Tegaki.ghostCanvas.nextElementSibling.nextElementSibling - ); - opt.parentNode.insertBefore(opt, opt.previousElementSibling); - tmpId = idx + 1; - } - else { - if (canvas.previousElementSibling.id === 'tegaki-canvas') { return false; } - canvas.parentNode.insertBefore(canvas, canvas.previousElementSibling); - opt.parentNode.insertBefore(opt, opt.nextElementSibling.nextElementSibling); - tmpId = idx - 1; - } - - Tegaki.updateGhostLayerPos(); - - tmp = Tegaki.layers[tmpId]; - Tegaki.layers[tmpId] = Tegaki.layers[idx]; - Tegaki.layers[idx] = tmp; - - Tegaki.activeLayer = tmpId; - - return new TegakiHistoryActions.MoveLayer(id, up); - }, - - setLayerVisibility: function(ids, flag) { - var i, len, sel, idx, layer, optIdx; - - sel = T$.id('tegaki-layer'); - optIdx = Tegaki.layers.length - 1; - - for (i = 0, len = ids.length; i < len; ++i) { - idx = Tegaki.getLayerIndex(ids[i]); - layer = Tegaki.layers[idx]; - layer.visible = flag; - - if (flag) { - sel.options[optIdx - idx].classList.remove('tegaki-strike'); - layer.canvas.classList.remove('tegaki-hidden'); - } - else { - sel.options[optIdx - idx].classList.add('tegaki-strike'); - layer.canvas.classList.add('tegaki-hidden'); - } - } - }, - - setActiveLayer: function(id) { - var ctx, idx; - - idx = id ? Tegaki.getLayerIndex(id) : Tegaki.layers.length - 1; - - if (idx < 0 || idx > Tegaki.maxLayers) { - return; - } - - ctx = Tegaki.layers[idx].ctx; - - if (Tegaki.activeCtx) { - Tegaki.copyContextState(Tegaki.activeCtx, ctx); - } - - Tegaki.activeCtx = ctx; - Tegaki.activeLayer = idx; - T$.id('tegaki-layer').selectedIndex = Tegaki.layers.length - idx - 1; - - Tegaki.updateGhostLayerPos(); - }, - - updateGhostLayerPos: function() { - Tegaki.layersCnt.insertBefore( - Tegaki.ghostCanvas, - Tegaki.activeCtx.canvas.nextElementSibling - ); - }, - - copyContextState: function(src, dest) { - var i, p, props = [ - 'lineCap', 'lineJoin', 'strokeStyle', 'fillStyle', 'globalAlpha', - 'lineWidth', 'globalCompositeOperation' - ]; - - for (i = 0; p = props[i]; ++i) { - dest[p] = src[p]; - } - }, - - updateCursor: function() { - var radius; - - radius = 0 | (Tegaki.tool.size / 2); - - if (Tegaki.tool.noCursor || radius < 1) { - Tegaki.layersCnt.style.cursor = 'default'; - return; - } - - Tegaki.layersCnt.style.cursor = 'url("' - + Tegaki.renderCircle(radius).toDataURL('image/png') - + '") 16 16, default'; - }, - - updatePosOffset: function() { - var aabb = Tegaki.canvas.getBoundingClientRect(); - Tegaki.offsetX = aabb.left + window.pageXOffset + Tegaki.cnt.scrollLeft; - Tegaki.offsetY = aabb.top + window.pageYOffset + Tegaki.cnt.scrollTop; - }, - - onMouseMove: function(e) { - if (Tegaki.isPainting) { - Tegaki.tool.draw(Tegaki.getCursorPos(e, 0), Tegaki.getCursorPos(e, 1)); - } - else if (Tegaki.isColorPicking) { - TegakiPipette.draw(Tegaki.getCursorPos(e, 0), Tegaki.getCursorPos(e, 1)); - } - }, - - onMouseDown: function(e) { - if (e.target.parentNode === Tegaki.layersCnt) { - if (Tegaki.activeLayer === null) { - alert(TegakiStrings.noActiveLayer); - return; - } - if (!Tegaki.layers[Tegaki.activeLayer].visible) { - alert(TegakiStrings.hiddenActiveLayer); - return; - } - } - else if (e.target !== Tegaki.bg) { - return; - } - - if (e.which === 3 || e.altKey) { - Tegaki.isColorPicking = true; - TegakiPipette.draw(Tegaki.getCursorPos(e, 0), Tegaki.getCursorPos(e, 1)); - } - else { - Tegaki.isPainting = true; - TegakiHistory.pendingAction = new TegakiHistoryActions.Draw( - Tegaki.layers[Tegaki.activeLayer].id - ); - TegakiHistory.pendingAction.addCanvasState(Tegaki.activeCtx.canvas, 0); - Tegaki.tool.draw(Tegaki.getCursorPos(e, 0), Tegaki.getCursorPos(e, 1), true); - } - }, - - onMouseUp: function(e) { - if (Tegaki.isPainting) { - Tegaki.tool.commit && Tegaki.tool.commit(); - TegakiHistory.pendingAction.addCanvasState(Tegaki.activeCtx.canvas, 1); - TegakiHistory.push(TegakiHistory.pendingAction); - Tegaki.isPainting = false; - } - else if (Tegaki.isColorPicking) { - e.preventDefault(); - Tegaki.isColorPicking = false; - } - }, - - onDummy: function(e) { - e.preventDefault(); - e.stopPropagation(); - } -}; diff --git a/misc/website/backend/app/static/op_audio.png b/misc/website/backend/app/static/op_audio.png deleted file mode 100755 index e6f3f59..0000000 Binary files a/misc/website/backend/app/static/op_audio.png and /dev/null differ diff --git a/misc/website/backend/app/static/op_spoiler.png b/misc/website/backend/app/static/op_spoiler.png deleted file mode 100755 index 683a8b9..0000000 Binary files a/misc/website/backend/app/static/op_spoiler.png and /dev/null differ diff --git a/misc/website/backend/app/static/post_audio.png b/misc/website/backend/app/static/post_audio.png deleted file mode 100755 index 6ce0217..0000000 Binary files a/misc/website/backend/app/static/post_audio.png and /dev/null differ diff --git a/misc/website/backend/app/static/post_spoiler.png b/misc/website/backend/app/static/post_spoiler.png deleted file mode 100755 index 3779307..0000000 Binary files a/misc/website/backend/app/static/post_spoiler.png and /dev/null differ diff --git a/misc/website/backend/data/favicon.ico b/misc/website/backend/data/favicon.ico deleted file mode 100755 index dc9fcd3..0000000 Binary files a/misc/website/backend/data/favicon.ico and /dev/null differ diff --git a/misc/website/backend/data/files/.keep b/misc/website/backend/data/files/.keep deleted file mode 100755 index e69de29..0000000 diff --git a/misc/website/backend/data/secrets/.keep b/misc/website/backend/data/secrets/.keep deleted file mode 100755 index e69de29..0000000 diff --git a/misc/website/backend/data/backup/.keep b/misc/website/backend/data/static/.gitkeep old mode 100755 new mode 100644 similarity index 100% rename from misc/website/backend/data/backup/.keep rename to misc/website/backend/data/static/.gitkeep diff --git a/misc/website/backend/resources/op_spoiler.psd b/misc/website/backend/resources/op_spoiler.psd deleted file mode 100755 index cdc656e..0000000 Binary files a/misc/website/backend/resources/op_spoiler.psd and /dev/null differ diff --git a/misc/website/backend/resources/post_spoiler.psd b/misc/website/backend/resources/post_spoiler.psd deleted file mode 100755 index 88adf11..0000000 Binary files a/misc/website/backend/resources/post_spoiler.psd and /dev/null differ diff --git a/misc/website/backend/sass.sh b/misc/website/backend/sass.sh deleted file mode 100755 index cf18c07..0000000 --- a/misc/website/backend/sass.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -sass.bat app/src/sass:app/static/css -s compressed --watch diff --git a/misc/website/backend/ts.sh b/misc/website/backend/ts.sh deleted file mode 100755 index b2175b4..0000000 --- a/misc/website/backend/ts.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -tsc diff --git a/misc/website/backend/tsconfig.json b/misc/website/backend/tsconfig.json deleted file mode 100755 index 555521f..0000000 --- a/misc/website/backend/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "module": "es6", - "outDir": "app/static/js", - "alwaysStrict": true, - "noEmitOnError": true, - "pretty": true, - "removeComments": true, - "sourceMap": true, - "watch": true - } -} diff --git a/misc/website/frontend/.browserslistrc b/misc/website/frontend/.browserslistrc old mode 100644 new mode 100755 diff --git a/misc/website/frontend/.editorconfig b/misc/website/frontend/.editorconfig old mode 100644 new mode 100755 diff --git a/misc/website/frontend/.env b/misc/website/frontend/.env old mode 100644 new mode 100755 index e74863b..ade7be1 --- a/misc/website/frontend/.env +++ b/misc/website/frontend/.env @@ -1,2 +1,2 @@ NODE_ENV = 'production' -VUE_APP_BASE_API = 'http://127.0.0.1:9527/' \ No newline at end of file +1VUE_APP_BASE_API = 'http://192.168.208.128:8000/' diff --git a/misc/website/frontend/.eslintignore b/misc/website/frontend/.eslintignore old mode 100644 new mode 100755 diff --git a/misc/website/frontend/.eslintrc.js b/misc/website/frontend/.eslintrc.js old mode 100644 new mode 100755 diff --git a/misc/website/frontend/.gitignore b/misc/website/frontend/.gitignore old mode 100644 new mode 100755 diff --git a/misc/website/frontend/LICENSE b/misc/website/frontend/LICENSE old mode 100644 new mode 100755 diff --git a/misc/website/frontend/README-zh.md b/misc/website/frontend/README-zh.md old mode 100644 new mode 100755 diff --git a/misc/website/frontend/README.md b/misc/website/frontend/README.md old mode 100644 new mode 100755 diff --git a/misc/website/frontend/babel.config.js b/misc/website/frontend/babel.config.js old mode 100644 new mode 100755 diff --git a/misc/website/frontend/cypress.json b/misc/website/frontend/cypress.json old mode 100644 new mode 100755 diff --git a/misc/website/frontend/jest.config.js b/misc/website/frontend/jest.config.js old mode 100644 new mode 100755 diff --git a/misc/website/frontend/package-lock.json b/misc/website/frontend/package-lock.json old mode 100644 new mode 100755 diff --git a/misc/website/frontend/package.json b/misc/website/frontend/package.json old mode 100644 new mode 100755 diff --git a/misc/website/frontend/postcss.config.js b/misc/website/frontend/postcss.config.js old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/favicon.ico b/misc/website/frontend/public/favicon.ico old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/android-chrome-192x192.png b/misc/website/frontend/public/img/icons/android-chrome-192x192.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/android-chrome-512x512.png b/misc/website/frontend/public/img/icons/android-chrome-512x512.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/apple-touch-icon-120x120.png b/misc/website/frontend/public/img/icons/apple-touch-icon-120x120.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/apple-touch-icon-152x152.png b/misc/website/frontend/public/img/icons/apple-touch-icon-152x152.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/apple-touch-icon-180x180.png b/misc/website/frontend/public/img/icons/apple-touch-icon-180x180.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/apple-touch-icon-60x60.png b/misc/website/frontend/public/img/icons/apple-touch-icon-60x60.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/apple-touch-icon-76x76.png b/misc/website/frontend/public/img/icons/apple-touch-icon-76x76.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/apple-touch-icon.png b/misc/website/frontend/public/img/icons/apple-touch-icon.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/favicon-16x16.png b/misc/website/frontend/public/img/icons/favicon-16x16.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/favicon-32x32.png b/misc/website/frontend/public/img/icons/favicon-32x32.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/msapplication-icon-144x144.png b/misc/website/frontend/public/img/icons/msapplication-icon-144x144.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/mstile-150x150.png b/misc/website/frontend/public/img/icons/mstile-150x150.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/img/icons/safari-pinned-tab.svg b/misc/website/frontend/public/img/icons/safari-pinned-tab.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/index.html b/misc/website/frontend/public/index.html old mode 100644 new mode 100755 diff --git a/misc/website/frontend/public/manifest.json b/misc/website/frontend/public/manifest.json old mode 100644 new mode 100755 index 67ada24..0db1db9 --- a/misc/website/frontend/public/manifest.json +++ b/misc/website/frontend/public/manifest.json @@ -1,6 +1,6 @@ { - "name": "SWT Admin", - "short_name": "SWT Admin", + "name": "Vue Typescript Admin", + "short_name": "Vue Ts Admin", "icons": [ { "src": "./img/icons/android-chrome-192x192.png", diff --git a/misc/website/frontend/public/robots.txt b/misc/website/frontend/public/robots.txt old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/App.vue b/misc/website/frontend/src/App.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/api/articles.ts b/misc/website/frontend/src/api/articles.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/api/node.ts b/misc/website/frontend/src/api/node.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/api/types.d.ts b/misc/website/frontend/src/api/types.d.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/api/users.ts b/misc/website/frontend/src/api/users.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/assets/404-images/404-cloud.png b/misc/website/frontend/src/assets/404-images/404-cloud.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/assets/404-images/404.png b/misc/website/frontend/src/assets/404-images/404.png old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/components/Breadcrumb/index.vue b/misc/website/frontend/src/components/Breadcrumb/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/components/Hamburger/index.vue b/misc/website/frontend/src/components/Hamburger/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/define.d.ts b/misc/website/frontend/src/define.d.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/README.md b/misc/website/frontend/src/icons/README.md old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/dashboard.ts b/misc/website/frontend/src/icons/components/dashboard.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/example.ts b/misc/website/frontend/src/icons/components/example.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/eye-off.ts b/misc/website/frontend/src/icons/components/eye-off.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/eye-on.ts b/misc/website/frontend/src/icons/components/eye-on.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/form.ts b/misc/website/frontend/src/icons/components/form.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/hamburger.ts b/misc/website/frontend/src/icons/components/hamburger.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/index.ts b/misc/website/frontend/src/icons/components/index.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/link.ts b/misc/website/frontend/src/icons/components/link.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/nested.ts b/misc/website/frontend/src/icons/components/nested.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/password.ts b/misc/website/frontend/src/icons/components/password.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/table.ts b/misc/website/frontend/src/icons/components/table.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/tree.ts b/misc/website/frontend/src/icons/components/tree.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/components/user.ts b/misc/website/frontend/src/icons/components/user.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/dashboard.svg b/misc/website/frontend/src/icons/svg/dashboard.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/example.svg b/misc/website/frontend/src/icons/svg/example.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/eye-off.svg b/misc/website/frontend/src/icons/svg/eye-off.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/eye-on.svg b/misc/website/frontend/src/icons/svg/eye-on.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/form.svg b/misc/website/frontend/src/icons/svg/form.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/hamburger.svg b/misc/website/frontend/src/icons/svg/hamburger.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/link.svg b/misc/website/frontend/src/icons/svg/link.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/nested.svg b/misc/website/frontend/src/icons/svg/nested.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/password.svg b/misc/website/frontend/src/icons/svg/password.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/table.svg b/misc/website/frontend/src/icons/svg/table.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/tree.svg b/misc/website/frontend/src/icons/svg/tree.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/icons/svg/user.svg b/misc/website/frontend/src/icons/svg/user.svg old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/layout/components/AppMain.vue b/misc/website/frontend/src/layout/components/AppMain.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/layout/components/Navbar/index.vue b/misc/website/frontend/src/layout/components/Navbar/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/layout/components/Sidebar/SidebarItem.vue b/misc/website/frontend/src/layout/components/Sidebar/SidebarItem.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/layout/components/Sidebar/SidebarItemLink.vue b/misc/website/frontend/src/layout/components/Sidebar/SidebarItemLink.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/layout/components/Sidebar/index.vue b/misc/website/frontend/src/layout/components/Sidebar/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/layout/components/index.ts b/misc/website/frontend/src/layout/components/index.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/layout/index.vue b/misc/website/frontend/src/layout/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/layout/mixin/resize.ts b/misc/website/frontend/src/layout/mixin/resize.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/main.ts b/misc/website/frontend/src/main.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/permission.ts b/misc/website/frontend/src/permission.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/registerServiceWorker.ts b/misc/website/frontend/src/registerServiceWorker.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/router.ts b/misc/website/frontend/src/router.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/shims-vue.d.ts b/misc/website/frontend/src/shims-vue.d.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/store/index.ts b/misc/website/frontend/src/store/index.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/store/modules/app.ts b/misc/website/frontend/src/store/modules/app.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/store/modules/user.ts b/misc/website/frontend/src/store/modules/user.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/styles/_mixins.scss b/misc/website/frontend/src/styles/_mixins.scss old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/styles/_svgicon.scss b/misc/website/frontend/src/styles/_svgicon.scss old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/styles/_transition.scss b/misc/website/frontend/src/styles/_transition.scss old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/styles/_variables.scss b/misc/website/frontend/src/styles/_variables.scss old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/styles/_variables.scss.d.ts b/misc/website/frontend/src/styles/_variables.scss.d.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/styles/element-variables.scss b/misc/website/frontend/src/styles/element-variables.scss old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/styles/index.scss b/misc/website/frontend/src/styles/index.scss old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/utils/cookies.ts b/misc/website/frontend/src/utils/cookies.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/utils/request.ts b/misc/website/frontend/src/utils/request.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/utils/validate.ts b/misc/website/frontend/src/utils/validate.ts old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/404.vue b/misc/website/frontend/src/views/404.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/dashboard/index.vue b/misc/website/frontend/src/views/dashboard/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/debug/index.vue b/misc/website/frontend/src/views/debug/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/flamegraph/flamegraph.js b/misc/website/frontend/src/views/flamegraph/flamegraph.js old mode 100644 new mode 100755 index 2156450..d214ae2 --- a/misc/website/frontend/src/views/flamegraph/flamegraph.js +++ b/misc/website/frontend/src/views/flamegraph/flamegraph.js @@ -372,7 +372,7 @@ const contextify = (function () { sampleInfo = `(${samplesTxt} ${countName} ${utils.formatTime(costTime)}), ${pctTxt}%)` } if (opts.profile.get_sample) { - sampleInfo = opts.profile.get_sample(node, opts, timeMax) + sampleInfo = opts.profile.get_sample(node, opts, timeMax) } let x1 = oneDecimal(xpad + stime * widthPerTime) diff --git a/misc/website/frontend/src/views/flamegraph/flamegraph.vue b/misc/website/frontend/src/views/flamegraph/flamegraph.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/form/index.vue b/misc/website/frontend/src/views/form/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/login/index.vue b/misc/website/frontend/src/views/login/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/nested/menu1/index.vue b/misc/website/frontend/src/views/nested/menu1/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/nested/menu1/menu1-1/index.vue b/misc/website/frontend/src/views/nested/menu1/menu1-1/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/nested/menu1/menu1-2/index.vue b/misc/website/frontend/src/views/nested/menu1/menu1-2/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-1/index.vue b/misc/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-1/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-2/index.vue b/misc/website/frontend/src/views/nested/menu1/menu1-2/menu1-2-2/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/nested/menu1/menu1-3/index.vue b/misc/website/frontend/src/views/nested/menu1/menu1-3/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/nested/menu2/index.vue b/misc/website/frontend/src/views/nested/menu2/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/nodeFilter.vue b/misc/website/frontend/src/views/nodeFilter.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/profiler/index.vue b/misc/website/frontend/src/views/profiler/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/table/index.vue b/misc/website/frontend/src/views/table/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/src/views/tree/index.vue b/misc/website/frontend/src/views/tree/index.vue old mode 100644 new mode 100755 diff --git a/misc/website/frontend/tests/unit/.eslintrc.js b/misc/website/frontend/tests/unit/.eslintrc.js deleted file mode 100644 index 958d51b..0000000 --- a/misc/website/frontend/tests/unit/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - env: { - jest: true - } -} diff --git a/misc/website/frontend/tests/unit/components/Breadcrumb.spec.ts b/misc/website/frontend/tests/unit/components/Breadcrumb.spec.ts deleted file mode 100644 index 3db72be..0000000 --- a/misc/website/frontend/tests/unit/components/Breadcrumb.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { mount, createLocalVue } from '@vue/test-utils' -import VueRouter from 'vue-router' -import ElementUI from 'element-ui' -import Breadcrumb from '@/components/Breadcrumb/index.vue' - -const localVue = createLocalVue() -localVue.use(VueRouter) -localVue.use(ElementUI) - -const routes = [ - { - path: '/', - children: [{ - path: 'dashboard' - }] - }, - { - path: '/menu', - children: [{ - path: 'menu1', - meta: { title: 'menu1' }, - children: [{ - path: 'menu1-1', - meta: { title: 'menu1-1' } - }, - { - path: 'menu1-2', - redirect: 'noredirect', - meta: { title: 'menu1-2' }, - children: [{ - path: 'menu1-2-1', - meta: { title: 'menu1-2-1' } - }, - { - path: 'menu1-2-2' - }] - }] - }] - }] - -const router = new VueRouter({ - mode: 'hash', // 'history', - routes -}) - -describe('Breadcrumb.vue', () => { - const wrapper = mount(Breadcrumb, { - localVue, - router - }) - - it('dashboard', () => { - router.push('/dashboard') - const len = wrapper.findAll('.el-breadcrumb__inner').length - expect(len).toBe(1) - }) - - it('normal route', () => { - router.push('/menu/menu1') - const len = wrapper.findAll('.el-breadcrumb__inner').length - expect(len).toBe(2) - }) - - it('nested route', () => { - router.push('/menu/menu1/menu1-2/menu1-2-1') - const len = wrapper.findAll('.el-breadcrumb__inner').length - expect(len).toBe(4) - }) - - it('no meta.title', () => { - router.push('/menu/menu1/menu1-2/menu1-2-2') - const len = wrapper.findAll('.el-breadcrumb__inner').length - expect(len).toBe(3) - }) - - it('click link', () => { - router.push('/menu/menu1/menu1-2/menu1-2-2') - const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') - const second = breadcrumbArray.at(1) - const href = second.find('a').text() - expect(href).toBe('menu1') - }) - - it('noredirect', () => { - router.push('/menu/menu1/menu1-2/menu1-2-1') - const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') - const redirectBreadcrumb = breadcrumbArray.at(2) - expect(redirectBreadcrumb.contains('a')).toBe(false) - }) - - it('last breadcrumb', () => { - router.push('/menu/menu1/menu1-2/menu1-2-1') - const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner') - const redirectBreadcrumb = breadcrumbArray.at(3) - expect(redirectBreadcrumb.contains('a')).toBe(false) - }) -}) diff --git a/misc/website/frontend/tests/unit/utils/validate.spec.ts b/misc/website/frontend/tests/unit/utils/validate.spec.ts deleted file mode 100644 index 5403c95..0000000 --- a/misc/website/frontend/tests/unit/utils/validate.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { isValidUsername, isExternal } from '@/utils/validate' - -describe('Utils:validate', () => { - it('isValidUsername', () => { - expect(isValidUsername('admin')).toBe(true) - expect(isValidUsername('editor')).toBe(true) - expect(isValidUsername('xxxx')).toBe(false) - }) - - it('isExternal', () => { - expect(isExternal('https://www.armour.com/')).toBe(true) - expect(isExternal('mailto:someone@test.com')).toBe(true) - expect(isExternal('123aBC')).toBe(false) - }) -}) diff --git a/misc/website/frontend/tsconfig.json b/misc/website/frontend/tsconfig.json old mode 100644 new mode 100755 diff --git a/misc/website/frontend/vue.config.js b/misc/website/frontend/vue.config.js old mode 100644 new mode 100755 index 042157a..65c092e --- a/misc/website/frontend/vue.config.js +++ b/misc/website/frontend/vue.config.js @@ -1,16 +1,9 @@ const path = require('path') -const name = 'SWT Admin' - -// TODO: Remember to change this to fit your need -// NODE_ENV = 'production' -// VUE_APP_BASE_API = 'http://10.0.0.142:9518/' - -// NODE_ENV = 'development' -// VUE_APP_BASE_API = 'http://127.0.0.1:9527/' +const name = 'Vue Typescript Admin' module.exports = { - outputDir: "../backend/www/admin", - publicPath: process.env.NODE_ENV === 'production' ? '/admin/' : '/', + outputDir: "../admin", + publicPath: process.env.NODE_ENV === 'production' ? '/admin/' : '/', // TODO: Remember to change this to fit your need lintOnSave: process.env.NODE_ENV === 'development', pwa: { name: name diff --git a/misc/website/frontend/yarn.lock b/misc/website/frontend/yarn.lock old mode 100644 new mode 100755 index 175dc81..c54b120 --- a/misc/website/frontend/yarn.lock +++ b/misc/website/frontend/yarn.lock @@ -1762,7 +1762,7 @@ append-transform@^0.4.0: dependencies: default-require-extensions "^1.0.0" -aproba@^1.1.1: +aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== @@ -1772,6 +1772,14 @@ arch@2.1.1, arch@^2.1.1: resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -3157,6 +3165,11 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + consolidate@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" @@ -3607,7 +3620,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: dependencies: ms "2.0.0" -debug@3.2.6, debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: +debug@3.2.6, debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -3650,6 +3663,11 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -3736,6 +3754,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" @@ -3761,7 +3784,7 @@ detect-indent@^4.0.0: dependencies: repeating "^2.0.0" -detect-libc@^1.0.3: +detect-libc@^1.0.2, detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= @@ -5099,6 +5122,13 @@ fs-extra@^7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + fs-plus@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/fs-plus/-/fs-plus-3.1.1.tgz#02c085ba0a013084cff2f3e89b17c60c1d9b4ab5" @@ -5147,6 +5177,20 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" @@ -5382,6 +5426,11 @@ has-symbols@^1.0.0, has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -5650,7 +5699,7 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== -iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5679,6 +5728,13 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= +ignore-walk@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== + dependencies: + minimatch "^3.0.4" + ignore@^3.3.3, ignore@^3.3.5: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" @@ -5790,7 +5846,7 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4: +ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -7918,6 +7974,21 @@ minimist@~0.0.1: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + mississippi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" @@ -7958,7 +8029,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.1, mkdirp@0.x, mkdirp@^0.5.1, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -8056,6 +8127,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +needle@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -8150,6 +8230,22 @@ node-notifier@^5.2.1, node-notifier@^5.4.2: shellwords "^0.1.1" which "^1.3.0" +node-pre-gyp@*: + version "0.14.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" + integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4.4.2" + node-releases@^1.1.44: version "1.1.44" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.44.tgz#cd66438a6eb875e3eb012b6a12e48d9f4326ffd7" @@ -8157,7 +8253,7 @@ node-releases@^1.1.44: dependencies: semver "^6.3.0" -nopt@~4.0.1: +nopt@^4.0.1, nopt@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= @@ -8222,6 +8318,26 @@ normalize.css@^8.0.1: resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== +npm-bundled@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" + integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== + dependencies: + npm-normalize-package-bin "^1.0.1" + +npm-normalize-package-bin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +npm-packlist@^1.1.6: + version "1.4.7" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.7.tgz#9e954365a06b80b18111ea900945af4f88ed4848" + integrity sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -8236,6 +8352,16 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + nprogress@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" @@ -9494,6 +9620,16 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-is@^16.8.4: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" @@ -9560,7 +9696,7 @@ read-pkg@^5.0.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== @@ -10169,7 +10305,7 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" -set-blocking@^2.0.0: +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -10591,7 +10727,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -10858,6 +10994,19 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tar@^4.4.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + terser-webpack-plugin@^1.2.3, terser-webpack-plugin@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" @@ -11842,6 +11991,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -12088,7 +12244,7 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= -yallist@^3.0.2: +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==