Commit Graph

1276 Commits

Author SHA1 Message Date
evazion
4847c6c9dd docker: add foreman to base image.
Add `foreman` to the base Docker image. This way you can do this:

   docker run --rm -it -v $PWD:/danbooru ghcr.io/danbooru/danbooru foreman start

to start everything needed to run Danbooru in development mode (except
for the Postgres database). This will start everything listed in the
Procfile:

   bin/rails server
   bin/good_job start
   bin/rails danbooru:cron
   bin/webpack-dev-server
2022-03-06 23:28:53 -06:00
evazion
0f0f7c768b jobs: use GoodJob in development.
Use the GoodJob job adapter instead of the default Rails async job
adapter in development mode.

The default async adapter runs jobs in a background thread in the
`bin/rails server` process, but this sometimes has problems with jobs
blocking the main server thread. The job queue interface at `/jobs` also
didn't work with this.

This means that now you have to run `bin/good_job start` in development
mode in order to work background jobs. This is required for uploads to
work.
2022-03-06 20:29:56 -06:00
evazion
7b009cc893 nicoseiga: fix inability to login to nicoseiga.
NicoSeiga changed it so that on every login, you must enter a 2FA code
sent by email. This broke the NicoSeiga strategy. The fix is to just use
a static session cookie instead (and hope it doesn't expire, and isn't
tied to an IP).

The `nico_seiga_login` and `nico_seiga_password` config settings have
been removed from config/danbooru_default_config.rb and replaced by
`nico_seiga_user_session`. If you run your own Danbooru instance, you
will have to update your config file manually.
2022-02-22 12:23:01 -06:00
evazion
60a26af6e3 rails: add 'URL' inflection.
Make it so we can write `ArtistURL` instead of `ArtistUrl`.
2022-02-22 00:17:53 -06:00
evazion
68ba447494 uploads: remove batch upload page.
* Make /uploads/batch redirect to /uploads/new.
* Remove /uploads/image_proxy.
2022-02-21 00:03:43 -06:00
evazion
049750e512 uploads: fix My Uploads page showing Admins uploads for other users.
Fix the "My Uploads" page showing Admins all uploads, not just their own
uploads.

Changes the URL of the My Uploads page from /uploads to /users/:id/uploads.
2022-02-16 14:11:40 -06:00
evazion
229759cc72 uploads: add /upload_media_assets index page.
This page shows each individual file you've uploaded. This is different
from the regular uploads page because files in multi-file uploads are
not grouped together.
2022-02-14 00:41:08 -06:00
evazion
44c9c7f1ac uploads: removed unused /uploads/preprocess route. 2022-02-11 03:15:12 -06:00
evazion
a02d409068 Fix #4730: Trying to change email to already taken email returns "Email address normalized address has already been taken"
Fix the error message to return "Email address has already been taken".
2022-02-08 19:18:11 -06:00
evazion
8b5d687b7a rails: add more attributes to filtered params list.
Add more sensitive attributes to the filtered parameters list so that
they aren't shown in exception messages, and aren't logged in log files
or to NewRelic.

Only do this in production so that in testing and development, you can
still see these things when inspecting objects on the console.
2022-02-06 18:09:54 -06:00
evazion
abdab7a0a8 uploads: rework upload process.
Rework the upload process so that files are saved to Danbooru first
before the user starts tagging the upload.

The main user-visible change is that you have to select the file first
before you can start tagging it. Saving the file first lets us fix a
number of problems:

* We can check for dupes before the user tags the upload.
* We can perform dupe checks and show preview images for users not using the bookmarklet.
* We can show preview images without having to proxy images through Danbooru.
* We can show previews of videos and ugoira files.
* We can reliably show the filesize and resolution of the image.
* We can let the user save files to upload later.
* We can get rid of a lot of spaghetti code related to preprocessing
  uploads. This was the cause of most weird "md5 confirmation doesn't
  match md5" errors.

(Not all of these are implemented yet.)

Internally, uploading is now a two-step process: first we create an upload
object, then we create a post from the upload. This is how it works:

* The user goes to /uploads/new and chooses a file or pastes an URL into
  the file upload component.
* The file upload component calls `POST /uploads` to create an upload.
* `POST /uploads` immediately returns a new upload object in the `pending` state.
* Danbooru starts processing the upload in a background job (downloading,
  resizing, and transferring the image to the image servers).
* The file upload component polls `/uploads/$id.json`, checking the
  upload `status` until it returns `completed` or `error`.
* When the upload status is `completed`, the user is redirected to /uploads/$id.
* On the /uploads/$id page, the user can tag the upload and submit it.
* The upload form calls `POST /posts` to create a new post from the upload.
* The user is redirected to the new post.

This is the data model:

* An upload represents a set of files uploaded to Danbooru by a user.
  Uploaded files don't have to belong to a post. An upload has an
  uploader, a status (pending, processing, completed, or error), a
  source (unless uploading from a file), and a list of media assets
  (image or video files).

* There is a has-and-belongs-to-many relationship between uploads and
  media assets. An upload can have many media assets, and a media asset
  can belong to multiple uploads. Uploads are joined to media assets
  through a upload_media_assets table.

  An upload could potentially have multiple media assets if it's a Pixiv
  or Twitter gallery. This is not yet implemented (at the moment all
  uploads have one media asset).

  A media asset can belong to multiple uploads if multiple people try
  to upload the same file, or if the same user tries to upload the same
  file more than once.

New features:

* On the upload page, you can press Ctrl+V to paste an URL and immediately upload it.
* You can save files for upload later. Your saved files are at /uploads.

Fixes:

* Improved error messages when uploading invalid files, bad URLs, and
  when forgetting the rating.
2022-01-28 04:13:22 -06:00
evazion
90be15e0b5 Fix #4973: Wiki pages json index returns 404.
Fix regression introduced in 0db20e0ca. Setting `format: false` on the
wiki pages resource disabled format negotiation on all wiki page routes,
not just the show page, which meant /wiki_pages.json no longer worked.

The fix to monkey patch the internal Rails method that parses the file
extension from the URL, and have it ignore everything but the .html,
.json, .js, and .xml extensions. This is really hacky and may break in
future Rails releases.
2022-01-22 16:52:20 -06:00
evazion
c8d27c2719 Fix #4669: Track moderation report status.
* Add ability to mark moderation reports as 'handled' or 'rejected'.
* Automatically mark reports as handled when the comment or forum post
  is deleted.
* Send a dmail to the reporter when their report is handled.
* Don't show the report notice on comments or forum posts when all
  reports against it have been handled or rejected.
* Add a fix script to mark all existing reports for deleted comments,
  forum posts, or dmails as handled.
2022-01-20 20:50:23 -06:00
evazion
0db20e0cab Fix #4591: Wiki pages with filename-like name are broken by default.
Fix wiki pages like this returning 406 errors:

* https://danbooru.donmai.us/wiki_pages/rnd.jpg

Caused by Rails parsing the .jpg part as a file extension and trying to
return a JPEG in response. This happens deep in Rails' MIME negotiation
code, so it's hard to override. The fix is to pass `format: false` in
the route to disable all special handling of file extensions by Rails,
and then handle it ourselves in the controller. Ugly.

This only affected two tags: `rnd.jpg` and `haru.jpg`.
2022-01-19 21:44:40 -06:00
GiantFrog
b1b706aaff Add configurable upload limits 2022-01-17 13:28:24 -07:00
evazion
4cb01d5813 docker: set MALLOC_CONF in docker image.
Set the MALLOC_CONF environment variable in the Docker image to tune the
Jemalloc configuration. Configuring Jemalloc to use two memory arenas
reduces memory fragmentation, and using background threads and low decay
times allows freed memory to be returned to the OS sooner.

Previously we set this environment variable at runtime in Kubernetes,
but baking it into the image is simpler.
2022-01-12 17:39:49 -06:00
evazion
bd7018a3ae rails: update cache format version to 7.0. 2022-01-10 11:39:09 -06:00
evazion
104234126f robots.txt: add more static pages.
Let Google index a few more static pages.
2022-01-10 11:00:13 -06:00
evazion
841990709d rails: enable Server-Timing HTTP header in production.
This returns a Server-Timing header on all HTTP responses, which
includes details on how long it took the server to render the response.
Browsers can show this timing information in the devtools. In Chrome, go
to the Network panel, then click a HTTP request, then click the Timing tab.
2022-01-07 21:11:04 -06:00
evazion
87dfc66073 rails: update framework files and settings to 7.0.
* Update framework files with `bin/rails app:update`.
* Update to use new Rails 7.0 default settings, except for a couple
  things regarding new cookie and cache formats that would prevent us
  from rolling back to Rails 6.1 if necessary.
2022-01-07 21:10:55 -06:00
evazion
346aeca791 rails: remove sprockets references.
Remove a dummy Sprockets config file needed to get `bin/derailed` to
run. This is no longer necessary since Rails 7.0 no longer depends on
Sprockets.
2022-01-07 14:49:14 -06:00
evazion
82211ba935 jobs: add ability to search jobs on /jobs page.
Add ability to search jobs on the /jobs page by job type or by status.

Fixes #2577 (Search filters for delayed jobs). This wasn't possible
before with DelayedJobs because it stored the job data in a YAML string,
which made it difficult to search jobs by type. GoodJobs stores job data
in a JSON object, which is easier to search in Postgres.
2022-01-04 17:18:36 -06:00
evazion
3841fba78e jobs: remove DelayedJobs.
Remove the DelayedJobs gem and database table. Completes the transition
to GoodJob started in c06bfa64f and f4953549a.

Downstream users can upgrade as follows:

* Stop the Rails server.
* Stop the DelayedJobs worker (normally running as `bin/delayed_job` or `bin/rails jobs:work`).
* Run `bin/rails jobs:work` to finish any pending delayed jobs.
* Run `bin/rails db:migrate` to create the good_jobs table and drop the delayed_jobs table.
* Start the Rails server again.
* Start the GoodJobs worker with `bin/good_job start`.
2022-01-04 15:58:12 -06:00
evazion
f4953549ae jobs: switch from DelayedJob to GoodJob.
Switch the ActiveJob backend from DelayedJob to GoodJob. Differences:

* The job worker is run with `bin/good_job start` instead of `bin/delayed_job`.
* Jobs have an 8 hour timeout instead of a 4 hour timeout.
* Jobs don't automatically retry on failure.
* Finishing jobs are preserved and pruned after 7 days.
2022-01-04 13:52:08 -06:00
evazion
21a9bb2c63 jobs: rename /delayed_jobs to /jobs.
Rename the /delayed_jobs endpoint to just /jobs since it's no longer
based on DelayedJob.
2022-01-02 21:21:16 -06:00
evazion
b8356c97c1 Add Danbooru Winter Sale. 2021-12-28 14:41:37 -06:00
evazion
4d9028f1fd docker: switch Ruby memory allocator to Jemalloc.
Switch the Ruby memory allocator from Glibc malloc to Jemalloc. Jemalloc
supposedly uses less memory than Glibc malloc because it's better at
handling memory fragmentation. It also has detailed internal statistics
to help monitor allocator behavior.

We use the LD_PRELOAD method of loading Jemalloc instead of building it
into Ruby so that we can switch allocators at runtime.
2021-12-19 03:44:59 -06:00
evazion
163ba8e7da posts: micro-optimize allocations during thumbnail generation.
Do a few micro-optimizations to reduce the number of memory allocations
during thumbnail generation.

This commit, combined with freezing string literals in a7dc05 and
67b961, reduces the number of allocations on the front page from 180,000
to 150,000, and the number of retained objects from 8,000 to 4,000.
2021-12-16 00:53:48 -06:00
evazion
3fcecd59a8 Add derailed_benchmarks gem. 2021-12-16 00:53:48 -06:00
evazion
67b96135dd Make Symbol#to_s return frozen string.
Monkey-patch Symbol#to_s to return a frozen (immutable) string instead
of a mutable string.

This should reduce string allocations, and thereby reduce memory usage
and garbage collector pressure, but it may be incompatible with
libraries that expect Symbol#to_s to return a mutable string.

https://bugs.ruby-lang.org/issues/16150
https://github.com/Shopify/symbol-fstring
2021-12-14 21:33:27 -06:00
evazion
6fc0854b4c Remove StorageManager::SFTP.
Remove the SFTP file storage backend. Downstream users can use either
sshfs (which is what Danbooru now uses in production) or rclone instead.
The Ruby SFTP gem was much slower than sshfs.
2021-12-01 23:46:20 -06:00
evazion
c1a37d9577 docker: set MALLOC_ARENA_MAX=2.
Setting MALLOC_ARENA_MAX=2 is claimed to reduce memory bloat caused by
fragmentation. Jemalloc 3.x is also claimed to reduce memory bloat, but
modern distros only ship Jemalloc 5.x, and supposedly only 3.x works.

https://devcenter.heroku.com/articles/tuning-glibc-memory-behavior
https://bugs.ruby-lang.org/issues/14718
https://www.speedshop.co/2017/12/04/malloc-doubles-ruby-memory.html
https://www.joyfulbikeshedding.com/blog/2019-03-14-what-causes-ruby-memory-bloat.html
2021-11-28 22:45:50 -06:00
evazion
9ec93e4dc7 docker: replace libjpeg-turbo with mozjpeg.
Build vips with mozjpeg instead of libjpeg-turbo. Mozjpeg is slower but
has better compression.

https://github.com/mozilla/mozjpeg
https://libjpeg-turbo.org/About/Mozjpeg
https://siipo.la/blog/is-webp-really-better-than-jpeg
https://www.ctrl.blog/entry/webp-avif-comparison.html
http://web.archive.org/web/20151204210024/http://people.mozilla.org/~josh/lossy_compressed_image_study_july_2014/
2021-11-28 17:58:43 -06:00
evazion
2c63fcf081 docker: upgrade Ruby to 3.0.3.
Upgrade bootsnap to 1.9.3 too because Ruby 3.0.3 has a bug that causes
Rails to fail to boot when bootsnap is enabled. Bootsnap 1.9.3 works
around this bug.

Also add libgmp to build with bignum support.
2021-11-28 17:58:43 -06:00
evazion
1e6f57a9ef docker: upgrade libvips to 8.12.1; add .avif support. 2021-11-28 05:20:29 -06:00
evazion
a45e6b5cfe Fix #4931: Add popup voter list for comments.
Show the comment's upvote and downvote count when you hover over a
comment's score. For mods, show the list of voters as well.
2021-11-24 22:18:48 -06:00
evazion
353e708538 votes: allow admins to remove post votes.
Allow admins to remove votes on posts. This is for fixing vote abuse.

Votes can be removed by going to the vote list on the /post_votes page,
or by clicking on a post's score, then using the "Remove" option in the
"..." dropdown menu next to the vote.

Votes are soft-deleted - they're marked as deleted in the database, but
not fully deleted. Removed votes are only visible to admins, not to
regular users. When a vote is removed by an admin, it leaves a mod
action.

Technically it's possible to undelete votes, but there's no UI for it.
2021-11-23 23:18:54 -06:00
evazion
3ae62d08eb favorites: show favlist when hovering over favcount.
Changes:

* Make it so you can click or hover over a post's favorite count to see
  the list of public favorites.
* Remove the "Show »" button next to the favorite count.
* Make the favorites list visible to all users. Before favorites were
  only visible to Gold users.
* Make the /favorites page show the list of all public favorites,
  instead of redirecting to the current user's favorites.
* Add /posts/:id/favorites endpoint.
* Add /users/:id/favorites endpoint.

This is for several reasons:

* To make viewing favorites work the same way as viewing upvotes.
* To make posts load faster for Gold users. Before, we loaded all the
  favorites when viewing a post, even when the user didn't look at them.
  This made pageloads slower for posts that had hundreds or thousands of
  favorites. Now we only load the favlist if the user hovers over the favcount.
* To make the favorite list visible to all users. Before, it wasn't
  visible to non-Gold users, because of the performance issue listed above.
* To make it more obvious that favorites are public by default. Before,
  since regular users could only see the favcount, they may have
  mistakenly believed other users couldn't see their favorites.
2021-11-20 02:40:18 -06:00
evazion
ab6d9bd0e8 post votes: fix exception when voting on posts using API.
Fix an `undefined method post_vote_url` exception when doing
`POST https://danbooru.donmai.us/posts/1/votes.json`.

Also add the following API endpoints:

* https://danbooru.donmai.us/post_votes/:id.json
* https://danbooru.donmai.us/comment_votes/:id.json
* https://danbooru.donmai.us/forum_post_votes/:id.json

where `:id` is the vote ID, not the post ID.
2021-11-14 20:11:38 -06:00
evazion
8cf00cd1a6 Add sandbox for running untrusted code.
Add a Sandbox class for running untrusted external programs like ffmpeg
or exiftool inside a sandbox. This uses Linux namespaces to run the
process in an isolated container, much like a Docker container. Unlike a
Docker container, we can use it to sandbox programs when Danbooru itself
is already running inside a Docker container.

This is also more restrictive than Docker in several ways:

* It has a system call filter that is more restrictive and more
  customizable than Docker's filter by default. Even if the process
  breaks out of the container, the syscall filter will limit what it can
  do, even if it escalates to root.
* It blocks the use of setuid binaries, so the process can't use things
  like sudo to escalate to root inside the sandbox.
* It blocks all network access inside the sandbox by default.
* All files in the container are read-only by default. The sandboxed
  process can only communicate by writing to stdout.

See app/logical/sandbox.rb for more details.

This isn't actually enabled yet. It will be rolled out progressively to
ensure it doesn't break things.
2021-11-13 05:25:10 -06:00
evazion
c68043bf26 docker: add missing libseccomp-dev package.
Fix app/logical/seccomp.rb failing to load because libseccomp.so wasn't
available.
2021-11-12 16:39:45 -06:00
evazion
908df7921f Add Ruby wrapper around libseccomp.
Add a Ruby wrapper library around the libseccomp library. Seccomp is
used to restrict the syscalls a program can make. See comments in
app/logical/seccomp.rb for further details.

This is not used for anything yet. It's simply adding part of the
sandboxing infrastructure for later use.
2021-11-11 09:20:57 -06:00
evazion
3f9a85a828 Rails: send logs to stderr by default, not stdout.
Send all logs to stderr by default instead of stdout. Fixes a problem
where parsing the output of sandboxed commands could fail, because they
could contain Rails log messages in their stdout.

When we run a command in a sandbox, we call fork+exec to run the command
in the background so we can capture its output. If Rails prints
anything to stdout between the fork and exec calls, then it will be
inadvertently captured along with the command's output. This will break
parsing of the command's output. This can happen if warning messages are
printed by Rails while setting up the sandbox between the fork and exec
calls.

Writing to stderr is also more correct, since stdout is buffered by
default, which means logs could potentially be lost if the process dies
unexpectedly before the buffers are flushed. Stderr is unbuffered by
default, which means logs will always be output immediately.
2021-11-11 09:20:57 -06:00
Lily
66551e7ff2 remove unused confirm_ban route
The associated action was removed in e29e9eda49 so this route causes an error in development and a blank page in production.
2021-10-28 21:05:55 -03:00
evazion
3b44e08783 docker: update ffmpeg and libvips. 2021-10-27 04:21:01 -05:00
evazion
94ad853a5a docker: add rclone to base Docker image.
Add Rclone to the Docker image so the Rclone storage manager is usable.
2021-10-27 03:50:34 -05:00
evazion
7dc885f216 docker: remove Postgres Dockerfile.
This now lives in the https://github.com/danbooru/danbooru-infrastructure
repository.
2021-10-14 04:15:41 -05:00
evazion
206a4b5de5 docker: avoid rebuilding CSS/JS assets on every commit.
Restructure the Dockerfile and the CSS/JS files so that we only rebuild
the CSS and JS when they change, not on every commit.

Before it took several minutes to rebuild the Docker image after every
commit, even when the JS/CSS files didn't change. This also made pulling
images slower.

This requires refactoring the CSS and JS to not use embedded Ruby (ERB)
templates, since this made the CSS and JS dependent on the Ruby
codebase, which is why we had to rebuild the assets after every Ruby
change.
2021-10-13 02:48:30 -05:00
evazion
587a9d0c8f tags: move tag category definitions out of the config file.
Move all the code for defining tag categories from the config file to
TagCategory. It didn't belong in the config because it's not possible to
add new tag categories purely in the config without editing other things
like the CSS.

Also change it so that tag colors are hardcoded in the CSS instead of
generated using ERB. Generating the CSS in ERB meant that the Docker
build had to recompile the CSS on every commit, even when it didn't
change, because it relied on Ruby code outside the CSS that we couldn't
guarantee didn't change.
2021-10-12 21:17:17 -05:00
evazion
f6abf39ebc search: try to optimize slow searches.
Try to optimize certain types of common slow searches:

* Searches for mutually-exclusive tags (e.g. `1girl multiple_girls`,
  `touhou solo -1girl -1boy`)
* Relatively large tags that are heavily skewed towards old posts
  (e.g. lucky_star, haruhi_suzumiya_no_yuuutsu, inazuma_eleven_(series),
  imageboard_desourced).
* Mid-sized tags in the <30k post range that Postgres thinks are
  big enough for a post id index scan, but a tag index scan is faster.

The general pattern is Postgres not using the tag index because it
thinks scanning down the post id index would be faster, but it's
actually much slower because it degrades to a full table scan. This
usually happens when Postgres thinks a tag is larger or more common than
it really is. Here we try to force Postgres into using the tag index
when we know the search is small.

One case that is still slow is `2girls -multiple_girls`. This returns no
results, but we can't know that without searching all of `2girls`. The
general case is searching for `A -B` where A is a subset of B and A and B
are both large tags.

Hopefully fixes #581, #654, #743, #1020, #1039, #1421, #2207, #4070,
 #4337, #4896, and various other issues raised over the years regarding
slow searches.
2021-10-12 02:30:30 -05:00