├── .dockerignore
├── .eslintrc.json
├── .gitattributes
├── .github
└── workflows
│ └── docker-image.yml
├── .gitignore
├── .gitmodules
├── .perltidyrc
├── COPYING
├── Dockerfile
├── README.md
├── jquery.dataTables.min.js
├── lib
├── LANraragi.pm
├── LANraragi
│ ├── Controller
│ │ ├── Api
│ │ │ ├── Archive.pm
│ │ │ ├── Category.pm
│ │ │ ├── Database.pm
│ │ │ ├── Minion.pm
│ │ │ ├── Other.pm
│ │ │ ├── Search.pm
│ │ │ └── Shinobu.pm
│ │ ├── Backup.pm
│ │ ├── Batch.pm
│ │ ├── Category.pm
│ │ ├── Config.pm
│ │ ├── Edit.pm
│ │ ├── Index.pm
│ │ ├── Logging.pm
│ │ ├── Login.pm
│ │ ├── Plugins.pm
│ │ ├── Reader.pm
│ │ ├── Stats.pm
│ │ └── Upload.pm
│ ├── Model
│ │ ├── Archive.pm
│ │ ├── Backup.pm
│ │ ├── Category.pm
│ │ ├── Config.pm
│ │ ├── Plugins.pm
│ │ ├── Reader.pm
│ │ ├── Search.pm
│ │ ├── Stats.pm
│ │ └── Upload.pm
│ ├── Plugin
│ │ ├── Download
│ │ │ ├── Chaika.pm
│ │ │ └── EHentai.pm
│ │ ├── Login
│ │ │ ├── EHentai.pm
│ │ │ ├── Fakku.pm
│ │ │ └── nHentai.pm
│ │ ├── Metadata
│ │ │ ├── Chaika.pm
│ │ │ ├── ChineseMeta.pm
│ │ │ ├── CopyTags.pm
│ │ │ ├── DateAdded.pm
│ │ │ ├── EHentai.pm
│ │ │ ├── Ehviewer.pm
│ │ │ ├── Eze.pm
│ │ │ ├── Fakku.pm
│ │ │ ├── Hdoujin.pm
│ │ │ ├── Hitomi.pm
│ │ │ ├── Koromo.pm
│ │ │ ├── MEMS.pm
│ │ │ ├── RegexParse.pm
│ │ │ └── nHentai.pm
│ │ └── Scripts
│ │ │ ├── BlacklistMigrate.pm
│ │ │ ├── FolderToCat.pm
│ │ │ ├── SourceFinder.pm
│ │ │ └── nHentaiSourceConverter.pm
│ └── Utils
│ │ ├── Archive.pm
│ │ ├── Database.pm
│ │ ├── Generic.pm
│ │ ├── Logging.pm
│ │ ├── Minion.pm
│ │ ├── Plugins.pm
│ │ ├── Routing.pm
│ │ ├── Tags.pm
│ │ └── TempFolder.pm
└── Shinobu.pm
├── log
└── .gitignore
├── lrr.conf
├── package.json
├── public
├── .gitignore
├── css
│ ├── .gitignore
│ ├── config.css
│ └── lrr.css
├── favicon.ico
├── img
│ ├── .gitignore
│ ├── empty.png
│ ├── flubbed.gif
│ ├── logo.png
│ ├── noThumb.png
│ ├── notfound.jpg
│ ├── theme_preview
│ │ ├── hachikuji.png
│ │ ├── hverse.png
│ │ ├── nadeko.png
│ │ ├── sadpanda.png
│ │ └── yotsugi.png
│ └── wait_warmly.jpg
├── js
│ ├── .gitignore
│ ├── backup.js
│ ├── batch.js
│ ├── category.js
│ ├── common.js
│ ├── config.js
│ ├── edit.js
│ ├── index.js
│ ├── index_datatables.js
│ ├── logs.js
│ ├── plugins.js
│ ├── reader.js
│ ├── server.js
│ ├── stats.js
│ └── upload.js
├── robots.txt
└── themes
│ ├── ex.css
│ ├── g.css
│ ├── modern.css
│ ├── modern_clear.css
│ └── modern_red.css
├── script
├── backup
├── lanraragi
└── launcher.pl
├── templates
├── backup.html.tt2
├── batch.html.tt2
├── category.html.tt2
├── config.html.tt2
├── edit.html.tt2
├── exception.production.html.ep
├── footer.html.tt2
├── index.html.tt2
├── login.html.tt2
├── logs.html.tt2
├── not_found.production.html.ep
├── opds.html.tt2
├── opds_entry.html.tt2
├── plugins.html.tt2
├── reader.html.tt2
├── stats.html.tt2
├── templates_config
│ ├── config_files.html.tt2
│ ├── config_global.html.tt2
│ ├── config_security.html.tt2
│ ├── config_shinobu.html.tt2
│ ├── config_tags.html.tt2
│ └── config_theme.html.tt2
└── upload.html.tt2
├── tests
├── LANraragi
│ ├── Plugin
│ │ └── Metadata
│ │ │ ├── Chaika.t
│ │ │ ├── EHentai.t
│ │ │ ├── Eze.t
│ │ │ ├── Fakku.t
│ │ │ ├── Generic.t
│ │ │ ├── Hitomi.t
│ │ │ ├── Koromo.t
│ │ │ └── nHentai.t
│ └── Utils
│ │ ├── Generic.t
│ │ └── Tags.t
├── backup.t
├── mocks.pl
├── modules.t
├── opds.t
├── plugins.t
├── samples
│ ├── chaika
│ │ ├── 001_gid_27240.json
│ │ └── 002_sha1_response.json
│ ├── eh
│ │ ├── 001_gid-1866546.json
│ │ └── 002_search_results.html
│ ├── eze
│ │ ├── eze_full_sample.json
│ │ └── eze_lite_sample.json
│ ├── fakku
│ │ ├── 001_search_response.html
│ │ └── 002_gallery_front.html
│ ├── hitomi
│ │ └── 2261881.js
│ ├── koromo
│ │ └── koromo_sample.json
│ ├── nh
│ │ ├── 001_search_results.html
│ │ └── 002_gid_52249.html
│ └── opds
│ │ └── opds_sample.xml
└── search.t
└── tools
├── Documentation
├── .gitbook.yaml
├── .gitignore
├── .screenshots
│ ├── backup.png
│ ├── batch.png
│ ├── batchlog.png
│ ├── brew.jpg
│ ├── categories.png
│ ├── category_filtered.png
│ ├── cloud.PNG
│ ├── download.png
│ ├── downloaders.png
│ ├── dureader.jpg
│ ├── edit.PNG
│ ├── favtags.jpg
│ ├── ichaival.png
│ ├── jails.jpg
│ ├── karen-dark.jpg
│ ├── karen-distro.png
│ ├── karen-light.jpg
│ ├── karen-startmenu.png
│ ├── karen.jpg
│ ├── login.png
│ ├── mountpoints.jpg
│ ├── opds.jpg
│ ├── ratings.png
│ ├── reader.jpg
│ ├── reader_options.png
│ ├── search.png
│ ├── shell.jpg
│ ├── shiggy.png
│ ├── tachiyomi.jpg
│ ├── themes.png
│ ├── thumbchange.png
│ ├── uploading.png
│ └── webext.png
├── README.md
├── SUMMARY.md
├── advanced-usage
│ ├── backup-and-restore.md
│ ├── batch-tagging.md
│ ├── categories.md
│ ├── downloading.md
│ ├── external-readers.md
│ ├── network-interfaces.md
│ ├── proxy-setup.md
│ └── tag-rules.md
├── api-documentation
│ ├── archive-api.md
│ ├── category-api.md
│ ├── database-api.md
│ ├── getting-started.md
│ ├── minion-api.md
│ ├── miscellaneous-other-api.md
│ ├── search-api.md
│ └── shinobu-api.md
├── basic-operations
│ ├── archives.md
│ ├── first-steps.md
│ ├── metadata.md
│ ├── searching.md
│ ├── stats.md
│ └── themes.md
├── extending-lanraragi
│ ├── architecture.md
│ └── index.md
├── installing-lanraragi
│ ├── community.md
│ ├── docker.md
│ ├── jail.md
│ ├── macos.md
│ ├── methods.md
│ ├── source.md
│ └── windows.md
└── plugin-docs
│ ├── code-examples.md
│ ├── download.md
│ ├── index.md
│ ├── login.md
│ ├── metadata.md
│ └── scripts.md
├── _screenshots
├── archive_list.png
├── archive_thumb.png
├── cfg.jpg
├── cfg.png
├── cfg_plugin.png
├── reader.jpg
└── reader_overlay.jpg
├── build
├── docker
│ ├── Dockerfile
│ ├── Dockerfile-legacy
│ ├── install-everything.sh
│ ├── redis.conf
│ ├── s6
│ │ ├── cont-init.d
│ │ │ └── 01-lrr-setup
│ │ ├── fix-attrs.d
│ │ │ └── 01-lrr-dirs
│ │ └── services.d
│ │ │ ├── lanraragi
│ │ │ ├── finish
│ │ │ └── run
│ │ │ └── redis
│ │ │ ├── finish
│ │ │ └── run
│ └── wsl.conf
├── homebrew
│ ├── Lanraragi.rb
│ ├── lanraragi
│ └── redis.conf
└── windows
│ └── build.ps1
├── cpanfile
├── install.pl
├── lanraragi-systemd.service
└── repository-open-graph-template.jpg
/.dockerignore:
--------------------------------------------------------------------------------
1 | tools/_screenshots
2 | tools/Documentation
3 | tools/build/windows
4 | tools/build/homebrew
5 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true,
5 | "jquery": true
6 | },
7 | "extends": [
8 | "airbnb-base",
9 | "eslint:recommended"
10 | ],
11 | "parserOptions": {
12 | "ecmaVersion": 12,
13 | "sourceType": "module"
14 | },
15 | "globals": {
16 | "LRR": "readonly",
17 | "Common": "readonly",
18 | "Server": "readonly",
19 | "tagger": "readonly",
20 | "marked": "readonly",
21 | "Awesomplete": "readonly",
22 | "tippy": "readonly",
23 | "Index": "readonly",
24 | "IndexTable": "readonly",
25 | "Swiper": "readonly"
26 | },
27 | "ignorePatterns": ["**/vendor/*.js"],
28 | "rules": {
29 | "func-names": ["error", "never"],
30 | "indent": ["error", 4],
31 | "quotes": ["error", "double"],
32 | "no-alert": "off",
33 | "no-else-return": "off",
34 | "no-param-reassign": ["error", { "props": false }],
35 | "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
36 | "no-multi-assign": ["error", { "ignoreNonDeclaration": true }],
37 | "no-unused-expressions": [
38 | "error",
39 | {
40 | "allowShortCircuit": true,
41 | "allowTernary": true
42 | }
43 | ],
44 | "one-var": "off",
45 | "one-var-declaration-per-line": ["error", "initializations"],
46 | "prefer-destructuring": ["error", {"object": true, "array": false}],
47 | "function-paren-newline": "off",
48 | "function-call-argument-newline": "off"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 | *.{cmd,[cC][mM][dD]} text eol=crlf
3 | *.{bat,[bB][aA][tT]} text eol=crlf
4 |
--------------------------------------------------------------------------------
/.github/workflows/docker-image.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - main
5 | - test-builds
6 | - actions-testing
7 | name: Docker_Image_lanraragi
8 | jobs:
9 | buildDocker:
10 | name: Build and Push Docker Image
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@master
14 | - name: Set up QEMU
15 | uses: docker/setup-qemu-action@v2
16 | - name: Set up Docker Buildx
17 | uses: docker/setup-buildx-action@v2
18 | - uses: actions/cache@v3
19 | with:
20 | path: /tmp/buildxcache
21 | key: ${{ runner.os }}-docker-buildx-${{ github.sha }}
22 | restore-keys: |
23 | ${{ runner.os }}-docker-buildx-
24 | - name: Docker Login
25 | env:
26 | DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
27 | DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
28 | run: |
29 | echo "${DOCKER_PASSWORD}" | docker login -u ${DOCKER_USERNAME} --password-stdin
30 | - name: Build/Push Nightly Docker
31 | run: |
32 | docker buildx build \
33 | --platform linux/amd64 \
34 | --output "type=image,push=true" \
35 | --tag dezhao/lanraragi_test:0.8.7 \
36 | --cache-from "type=local,src=/tmp/buildxcache" \
37 | --cache-to "type=local,dest=/tmp/buildxcache" \
38 | --file ./tools/build/docker/Dockerfile .
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | content
3 | .minion.db*
4 | npm-debug.log
5 | dump.rdb
6 | package-lock.json
7 | .vstags
8 | .gitbook
9 | *qemu-*-static*
10 | Dockerfile.*
11 | .DS_Store
12 | config.log
13 | autobackup.json
14 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "tools/build/windows/Karen"]
2 | path = tools/build/windows/Karen
3 | url = https://github.com/Difegue/Karen.git
4 |
--------------------------------------------------------------------------------
/.perltidyrc:
--------------------------------------------------------------------------------
1 | -bar
2 | -ce
3 | -vt=2
4 | -nsfs
5 | -nolq
6 | -l=132
7 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Difegue
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # DOCKER-VERSION 0.3.4
2 | FROM alpine:3.12
3 | LABEL git="https://github.com/uparrows/LANraragi_cn"
4 |
5 | ENV S6_OVERLAY_RELEASE v2.0.0.1
6 | ENV S6_KEEP_ENV 1
7 |
8 | # warn if we can't run stage2 (fix-attrs/cont-init)
9 | ENV S6_BEHAVIOUR_IF_STAGE2_FAILS 1
10 |
11 | # wait 10s before KILLing
12 | ENV S6_KILL_GRACETIME 10000
13 |
14 | # s6
15 | ENTRYPOINT ["/init"]
16 |
17 | # Check application health
18 | HEALTHCHECK --interval=1m --timeout=10s --retries=3 \
19 | CMD wget --quiet --tries=1 --no-check-certificate --spider \
20 | http://localhost:3000 || exit 1
21 |
22 | #Default mojo server port
23 | EXPOSE 3000
24 |
25 | #Enable UTF-8 (might not do anything extra on alpine tho)
26 | ENV LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 \
27 | #rootless user id
28 | LRR_UID=0 LRR_GID=0 \
29 | #Environment variables overridable by the user on container deployment
30 | LRR_NETWORK=http://*:3000 \
31 | # extra variables
32 | EV_EXTRA_DEFS=-DEV_NO_ATFORK
33 |
34 |
35 |
36 | # we use s6-overlay-nobin to just pull in the s6-overlay arch agnostic (shell)
37 | # components, since we apk install the binaries of s6 later which are arch specific
38 | # /!\ While the s6 version here is fixed by an envvar, the apk install is not pinned and takes whatever's in alpine:latest! This certainly needs a fix.
39 | ADD https://github.com/just-containers/s6-overlay/releases/download/${S6_OVERLAY_RELEASE}/s6-overlay-nobin.tar.gz /tmp/s6-overlay-nobin.tar.gz
40 | RUN tar -C / -xzf /tmp/s6-overlay-nobin.tar.gz && rm -f /tmp/s6-overlay-nobin.tar.gz
41 |
42 |
43 | WORKDIR /root/lanraragi
44 |
45 | #Copy cpanfile and install script before copying the entire context
46 | #This allows for Docker cache to preserve cpan dependencies
47 | COPY --chown=root:root /tools/cpanfile /tools/install.pl /tools/build/docker/install-everything.sh tools/
48 | COPY --chown=root:root /package.json package.json
49 |
50 | # Run the install script as root
51 | RUN sh ./tools/install-everything.sh
52 | RUN rm -f /root/lanraragi/public/js/vendor/jquery.dataTables.min.js
53 | #Copy remaining LRR files from context
54 | # consider chowning in s6 setup scripts instead
55 | COPY --chown=root:root /lib lib
56 | COPY --chown=root:root /public public
57 | COPY --chown=root:root /script script
58 | COPY --chown=root:root /templates templates
59 | COPY --chown=root:root /tests tests
60 | COPY --chown=root:root /lrr.conf lrr.conf
61 | COPY --chown=root:root /tools/build/docker/redis.conf tools/build/docker/
62 | COPY /tools/build/docker/wsl.conf /etc/wsl.conf
63 | COPY /tools/build/docker/s6/cont-init.d/ /etc/cont-init.d/
64 | COPY /tools/build/docker/s6/services.d/ /etc/services.d/
65 | COPY --chown=root:root /jquery.dataTables.min.js /root/lanraragi/public/js/vendor/jquery.dataTables.min.js
66 | #COPY /tools/build/docker/s6/fix-attrs.d/ /etc/fix-attrs.d/
67 |
68 | # Persistent volumes
69 | VOLUME [ "/root/lanraragi/content" ]
70 | VOLUME [ "/root/lanraragi/database"]
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## LANraragi_cn
2 |
3 | This repo is a fork of [Difegue / LANraragi](https://github.com/Difegue/LANraragi) , those things i've done was to translate this repo into chinese ,and fix chrome browser js problem .also use user root instead of user koyomi to fix the access permissions of the content folder on synology nas.
4 |
5 | also i've build a docker image , if you are a user of docker ,you need to mount your comic folder to "/root/lanraragi/content" directory, and the database folder to "/root/lanraragi/database" directory.
6 |
7 | Thanks to Difegue for developing this software !
8 |
9 | 这是LANraragi的汉化版本,相较与原版汉化了界面,修复了chrome的js报错,并且使用root账户代替koyomi解决群晖nas上面的无法访问挂载文件夹/home/koyomi/lanraragi/content目录的问题,我构建了一个docker镜像,如果你是docker用户,你需要将漫画文件夹挂载到/root/lanraragi/content,数据库挂载到/root/lanraragi/database。
10 |
11 | ## 简介
12 |
13 |
14 | Lanraragi是一个开源的压缩包漫画阅读器,运行在Mojolicious和Redis基础上。
15 |
16 | [
](https://hub.docker.com/r/dezhao/lanraragi_cn/)
17 | [](https://github.com/uparrows/LANraragi_cn/actions/workflows/docker-image.yml)
18 |
19 | [⏬ 下载源码](https://github.com/uparrows/LANraragi_cn/releases/latest) |[📄 教程](http://yuanfangblog.xyz/technology/251.html) |
20 |
21 | 安卓客户端(已提交中文支持):https://f-droid.org/packages/com.utazukin.ichaival/
22 |
23 | IOS客户端(用AltStore安装): https://github.com/Doraemoe/DuReader/releases
24 | AltStore:https://altstore.io/
25 |
26 | Windows客户端(已提交中文支持): https://www.microsoft.com/zh-cn/p/lrreader/9mz6bwwvswjh
27 |
28 | ## 扫码直接查看教程
29 |
30 | [
](http://yuanfangblog.xyz/technology/251.html)
31 |
32 |
33 |
34 | ## 截图
35 |
36 | |主页, 预览图 | 主页, 列表模式 |
37 | |---|---|
38 | | [](https://user-images.githubusercontent.com/38988286/111873262-6f619f80-89ca-11eb-8891-7437f1d08cb4.png) | [](https://user-images.githubusercontent.com/38988286/111873240-5822b200-89ca-11eb-8c0c-17b3bd374a9b.png) |
39 |
40 | |阅读器 | 预览 |
41 | |---|---|
42 | | [](https://user-images.githubusercontent.com/38988286/111873285-899b7d80-89ca-11eb-8868-5431e7a117f9.jpg) | [](https://user-images.githubusercontent.com/38988286/111873298-915b2200-89ca-11eb-8d61-cc67dca038f0.jpg) |
43 |
44 |
45 | |配置 | 插件配置 |
46 | |---|---|
47 | | [](https://user-images.githubusercontent.com/38988286/111873270-78527100-89ca-11eb-9526-35f1f78b578f.png) | [](https://user-images.githubusercontent.com/38988286/111873273-7f797f00-89ca-11eb-89b4-b3c21228c949.png) |
48 |
49 |
50 | ## 功能
51 |
52 | *以存档格式存储您的漫画。 (支持zip / rar / targz / lzma / 7z / xz / cbz / cbr / pdf,epub准支持)
53 |
54 | *直接从Web浏览器读取档案:服务器使用临时文件夹从压缩文件中读取。
55 |
56 | *使用内置的OPDS目录在专用的阅读器软件中阅读档案
57 |
58 | *使用客户端API与其他程序中的LANraragi进行交互
59 |
60 | *两种不同的用户界面:紧凑的存档列表,带有悬停缩略图或缩略图视图。
61 |
62 | *从5种预装的响应库样式中进行选择,或使用CSS添加您自己的样式。
63 |
64 | *命名空间的完整标签支持:添加您自己的名称或使用插件从其他来源导入它们。
65 |
66 | *设置收藏夹标签,以便能够快速在您的收藏夹中找到包含它们的档案
67 |
68 | *自动标记:将存档添加到LANraragi后,将使用插件自动导入元数据。
69 |
70 | *将数据库备份为JSON,以将标签传递到另一个LANraragi实例。
71 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Api/Database.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Api::Database;
2 | use Mojo::Base 'Mojolicious::Controller';
3 |
4 | use Redis;
5 | use Mojo::JSON qw(decode_json);
6 |
7 | use LANraragi::Model::Backup;
8 | use LANraragi::Model::Stats;
9 | use LANraragi::Utils::Generic qw(render_api_response);
10 | use LANraragi::Utils::Database qw(invalidate_cache);
11 |
12 | sub serve_backup {
13 | my $self = shift;
14 | $self->render( json => decode_json(LANraragi::Model::Backup::build_backup_JSON) );
15 | }
16 |
17 | sub drop_database {
18 | LANraragi::Utils::Database::drop_database();
19 | render_api_response( shift, "drop_database" );
20 | }
21 |
22 | sub serve_tag_stats {
23 | my $self = shift;
24 | my $minscore = $self->req->param('minweight') || "1";
25 |
26 | $self->render( json => LANraragi::Model::Stats::build_tag_stats($minscore) );
27 | }
28 |
29 | sub clean_database {
30 | my ( $deleted, $unlinked ) = LANraragi::Utils::Database::clean_database;
31 |
32 | #Force a refresh
33 | invalidate_cache(1);
34 |
35 | shift->render(
36 | json => {
37 | operation => "clean_database",
38 | deleted => $deleted,
39 | unlinked => $unlinked,
40 | success => 1
41 | }
42 | );
43 | }
44 |
45 | #Clear new flag in all archives.
46 | sub clear_new_all {
47 |
48 | my $self = shift;
49 | my $redis = $self->LRR_CONF->get_redis();
50 |
51 | # Get all archives thru redis
52 | # 40-character long keys only => Archive IDs
53 | my @keys = $redis->keys('????????????????????????????????????????');
54 |
55 | foreach my $idall (@keys) {
56 | $redis->hset( $idall, "isnew", "false" );
57 | }
58 |
59 | # Bust search cache completely, this is a big change
60 | invalidate_cache(1);
61 | $redis->quit();
62 | render_api_response( $self, "clear_new_all" );
63 | }
64 |
65 | 1;
66 |
67 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Api/Minion.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Api::Minion;
2 | use Mojo::Base 'Mojolicious::Controller';
3 |
4 | use Mojo::JSON qw(encode_json decode_json);
5 | use Redis;
6 |
7 | use LANraragi::Model::Stats;
8 | use LANraragi::Utils::TempFolder qw(get_tempsize clean_temp_full);
9 | use LANraragi::Utils::Generic qw(render_api_response);
10 | use LANraragi::Utils::Plugins qw(get_plugin get_plugins get_plugin_parameters use_plugin);
11 |
12 | # Returns basic info for the given Minion job id.
13 | sub minion_job_status {
14 | my $self = shift;
15 | my $id = $self->stash('jobid');
16 | my $job = $self->minion->job($id);
17 |
18 | if ($job) {
19 |
20 | my %info = %{ $job->info };
21 |
22 | # Render a basic json containing only task, state and error
23 | $self->render(
24 | json => {
25 | task => $info{task},
26 | state => $info{state},
27 | error => $info{error}
28 | }
29 | );
30 |
31 | } else {
32 | render_api_response( $self, "minion_job_status", "没有使用此 ID 的工作." );
33 | }
34 | }
35 |
36 | # Returns the full info for the given Minion job id.
37 | sub minion_job_detail {
38 | my $self = shift;
39 | my $id = $self->stash('jobid');
40 | my $job = $self->minion->job($id);
41 |
42 | if ($job) {
43 | $self->render( json => $job->info );
44 | } else {
45 | render_api_response( $self, "minion_job_detail", "没有使用此 ID 的工作." );
46 | }
47 | }
48 |
49 | # Queues a job into Minion.
50 | sub queue_minion_job {
51 |
52 | my ($self) = shift;
53 | my $jobname = $self->stash('jobname');
54 | my @jobargs = decode_json( $self->req->param('args') );
55 | my $priority = $self->req->param('priority') || 0;
56 |
57 | my $jobid = $self->minion->enqueue( $jobname => @jobargs => { priority => $priority } );
58 |
59 | $self->render(
60 | json => {
61 | operation => "queue_minion_job",
62 | success => 1,
63 | job => $jobid
64 | }
65 | );
66 | }
67 |
68 | 1;
69 |
70 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Api/Shinobu.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Api::Shinobu;
2 | use Mojo::Base 'Mojolicious::Controller';
3 | use Storable;
4 |
5 | use LANraragi::Utils::Generic qw(start_shinobu render_api_response);
6 | use LANraragi::Utils::TempFolder qw(get_temp);
7 |
8 | sub shinobu_status {
9 | my $self = shift;
10 | my $shinobu = ${ retrieve( get_temp . "/shinobu.pid" ) };
11 |
12 | $self->render(
13 | json => {
14 | operation => "shinobu_status",
15 | success => 1,
16 | is_alive => $shinobu->poll(),
17 | pid => $shinobu->pid
18 | }
19 | );
20 | }
21 |
22 | sub reset_filemap {
23 | my $self = shift;
24 |
25 | # This is a shinobu endpoint even though we're deleting stuff in redis
26 | # since we'll have to restart shinobu anyway to proc filemap re-creation.
27 |
28 | my $redis = $self->LRR_CONF->get_redis;
29 | $redis->del("LRR_FILEMAP");
30 |
31 | my $shinobu = ${ retrieve( get_temp . "/shinobu.pid" ) };
32 |
33 | #commit sudoku
34 | $shinobu->kill();
35 |
36 | # Create a new Process, automatically stored in TEMP_FOLDER/shinobu.pid
37 | my $proc = start_shinobu($self);
38 |
39 | $self->render(
40 | json => {
41 | operation => "shinobu_rescan",
42 | success => $proc->poll(),
43 | new_pid => $proc->pid
44 | }
45 | );
46 | }
47 |
48 | sub stop_shinobu {
49 | my $self = shift;
50 | my $shinobu = ${ retrieve( get_temp . "/shinobu.pid" ) };
51 |
52 | #commit sudoku
53 | $shinobu->kill();
54 | render_api_response( $self, "shinobu_stop" );
55 | }
56 |
57 | sub restart_shinobu {
58 | my $self = shift;
59 | my $shinobu = ${ retrieve( get_temp . "/shinobu.pid" ) };
60 |
61 | #commit sudoku
62 | $shinobu->kill();
63 |
64 | # Create a new Process, automatically stored in TEMP_FOLDER/shinobu.pid
65 | my $proc = start_shinobu($self);
66 |
67 | $self->render(
68 | json => {
69 | operation => "shinobu_restart",
70 | success => $proc->poll(),
71 | new_pid => $proc->pid
72 | }
73 | );
74 | }
75 |
76 | 1;
77 |
78 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Backup.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Backup;
2 | use Mojo::Base 'Mojolicious::Controller';
3 |
4 | use LANraragi::Utils::Generic qw(generate_themes_header);
5 | use LANraragi::Model::Backup;
6 |
7 | # This action will render a template
8 | sub index {
9 | my $self = shift;
10 |
11 | #GET with a parameter => do backup
12 | if ( $self->req->param('dobackup') ) {
13 | my $json = LANraragi::Model::Backup::build_backup_JSON();
14 |
15 | #Write json to file in the user directory and serve that file through render_static
16 | my $file = $self->LRR_CONF->get_userdir . '/backup.json';
17 |
18 | if ( -e $file ) { unlink $file }
19 |
20 | my $OUTFILE;
21 |
22 | open $OUTFILE, '>>', $file;
23 | print {$OUTFILE} $json;
24 | close $OUTFILE;
25 |
26 | $self->render_file( filepath => $file );
27 |
28 | } else { #Get with no parameters => Regular HTML printout
29 | $self->render(
30 | template => "backup",
31 | title => $self->LRR_CONF->get_htmltitle,
32 | descstr => $self->LRR_DESC,
33 | csshead => generate_themes_header($self),
34 | version => $self->LRR_VERSION
35 | );
36 | }
37 | }
38 |
39 | sub restore {
40 | my $self = shift;
41 | my $file = $self->req->upload('file');
42 |
43 | if ( $file->headers->content_type eq "application/json" ) {
44 |
45 | my $json = $file->slurp;
46 | LANraragi::Model::Backup::restore_from_JSON($json);
47 |
48 | $self->render(
49 | json => {
50 | operation => "restore_backup",
51 | success => 1
52 | }
53 | );
54 | } else {
55 | $self->render(
56 | json => {
57 | operation => "restore_backup",
58 | success => 0,
59 | error => "Not a JSON file."
60 | }
61 | );
62 | }
63 | }
64 |
65 | 1;
66 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Category.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Category;
2 | use Mojo::Base 'Mojolicious::Controller';
3 |
4 | use utf8;
5 | use URI::Escape;
6 | use Redis;
7 | use Encode;
8 | use Mojo::Util qw(xml_escape);
9 |
10 | use LANraragi::Utils::Generic qw(generate_themes_header);
11 | use LANraragi::Utils::Database qw(redis_decode);
12 |
13 | # Go through the archives in the content directory and build the template at the end.
14 | sub index {
15 |
16 | my $self = shift;
17 | my $redis = $self->LRR_CONF->get_redis();
18 | my $force = 0;
19 |
20 | my $userlogged = $self->LRR_CONF->enable_pass == 0 || $self->session('is_logged');
21 |
22 | $redis->quit();
23 |
24 | #Then complete it with the rest from the database.
25 | #40-character long keys only => Archive IDs
26 | my @keys = $redis->keys('????????????????????????????????????????');
27 |
28 | #Parse the archive list and build
elements accordingly.
29 | my $arclist = "";
30 |
31 | #Only show IDs that still have their files present.
32 | foreach my $id (@keys) {
33 | my $zipfile = $redis->hget( $id, "file" );
34 | my $title = $redis->hget( $id, "title" );
35 | $title = redis_decode($title);
36 | $title = xml_escape($title);
37 |
38 | if ( -e $zipfile ) {
39 | $arclist .=
40 | "";
41 | $arclist .= "";
42 | }
43 | }
44 |
45 | $redis->quit();
46 |
47 | $self->render(
48 | template => "category",
49 | arclist => $arclist,
50 | title => $self->LRR_CONF->get_htmltitle,
51 | descstr => $self->LRR_DESC,
52 | csshead => generate_themes_header($self),
53 | version => $self->LRR_VERSION
54 | );
55 | }
56 |
57 | 1;
58 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Edit.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Edit;
2 | use Mojo::Base 'Mojolicious::Controller';
3 |
4 | use File::Basename;
5 | use Redis;
6 | use Encode;
7 | use Template;
8 | use Mojo::Util qw(xml_escape);
9 |
10 | use LANraragi::Utils::Generic qw(generate_themes_header);
11 | use LANraragi::Utils::Database qw(redis_decode);
12 | use LANraragi::Utils::Plugins qw(get_plugins);
13 |
14 | sub index {
15 | my $self = shift;
16 |
17 | #Does the passed file exist in the database?
18 | my $id = $self->req->param('id');
19 |
20 | my $redis = $self->LRR_CONF->get_redis();
21 |
22 | if ( $redis->exists($id) ) {
23 | my %hash = $redis->hgetall($id);
24 |
25 | my ( $name, $title, $tags, $file, $thumbhash ) = @hash{qw(name title tags file thumbhash)};
26 |
27 | ( $_ = redis_decode($_) ) for ( $name, $title, $tags );
28 |
29 | #Build plugin listing
30 | my @pluginlist = get_plugins("metadata");
31 |
32 | $redis->quit();
33 |
34 | $self->render(
35 | template => "edit",
36 | id => $id,
37 | name => $name,
38 | arctitle => xml_escape($title),
39 | tags => xml_escape($tags),
40 | file => decode_utf8($file),
41 | thumbhash => $thumbhash,
42 | plugins => \@pluginlist,
43 | title => $self->LRR_CONF->get_htmltitle,
44 | descstr => $self->LRR_DESC,
45 | csshead => generate_themes_header($self),
46 | version => $self->LRR_VERSION
47 | );
48 | } else {
49 | $self->redirect_to('index');
50 | }
51 | }
52 |
53 | 1;
54 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Index.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Index;
2 | use Mojo::Base 'Mojolicious::Controller';
3 |
4 | use utf8;
5 | use URI::Escape;
6 | use Redis;
7 | use Encode;
8 | use File::Basename;
9 | use Authen::Passphrase;
10 |
11 | use LANraragi::Utils::Generic qw(generate_themes_header);
12 |
13 | # This endpoint is technically superseded by /api/search/random, but it's still useful in the Reader.
14 | sub random_archive {
15 | my $self = shift;
16 | my $archive = "";
17 | my $archiveexists = 0;
18 |
19 | my $redis = $self->LRR_CONF->get_redis();
20 |
21 | # We get a random archive ID.
22 | # We check for the length to (sort-of) avoid not getting an archive ID.
23 | # TODO: This will loop infinitely if there are zero archives in store.
24 | until ($archiveexists) {
25 | $archive = $redis->randomkey();
26 |
27 | $self->LRR_LOGGER->debug("Found key $archive");
28 |
29 | #We got a key, but does the matching archive still exist on the server?
30 | if ( length($archive) == 40
31 | && $redis->type($archive) eq "hash"
32 | && $redis->hexists( $archive, "file" ) ) {
33 | my $arclocation = $redis->hget( $archive, "file" );
34 | if ( -e $arclocation ) { $archiveexists = 1; }
35 | }
36 | }
37 |
38 | $redis->quit();
39 |
40 | #We redirect to the reader, with the key as parameter.
41 | $self->redirect_to( '/reader?id=' . $archive );
42 | }
43 |
44 | # Render the index template with a few prefilled arguments.
45 | # Most of the work is done in JS these days.
46 | sub index {
47 |
48 | my $self = shift;
49 |
50 | #Checking if the user still has the default password enabled
51 | my $ppr = Authen::Passphrase->from_rfc2307( $self->LRR_CONF->get_password );
52 | my $passcheck = ( $ppr->match("kamimamita") && $self->LRR_CONF->enable_pass );
53 |
54 | my $userlogged = $self->LRR_CONF->enable_pass == 0 || $self->session('is_logged');
55 |
56 | # Get static category list to populate the right-click menu
57 | my @categories = LANraragi::Model::Category->get_static_category_list;
58 |
59 | $self->render(
60 | template => "index",
61 | version => $self->LRR_VERSION,
62 | title => $self->LRR_CONF->get_htmltitle,
63 | descstr => $self->LRR_DESC,
64 | userlogged => $userlogged,
65 | categories => \@categories,
66 | motd => $self->LRR_CONF->get_motd,
67 | csshead => generate_themes_header($self),
68 | usingdefpass => $passcheck
69 | );
70 | }
71 |
72 | 1;
73 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Logging.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Logging;
2 | use Mojo::Base 'Mojolicious::Controller';
3 |
4 | use Redis;
5 | use Encode;
6 |
7 | use LANraragi::Utils::Generic qw(generate_themes_header);
8 | use LANraragi::Utils::Logging qw(get_logdir get_lines_from_file);
9 |
10 | # This action will render a template
11 | sub index {
12 |
13 | my $self = shift;
14 |
15 | $self->render(
16 | template => "logs",
17 | title => $self->LRR_CONF->get_htmltitle,
18 | descstr => $self->LRR_DESC,
19 | csshead => generate_themes_header($self),
20 | version => $self->LRR_VERSION
21 | );
22 | }
23 |
24 | sub print_lines_from_file {
25 |
26 | my ( $mojo, $file ) = @_;
27 |
28 | # Number of lines to read
29 | my $lines = 100;
30 | my $logdir = get_logdir();
31 |
32 | if ( $mojo->req->param('lines') ) {
33 | $lines = $mojo->req->param('lines');
34 | }
35 |
36 | $mojo->render( text => get_lines_from_file( $lines, $logdir . "/$file.log" ) );
37 |
38 | }
39 |
40 | sub print_general {
41 | print_lines_from_file( shift, "lanraragi" );
42 | }
43 |
44 | sub print_shinobu {
45 | print_lines_from_file( shift, "shinobu" );
46 | }
47 |
48 | sub print_plugins {
49 | print_lines_from_file( shift, "plugins" );
50 | }
51 |
52 | sub print_redis {
53 | print_lines_from_file( shift, "redis" );
54 | }
55 |
56 | sub print_mojo {
57 |
58 | my $self = shift;
59 |
60 | #Depending on the mode, look for development or production.log
61 | my $mode = $self->app->mode;
62 | print_lines_from_file( $self, $mode );
63 | }
64 |
65 | 1;
66 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Login.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Login;
2 | use Mojo::Base 'Mojolicious::Controller';
3 | use MIME::Base64;
4 |
5 | use Redis;
6 | use Authen::Passphrase;
7 |
8 | use LANraragi::Utils::Generic qw(generate_themes_header);
9 |
10 | sub check {
11 | my $self = shift;
12 |
13 | my $pw = $self->req->param('password') || '';
14 |
15 | #match password we got with the authen hash stored in redis
16 | my $ppr = Authen::Passphrase->from_rfc2307( $self->LRR_CONF->get_password );
17 |
18 | if ( $ppr->match($pw) ) {
19 |
20 | $self->LRR_LOGGER->info( "Successful login attempt from " . $self->tx->remote_address );
21 |
22 | $self->session( is_logged => 1 );
23 | $self->session( expiration => 60 * 60 * 24 );
24 | $self->redirect_to('index');
25 | } else {
26 |
27 | $self->LRR_LOGGER->warn( "Failed login attempt with password '$pw' from " . $self->tx->remote_address );
28 |
29 | $self->render(
30 | template => "login",
31 | title => $self->LRR_CONF->get_htmltitle,
32 | descstr => $self->LRR_DESC,
33 | csshead => generate_themes_header($self),
34 | version => $self->LRR_VERSION,
35 | wrongpass => 1
36 | );
37 | }
38 | }
39 |
40 | #The request can be authentified in two ways:
41 | #Logged in normally with a session cookie
42 | #Password protection disabled
43 | sub logged_in {
44 | my $self = shift;
45 | return 1
46 | if $self->session('is_logged')
47 | || $self->LRR_CONF->enable_pass == 0;
48 | $self->redirect_to('login');
49 | return 0;
50 | }
51 |
52 | # For APIs, the request can also be authentified with a valid API Key.
53 | sub logged_in_api {
54 | my $self = shift;
55 |
56 | # The API key is in the Authentication header.
57 | my $expected_key = $self->LRR_CONF->get_apikey;
58 |
59 | my $auth_header = $self->req->headers->authorization || "";
60 | my $expected_header = "Bearer " . encode_base64( $expected_key, "" );
61 |
62 | return 1
63 | if ( $expected_key ne "" && $auth_header eq $expected_header )
64 | || $self->session('is_logged')
65 | || $self->LRR_CONF->enable_pass == 0;
66 | $self->render(
67 | json => { error => "This API is protected and requires login or an API Key." },
68 | status => 401
69 | );
70 | return 0;
71 | }
72 |
73 | sub setup_cors {
74 | my $self = shift;
75 |
76 | # Set Allow-Origin to wildcard
77 | $self->res->headers->header( 'Access-Control-Allow-Origin' => '*' );
78 |
79 | # Explicitly say requests with an Authorization header (private API requests) are allowed
80 | $self->res->headers->header( 'Access-Control-Allow-Headers' => 'Authorization' );
81 |
82 | return 1;
83 | }
84 |
85 | sub logout {
86 | my $self = shift;
87 | $self->session( expires => 1 );
88 | $self->redirect_to('index');
89 | }
90 |
91 | sub index {
92 | my $self = shift;
93 | $self->redirect_to('index') if $self->session('is_logged');
94 |
95 | $self->render(
96 | template => "login",
97 | title => $self->LRR_CONF->get_htmltitle,
98 | descstr => $self->LRR_DESC,
99 | csshead => generate_themes_header($self),
100 | version => $self->LRR_VERSION
101 | );
102 | }
103 |
104 | 1;
105 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Reader.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Reader;
2 | use Mojo::Base 'Mojolicious::Controller';
3 | use Mojo::URL;
4 |
5 | use Encode;
6 |
7 | use LANraragi::Utils::Generic qw(generate_themes_header);
8 |
9 | use LANraragi::Model::Reader;
10 |
11 | # This action will render a template
12 | sub index {
13 | my $self = shift;
14 |
15 | if ( $self->req->param('id') ) {
16 |
17 | # Allow adding to static categories
18 | my @categories = LANraragi::Model::Category->get_static_category_list;
19 |
20 | # Get query string from referrer URL, if there's one
21 | my $referrer = $self->req->headers->referrer;
22 | my $query = "";
23 |
24 | if ($referrer) {
25 | $query = Mojo::URL->new($referrer)->query->to_string;
26 | }
27 |
28 | $self->render(
29 | template => "reader",
30 | title => $self->LRR_CONF->get_htmltitle,
31 | use_local => $self->LRR_CONF->enable_localprogress,
32 | id => $self->req->param('id'),
33 | categories => \@categories,
34 | csshead => generate_themes_header($self),
35 | version => $self->LRR_VERSION,
36 | ref_query => $query,
37 | userlogged => $self->LRR_CONF->enable_pass == 0 || $self->session('is_logged')
38 | );
39 | } else {
40 |
41 | # No parameters back the fuck off
42 | $self->redirect_to('index');
43 | }
44 | }
45 |
46 | 1;
47 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Stats.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Stats;
2 | use Mojo::Base 'Mojolicious::Controller';
3 |
4 | use LANraragi::Model::Stats;
5 | use LANraragi::Utils::Generic qw(generate_themes_header);
6 |
7 | # This action will render a template
8 | sub index {
9 | my $self = shift;
10 |
11 | $self->render(
12 | template => "stats",
13 | title => $self->LRR_CONF->get_htmltitle,
14 | descstr => $self->LRR_DESC,
15 | csshead => generate_themes_header($self),
16 | archivecount => LANraragi::Model::Stats::get_archive_count,
17 | arcsize => LANraragi::Model::Stats::compute_content_size,
18 | pagestat => LANraragi::Model::Stats::get_page_stat,
19 | version => $self->LRR_VERSION
20 | );
21 | }
22 |
23 | 1;
24 |
--------------------------------------------------------------------------------
/lib/LANraragi/Controller/Upload.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Controller::Upload;
2 | use Mojo::Base 'Mojolicious::Controller';
3 |
4 | use Redis;
5 | use File::Temp qw(tempdir);
6 | use File::Copy;
7 | use File::Find;
8 | use File::Basename;
9 |
10 | use LANraragi::Utils::Generic qw(generate_themes_header is_archive get_bytelength);
11 |
12 | sub process_upload {
13 | my $self = shift;
14 |
15 | #Receive uploaded file.
16 | my $file = $self->req->upload('file');
17 | my $catid = $self->req->param('catid');
18 | my $filename = $file->filename;
19 |
20 | my $uploadMime = $file->headers->content_type;
21 |
22 | #Check if the uploaded file's extension matches one we accept
23 | if ( is_archive($filename) ) {
24 |
25 | # Move file to a temp folder (not the default LRR one)
26 | my $tempdir = tempdir();
27 |
28 | my ( $fn, $path, $ext ) = fileparse( $filename, qr/\.[^.]*/ );
29 | my $byte_limit = LANraragi::Model::Config->enable_cryptofs ? 143 : 255;
30 |
31 | # don't allow the main filename to exceed 143/255 bytes after accounting
32 | # for extension and .upload prefix used by `handle_incoming_file`
33 | $filename = $fn;
34 | while ( get_bytelength( $filename . $ext . ".upload" ) > $byte_limit ) {
35 | $filename = substr( $filename, 0, -1 );
36 | }
37 | $filename = $filename . $ext;
38 |
39 | my $tempfile = $tempdir . '/' . $filename;
40 | $file->move_to($tempfile) or die "无法移动上传的文件.";
41 |
42 | # Update $tempfile to the exact reference created by the host filesystem
43 | # This is done by finding the first (and only) file in $tempdir.
44 | find(
45 | sub {
46 | return if -d $_;
47 | $tempfile = $File::Find::name;
48 | $filename = $_;
49 | },
50 | $tempdir
51 | );
52 |
53 | # Send a job to Minion to handle the uploaded file.
54 | my $jobid = $self->minion->enqueue( handle_upload => [ $tempfile, $catid ] => { priority => 2 } );
55 |
56 | # Reply with a reference to the job so the client can check on its progress.
57 | $self->render(
58 | json => {
59 | operation => "upload",
60 | name => $file->filename,
61 | debug_name => $filename,
62 | type => $uploadMime,
63 | success => 1,
64 | job => $jobid
65 | }
66 | );
67 |
68 | } else {
69 |
70 | $self->render(
71 | json => {
72 | operation => "upload",
73 | name => $file->filename,
74 | type => $uploadMime,
75 | success => 0,
76 | error => "不支持的文件类型. (" . $uploadMime . ")"
77 | }
78 | );
79 | }
80 | }
81 |
82 | sub index {
83 |
84 | my $self = shift;
85 |
86 | # Allow adding to category on direct uploads
87 | my @categories = LANraragi::Model::Category->get_static_category_list;
88 |
89 | $self->render(
90 | template => "upload",
91 | title => $self->LRR_CONF->get_htmltitle,
92 | descstr => $self->LRR_DESC,
93 | categories => \@categories,
94 | csshead => generate_themes_header($self),
95 | version => $self->LRR_VERSION
96 | );
97 | }
98 |
99 | 1;
100 |
--------------------------------------------------------------------------------
/lib/LANraragi/Plugin/Download/Chaika.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Plugin::Download::Chaika;
2 |
3 | use strict;
4 | use warnings;
5 | no warnings 'uninitialized';
6 | use utf8;
7 | # Meta-information about your plugin.
8 | sub plugin_info {
9 |
10 | return (
11 | # Standard metadata
12 | name => "Chaika.moe Downloader",
13 | type => "download",
14 | namespace => "chaikadl",
15 | author => "Difegue",
16 | version => "1.0",
17 | description => "下载给定的 chaika.moe URL 并将其添加到 LANraragi。 暂时不支持图库链接!",
18 |
19 | # Downloader-specific metadata
20 | # https://panda.chaika.moe/archive/_____/
21 | url_regex => "https?:\/\/panda.chaika.moe\/archive\/.*"
22 | );
23 |
24 | }
25 |
26 | # Mandatory function to be implemented by your downloader
27 | sub provide_url {
28 | shift;
29 | my $lrr_info = shift;
30 |
31 | # Get the URL to download
32 | my $url = $lrr_info->{url};
33 |
34 | # Wow!
35 | return ( download_url => $url . "/download" );
36 | }
37 |
38 | 1;
39 |
--------------------------------------------------------------------------------
/lib/LANraragi/Plugin/Login/Fakku.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Plugin::Login::Fakku;
2 |
3 | use strict;
4 | use warnings;
5 | no warnings 'uninitialized';
6 | use utf8;
7 | use Mojo::UserAgent;
8 | use LANraragi::Utils::Logging qw(get_logger);
9 |
10 | sub plugin_info {
11 |
12 | return (
13 | name => "Fakku",
14 | type => "login",
15 | namespace => "fakkulogin",
16 | author => "Nodja",
17 | version => "0.1",
18 | description =>
19 | "处理fakku登录。cookie的有效期只有 7 天,所以不要忘记更新它.",
20 | parameters => [
21 | { type => "string", desc => "fakku_sid cookie value" }
22 | ]
23 | );
24 |
25 | }
26 |
27 | sub do_login {
28 |
29 | shift;
30 | my ( $fakku_sid ) = @_;
31 |
32 | my $logger = get_logger( "Fakku Login", "plugins" );
33 | my $ua = Mojo::UserAgent->new;
34 |
35 | if ( $fakku_sid ne "" ) {
36 | $logger->info("Cookie provided ($fakku_sid)!");
37 | $ua->cookie_jar->add(
38 | Mojo::Cookie::Response->new(
39 | name => 'fakku_sid',
40 | value => $fakku_sid,
41 | domain => 'fakku.net',
42 | path => '/'
43 | )
44 | );
45 | } else {
46 | $logger->info("No cookies provided, returning blank UserAgent.");
47 | }
48 |
49 | return $ua;
50 | }
51 | 1;
--------------------------------------------------------------------------------
/lib/LANraragi/Plugin/Login/nHentai.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Plugin::Login::nHentai;
2 |
3 | use strict;
4 | use warnings;
5 | no warnings 'uninitialized';
6 | use utf8;
7 | use Mojo::UserAgent;
8 | use LANraragi::Utils::Logging qw(get_logger);
9 |
10 | #Meta-information about your plugin.
11 | sub plugin_info {
12 |
13 | return (
14 | #Standard metadata
15 | name => "nHentai CF Bypass",
16 | type => "login",
17 | namespace => "nhentaicfbypass",
18 | author => "Pheromir",
19 | version => "0.1",
20 | description =>
21 | "通过重新使用浏览器中的 cookie 绕过Cloudflare的js。 CF cookie 和用户代理必须来自同一个网络浏览器.",
22 | parameters => [
23 | { type => "string", desc => "浏览器 UserAgent 字符串(可以在 http://useragentstring.com/ 找到您的浏览器)" },
24 | { type => "string", desc => "域名nhentai.net的csrftoken cookie" },
25 | { type => "string", desc => "域名nhentai.net的cf_clearance cookie" }
26 | ]
27 | );
28 |
29 | }
30 |
31 |
32 | # Mandatory function to be implemented by your login plugin
33 | # Returns a Mojo::UserAgent object only!
34 | sub do_login {
35 |
36 | # Login plugins only receive the parameters entered by the user.
37 | shift;
38 | my ( $useragent, $csrftoken, $cf_clearance ) = @_;
39 | return get_user_agent( $useragent, $csrftoken, $cf_clearance );
40 | }
41 |
42 | # get_user_agent(useragent, cf cookies)
43 | # Try crafting a Mojo::UserAgent object that can access nHentai.
44 | # Returns the UA object created.
45 | sub get_user_agent {
46 |
47 | my ( $useragent, $csrftoken, $cf_clearance ) = @_;
48 |
49 | my $logger = get_logger( "nHentai Cloudflare Bypass", "plugins" );
50 | my $ua = Mojo::UserAgent->new;
51 |
52 | if ( $useragent ne "" && $csrftoken ne "" && $cf_clearance ne "") {
53 | $logger->info("Useragent and Cookies provided ($useragent $csrftoken $cf_clearance)!");
54 | $ua->transactor->name($useragent);
55 |
56 | #Setup the needed cookies
57 | $ua->cookie_jar->add(
58 | Mojo::Cookie::Response->new(
59 | name => 'csrftoken',
60 | value => $csrftoken,
61 | domain => 'nhentai.net',
62 | path => '/'
63 | )
64 | );
65 |
66 | $ua->cookie_jar->add(
67 | Mojo::Cookie::Response->new(
68 | name => 'cf_clearance',
69 | value => $cf_clearance,
70 | domain => 'nhentai.net',
71 | path => '/'
72 | )
73 | );
74 |
75 | } else {
76 | $logger->info("No cookies provided, returning blank UserAgent.");
77 | }
78 |
79 | return $ua;
80 |
81 | }
82 |
83 | 1;
--------------------------------------------------------------------------------
/lib/LANraragi/Plugin/Metadata/CopyTags.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Plugin::Metadata::CopyTags;
2 |
3 | use strict;
4 | use warnings;
5 | use utf8;
6 | use LANraragi::Model::Plugins;
7 | use LANraragi::Utils::Logging qw(get_logger);
8 |
9 | #Meta-information about your plugin.
10 | sub plugin_info {
11 |
12 | return (
13 | #Standard metadata
14 | name => "Tag Copier",
15 | type => "metadata",
16 | namespace => "copytags",
17 | author => "Difegue",
18 | version => "2.1",
19 | description => "应用自定义标签修改.",
20 | icon =>
21 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA\nB3RJTUUH4wYCFQ05iQtpeQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH\nAAAD8ElEQVQ4y4WUW2yURRTHfzPfty2UWrZbqdrdhktowQAJiQ/iDUwkaFJ5IAQfGjWBByMhwYAm\nBH3xqT5ArARS9cUgJC1qjCDxCY1JEy6lLVJrW9lKq6Wlgcqy2W7b/S4zx4fdbstFneRMZjLJb/7n\nf86MOn78+MlEIvE6gIhgRRARECGTmaS0tJT6+vrvo7HKxserq6f4v9Hc3PyxiBh5yBgYGJBUKiW/\n9vZKV/eVb7s6OyN79uxhbGwMKVx8f2gRUUEQEAQhfhDg+wGeHxAaA8DZs2e50tVNZ+flJ3v7B8ra\n29uJx+M0NTXhed6DCg8dPtzs+77xfV883xfP8yVXCM/zxA9CCcNQzv3402+bX96yuK2tjd27dxOP\nx2loaCCdTj+gkIJlICAFLz0vx/RMjmx2kunpaXK5nF5au3RJb29fzdq162ouX+p4ZHh4mO3bt98j\n0DU2n5oUZqUUvudz8cIFEvEEoQkRIBarXLFr584frLVmdHRUj4yNXu3r69tbVVV1u7W1lcbGxjxQ\njMzjKxAw1rJoURlr1q7B9wMEQaFKBerDwMfzPKKLo6t+uXp12Z07d16pq6tL79ixg0gkgjamkCv5\nvItKUfkrFCilmN0orZmayrJgQSlDQ0NPHz3WskhrTXd3NwBaxBR8m007v7aza+a8RQTXcaipTTAx\nMYHn+2SnJ4nFYgwODuaBRmxB3FylEEGsLRKliBZcHSFVkSFVOUWiOk5uxiObzZJKpfJAa8wciPvA\nzIflzxSKW+EtDgx8wKUbXVSYcuM4TtEWba1VSkGkJILruvmIuFibh2gNjtI4SuNqB6XBVQ7jFTdo\nK/+GiWemNmmt59omFouNfXf6dLsIalZZGJrI6lV1zwIcOP8hQ871YsE0mpRJUVFWTnZ5WjpSF09t\n+WhbJdf4DMDdv2/foYMHDx46c+YMW7dupba2lutDw1UrV674G+B3/xr9VT1gVLFMWjk4SoOg/ope\nZyo9/elzq1/QQIs7v8u11oyMjFBTU6OszKas0I4D6t4nG1qDAqwWJnWG0IbrAdz737ZSCmsNUvAw\nmAnxJsxcHyEoV+FUgDiWhXfL2RS89FXdtSfeYss8oDEGz/Ow1mKMQReq9vXmE4SSVyPAAl3KiT9b\n2X/7XZZMPcbz9sWTl94/9+bn63vUXvaKCxCGIclkkmQyCcCxlhZJ/jHI3XR60hqjpdinloiUSJ/0\nlZQuLC/Z0L9B4n2V73zR08ORXUcEyFd2fHycTCZT7KWLHR3q5s3xyslMxlhrlYjklVvLuv6V/tE3\nTr29vGzZxlffe+q1jV82hObRwFZXVxONRvnXn/e/ounCJzU/z5yPPOzsH4cGnEj6mhLzAAAAAElF\nTkSuQmCC",
22 | parameters => [ { type => "string", desc => "要复制的标签,以逗号分隔." } ]
23 | );
24 |
25 | }
26 |
27 | #Mandatory function to be implemented by your plugin
28 | sub get_tags {
29 |
30 | shift;
31 | my $lrr_info = shift; # Global info hash
32 | my ($tagstocopy) = @_; # Plugin parameters
33 |
34 | my $logger = get_logger( "Tag Copy", "plugins" );
35 |
36 | #Tags to copy is the first global argument
37 | $logger->debug( "Sending the following tags to LRR: " . $tagstocopy );
38 | return ( tags => $tagstocopy );
39 |
40 | }
41 |
42 | 1;
43 |
--------------------------------------------------------------------------------
/lib/LANraragi/Plugin/Metadata/DateAdded.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Plugin::Metadata::DateAdded;
2 |
3 | use strict;
4 | use warnings;
5 | use utf8;
6 | #Plugins can freely use all Perl packages already installed on the system
7 | #Try however to restrain yourself to the ones already installed for LRR (see tools/cpanfile) to avoid extra installations by the end-user.
8 | use Mojo::UserAgent;
9 |
10 | use LANraragi::Model::Plugins;
11 | use LANraragi::Utils::Logging qw(get_logger);
12 |
13 | #Meta-information about your plugin.
14 | sub plugin_info {
15 |
16 | return (
17 | #Standard metadata
18 | name => "Add Timestamp Tag",
19 | type => "metadata",
20 | namespace => "DateAddedPlugin",
21 | author => "Utazukin",
22 | version => "1.0",
23 | description =>
24 | "将时间戳标记添加到您的存档。
此插件遵循时间戳标签的服务器设置,如果服务器设置设置为“使用上次修改时间”,则将使用文件修改时间.",
25 | icon =>
26 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA\nB3RJTUUH4wYCFQY4HfiAJAAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH\nAAADKUlEQVQ4y6WVsUtrVxzHP+fmkkiqJr2CQWKkvCTwJgkJDpmyVAR1cVOhdq04tHNB7BD8A97S\nXYkO3dRRsMSlFoIOLYFEohiDiiTNNeaGpLn5dRDv06ev75V+4SyH8/2c3/n+zuEoEeFTqtfrb5RS\nJZ/P98m1iMirI5fLMT8/L+FwWEKhkIRCIXn79q2srKxIpVL5qE/7cINms8ny8rIkEgkpl8skk0lm\nZ2eZmZkhHo+TzWYJBoOyvr4u7Xb7RYHq6ZEvLi6Ynp6WVqvFwsIC4+PjRCIRDMNAKcXNzQ2lUols\nNsvOzg6xWIxMJqOeRuEAq9UqqVRKhoaGmJubY2pqCl3XiUajXF5e0t/fz+DgIIVCAbfbzdbWFtvb\n24yMjLC/v6+eZWjbNqurq5JIJGRtbU0syxLbtsU0TXmqXq8njUZDRERubm4knU6LYRiSyWScDBER\nGo0G4XBYFhcX5fz8XP4yTbGLf0hnd0s+plqtJru7u7K0tCSRSEQ6nc77ppycnFCv10kmk4yOjoII\n2kiIv3//lfbGu1dvh1KKVCrF2NgYmqaRy+UAHoCHh4f4fD4mJiZwuVz4fT74YhDvTz/TPv2TX378\ngWKx+Azo9/sZGBhAKYVhGBSLxa8doGmaABiGQT6fp9VqPbg0jcr897w7+I3FxUVs23aAlmVxe3tL\nPB7n/v6eWq22D6A/lq+UotlsEo1G8Xg8jvFNOMzCN99iGF/icrmc+b6+PrxeL6enp7hcLpR6aLT+\nuEDTNEqlErFYDMuy8Hq9AHg8HpaXv3uRYbfbRdM0TNNE096/Dweo6zoHBwfE43F0XXeAjyf4UJVK\nhUql8iwGJ8NHeb1e9vb2CAaDADQajRcgy7IACAQCHB0d/TtQ0zQuLi7Y3Nzk+vqacrkMwNXVFXd3\nd7Tbbc7Ozuh0OmxsbHB1dfViQ/21+3V8fIxpmkxOTmKaJrZt0263sW0b27ZJp9M0m010XX8RhwN8\nNPV6PQCKxSL5fB7DMAgEAnS7XarVKtVqFbfbjVIK27ZRSjkeB9jtdikUChQKBf6vlIg4Gb3Wzc/V\n8PDwV36//1x9zhfwX/QPryPQMvGWTdEAAAAASUVORK5CYII=",
27 | parameters => [],
28 | oneshot_arg =>
29 | "Use file modified time (yes/true), or use current time (no/false).
Leaving blank uses the server setting (default: current time)"
30 | );
31 |
32 | }
33 |
34 | #Mandatory function to be implemented by your plugin
35 | sub get_tags {
36 |
37 | shift;
38 | my $lrr_info = shift; # Global info hash
39 | my ($use_filetime) = LANraragi::Model::Config->use_lastmodified; # Server setting
40 |
41 | #Use the logger to output status - they'll be passed to a specialized logfile and written to STDOUT.
42 | my $logger = get_logger( "Date Added Plugin", "plugins" );
43 |
44 | #Work your magic here - You can create subroutines below to organize the code better
45 |
46 | $logger->debug( "Processing file: " . $lrr_info->{file_path} );
47 | my $newtags = "";
48 | my $oneshotarg = $lrr_info->{oneshot_param};
49 | my $oneshot_file_time = $oneshotarg =~ /^(yes|true)$/i;
50 | my $oneshot_current_time = $oneshotarg =~ /^(no|false)$/i;
51 |
52 | if ( $oneshot_file_time || ( $use_filetime && !$oneshot_current_time ) ) {
53 | $logger->info("Using file date");
54 | $newtags = "date_added:" . ( stat( $lrr_info->{file_path} ) )[9]; #9 is the unix time stamp for date modified.
55 | } else {
56 | $logger->info("Using current date");
57 | $newtags = "date_added:" . time();
58 | }
59 | return ( tags => $newtags );
60 | }
61 |
62 | 1;
63 |
64 |
--------------------------------------------------------------------------------
/lib/LANraragi/Plugin/Scripts/BlacklistMigrate.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Plugin::Scripts::BlacklistMigrate;
2 |
3 | use strict;
4 | use warnings;
5 | no warnings 'uninitialized';
6 | use utf8;
7 | use LANraragi::Utils::Logging qw(get_plugin_logger);
8 | use LANraragi::Utils::Database qw(save_computed_tagrules);
9 | use LANraragi::Utils::Tags qw(tags_rules_to_array restore_CRLF);
10 | use LANraragi::Utils::Generic qw(remove_spaces);
11 | use Mojo::JSON qw(encode_json);
12 | use LANraragi::Model::Config;
13 |
14 | #Meta-information about your plugin.
15 | sub plugin_info {
16 |
17 | return (
18 | #Standard metadata
19 | name => "Blacklist Migration",
20 | type => "script",
21 | namespace => "blist2rule",
22 | author => "Difegue",
23 | version => "1.0",
24 | description => "将您的黑名单从 LANraragi < 0.8.0 的数据库迁移到新的标签规则系统."
25 | );
26 |
27 | }
28 |
29 | # Mandatory function to be implemented by your script
30 | sub run_script {
31 | shift;
32 | my $lrr_info = shift; # Global info hash
33 |
34 | my $logger = get_plugin_logger();
35 | my $redis = LANraragi::Model::Config->get_redis;
36 |
37 | my $blist = LANraragi::Model::Config::get_redis_conf( "blacklist", undef );
38 | my $rules = LANraragi::Model::Config::get_redis_conf( "tagrules",
39 | "-already uploaded;-forbidden content;-incomplete;-ongoing;-complete;-various;-digital;-translated;-russian;-chinese;-portuguese;-french;-spanish;-italian;-vietnamese;-german;-indonesian"
40 | );
41 |
42 | unless ($blist) {
43 | $logger->info("No blacklist in config, nothing to migrate!");
44 | return ( status => "Nothing to migrate" );
45 | }
46 |
47 | $logger->debug("Blacklist is $blist");
48 | $logger->debug("Rules are $rules");
49 | my @blacklist = split( ',', $blist ); # array-ize the blacklist string
50 | my $migrated = 0;
51 |
52 | # Parse the blacklist and add matching tag rules.
53 | foreach my $tag (@blacklist) {
54 |
55 | remove_spaces($tag);
56 | if ( index( uc($rules), uc($tag) ) == -1 ) {
57 | $logger->debug("Adding rule -$tag");
58 | $rules = $rules . ";-$tag";
59 | $migrated++;
60 | }
61 | }
62 |
63 | # Save rules and recompute them
64 | $redis->hset( "LRR_CONFIG", "tagrules", $rules );
65 | $redis->hdel( "LRR_CONFIG", "blacklist" );
66 |
67 | my @computed_tagrules = tags_rules_to_array( restore_CRLF($rules) );
68 | $logger->debug( "Saving computed tag rules : " . encode_json( \@computed_tagrules ) );
69 | save_computed_tagrules( \@computed_tagrules );
70 |
71 | return ( migrated_tags => $migrated );
72 | }
73 |
74 | 1;
75 |
--------------------------------------------------------------------------------
/lib/LANraragi/Plugin/Scripts/SourceFinder.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Plugin::Scripts::SourceFinder;
2 |
3 | use strict;
4 | use warnings;
5 | no warnings 'uninitialized';
6 | use utf8;
7 | use Mojo::UserAgent;
8 | use LANraragi::Utils::Logging qw(get_plugin_logger);
9 | use LANraragi::Model::Stats;
10 | use LANraragi::Utils::Generic qw(trim_url);
11 |
12 | #Meta-information about your plugin.
13 | sub plugin_info {
14 |
15 | return (
16 | #Standard metadata
17 | name => "Source Finder",
18 | type => "script",
19 | namespace => "urlfinder",
20 | author => "Difegue",
21 | version => "2.0",
22 | description => "在数据库中查找匹配给定链接 'source:' 的标签 .",
23 | icon =>
24 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABZSURBVDhPzY5JCgAhDATzSl+e/2irOUjQSFzQog5hhqIl3uBEHPxIXK7oFXwVE+Hj5IYX4lYVtN6MUW4tGw5jNdjdt5bLkwX1q2rFU0/EIJ9OUEm8xquYOQFEhr9vvu2U8gAAAABJRU5ErkJggg==",
25 | oneshot_arg => "要搜索的网址."
26 | );
27 |
28 | }
29 |
30 | # Mandatory function to be implemented by your script
31 | sub run_script {
32 | shift;
33 | my $lrr_info = shift; # Global info hash
34 | my $logger = get_plugin_logger();
35 |
36 | # Only info we need is the URL to search
37 | my $url = $lrr_info->{oneshot_param};
38 | $logger->debug( "Looking for URL " . $url );
39 |
40 | trim_url($url);
41 |
42 | if ( $url eq "" ) {
43 | return ( error => "No URL specified!", total => 0 );
44 | }
45 |
46 | my $recorded_id = LANraragi::Model::Stats::is_url_recorded($url);
47 | if ($recorded_id) {
48 | return (
49 | total => 1,
50 | id => $recorded_id
51 | );
52 | }
53 |
54 | # Specific variant for EH/Ex URLs, where we'll check with the other domain as well.
55 | my $last_chance_id = "";
56 | if ( $url =~ /https?:\/\/exhentai\.org\/g\/([0-9]*)\/([0-z]*)\/*.*/gi ) {
57 | my $url2 = "https://e-hentai.org/g/$1/$2";
58 | $last_chance_id = LANraragi::Model::Stats::is_url_recorded($url2);
59 | }
60 |
61 | if ( $url =~ /https?:\/\/e-hentai\.org\/g\/([0-9]*)\/([0-z]*)\/*.*/gi ) {
62 | my $url2 = "https://exhentai.org/g/$1/$2";
63 | $last_chance_id = LANraragi::Model::Stats::is_url_recorded($url);
64 | }
65 |
66 | if ($last_chance_id) {
67 | return (
68 | total => 1,
69 | id => $last_chance_id
70 | );
71 | }
72 |
73 | return ( error => "URL not found in database.", total => 0 );
74 | }
75 |
76 | 1;
77 |
--------------------------------------------------------------------------------
/lib/LANraragi/Plugin/Scripts/nHentaiSourceConverter.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Plugin::Scripts::nHentaiSourceConverter;
2 |
3 | use strict;
4 | use warnings;
5 | no warnings 'uninitialized';
6 | use utf8;
7 | use LANraragi::Utils::Logging qw(get_plugin_logger);
8 | use LANraragi::Utils::Database qw(invalidate_cache);
9 | use LANraragi::Model::Config;
10 |
11 | #Meta-information about your plugin.
12 | sub plugin_info {
13 |
14 | return (
15 | #Standard metadata
16 | name => "nHentai Source Converter",
17 | type => "script",
18 | namespace => "nhsrcconv",
19 | author => "Guerra24",
20 | version => "1.0",
21 | description =>
22 | "转换 \"source:{id}\" 6 位或更少位数的标签 \"source:nhentai.net/g/{id}\""
23 | );
24 |
25 | }
26 |
27 | # Mandatory function to be implemented by your script
28 | sub run_script {
29 | shift;
30 | my $lrr_info = shift; # Global info hash
31 |
32 | my $logger = get_plugin_logger();
33 | my $redis = LANraragi::Model::Config->get_redis;
34 |
35 | my @keys = $redis->keys('????????????????????????????????????????'); #40-character long keys only => Archive IDs
36 |
37 | my $count = 0;
38 | #Parse the archive list and add them to JSON.
39 | foreach my $id (@keys) {
40 |
41 | my %hash = $redis->hgetall($id);
42 | my ( $tags ) = @hash{qw(tags)};
43 |
44 | if ( $tags =~ s/source:(\d{1,6})/source:nhentai\.net\/g\/$1/igm ) {
45 | $count++;
46 | }
47 |
48 | $redis->hset( $id, "tags", $tags );
49 | }
50 |
51 | invalidate_cache();
52 | $redis->quit();
53 |
54 | return ( modified => $count );
55 | }
56 |
57 | 1;
58 |
--------------------------------------------------------------------------------
/lib/LANraragi/Utils/Logging.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Utils::Logging;
2 |
3 | use strict;
4 | use warnings;
5 | use utf8;
6 |
7 | use feature 'say';
8 | use POSIX;
9 | use FindBin;
10 |
11 | use Encode;
12 | use File::ReadBackwards;
13 |
14 | # Contains all functions related to logging.
15 | use Exporter 'import';
16 | our @EXPORT_OK = qw(get_logger get_plugin_logger get_logdir get_lines_from_file);
17 |
18 | # Get the Log folder.
19 | sub get_logdir {
20 |
21 | my $log_folder = "$FindBin::Bin/../log";
22 |
23 | # Folder location can be overriden by LRR_LOG_DIRECTORY
24 | if ( $ENV{LRR_LOG_DIRECTORY} ) {
25 | $log_folder = $ENV{LRR_LOG_DIRECTORY};
26 | }
27 | mkdir $log_folder;
28 | return $log_folder;
29 | }
30 |
31 | # Returns a Logger object with a custom name and a filename for the log file.
32 | sub get_logger {
33 |
34 | #Customize log file location and minimum log level
35 | my $pgname = $_[0];
36 | my $logfile = $_[1];
37 |
38 | my $logpath = get_logdir . "/$logfile.log";
39 |
40 | if ( -e $logpath && -s $logpath > 1048576 ) {
41 |
42 | # Rotate log if it's > 1MB
43 | say "Rotating logfile $logfile";
44 | new Logfile::Rotate( File => $logpath, Gzip => 'lib' )->rotate();
45 | }
46 |
47 | my $log = Mojo::Log->new(
48 | path => $logpath,
49 | level => 'info'
50 | );
51 |
52 | my $devmode = LANraragi::Model::Config->enable_devmode;
53 |
54 | #Tell logger to store debug logs as well in debug mode
55 | if ($devmode) {
56 | $log->level('debug');
57 | }
58 |
59 | #Copy logged messages to STDOUT with the matching name
60 | $log->on(
61 | message => sub {
62 | my ( $time, $level, @lines ) = @_;
63 |
64 | #Like with logging to file, debug logs are only printed in debug mode
65 | unless ( $devmode == 0 && $level eq 'debug' ) {
66 | print "[$pgname] [$level] ";
67 | say $lines[0];
68 | }
69 | }
70 | );
71 |
72 | $log->format(
73 | sub {
74 | my ( $time, $level, @lines ) = @_;
75 | my $time2 = strftime( "%Y-%m-%d %H:%M:%S", localtime($time) );
76 |
77 | my $logstring = join( "\n", @lines );
78 |
79 | # We'd like to make sure we always show proper UTF-8.
80 | # redis_decode, while not initially designed for this, does the job.
81 | $logstring = LANraragi::Utils::Database::redis_decode($logstring);
82 |
83 | return "[$time2] [$pgname] [$level] $logstring\n";
84 | }
85 | );
86 |
87 | return $log;
88 | }
89 |
90 | sub get_plugin_logger {
91 |
92 | my ( $pkg, $filename, $line ) = caller;
93 |
94 | if ( !$pkg->can('plugin_info') ) {
95 | die "\"get_plugin_logger\" cannot be called from \"$pkg\"; line $line at $filename\n";
96 | }
97 | my %pi = $pkg->plugin_info();
98 | return get_logger( $pi{name}, "plugins" );
99 | }
100 |
101 | sub get_lines_from_file {
102 |
103 | my $lines = $_[0];
104 | my $file = $_[1];
105 |
106 | #Load the last X lines of file
107 | if ( -e $file ) {
108 | my $bw = File::ReadBackwards->new($file);
109 | my $res = "";
110 | for ( my $i = 0; $i <= $lines; $i++ ) {
111 | my $line = $bw->readline();
112 | if ($line) {
113 | $res = $line . $res;
114 | }
115 |
116 | }
117 |
118 | return decode_utf8($res);
119 | }
120 |
121 | return "No logs to be found here!";
122 |
123 | }
124 |
125 | 1;
126 |
--------------------------------------------------------------------------------
/lib/LANraragi/Utils/Tags.pm:
--------------------------------------------------------------------------------
1 | package LANraragi::Utils::Tags;
2 |
3 | use strict;
4 | use warnings;
5 | use utf8;
6 | use feature "switch";
7 | no warnings 'experimental';
8 |
9 | use LANraragi::Utils::Generic qw(remove_spaces remove_newlines);
10 |
11 | # Functions related to the Tag system.
12 | use Exporter 'import';
13 | our @EXPORT_OK =
14 | qw( unflat_tagrules replace_CRLF restore_CRLF tags_rules_to_array rewrite_tags split_tags_to_array );
15 |
16 | sub is_null_or_empty {
17 | return !length(shift);
18 | }
19 |
20 | sub replace_CRLF {
21 | my ($val) = @_;
22 | $val =~ s/\x{d}\x{a}/;/g if ( $val );
23 | return $val;
24 | }
25 |
26 | sub restore_CRLF {
27 | my ($val) = @_;
28 | $val =~ s/;/\x{d}\x{a}/g if ( $val );
29 | return $val;
30 | }
31 |
32 | sub unflat_tagrules {
33 | my ( $flattened_rules ) = @_;
34 | my @tagrules = ();
35 | while (@{$flattened_rules || []}) {
36 | push(@tagrules, [ splice(@$flattened_rules, 0, 3) ]);
37 | }
38 | return @tagrules;
39 | }
40 |
41 | sub split_tags_to_array {
42 | my ( $tags_string ) = @_;
43 | my @tags = split( ',', $tags_string );
44 | foreach my $tags (@tags) {
45 | remove_spaces($tags);
46 | remove_newlines($tags);
47 | }
48 | return @tags;
49 | }
50 |
51 | sub tags_rules_to_array {
52 | my ( $text_rules ) = @_;
53 | my @rules;
54 | my @lines = split( '\n', $text_rules );
55 | foreach my $line ( @lines ) {
56 | my ( $match, $value ) = split( '->', $line );
57 | remove_spaces($match);
58 | remove_spaces($value);
59 | if (!is_null_or_empty($match)) {
60 |
61 | my $rule_type;
62 | if ( !$value && $match =~ m/^-.*:\*$/ ) {
63 | $rule_type = 'remove_ns';
64 | $match = substr ($match, 1, length($match)-3);
65 | } elsif ( !$value && $match =~ m/^-/ ) {
66 | $rule_type = 'remove';
67 | $match = substr ($match, 1);
68 | } elsif ( !$value && $match =~ m/^~/ ) {
69 | $rule_type = 'strip_ns';
70 | $match = substr ($match, 1);
71 | } elsif ( $match =~ m/:\*$/ && $value =~ m/:\*$/ ) {
72 | $rule_type = 'replace_ns';
73 | $match = substr ($match, 0, length($match)-2);
74 | $value = substr ($value, 0, length($value)-2);
75 | } elsif ( !$value ) {
76 | $rule_type = 'remove'; # blacklist mode
77 | } else {
78 | $rule_type = 'replace'
79 | }
80 |
81 | push( @rules, [ $rule_type, lc $match, $value || '' ] ) if ($rule_type);
82 | }
83 | }
84 | return @rules;
85 | }
86 |
87 | sub rewrite_tags {
88 | my ( $tags, $rules ) = @_;
89 | return @$tags if ( !@$rules );
90 |
91 | my @parsed_tags;
92 | foreach my $tag ( @$tags ) {
93 | my $new_tag = apply_rules($tag, $rules);
94 | push(@parsed_tags, $new_tag) if ($new_tag);
95 | }
96 | return @parsed_tags;
97 | }
98 |
99 | sub apply_rules {
100 | my ( $tag, $rules ) = @_;
101 |
102 | foreach my $rule ( @$rules ) {
103 | my $match = $rule->[1];
104 | my $value = $rule->[2];
105 | given($rule->[0]) {
106 | when ('remove') { return if ( lc $tag eq $match ); }
107 | when ('remove_ns') { return if ( $tag =~ m/^$match:/i ); }
108 | when ('replace_ns') { $tag =~ s/^\Q$match:/$value\:/i; }
109 | when ('strip_ns') { $tag =~ s/^\Q$match://i; }
110 | default { $tag = $value if ( lc $tag eq $match ); }
111 | }
112 | }
113 |
114 | return $tag;
115 | }
116 |
117 | 1;
118 |
--------------------------------------------------------------------------------
/log/.gitignore:
--------------------------------------------------------------------------------
1 | *.log*
2 |
--------------------------------------------------------------------------------
/lrr.conf:
--------------------------------------------------------------------------------
1 | {
2 | redis_address => "127.0.0.1:6379",
3 | redis_database => "0",
4 | redis_database_minion => "1"
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lanraragi",
3 | "version": "0.8.7",
4 | "version_name": "外边",
5 | "description": "我一世英名恐怕要毁在日本文化的影响下了!",
6 | "scripts": {
7 | "test": "prove -r -l -v tests/",
8 | "lanraragi-installer": "perl ./tools/install.pl",
9 | "lint": "eslint --ext .js public/",
10 | "start": "perl ./script/launcher.pl -f ./script/lanraragi",
11 | "dev-server": "perl ./script/launcher.pl -m -v ./script/lanraragi",
12 | "docker-build": "docker build -t difegue/lanraragi -f ./tools/build/docker/Dockerfile .",
13 | "critic": "perlcritic ./lib/* ./script/* ./tools/install.pl",
14 | "backup-db": "perl ./script/backup",
15 | "get-version": "perl -Mojo -E \"my \\$conf = j(f(qw(package.json))->slurp); say %\\$conf{version} .q/ - '/. %\\$conf{version_name} .q/'/ \""
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/uparrows/LANraragi_cn.git"
20 | },
21 | "author": "dezhao",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/uparrows/LANraragi_cn/issues"
25 | },
26 | "homepage": "https://github.com/uparrows/LANraragi_cn#readme",
27 | "dependencies": {
28 | "@fortawesome/fontawesome-free": "^5.15.4",
29 | "@jcubic/tagger": "^0.4.2",
30 | "allcollapsible": "^1.1.0",
31 | "awesomplete": "^1.1.5",
32 | "blueimp-file-upload": "^10.32.0",
33 | "clsx": "^1.1.1",
34 | "datatables.net": "^1.11.5",
35 | "fscreen": "^1.2.0",
36 | "inter-ui": "^3.19.3",
37 | "jqcloud2": "^2.0.3",
38 | "jquery": "^3.6.0",
39 | "jquery-contextmenu": "^2.9.2",
40 | "marked": "^4.0.14",
41 | "open-sans-fontface": "^1.4.0",
42 | "preact": "^10.7.1",
43 | "react-toastify": "^9.0.0-rc-2",
44 | "roboto-fontface": "^0.8.0",
45 | "sweetalert2": "^11.4.10",
46 | "swiper": "^8.1.4",
47 | "tippy.js": "^6.3.7"
48 | },
49 | "devDependencies": {
50 | "eslint": "^7.32.0",
51 | "eslint-config-airbnb-base": "^15.0.0",
52 | "eslint-plugin-import": "^2.26.0"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/public/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | temp
3 |
--------------------------------------------------------------------------------
/public/css/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | webfonts
3 |
--------------------------------------------------------------------------------
/public/css/config.css:
--------------------------------------------------------------------------------
1 | /*** custom checkboxes ***/
2 | input[type='checkbox'] {
3 | -webkit-appearance:none;
4 | -moz-appearance:none;
5 | appearance:none;
6 | font-size:20px;
7 | cursor:pointer;
8 | margin-left: 0px;
9 | color: inherit;
10 | }
11 |
12 | input[type='checkbox']::before {
13 | content: "\f204";
14 | }
15 |
16 | input[type='checkbox']::after {
17 | font-size: 15px;
18 | margin-left: 5px;
19 | font-weight: bold;
20 | font-family: sans-serif;
21 | content: "OFF";
22 | }
23 |
24 | input[type='checkbox']:checked::before {
25 | content: "\f205";
26 | }
27 |
28 | input[type='checkbox']:checked::after {
29 | font-size: 15px;
30 | margin-left: 5px;
31 | font-weight: bold;
32 | font-family: sans-serif;
33 | content: "ON";
34 | }
35 |
36 | .config-panel {
37 | float:left;
38 | margin-left: 5% !important;
39 | text-align: left;
40 | width:90%;
41 | margin-bottom: 10px;
42 | }
43 |
44 | .config-btn {
45 | display:inline;
46 | min-width:96px !important;
47 | height:28px !important;
48 | }
49 |
50 | /* lrr.css override */
51 | .config-td {
52 | max-width: 2000px !important;
53 | width: 30vw;
54 | vertical-align: middle;
55 | text-align: left;
56 | padding-bottom: 10px;
57 | }
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/favicon.ico
--------------------------------------------------------------------------------
/public/img/.gitignore:
--------------------------------------------------------------------------------
1 | *.db
2 |
--------------------------------------------------------------------------------
/public/img/empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/empty.png
--------------------------------------------------------------------------------
/public/img/flubbed.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/flubbed.gif
--------------------------------------------------------------------------------
/public/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/logo.png
--------------------------------------------------------------------------------
/public/img/noThumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/noThumb.png
--------------------------------------------------------------------------------
/public/img/notfound.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/notfound.jpg
--------------------------------------------------------------------------------
/public/img/theme_preview/hachikuji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/hachikuji.png
--------------------------------------------------------------------------------
/public/img/theme_preview/hverse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/hverse.png
--------------------------------------------------------------------------------
/public/img/theme_preview/nadeko.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/nadeko.png
--------------------------------------------------------------------------------
/public/img/theme_preview/sadpanda.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/sadpanda.png
--------------------------------------------------------------------------------
/public/img/theme_preview/yotsugi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/theme_preview/yotsugi.png
--------------------------------------------------------------------------------
/public/img/wait_warmly.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/img/wait_warmly.jpg
--------------------------------------------------------------------------------
/public/js/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 |
--------------------------------------------------------------------------------
/public/js/backup.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Backup Operations.
3 | */
4 | const Backup = {};
5 |
6 | Backup.initializeAll = function () {
7 | // bind events to DOM
8 | $(document).on("click.return", "#return", () => { window.location.href = "/"; });
9 | $(document).on("click.do-backup", "#do-backup", () => { window.open("./backup?dobackup=1", "_blank"); });
10 |
11 | // Handler for file uploading.
12 | $("#fileupload").fileupload({
13 | dataType: "json",
14 | done(e, data) {
15 | $("#processing").attr("style", "display:none");
16 |
17 | if (data.result.success === 1) $("#result").html("Backup restored!");
18 | else $("#result").html(data.result.error);
19 | },
20 |
21 | fail() {
22 | $("#processing").attr("style", "display:none");
23 | $("#result").html("An error occured server-side. woops.
Maybe your JSON is badly formatted ?");
24 | },
25 |
26 | progressall() {
27 | $("#result").html("");
28 | $("#processing").attr("style", "");
29 | },
30 |
31 | });
32 | };
33 |
34 | jQuery(() => {
35 | Backup.initializeAll();
36 | });
37 |
--------------------------------------------------------------------------------
/public/js/edit.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uparrows/LANraragi_cn/6f90f482382ca9d7aea6ae2848ec26a44e0cc840/public/js/edit.js
--------------------------------------------------------------------------------
/public/js/logs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Logs Operations.
3 | */
4 | const Logs = {};
5 |
6 | Logs.lastType = "";
7 |
8 | Logs.initializeAll = function () {
9 | // bind events to DOM
10 | $(document).on("click.refresh", "#refresh", Logs.refreshLog);
11 | $(document).on("click.loglines", "#loglines", Logs.refreshLog);
12 | $(document).on("click.show-general", "#show-general", () => Logs.showLog("general"));
13 | $(document).on("click.show-shinobu", "#show-shinobu", () => Logs.showLog("shinobu"));
14 | $(document).on("click.show-plugins", "#show-plugins", () => Logs.showLog("plugins"));
15 | $(document).on("click.show-mojo", "#show-mojo", () => Logs.showLog("mojo"));
16 | $(document).on("click.show-redis", "#show-redis", () => Logs.showLog("redis"));
17 | $(document).on("click.return", "#return", () => { window.location.href = "/"; });
18 |
19 | Logs.showLog("general");
20 | };
21 |
22 | Logs.showLog = function (type) {
23 | fetch(`/logs/${type}?lines=${$("#loglines").val()}`)
24 | .then((response) => response.text())
25 | .then((data) => {
26 | $("#log-container").html(LRR.encodeHTML(data));
27 | $("#indicator").html(type);
28 | $("#log-container").scrollTop($("#log-container").prop("scrollHeight"));
29 | Logs.lastType = type;
30 | })
31 | .catch((error) => LRR.showErrorToast("从服务器获取日志时出错", error));
32 | };
33 |
34 | Logs.refreshLog = function () {
35 | Logs.showLog(Logs.lastType);
36 | };
37 |
38 | jQuery(() => {
39 | Logs.initializeAll();
40 | });
41 |
--------------------------------------------------------------------------------
/public/js/plugins.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Plugins Operations.
3 | */
4 | const Plugins = {};
5 |
6 | Plugins.initializeAll = function () {
7 | // bind events to DOM
8 | $(document).on("click.save", "#save", () => Server.saveFormData("#editPluginForm"));
9 | $(document).on("click.return", "#return", () => { window.location.href = "/"; });
10 |
11 | // Handler for file uploading.
12 | $("#fileupload").fileupload({
13 | url: "/config/plugins/upload",
14 | dataType: "json",
15 | done(e, data) {
16 | if (data.result.success) {
17 | LRR.toast({
18 | heading: "插件上传成功!",
19 | text: `该插件 "${data.result.name}" 已添加成功. 刷新页面即可查看.`,
20 | icon: "info",
21 | hideAfter: 10000,
22 | });
23 | } else {
24 | LRR.toast({
25 | heading: "上传插件出错",
26 | text: data.result.error,
27 | icon: "error",
28 | hideAfter: false,
29 | });
30 | }
31 | },
32 |
33 | });
34 | };
35 |
36 | jQuery(() => {
37 | Plugins.initializeAll();
38 | });
39 |
--------------------------------------------------------------------------------
/public/js/stats.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Stats Operations.
3 | */
4 | const Stats = {};
5 |
6 | Stats.initializeAll = function () {
7 | // bind events to DOM
8 | $(document).on("click.goback", "#goback", () => { window.location.replace("./"); });
9 |
10 | Server.callAPI("/api/database/stats?minweight=2", "GET", null, "无法加载标签统计数据",
11 | (data) => {
12 | $("#statsLoading").hide();
13 | $("#tagcount").html(data.length);
14 | $("#tagCloud").jQCloud(data, {
15 | autoResize: true,
16 | });
17 |
18 | // Sort data by weight
19 | data.sort((a, b) => b.weight - a.weight);
20 |
21 | // Buildup detailed stats
22 | const tagList = $("#tagList");
23 | data.forEach((tag) => {
24 | const namespacedTag = LRR.buildNamespacedTag(tag.namespace, tag.text);
25 | const url = LRR.getTagSearchURL(tag.namespace, tag.text);
26 |
27 | const ocss = "max-width: 95%; display: flex;";
28 | const icss = "text-overflow: ellipsis; white-space: nowrap; overflow: hidden; min-width: 0; max-width: 100%;";
29 |
30 | const html = `${namespacedTag} (${tag.weight})`;
31 | tagList.append(html);
32 | });
33 |
34 | $("#detailedStats").show();
35 | },
36 | );
37 | };
38 |
39 | jQuery(() => {
40 | Stats.initializeAll();
41 | });
42 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
--------------------------------------------------------------------------------
/script/backup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use strict;
4 | use warnings;
5 |
6 | use FindBin;
7 | BEGIN { unshift @INC, "$FindBin::Bin/../lib"; }
8 | use Mojolicious;
9 | use LANraragi::Model::Backup;
10 | use LANraragi::Model::Config;
11 |
12 | print LANraragi::Model::Backup::build_backup_JSON();
13 |
--------------------------------------------------------------------------------
/script/lanraragi:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use strict;
4 | use warnings;
5 |
6 | use FindBin;
7 | BEGIN { unshift @INC, "$FindBin::Bin/../lib"; }
8 | use Mojolicious::Commands;
9 |
10 | # Start command line interface for application
11 | Mojolicious::Commands->start_app('LANraragi');
12 |
--------------------------------------------------------------------------------
/script/launcher.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use strict;
4 | use warnings;
5 | use Cwd 'abs_path';
6 |
7 | use Mojo::Base -strict;
8 | use Mojo::Server::Morbo;
9 | use Mojo::Server::Prefork;
10 | use Mojo::Util qw(extract_usage getopt);
11 | use File::Path qw(make_path);
12 |
13 | getopt
14 | 'm|morbo' => \my $morbo,
15 | 'f|foreground' => \$ENV{HYPNOTOAD_FOREGROUND},
16 | 'h|help' => \my $help,
17 | 'v|verbose' => \$ENV{MORBO_VERBOSE};
18 |
19 | if ( $ENV{LRR_DATA_DIRECTORY} ) {
20 | make_path( $ENV{LRR_DATA_DIRECTORY} );
21 | }
22 |
23 | if ( $ENV{LRR_THUMB_DIRECTORY} ) {
24 | make_path( $ENV{LRR_THUMB_DIRECTORY} );
25 | }
26 |
27 | if ( $ENV{LRR_TEMP_DIRECTORY} ) {
28 | make_path( $ENV{LRR_TEMP_DIRECTORY} );
29 | }
30 |
31 | die extract_usage if $help || !( my $app = shift || $ENV{HYPNOTOAD_APP} );
32 |
33 | my @listen;
34 | if ( $ENV{LRR_NETWORK} ) {
35 | @listen = [ $ENV{LRR_NETWORK} ];
36 | } else {
37 | @listen = ["http://*:3000"];
38 | }
39 |
40 | # Relocate the Prefork PID file
41 | my $hypno_pid;
42 | if ( $ENV{LRR_TEMP_DIRECTORY} ) {
43 | $hypno_pid = $ENV{LRR_TEMP_DIRECTORY} . "/server.pid";
44 | } else {
45 | $hypno_pid = "./public/temp/server.pid";
46 | eval { make_path("./public/temp"); } # Try creating the temp directory
47 | }
48 |
49 | $hypno_pid = abs_path($hypno_pid);
50 |
51 | my $backend;
52 | if ($morbo) {
53 | $backend = Mojo::Server::Morbo->new( keep_alive_timeout => 30 );
54 | $ENV{MOJO_MODE} = "development";
55 | $backend->daemon->listen(@listen);
56 | $backend->run($app);
57 | } else {
58 | print "Server PID will be at " . $hypno_pid . "\n";
59 |
60 | $backend = Mojo::Server::Prefork->new( keep_alive_timeout => 30 );
61 | $backend->pid_file($hypno_pid);
62 | $backend->listen(@listen);
63 |
64 | $backend->load_app($app);
65 |
66 | $backend->start;
67 | $backend->daemonize if !$ENV{HYPNOTOAD_FOREGROUND};
68 |
69 | # Start accepting connections
70 | $backend->cleanup(1)->run;
71 | }
72 |
73 | exit;
74 |
75 | =encoding utf8
76 |
77 | =head1 NAME
78 |
79 | LRR launcher using either morbo or Prefork. Morbo always starts in dev mode.
80 | To change the listen port, use the LRR_NETWORK environment variable.
81 |
82 | =head1 SYNOPSIS
83 |
84 | Usage: perl launcher.pl [OPTIONS] lanraragi
85 |
86 | Options:
87 | -m, --morbo Use morbo instead of Prefork
88 | -f, --foreground Keep manager process in foreground (Prefork)
89 | -v, --verbose Print details about what files changed to
90 | STDOUT (morbo)
91 | -h, --help Show this message
92 |
93 | =head1 DESCRIPTION
94 |
95 | Start L and L applications with the
96 | L web server.
97 |
98 | =head1 SEE ALSO
99 |
100 | L, L, L.
101 |
102 | =cut
103 |
--------------------------------------------------------------------------------
/templates/backup.html.tt2:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [% title %] - 数据库备份/恢复
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | [% csshead %]
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
数据库备份/恢复
39 |
40 |
41 | 您可以在此处备份现有数据库,或还原现有备份。
42 | 通过备份,您可以下载一个包含所有类别和档案ID以及它们匹配的元数据的JSON
43 | 文件。
44 | 从备份还原将针对数据库中已存在的
ID还原此元数据。
45 | (分类将始终被恢复)
46 |
47 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
恢复备份中 ...
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | [% INCLUDE footer %]
88 |
89 |
90 |