mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-09 23:45:45 +01:00
Merge branch 'develop' into activitypub
This commit is contained in:
0
.docker/.gitkeep
Normal file
0
.docker/.gitkeep
Normal file
0
.docker/build/.gitkeep
Normal file
0
.docker/build/.gitkeep
Normal file
0
.docker/config/.gitkeep
Normal file
0
.docker/config/.gitkeep
Normal file
0
.docker/database/mongo/data/.gitkeep
Normal file
0
.docker/database/mongo/data/.gitkeep
Normal file
0
.docker/database/postgresql/data/.gitkeep
Normal file
0
.docker/database/postgresql/data/.gitkeep
Normal file
0
.docker/database/redis/data/.gitkeep
Normal file
0
.docker/database/redis/data/.gitkeep
Normal file
0
.docker/public/uploads/.gitkeep
Normal file
0
.docker/public/uploads/.gitkeep
Normal file
17
.github/workflows/docker.yml
vendored
17
.github/workflows/docker.yml
vendored
@@ -21,8 +21,6 @@ jobs:
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
@@ -37,6 +35,9 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get current date in NST
|
||||
run: echo "CURRENT_DATE_NST=$(date +'%Y%m%d-%H%M%S' -d '-3 hours -30 minutes')" >> $GITHUB_ENV
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -48,12 +49,22 @@ jobs:
|
||||
type=semver,pattern={{major}}.x
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
type=ref,event=branch,enable=${{ github.event.repository.default_branch != github.ref }}
|
||||
type=raw,value=${{ env.CURRENT_DATE_NST }}
|
||||
flavor: |
|
||||
latest=true
|
||||
|
||||
- name: Cache node_modules
|
||||
id: cache-node-modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: var-cache-node-modules
|
||||
key: var-cache-node-modules-${{ hashFiles('Dockerfile', 'install/package.json') }}
|
||||
|
||||
- name: Build and push Docker images
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
cache-to: type=gha,mode=min
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
|
||||
4
.github/workflows/test.yaml
vendored
4
.github/workflows/test.yaml
vendored
@@ -192,7 +192,7 @@ jobs:
|
||||
run: npm run coverage
|
||||
|
||||
- name: Test coverage
|
||||
uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 # v2.2.3
|
||||
uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 # v2.3.0
|
||||
if: matrix.coverage
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -206,7 +206,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Coveralls Finished
|
||||
uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 # v2.2.3
|
||||
uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 # v2.3.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
parallel-finished: true
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -71,4 +71,5 @@ package-lock.json
|
||||
link-plugins.sh
|
||||
test.sh
|
||||
|
||||
.docker/
|
||||
.docker/**
|
||||
!**/.gitkeep
|
||||
136
CHANGELOG.md
136
CHANGELOG.md
@@ -1,3 +1,139 @@
|
||||
#### v3.7.5 (2024-05-03)
|
||||
|
||||
##### Chores
|
||||
|
||||
* incrementing version number - v3.7.4 (6678744c)
|
||||
* update changelog for v3.7.4 (8a1b281b)
|
||||
* incrementing version number - v3.7.3 (2d62b6f6)
|
||||
* incrementing version number - v3.7.2 (cc257e7e)
|
||||
* incrementing version number - v3.7.1 (712365a5)
|
||||
* incrementing version number - v3.7.0 (9a6153d7)
|
||||
* incrementing version number - v3.6.7 (86a17e38)
|
||||
* incrementing version number - v3.6.6 (6604bf37)
|
||||
* incrementing version number - v3.6.5 (6c653625)
|
||||
* incrementing version number - v3.6.4 (83d131b4)
|
||||
* incrementing version number - v3.6.3 (fc7d2bfd)
|
||||
* incrementing version number - v3.6.2 (0f577a57)
|
||||
* incrementing version number - v3.6.1 (f1a69468)
|
||||
* incrementing version number - v3.6.0 (4cdf85f8)
|
||||
* incrementing version number - v3.5.3 (ed0e8783)
|
||||
* incrementing version number - v3.5.2 (52fbb2da)
|
||||
* incrementing version number - v3.5.1 (4c543488)
|
||||
* incrementing version number - v3.5.0 (d06fb4f0)
|
||||
* incrementing version number - v3.4.3 (5c984250)
|
||||
* incrementing version number - v3.4.2 (3f0dac38)
|
||||
* incrementing version number - v3.4.1 (01e69574)
|
||||
* incrementing version number - v3.4.0 (fd9247c5)
|
||||
* incrementing version number - v3.3.9 (5805e770)
|
||||
* incrementing version number - v3.3.8 (a5603565)
|
||||
* incrementing version number - v3.3.7 (b26f1744)
|
||||
* incrementing version number - v3.3.6 (7fb38792)
|
||||
* incrementing version number - v3.3.4 (a67f84ea)
|
||||
* incrementing version number - v3.3.3 (f94d239b)
|
||||
* incrementing version number - v3.3.2 (ec9dac97)
|
||||
* incrementing version number - v3.3.1 (151cc68f)
|
||||
* incrementing version number - v3.3.0 (fc1ad70f)
|
||||
* incrementing version number - v3.2.3 (b06d3e63)
|
||||
* incrementing version number - v3.2.2 (758ecfcd)
|
||||
* incrementing version number - v3.2.1 (20145074)
|
||||
* incrementing version number - v3.2.0 (9ecac38e)
|
||||
* incrementing version number - v3.1.7 (0b4e81ab)
|
||||
* incrementing version number - v3.1.6 (b3a3b130)
|
||||
* incrementing version number - v3.1.5 (ec19343a)
|
||||
* incrementing version number - v3.1.4 (2452783c)
|
||||
* incrementing version number - v3.1.3 (3b4e9d3f)
|
||||
* incrementing version number - v3.1.2 (40fa3489)
|
||||
* incrementing version number - v3.1.1 (40250733)
|
||||
* incrementing version number - v3.1.0 (0cb386bd)
|
||||
* incrementing version number - v3.0.1 (26f6ea49)
|
||||
* incrementing version number - v3.0.0 (224e08cd)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
* #12543, use PATCH (420c8999)
|
||||
|
||||
#### v3.7.4 (2024-04-17)
|
||||
|
||||
##### Chores
|
||||
|
||||
* up harmony (18990795)
|
||||
* up harmony (c2465a16)
|
||||
* up themes (ba86740a)
|
||||
* up themes (5d8a5571)
|
||||
* incrementing version number - v3.7.3 (2d62b6f6)
|
||||
* update changelog for v3.7.3 (8d450eea)
|
||||
* incrementing version number - v3.7.2 (cc257e7e)
|
||||
* incrementing version number - v3.7.1 (712365a5)
|
||||
* incrementing version number - v3.7.0 (9a6153d7)
|
||||
* incrementing version number - v3.6.7 (86a17e38)
|
||||
* incrementing version number - v3.6.6 (6604bf37)
|
||||
* incrementing version number - v3.6.5 (6c653625)
|
||||
* incrementing version number - v3.6.4 (83d131b4)
|
||||
* incrementing version number - v3.6.3 (fc7d2bfd)
|
||||
* incrementing version number - v3.6.2 (0f577a57)
|
||||
* incrementing version number - v3.6.1 (f1a69468)
|
||||
* incrementing version number - v3.6.0 (4cdf85f8)
|
||||
* incrementing version number - v3.5.3 (ed0e8783)
|
||||
* incrementing version number - v3.5.2 (52fbb2da)
|
||||
* incrementing version number - v3.5.1 (4c543488)
|
||||
* incrementing version number - v3.5.0 (d06fb4f0)
|
||||
* incrementing version number - v3.4.3 (5c984250)
|
||||
* incrementing version number - v3.4.2 (3f0dac38)
|
||||
* incrementing version number - v3.4.1 (01e69574)
|
||||
* incrementing version number - v3.4.0 (fd9247c5)
|
||||
* incrementing version number - v3.3.9 (5805e770)
|
||||
* incrementing version number - v3.3.8 (a5603565)
|
||||
* incrementing version number - v3.3.7 (b26f1744)
|
||||
* incrementing version number - v3.3.6 (7fb38792)
|
||||
* incrementing version number - v3.3.4 (a67f84ea)
|
||||
* incrementing version number - v3.3.3 (f94d239b)
|
||||
* incrementing version number - v3.3.2 (ec9dac97)
|
||||
* incrementing version number - v3.3.1 (151cc68f)
|
||||
* incrementing version number - v3.3.0 (fc1ad70f)
|
||||
* incrementing version number - v3.2.3 (b06d3e63)
|
||||
* incrementing version number - v3.2.2 (758ecfcd)
|
||||
* incrementing version number - v3.2.1 (20145074)
|
||||
* incrementing version number - v3.2.0 (9ecac38e)
|
||||
* incrementing version number - v3.1.7 (0b4e81ab)
|
||||
* incrementing version number - v3.1.6 (b3a3b130)
|
||||
* incrementing version number - v3.1.5 (ec19343a)
|
||||
* incrementing version number - v3.1.4 (2452783c)
|
||||
* incrementing version number - v3.1.3 (3b4e9d3f)
|
||||
* incrementing version number - v3.1.2 (40fa3489)
|
||||
* incrementing version number - v3.1.1 (40250733)
|
||||
* incrementing version number - v3.1.0 (0cb386bd)
|
||||
* incrementing version number - v3.0.1 (26f6ea49)
|
||||
* incrementing version number - v3.0.0 (224e08cd)
|
||||
|
||||
##### New Features
|
||||
|
||||
* #12495, add unblock button to users on /blocks (afe597a2)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
* zipObject (110ce66a)
|
||||
* index should be cid (8410cec7)
|
||||
* uniqCids (de0176ef)
|
||||
* if votes are not public, dont show upvoter names in tooltip (5af69dbc)
|
||||
* add canView check to /api/v3/users/:uid (5bee324c)
|
||||
* is user doesnt have view:users privilege (14f5774f)
|
||||
* copy dropdown closing on copy ip/text/link (48281f96)
|
||||
* align teaser avatar (5fcd1a8a)
|
||||
* if you have chat:privileged the chat icons should show up (ca07e517)
|
||||
* #12485, resolve flags on a topics posts on topic delete (d7256796)
|
||||
* #12471, remove readable strings (0b1f2546)
|
||||
|
||||
##### Refactors
|
||||
|
||||
* add new hooks for notifications/websockets (d2e042d1)
|
||||
* move parseInt to filter (52e71522)
|
||||
* better label for show to groups (cfd29db0)
|
||||
|
||||
##### Tests
|
||||
|
||||
* update spec (6816e39b)
|
||||
* fix tests and use action (acab46e6)
|
||||
|
||||
#### v3.7.3 (2024-04-03)
|
||||
|
||||
##### Chores
|
||||
|
||||
95
Dockerfile
95
Dockerfile
@@ -1,51 +1,74 @@
|
||||
FROM --platform=$BUILDPLATFORM node:lts as npm
|
||||
FROM node:lts as build
|
||||
|
||||
RUN mkdir -p /usr/src/build && \
|
||||
chown -R node:node /usr/src/build
|
||||
WORKDIR /usr/src/build
|
||||
ENV NODE_ENV=production \
|
||||
DAEMON=false \
|
||||
SILENT=false \
|
||||
USER=nodebb \
|
||||
UID=1001 \
|
||||
GID=1001
|
||||
|
||||
ARG NODE_ENV
|
||||
ENV NODE_ENV $NODE_ENV
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
COPY --chown=node:node install/package.json /usr/src/build/package.json
|
||||
COPY . /usr/src/app/
|
||||
|
||||
USER node
|
||||
# Install corepack to allow usage of other package managers
|
||||
RUN corepack enable
|
||||
|
||||
# Removing unnecessary files for us
|
||||
RUN find . -mindepth 1 -maxdepth 1 -name '.*' ! -name '.' ! -name '..' -exec bash -c 'echo "Deleting {}"; rm -rf {}' \;
|
||||
|
||||
# Prepage package.json
|
||||
RUN cp /usr/src/app/install/package.json /usr/src/app/
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive \
|
||||
apt-get -y --no-install-recommends install \
|
||||
tini
|
||||
|
||||
RUN groupadd --gid ${GID} ${USER} \
|
||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
||||
&& chown -R ${USER}:${USER} /usr/src/app/
|
||||
|
||||
USER ${USER}
|
||||
|
||||
RUN npm install --omit=dev
|
||||
# TODO: generate lockfiles for each package manager
|
||||
## pnpm import \
|
||||
|
||||
FROM node:lts as rebuild
|
||||
FROM node:lts-slim AS final
|
||||
|
||||
ARG BUILDPLATFORM
|
||||
ARG TARGETPLATFORM
|
||||
ENV NODE_ENV=production \
|
||||
DAEMON=false \
|
||||
SILENT=false \
|
||||
USER=nodebb \
|
||||
UID=1001 \
|
||||
GID=1001
|
||||
|
||||
RUN mkdir -p /usr/src/build && \
|
||||
chown -R node:node /usr/src/build
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
COPY --from=npm /usr/src/build /usr/src/build
|
||||
COPY --from=build --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/
|
||||
COPY --from=build --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/
|
||||
|
||||
RUN if [ $BUILDPLATFORM != $TARGETPLATFORM ]; then \
|
||||
npm rebuild && \
|
||||
npm cache clean --force; fi
|
||||
RUN corepack enable \
|
||||
&& groupadd --gid ${GID} ${USER} \
|
||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
||||
&& mkdir -p /usr/src/app/logs/ /opt/config/ \
|
||||
&& chown -R ${USER}:${USER} /usr/src/app/ /opt/config/ \
|
||||
&& chmod +x /usr/local/bin/entrypoint.sh \
|
||||
&& chmod +x /usr/local/bin/tini
|
||||
|
||||
FROM node:lts-slim as run
|
||||
# TODO: Have docker-compose use environment variables to create files like setup.json and config.json.
|
||||
# COPY --from=hairyhenderson/gomplate:stable /gomplate /usr/local/bin/gomplate
|
||||
|
||||
ARG NODE_ENV
|
||||
ENV NODE_ENV=$NODE_ENV \
|
||||
daemon=false \
|
||||
silent=false
|
||||
|
||||
RUN mkdir -p /usr/src/app && \
|
||||
chown -R node:node /usr/src/app
|
||||
|
||||
COPY --chown=node:node --from=rebuild /usr/src/build /usr/src/app
|
||||
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
USER node
|
||||
|
||||
COPY --chown=node:node . /usr/src/app
|
||||
USER ${USER}
|
||||
|
||||
EXPOSE 4567
|
||||
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config"]
|
||||
ENTRYPOINT ["./install/docker/entrypoint.sh"]
|
||||
|
||||
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config/"]
|
||||
|
||||
# Utilising tini as our init system within the Docker container for graceful start-up and termination.
|
||||
# Tini serves as an uncomplicated init system, adept at managing the reaping of zombie processes and forwarding signals.
|
||||
# This approach is crucial to circumvent issues with unmanaged subprocesses and signal handling in containerised environments.
|
||||
# By integrating tini, we enhance the reliability and stability of our Docker containers.
|
||||
# Ensures smooth start-up and shutdown processes, and reliable, safe handling of signal processing.
|
||||
ENTRYPOINT ["tini", "--", "entrypoint.sh"]
|
||||
76
dev.Dockerfile
Normal file
76
dev.Dockerfile
Normal file
@@ -0,0 +1,76 @@
|
||||
FROM node:lts AS git
|
||||
|
||||
ENV USER=nodebb \
|
||||
UID=1001 \
|
||||
GID=1001
|
||||
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
RUN groupadd --gid ${GID} ${USER} \
|
||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
||||
&& chown -R ${USER}:${USER} /usr/src/app/
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y --no-install-recommends install tini
|
||||
|
||||
USER ${USER}
|
||||
|
||||
# Change to the git branch you want to test
|
||||
RUN git clone --recurse-submodules -j8 --depth 1 https://github.com/NodeBB/NodeBB.git .
|
||||
|
||||
RUN find . -mindepth 1 -maxdepth 1 -name '.*' ! -name '.' ! -name '..' -exec bash -c 'echo "Deleting {}"; rm -rf {}' \;
|
||||
|
||||
FROM node:lts AS node_modules_touch
|
||||
|
||||
ENV NODE_ENV=development \
|
||||
USER=nodebb \
|
||||
UID=1001 \
|
||||
GID=1001
|
||||
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
RUN corepack enable \
|
||||
&& groupadd --gid ${GID} ${USER} \
|
||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
||||
&& chown -R ${USER}:${USER} /usr/src/app/
|
||||
|
||||
COPY --from=git --chown=${USER}:${USER} /usr/src/app/install/package.json /usr/src/app/
|
||||
|
||||
USER ${USER}
|
||||
|
||||
RUN npm install
|
||||
|
||||
FROM node:lts-slim AS final
|
||||
|
||||
ENV NODE_ENV=development \
|
||||
DAEMON=false \
|
||||
SILENT=false \
|
||||
USER=nodebb \
|
||||
UID=1001 \
|
||||
GID=1001
|
||||
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
COPY --from=build --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/
|
||||
COPY --from=build --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/
|
||||
COPY --from=node_modules_touch --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/
|
||||
COPY --from=git --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/
|
||||
|
||||
RUN corepack enable \
|
||||
&& groupadd --gid ${GID} ${USER} \
|
||||
&& useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \
|
||||
&& mkdir -p /usr/src/app/logs/ /opt/config/ \
|
||||
&& chown -R ${USER}:${USER} /usr/src/app/ /opt/config/ \
|
||||
&& chmod +x /usr/local/bin/entrypoint.sh \
|
||||
&& chmod +x /usr/local/bin/tini
|
||||
|
||||
# TODO: Have docker-compose use environment variables to create files like setup.json and config.json.
|
||||
# COPY --from=hairyhenderson/gomplate:stable /gomplate /usr/local/bin/gomplate
|
||||
|
||||
USER ${USER}
|
||||
|
||||
EXPOSE 4567
|
||||
|
||||
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config/"]
|
||||
|
||||
ENTRYPOINT ["tini", "--", "entrypoint.sh"]
|
||||
68
docker-compose-pgsql.yml
Normal file
68
docker-compose-pgsql.yml
Normal file
@@ -0,0 +1,68 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
nodebb:
|
||||
build: .
|
||||
# image: ghcr.io/nodebb/nodebb:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
|
||||
volumes:
|
||||
- nodebb-build:/usr/src/app/build
|
||||
- nodebb-uploads:/usr/src/app/public/uploads
|
||||
- nodebb-config:/opt/config
|
||||
- ./install/docker/setup.json:/usr/src/app/setup.json
|
||||
|
||||
postgres:
|
||||
image: postgres:16.1-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: nodebb
|
||||
POSTGRES_PASSWORD: nodebb
|
||||
POSTGRES_DB: nodebb
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:7.2.3-alpine
|
||||
restart: unless-stopped
|
||||
command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning']
|
||||
# command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/database/postgresql/data
|
||||
|
||||
redis-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/database/redis
|
||||
|
||||
nodebb-build:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/build
|
||||
|
||||
nodebb-uploads:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/public/uploads
|
||||
|
||||
nodebb-config:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/config
|
||||
51
docker-compose-redis.yml
Normal file
51
docker-compose-redis.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
nodebb:
|
||||
build: .
|
||||
# image: ghcr.io/nodebb/nodebb:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
|
||||
volumes:
|
||||
- nodebb-build:/usr/src/app/build
|
||||
- nodebb-uploads:/usr/src/app/public/uploads
|
||||
- nodebb-config:/opt/config
|
||||
- ./install/docker/setup.json:/usr/src/app/setup.json
|
||||
|
||||
redis:
|
||||
image: redis:7.2.3-alpine
|
||||
restart: unless-stopped
|
||||
command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning']
|
||||
# command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
|
||||
volumes:
|
||||
redis-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/database/redis
|
||||
|
||||
nodebb-build:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/build
|
||||
|
||||
nodebb-uploads:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/public/uploads
|
||||
|
||||
nodebb-config:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/config
|
||||
@@ -3,53 +3,68 @@ version: '3.8'
|
||||
services:
|
||||
nodebb:
|
||||
build: .
|
||||
# image: ghcr.io/nodebb/nodebb:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4567:4567/tcp" # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
|
||||
# uncomment if you want to use another container as a reverse proxy
|
||||
# expose:
|
||||
# - 4567
|
||||
- '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want
|
||||
volumes:
|
||||
- ./.docker/build:/usr/src/app/build
|
||||
- ./.docker/public/uploads:/usr/src/app/public/uploads
|
||||
- ./.docker:/opt/config
|
||||
- nodebb-build:/usr/src/app/build
|
||||
- nodebb-uploads:/usr/src/app/public/uploads
|
||||
- nodebb-config:/opt/config
|
||||
- ./install/docker/setup.json:/usr/src/app/setup.json
|
||||
|
||||
mongo:
|
||||
image: "mongo:7-jammy"
|
||||
image: 'mongo:7-jammy'
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "27017"
|
||||
ports:
|
||||
- '27017:27017'
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: nodebb
|
||||
MONGO_INITDB_ROOT_PASSWORD: nodebb
|
||||
MONGO_INITDB_DATABASE: nodebb
|
||||
volumes:
|
||||
- ./.docker/database/mongo/config:/etc/mongo
|
||||
- ./.docker/database/mongo/data:/data/db
|
||||
- mongo-data:/data/db
|
||||
- ./install/docker/mongodb-user-init.js:/docker-entrypoint-initdb.d/user-init.js
|
||||
profiles:
|
||||
- mongo
|
||||
postgres:
|
||||
image: postgres:16.2-alpine
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "5432"
|
||||
environment:
|
||||
POSTGRES_USER: nodebb
|
||||
POSTGRES_PASSWORD: nodebb
|
||||
POSTGRES_DB: nodebb
|
||||
volumes:
|
||||
- ./.docker/database/postgresql/data:/var/lib/postgresql/data
|
||||
profiles:
|
||||
- postgres
|
||||
redis:
|
||||
image: redis:7.2.3-alpine
|
||||
restart: unless-stopped
|
||||
command: ["redis-server", "--appendonly", "yes", "--loglevel", "warning"]
|
||||
# command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF
|
||||
expose:
|
||||
- "6379"
|
||||
command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning']
|
||||
# command: ['redis-server', '--save', '60', '1', '--loglevel', 'warning'] # uncomment if you want to use snapshotting instead of AOF
|
||||
volumes:
|
||||
- ./.docker/database/redis:/data
|
||||
profiles:
|
||||
- redis
|
||||
- redis-data:/data
|
||||
|
||||
volumes:
|
||||
mongo-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/database/mongo/data
|
||||
|
||||
redis-data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/database/redis
|
||||
|
||||
nodebb-build:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/build
|
||||
|
||||
nodebb-uploads:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/public/uploads
|
||||
|
||||
nodebb-config:
|
||||
driver: local
|
||||
driver_opts:
|
||||
o: bind
|
||||
type: none
|
||||
device: ./.docker/config
|
||||
|
||||
@@ -1,46 +1,192 @@
|
||||
#!/bin/bash
|
||||
|
||||
export CONFIG_DIR="${CONFIG_DIR:-/opt/config}"
|
||||
export CONFIG=$CONFIG_DIR/config.json
|
||||
export FORCE_BUILD_BEFORE_START="${FORCE_BUILD_BEFORE_START:-false}"
|
||||
set -e
|
||||
|
||||
# Supported verbs: install (web install), setup (interactive CLI session). Default: web install
|
||||
# TODO: constraint it using a hash set (or hash table)
|
||||
export NODEBB_INIT_VERB="${NODEBB_INIT_VERB:-install}"
|
||||
# Setup variable for backward compatibility, default: <empty>
|
||||
export SETUP="${SETUP:-}"
|
||||
# Function to set default values for environment variables
|
||||
set_defaults() {
|
||||
export CONFIG_DIR="${CONFIG_DIR:-/opt/config}"
|
||||
export CONFIG="$CONFIG_DIR/config.json"
|
||||
export NODEBB_INIT_VERB="${NODEBB_INIT_VERB:-install}"
|
||||
export START_BUILD="${START_BUILD:-false}"
|
||||
export SETUP="${SETUP:-}"
|
||||
export PACKAGE_MANAGER="${PACKAGE_MANAGER:-npm}"
|
||||
export OVERRIDE_UPDATE_LOCK="${OVERRIDE_UPDATE_LOCK:-false}"
|
||||
}
|
||||
|
||||
mkdir -p $CONFIG_DIR
|
||||
|
||||
# if the folder is mounted as a volume this can fail, the check below is to ensure there is still write access
|
||||
chmod -fR 760 $CONFIG_DIR 2> /dev/null
|
||||
|
||||
if [[ ! -w $CONFIG_DIR ]]; then
|
||||
echo "panic: no write permission for $CONFIG_DIR"
|
||||
# Function to check if a directory exists and is writable
|
||||
check_directory() {
|
||||
local dir="$1"
|
||||
if [ ! -d "$dir" ]; then
|
||||
echo "Error: Directory $dir does not exist. Creating..."
|
||||
mkdir -p "$dir" || {
|
||||
echo "Error: Failed to create directory $dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[[ -f $CONFIG_DIR/package.json ]] || cp install/package.json $CONFIG_DIR/package.json
|
||||
[[ -f $CONFIG_DIR/package-lock.json ]] || touch $CONFIG_DIR/package-lock.json
|
||||
|
||||
ln -fs $CONFIG_DIR/package.json package.json
|
||||
ln -fs $CONFIG_DIR/package-lock.json package-lock.json
|
||||
|
||||
npm install --omit=dev
|
||||
|
||||
if [[ -n $SETUP ]]; then
|
||||
echo "Setup environmental variable detected"
|
||||
echo "Starting setup session"
|
||||
./nodebb setup --config=$CONFIG
|
||||
elif [ -f $CONFIG ]; then
|
||||
echo "Config file exist at $CONFIG, assuming it is a valid config"
|
||||
echo "Starting forum"
|
||||
if [ "$FORCE_BUILD_BEFORE_START" = true ]; then
|
||||
./nodebb build --config=$CONFIG
|
||||
}
|
||||
fi
|
||||
./nodebb start --config=$CONFIG
|
||||
else
|
||||
echo "Config file not found at $CONFIG"
|
||||
if [ ! -w "$dir" ]; then
|
||||
echo "Error: No write permission for directory $dir"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to copy or link package.json and lock files based on package manager
|
||||
copy_or_link_files() {
|
||||
local src_dir="$1"
|
||||
local dest_dir="$2"
|
||||
local package_manager="$3"
|
||||
local lock_file
|
||||
|
||||
case "$package_manager" in
|
||||
yarn) lock_file="yarn.lock" ;;
|
||||
npm) lock_file="package-lock.json" ;;
|
||||
pnpm) lock_file="pnpm-lock.yaml" ;;
|
||||
*)
|
||||
echo "Unknown package manager: $package_manager"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check if source and destination files are the same
|
||||
if [ "$(realpath "$src_dir/package.json")" != "$(realpath "$dest_dir/package.json")" ]; then
|
||||
cp "$src_dir/package.json" "$dest_dir/package.json"
|
||||
fi
|
||||
|
||||
if [ "$(realpath "$src_dir/$lock_file")" != "$(realpath "$dest_dir/$lock_file")" ]; then
|
||||
cp "$src_dir/$lock_file" "$dest_dir/$lock_file"
|
||||
fi
|
||||
|
||||
# Remove unnecessary lock files in src_dir
|
||||
rm -f "$src_dir/"{yarn.lock,package-lock.json,pnpm-lock.yaml}
|
||||
|
||||
# Symbolically link the copied files in src_dir to dest_dir
|
||||
ln -fs "$dest_dir/package.json" "$src_dir/package.json"
|
||||
ln -fs "$dest_dir/$lock_file" "$src_dir/$lock_file"
|
||||
}
|
||||
|
||||
# Function to install dependencies using pnpm
|
||||
install_dependencies() {
|
||||
case "$PACKAGE_MANAGER" in
|
||||
yarn) yarn install || {
|
||||
echo "Failed to install dependencies with yarn"
|
||||
exit 1
|
||||
} ;;
|
||||
npm) npm install || {
|
||||
echo "Failed to install dependencies with npm"
|
||||
exit 1
|
||||
} ;;
|
||||
pnpm) pnpm install || {
|
||||
echo "Failed to install dependencies with pnpm"
|
||||
exit 1
|
||||
} ;;
|
||||
*)
|
||||
echo "Unknown package manager: $PACKAGE_MANAGER"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Function to start setup session
|
||||
start_setup_session() {
|
||||
local config="$1"
|
||||
echo "Starting setup session"
|
||||
exec /usr/src/app/nodebb setup --config="$config"
|
||||
}
|
||||
|
||||
# Handle building and upgrading NodeBB
|
||||
build_forum() {
|
||||
local config="$1"
|
||||
local start_build="$2"
|
||||
local package_hash=$(md5sum install/package.json | head -c 32)
|
||||
if [ package_hash = "$(cat $CONFIG_DIR/install_hash.md5)" ]; then
|
||||
echo "package.json was updated. Upgrading..."
|
||||
/usr/src/app/nodebb upgrade --config="$config" || {
|
||||
echo "Failed to build NodeBB. Exiting..."
|
||||
exit 1
|
||||
}
|
||||
elif [ "$start_build" = true ]; then
|
||||
echo "Build before start is enabled. Building..."
|
||||
/usr/src/app/nodebb "${NODEBB_BUILD_VERB}" --config="$config" || {
|
||||
echo "Failed to build NodeBB. Exiting..."
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
echo "No changes in package.json. Skipping build..."
|
||||
return
|
||||
fi
|
||||
echo -n $package_hash > $CONFIG_DIR/install_hash.md5
|
||||
}
|
||||
|
||||
|
||||
# Function to start forum
|
||||
start_forum() {
|
||||
local config="$1"
|
||||
local start_build="$2"
|
||||
|
||||
build_forum "$config" "$start_build"
|
||||
|
||||
case "$PACKAGE_MANAGER" in
|
||||
yarn)
|
||||
yarn start --config="$config" --no-silent --no-daemon || {
|
||||
echo "Failed to start forum with yarn"
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
npm)
|
||||
npm start -- --config="$config" --no-silent --no-daemon || {
|
||||
echo "Failed to start forum with npm"
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
pnpm)
|
||||
pnpm start -- --config="$config" --no-silent --no-daemon || {
|
||||
echo "Failed to start forum with pnpm"
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
*)
|
||||
echo "Unknown package manager: $PACKAGE_MANAGER"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Function to start installation session
|
||||
start_installation_session() {
|
||||
local nodebb_init_verb="$1"
|
||||
local config="$2"
|
||||
|
||||
echo "Config file not found at $config"
|
||||
echo "Starting installation session"
|
||||
./nodebb "${NODEBB_INIT_VERB}" --config=$CONFIG
|
||||
fi
|
||||
exec /usr/src/app/nodebb "$nodebb_init_verb" --config="$config"
|
||||
}
|
||||
|
||||
# Function for debugging and logging
|
||||
debug_log() {
|
||||
local message="$1"
|
||||
echo "DEBUG: $message"
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
set_defaults
|
||||
check_directory "$CONFIG_DIR"
|
||||
copy_or_link_files /usr/src/app "$CONFIG_DIR" "$PACKAGE_MANAGER"
|
||||
install_dependencies
|
||||
|
||||
debug_log "PACKAGE_MANAGER: $PACKAGE_MANAGER"
|
||||
debug_log "CONFIG location: $CONFIG"
|
||||
debug_log "START_BUILD: $START_BUILD"
|
||||
|
||||
if [ -n "$SETUP" ]; then
|
||||
start_setup_session "$CONFIG"
|
||||
fi
|
||||
|
||||
if [ -f "$CONFIG" ]; then
|
||||
start_forum "$CONFIG" "$START_BUILD"
|
||||
else
|
||||
start_installation_session "$NODEBB_INIT_VERB" "$CONFIG"
|
||||
fi
|
||||
}
|
||||
|
||||
# Execute main function
|
||||
main "$@"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"defaults": {
|
||||
"mongo": {
|
||||
"host": "mongo",
|
||||
"port": 27017,
|
||||
@@ -18,4 +19,5 @@
|
||||
"username": "nodebb",
|
||||
"password": "nodebb"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPL-3.0",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "3.7.3",
|
||||
"version": "3.7.5",
|
||||
"homepage": "https://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -64,7 +64,7 @@
|
||||
"csrf-sync": "4.0.3",
|
||||
"daemon": "1.1.0",
|
||||
"diff": "5.2.0",
|
||||
"esbuild": "0.20.2",
|
||||
"esbuild": "0.21.1",
|
||||
"express": "4.19.2",
|
||||
"express-session": "1.18.0",
|
||||
"express-useragent": "1.0.15",
|
||||
@@ -80,16 +80,16 @@
|
||||
"jquery-deserialize": "2.0.0",
|
||||
"jquery-form": "4.3.0",
|
||||
"jquery-serializeobject": "1.0.0",
|
||||
"jquery-ui": "1.13.2",
|
||||
"jquery-ui": "1.13.3",
|
||||
"jsesc": "3.0.2",
|
||||
"json2csv": "5.0.7",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
"lodash": "4.17.21",
|
||||
"logrotate-stream": "0.2.9",
|
||||
"lru-cache": "10.2.0",
|
||||
"lru-cache": "10.2.2",
|
||||
"mime": "3.0.0",
|
||||
"mkdirp": "3.0.1",
|
||||
"mongodb": "6.5.0",
|
||||
"mongodb": "6.6.1",
|
||||
"morgan": "1.10.0",
|
||||
"mousetrap": "1.6.5",
|
||||
"multiparty": "4.2.3",
|
||||
@@ -106,8 +106,8 @@
|
||||
"nodebb-rewards-essentials": "1.0.0",
|
||||
"nodebb-theme-harmony": "2.0.0-pre.22",
|
||||
"nodebb-theme-lavender": "7.1.8",
|
||||
"nodebb-theme-peace": "2.2.4",
|
||||
"nodebb-theme-persona": "13.3.18",
|
||||
"nodebb-theme-peace": "2.2.5",
|
||||
"nodebb-theme-persona": "13.3.19",
|
||||
"nodebb-widget-essentials": "7.0.16",
|
||||
"nodemailer": "6.9.13",
|
||||
"nprogress": "0.2.0",
|
||||
@@ -121,12 +121,12 @@
|
||||
"progress-webpack-plugin": "1.0.16",
|
||||
"prompt": "1.3.0",
|
||||
"ioredis": "5.4.1",
|
||||
"rimraf": "5.0.5",
|
||||
"rimraf": "5.0.6",
|
||||
"rss": "1.2.2",
|
||||
"rtlcss": "4.1.1",
|
||||
"sanitize-html": "2.13.0",
|
||||
"sass": "1.75.0",
|
||||
"semver": "7.6.0",
|
||||
"sass": "1.77.1",
|
||||
"semver": "7.6.2",
|
||||
"serve-favicon": "2.5.0",
|
||||
"sharp": "0.32.6",
|
||||
"sitemap": "7.1.1",
|
||||
@@ -142,8 +142,8 @@
|
||||
"timeago": "1.6.7",
|
||||
"tinycon": "0.6.8",
|
||||
"toobusy-js": "0.5.1",
|
||||
"tough-cookie": "4.1.3",
|
||||
"validator": "13.11.0",
|
||||
"tough-cookie": "4.1.4",
|
||||
"validator": "13.12.0",
|
||||
"webpack": "5.91.0",
|
||||
"webpack-merge": "5.10.0",
|
||||
"winston": "3.13.0",
|
||||
@@ -173,7 +173,7 @@
|
||||
"smtp-server": "3.13.4"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"sass-embedded": "1.75.0"
|
||||
"sass-embedded": "1.77.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"*/jquery": "3.7.1"
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"expand-analytics": "Analytik erweitern",
|
||||
"clear-search-history": "Suchverlauf löschen",
|
||||
"clear-search-history-confirm": "Bist du dir sicher, dass du den gesamten Suchverlauf löschen möchten?",
|
||||
"search-term": "Term",
|
||||
"search-count": "Count",
|
||||
"view-all": "View all"
|
||||
"search-term": "Bezeichnung",
|
||||
"search-count": "Anzahl",
|
||||
"view-all": "Zeige alle"
|
||||
}
|
||||
|
||||
@@ -90,6 +90,6 @@
|
||||
"bulk-actions": "Azioni in blocco",
|
||||
"bulk-resolve": "Risolvi segnalazione(i)",
|
||||
"bulk-success": "%1 segnalazioni aggiornate",
|
||||
"flagged-timeago": "Flagged <span class=\"timeago\" title=\"%1\"></span>",
|
||||
"flagged-timeago": "Segnalato <span class=\"timeago\" title=\"%1\"></span>",
|
||||
"auto-flagged": "[Contrassegnato automaticamente] Ha ricevuto %1 voti negativi."
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"user-menu": "Menu utente",
|
||||
"banned": "Bannato",
|
||||
"unbanned": "Unbanned",
|
||||
"unbanned": "Non bannato",
|
||||
"muted": "Silenziato",
|
||||
"unmuted": "Unmuted",
|
||||
"unmuted": "Non silenziato",
|
||||
"offline": "Non in linea",
|
||||
"deleted": "Eliminato",
|
||||
"username": "Nome Utente",
|
||||
@@ -164,16 +164,16 @@
|
||||
"sso.dissociate-confirm-title": "Conferma dissociazione",
|
||||
"sso.dissociate-confirm": "Sei sicuro di voler dissociare il tuo account da %1?",
|
||||
"info.latest-flags": "Ultime segnalazioni",
|
||||
"info.profile": "Profile",
|
||||
"info.profile": "Profilo",
|
||||
"info.post": "Post",
|
||||
"info.view-flag": "View flag",
|
||||
"info.reported-by": "Reported by:",
|
||||
"info.view-flag": "Visualizza segnalazioni",
|
||||
"info.reported-by": "Segnalato da:",
|
||||
"info.no-flags": "Non è stato trovato nessun post segnalato",
|
||||
"info.ban-history": "Storico dei Ban recenti",
|
||||
"info.no-ban-history": "Questo utente non è mai stato bannato",
|
||||
"info.banned-until": "Bannato fino %1",
|
||||
"info.banned-expiry": "Scadenza",
|
||||
"info.ban-expired": "Ban expired",
|
||||
"info.ban-expired": "Ban scaduto",
|
||||
"info.banned-permanently": "Bannato permanentemente",
|
||||
"info.banned-reason-label": "Motivo",
|
||||
"info.banned-no-reason": "Non è stata data nessuna motivazione.",
|
||||
|
||||
@@ -45,10 +45,10 @@
|
||||
"rebuild-and-restart": "สร้างใหม่ & เริ่มต้นระบบใหม่",
|
||||
"restart": "เริ่มต้นระบบใหม่",
|
||||
"restart-warning": "การสร้างหรือเริ่มต้นระบบ NodeBB ของคุณจะทำให้การเชื่อมต่อถูกตัดเป็นเวลาหลายวินาที",
|
||||
"restart-disabled": "Rebuilding and Restarting your NodeBB has been disabled as you do not seem to be running it via the appropriate daemon.",
|
||||
"restart-disabled": "การสร้างและเริ่มต้นระบบ NodeBB ของคุณไม่เปิดให้ใช้งาน เนื่องจากคุณไม่ได้เรียกใช้จาก daemon ที่ถูกต้อง",
|
||||
"maintenance-mode": "โหมดการซ่อมบำรุง",
|
||||
"maintenance-mode-title": "Click here to set up maintenance mode for NodeBB",
|
||||
"dark-mode": "Dark Mode",
|
||||
"dark-mode": "โหมดมืด",
|
||||
"realtime-chart-updates": "Realtime Chart Updates",
|
||||
|
||||
"active-users": "Active Users",
|
||||
|
||||
@@ -69,6 +69,8 @@ get:
|
||||
properties:
|
||||
value:
|
||||
type: string
|
||||
byUid:
|
||||
type: string
|
||||
timestamp:
|
||||
type: number
|
||||
timestampISO:
|
||||
@@ -81,6 +83,10 @@ get:
|
||||
properties:
|
||||
value:
|
||||
type: string
|
||||
byUid:
|
||||
type: string
|
||||
byUser:
|
||||
type: object
|
||||
timestamp:
|
||||
type: number
|
||||
timestampISO:
|
||||
|
||||
@@ -77,7 +77,7 @@ define('forum/chats/message-search', [
|
||||
async function displayResults(data) {
|
||||
removeResults();
|
||||
|
||||
if (!data.length) {
|
||||
if (!data.messages.length) {
|
||||
resultListEl.removeClass('hidden');
|
||||
chatContent.addClass('hidden');
|
||||
return resultListEl.find('[component="chat/message/search/no-results"]').removeClass('hidden');
|
||||
@@ -85,7 +85,7 @@ define('forum/chats/message-search', [
|
||||
resultListEl.find('[component="chat/message/search/no-results"]').addClass('hidden');
|
||||
|
||||
const html = await app.parseAndTranslate('partials/chats/messages', {
|
||||
messages: data,
|
||||
messages: data.messages,
|
||||
isAdminOrGlobalMod: app.user.isAdmin || app.user.isGlobalMod,
|
||||
});
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ async function xhr(options) {
|
||||
const isJSON = contentType && contentType.startsWith('application/json');
|
||||
|
||||
let response;
|
||||
if (options.method !== 'head') {
|
||||
if (options.method !== 'HEAD') {
|
||||
if (isJSON) {
|
||||
response = await res.json();
|
||||
} else {
|
||||
@@ -100,14 +100,14 @@ export function get(route, data, onSuccess) {
|
||||
export function head(route, data, onSuccess) {
|
||||
return call({
|
||||
url: route + (data && Object.keys(data).length ? ('?' + $.param(data)) : ''),
|
||||
method: 'head',
|
||||
method: 'HEAD',
|
||||
}, onSuccess);
|
||||
}
|
||||
|
||||
export function post(route, data, onSuccess) {
|
||||
return call({
|
||||
url: route,
|
||||
method: 'post',
|
||||
method: 'POST',
|
||||
data,
|
||||
headers: {
|
||||
'x-csrf-token': config.csrf_token,
|
||||
@@ -118,7 +118,7 @@ export function post(route, data, onSuccess) {
|
||||
export function patch(route, data, onSuccess) {
|
||||
return call({
|
||||
url: route,
|
||||
method: 'patch',
|
||||
method: 'PATCH',
|
||||
data,
|
||||
headers: {
|
||||
'x-csrf-token': config.csrf_token,
|
||||
@@ -129,7 +129,7 @@ export function patch(route, data, onSuccess) {
|
||||
export function put(route, data, onSuccess) {
|
||||
return call({
|
||||
url: route,
|
||||
method: 'put',
|
||||
method: 'PUT',
|
||||
data,
|
||||
headers: {
|
||||
'x-csrf-token': config.csrf_token,
|
||||
@@ -140,7 +140,7 @@ export function put(route, data, onSuccess) {
|
||||
export function del(route, data, onSuccess) {
|
||||
return call({
|
||||
url: route,
|
||||
method: 'delete',
|
||||
method: 'DELETE',
|
||||
data,
|
||||
headers: {
|
||||
'x-csrf-token': config.csrf_token,
|
||||
|
||||
@@ -438,7 +438,7 @@ usersAPI.addEmail = async (caller, { email, skipConfirmation, uid }) => {
|
||||
throw new Error('[[error:email-taken]]');
|
||||
}
|
||||
await user.setUserField(uid, 'email', email);
|
||||
await user.email.confirmByUid(uid);
|
||||
await user.email.confirmByUid(uid, caller.uid);
|
||||
}
|
||||
} else {
|
||||
await usersAPI.update(caller, { uid, email });
|
||||
@@ -488,7 +488,7 @@ usersAPI.confirmEmail = async (caller, { uid, email, sessionId }) => {
|
||||
await user.email.confirmByCode(code, sessionId);
|
||||
return true;
|
||||
} else if (current && current === email) { // i.e. old account w/ unconf. email in user hash
|
||||
await user.email.confirmByUid(uid);
|
||||
await user.email.confirmByUid(uid, caller.uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,31 +22,31 @@ mongoModule.questions = [
|
||||
{
|
||||
name: 'mongo:uri',
|
||||
description: 'MongoDB connection URI: (leave blank if you wish to specify host, port, username/password and database individually)\nFormat: mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]',
|
||||
default: nconf.get('mongo:uri') || '',
|
||||
default: nconf.get('mongo:uri') || nconf.get('defaults:mongo:uri') || '',
|
||||
hideOnWebInstall: true,
|
||||
},
|
||||
{
|
||||
name: 'mongo:host',
|
||||
description: 'Host IP or address of your MongoDB instance',
|
||||
default: nconf.get('mongo:host') || '127.0.0.1',
|
||||
default: nconf.get('mongo:host') || nconf.get('defaults:mongo:host') || '127.0.0.1',
|
||||
ask: isUriNotSpecified,
|
||||
},
|
||||
{
|
||||
name: 'mongo:port',
|
||||
description: 'Host port of your MongoDB instance',
|
||||
default: nconf.get('mongo:port') || 27017,
|
||||
default: nconf.get('mongo:port') || nconf.get('defaults:mongo:port') || 27017,
|
||||
ask: isUriNotSpecified,
|
||||
},
|
||||
{
|
||||
name: 'mongo:username',
|
||||
description: 'MongoDB username',
|
||||
default: nconf.get('mongo:username') || '',
|
||||
default: nconf.get('mongo:username') || nconf.get('defaults:mongo:username') || '',
|
||||
ask: isUriNotSpecified,
|
||||
},
|
||||
{
|
||||
name: 'mongo:password',
|
||||
description: 'Password of your MongoDB database',
|
||||
default: nconf.get('mongo:password') || '',
|
||||
default: nconf.get('mongo:password') || nconf.get('defaults:mongo:password') || '',
|
||||
hidden: true,
|
||||
ask: isUriNotSpecified,
|
||||
before: function (value) { value = value || nconf.get('mongo:password') || ''; return value; },
|
||||
@@ -54,7 +54,7 @@ mongoModule.questions = [
|
||||
{
|
||||
name: 'mongo:database',
|
||||
description: 'MongoDB database name',
|
||||
default: nconf.get('mongo:database') || 'nodebb',
|
||||
default: nconf.get('mongo:database') || nconf.get('defaults:mongo:database') || 'nodebb',
|
||||
ask: isUriNotSpecified,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -13,34 +13,34 @@ postgresModule.questions = [
|
||||
{
|
||||
name: 'postgres:host',
|
||||
description: 'Host IP or address of your PostgreSQL instance',
|
||||
default: nconf.get('postgres:host') || '127.0.0.1',
|
||||
default: nconf.get('postgres:host') || nconf.get('defaults:postgres:host') || '127.0.0.1',
|
||||
},
|
||||
{
|
||||
name: 'postgres:port',
|
||||
description: 'Host port of your PostgreSQL instance',
|
||||
default: nconf.get('postgres:port') || 5432,
|
||||
default: nconf.get('postgres:port') || nconf.get('defaults:postgres:port') || 5432,
|
||||
},
|
||||
{
|
||||
name: 'postgres:username',
|
||||
description: 'PostgreSQL username',
|
||||
default: nconf.get('postgres:username') || '',
|
||||
default: nconf.get('postgres:username') || nconf.get('defaults:postgres:username') || '',
|
||||
},
|
||||
{
|
||||
name: 'postgres:password',
|
||||
description: 'Password of your PostgreSQL database',
|
||||
hidden: true,
|
||||
default: nconf.get('postgres:password') || '',
|
||||
default: nconf.get('postgres:password') || nconf.get('defaults:postgres:password') || '',
|
||||
before: function (value) { value = value || nconf.get('postgres:password') || ''; return value; },
|
||||
},
|
||||
{
|
||||
name: 'postgres:database',
|
||||
description: 'PostgreSQL database name',
|
||||
default: nconf.get('postgres:database') || 'nodebb',
|
||||
default: nconf.get('postgres:database') || nconf.get('defaults:postgres:database') || 'nodebb',
|
||||
},
|
||||
{
|
||||
name: 'postgres:ssl',
|
||||
description: 'Enable SSL for PostgreSQL database access',
|
||||
default: nconf.get('postgres:ssl') || false,
|
||||
default: nconf.get('postgres:ssl') || nconf.get('defaults:postgres:ssl') || false,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -11,24 +11,24 @@ redisModule.questions = [
|
||||
{
|
||||
name: 'redis:host',
|
||||
description: 'Host IP or address of your Redis instance',
|
||||
default: nconf.get('redis:host') || '127.0.0.1',
|
||||
default: nconf.get('redis:host') || nconf.get('defaults:redis:host') || '127.0.0.1',
|
||||
},
|
||||
{
|
||||
name: 'redis:port',
|
||||
description: 'Host port of your Redis instance',
|
||||
default: nconf.get('redis:port') || 6379,
|
||||
default: nconf.get('redis:port') || nconf.get('defaults:redis:port') || 6379,
|
||||
},
|
||||
{
|
||||
name: 'redis:password',
|
||||
description: 'Password of your Redis database',
|
||||
hidden: true,
|
||||
default: nconf.get('redis:password') || '',
|
||||
default: nconf.get('redis:password') || nconf.get('defaults:redis:password') || '',
|
||||
before: function (value) { value = value || nconf.get('redis:password') || ''; return value; },
|
||||
},
|
||||
{
|
||||
name: 'redis:database',
|
||||
description: 'Which database to use (0..n)',
|
||||
default: nconf.get('redis:database') || 0,
|
||||
default: nconf.get('redis:database') || nconf.get('defaults:redis:database') || 0,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ User.validateEmail = async function (socket, uids) {
|
||||
if (email) {
|
||||
await user.setUserField(uid, 'email', email);
|
||||
}
|
||||
await user.email.confirmByUid(uid);
|
||||
await user.email.confirmByUid(uid, socket.uid);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -89,9 +89,9 @@ module.exports = function (SocketPosts) {
|
||||
privileges.users.isAdministrator(uid),
|
||||
privileges.users.isModerator(uid, cids),
|
||||
]);
|
||||
const cidToAllowed = _.zip(uniqCids, canRead);
|
||||
const cidToAllowed = _.zipObject(uniqCids, canRead);
|
||||
const checks = cids.map(
|
||||
(cid, index) => isAdmin || isMod[index] || (cidToAllowed[index] && !!meta.config.votesArePublic)
|
||||
(cid, index) => isAdmin || isMod[index] || (cidToAllowed[cid] && !!meta.config.votesArePublic)
|
||||
);
|
||||
return isArray ? checks : checks[0];
|
||||
}
|
||||
|
||||
@@ -214,10 +214,11 @@ UserEmail.confirmByCode = async function (code, sessionId) {
|
||||
};
|
||||
|
||||
// confirm uid's email via ACP
|
||||
UserEmail.confirmByUid = async function (uid) {
|
||||
UserEmail.confirmByUid = async function (uid, callerUid = 0) {
|
||||
if (!(parseInt(uid, 10) > 0)) {
|
||||
throw new Error('[[error:invalid-uid]]');
|
||||
}
|
||||
callerUid = callerUid || uid;
|
||||
const currentEmail = await user.getUserField(uid, 'email');
|
||||
if (!currentEmail) {
|
||||
throw new Error('[[error:invalid-email]]');
|
||||
@@ -241,7 +242,7 @@ UserEmail.confirmByUid = async function (uid) {
|
||||
db.sortedSetAddBulk([
|
||||
['email:uid', uid, currentEmail.toLowerCase()],
|
||||
['email:sorted', 0, `${currentEmail.toLowerCase()}:${uid}`],
|
||||
[`user:${uid}:emails`, Date.now(), `${currentEmail}:${Date.now()}`],
|
||||
[`user:${uid}:emails`, Date.now(), `${currentEmail}:${Date.now()}:${callerUid}`],
|
||||
]),
|
||||
user.setUserField(uid, 'email:confirmed', 1),
|
||||
groups.join('verified-users', uid),
|
||||
|
||||
@@ -60,13 +60,24 @@ module.exports = function (User) {
|
||||
|
||||
User.getHistory = async function (set) {
|
||||
const data = await db.getSortedSetRevRangeWithScores(set, 0, -1);
|
||||
return data.map((set) => {
|
||||
data.forEach((set) => {
|
||||
set.timestamp = set.score;
|
||||
set.timestampISO = utils.toISOString(set.score);
|
||||
set.value = validator.escape(String(set.value.split(':')[0]));
|
||||
const parts = set.value.split(':');
|
||||
set.value = validator.escape(String(parts[0]));
|
||||
set.byUid = validator.escape(String(parts[2] || ''));
|
||||
delete set.score;
|
||||
return set;
|
||||
});
|
||||
|
||||
const uids = _.uniq(data.map(d => d && d.byUid).filter(Boolean));
|
||||
const usersData = await User.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture']);
|
||||
const uidToUser = _.zipObject(uids, usersData);
|
||||
data.forEach((d) => {
|
||||
if (d.byUid) {
|
||||
d.byUser = uidToUser[d.byUid];
|
||||
}
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
async function getFlagMetadata(flags) {
|
||||
|
||||
@@ -49,7 +49,7 @@ module.exports = function (User) {
|
||||
if (field === 'email') {
|
||||
return await updateEmail(updateUid, data.email);
|
||||
} else if (field === 'username') {
|
||||
return await updateUsername(updateUid, data.username);
|
||||
return await updateUsername(updateUid, data.username, uid);
|
||||
} else if (field === 'fullname') {
|
||||
return await updateFullname(updateUid, data.fullname);
|
||||
}
|
||||
@@ -249,7 +249,7 @@ module.exports = function (User) {
|
||||
}
|
||||
}
|
||||
|
||||
async function updateUsername(uid, newUsername) {
|
||||
async function updateUsername(uid, newUsername, callerUid) {
|
||||
if (!newUsername) {
|
||||
return;
|
||||
}
|
||||
@@ -262,7 +262,7 @@ module.exports = function (User) {
|
||||
await Promise.all([
|
||||
updateUidMapping('username', uid, newUsername, userData.username),
|
||||
updateUidMapping('userslug', uid, newUserslug, userData.userslug),
|
||||
db.sortedSetAdd(`user:${uid}:usernames`, now, `${newUsername}:${now}`),
|
||||
db.sortedSetAdd(`user:${uid}:usernames`, now, `${newUsername}:${now}:${callerUid}`),
|
||||
]);
|
||||
await db.sortedSetRemove('username:sorted', `${userData.username.toLowerCase()}:${uid}`);
|
||||
await db.sortedSetAdd('username:sorted', 0, `${newUsername.toLowerCase()}:${uid}`);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div>
|
||||
<label for="agree-terms">[[register:terms-of-use]]</label>
|
||||
<label class="form-label" for="agree-terms">[[register:terms-of-use]]</label>
|
||||
<div class="tos">{termsOfUse}</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="agree-terms" id="agree-terms">
|
||||
|
||||
Reference in New Issue
Block a user