diff --git a/CHANGELOG.md b/CHANGELOG.md index aaedba113..b1aebda19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,42 @@ Changelog All notable changes to this project will be documented in this file. +## [4.1.6] - 2023-07-28 + +### Fixed + +- Fix memory leak in streaming server ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26228)) +- Fix wrong filters sometimes applying in streaming ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26159), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26213), [renchap](https://github.com/mastodon/mastodon/pull/26233)) +- Fix incorrect connect timeout in outgoing requests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26116)) + +## [4.1.5] - 2023-07-21 + +### Added + +- Add check preventing Sidekiq workers from running with Makara configured ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25850)) + +### Changed + +- Change request timeout handling to use a longer deadline ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26055)) + +### Fixed + +- Fix moderation interface for remote instances with a .zip TLD ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25886)) +- Fix remote accounts being possibly persisted to database with incomplete protocol values ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25886)) +- Fix trending publishers table not rendering correctly on narrow screens ([vmstan](https://github.com/mastodon/mastodon/pull/25945)) + +### Security + +- Fix CSP headers being unintentionally wide ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26105)) + +## [4.1.4] - 2023-07-07 + +### Fixed + +- Fix branding:generate_app_icons failing because of disallowed ICO coder ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25794)) +- Fix crash in admin interface when viewing a remote user with verified links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25796)) +- Fix processing of media files with unusual names ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25788)) + ## [4.1.3] - 2023-07-06 ### Added @@ -30,7 +66,7 @@ All notable changes to this project will be documented in this file. - Fix multiple inefficiencies in automatic post cleanup worker ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24607), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24785), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24840)) - Fix performance of streaming by parsing message JSON once ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25278), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25361)) - Fix CSP headers when `S3_ALIAS_HOST` includes a path component ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25273)) -- Fix `tootctl accounts approve --number N` not aproving N earliest registrations ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24605)) +- Fix `tootctl accounts approve --number N` not approving N earliest registrations ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24605)) - Fix reports not being closed when performing batch suspensions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24988)) - Fix being able to vote on your own polls ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25015)) - Fix race condition when reblogging a status ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25016)) diff --git a/Containerfile b/Containerfile new file mode 100644 index 000000000..cac82ae2a --- /dev/null +++ b/Containerfile @@ -0,0 +1,99 @@ +# syntax=docker/dockerfile:1.4 +# This needs to be bullseye-slim because the Ruby image is built on bullseye-slim +ARG NODE_VERSION="16.18.1-bullseye-slim" + +FROM ghcr.io/moritzheiber/ruby-jemalloc:3.0.6-slim as ruby +FROM node:${NODE_VERSION} as build + +COPY --from=ruby /opt/ruby /opt/ruby + +ENV DEBIAN_FRONTEND="noninteractive" \ + PATH="${PATH}:/opt/ruby/bin" + +#SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +WORKDIR /opt/mastodon +COPY Gemfile* package.json yarn.lock /opt/mastodon/ + +# hadolint ignore=DL3008 +RUN apt-get update && \ + apt-get install -y --no-install-recommends build-essential \ + ca-certificates \ + git \ + libicu-dev \ + libidn11-dev \ + libpq-dev \ + libjemalloc-dev \ + zlib1g-dev \ + libgdbm-dev \ + libgmp-dev \ + libssl-dev \ + libyaml-0-2 \ + ca-certificates \ + libreadline8 \ + python3 \ + shared-mime-info && \ + bundle config set --local deployment 'true' && \ + bundle config set --local without 'development test' && \ + bundle config set silence_root_warning true && \ + bundle install -j"$(nproc)" && \ + yarn install --pure-lockfile --network-timeout 600000 + +FROM node:${NODE_VERSION} + +ARG UID="991" +ARG GID="991" + +COPY --from=ruby /opt/ruby /opt/ruby + +#SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +ENV DEBIAN_FRONTEND="noninteractive" \ + PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" + +# Ignoreing these here since we don't want to pin any versions and the Debian image removes apt-get content after use +# hadolint ignore=DL3008,DL3009 +RUN apt-get update && \ + echo "Etc/UTC" > /etc/localtime && \ + groupadd -g "${GID}" mastodon && \ + useradd -l -u "$UID" -g "${GID}" -m -d /opt/mastodon mastodon && \ + apt-get -y --no-install-recommends install whois \ + wget \ + procps \ + libssl1.1 \ + libpq5 \ + imagemagick \ + ffmpeg \ + libjemalloc2 \ + libicu67 \ + libidn11 \ + libyaml-0-2 \ + file \ + ca-certificates \ + tzdata \ + libreadline8 \ + tini && \ + ln -s /opt/mastodon /mastodon + +# Note: no, cleaning here since Debian does this automatically +# See the file /etc/apt/apt.conf.d/docker-clean within the Docker image's filesystem + +COPY --chown=mastodon:mastodon . /opt/mastodon +COPY --chown=mastodon:mastodon --from=build /opt/mastodon /opt/mastodon + +ENV RAILS_ENV="production" \ + NODE_ENV="production" \ + RAILS_SERVE_STATIC_FILES="true" \ + BIND="0.0.0.0" + +# Set the run user +USER mastodon +WORKDIR /opt/mastodon + +# Precompile assets +RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder rails assets:precompile && \ + yarn cache clean + +# Set the work dir and the container entry point +ENTRYPOINT ["/usr/bin/tini", "--"] +EXPOSE 3000 4000 diff --git a/Gemfile.lock b/Gemfile.lock index 23096c387..2fd88754c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -627,7 +627,7 @@ GEM fugit (~> 1.1, >= 1.1.6) safety_net_attestation (0.4.0) jwt (~> 2.0) - sanitize (6.0.1) + sanitize (6.0.2) crass (~> 1.0.2) nokogiri (>= 1.12.0) scenic (1.7.0) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index d9c530fc1..9b6f84fa1 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -90,7 +90,7 @@ class ComposeForm extends ImmutablePureComponent { const fulltext = this.getFulltextForCharacterCounting(); const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0; - return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 2500 || (isOnlyWhitespace && !anyMedia)); + return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 5000 || (isOnlyWhitespace && !anyMedia)); }; handleSubmit = (e) => { @@ -280,7 +280,7 @@ class ComposeForm extends ImmutablePureComponent {
/g, '\n\n');
- const searchIndex = JSDOM.fragment(searchContent).textContent;
+ // Apply cachedFilters against the payload, constructing a
+ // `filter_results` array of FilterResult entities
+ if (req.cachedFilters) {
+ const status = payload;
+ // TODO: Calculate searchableContent in Ruby on Rails:
+ const searchableContent = ([status.spoiler_text || '', status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(/
/g, '\n').replace(/<\/p>
/g, '\n\n');
+ const searchableTextContent = JSDOM.fragment(searchableContent).textContent;
const now = new Date();
- payload.filtered = [];
- Object.values(req.cachedFilters).forEach((cachedFilter) => {
- if ((cachedFilter.expires_at === null || cachedFilter.expires_at > now)) {
- const keyword_matches = searchIndex.match(cachedFilter.regexp);
- if (keyword_matches) {
- payload.filtered.push({
- filter: cachedFilter.repr,
- keyword_matches,
- });
- }
+ const filter_results = Object.values(req.cachedFilters).reduce((results, cachedFilter) => {
+ // Check the filter hasn't expired before applying:
+ if (cachedFilter.expires_at !== null && cachedFilter.expires_at < now) {
+ return results;
}
- });
- }
- transmit();
+ // Just in-case JSDOM fails to find textContent in searchableContent
+ if (!searchableTextContent) {
+ return results;
+ }
+
+ const keyword_matches = searchableTextContent.match(cachedFilter.regexp);
+ if (keyword_matches) {
+ // results is an Array of FilterResult; status_matches is always
+ // null as we only are only applying the keyword-based custom
+ // filters, not the status-based custom filters.
+ // https://docs.joinmastodon.org/entities/FilterResult/
+ results.push({
+ filter: cachedFilter.filter,
+ keyword_matches,
+ status_matches: null
+ });
+ }
+
+ return results;
+ }, []);
+
+ // Send the payload + the FilterResults as the `filtered` property
+ // to the streaming connection. To reach this code, the `event` must
+ // have been either `update` or `status.update`, meaning the
+ // `payload` is a Status entity, which has a `filtered` property:
+ //
+ // filtered: https://docs.joinmastodon.org/entities/Status/#filtered
+ transmit(event, {
+ ...payload,
+ filtered: filter_results
+ });
+ } else {
+ transmit(event, payload);
+ }
}).catch(err => {
+ releasePgConnection();
log.error(err);
- done();
});
});
};
@@ -775,7 +844,7 @@ const startWorker = async (workerId) => {
subscribe(`${redisPrefix}${id}`, listener);
});
- if (attachCloseHandler) {
+ if (typeof attachCloseHandler === 'function') {
attachCloseHandler(ids.map(id => `${redisPrefix}${id}`), listener);
}
@@ -812,12 +881,13 @@ const startWorker = async (workerId) => {
/**
* @param {any} req
* @param {function(): void} [closeHandler]
- * @return {function(string[]): void}
+ * @returns {function(string[], SubscriptionListener): void}
*/
- const streamHttpEnd = (req, closeHandler = undefined) => (ids) => {
+
+ const streamHttpEnd = (req, closeHandler = undefined) => (ids, listener) => {
req.on('close', () => {
ids.forEach(id => {
- unsubscribe(id);
+ unsubscribe(id, listener);
});
if (closeHandler) {
@@ -1077,7 +1147,7 @@ const startWorker = async (workerId) => {
* @typedef WebSocketSession
* @property {any} socket
* @property {any} request
- * @property {Object.