Compare commits
9 Commits
socket-not
...
v0.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45c62da51c | ||
|
|
f88d7e6170 | ||
|
|
13b4aa2282 | ||
|
|
995fb22600 | ||
|
|
5de430deb5 | ||
|
|
e1c9bd7229 | ||
|
|
eabbc81b40 | ||
|
|
e29aa5abf8 | ||
|
|
cde7168f9c |
@@ -1,30 +0,0 @@
|
||||
# Save as .codeclimate.yml (note leading .) in project root directory
|
||||
version: "2"
|
||||
languages:
|
||||
Ruby: false
|
||||
JavaScript: true
|
||||
PHP: false
|
||||
checks:
|
||||
file-lines:
|
||||
config:
|
||||
threshold: 500
|
||||
method-lines:
|
||||
config:
|
||||
threshold: 75
|
||||
method-complexity:
|
||||
config:
|
||||
threshold: 10
|
||||
similar-code:
|
||||
config:
|
||||
threshold: 65
|
||||
plugins:
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
javascript:
|
||||
mass_threshold: 110
|
||||
count_threshold: 3
|
||||
exclude_paths:
|
||||
- "public/vendor/*"
|
||||
- "test/*"
|
||||
@@ -1,10 +0,0 @@
|
||||
.*
|
||||
logs
|
||||
test
|
||||
node_modules
|
||||
commitlint.config.js
|
||||
nodebb.bat
|
||||
renovate.json
|
||||
*.yml
|
||||
*.md
|
||||
Dockerfile
|
||||
@@ -1,8 +1,4 @@
|
||||
root = true
|
||||
|
||||
[{*.js, *.css, *.tpl, *.json}]
|
||||
[*.js, *.css, *.tpl]
|
||||
indent_style = tab
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = false
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
node_modules/
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.project
|
||||
.vagrant
|
||||
.DS_Store
|
||||
logs/
|
||||
/public/templates
|
||||
/public/uploads
|
||||
/public/vendor
|
||||
/public/src/modules/string.js
|
||||
.idea/
|
||||
.vscode/
|
||||
*.ipr
|
||||
*.iws
|
||||
/coverage
|
||||
/build
|
||||
.eslintrc
|
||||
test/files
|
||||
*.min.js
|
||||
install/docker/
|
||||
13
.gitattributes
vendored
@@ -1,13 +0,0 @@
|
||||
# These files are text and should be normalized (convert crlf => lf)
|
||||
*.json text
|
||||
*.css text
|
||||
*.less text
|
||||
*.tpl text
|
||||
*.html text
|
||||
*.js text
|
||||
*.md text
|
||||
|
||||
# Images should be treated as binary
|
||||
# (binary is a macro for -text -diff)
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
69
.github/CONTRIBUTING.md
vendored
@@ -1,69 +0,0 @@
|
||||
# Submitting a Pull Request to NodeBB?
|
||||
|
||||
First of all, thank you! Before submission, please run `npm test` to lint and run the automated NodeBB tests. If everything passes, you're good to go. If you have any errors, please fix them and re-run `npm test` to make sure there aren't any others.
|
||||
|
||||
## Styleguide and linting
|
||||
|
||||
NodeBB mostly conforms to the [AirBnB Javascript style guide](https://github.com/airbnb/javascript#readme). If you're running into a lot of ESlint errors, you may want to install an editor plugin to display them in real time.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
Thank you for considering contributing to NodeBB. **Before you are able to submit a pull request, please take a moment to read our [contributor license agreement](https://gist.github.com/psychobunny/65946d7aa8854b12fab9)** and agree to it on the pull request page on GitHub. In summary, signing this document means that 1) you own the code that you are contributing and 2) you give permission to NodeBB Inc. to license the code to others. This agreement applies to any repository under the NodeBB organization.
|
||||
|
||||
If you are writing contributions as part of employment from another company / individual, then your employer will need to sign a separate agreement. Please [contact us](mailto:accounts@nodebb.org) so that we can send this additional agreement to your employer.
|
||||
|
||||
|
||||
# Having problems installing NodeBB?
|
||||
|
||||
Chances are somebody has run into this problem before. After consulting our [documentation](https://docs.nodebb.org/installing/os/), please head over to our [community support forum](https://community.nodebb.org) for advice.
|
||||
|
||||
# Found a Security Vulnerability?
|
||||
|
||||
If you believe you have identified a security vulnerability with NodeBB, report it as soon as possible via email to **security@nodebb.org**.
|
||||
A member of the NodeBB security team will respond to the issue.
|
||||
Please do not post it to the public bug tracker.
|
||||
|
||||
# Issues & Bugs
|
||||
|
||||
Thanks for reporting an issue with NodeBB! Please follow these guidelines in order to streamline the debugging process. The more guidelines you follow, the easier it will be for us to reproduce your problem.
|
||||
|
||||
In general, if we can't reproduce it, we can't fix it!
|
||||
|
||||
## Try the latest version of NodeBB
|
||||
|
||||
There is a chance that the issue you are experiencing may have already been fixed.
|
||||
|
||||
## Provide the NodeBB version number and git hash
|
||||
|
||||
You can find the NodeBB version number in the Admin Control Panel (ACP), as well as the first line output to the shell when running NodeBB
|
||||
|
||||
``` plaintext
|
||||
3/4 12:38:57 [10752] - info: NodeBB v1.4.5 Copyright (C) 2013-2017 NodeBB Inc.
|
||||
3/4 12:38:57 [10752] - info: This program comes with ABSOLUTELY NO WARRANTY.
|
||||
3/4 12:38:57 [10752] - info: This is free software, and you are welcome to redistribute it under certain conditions.
|
||||
```
|
||||
|
||||
If you are running NodeBB via git, it is also helpful to let the maintainers know what commit hash you are on. To find the commit hash, execute the following command:
|
||||
|
||||
``` bash
|
||||
$ cd /path/to/my/nodebb
|
||||
$ git rev-parse HEAD
|
||||
```
|
||||
|
||||
If you have downloaded the `.zip` or `.tar.gz` packages from GitHub (or elsewhere), please let us know.
|
||||
|
||||
## Provide theme versions if issue is related to the theme/display
|
||||
Use `npm ls` to list the versions of the theme you're using. In this example, we're running the Persona theme, which depends on the Vanilla theme.
|
||||
|
||||
``` bash
|
||||
$ npm ls nodebb-theme-vanilla nodebb-theme-persona
|
||||
nodebb@1.4.3 /path/to/nodebb
|
||||
+-- nodebb-theme-persona@4.2.4
|
||||
`-- nodebb-theme-vanilla@5.2.0
|
||||
```
|
||||
|
||||
## Attempt to use `git bisect`
|
||||
|
||||
If you have installed NodeBB via GitHub clone, are familiar with utilising git, and are willing to help us narrow down the specific commit that causes a bug, consider running `git bisect`.
|
||||
|
||||
A full guide can be found here: [Debugging with Git/Binary Search](http://git-scm.com/book/en/Git-Tools-Debugging-with-Git#Binary-Search)
|
||||
81
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,81 +0,0 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Github Issues are for bug reports and feature requests only
|
||||
Please visit https://community.nodebb.org for other support
|
||||
Found a security exploit? Please email us at security@nodebb.org instead for immediate attention
|
||||
### → DO NOT SUBMIT VULNERABILITIES TO THE PUBLIC BUG TRACKER ←
|
||||
- type: input
|
||||
attributes:
|
||||
label: NodeBB version
|
||||
placeholder: e.g. v2.2.0
|
||||
- type: input
|
||||
attributes:
|
||||
label: NodeBB git hash
|
||||
description: to find your git hash, execute `git rev-parse HEAD` from the main NodeBB directory
|
||||
placeholder: e.g. 783250ee6f8c51cdc243ce3b8d9f1a080517247e
|
||||
- type: input
|
||||
attributes:
|
||||
label: NodeJS version
|
||||
placeholder: e.g. v16.15.1
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Installed NodeBB plugins
|
||||
description: to find installed plugins run `./nodebb plugins`
|
||||
placeholder: |
|
||||
e.g.
|
||||
* nodebb-plugin-2factor@5.0.1 (installed, disabled)
|
||||
* nodebb-plugin-composer-default@8.0.0 (installed, enabled)
|
||||
* nodebb-plugin-dbsearch@5.1.5 (installed, disabled)
|
||||
* nodebb-plugin-emoji@4.0.4 (installed, enabled)
|
||||
* nodebb-plugin-emoji-android@3.0.0 (installed, enabled)
|
||||
* nodebb-plugin-markdown@10.0.0 (installed, enabled)
|
||||
* nodebb-plugin-mentions@3.0.11 (installed, enabled)
|
||||
* nodebb-plugin-spam-be-gone@1.0.0 (installed, disabled)
|
||||
* nodebb-rewards-essentials@0.2.1 (installed, enabled)
|
||||
* nodebb-theme-lavender@6.0.0 (installed, disabled)
|
||||
* nodebb-theme-persona@12.0.11 (installed, enabled)
|
||||
* nodebb-theme-slick@2.0.2 (installed, disabled)
|
||||
* nodebb-theme-vanilla@12.1.18 (installed, disabled)
|
||||
* nodebb-widget-essentials@6.0.0 (installed, enabled)
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Database type
|
||||
multiple: true
|
||||
options:
|
||||
- MongoDB
|
||||
- Redis
|
||||
- PostgreSQL
|
||||
- type: input
|
||||
attributes:
|
||||
label: Database version
|
||||
description: "`mongod --version`, `redis-server --version`, or `postgres --version`"
|
||||
placeholder: e.g. v5.0.9
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Exact steps to cause this issue
|
||||
placeholder: |
|
||||
1. First I did this...
|
||||
2. Then, I clicked on this item...
|
||||
|
||||
A quick note: MP4 and MOV formatted video files are now allowed to be uploaded to GH.
|
||||
Please upload if reproduction steps are hard to describe or reproduce reliably.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What you expected
|
||||
placeholder: e.g. I expected *abc* to *xyz*
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What happened instead
|
||||
placeholder: e.g. Instead, I got *zyx* and NodeBB set fire to my house
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: Any additional context about the issue you're encountering
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "**Thank you!**"
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Community Forum
|
||||
url: https://community.nodebb.org
|
||||
about: Github Issues are for bug reports and feature requests only, please use community forum for other support
|
||||
24
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -1,24 +0,0 @@
|
||||
name: Feature Request
|
||||
description: Suggest a new feature
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Github Issues are for bug reports and feature requests only
|
||||
Please visit https://community.nodebb.org for other support
|
||||
Found a security exploit? Please email us at security@nodebb.org instead for immediate attention
|
||||
### → DO NOT SUBMIT VULNERABILITIES TO THE PUBLIC BUG TRACKER ←
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Community forum reference
|
||||
description: If this feature was already discussed on the Community Forum link it here
|
||||
placeholder: https://community.nodebb.org/topic/0/example-feature-request
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: "**Thank you!**"
|
||||
9
.github/SECURITY.md
vendored
@@ -1,9 +0,0 @@
|
||||
# Reporting a security vulnerability
|
||||
|
||||
NodeBB's security policy is based around a private bug bounty program. Users are invited to explore NodeBB for vulnerabilities, and report them to the NodeBB team so that they can be patched.
|
||||
|
||||
If you have found a security vulnerability, **do not post it onto our GitHub tracker**. Some security vulnerabilities are quite severe and discretion is recommended. Email the NodeBB Security Team at security@nodebb.org, instead, even if you are not sure whether something qualifies.
|
||||
|
||||
# Bug Bounty Program
|
||||
|
||||
Security vulnerability reports may be eligible for a bounty based on severity and confirmation from NodeBB team members. For full details regarding our bug bounty program, including the bounty amounts, please consult the [dedicated page for our Bug Bounty Program](https://nodebb.org/bounty).
|
||||
61
.github/workflows/docker.yml
vendored
@@ -1,61 +0,0 @@
|
||||
name: Run Docker
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'v*.x'
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
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 }}
|
||||
|
||||
- name: Build and push Docker images
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
212
.github/workflows/test.yaml
vendored
@@ -1,212 +0,0 @@
|
||||
name: Lint and test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
permissions:
|
||||
checks: write # for coverallsapp/github-action to create new checks
|
||||
contents: read # for actions/checkout to fetch code
|
||||
name: Lint and test
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node: [18, 20]
|
||||
database: [mongo-dev, mongo, redis, postgres]
|
||||
include:
|
||||
# only run coverage once
|
||||
- os: ubuntu-latest
|
||||
node: 18
|
||||
coverage: true
|
||||
# test under development once
|
||||
- database: mongo-dev
|
||||
test_env: development
|
||||
# only run eslint once
|
||||
- os: ubuntu-latest
|
||||
node: 18
|
||||
database: mongo-dev
|
||||
lint: true
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
TEST_ENV: ${{ matrix.test_env || 'production' }}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: 'postgres:16-alpine'
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
# Set health checks to wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps port 5432 on service container to the host
|
||||
- 5432:5432
|
||||
|
||||
redis:
|
||||
image: 'redis:7.2.3'
|
||||
# Set health checks to wait until redis has started
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps port 6379 on service container to the host
|
||||
- 6379:6379
|
||||
|
||||
mongo:
|
||||
image: 'mongo:7.0'
|
||||
ports:
|
||||
# Maps port 27017 on service container to the host
|
||||
- 27017:27017
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: cp install/package.json package.json
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: NPM Install
|
||||
uses: bahmutov/npm-install@v1
|
||||
with:
|
||||
useLockFile: false
|
||||
|
||||
- name: Setup on MongoDB
|
||||
if: startsWith(matrix.database, 'mongo')
|
||||
env:
|
||||
SETUP: >-
|
||||
{
|
||||
"url": "http://127.0.0.1:4567",
|
||||
"secret": "abcdef",
|
||||
"admin:username": "admin",
|
||||
"admin:email": "test@example.org",
|
||||
"admin:password": "hAN3Eg8W",
|
||||
"admin:password:confirm": "hAN3Eg8W",
|
||||
|
||||
"database": "mongo",
|
||||
"mongo:host": "127.0.0.1",
|
||||
"mongo:port": 27017,
|
||||
"mongo:username": "",
|
||||
"mongo:password": "",
|
||||
"mongo:database": "nodebb"
|
||||
}
|
||||
CI: >-
|
||||
{
|
||||
"host": "127.0.0.1",
|
||||
"port": 27017,
|
||||
"database": "ci_test"
|
||||
}
|
||||
run: |
|
||||
node app --setup="${SETUP}" --ci="${CI}"
|
||||
|
||||
- name: Setup on PostgreSQL
|
||||
if: startsWith(matrix.database, 'postgres')
|
||||
env:
|
||||
SETUP: >-
|
||||
{
|
||||
"url": "http://127.0.0.1:4567",
|
||||
"secret": "abcdef",
|
||||
"admin:username": "admin",
|
||||
"admin:email": "test@example.org",
|
||||
"admin:password": "hAN3Eg8W",
|
||||
"admin:password:confirm": "hAN3Eg8W",
|
||||
|
||||
"database": "postgres",
|
||||
"postgres:host": "127.0.0.1",
|
||||
"postgres:port": 5432,
|
||||
"postgres:username": "postgres",
|
||||
"postgres:password": "postgres",
|
||||
"postgres:database": "nodebb"
|
||||
}
|
||||
CI: >-
|
||||
{
|
||||
"host": "127.0.0.1",
|
||||
"database": "ci_test",
|
||||
"port": 5432,
|
||||
"username": "postgres",
|
||||
"password": "postgres"
|
||||
}
|
||||
run: |
|
||||
node -e "const { Client } = require('pg'); const c = new Client({ host: '127.0.0.1', port: 5432, user: 'postgres', password: 'postgres' }); c.connect().then(() => c.query('CREATE DATABASE nodebb')).then(() => c.query('CREATE DATABASE ci_test')).then(() => c.end())"
|
||||
node app --setup="${SETUP}" --ci="${CI}"
|
||||
|
||||
- name: Setup on Redis
|
||||
if: startsWith(matrix.database, 'redis')
|
||||
env:
|
||||
SETUP: >-
|
||||
{
|
||||
"url": "http://127.0.0.1:4567/forum",
|
||||
"secret": "abcdef",
|
||||
"admin:username": "admin",
|
||||
"admin:email": "test@example.org",
|
||||
"admin:password": "hAN3Eg8W",
|
||||
"admin:password:confirm": "hAN3Eg8W",
|
||||
|
||||
"database": "redis",
|
||||
"redis:host": "127.0.0.1",
|
||||
"redis:port": 6379,
|
||||
"redis:password": "",
|
||||
"redis:database": 0
|
||||
}
|
||||
CI: >-
|
||||
{
|
||||
"host": "127.0.0.1",
|
||||
"database": 1,
|
||||
"port": 6379
|
||||
}
|
||||
run: |
|
||||
node app --setup="${SETUP}" --ci="${CI}"
|
||||
|
||||
- name: Run ESLint
|
||||
if: matrix.lint
|
||||
run: npm run lint
|
||||
|
||||
- name: Node tests
|
||||
run: npm test
|
||||
|
||||
- name: Extract coverage info
|
||||
run: npm run coverage
|
||||
|
||||
- name: Test coverage
|
||||
uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 # v2.2.3
|
||||
if: matrix.coverage
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
flag-name: ${{ matrix.os }}-node-${{ matrix.node }}-db-${{ matrix.database }}
|
||||
parallel: true
|
||||
|
||||
finish:
|
||||
permissions:
|
||||
checks: write # for coverallsapp/github-action to create new checks
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Coveralls Finished
|
||||
uses: coverallsapp/github-action@3dfc5567390f6fa9267c0ee9c251e4c8c3f18949 # v2.2.3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
parallel-finished: true
|
||||
57
.gitignore
vendored
@@ -1,16 +1,14 @@
|
||||
dist/
|
||||
yarn.lock
|
||||
npm-debug.log
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
config.json
|
||||
jsconfig.json
|
||||
public/src/nodebb.min.js
|
||||
!src/views/config.json
|
||||
public/config.json
|
||||
public/css/*.css
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.project
|
||||
.idea
|
||||
*.swp
|
||||
Vagrantfile
|
||||
.vagrant
|
||||
@@ -18,57 +16,12 @@ provision.sh
|
||||
*.komodoproject
|
||||
.DS_Store
|
||||
feeds/recent.rss
|
||||
.eslintcache
|
||||
.svn
|
||||
|
||||
logs/
|
||||
# winston?
|
||||
error.log
|
||||
events.log
|
||||
|
||||
pidfile
|
||||
|
||||
# templates
|
||||
/public/templates
|
||||
|
||||
/public/uploads
|
||||
/test/uploads
|
||||
|
||||
# compiled files
|
||||
/public/stylesheet.css
|
||||
/public/admin.css
|
||||
/public/nodebb.min.js
|
||||
/public/nodebb.min.js.map
|
||||
/public/acp.min.js
|
||||
/public/acp.min.js.map
|
||||
/public/installer.css
|
||||
/public/installer.min.js
|
||||
/public/bootstrap.min.css
|
||||
/public/logo.png
|
||||
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
|
||||
*.iml
|
||||
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
## Transifex
|
||||
tx.exe
|
||||
.transifexrc
|
||||
|
||||
##Coverage output
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
*.log
|
||||
test/files/normalise.jpg.png
|
||||
test/files/normalise-resized.jpg
|
||||
package-lock.json
|
||||
/package.json
|
||||
*.mongodb
|
||||
link-plugins.sh
|
||||
test.sh
|
||||
|
||||
.docker/
|
||||
1
.husky/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
_
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit $1
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install lint-staged
|
||||
17
.jsbeautifyrc
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"indent_size": 4,
|
||||
"indent_char": " ",
|
||||
"indent_level": 0,
|
||||
"indent_with_tabs": true,
|
||||
"preserve_newlines": true,
|
||||
"max_preserve_newlines": 10,
|
||||
"jslint_happy": true,
|
||||
"brace_style": "collapse",
|
||||
"keep_array_indentation": false,
|
||||
"keep_function_indentation": false,
|
||||
"space_before_conditional": true,
|
||||
"break_chained_methods": false,
|
||||
"eval_code": false,
|
||||
"unescape_strings": false,
|
||||
"wrap_line_length": 0
|
||||
}
|
||||
86
.jshintrc
Normal file
@@ -0,0 +1,86 @@
|
||||
{
|
||||
// JSHint Default Configuration File (as on JSHint website)
|
||||
// See http://jshint.com/docs/ for more details
|
||||
|
||||
"maxerr" : 50, // {int} Maximum error before stopping
|
||||
|
||||
// Enforcing
|
||||
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
|
||||
"camelcase" : false, // true: Identifiers must be in camelCase
|
||||
"curly" : true, // true: Require {} for every new block or scope
|
||||
"eqeqeq" : true, // true: Require triple equals (===) for comparison
|
||||
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
|
||||
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
|
||||
"indent" : 4, // {int} Number of spaces to use for indentation
|
||||
"latedef" : false, // true: Require variables/functions to be defined before being used
|
||||
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
|
||||
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
|
||||
"noempty" : true, // true: Prohibit use of empty blocks
|
||||
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
|
||||
"plusplus" : false, // true: Prohibit use of `++` & `--`
|
||||
"quotmark" : false, // Quotation mark consistency:
|
||||
// false : do nothing (default)
|
||||
// true : ensure whatever is used is consistent
|
||||
// "single" : require single quotes
|
||||
// "double" : require double quotes
|
||||
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
|
||||
"unused" : false, // true: Require all defined variables be used TODO: Set this to true, update codebase.
|
||||
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
|
||||
"trailing" : false, // true: Prohibit trailing whitespaces
|
||||
"maxparams" : false, // {int} Max number of formal params allowed per function
|
||||
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
|
||||
"maxstatements" : false, // {int} Max number statements per function
|
||||
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
|
||||
"maxlen" : false, // {int} Max number of characters per line
|
||||
|
||||
// Relaxing
|
||||
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
|
||||
"boss" : false, // true: Tolerate assignments where comparisons would be expected
|
||||
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
|
||||
"eqnull" : false, // true: Tolerate use of `== null`
|
||||
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
|
||||
"esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
|
||||
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
|
||||
// (ex: `for each`, multiple try/catch, function expression…)
|
||||
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
|
||||
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
|
||||
"funcscope" : false, // true: Tolerate defining variables inside control statements"
|
||||
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
|
||||
"iterator" : false, // true: Tolerate using the `__iterator__` property
|
||||
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
|
||||
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
|
||||
"laxcomma" : false, // true: Tolerate comma-first style coding
|
||||
"loopfunc" : false, // true: Tolerate functions being defined in loops
|
||||
"multistr" : false, // true: Tolerate multi-line strings
|
||||
"proto" : false, // true: Tolerate using the `__proto__` property
|
||||
"scripturl" : false, // true: Tolerate script-targeted URLs
|
||||
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
|
||||
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
|
||||
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
|
||||
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
|
||||
"validthis" : false, // true: Tolerate using this in a non-constructor function
|
||||
|
||||
// Environments
|
||||
"browser" : true, // Web Browser (window, document, etc)
|
||||
"couch" : false, // CouchDB
|
||||
"devel" : true, // Development/debugging (alert, confirm, etc)
|
||||
"dojo" : false, // Dojo Toolkit
|
||||
"jquery" : true, // jQuery
|
||||
"mootools" : false, // MooTools
|
||||
"node" : true, // Node.js
|
||||
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
|
||||
"prototypejs" : false, // Prototype and Scriptaculous
|
||||
"rhino" : false, // Rhino
|
||||
"worker" : false, // Web Workers
|
||||
"wsh" : false, // Windows Scripting Host
|
||||
"yui" : false, // Yahoo User Interface
|
||||
|
||||
// Legacy
|
||||
"nomen" : false, // true: Prohibit dangling `_` in variables
|
||||
"onevar" : false, // true: Allow only one `var` statement per function
|
||||
"passfail" : false, // true: Stop on first error
|
||||
"white" : false, // true: Check against strict whitespace and indentation rules
|
||||
|
||||
// Custom Globals
|
||||
"globals" : {} // additional predefined global variables
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
reporter: dot
|
||||
timeout: 25000
|
||||
exit: true
|
||||
bail: true
|
||||
12
.travis.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
services:
|
||||
- redis-server
|
||||
before_install:
|
||||
- npm i --production
|
||||
- node app --setup="{\"base_url\":\"http://127.0.0.1\",\"port\":4567,\"use_port\":false,\"secret\":\"abcdef\",\"bind_address\":\"0.0.0.0\",\"database\":\"redis\",\"redis:host\":\"127.0.0.1\",\"redis:port\":6379,\"redis:password\":\"\",\"redis:database\":0,\"admin:username\":\"admin\",\"admin:email\":\"test@example.org\",\"admin:password\":\"abcdef\",\"admin:password:confirm\":\"abcdef\"}" --ci="{\"host\":\"127.0.0.1\",\"port\":6379,\"database\":0}"
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
4386
.tx/config
4
ATTRIBUTION.md
Normal file
@@ -0,0 +1,4 @@
|
||||
## Sound Assets
|
||||
|
||||
* "Waterdrop" by [Porphyr](freesound.org/people/Porphyr) / [CC BY 3.0](creativecommons.org/licenses/by/3.0)
|
||||
* "SweetAlertSound2.wav" by [KIZILSUNGUR](http://www.freesound.org/people/KIZILSUNGUR/) / [Public Domain / CC 0](http://creativecommons.org/publicdomain/zero/1.0/)
|
||||
9984
CHANGELOG.md
51
Dockerfile
@@ -1,51 +0,0 @@
|
||||
FROM --platform=$BUILDPLATFORM node:lts as npm
|
||||
|
||||
RUN mkdir -p /usr/src/build && \
|
||||
chown -R node:node /usr/src/build
|
||||
WORKDIR /usr/src/build
|
||||
|
||||
ARG NODE_ENV
|
||||
ENV NODE_ENV $NODE_ENV
|
||||
|
||||
COPY --chown=node:node install/package.json /usr/src/build/package.json
|
||||
|
||||
USER node
|
||||
|
||||
RUN npm install --omit=dev
|
||||
|
||||
FROM node:lts as rebuild
|
||||
|
||||
ARG BUILDPLATFORM
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
RUN mkdir -p /usr/src/build && \
|
||||
chown -R node:node /usr/src/build
|
||||
|
||||
COPY --from=npm /usr/src/build /usr/src/build
|
||||
|
||||
RUN if [ $BUILDPLATFORM != $TARGETPLATFORM ]; then \
|
||||
npm rebuild && \
|
||||
npm cache clean --force; fi
|
||||
|
||||
FROM node:lts-slim as run
|
||||
|
||||
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
|
||||
|
||||
EXPOSE 4567
|
||||
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config"]
|
||||
ENTRYPOINT ["./install/docker/entrypoint.sh"]
|
||||
201
Gruntfile.js
@@ -1,201 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const nconf = require('nconf');
|
||||
|
||||
nconf.argv().env({
|
||||
separator: '__',
|
||||
});
|
||||
const winston = require('winston');
|
||||
const { fork } = require('child_process');
|
||||
|
||||
const { env } = process;
|
||||
let worker;
|
||||
|
||||
env.NODE_ENV = env.NODE_ENV || 'development';
|
||||
|
||||
const configFile = path.resolve(__dirname, nconf.any(['config', 'CONFIG']) || 'config.json');
|
||||
const prestart = require('./src/prestart');
|
||||
|
||||
prestart.loadConfig(configFile);
|
||||
|
||||
const db = require('./src/database');
|
||||
const plugins = require('./src/plugins');
|
||||
|
||||
module.exports = function (grunt) {
|
||||
const args = [];
|
||||
|
||||
if (!grunt.option('verbose')) {
|
||||
args.push('--log-level=info');
|
||||
nconf.set('log-level', 'info');
|
||||
}
|
||||
prestart.setupWinston();
|
||||
|
||||
grunt.initConfig({
|
||||
watch: {},
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
|
||||
grunt.registerTask('default', ['watch']);
|
||||
|
||||
grunt.registerTask('init', async function () {
|
||||
const done = this.async();
|
||||
let pluginList = [];
|
||||
if (!process.argv.includes('--core')) {
|
||||
await db.init();
|
||||
pluginList = await plugins.getActive();
|
||||
addBaseThemes(pluginList);
|
||||
if (!pluginList.includes('nodebb-plugin-composer-default')) {
|
||||
pluginList.push('nodebb-plugin-composer-default');
|
||||
}
|
||||
if (!pluginList.includes('nodebb-theme-harmony')) {
|
||||
pluginList.push('nodebb-theme-harmony');
|
||||
}
|
||||
}
|
||||
|
||||
const styleUpdated_Client = pluginList.map(p => `node_modules/${p}/*.scss`)
|
||||
.concat(pluginList.map(p => `node_modules/${p}/*.css`))
|
||||
.concat(pluginList.map(p => `node_modules/${p}/+(public|static|scss)/**/*.scss`))
|
||||
.concat(pluginList.map(p => `node_modules/${p}/+(public|static)/**/*.css`));
|
||||
|
||||
const clientUpdated = pluginList.map(p => `node_modules/${p}/+(public|static)/**/*.js`);
|
||||
const serverUpdated = pluginList.map(p => `node_modules/${p}/*.js`)
|
||||
.concat(pluginList.map(p => `node_modules/${p}/+(lib|src)/**/*.js`));
|
||||
|
||||
const templatesUpdated = pluginList.map(p => `node_modules/${p}/+(public|static|templates)/**/*.tpl`);
|
||||
const langUpdated = pluginList.map(p => `node_modules/${p}/+(public|static|languages)/**/*.json`);
|
||||
const interval = 100;
|
||||
grunt.config(['watch'], {
|
||||
styleUpdated: {
|
||||
files: [
|
||||
'public/scss/**/*.scss',
|
||||
...styleUpdated_Client,
|
||||
],
|
||||
options: {
|
||||
interval: interval,
|
||||
},
|
||||
},
|
||||
clientUpdated: {
|
||||
files: [
|
||||
'public/src/**/*.js',
|
||||
'public/vendor/**/*.js',
|
||||
...clientUpdated,
|
||||
'node_modules/benchpressjs/build/benchpress.js',
|
||||
],
|
||||
options: {
|
||||
interval: interval,
|
||||
},
|
||||
},
|
||||
serverUpdated: {
|
||||
files: [
|
||||
'app.js',
|
||||
'install/*.js',
|
||||
'src/**/*.js',
|
||||
'public/src/modules/translator.common.js',
|
||||
'public/src/modules/helpers.common.js',
|
||||
'public/src/utils.common.js',
|
||||
serverUpdated,
|
||||
'!src/upgrades/**',
|
||||
],
|
||||
options: {
|
||||
interval: interval,
|
||||
},
|
||||
},
|
||||
templatesUpdated: {
|
||||
files: [
|
||||
'src/views/**/*.tpl',
|
||||
...templatesUpdated,
|
||||
],
|
||||
options: {
|
||||
interval: interval,
|
||||
},
|
||||
},
|
||||
langUpdated: {
|
||||
files: [
|
||||
'public/language/en-GB/*.json',
|
||||
'public/language/en-GB/**/*.json',
|
||||
...langUpdated,
|
||||
],
|
||||
options: {
|
||||
interval: interval,
|
||||
},
|
||||
},
|
||||
});
|
||||
const build = require('./src/meta/build');
|
||||
if (!grunt.option('skip')) {
|
||||
await build.build(true, { watch: true });
|
||||
}
|
||||
run();
|
||||
done();
|
||||
});
|
||||
|
||||
function run() {
|
||||
if (worker) {
|
||||
worker.kill();
|
||||
}
|
||||
|
||||
const execArgv = [];
|
||||
const inspect = process.argv.find(a => a.startsWith('--inspect'));
|
||||
|
||||
if (inspect) {
|
||||
execArgv.push(inspect);
|
||||
}
|
||||
|
||||
worker = fork('app.js', args, {
|
||||
env,
|
||||
execArgv,
|
||||
});
|
||||
}
|
||||
|
||||
grunt.task.run('init');
|
||||
|
||||
grunt.event.removeAllListeners('watch');
|
||||
grunt.event.on('watch', (action, filepath, target) => {
|
||||
let compiling;
|
||||
if (target === 'styleUpdated') {
|
||||
compiling = ['clientCSS', 'acpCSS'];
|
||||
} else if (target === 'clientUpdated') {
|
||||
compiling = ['js'];
|
||||
} else if (target === 'templatesUpdated') {
|
||||
compiling = ['tpl'];
|
||||
} else if (target === 'langUpdated') {
|
||||
compiling = ['lang'];
|
||||
} else if (target === 'serverUpdated') {
|
||||
// empty require cache
|
||||
const paths = ['./src/meta/build.js', './src/meta/index.js'];
|
||||
paths.forEach(p => delete require.cache[require.resolve(p)]);
|
||||
return run();
|
||||
}
|
||||
|
||||
require('./src/meta/build').build(compiling, { webpack: false }, (err) => {
|
||||
if (err) {
|
||||
winston.error(err.stack);
|
||||
}
|
||||
if (worker) {
|
||||
worker.send({ compiling: compiling });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function addBaseThemes(pluginList) {
|
||||
let themeId = pluginList.find(p => p.includes('nodebb-theme-'));
|
||||
if (!themeId) {
|
||||
return pluginList;
|
||||
}
|
||||
let baseTheme;
|
||||
do {
|
||||
try {
|
||||
baseTheme = require(`${themeId}/theme`).baseTheme;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
if (baseTheme) {
|
||||
pluginList.push(baseTheme);
|
||||
themeId = baseTheme;
|
||||
}
|
||||
} while (baseTheme);
|
||||
return pluginList;
|
||||
}
|
||||
15
NOTES.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## 0.4x Refactor Notes
|
||||
|
||||
Please remove this file after 0.4x (or perhaps organize it so that we can see the history of breaking changes)
|
||||
|
||||
### Immediate Deprecation Notices
|
||||
|
||||
* `action:ajaxifying` is no longer triggered on body but on window instead, in line with other similar hooks.
|
||||
* `filter:server.create_routes` and `filter:admin.create_routes` will have limited support (ajaxify works, but first-load will not). Please have a look at [this plugin](https://github.com/psychobunny/nodebb-plugin-kitchen-sink/blob/master/library.js#L16-L22) for an example on how to create routes in plugins from now on.
|
||||
|
||||
### Upcoming Deprecation Warnings
|
||||
|
||||
* `filter:footer.build` will be deprecated for 0.4x in favour of the widget system (WIP)
|
||||
* templates.setGlobal (server-side only) deprecated in favour of using res.locals
|
||||
* `plugins/fireHook` route will be deprecated for 0.4x
|
||||
* synchronous hooks will be deprecated for 0.4x - we're reducing complexity by removing the `callbacked: true` property in `plugin.json` - just use callbacks.
|
||||
142
README.md
@@ -1,84 +1,58 @@
|
||||
# 
|
||||
|
||||
[](https://github.com/NodeBB/NodeBB/actions/workflows/test.yaml)
|
||||
[](https://coveralls.io/github/NodeBB/NodeBB?branch=master)
|
||||
[](https://codeclimate.com/github/NodeBB/NodeBB)
|
||||
[](https://discord.gg/p6YKPXu7er)
|
||||
|
||||
[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and supports either Redis, MongoDB, or a PostgreSQL database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB takes the best of the modern web: real-time streaming discussions, mobile responsiveness, and rich RESTful read/write APIs, while staying true to the original bulletin board/forum format → categorical hierarchies, local user accounts, and asynchronous messaging.
|
||||
|
||||
NodeBB by itself contains a "common core" of basic functionality, while additional functionality and integrations are enabled through the use of third-party plugins.
|
||||
|
||||
### [Try it now](//try.nodebb.org) | [Documentation](//docs.nodebb.org)
|
||||
|
||||
## Screenshots
|
||||
|
||||
NodeBB's theming engine is highly flexible and does not restrict your design choices. Check out some themed installs in these screenshots below:
|
||||
|
||||
[](http://i.imgur.com/VCoOFyq.png)
|
||||
[](http://i.imgur.com/FLOUuIq.png)
|
||||
[](http://i.imgur.com/Ud1LrfI.png)
|
||||
[](http://i.imgur.com/h6yZ66s.png)
|
||||
[](http://i.imgur.com/o90kVPi.png)
|
||||
[](http://i.imgur.com/AaRRrU2.png)
|
||||
[](http://i.imgur.com/LmHtPho.png)
|
||||
[](http://i.imgur.com/paiJPJk.jpg)
|
||||
|
||||
Our minimalist "Harmony" theme gets you going right away, no coding experience required.
|
||||
|
||||

|
||||
|
||||
## How can I follow along/contribute?
|
||||
|
||||
* If you are a developer, feel free to check out the source and submit pull requests. We also have a wide array of [plugins](http://community.nodebb.org/category/7/nodebb-plugins) which would be a great starting point for learning the codebase.
|
||||
* If you are a designer, [NodeBB needs themes](http://community.nodebb.org/category/10/nodebb-themes)! NodeBB's theming system allows extension of the base templates as well as styling via SCSS or CSS. NodeBB's base theme utilizes [Bootstrap 5](http://getbootstrap.com/) as a frontend toolkit.
|
||||
* If you know languages other than English you can help us translate NodeBB. We use [Transifex](https://explore.transifex.com/nodebb/nodebb/) for internationalization.
|
||||
* Please don't forget to **like**, **follow**, and **star our repo**! Join our growing [community](http://community.nodebb.org) to keep up to date with the latest NodeBB development.
|
||||
|
||||
## Requirements
|
||||
|
||||
NodeBB requires the following software to be installed:
|
||||
|
||||
* A version of Node.js at least 16 or greater ([installation/upgrade instructions](https://github.com/nodesource/distributions))
|
||||
* MongoDB, version 3.6 or greater **or** Redis, version 2.8.9 or greater
|
||||
* If you are using [clustering](https://docs.nodebb.org/configuring/scaling/) you need Redis installed and configured.
|
||||
* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB)
|
||||
|
||||
## Installation
|
||||
|
||||
[Please refer to platform-specific installation documentation](https://docs.nodebb.org/installing/os).
|
||||
If installing via the cloud (or using Docker), [please see cloud-based installation documentation](https://docs.nodebb.org/installing/cloud/).
|
||||
|
||||
## Securing NodeBB
|
||||
|
||||
It is important to ensure that your NodeBB and database servers are secured. Bear these points in mind:
|
||||
|
||||
1. While some distributions set up Redis with a more restrictive configuration, Redis by default listens to all interfaces, which is especially dangerous when a server is open to the public. Some suggestions:
|
||||
* Set `bind_address` to `127.0.0.1` so as to restrict access to the local machine only
|
||||
* Use `requirepass` to secure Redis behind a password (preferably a long one)
|
||||
* Familiarise yourself with [Redis Security](http://redis.io/topics/security)
|
||||
2. Use `iptables` to secure your server from unintended open ports. In Ubuntu, `ufw` provides a friendlier interface to working with `iptables`.
|
||||
* e.g. If your NodeBB is proxied, no ports should be open except 80 (and possibly 22, for SSH access)
|
||||
|
||||
|
||||
## Upgrading NodeBB
|
||||
|
||||
Detailed upgrade instructions are listed in [Upgrading NodeBB](https://docs.nodebb.org/configuring/upgrade/)
|
||||
|
||||
## License
|
||||
|
||||
NodeBB is licensed under the **GNU General Public License v3 (GPL-3)** (http://www.gnu.org/copyleft/gpl.html).
|
||||
|
||||
Interested in a sublicense agreement for use of NodeBB in a non-free/restrictive environment? Contact us at sales@nodebb.org.
|
||||
|
||||
## More Information/Links
|
||||
|
||||
* [Demo](https://try.nodebb.org)
|
||||
* [Developer Community](http://community.nodebb.org)
|
||||
* [Documentation & Installation Instructions](https://docs.nodebb.org)
|
||||
* [Help translate NodeBB](https://explore.transifex.com/nodebb/nodebb/)
|
||||
* [NodeBB Blog](https://nodebb.org/blog)
|
||||
* [Premium Hosting for NodeBB](https://www.nodebb.org/ "NodeBB")
|
||||
* Unofficial IRC community – channel `#nodebb` on Libera.chat
|
||||
* [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
|
||||
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
||||
# <img alt="NodeBB" src="http://i.imgur.com/3yj1n6N.png" />
|
||||
[](https://travis-ci.org/designcreateplay/NodeBB)
|
||||
[](https://david-dm.org/designcreateplay/nodebb)
|
||||
[](https://codeclimate.com/github/designcreateplay/NodeBB)
|
||||
|
||||
**NodeBB Forum Software** is powered by Node.js and built on a Redis database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB is compatible down to IE8 and has many modern features out of the box such as social network integration and streaming discussions.
|
||||
|
||||
* [Get NodeBB](http://www.nodebb.org/ "NodeBB")
|
||||
* [Demo & Meta Discussion](http://community.nodebb.org)
|
||||
* [NodeBB Blog](http://blog.nodebb.org)
|
||||
* [Wiki Guides](https://github.com/designcreateplay/NodeBB/wiki) - includes setup for other platforms
|
||||
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
|
||||
* [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
|
||||
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
||||
* [Get Plugins](http://community.nodebb.org/category/7/nodebb-plugins "NodeBB Plugins")
|
||||
* [Get Themes](http://community.nodebb.org/category/10/nodebb-themes "NodeBB Themes")
|
||||
|
||||
## Screenshots
|
||||
|
||||
[<img src="http://i.imgur.com/FLOUuIqb.png" />](http://i.imgur.com/FLOUuIq.png) [<img src="http://i.imgur.com/Ud1LrfIb.png" />](http://i.imgur.com/Ud1LrfI.png) [<img src="http://i.imgur.com/ZC8W39ab.png" />](http://i.imgur.com/ZC8W39a.png) [<img src="http://i.imgur.com/o90kVPib.png" />](http://i.imgur.com/o90kVPi.png) [<img src="http://i.imgur.com/AaRRrU2b.png" />](http://i.imgur.com/AaRRrU2.png) [<img src="http://i.imgur.com/LmHtPhob.png" />](http://i.imgur.com/LmHtPho.png) [<img src="http://i.imgur.com/paiJPJkb.jpg" />](http://i.imgur.com/paiJPJk.jpg) [<img src="http://i.imgur.com/ZfavPHDb.png" />](http://i.imgur.com/ZfavPHD.png) [<img src="http://i.imgur.com/8vc1Ytc.png" />](http://i.imgur.com/8vc1Ytc.png)
|
||||
|
||||
|
||||
Credit: [Convoe](http://www.convoe.com), [Kano](http://www.kano.me), [Manchester United Forum](http://manutdforums.com/).
|
||||
|
||||
|
||||
## How can I follow along/contribute?
|
||||
|
||||
* Our feature roadmap is hosted on the project wiki's [Version History / Roadmap](https://github.com/designcreateplay/NodeBB/wiki/Version-History-%26-Roadmap)
|
||||
* If you are a developer, feel free to check out the source and submit pull requests. We also have a wide array of [plugins](http://community.nodebb.org/category/7/nodebb-plugins) which would be a great starting point for learning the codebase.
|
||||
* If you are a designer, [NodeBB needs themes](http://community.nodebb.org/category/10/nodebb-themes)! NodeBB's theming system allows extention of the base templates as well as styling via LESS or CSS. NodeBB's base theme utilizes [Bootstrap 3](http://getbootstrap.com/) but themes can choose to use a different framework altogether.
|
||||
* Please don't forget to **like**, **follow**, and **star our repo**! Join our growing [community](http://community.nodebb.org) to keep up to date with the latest NodeBB development.
|
||||
|
||||
## Requirements
|
||||
|
||||
NodeBB requires the following software to be installed:
|
||||
|
||||
* A version of Node.js at least 0.10 or greater
|
||||
* Redis, version 2.6 or greater **or** MongoDB, version 2.4 or greater
|
||||
* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB)
|
||||
|
||||
## Installation
|
||||
|
||||
[Please refer to platform-specific installation documentation](https://github.com/designcreateplay/NodeBB/wiki#wiki-installing-nodebb)
|
||||
|
||||
## Securing NodeBB
|
||||
|
||||
It is important to ensure that your NodeBB and database servers are secured. Bear these points in mind:
|
||||
|
||||
1. While some distributions set up Redis with a more restrictive configuration, Redis by default listens to all interfaces, which is especially dangerous when a server is open to the public. Some suggestions:
|
||||
* Set `bind_address` to `127.0.0.1` so as to restrict access to the local machine only
|
||||
* Use `requirepass` to secure Redis behind a password (preferably a long one)
|
||||
* Familiarise yourself with [Redis Security](http://redis.io/topics/security)
|
||||
2. Use `iptables` to secure your server from unintended open ports. In Ubuntu, `ufw` provides a friendlier interface to working with `iptables`.
|
||||
* e.g. If your NodeBB is proxied, no ports should be open except 80 (and possibly 22, for SSH access)
|
||||
|
||||
## Upgrading NodeBB
|
||||
|
||||
Detailed upgrade instructions are listed in [Upgrading NodeBB](https://github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB)
|
||||
|
||||
391
app.js
@@ -1,82 +1,309 @@
|
||||
/*
|
||||
NodeBB - A better forum platform for the modern web
|
||||
https://github.com/NodeBB/NodeBB/
|
||||
Copyright (C) 2013-2021 NodeBB Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
require('./require-main');
|
||||
|
||||
const nconf = require('nconf');
|
||||
|
||||
nconf.argv().env({
|
||||
separator: '__',
|
||||
});
|
||||
|
||||
const winston = require('winston');
|
||||
const path = require('path');
|
||||
|
||||
const file = require('./src/file');
|
||||
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||
global.env = process.env.NODE_ENV || 'production';
|
||||
|
||||
// Alternate configuration file support
|
||||
const configFile = path.resolve(__dirname, nconf.any(['config', 'CONFIG']) || 'config.json');
|
||||
|
||||
const configExists = file.existsSync(configFile) || (nconf.get('url') && nconf.get('secret') && nconf.get('database'));
|
||||
|
||||
const prestart = require('./src/prestart');
|
||||
|
||||
prestart.loadConfig(configFile);
|
||||
prestart.setupWinston();
|
||||
prestart.versionCheck();
|
||||
winston.verbose('* using configuration stored in: %s', configFile);
|
||||
|
||||
if (!process.send) {
|
||||
// If run using `node app`, log GNU copyright info along with server info
|
||||
winston.info(`NodeBB v${nconf.get('version')} Copyright (C) 2013-${(new Date()).getFullYear()} NodeBB Inc.`);
|
||||
winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
|
||||
winston.info('This is free software, and you are welcome to redistribute it under certain conditions.');
|
||||
winston.info('');
|
||||
}
|
||||
|
||||
if (nconf.get('setup') || nconf.get('install')) {
|
||||
require('./src/cli/setup').setup();
|
||||
} else if (!configExists) {
|
||||
require('./install/web').install(nconf.get('port'));
|
||||
} else if (nconf.get('upgrade')) {
|
||||
require('./src/cli/upgrade').upgrade(true);
|
||||
} else if (nconf.get('reset')) {
|
||||
require('./src/cli/reset').reset({
|
||||
theme: nconf.get('t'),
|
||||
plugin: nconf.get('p'),
|
||||
widgets: nconf.get('w'),
|
||||
settings: nconf.get('s'),
|
||||
all: nconf.get('a'),
|
||||
});
|
||||
} else if (nconf.get('activate')) {
|
||||
require('./src/cli/manage').activate(nconf.get('activate'));
|
||||
} else if (nconf.get('plugins') && typeof nconf.get('plugins') !== 'object') {
|
||||
require('./src/cli/manage').listPlugins();
|
||||
} else if (nconf.get('build')) {
|
||||
require('./src/cli/manage').build(nconf.get('build'));
|
||||
} else if (nconf.get('events')) {
|
||||
require('./src/cli/manage').listEvents();
|
||||
} else {
|
||||
require('./src/start').start();
|
||||
}
|
||||
/*
|
||||
NodeBB - A forum powered by node in development by designcreateplay
|
||||
Copyright (C) 2013 DesignCreatePlay Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
"use strict";
|
||||
|
||||
var nconf = require('nconf');
|
||||
nconf.argv().env();
|
||||
|
||||
var fs = require('fs'),
|
||||
os = require('os'),
|
||||
semver = require('semver'),
|
||||
winston = require('winston'),
|
||||
path = require('path'),
|
||||
pkg = require('./package.json'),
|
||||
utils = require('./public/src/utils.js');
|
||||
|
||||
|
||||
global.env = process.env.NODE_ENV || 'production';
|
||||
|
||||
winston.remove(winston.transports.Console);
|
||||
winston.add(winston.transports.Console, {
|
||||
colorize: true
|
||||
});
|
||||
|
||||
winston.add(winston.transports.File, {
|
||||
filename: 'logs/error.log',
|
||||
level: 'error'
|
||||
});
|
||||
|
||||
// TODO: remove once https://github.com/flatiron/winston/issues/280 is fixed
|
||||
winston.err = function (err) {
|
||||
winston.error(err.stack);
|
||||
};
|
||||
|
||||
if(os.platform() === 'linux') {
|
||||
require('child_process').exec('/usr/bin/which convert', function(err, stdout, stderr) {
|
||||
if(err || !stdout) {
|
||||
winston.warn('Couldn\'t find convert. Did you install imagemagick?');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Log GNU copyright info along with server info
|
||||
winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.');
|
||||
winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
|
||||
winston.info('This is free software, and you are welcome to redistribute it under certain conditions.');
|
||||
winston.info('');
|
||||
|
||||
// Alternate configuration file support
|
||||
var configFile = __dirname + '/config.json',
|
||||
configExists;
|
||||
if (nconf.get('config')) {
|
||||
configFile = path.resolve(__dirname, nconf.get('config'));
|
||||
}
|
||||
configExists = fs.existsSync(configFile);
|
||||
|
||||
if (!nconf.get('help') && !nconf.get('setup') && !nconf.get('install') && !nconf.get('upgrade') && !nconf.get('reset') && configExists) {
|
||||
start();
|
||||
} else if (nconf.get('setup') || nconf.get('install') || !configExists) {
|
||||
setup();
|
||||
} else if (nconf.get('upgrade')) {
|
||||
upgrade();
|
||||
} else if (nconf.get('reset')) {
|
||||
reset();
|
||||
} else {
|
||||
displayHelp();
|
||||
}
|
||||
|
||||
function loadConfig() {
|
||||
nconf.file({
|
||||
file: configFile
|
||||
});
|
||||
|
||||
nconf.defaults({
|
||||
base_dir: __dirname,
|
||||
themes_path: path.join(__dirname, 'node_modules'),
|
||||
upload_url: path.join(path.sep, 'uploads', path.sep),
|
||||
views_dir: path.join(__dirname, 'public/templates')
|
||||
});
|
||||
|
||||
// Ensure themes_path is a full filepath
|
||||
nconf.set('themes_path', path.resolve(__dirname, nconf.get('themes_path')));
|
||||
}
|
||||
|
||||
function start() {
|
||||
loadConfig();
|
||||
|
||||
winston.info('Time: ' + new Date());
|
||||
winston.info('Initializing NodeBB v' + pkg.version);
|
||||
winston.info('* using configuration stored in: ' + configFile);
|
||||
var host = nconf.get(nconf.get('database') + ':host');
|
||||
winston.info('* using ' + nconf.get('database') +' store at ' + host + (host.indexOf('/') === -1 ? ':' + nconf.get(nconf.get('database') + ':port') : ''));
|
||||
winston.info('* using themes stored in: ' + nconf.get('themes_path'));
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('Base Configuration OK.');
|
||||
}
|
||||
|
||||
var meta = require('./src/meta');
|
||||
|
||||
require('./src/database').init(function(err) {
|
||||
meta.configs.init(function () {
|
||||
var templates = require('./public/src/templates'),
|
||||
translator = require('./public/src/translator'),
|
||||
webserver = require('./src/webserver'),
|
||||
sockets = require('./src/socket.io'),
|
||||
plugins = require('./src/plugins'),
|
||||
upgrade = require('./src/upgrade');
|
||||
|
||||
templates.setGlobal('relative_path', nconf.get('relative_path'));
|
||||
|
||||
upgrade.check(function(schema_ok) {
|
||||
if (schema_ok || nconf.get('check-schema') === false) {
|
||||
sockets.init(webserver.server);
|
||||
plugins.init();
|
||||
translator.loadServer();
|
||||
|
||||
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path'));
|
||||
nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-vanilla/templates'));
|
||||
nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path'));
|
||||
|
||||
plugins.ready(function() {
|
||||
webserver.init();
|
||||
});
|
||||
|
||||
process.on('SIGTERM', shutdown);
|
||||
process.on('SIGINT', shutdown);
|
||||
process.on('SIGHUP', restart);
|
||||
} else {
|
||||
winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:');
|
||||
winston.warn(' node app --upgrade');
|
||||
winston.warn('To ignore this error (not recommended):');
|
||||
winston.warn(' node app --no-check-schema');
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setup() {
|
||||
loadConfig();
|
||||
|
||||
if (nconf.get('setup')) {
|
||||
winston.info('NodeBB Setup Triggered via Command Line');
|
||||
} else {
|
||||
winston.warn('Configuration not found, starting NodeBB setup');
|
||||
}
|
||||
|
||||
var install = require('./src/install');
|
||||
|
||||
winston.info('Welcome to NodeBB!');
|
||||
winston.info('This looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.');
|
||||
winston.info('Press enter to accept the default setting (shown in brackets).');
|
||||
|
||||
install.setup(function (err) {
|
||||
if (err) {
|
||||
winston.error('There was a problem completing NodeBB setup: ', err.message);
|
||||
} else {
|
||||
winston.info('NodeBB Setup Completed. Run \'./nodebb start\' to manually start your NodeBB server.');
|
||||
}
|
||||
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
function upgrade() {
|
||||
loadConfig();
|
||||
|
||||
var meta = require('./src/meta');
|
||||
|
||||
require('./src/database').init(function(err) {
|
||||
meta.configs.init(function () {
|
||||
require('./src/upgrade').upgrade();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function reset() {
|
||||
loadConfig();
|
||||
|
||||
require('./src/database').init(function(err) {
|
||||
if (err) {
|
||||
winston.error(err.message);
|
||||
process.exit();
|
||||
}
|
||||
|
||||
if (nconf.get('themes')) {
|
||||
resetThemes();
|
||||
} else if (nconf.get('plugins')) {
|
||||
resetPlugins();
|
||||
} else if (nconf.get('widgets')) {
|
||||
resetWidgets();
|
||||
} else if (nconf.get('settings')) {
|
||||
resetSettings();
|
||||
} else if (nconf.get('all')) {
|
||||
require('async').series([resetWidgets, resetThemes, resetPlugins, resetSettings], function(err) {
|
||||
if (!err) {
|
||||
winston.info('[reset] Reset complete.');
|
||||
} else {
|
||||
winston.error('[reset] Errors were encountered while resetting your forum settings: ' + err.message);
|
||||
}
|
||||
process.exit();
|
||||
});
|
||||
} else {
|
||||
console.log('no match');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetSettings(callback) {
|
||||
var meta = require('./src/meta');
|
||||
meta.configs.set('allowLocalLogin', 1, function(err) {
|
||||
winston.info('[reset] Settings reset to default');
|
||||
if (typeof callback === 'function') {
|
||||
callback(err);
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetThemes(callback) {
|
||||
var meta = require('./src/meta');
|
||||
|
||||
meta.themes.set({
|
||||
type: 'local',
|
||||
id: 'nodebb-theme-vanilla'
|
||||
}, function(err) {
|
||||
winston.info('[reset] Theme reset to Vanilla');
|
||||
if (typeof callback === 'function') {
|
||||
callback(err);
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetPlugins(callback) {
|
||||
var db = require('./src/database');
|
||||
db.delete('plugins:active', function(err) {
|
||||
winston.info('[reset] All Plugins De-activated');
|
||||
if (typeof callback === 'function') {
|
||||
callback(err);
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetWidgets(callback) {
|
||||
require('./src/widgets').reset(function(err) {
|
||||
winston.info('[reset] All Widgets moved to Draft Zone');
|
||||
if (typeof callback === 'function') {
|
||||
callback(err);
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function shutdown(code) {
|
||||
winston.info('[app] Shutdown (SIGTERM/SIGINT) Initialised.');
|
||||
require('./src/database').close();
|
||||
winston.info('[app] Database connection closed.');
|
||||
|
||||
winston.info('[app] Shutdown complete.');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
function restart() {
|
||||
if (process.send) {
|
||||
winston.info('[app] Restarting...');
|
||||
process.send({
|
||||
action: 'restart'
|
||||
});
|
||||
} else {
|
||||
winston.error('[app] Could not restart server. Shutting down.');
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
function displayHelp() {
|
||||
winston.info('Usage: node app [options] [arguments]');
|
||||
winston.info(' [NODE_ENV=development | NODE_ENV=production] node app [--start] [arguments]');
|
||||
winston.info('');
|
||||
winston.info('Options:');
|
||||
winston.info(' --help displays this usage information');
|
||||
winston.info(' --setup configure your environment and setup NodeBB');
|
||||
winston.info(' --upgrade upgrade NodeBB, first read: github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB');
|
||||
winston.info(' --reset soft resets NodeBB; disables all plugins and restores selected theme to Vanilla');
|
||||
winston.info(' --start manually start NodeBB (default when no options are given)');
|
||||
}
|
||||
|
||||
4
build/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
*
|
||||
*/
|
||||
!export
|
||||
!.gitignore
|
||||
3
build/export/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
.
|
||||
!.gitignore
|
||||
!README
|
||||
@@ -1,5 +0,0 @@
|
||||
This directory contains archives of user uploads that are prepared on-demand
|
||||
when a user wants to retrieve a copy of their uploaded content.
|
||||
|
||||
You can delete the files in here at will. They will just be regenerated if
|
||||
requested again.
|
||||
@@ -1,26 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-angular'],
|
||||
rules: {
|
||||
'header-max-length': [1, 'always', 72],
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'breaking',
|
||||
'build',
|
||||
'chore',
|
||||
'ci',
|
||||
'docs',
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'refactor',
|
||||
'revert',
|
||||
'style',
|
||||
'test',
|
||||
],
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,55 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
nodebb:
|
||||
build: .
|
||||
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
|
||||
volumes:
|
||||
- ./.docker/build:/usr/src/app/build
|
||||
- ./.docker/public/uploads:/usr/src/app/public/uploads
|
||||
- ./.docker:/opt/config
|
||||
- ./install/docker/setup.json:/usr/src/app/setup.json
|
||||
mongo:
|
||||
image: "mongo:7-jammy"
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "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
|
||||
- ./install/docker/mongodb-user-init.js:/docker-entrypoint-initdb.d/user-init.js
|
||||
profiles:
|
||||
- mongo
|
||||
postgres:
|
||||
image: postgres:16.1-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"
|
||||
volumes:
|
||||
- ./.docker/database/redis:/data
|
||||
profiles:
|
||||
- redis
|
||||
@@ -1,38 +1,98 @@
|
||||
[
|
||||
{
|
||||
"name": "Announcements",
|
||||
"description": "Announcements regarding our community",
|
||||
"descriptionParsed": "<p>Announcements regarding our community</p>\n",
|
||||
"bgColor": "#fda34b",
|
||||
"color": "#ffffff",
|
||||
"icon" : "fa-bullhorn",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"name": "General Discussion",
|
||||
"description": "A place to talk about whatever you want",
|
||||
"descriptionParsed": "<p>A place to talk about whatever you want</p>\n",
|
||||
"bgColor": "#59b3d0",
|
||||
"color": "#ffffff",
|
||||
"icon" : "fa-comments-o",
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"name": "Blogs",
|
||||
"description": "Blog posts from individual members",
|
||||
"descriptionParsed": "<p>Blog posts from individual members</p>\n",
|
||||
"bgColor": "#86ba4b",
|
||||
"color": "#ffffff",
|
||||
"icon" : "fa-newspaper-o",
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"name": "Comments & Feedback",
|
||||
"description": "Got a question? Ask away!",
|
||||
"descriptionParsed": "<p>Got a question? Ask away!</p>\n",
|
||||
"bgColor": "#e95c5a",
|
||||
"color": "#ffffff",
|
||||
"icon" : "fa-question",
|
||||
"order": 3
|
||||
}
|
||||
[
|
||||
{
|
||||
"name": "Announcements",
|
||||
"description": "Announcements regarding our community",
|
||||
"bgColor": "#0059B2",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-bullhorn",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"name": "General Discussion",
|
||||
"description": "A place to talk about whateeeever you want",
|
||||
"bgColor": "#0059B2",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-comment",
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Development",
|
||||
"description": "NodeBB development news and announcements",
|
||||
"bgColor": "#0059B2",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-github",
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"name": "Blogs",
|
||||
"description": "Blog posts from individual members",
|
||||
"bgColor": "#0059B2",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-pencil",
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"name": "Feature Requests",
|
||||
"description": "Got a feature request you'd like to see? Give us a shout here.",
|
||||
"bgColor": "#ab1290",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-lightbulb-o",
|
||||
"order": 5
|
||||
},
|
||||
{
|
||||
"name": "Bug Reports",
|
||||
"description": "Having trouble with NodeBB? Let us know...",
|
||||
"bgColor": "#ab1290",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-cogs",
|
||||
"order": 6
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Plugins",
|
||||
"description": "Enhance your NodeBB with plugins!",
|
||||
"bgColor": "#ab1290",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-plus-square",
|
||||
"order": 7
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Link Exchange",
|
||||
"description": "Link exchange",
|
||||
"bgColor": "#ab1290",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-exchange",
|
||||
"order": 8
|
||||
},
|
||||
{
|
||||
"name": "News",
|
||||
"description": "News from around the world",
|
||||
"bgColor": "#004C66",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-globe",
|
||||
"order": 9
|
||||
},
|
||||
{
|
||||
"name": "Movies",
|
||||
"description": "Discuss the latest movies here",
|
||||
"bgColor": "#004C66",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-film",
|
||||
"order": 10
|
||||
},
|
||||
{
|
||||
"name": "Games",
|
||||
"description": "Discuss the latest games here",
|
||||
"bgColor": "#004C66",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-crosshairs",
|
||||
"order": 11
|
||||
},
|
||||
{
|
||||
"name": "Random",
|
||||
"description": "Anything and (almost) everything welcome!",
|
||||
"bgColor": "#004C66",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-beer",
|
||||
"order": 12
|
||||
}
|
||||
]
|
||||
@@ -1,191 +0,0 @@
|
||||
{
|
||||
"title": "NodeBB",
|
||||
"showSiteTitle": 1,
|
||||
"defaultLang": "en-GB",
|
||||
"loginDays": 14,
|
||||
"loginSeconds": 0,
|
||||
"sessionDuration": 0,
|
||||
"loginAttempts": 5,
|
||||
"lockoutDuration": 60,
|
||||
"adminReloginDuration": 60,
|
||||
"postDelay": 10,
|
||||
"initialPostDelay": 10,
|
||||
"newbiePostDelay": 120,
|
||||
"postEditDuration": 0,
|
||||
"newbiePostEditDuration": 3600,
|
||||
"postDeleteDuration": 0,
|
||||
"enablePostHistory": 1,
|
||||
"topicBacklinks": 1,
|
||||
"postCacheSize": 20971520,
|
||||
"disableChat": 0,
|
||||
"chatEditDuration": 0,
|
||||
"chatDeleteDuration": 0,
|
||||
"chatMessageDelay": 2000,
|
||||
"newbieChatMessageDelay": 120000,
|
||||
"notificationSendDelay": 60,
|
||||
"newbieReputationThreshold": 3,
|
||||
"postQueue": 0,
|
||||
"postQueueReputationThreshold": 0,
|
||||
"groupsExemptFromPostQueue": ["administrators", "Global Moderators"],
|
||||
"groupsExemptFromNewUserRestrictions": ["administrators", "Global Moderators"],
|
||||
"groupsExemptFromMaintenanceMode": ["administrators", "Global Moderators"],
|
||||
"minimumPostLength": 8,
|
||||
"maximumPostLength": 32767,
|
||||
"systemTags": "",
|
||||
"minimumTagsPerTopic": 0,
|
||||
"maximumTagsPerTopic": 5,
|
||||
"minimumTagLength": 3,
|
||||
"maximumTagLength": 15,
|
||||
"undoTimeout": 10000,
|
||||
"allowTopicsThumbnail": 1,
|
||||
"registrationType": "normal",
|
||||
"registrationApprovalType": "normal",
|
||||
"allowAccountDelete": 1,
|
||||
"privateUploads": 0,
|
||||
"allowedFileExtensions": "png,jpg,bmp,txt",
|
||||
"uploadRateLimitThreshold": 10,
|
||||
"uploadRateLimitCooldown": 60,
|
||||
"allowUserHomePage": 1,
|
||||
"allowMultipleBadges": 0,
|
||||
"maximumFileSize": 2048,
|
||||
"stripEXIFData": 1,
|
||||
"orphanExpiryDays": 0,
|
||||
"resizeImageWidthThreshold": 2000,
|
||||
"resizeImageWidth": 760,
|
||||
"rejectImageWidth": 5000,
|
||||
"rejectImageHeight": 5000,
|
||||
"resizeImageQuality": 80,
|
||||
"topicThumbSize": 512,
|
||||
"minimumTitleLength": 3,
|
||||
"maximumTitleLength": 255,
|
||||
"minimumUsernameLength": 2,
|
||||
"maximumUsernameLength": 16,
|
||||
"minimumPasswordLength": 6,
|
||||
"minimumPasswordStrength": 1,
|
||||
"maximumSignatureLength": 255,
|
||||
"maximumAboutMeLength": 1000,
|
||||
"maximumUsersInChatRoom": 0,
|
||||
"maximumChatMessageLength": 1000,
|
||||
"maximumChatRoomNameLength": 50,
|
||||
"maximumProfileImageSize": 256,
|
||||
"maximumCoverImageSize": 2048,
|
||||
"profileImageDimension": 200,
|
||||
"profile:convertProfileImageToPNG": 0,
|
||||
"profile:keepAllUserImages": 0,
|
||||
"gdpr_enabled": 1,
|
||||
"allowProfileImageUploads": 1,
|
||||
"teaserPost": "last-reply",
|
||||
"showPostPreviewsOnHover": 1,
|
||||
"allowPrivateGroups": 1,
|
||||
"unreadCutoff": 2,
|
||||
"bookmarkThreshold": 5,
|
||||
"autoDetectLang": 1,
|
||||
"reputation:disabled": 0,
|
||||
"downvote:disabled": 0,
|
||||
"disableSignatures": 0,
|
||||
"signatures:hideDuplicates": 0,
|
||||
"upvotesPerDay": 20,
|
||||
"upvotesPerUserPerDay": 6,
|
||||
"downvotesPerDay": 10,
|
||||
"downvotesPerUserPerDay": 3,
|
||||
"min:rep:chat": 0,
|
||||
"min:rep:downvote": 0,
|
||||
"min:rep:upvote": 0,
|
||||
"min:rep:post-links": 0,
|
||||
"min:rep:flag": 0,
|
||||
"min:rep:profile-picture": 0,
|
||||
"min:rep:cover-picture": 0,
|
||||
"min:rep:website": 0,
|
||||
"min:rep:aboutme": 0,
|
||||
"min:rep:signature": 0,
|
||||
"flags:limitPerTarget": 0,
|
||||
"flags:autoFlagOnDownvoteThreshold": 0,
|
||||
"flags:actionOnResolve": "rescind",
|
||||
"flags:actionOnReject": "rescind",
|
||||
"notificationType_upvote": "notification",
|
||||
"notificationType_new-topic": "notification",
|
||||
"notificationType_new-reply": "notification",
|
||||
"notificationType_post-edit": "notification",
|
||||
"notificationType_follow": "notification",
|
||||
"notificationType_new-chat": "notification",
|
||||
"notificationType_new-group-chat": "notification",
|
||||
"notificationType_group-invite": "notification",
|
||||
"notificationType_group-leave": "notification",
|
||||
"notificationType_group-request-membership": "notification",
|
||||
"notificationType_mention": "notification",
|
||||
"notificationType_new-register": "notification",
|
||||
"notificationType_post-queue": "notification",
|
||||
"notificationType_new-post-flag": "notification",
|
||||
"notificationType_new-user-flag": "notification",
|
||||
"topicStaleDays": 60,
|
||||
"maxTopicsPerPage": 20,
|
||||
"maxPostsPerPage": 20,
|
||||
"topicsPerPage": 20,
|
||||
"postsPerPage": 20,
|
||||
"categoriesPerPage": 50,
|
||||
"userSearchResultsPerPage": 50,
|
||||
"searchDefaultSortBy": "relevance",
|
||||
"searchDefaultIn": "titlesposts",
|
||||
"searchDefaultInQuick": "titles",
|
||||
"maximumGroupNameLength": 255,
|
||||
"maximumGroupTitleLength": 40,
|
||||
"preventTopicDeleteAfterReplies": 0,
|
||||
"feeds:disableSitemap": 0,
|
||||
"feeds:disableRSS": 0,
|
||||
"sitemapTopics": 500,
|
||||
"maintenanceMode": 0,
|
||||
"maintenanceModeStatus": 503,
|
||||
"votesArePublic": 0,
|
||||
"maximumInvites": 0,
|
||||
"username:disableEdit": 0,
|
||||
"email:disableEdit": 0,
|
||||
"email:smtpTransport:pool": 0,
|
||||
"hideFullname": 0,
|
||||
"hideEmail": 0,
|
||||
"showFullnameAsDisplayName": 0,
|
||||
"allowGuestHandles": 0,
|
||||
"guestsIncrementTopicViews": 1,
|
||||
"allowGuestReplyNotifications": 1,
|
||||
"incrementTopicViewsInterval": 60,
|
||||
"recentMaxTopics": 200,
|
||||
"disableRecentCategoryFilter": 0,
|
||||
"maximumRelatedTopics": 0,
|
||||
"disableEmailSubscriptions": 0,
|
||||
"emailConfirmInterval": 10,
|
||||
"emailConfirmExpiry": 24,
|
||||
"removeEmailNotificationImages": 0,
|
||||
"sendValidationEmail": 1,
|
||||
"includeUnverifiedEmails": 0,
|
||||
"emailPrompt": 1,
|
||||
"sendEmailToBanned": 0,
|
||||
"requireEmailAddress": 0,
|
||||
"inviteExpiration": 7,
|
||||
"dailyDigestFreq": "off",
|
||||
"digestHour": 17,
|
||||
"passwordExpiryDays": 0,
|
||||
"cross-origin-embedder-policy": 0,
|
||||
"cross-origin-opener-policy": "same-origin",
|
||||
"cross-origin-resource-policy": "same-origin",
|
||||
"hsts-maxage": 31536000,
|
||||
"hsts-subdomains": 0,
|
||||
"hsts-preload": 0,
|
||||
"hsts-enabled": 0,
|
||||
"eventLoopCheckEnabled": 1,
|
||||
"eventLoopLagThreshold": 100,
|
||||
"eventLoopInterval": 500,
|
||||
"onlineCutoff": 30,
|
||||
"timeagoCutoff": 30,
|
||||
"necroThreshold": 7,
|
||||
"categoryWatchState": "watching",
|
||||
"submitPluginUsage": 1,
|
||||
"showAverageApprovalTime": 1,
|
||||
"autoApproveTime": 0,
|
||||
"maxUserSessions": 10,
|
||||
"useCompression": 0,
|
||||
"updateUrlWithPostIndex": 1,
|
||||
"composer:showHelpTab": 1,
|
||||
"composer:allowPluginHelp": 1,
|
||||
"maxReconnectionAttempts": 5,
|
||||
"reconnectionDelay": 1500,
|
||||
"disableCustomUserSkins": 0
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
[
|
||||
{
|
||||
"widget": "html",
|
||||
"data" : {
|
||||
"html": "<footer id=\"footer\" class=\"container footer d-flex flex-column align-items-center gap-1 mb-2\">\n\t<span>Powered by <a class=\"link-secondary text-decoration-underline\" target=\"_blank\" href=\"https://nodebb.org\">NodeBB</a></span>\n\t<span><a class=\"link-secondary\" target=\"_blank\" href=\"//github.com/NodeBB/NodeBB/graphs/contributors\"><i class=\"fa fa-users\"></i> <span class=\"text-decoration-underline\">Contributors<span></a></span>\n</footer>",
|
||||
"title":"",
|
||||
"container":""
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,69 +0,0 @@
|
||||
[
|
||||
{
|
||||
"route": "/categories",
|
||||
"title": "[[global:header.categories]]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-list",
|
||||
"textClass": "d-lg-none",
|
||||
"text": "[[global:header.categories]]"
|
||||
},
|
||||
{
|
||||
"id": "unread-count",
|
||||
"route": "/unread",
|
||||
"title": "[[global:header.unread]]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-inbox",
|
||||
"textClass": "d-lg-none",
|
||||
"text": "[[global:header.unread]]",
|
||||
"groups": ["registered-users"]
|
||||
},
|
||||
{
|
||||
"route": "/recent",
|
||||
"title": "[[global:header.recent]]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-clock-o",
|
||||
"textClass": "d-lg-none",
|
||||
"text": "[[global:header.recent]]"
|
||||
},
|
||||
{
|
||||
"route": "/tags",
|
||||
"title": "[[global:header.tags]]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-tags",
|
||||
"textClass": "d-lg-none",
|
||||
"text": "[[global:header.tags]]"
|
||||
},
|
||||
{
|
||||
"route": "/popular",
|
||||
"title": "[[global:header.popular]]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-fire",
|
||||
"textClass": "d-lg-none",
|
||||
"text": "[[global:header.popular]]"
|
||||
},
|
||||
{
|
||||
"route": "/users",
|
||||
"title": "[[global:header.users]]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-user",
|
||||
"textClass": "d-lg-none",
|
||||
"text": "[[global:header.users]]"
|
||||
},
|
||||
{
|
||||
"route": "/groups",
|
||||
"title": "[[global:header.groups]]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-group",
|
||||
"textClass": "d-lg-none",
|
||||
"text": "[[global:header.groups]]"
|
||||
},
|
||||
{
|
||||
"route": "/admin",
|
||||
"title": "[[global:header.admin]]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-cogs",
|
||||
"textClass": "d-lg-none",
|
||||
"text": "[[global:header.admin]]",
|
||||
"groups": ["administrators"]
|
||||
}
|
||||
]
|
||||
@@ -1,10 +0,0 @@
|
||||
### Welcome to your brand new NodeBB forum!
|
||||
|
||||
This is what a topic and post looks like. As an administrator, you can edit the post\'s title and content.
|
||||
To customise your forum, go to the [Administrator Control Panel](../../admin). You can modify all aspects of your forum there, including installation of third-party plugins.
|
||||
|
||||
#### Additional Resources
|
||||
|
||||
* [NodeBB Documentation](https://docs.nodebb.org)
|
||||
* [Community Support Forum](https://community.nodebb.org)
|
||||
* [Project repository](https://github.com/nodebb/nodebb)
|
||||
@@ -1,87 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const prompt = require('prompt');
|
||||
const winston = require('winston');
|
||||
|
||||
const questions = {
|
||||
redis: require('../src/database/redis').questions,
|
||||
mongo: require('../src/database/mongo').questions,
|
||||
postgres: require('../src/database/postgres').questions,
|
||||
};
|
||||
|
||||
module.exports = async function (config) {
|
||||
winston.info(`\nNow configuring ${config.database} database:`);
|
||||
const databaseConfig = await getDatabaseConfig(config);
|
||||
return saveDatabaseConfig(config, databaseConfig);
|
||||
};
|
||||
|
||||
async function getDatabaseConfig(config) {
|
||||
if (!config) {
|
||||
throw new Error('invalid config, aborted');
|
||||
}
|
||||
|
||||
if (config.database === 'redis') {
|
||||
if (config['redis:host'] && config['redis:port']) {
|
||||
return config;
|
||||
}
|
||||
return await prompt.get(questions.redis);
|
||||
} else if (config.database === 'mongo') {
|
||||
if ((config['mongo:host'] && config['mongo:port']) || config['mongo:uri']) {
|
||||
return config;
|
||||
}
|
||||
return await prompt.get(questions.mongo);
|
||||
} else if (config.database === 'postgres') {
|
||||
if (config['postgres:host'] && config['postgres:port']) {
|
||||
return config;
|
||||
}
|
||||
return await prompt.get(questions.postgres);
|
||||
}
|
||||
throw new Error(`unknown database : ${config.database}`);
|
||||
}
|
||||
|
||||
function saveDatabaseConfig(config, databaseConfig) {
|
||||
if (!databaseConfig) {
|
||||
throw new Error('invalid config, aborted');
|
||||
}
|
||||
|
||||
// Translate redis properties into redis object
|
||||
if (config.database === 'redis') {
|
||||
config.redis = {
|
||||
host: databaseConfig['redis:host'],
|
||||
port: databaseConfig['redis:port'],
|
||||
password: databaseConfig['redis:password'],
|
||||
database: databaseConfig['redis:database'],
|
||||
};
|
||||
|
||||
if (config.redis.host.slice(0, 1) === '/') {
|
||||
delete config.redis.port;
|
||||
}
|
||||
} else if (config.database === 'mongo') {
|
||||
config.mongo = {
|
||||
host: databaseConfig['mongo:host'],
|
||||
port: databaseConfig['mongo:port'],
|
||||
username: databaseConfig['mongo:username'],
|
||||
password: databaseConfig['mongo:password'],
|
||||
database: databaseConfig['mongo:database'],
|
||||
uri: databaseConfig['mongo:uri'],
|
||||
};
|
||||
} else if (config.database === 'postgres') {
|
||||
config.postgres = {
|
||||
host: databaseConfig['postgres:host'],
|
||||
port: databaseConfig['postgres:port'],
|
||||
username: databaseConfig['postgres:username'],
|
||||
password: databaseConfig['postgres:password'],
|
||||
database: databaseConfig['postgres:database'],
|
||||
ssl: databaseConfig['postgres:ssl'],
|
||||
};
|
||||
} else {
|
||||
throw new Error(`unknown database : ${config.database}`);
|
||||
}
|
||||
|
||||
const allQuestions = questions.redis.concat(questions.mongo).concat(questions.postgres);
|
||||
for (let x = 0; x < allQuestions.length; x += 1) {
|
||||
delete config[allQuestions[x].name];
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/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}"
|
||||
|
||||
# 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:-}"
|
||||
|
||||
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"
|
||||
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"
|
||||
echo "Starting installation session"
|
||||
./nodebb "${NODEBB_INIT_VERB}" --config=$CONFIG
|
||||
fi
|
||||
@@ -1 +0,0 @@
|
||||
db.createUser( { user: 'nodebb', pwd: 'nodebb', roles: [ { role: 'readWrite', db: 'nodebb' }, { role: 'clusterMonitor', db: 'admin' } ] } )
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"mongo": {
|
||||
"host": "mongo",
|
||||
"port": 27017,
|
||||
"database": "nodebb",
|
||||
"username": "nodebb",
|
||||
"password": "nodebb"
|
||||
},
|
||||
"redis": {
|
||||
"host": "redis",
|
||||
"port": 6379,
|
||||
"database": 0
|
||||
},
|
||||
"postgres": {
|
||||
"host": "postgres",
|
||||
"port": 5432,
|
||||
"database": "nodebb",
|
||||
"username": "nodebb",
|
||||
"password": "nodebb"
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
{
|
||||
"name": "nodebb",
|
||||
"license": "GPL-3.0",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "3.5.1",
|
||||
"homepage": "https://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/NodeBB/NodeBB/"
|
||||
},
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "node loader.js",
|
||||
"lint": "eslint --cache ./nodebb .",
|
||||
"test": "nyc --reporter=html --reporter=text-summary mocha",
|
||||
"coverage": "nyc report --reporter=text-lcov > ./coverage/lcov.info",
|
||||
"coveralls": "nyc report --reporter=text-lcov | coveralls && rm -r coverage"
|
||||
},
|
||||
"nyc": {
|
||||
"exclude": [
|
||||
"src/upgrades/*",
|
||||
"test/*"
|
||||
]
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@adactive/bootstrap-tagsinput": "0.8.2",
|
||||
"@fontsource/inter": "5.0.15",
|
||||
"@fontsource/poppins": "5.0.8",
|
||||
"@fortawesome/fontawesome-free": "6.4.2",
|
||||
"@isaacs/ttlcache": "1.4.1",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"ace-builds": "1.31.2",
|
||||
"archiver": "6.0.1",
|
||||
"async": "3.2.5",
|
||||
"autoprefixer": "10.4.16",
|
||||
"bcryptjs": "2.4.3",
|
||||
"benchpressjs": "2.5.1",
|
||||
"body-parser": "1.20.2",
|
||||
"bootbox": "6.0.0",
|
||||
"bootstrap": "5.3.2",
|
||||
"bootswatch": "5.3.2",
|
||||
"chalk": "4.1.2",
|
||||
"chart.js": "4.4.0",
|
||||
"cli-graph": "3.2.2",
|
||||
"clipboard": "2.0.11",
|
||||
"colors": "1.4.0",
|
||||
"commander": "11.1.0",
|
||||
"compare-versions": "6.1.0",
|
||||
"compression": "1.7.4",
|
||||
"connect-flash": "0.1.1",
|
||||
"connect-mongo": "5.1.0",
|
||||
"connect-multiparty": "2.2.0",
|
||||
"connect-pg-simple": "9.0.1",
|
||||
"connect-redis": "7.1.0",
|
||||
"cookie-parser": "1.4.6",
|
||||
"cron": "3.1.6",
|
||||
"cropperjs": "1.6.1",
|
||||
"csrf-sync": "4.0.1",
|
||||
"daemon": "1.1.0",
|
||||
"diff": "5.1.0",
|
||||
"esbuild": "0.19.5",
|
||||
"express": "4.18.2",
|
||||
"express-session": "1.17.3",
|
||||
"express-useragent": "1.0.15",
|
||||
"file-loader": "6.2.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"graceful-fs": "4.2.11",
|
||||
"helmet": "7.1.0",
|
||||
"html-to-text": "9.0.5",
|
||||
"imagesloaded": "5.0.0",
|
||||
"ipaddr.js": "2.1.0",
|
||||
"jquery": "3.7.1",
|
||||
"jquery-deserialize": "2.0.0",
|
||||
"jquery-form": "4.3.0",
|
||||
"jquery-serializeobject": "1.0.0",
|
||||
"jquery-ui": "1.13.2",
|
||||
"jsesc": "3.0.2",
|
||||
"json2csv": "5.0.7",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
"lodash": "4.17.21",
|
||||
"logrotate-stream": "0.2.9",
|
||||
"lru-cache": "10.0.2",
|
||||
"mime": "3.0.0",
|
||||
"mkdirp": "3.0.1",
|
||||
"mongodb": "6.3.0",
|
||||
"morgan": "1.10.0",
|
||||
"mousetrap": "1.6.5",
|
||||
"multiparty": "4.2.3",
|
||||
"nconf": "0.12.1",
|
||||
"nodebb-plugin-2factor": "7.4.0",
|
||||
"nodebb-plugin-composer-default": "10.2.27",
|
||||
"nodebb-plugin-dbsearch": "6.2.3",
|
||||
"nodebb-plugin-emoji": "5.1.13",
|
||||
"nodebb-plugin-emoji-android": "4.0.0",
|
||||
"nodebb-plugin-markdown": "12.2.5",
|
||||
"nodebb-plugin-mentions": "4.3.7",
|
||||
"nodebb-plugin-ntfy": "1.7.3",
|
||||
"nodebb-plugin-spam-be-gone": "2.2.0",
|
||||
"nodebb-rewards-essentials": "1.0.0",
|
||||
"nodebb-theme-harmony": "1.1.99",
|
||||
"nodebb-theme-lavender": "7.1.5",
|
||||
"nodebb-theme-peace": "2.1.25",
|
||||
"nodebb-theme-persona": "13.2.47",
|
||||
"nodebb-widget-essentials": "7.0.14",
|
||||
"nodemailer": "6.9.7",
|
||||
"nprogress": "0.2.0",
|
||||
"passport": "0.6.0",
|
||||
"passport-http-bearer": "1.0.1",
|
||||
"passport-local": "1.0.0",
|
||||
"pg": "8.11.3",
|
||||
"pg-cursor": "2.10.3",
|
||||
"postcss": "8.4.31",
|
||||
"postcss-clean": "1.2.0",
|
||||
"progress-webpack-plugin": "1.0.16",
|
||||
"prompt": "1.3.0",
|
||||
"ioredis": "5.3.2",
|
||||
"request": "2.88.2",
|
||||
"request-promise-native": "1.0.9",
|
||||
"rimraf": "5.0.5",
|
||||
"rss": "1.2.2",
|
||||
"rtlcss": "4.1.1",
|
||||
"sanitize-html": "2.11.0",
|
||||
"sass": "1.69.5",
|
||||
"semver": "7.5.4",
|
||||
"serve-favicon": "2.5.0",
|
||||
"sharp": "0.32.6",
|
||||
"sitemap": "7.1.1",
|
||||
"socket.io": "4.7.2",
|
||||
"socket.io-client": "4.7.2",
|
||||
"@socket.io/redis-adapter": "8.2.1",
|
||||
"sortablejs": "1.15.0",
|
||||
"spdx-license-list": "6.8.0",
|
||||
"spider-detector": "2.0.1",
|
||||
"terser-webpack-plugin": "5.3.9",
|
||||
"textcomplete": "0.18.2",
|
||||
"textcomplete.contenteditable": "0.1.1",
|
||||
"timeago": "1.6.7",
|
||||
"tinycon": "0.6.8",
|
||||
"toobusy-js": "0.5.1",
|
||||
"validator": "13.11.0",
|
||||
"webpack": "5.89.0",
|
||||
"webpack-merge": "5.10.0",
|
||||
"winston": "3.11.0",
|
||||
"workerpool": "8.0.0",
|
||||
"xml": "1.0.1",
|
||||
"xregexp": "5.1.1",
|
||||
"yargs": "17.7.2",
|
||||
"zxcvbn": "4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apidevtools/swagger-parser": "10.1.0",
|
||||
"@commitlint/cli": "18.4.2",
|
||||
"@commitlint/config-angular": "18.4.2",
|
||||
"coveralls": "3.1.1",
|
||||
"eslint": "8.54.0",
|
||||
"eslint-config-nodebb": "0.2.1",
|
||||
"eslint-plugin-import": "2.29.0",
|
||||
"grunt": "1.6.1",
|
||||
"grunt-contrib-watch": "1.1.0",
|
||||
"husky": "8.0.3",
|
||||
"jsdom": "22.1.0",
|
||||
"lint-staged": "15.1.0",
|
||||
"mocha": "10.2.0",
|
||||
"mocha-lcov-reporter": "1.3.0",
|
||||
"mockdate": "3.0.5",
|
||||
"nyc": "15.1.0",
|
||||
"smtp-server": "3.13.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"sass-embedded": "1.69.5"
|
||||
},
|
||||
"resolutions": {
|
||||
"*/jquery": "3.7.1"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/NodeBB/NodeBB/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Julian Lam",
|
||||
"email": "julian@nodebb.org",
|
||||
"url": "https://github.com/julianlam"
|
||||
},
|
||||
{
|
||||
"name": "Barış Soner Uşaklı",
|
||||
"email": "baris@nodebb.org",
|
||||
"url": "https://github.com/barisusakli"
|
||||
}
|
||||
]
|
||||
}
|
||||
319
install/web.js
@@ -1,319 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const winston = require('winston');
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const childProcess = require('child_process');
|
||||
|
||||
const webpack = require('webpack');
|
||||
const nconf = require('nconf');
|
||||
|
||||
const Benchpress = require('benchpressjs');
|
||||
const { mkdirp } = require('mkdirp');
|
||||
const { paths } = require('../src/constants');
|
||||
const sass = require('../src/utils').getSass();
|
||||
|
||||
const app = express();
|
||||
let server;
|
||||
|
||||
const formats = [
|
||||
winston.format.colorize(),
|
||||
];
|
||||
|
||||
const timestampFormat = winston.format((info) => {
|
||||
const dateString = `${new Date().toISOString()} [${global.process.pid}]`;
|
||||
info.level = `${dateString} - ${info.level}`;
|
||||
return info;
|
||||
});
|
||||
formats.push(timestampFormat());
|
||||
formats.push(winston.format.splat());
|
||||
formats.push(winston.format.simple());
|
||||
|
||||
winston.configure({
|
||||
level: 'verbose',
|
||||
format: winston.format.combine.apply(null, formats),
|
||||
transports: [
|
||||
new winston.transports.Console({
|
||||
handleExceptions: true,
|
||||
}),
|
||||
new winston.transports.File({
|
||||
filename: 'logs/webinstall.log',
|
||||
handleExceptions: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const web = module.exports;
|
||||
let installing = false;
|
||||
let success = false;
|
||||
let error = false;
|
||||
let launchUrl;
|
||||
let timeStart = 0;
|
||||
const totalTime = 1000 * 60 * 3;
|
||||
|
||||
|
||||
const viewsDir = path.join(paths.baseDir, 'build/public/templates');
|
||||
|
||||
web.install = async function (port) {
|
||||
port = port || 4567;
|
||||
winston.info(`Launching web installer on port ${port}`);
|
||||
|
||||
app.use(express.static('public', {}));
|
||||
app.use('/assets', express.static(path.join(__dirname, '../build/public'), {}));
|
||||
|
||||
app.engine('tpl', (filepath, options, callback) => {
|
||||
filepath = filepath.replace(/\.tpl$/, '.js');
|
||||
|
||||
Benchpress.__express(filepath, options, callback);
|
||||
});
|
||||
app.set('view engine', 'tpl');
|
||||
app.set('views', viewsDir);
|
||||
app.use(bodyParser.urlencoded({
|
||||
extended: true,
|
||||
}));
|
||||
try {
|
||||
await Promise.all([
|
||||
compileTemplate(),
|
||||
compileSass(),
|
||||
runWebpack(),
|
||||
copyCSS(),
|
||||
loadDefaults(),
|
||||
]);
|
||||
setupRoutes();
|
||||
launchExpress(port);
|
||||
} catch (err) {
|
||||
winston.error(err.stack);
|
||||
}
|
||||
};
|
||||
|
||||
async function runWebpack() {
|
||||
const util = require('util');
|
||||
const webpackCfg = require('../webpack.installer');
|
||||
const compiler = webpack(webpackCfg);
|
||||
const webpackRun = util.promisify(compiler.run).bind(compiler);
|
||||
await webpackRun();
|
||||
}
|
||||
|
||||
function launchExpress(port) {
|
||||
server = app.listen(port, () => {
|
||||
winston.info('Web installer listening on http://%s:%s', '0.0.0.0', port);
|
||||
});
|
||||
}
|
||||
|
||||
function setupRoutes() {
|
||||
app.get('/', welcome);
|
||||
app.post('/', install);
|
||||
app.get('/testdb', testDatabase);
|
||||
app.get('/ping', ping);
|
||||
app.get('/sping', ping);
|
||||
}
|
||||
|
||||
async function testDatabase(req, res) {
|
||||
let db;
|
||||
try {
|
||||
const keys = Object.keys(req.query);
|
||||
const dbName = keys[0].split(':')[0];
|
||||
db = require(`../src/database/${dbName}`);
|
||||
|
||||
const opts = {};
|
||||
keys.forEach((key) => {
|
||||
opts[key.replace(`${dbName}:`, '')] = req.query[key];
|
||||
});
|
||||
|
||||
await db.init(opts);
|
||||
const global = await db.getObject('global');
|
||||
await db.close();
|
||||
res.json({ success: 1, dbfull: !!global });
|
||||
} catch (err) {
|
||||
res.json({ error: err.stack });
|
||||
}
|
||||
}
|
||||
|
||||
function ping(req, res) {
|
||||
res.status(200).send(req.path === '/sping' ? 'healthy' : '200');
|
||||
}
|
||||
|
||||
function welcome(req, res) {
|
||||
const dbs = ['mongo', 'redis', 'postgres'];
|
||||
const databases = dbs.map((databaseName) => {
|
||||
const questions = require(`../src/database/${databaseName}`).questions.filter(question => question && !question.hideOnWebInstall);
|
||||
|
||||
return {
|
||||
name: databaseName,
|
||||
questions: questions,
|
||||
};
|
||||
});
|
||||
|
||||
const defaults = require('./data/defaults.json');
|
||||
res.render('install/index', {
|
||||
url: nconf.get('url') || (`${req.protocol}://${req.get('host')}`),
|
||||
launchUrl: launchUrl,
|
||||
skipGeneralSetup: !!nconf.get('url'),
|
||||
databases: databases,
|
||||
skipDatabaseSetup: !!nconf.get('database'),
|
||||
error: error,
|
||||
success: success,
|
||||
values: req.body,
|
||||
minimumPasswordLength: defaults.minimumPasswordLength,
|
||||
minimumPasswordStrength: defaults.minimumPasswordStrength,
|
||||
installing: installing,
|
||||
percentInstalled: installing ? ((Date.now() - timeStart) / totalTime * 100).toFixed(2) : 0,
|
||||
});
|
||||
}
|
||||
|
||||
function install(req, res) {
|
||||
if (installing) {
|
||||
return welcome(req, res);
|
||||
}
|
||||
timeStart = Date.now();
|
||||
req.setTimeout(0);
|
||||
installing = true;
|
||||
|
||||
const database = nconf.get('database') || req.body.database || 'mongo';
|
||||
const setupEnvVars = {
|
||||
...process.env,
|
||||
CONFIG: nconf.get('config'),
|
||||
NODEBB_CONFIG: nconf.get('config'),
|
||||
NODEBB_URL: nconf.get('url') || req.body.url || (`${req.protocol}://${req.get('host')}`),
|
||||
NODEBB_PORT: nconf.get('port') || 4567,
|
||||
NODEBB_ADMIN_USERNAME: nconf.get('admin:username') || req.body['admin:username'],
|
||||
NODEBB_ADMIN_PASSWORD: nconf.get('admin:password') || req.body['admin:password'],
|
||||
NODEBB_ADMIN_EMAIL: nconf.get('admin:email') || req.body['admin:email'],
|
||||
NODEBB_DB: database,
|
||||
NODEBB_DB_HOST: nconf.get(`${database}:host`) || req.body[`${database}:host`],
|
||||
NODEBB_DB_PORT: nconf.get(`${database}:port`) || req.body[`${database}:port`],
|
||||
NODEBB_DB_USER: nconf.get(`${database}:username`) || req.body[`${database}:username`],
|
||||
NODEBB_DB_PASSWORD: nconf.get(`${database}:password`) || req.body[`${database}:password`],
|
||||
NODEBB_DB_NAME: nconf.get(`${database}:database`) || req.body[`${database}:database`],
|
||||
NODEBB_DB_SSL: nconf.get(`${database}:ssl`) || req.body[`${database}:ssl`],
|
||||
defaultPlugins: JSON.stringify(nconf.get('defaultplugins') || nconf.get('defaultPlugins') || []),
|
||||
};
|
||||
|
||||
winston.info('Starting setup process');
|
||||
launchUrl = setupEnvVars.NODEBB_URL;
|
||||
|
||||
const child = require('child_process').fork('app', ['--setup'], {
|
||||
env: setupEnvVars,
|
||||
});
|
||||
child.on('error', (err) => {
|
||||
error = true;
|
||||
success = false;
|
||||
winston.error(err.stack);
|
||||
});
|
||||
child.on('close', (data) => {
|
||||
success = data === 0;
|
||||
error = data !== 0;
|
||||
launch();
|
||||
});
|
||||
welcome(req, res);
|
||||
}
|
||||
|
||||
async function launch() {
|
||||
try {
|
||||
server.close();
|
||||
let child;
|
||||
|
||||
if (!nconf.get('launchCmd')) {
|
||||
child = childProcess.spawn('node', ['loader.js'], {
|
||||
detached: true,
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
});
|
||||
|
||||
console.log('\nStarting NodeBB');
|
||||
console.log(' "./nodebb stop" to stop the NodeBB server');
|
||||
console.log(' "./nodebb log" to view server output');
|
||||
console.log(' "./nodebb restart" to restart NodeBB');
|
||||
} else {
|
||||
// Use launchCmd instead, if specified
|
||||
child = childProcess.exec(nconf.get('launchCmd'), {
|
||||
detached: true,
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
});
|
||||
}
|
||||
|
||||
const filesToDelete = [
|
||||
path.join(__dirname, '../public', 'installer.css'),
|
||||
path.join(__dirname, '../public', 'bootstrap.min.css'),
|
||||
path.join(__dirname, '../build/public', 'installer.min.js'),
|
||||
];
|
||||
try {
|
||||
await Promise.all(
|
||||
filesToDelete.map(
|
||||
filename => fs.promises.unlink(filename)
|
||||
)
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err.stack);
|
||||
}
|
||||
|
||||
child.unref();
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
winston.error(err.stack);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// this is necessary because otherwise the compiled templates won't be available on a clean install
|
||||
async function compileTemplate() {
|
||||
const sourceFile = path.join(__dirname, '../src/views/install/index.tpl');
|
||||
const destTpl = path.join(viewsDir, 'install/index.tpl');
|
||||
const destJs = path.join(viewsDir, 'install/index.js');
|
||||
|
||||
const source = await fs.promises.readFile(sourceFile, 'utf8');
|
||||
|
||||
const [compiled] = await Promise.all([
|
||||
Benchpress.precompile(source, { filename: 'install/index.tpl' }),
|
||||
mkdirp(path.dirname(destJs)),
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
fs.promises.writeFile(destJs, compiled),
|
||||
fs.promises.writeFile(destTpl, source),
|
||||
]);
|
||||
}
|
||||
|
||||
async function compileSass() {
|
||||
try {
|
||||
const installSrc = path.join(__dirname, '../public/scss/install.scss');
|
||||
const style = await fs.promises.readFile(installSrc);
|
||||
const scssOutput = sass.compileString(String(style), {
|
||||
loadPaths: [
|
||||
path.join(__dirname, '../public/scss'),
|
||||
],
|
||||
});
|
||||
|
||||
await fs.promises.writeFile(path.join(__dirname, '../public/installer.css'), scssOutput.css.toString());
|
||||
} catch (err) {
|
||||
winston.error(`Unable to compile SASS: \n${err.stack}`);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function copyCSS() {
|
||||
await fs.promises.copyFile(
|
||||
path.join(__dirname, '../node_modules/bootstrap/dist/css/bootstrap.min.css'),
|
||||
path.join(__dirname, '../public/bootstrap.min.css'),
|
||||
);
|
||||
}
|
||||
|
||||
async function loadDefaults() {
|
||||
const setupDefaultsPath = path.join(__dirname, '../setup.json');
|
||||
try {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
await fs.promises.access(setupDefaultsPath, fs.constants.F_OK | fs.constants.R_OK);
|
||||
} catch (err) {
|
||||
// setup.json not found or inaccessible, proceed with no defaults
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
winston.info('[installer] Found setup.json, populating default values');
|
||||
nconf.file({
|
||||
file: setupDefaultsPath,
|
||||
});
|
||||
}
|
||||
298
loader.js
@@ -1,232 +1,86 @@
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
const nconf = require('nconf');
|
||||
const fs = require('fs');
|
||||
const url = require('url');
|
||||
const path = require('path');
|
||||
const { fork } = require('child_process');
|
||||
const logrotate = require('logrotate-stream');
|
||||
const { mkdirp } = require('mkdirp');
|
||||
|
||||
const file = require('./src/file');
|
||||
const pkg = require('./package.json');
|
||||
|
||||
const pathToConfig = path.resolve(__dirname, process.env.CONFIG || 'config.json');
|
||||
|
||||
nconf.argv().env().file({
|
||||
file: pathToConfig,
|
||||
});
|
||||
|
||||
const pidFilePath = path.join(__dirname, 'pidfile');
|
||||
|
||||
const outputLogFilePath = path.join(__dirname, nconf.get('logFile') || 'logs/output.log');
|
||||
|
||||
const logDir = path.dirname(outputLogFilePath);
|
||||
if (!fs.existsSync(logDir)) {
|
||||
mkdirp.sync(path.dirname(outputLogFilePath));
|
||||
}
|
||||
|
||||
const output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
|
||||
const silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false;
|
||||
let numProcs;
|
||||
const workers = [];
|
||||
const Loader = {};
|
||||
const appPath = path.join(__dirname, 'app.js');
|
||||
|
||||
Loader.init = function () {
|
||||
if (silent) {
|
||||
console.log = (...args) => {
|
||||
output.write(`${args.join(' ')}\n`);
|
||||
};
|
||||
}
|
||||
|
||||
process.on('SIGHUP', Loader.restart);
|
||||
process.on('SIGTERM', Loader.stop);
|
||||
};
|
||||
|
||||
Loader.displayStartupMessages = function () {
|
||||
console.log('');
|
||||
console.log(`NodeBB v${pkg.version} Copyright (C) 2013-${(new Date()).getFullYear()} NodeBB Inc.`);
|
||||
console.log('This program comes with ABSOLUTELY NO WARRANTY.');
|
||||
console.log('This is free software, and you are welcome to redistribute it under certain conditions.');
|
||||
console.log('For the full license, please visit: http://www.gnu.org/copyleft/gpl.html');
|
||||
console.log('');
|
||||
};
|
||||
|
||||
Loader.addWorkerEvents = function (worker) {
|
||||
worker.on('exit', (code, signal) => {
|
||||
console.log(`[cluster] Child Process (${worker.pid}) has exited (code: ${code}, signal: ${signal})`);
|
||||
if (!(worker.suicide || code === 0)) {
|
||||
console.log('[cluster] Spinning up another process...');
|
||||
|
||||
forkWorker(worker.index, worker.isPrimary);
|
||||
}
|
||||
});
|
||||
|
||||
worker.on('message', (message) => {
|
||||
if (message && typeof message === 'object' && message.action) {
|
||||
switch (message.action) {
|
||||
case 'restart':
|
||||
console.log('[cluster] Restarting...');
|
||||
Loader.restart();
|
||||
break;
|
||||
case 'pubsub':
|
||||
workers.forEach((w) => {
|
||||
w.send(message);
|
||||
});
|
||||
break;
|
||||
case 'socket.io':
|
||||
workers.forEach((w) => {
|
||||
if (w !== worker) {
|
||||
w.send(message);
|
||||
var nconf = require('nconf'),
|
||||
fs = require('fs'),
|
||||
pidFilePath = __dirname + '/pidfile',
|
||||
start = function() {
|
||||
var fork = require('child_process').fork,
|
||||
nbb_start = function() {
|
||||
nbb = fork('./app', process.argv.slice(2), {
|
||||
env: {
|
||||
'NODE_ENV': process.env.NODE_ENV
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Loader.start = function () {
|
||||
numProcs = getPorts().length;
|
||||
console.log(`Clustering enabled: Spinning up ${numProcs} process(es).\n`);
|
||||
nbb.on('message', function(message) {
|
||||
if (message && typeof message === 'object' && message.action) {
|
||||
if (message.action === 'restart') {
|
||||
nbb_restart();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (let x = 0; x < numProcs; x += 1) {
|
||||
forkWorker(x, x === 0);
|
||||
}
|
||||
};
|
||||
|
||||
function forkWorker(index, isPrimary) {
|
||||
const ports = getPorts();
|
||||
const args = [];
|
||||
|
||||
if (!ports[index]) {
|
||||
return console.log(`[cluster] invalid port for worker : ${index} ports: ${ports.length}`);
|
||||
}
|
||||
|
||||
process.env.isPrimary = isPrimary;
|
||||
process.env.isCluster = nconf.get('isCluster') || ports.length > 1;
|
||||
process.env.port = ports[index];
|
||||
|
||||
const worker = fork(appPath, args, {
|
||||
silent: silent,
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
worker.index = index;
|
||||
worker.isPrimary = isPrimary;
|
||||
|
||||
workers[index] = worker;
|
||||
|
||||
Loader.addWorkerEvents(worker);
|
||||
|
||||
if (silent) {
|
||||
const output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
|
||||
worker.stdout.pipe(output);
|
||||
worker.stderr.pipe(output);
|
||||
}
|
||||
}
|
||||
|
||||
function getPorts() {
|
||||
const _url = nconf.get('url');
|
||||
if (!_url) {
|
||||
console.log('[cluster] url is undefined, please check your config.json');
|
||||
process.exit();
|
||||
}
|
||||
const urlObject = url.parse(_url);
|
||||
let port = nconf.get('PORT') || nconf.get('port') || urlObject.port || 4567;
|
||||
if (!Array.isArray(port)) {
|
||||
port = [port];
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
Loader.restart = function () {
|
||||
killWorkers();
|
||||
|
||||
nconf.remove('file');
|
||||
nconf.use('file', { file: pathToConfig });
|
||||
|
||||
fs.readFile(pathToConfig, { encoding: 'utf-8' }, (err, configFile) => {
|
||||
if (err) {
|
||||
console.error('Error reading config');
|
||||
throw err;
|
||||
}
|
||||
|
||||
const conf = JSON.parse(configFile);
|
||||
|
||||
nconf.stores.env.readOnly = false;
|
||||
nconf.set('url', conf.url);
|
||||
nconf.stores.env.readOnly = true;
|
||||
|
||||
if (process.env.url !== conf.url) {
|
||||
process.env.url = conf.url;
|
||||
}
|
||||
Loader.start();
|
||||
});
|
||||
};
|
||||
|
||||
Loader.stop = function () {
|
||||
killWorkers();
|
||||
|
||||
// Clean up the pidfile
|
||||
if (nconf.get('daemon') !== 'false' && nconf.get('daemon') !== false) {
|
||||
fs.unlinkSync(pidFilePath);
|
||||
}
|
||||
};
|
||||
|
||||
function killWorkers() {
|
||||
workers.forEach((worker) => {
|
||||
worker.suicide = true;
|
||||
worker.kill();
|
||||
});
|
||||
}
|
||||
|
||||
fs.open(pathToConfig, 'r', (err) => {
|
||||
if (err) {
|
||||
// No config detected, kickstart web installer
|
||||
fork('app');
|
||||
return;
|
||||
}
|
||||
|
||||
if (nconf.get('daemon') !== 'false' && nconf.get('daemon') !== false) {
|
||||
if (file.existsSync(pidFilePath)) {
|
||||
let pid = 0;
|
||||
try {
|
||||
pid = fs.readFileSync(pidFilePath, { encoding: 'utf-8' });
|
||||
if (pid) {
|
||||
process.kill(pid, 0);
|
||||
console.info(`Process "${pid}" from pidfile already running, exiting`);
|
||||
process.exit();
|
||||
} else {
|
||||
console.info(`Invalid pid "${pid}" from pidfile, deleting pidfile`);
|
||||
fs.unlinkSync(pidFilePath);
|
||||
nbb.on('exit', function(code, signal) {
|
||||
if (code) {
|
||||
nbb_start();
|
||||
} else {
|
||||
nbb_stop();
|
||||
}
|
||||
});
|
||||
},
|
||||
nbb_stop = function() {
|
||||
nbb.kill();
|
||||
if (fs.existsSync(pidFilePath)) {
|
||||
var pid = parseInt(fs.readFileSync(pidFilePath, { encoding: 'utf-8' }), 10);
|
||||
if (process.pid === pid) {
|
||||
fs.unlinkSync(pidFilePath);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.code === 'ESRCH') {
|
||||
console.info(`Process "${pid}" from pidfile not found, deleting pidfile`);
|
||||
fs.unlinkSync(pidFilePath);
|
||||
} else {
|
||||
console.error(err.stack);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
nbb_restart = function() {
|
||||
nbb.on('exit', function() {
|
||||
nbb_start();
|
||||
});
|
||||
nbb.kill();
|
||||
};
|
||||
|
||||
process.on('SIGINT', nbb_stop);
|
||||
process.on('SIGTERM', nbb_stop);
|
||||
process.on('SIGHUP', nbb_restart);
|
||||
|
||||
nbb_start();
|
||||
},
|
||||
nbb;
|
||||
|
||||
nconf.argv();
|
||||
|
||||
// Start the daemon!
|
||||
if (nconf.get('d')) {
|
||||
// Check for a still-active NodeBB process
|
||||
if (fs.existsSync(pidFilePath)) {
|
||||
try {
|
||||
var pid = fs.readFileSync(pidFilePath, { encoding: 'utf-8' });
|
||||
process.kill(pid, 0);
|
||||
console.log('\n Error: Another NodeBB is already running!');
|
||||
process.exit();
|
||||
} catch (e) {
|
||||
fs.unlinkSync(pidFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialise logging streams
|
||||
var outputStream = fs.createWriteStream(__dirname + '/logs/output.log');
|
||||
outputStream.on('open', function(fd) {
|
||||
// Daemonize
|
||||
require('daemon')({
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
cwd: process.cwd(),
|
||||
stdout: fd
|
||||
});
|
||||
|
||||
fs.writeFileSync(pidFilePath, String(process.pid));
|
||||
}
|
||||
try {
|
||||
Loader.init();
|
||||
Loader.displayStartupMessages();
|
||||
Loader.start();
|
||||
} catch (err) {
|
||||
console.error('[loader] Error during startup');
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
// Write its pid to a pidfile
|
||||
fs.writeFile(__dirname + '/pidfile', process.pid);
|
||||
|
||||
start();
|
||||
});
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
58
minifier.js
Normal file
@@ -0,0 +1,58 @@
|
||||
var uglifyjs = require('uglify-js'),
|
||||
less = require('less'),
|
||||
async = require('async'),
|
||||
fs = require('fs'),
|
||||
|
||||
Minifier = {
|
||||
js: {},
|
||||
css: {}
|
||||
};
|
||||
|
||||
/* Javascript */
|
||||
Minifier.js.minify = function (scripts, callback) {
|
||||
try {
|
||||
var minified = uglifyjs.minify(scripts);
|
||||
callback(minified.code);
|
||||
} catch(err) {
|
||||
process.send({
|
||||
action: 'error',
|
||||
error: err
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Minifier.js.concatenate = function(scripts, callback) {
|
||||
async.map(scripts, function(path, next) {
|
||||
fs.readFile(path, { encoding: 'utf-8' }, next);
|
||||
}, function(err, contents) {
|
||||
if (err) {
|
||||
process.send({
|
||||
action: 'error',
|
||||
error: err
|
||||
});
|
||||
} else {
|
||||
callback(contents.reduce(function(output, src) {
|
||||
return output.length ? output + ';\n' + src : src;
|
||||
}, ''));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
process.on('message', function(payload) {
|
||||
var executeCallback = function(data) {
|
||||
process.send({
|
||||
action: payload.action,
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
switch(payload.action) {
|
||||
case 'js.minify':
|
||||
Minifier.js.minify(payload.scripts, executeCallback);
|
||||
break;
|
||||
|
||||
case 'js.concatenate':
|
||||
Minifier.js.concatenate(payload.scripts, executeCallback);
|
||||
break;
|
||||
}
|
||||
})
|
||||
95
mocks/databasemock.js
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Database Mock - wrapper for database.js, makes system use separate test db, instead of production
|
||||
* ATTENTION: testing db is flushed before every use!
|
||||
*/
|
||||
|
||||
(function(module) {
|
||||
'use strict';
|
||||
|
||||
var utils = require('./../public/src/utils.js'),
|
||||
path = require('path'),
|
||||
nconf = require('nconf'),
|
||||
winston = require('winston'),
|
||||
errorText;
|
||||
|
||||
|
||||
nconf.file({ file: path.join(__dirname, '../config.json') });
|
||||
nconf.defaults({
|
||||
base_dir: path.join(__dirname,'..'),
|
||||
themes_path: path.join(__dirname, '../node_modules'),
|
||||
upload_url: path.join(path.sep, '../uploads', path.sep),
|
||||
views_dir: path.join(__dirname, '../public/templates')
|
||||
});
|
||||
|
||||
var dbType = nconf.get('database'),
|
||||
testDbConfig = nconf.get('test_database'),
|
||||
productionDbConfig = nconf.get(dbType);
|
||||
|
||||
if(!testDbConfig){
|
||||
errorText = 'test_database is not defined';
|
||||
winston.info(
|
||||
"\n===========================================================\n"+
|
||||
"Please, add parameters for test database in config.json\n"+
|
||||
"For example (redis):\n"+
|
||||
'"test_database": {' + '\n' +
|
||||
' "host": "127.0.0.1",' + '\n' +
|
||||
' "port": "6379",' + '\n' +
|
||||
' "password": "",' + '\n' +
|
||||
' "database": "1"' + '\n' +
|
||||
'}\n'+
|
||||
" or (mongo):\n" +
|
||||
'"test_database": {' + '\n' +
|
||||
' "host": "127.0.0.1",' + '\n' +
|
||||
' "port": "27017",' + '\n' +
|
||||
' "password": "",' + '\n' +
|
||||
' "database": "1"' + '\n' +
|
||||
'}\n'+
|
||||
"==========================================================="
|
||||
);
|
||||
winston.error(errorText);
|
||||
throw new Error(errorText);
|
||||
}
|
||||
|
||||
if( testDbConfig.database === productionDbConfig.database &&
|
||||
testDbConfig.host === productionDbConfig.host &&
|
||||
testDbConfig.port === productionDbConfig.port
|
||||
){
|
||||
errorText = 'test_database has the same config as production db';
|
||||
winston.error(errorText);
|
||||
throw new Error(errorText);
|
||||
}
|
||||
|
||||
nconf.set(dbType, testDbConfig);
|
||||
|
||||
var db = require('../src/database'),
|
||||
meta = require('../src/meta');
|
||||
|
||||
before(function(done) {
|
||||
db.init(function(err) {
|
||||
//Clean up
|
||||
db.flushdb(function(err) {
|
||||
if(err) {
|
||||
winston.error(err);
|
||||
throw new Error(err);
|
||||
}
|
||||
|
||||
winston.info('test_database flushed');
|
||||
|
||||
meta.configs.init(function () {
|
||||
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path'));
|
||||
nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-vanilla/templates'));
|
||||
nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path'));
|
||||
|
||||
var webserver = require('../src/webserver'),
|
||||
sockets = require('../src/socket.io');
|
||||
sockets.init(webserver.server);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = db;
|
||||
|
||||
}(module));
|
||||
113
nodebb
@@ -1,5 +1,112 @@
|
||||
#!/usr/bin/env node
|
||||
#!/bin/bash
|
||||
|
||||
'use strict';
|
||||
# $0 script path
|
||||
# $1 action
|
||||
# $2 subaction
|
||||
|
||||
require('./src/cli');
|
||||
function pidExists() {
|
||||
if [ -e "pidfile" ];
|
||||
then
|
||||
kill -s 0 $(cat pidfile);
|
||||
if [ !$? ];
|
||||
then return 1;
|
||||
else return 0;
|
||||
fi
|
||||
else
|
||||
return 0;
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo "Starting NodeBB";
|
||||
echo " \"./nodebb stop\" to stop the NodeBB server";
|
||||
echo " \"./nodebb log\" to view server output";
|
||||
|
||||
if [ -f "./logs/output.log" ]; # Preserve the last output log
|
||||
then
|
||||
mv ./logs/output.log ./logs/output.1.log;
|
||||
fi;
|
||||
|
||||
# Start the loader daemon
|
||||
node loader -d "$@"
|
||||
;;
|
||||
|
||||
stop)
|
||||
echo "Stopping NodeBB. Goodbye!";
|
||||
kill $(cat pidfile);
|
||||
;;
|
||||
|
||||
reload|restart)
|
||||
echo "Restarting NodeBB.";
|
||||
kill -1 $(cat pidfile);
|
||||
;;
|
||||
|
||||
status)
|
||||
pidExists;
|
||||
if [ 0 -eq $? ];
|
||||
then
|
||||
echo "NodeBB is not running";
|
||||
echo " \"./nodebb start\" to launch the NodeBB server";
|
||||
else
|
||||
echo "NodeBB Running (pid $(cat pidfile))";
|
||||
echo " \"./nodebb stop\" to stop the NodeBB server";
|
||||
echo " \"./nodebb log\" to view server output";
|
||||
echo " \"./nodebb restart\" to restart NodeBB";
|
||||
fi
|
||||
;;
|
||||
|
||||
log)
|
||||
clear;
|
||||
tail -F ./logs/output.log;
|
||||
;;
|
||||
|
||||
upgrade)
|
||||
npm install
|
||||
ls -d node_modules/nodebb* | xargs -n1 basename | xargs npm install
|
||||
ls -d node_modules/nodebb* | xargs -n1 basename | xargs npm update
|
||||
node loader --upgrade
|
||||
touch package.json
|
||||
echo -e "\n\e[00;32mNodeBB Dependencies up-to-date!\e[00;00m";
|
||||
;;
|
||||
|
||||
setup)
|
||||
node loader --setup "$@"
|
||||
;;
|
||||
|
||||
reset)
|
||||
node loader --reset --$2
|
||||
;;
|
||||
|
||||
dev)
|
||||
echo "Launching NodeBB in \"development\" mode."
|
||||
echo "To run the production build of NodeBB, please use \"forever\"."
|
||||
echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB"
|
||||
NODE_ENV=development node loader "$@"
|
||||
;;
|
||||
|
||||
watch)
|
||||
echo "Launching NodeBB in \"development\" mode."
|
||||
echo "To run the production build of NodeBB, please use \"forever\"."
|
||||
echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB"
|
||||
NODE_ENV=development supervisor -q --ignore public/templates --extensions 'node|js|tpl' -- app "$@"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Welcome to NodeBB"
|
||||
echo $"Usage: $0 {start|stop|reload|restart|log|setup|reset|upgrade|dev|watch}"
|
||||
echo ''
|
||||
column -s ' ' -t <<< '
|
||||
start Start the NodeBB server
|
||||
stop Stops the NodeBB server
|
||||
reload Restarts NodeBB
|
||||
restart Restarts NodeBB
|
||||
log Opens the logging interface (useful for debugging)
|
||||
setup Runs the NodeBB setup script
|
||||
reset Disables all plugins, restores the default theme.
|
||||
upgrade Run NodeBB upgrade scripts, ensure packages are up-to-date
|
||||
dev Start NodeBB in interactive development mode
|
||||
watch Start NodeBB in development mode and watch for changes
|
||||
'
|
||||
exit 1
|
||||
esac
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
@echo off && cd %~dp0 && node ./nodebb %*
|
||||
843
npm-shrinkwrap.json
generated
Normal file
@@ -0,0 +1,843 @@
|
||||
{
|
||||
"name": "nodebb",
|
||||
"version": "0.4.1",
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "0.2.10",
|
||||
"from": "async@~0.2.8"
|
||||
},
|
||||
"bcryptjs": {
|
||||
"version": "0.7.12",
|
||||
"from": "bcryptjs@~0.7.10"
|
||||
},
|
||||
"connect-mongo": {
|
||||
"version": "0.4.0",
|
||||
"from": "connect-mongo@0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-0.4.0.tgz"
|
||||
},
|
||||
"connect-redis": {
|
||||
"version": "1.4.5",
|
||||
"from": "connect-redis@1.4.5",
|
||||
"dependencies": {
|
||||
"redis": {
|
||||
"version": "0.7.3",
|
||||
"from": "redis@0.7.x",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-0.7.3.tgz"
|
||||
},
|
||||
"debug": {
|
||||
"version": "0.8.0",
|
||||
"from": "debug@*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cron": {
|
||||
"version": "1.0.4",
|
||||
"from": "cron@~1.0.4"
|
||||
},
|
||||
"daemon": {
|
||||
"version": "1.1.0",
|
||||
"from": "daemon@~1.1.0"
|
||||
},
|
||||
"express": {
|
||||
"version": "3.2.0",
|
||||
"from": "express@3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-3.2.0.tgz",
|
||||
"dependencies": {
|
||||
"connect": {
|
||||
"version": "2.7.6",
|
||||
"from": "connect@2.7.6",
|
||||
"dependencies": {
|
||||
"qs": {
|
||||
"version": "0.5.1",
|
||||
"from": "qs@0.5.1"
|
||||
},
|
||||
"formidable": {
|
||||
"version": "1.0.11",
|
||||
"from": "formidable@1.0.11"
|
||||
},
|
||||
"buffer-crc32": {
|
||||
"version": "0.1.1",
|
||||
"from": "buffer-crc32@0.1.1"
|
||||
},
|
||||
"bytes": {
|
||||
"version": "0.2.0",
|
||||
"from": "bytes@0.2.0"
|
||||
},
|
||||
"pause": {
|
||||
"version": "0.0.1",
|
||||
"from": "pause@0.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "0.6.1",
|
||||
"from": "commander@0.6.1"
|
||||
},
|
||||
"range-parser": {
|
||||
"version": "0.0.4",
|
||||
"from": "range-parser@0.0.4"
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.0.5",
|
||||
"from": "cookie@0.0.5"
|
||||
},
|
||||
"buffer-crc32": {
|
||||
"version": "0.2.1",
|
||||
"from": "buffer-crc32@~0.2.1"
|
||||
},
|
||||
"fresh": {
|
||||
"version": "0.1.0",
|
||||
"from": "fresh@0.1.0"
|
||||
},
|
||||
"methods": {
|
||||
"version": "0.0.1",
|
||||
"from": "methods@0.0.1"
|
||||
},
|
||||
"send": {
|
||||
"version": "0.1.0",
|
||||
"from": "send@0.1.0",
|
||||
"dependencies": {
|
||||
"mime": {
|
||||
"version": "1.2.6",
|
||||
"from": "mime@1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.2.6.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.1",
|
||||
"from": "cookie-signature@1.0.1"
|
||||
},
|
||||
"debug": {
|
||||
"version": "0.8.0",
|
||||
"from": "debug@*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"express-namespace": {
|
||||
"version": "0.1.1",
|
||||
"from": "express-namespace@~0.1.1",
|
||||
"dependencies": {
|
||||
"methods": {
|
||||
"version": "0.0.1",
|
||||
"from": "methods@0.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gm": {
|
||||
"version": "1.14.2",
|
||||
"from": "gm@1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/gm/-/gm-1.14.2.tgz",
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "0.7.0",
|
||||
"from": "debug@0.7.0"
|
||||
},
|
||||
"array-series": {
|
||||
"version": "0.1.5",
|
||||
"from": "array-series@~0.1.0"
|
||||
},
|
||||
"array-parallel": {
|
||||
"version": "0.1.3",
|
||||
"from": "array-parallel@~0.1.0"
|
||||
},
|
||||
"stream-to-buffer": {
|
||||
"version": "0.0.1",
|
||||
"from": "stream-to-buffer@~0.0.1"
|
||||
},
|
||||
"through": {
|
||||
"version": "2.3.4",
|
||||
"from": "through@~2.3.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gravatar": {
|
||||
"version": "1.0.6",
|
||||
"from": "gravatar@1.0.6"
|
||||
},
|
||||
"hiredis": {
|
||||
"version": "0.1.16",
|
||||
"from": "hiredis@~0.1.15",
|
||||
"dependencies": {
|
||||
"bindings": {
|
||||
"version": "1.2.0",
|
||||
"from": "bindings@*"
|
||||
}
|
||||
}
|
||||
},
|
||||
"less": {
|
||||
"version": "1.6.3",
|
||||
"from": "less@~1.6.3",
|
||||
"dependencies": {
|
||||
"mime": {
|
||||
"version": "1.2.11",
|
||||
"from": "mime@1.2.x"
|
||||
},
|
||||
"request": {
|
||||
"version": "2.34.0",
|
||||
"from": "request@>=2.12.0",
|
||||
"dependencies": {
|
||||
"qs": {
|
||||
"version": "0.6.6",
|
||||
"from": "qs@~0.6.0"
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.0",
|
||||
"from": "json-stringify-safe@~5.0.0"
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.5.2",
|
||||
"from": "forever-agent@~0.5.0"
|
||||
},
|
||||
"node-uuid": {
|
||||
"version": "1.4.1",
|
||||
"from": "node-uuid@~1.4.0"
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "0.12.1",
|
||||
"from": "tough-cookie@>=0.12.0",
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "1.2.4",
|
||||
"from": "punycode@>=0.2.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "0.1.2",
|
||||
"from": "form-data@~0.1.0",
|
||||
"dependencies": {
|
||||
"combined-stream": {
|
||||
"version": "0.0.4",
|
||||
"from": "combined-stream@~0.0.4",
|
||||
"dependencies": {
|
||||
"delayed-stream": {
|
||||
"version": "0.0.5",
|
||||
"from": "delayed-stream@0.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.3.0",
|
||||
"from": "tunnel-agent@~0.3.0"
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "0.10.0",
|
||||
"from": "http-signature@~0.10.0",
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "0.1.2",
|
||||
"from": "assert-plus@0.1.2"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.1.11",
|
||||
"from": "asn1@0.1.11"
|
||||
},
|
||||
"ctype": {
|
||||
"version": "0.5.2",
|
||||
"from": "ctype@0.5.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.3.0",
|
||||
"from": "oauth-sign@~0.3.0"
|
||||
},
|
||||
"hawk": {
|
||||
"version": "1.0.0",
|
||||
"from": "hawk@~1.0.0",
|
||||
"dependencies": {
|
||||
"hoek": {
|
||||
"version": "0.9.1",
|
||||
"from": "hoek@0.9.x"
|
||||
},
|
||||
"boom": {
|
||||
"version": "0.4.2",
|
||||
"from": "boom@0.4.x"
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "0.2.2",
|
||||
"from": "cryptiles@0.2.x"
|
||||
},
|
||||
"sntp": {
|
||||
"version": "0.2.4",
|
||||
"from": "sntp@0.2.x"
|
||||
}
|
||||
}
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.5.0",
|
||||
"from": "aws-sign2@~0.5.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"clean-css": {
|
||||
"version": "2.0.8",
|
||||
"from": "clean-css@2.0.x",
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.0.0",
|
||||
"from": "commander@2.0.x"
|
||||
}
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.1.33",
|
||||
"from": "source-map@0.1.x",
|
||||
"dependencies": {
|
||||
"amdefine": {
|
||||
"version": "0.1.0",
|
||||
"from": "amdefine@>=0.0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"marked": {
|
||||
"version": "0.2.8",
|
||||
"from": "marked@0.2.8"
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.3.5",
|
||||
"from": "mkdirp@~0.3.5"
|
||||
},
|
||||
"mongodb": {
|
||||
"version": "1.3.23",
|
||||
"from": "mongodb@~1.3.19",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-1.3.23.tgz",
|
||||
"dependencies": {
|
||||
"bson": {
|
||||
"version": "0.2.5",
|
||||
"from": "bson@0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-0.2.5.tgz"
|
||||
},
|
||||
"kerberos": {
|
||||
"version": "0.0.3",
|
||||
"from": "kerberos@0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kerberos/-/kerberos-0.0.3.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nconf": {
|
||||
"version": "0.6.9",
|
||||
"from": "nconf@~0.6.7",
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "0.2.9",
|
||||
"from": "async@0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.2.9.tgz"
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.1.0",
|
||||
"from": "ini@1.x.x"
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.0",
|
||||
"from": "optimist@0.6.0",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2",
|
||||
"from": "wordwrap@~0.0.2"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"from": "minimist@~0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodebb-plugin-markdown": {
|
||||
"version": "0.4.1",
|
||||
"from": "nodebb-plugin-markdown@~0.4.1",
|
||||
"dependencies": {
|
||||
"marked": {
|
||||
"version": "0.2.10",
|
||||
"from": "marked@~0.2.9"
|
||||
},
|
||||
"pygmentize-bundled": {
|
||||
"version": "2.1.0",
|
||||
"from": "pygmentize-bundled@~2.1.0",
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "1.0.26-4",
|
||||
"from": "readable-stream@~1.0.17",
|
||||
"dependencies": {
|
||||
"core-util-is": {
|
||||
"version": "1.0.1",
|
||||
"from": "core-util-is@~1.0.0"
|
||||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"from": "isarray@0.0.1"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.25-1",
|
||||
"from": "string_decoder@~0.10.x"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"from": "inherits@~2.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bl": {
|
||||
"version": "0.4.2",
|
||||
"from": "bl@~0.4.1"
|
||||
},
|
||||
"through2": {
|
||||
"version": "0.2.3",
|
||||
"from": "through2@~0.2.1",
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "1.1.12",
|
||||
"from": "readable-stream@~1.1.9",
|
||||
"dependencies": {
|
||||
"core-util-is": {
|
||||
"version": "1.0.1",
|
||||
"from": "core-util-is@~1.0.0"
|
||||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"from": "isarray@0.0.1"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.25-1",
|
||||
"from": "string_decoder@~0.10.x"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"from": "inherits@^2.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"xtend": {
|
||||
"version": "2.1.2",
|
||||
"from": "xtend@~2.1.1",
|
||||
"dependencies": {
|
||||
"object-keys": {
|
||||
"version": "0.4.0",
|
||||
"from": "object-keys@~0.4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodebb-plugin-mentions": {
|
||||
"version": "0.4.1-2",
|
||||
"from": "nodebb-plugin-mentions@~0.4.0"
|
||||
},
|
||||
"nodebb-theme-lavender": {
|
||||
"version": "0.0.25-4",
|
||||
"from": "nodebb-theme-lavender@~0.0.25"
|
||||
},
|
||||
"nodebb-theme-vanilla": {
|
||||
"version": "0.0.19-13",
|
||||
"from": "nodebb-theme-vanilla@~0.0.19"
|
||||
},
|
||||
"nodebb-widget-essentials": {
|
||||
"version": "0.0.21-1",
|
||||
"from": "nodebb-widget-essentials@~0.0.21",
|
||||
"dependencies": {
|
||||
"marked": {
|
||||
"version": "0.3.2",
|
||||
"from": "marked@^0.3.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passport": {
|
||||
"version": "0.2.0",
|
||||
"from": "passport@~0.2.0",
|
||||
"dependencies": {
|
||||
"passport-strategy": {
|
||||
"version": "1.0.0",
|
||||
"from": "passport-strategy@1.x.x"
|
||||
},
|
||||
"pause": {
|
||||
"version": "0.0.1",
|
||||
"from": "pause@0.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"passport-local": {
|
||||
"version": "0.1.6",
|
||||
"from": "passport-local@0.1.6",
|
||||
"dependencies": {
|
||||
"pkginfo": {
|
||||
"version": "0.2.3",
|
||||
"from": "pkginfo@0.2.x"
|
||||
},
|
||||
"passport": {
|
||||
"version": "0.1.18",
|
||||
"from": "passport@~0.1.1",
|
||||
"dependencies": {
|
||||
"pause": {
|
||||
"version": "0.0.1",
|
||||
"from": "pause@0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"version": "0.2.12",
|
||||
"from": "prompt@~0.2.11",
|
||||
"dependencies": {
|
||||
"pkginfo": {
|
||||
"version": "0.3.0",
|
||||
"from": "pkginfo@0.x.x"
|
||||
},
|
||||
"read": {
|
||||
"version": "1.0.5",
|
||||
"from": "read@1.0.x",
|
||||
"dependencies": {
|
||||
"mute-stream": {
|
||||
"version": "0.0.4",
|
||||
"from": "mute-stream@~0.0.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"revalidator": {
|
||||
"version": "0.1.6",
|
||||
"from": "revalidator@0.1.x"
|
||||
},
|
||||
"utile": {
|
||||
"version": "0.2.1",
|
||||
"from": "utile@0.2.x",
|
||||
"dependencies": {
|
||||
"deep-equal": {
|
||||
"version": "0.2.1",
|
||||
"from": "deep-equal@*"
|
||||
},
|
||||
"i": {
|
||||
"version": "0.3.2",
|
||||
"from": "i@0.3.x"
|
||||
},
|
||||
"ncp": {
|
||||
"version": "0.4.2",
|
||||
"from": "ncp@0.4.x"
|
||||
}
|
||||
}
|
||||
},
|
||||
"winston": {
|
||||
"version": "0.6.2",
|
||||
"from": "winston@0.6.x",
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "0.1.22",
|
||||
"from": "async@0.1.x"
|
||||
},
|
||||
"colors": {
|
||||
"version": "0.6.2",
|
||||
"from": "colors@0.x.x"
|
||||
},
|
||||
"cycle": {
|
||||
"version": "1.0.3",
|
||||
"from": "cycle@1.0.x"
|
||||
},
|
||||
"eyes": {
|
||||
"version": "0.1.8",
|
||||
"from": "eyes@0.1.x"
|
||||
},
|
||||
"pkginfo": {
|
||||
"version": "0.2.3",
|
||||
"from": "pkginfo@0.2.x"
|
||||
},
|
||||
"request": {
|
||||
"version": "2.9.203",
|
||||
"from": "request@2.9.x"
|
||||
},
|
||||
"stack-trace": {
|
||||
"version": "0.0.9",
|
||||
"from": "stack-trace@0.0.x"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"redis": {
|
||||
"version": "0.8.3",
|
||||
"from": "redis@0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-0.8.3.tgz"
|
||||
},
|
||||
"reds": {
|
||||
"version": "0.2.4",
|
||||
"from": "reds@~0.2.4",
|
||||
"dependencies": {
|
||||
"natural": {
|
||||
"version": "0.1.17",
|
||||
"from": "natural@0.1.17",
|
||||
"dependencies": {
|
||||
"sylvester": {
|
||||
"version": "0.0.21",
|
||||
"from": "sylvester@>= 0.0.12"
|
||||
},
|
||||
"apparatus": {
|
||||
"version": "0.0.8",
|
||||
"from": "apparatus@>= 0.0.6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"redis": {
|
||||
"version": "0.7.2",
|
||||
"from": "redis@0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-0.7.2.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.2.6",
|
||||
"from": "rimraf@~2.2.6"
|
||||
},
|
||||
"rss": {
|
||||
"version": "0.2.1",
|
||||
"from": "rss@~0.2.0",
|
||||
"dependencies": {
|
||||
"xml": {
|
||||
"version": "0.0.12",
|
||||
"from": "xml@~0.0.4"
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.2.11",
|
||||
"from": "mime@~1.2.11"
|
||||
}
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "2.2.1",
|
||||
"from": "semver@~2.2.1"
|
||||
},
|
||||
"sitemap": {
|
||||
"version": "0.7.2",
|
||||
"from": "sitemap@~0.7.1"
|
||||
},
|
||||
"socket.io": {
|
||||
"version": "0.9.16",
|
||||
"from": "socket.io@~0.9.16",
|
||||
"dependencies": {
|
||||
"socket.io-client": {
|
||||
"version": "0.9.16",
|
||||
"from": "socket.io-client@0.9.16",
|
||||
"dependencies": {
|
||||
"uglify-js": {
|
||||
"version": "1.2.5",
|
||||
"from": "uglify-js@1.2.5"
|
||||
},
|
||||
"ws": {
|
||||
"version": "0.4.31",
|
||||
"from": "ws@0.4.x",
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "0.6.1",
|
||||
"from": "commander@~0.6.1"
|
||||
},
|
||||
"nan": {
|
||||
"version": "0.3.2",
|
||||
"from": "nan@~0.3.0"
|
||||
},
|
||||
"tinycolor": {
|
||||
"version": "0.0.1",
|
||||
"from": "tinycolor@0.x"
|
||||
},
|
||||
"options": {
|
||||
"version": "0.0.5",
|
||||
"from": "options@>=0.0.5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"xmlhttprequest": {
|
||||
"version": "1.4.2",
|
||||
"from": "xmlhttprequest@1.4.2"
|
||||
},
|
||||
"active-x-obfuscator": {
|
||||
"version": "0.0.1",
|
||||
"from": "active-x-obfuscator@0.0.1",
|
||||
"dependencies": {
|
||||
"zeparser": {
|
||||
"version": "0.0.5",
|
||||
"from": "zeparser@0.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyfile": {
|
||||
"version": "0.0.4",
|
||||
"from": "policyfile@0.0.4"
|
||||
},
|
||||
"base64id": {
|
||||
"version": "0.1.0",
|
||||
"from": "base64id@0.1.0"
|
||||
},
|
||||
"redis": {
|
||||
"version": "0.7.3",
|
||||
"from": "redis@0.7.x",
|
||||
"resolved": "https://registry.npmjs.org/redis/-/redis-0.7.3.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"socket.io-wildcard": {
|
||||
"version": "0.1.1",
|
||||
"from": "socket.io-wildcard@~0.1.1"
|
||||
},
|
||||
"string": {
|
||||
"version": "1.7.0",
|
||||
"from": "string@~1.7.0"
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "2.4.13",
|
||||
"from": "uglify-js@~2.4.0",
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.1.33",
|
||||
"from": "source-map@~0.1.33",
|
||||
"dependencies": {
|
||||
"amdefine": {
|
||||
"version": "0.1.0",
|
||||
"from": "amdefine@>=0.0.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.3.7",
|
||||
"from": "optimist@~0.3.5",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2",
|
||||
"from": "wordwrap@~0.0.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"uglify-to-browserify": {
|
||||
"version": "1.0.2",
|
||||
"from": "uglify-to-browserify@~1.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.6.0",
|
||||
"from": "underscore@~1.6.0"
|
||||
},
|
||||
"validator": {
|
||||
"version": "3.4.0",
|
||||
"from": "validator@~3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-3.4.0.tgz"
|
||||
},
|
||||
"winston": {
|
||||
"version": "0.7.3",
|
||||
"from": "winston@~0.7.2",
|
||||
"dependencies": {
|
||||
"colors": {
|
||||
"version": "0.6.2",
|
||||
"from": "colors@0.6.x"
|
||||
},
|
||||
"cycle": {
|
||||
"version": "1.0.3",
|
||||
"from": "cycle@1.0.x"
|
||||
},
|
||||
"eyes": {
|
||||
"version": "0.1.8",
|
||||
"from": "eyes@0.1.x"
|
||||
},
|
||||
"pkginfo": {
|
||||
"version": "0.3.0",
|
||||
"from": "pkginfo@0.3.x"
|
||||
},
|
||||
"request": {
|
||||
"version": "2.16.6",
|
||||
"from": "request@2.16.x",
|
||||
"dependencies": {
|
||||
"form-data": {
|
||||
"version": "0.0.10",
|
||||
"from": "form-data@~0.0.3",
|
||||
"dependencies": {
|
||||
"combined-stream": {
|
||||
"version": "0.0.4",
|
||||
"from": "combined-stream@~0.0.4",
|
||||
"dependencies": {
|
||||
"delayed-stream": {
|
||||
"version": "0.0.5",
|
||||
"from": "delayed-stream@0.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.2.11",
|
||||
"from": "mime@~1.2.7"
|
||||
},
|
||||
"hawk": {
|
||||
"version": "0.10.2",
|
||||
"from": "hawk@~0.10.2",
|
||||
"dependencies": {
|
||||
"hoek": {
|
||||
"version": "0.7.6",
|
||||
"from": "hoek@0.7.x"
|
||||
},
|
||||
"boom": {
|
||||
"version": "0.3.8",
|
||||
"from": "boom@0.3.x"
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "0.1.3",
|
||||
"from": "cryptiles@0.1.x"
|
||||
},
|
||||
"sntp": {
|
||||
"version": "0.1.4",
|
||||
"from": "sntp@0.1.x"
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-uuid": {
|
||||
"version": "1.4.1",
|
||||
"from": "node-uuid@~1.4.0"
|
||||
},
|
||||
"cookie-jar": {
|
||||
"version": "0.2.0",
|
||||
"from": "cookie-jar@~0.2.0"
|
||||
},
|
||||
"aws-sign": {
|
||||
"version": "0.2.0",
|
||||
"from": "aws-sign@~0.2.0"
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.2.0",
|
||||
"from": "oauth-sign@~0.2.0"
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.2.0",
|
||||
"from": "forever-agent@~0.2.0"
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.2.0",
|
||||
"from": "tunnel-agent@~0.2.0"
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "3.0.0",
|
||||
"from": "json-stringify-safe@~3.0.0"
|
||||
},
|
||||
"qs": {
|
||||
"version": "0.5.6",
|
||||
"from": "qs@~0.5.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"stack-trace": {
|
||||
"version": "0.0.9",
|
||||
"from": "stack-trace@0.0.x"
|
||||
}
|
||||
}
|
||||
},
|
||||
"xregexp": {
|
||||
"version": "2.0.0",
|
||||
"from": "xregexp@~2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
85
package.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "nodebb",
|
||||
"license": "GPLv3 or later",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "0.4.1",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/designcreateplay/NodeBB/"
|
||||
},
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "./nodebb start",
|
||||
"stop": "./nodebb stop",
|
||||
"test": "mocha ./tests -t 10000"
|
||||
},
|
||||
"dependencies": {
|
||||
"socket.io": "~0.9.16",
|
||||
"express": "3.2.0",
|
||||
"express-namespace": "~0.1.1",
|
||||
"passport": "~0.2.0",
|
||||
"passport-local": "0.1.6",
|
||||
"marked": "0.2.8",
|
||||
"async": "~0.2.8",
|
||||
"gm": "1.14.2",
|
||||
"gravatar": "1.0.6",
|
||||
"nconf": "~0.6.7",
|
||||
"sitemap": "~0.7.1",
|
||||
"reds": "~0.2.4",
|
||||
"winston": "~0.7.2",
|
||||
"rss": "~0.2.0",
|
||||
"prompt": "~0.2.11",
|
||||
"uglify-js": "~2.4.0",
|
||||
"validator": "~3.4.0",
|
||||
"cron": "~1.0.4",
|
||||
"semver": "~2.2.1",
|
||||
"string": "~1.7.0",
|
||||
"xregexp": "~2.0.0",
|
||||
"socket.io-wildcard": "~0.1.1",
|
||||
"bcryptjs": "~0.7.10",
|
||||
"nodebb-plugin-mentions": "~0.4.0",
|
||||
"nodebb-plugin-markdown": "~0.4.1",
|
||||
"nodebb-widget-essentials": "~0.0.21",
|
||||
"nodebb-theme-vanilla": "~0.0.19",
|
||||
"nodebb-theme-lavender": "~0.0.25",
|
||||
"less": "~1.6.3",
|
||||
"daemon": "~1.1.0",
|
||||
"underscore": "~1.6.0",
|
||||
"mkdirp": "~0.3.5",
|
||||
"rimraf": "~2.2.6"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"redis": "0.8.3",
|
||||
"hiredis": "~0.1.15",
|
||||
"connect-redis": "1.4.5",
|
||||
"mongodb": "~1.3.19",
|
||||
"connect-mongo": "0.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.13.0"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/designcreateplay/NodeBB/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Andrew Rodrigues",
|
||||
"email": "andrew@designcreateplay.com",
|
||||
"url": "https://github.com/psychobunny"
|
||||
},
|
||||
{
|
||||
"name": "Julian Lam",
|
||||
"email": "julian@designcreateplay.com",
|
||||
"url": "https://github.com/julianlam"
|
||||
},
|
||||
{
|
||||
"name": "Barış Soner Uşaklı",
|
||||
"email": "baris@designcreateplay.com",
|
||||
"url": "https://github.com/barisusakli"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "nodebb/public"
|
||||
}
|
||||
176
public/503.html
@@ -1,176 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Excessive Load Warning</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: #00A9EA;
|
||||
color: white;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
text-align: center;
|
||||
-webkit-transform-style: preserve-3d;
|
||||
-moz-transform-style: preserve-3d;
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 250px;
|
||||
color: #fff;
|
||||
opacity: 0.5;
|
||||
margin: 10px;
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
p strong {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
h1 {
|
||||
font-size: 125px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
p strong {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(50%);
|
||||
-ms-transform: translateY(50%);
|
||||
transform: translateY(50%);
|
||||
}
|
||||
|
||||
@-webkit-keyframes bounce {
|
||||
0%, 20%, 53%, 80%, 100% {
|
||||
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
|
||||
40%, 43% {
|
||||
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
-webkit-transform: translate3d(0, -30px, 0);
|
||||
transform: translate3d(0, -30px, 0);
|
||||
}
|
||||
|
||||
70% {
|
||||
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
-webkit-transform: translate3d(0, -15px, 0);
|
||||
transform: translate3d(0, -15px, 0);
|
||||
}
|
||||
|
||||
90% {
|
||||
-webkit-transform: translate3d(0,-4px,0);
|
||||
transform: translate3d(0,-4px,0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 20%, 53%, 80%, 100% {
|
||||
-webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
|
||||
40%, 43% {
|
||||
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
-webkit-transform: translate3d(0, -30px, 0);
|
||||
transform: translate3d(0, -30px, 0);
|
||||
}
|
||||
|
||||
70% {
|
||||
-webkit-transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
transition-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060);
|
||||
-webkit-transform: translate3d(0, -15px, 0);
|
||||
transform: translate3d(0, -15px, 0);
|
||||
}
|
||||
|
||||
90% {
|
||||
-webkit-transform: translate3d(0,-4px,0);
|
||||
transform: translate3d(0,-4px,0);
|
||||
}
|
||||
}
|
||||
|
||||
.bounce {
|
||||
-webkit-animation-name: bounce;
|
||||
animation-name: bounce;
|
||||
-webkit-transform-origin: center bottom;
|
||||
-ms-transform-origin: center bottom;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
.animated {
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.animated.infinite {
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.animated.hinge {
|
||||
-webkit-animation-duration: 2s;
|
||||
animation-duration: 2s;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
var count = 0,
|
||||
bounce = document.getElementById('click-me');
|
||||
bounce.onclick = function() {
|
||||
count++;
|
||||
bounce.className = '';
|
||||
setTimeout(function() {
|
||||
bounce.className = 'animated bounce';
|
||||
}, 50);
|
||||
|
||||
if (count > 5) {
|
||||
document.getElementById('hide').className = '';
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<div class="center">
|
||||
<h1 id="click-me" class="animated bounce">503</h1>
|
||||
<p>
|
||||
<strong>This forum is temporarily unavailable due to excessive load.</strong>
|
||||
</p>
|
||||
<p>
|
||||
We shouldn't be down for long. Please check back shortly. Sorry for the inconvenience!
|
||||
</p>
|
||||
<p>
|
||||
<small id="hide" class="hide">Alright. You can stop clicking... it's not going to make the site come back sooner!</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,16 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="321" height="75" viewBox="0 0 321 75">
|
||||
<defs>
|
||||
<radialGradient id="nodebb-a" cx="65.599%" cy="0%" r="169.036%" fx="65.599%" fy="0%" gradientTransform="matrix(-.5183 .5916 -.38031 -.80624 .996 -.388)">
|
||||
<stop offset="0%" stop-color="#2A6CBE"/>
|
||||
<stop offset="38.688%" stop-color="#2062BC"/>
|
||||
<stop offset="49.182%" stop-color="#1F5FBC"/>
|
||||
<stop offset="66.583%" stop-color="#1C5ABD"/>
|
||||
<stop offset="85.354%" stop-color="#1956BC"/>
|
||||
<stop offset="100%" stop-color="#1851BE"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<g fill="none" transform="translate(1 1)">
|
||||
<path fill="#000306" stroke="#000306" stroke-width=".275" d="M195.810017,20 C210.691849,20 221.256813,28.7272272 221.484017,45.845679 C221.490864,46.3615394 221.461776,47.1194739 221.396754,48.1194827 C221.328352,49.1714504 220.455158,49.9897119 219.400969,49.9897119 L179.110557,49.9897119 C179.110557,56.6336943 182.723978,64.2757202 196.378026,64.2757202 C204.088827,64.2757202 211.625406,61.928339 218.987763,57.2335766 L219.553745,56.8677968 C220.246813,56.4139645 221.17656,56.6079034 221.630392,57.3009716 C221.757029,57.4943637 221.837254,57.7144077 221.864806,57.943925 C222.131013,60.1615294 222.004084,61.5832211 221.484017,62.2089999 C220.452051,63.4507301 210.901843,73 196.378026,73 C180.58738,73 169,61.8765432 169,46.3909465 C169,31.3415638 180.019372,20 195.810017,20 Z M162.92733,0 C164.031899,-4.2495073e-16 164.92733,0.8954305 164.92733,2 L164.928075,44.1266361 C164.975781,44.8118124 165,45.5058194 165,46.2079256 C165,61.5334332 153.460581,73 138,73 C122.427386,73 111,61.5334332 111,46.2079256 C111,30.882418 122.427386,19.4158511 138,19.4158511 C144.981278,19.4158511 151.163029,21.7538895 155.843826,25.7295173 L155.843557,4.54188628 C155.843557,2.03347175 157.877029,4.60788274e-16 160.385444,0 L162.92733,0 Z M81,20 C96.4605809,20 108,31.3415638 108,46.5 C108,61.6584362 96.4605809,73 81,73 C65.4273859,73 54,61.6584362 54,46.5 C54,31.3415638 65.4273859,20 81,20 Z M27.324263,20 C41.6099773,20 50,29.1649049 50,41.7801268 L50,67.5 C50,69.4329966 48.4329966,71 46.5,71 L42.2494331,71 C41.1448636,71 40.2494331,70.1045695 40.2494331,69 L40.2494331,42.5348837 C40.2494331,34.3403805 35.8276644,28.6257928 26.3038549,28.6257928 C10.0816327,28.6257928 9.75718821,35.7795705 9.75069932,44.6831843 L9.75056689,71 L2,71 C0.8954305,71 1.3527075e-16,70.1045695 0,69 L0,35.2932975 L0,35.2932975 C0,24.3935718 19.1609977,20 27.324263,20 Z M81,28.7242798 C70.8049793,28.7242798 63.6348548,36.4670782 63.6348548,46.5 C63.6348548,56.3148148 70.8049793,64.2757202 81,64.2757202 C91.1950207,64.2757202 98.3651452,56.3148148 98.3651452,46.5 C98.3651452,36.4670782 91.1950207,28.7242798 81,28.7242798 Z M138,28.2362872 C127.804979,28.2362872 120.634855,36.0644241 120.634855,46.2079256 C120.634855,56.1309161 127.804979,64.179564 138,64.179564 C148.195021,64.179564 155.365145,56.1309161 155.365145,46.2079256 C155.365145,36.0644241 148.195021,28.2362872 138,28.2362872 Z M195.810017,28.7242798 C186.721876,28.7242798 180.473779,33.9588477 179.224159,41.8106996 L211.259858,41.8106996 C210.010238,33.9588477 204.443752,28.7242798 195.810017,28.7242798 Z"/>
|
||||
<path fill="url(#nodebb-a)" d="M277,19 L276.999615,57.7303365 C276.99251,64.180962 276.88949,68.6242617 276.88949,73 L276.88949,73 L255.649055,73 C242.962262,73 236,67.06 236,57.7257143 C236,51.4771429 239.558488,46.9257143 245.1283,45.0742857 C240.564149,43.1457143 237.779243,38.8257143 237.779243,33.5028571 C237.779243,24.8628571 244.664149,19 256.732074,19 L256.732074,19 L277,19 Z M299.267926,19 C311.335851,19 318.220757,24.8628571 318.220757,33.5028571 C318.220757,38.8257143 315.435851,43.1457143 310.8717,45.0742857 C316.441512,46.9257143 320,51.4771429 320,57.7257143 C320,67.06 313.037738,73 300.350945,73 L279.11051,73 L279.11051,73 C279.11051,68.6242617 279.00749,64.180962 279.000385,57.7303365 L279,19 L299.267926,19 Z M266.092452,49.8571429 L256.499999,49.8571429 C250.156602,49.8571429 246.984904,52.2485714 246.984904,56.9542857 C246.984904,61.8914286 249.924527,64.36 255.726414,64.36 L255.726414,64.36 L260.754716,64.36 C264.777358,64.36 266.092452,62.4314286 266.092452,56.5685714 L266.092452,56.5685714 L266.092452,49.8571429 Z M299.500001,49.8571429 L289.907548,49.8571429 L289.907548,56.5685714 C289.907548,62.4314286 291.222642,64.36 295.245284,64.36 L300.273586,64.36 C306.075473,64.36 309.015096,61.8914286 309.015096,56.9542857 C309.015096,52.2485714 305.843398,49.8571429 299.500001,49.8571429 Z M266.169811,27.64 L257.350942,27.64 C251.703772,27.64 248.764149,30.1857143 248.764149,34.5057143 C248.764149,38.8257143 251.703772,41.3714286 257.350942,41.3714286 L257.350942,41.3714286 L266.169811,41.3714286 L266.169811,27.64 Z M298.649058,27.64 L289.830189,27.64 L289.830189,41.3714286 L298.649058,41.3714286 C304.296228,41.3714286 307.235851,38.8257143 307.235851,34.5057143 C307.235851,30.1857143 304.296228,27.64 298.649058,27.64 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 86 B |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
@@ -1,14 +0,0 @@
|
||||
# Important note about localising NodeBB
|
||||
|
||||
The files here are read-only and overwritten daily (if there are changes) by the
|
||||
helper bot [Misty](https://github.com/nodebb-misty).
|
||||
|
||||
Our localisation efforts are handled via [our Transifex Project](https://explore.transifex.com/nodebb/nodebb/),
|
||||
and any pull requests made to this directory will be automatically closed because
|
||||
localisations can go out-of-sync when edited directly.
|
||||
|
||||
If there are non-localised strings and you cannot find them in Transifex, please
|
||||
[open a new issue on our bug tracker](https://github.com/NodeBB/NodeBB/issues/new)
|
||||
so we can take a look.
|
||||
|
||||
Thank you for helping localise NodeBB!
|
||||
3
public/language/TODO
Normal file
@@ -0,0 +1,3 @@
|
||||
NPM INSTALL
|
||||
For now, language packs will be stored here. Eventually, will be moved to server side to allow for npm install-ability.
|
||||
When that happens, server code will generate compressed JSON language files in this folder.
|
||||
@@ -1,3 +0,0 @@
|
||||
# The files here are not meant to be edited directly
|
||||
|
||||
Please see the → [Internalization README](../README.md).
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"alert.confirm-rebuild-and-restart": "هل أنت متأكد إنك تريد إعادة بناء وتشغيل الـ NodeBB؟",
|
||||
"alert.confirm-restart": "هل تريد بالتأكيد إعادة تشغيل NodeBB؟",
|
||||
|
||||
"acp-title": "لوحة تحكم إدارة NodeBB | %1",
|
||||
"settings-header-contents": "محتويات",
|
||||
"changes-saved": "Changes Saved",
|
||||
"changes-saved-message": "Your changes to the NodeBB configuration have been saved.",
|
||||
"changes-not-saved": "لم يتم حفظ التغييرات",
|
||||
"changes-not-saved-message": "حدثت مشكلة أثناء حفظ التغييرات في NodeBB. (%1)",
|
||||
"save-changes": "Save changes",
|
||||
"min": "Min:",
|
||||
"max": "Max:",
|
||||
"view": "View",
|
||||
"edit": "Edit",
|
||||
"add": "Add",
|
||||
"select-icon": "Select Icon"
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"cache": "Cache",
|
||||
"post-cache": "التخزين المؤقت للمشاركات",
|
||||
"group-cache": "التخزين المؤقت للمجموعات",
|
||||
"local-cache": "تخزين مؤقت محلي",
|
||||
"object-cache": "تخزين مؤقت للأشياء",
|
||||
"percent-full": "1% كاملة",
|
||||
"post-cache-size": "حجم التخزين المؤقت للمشاركات",
|
||||
"items-in-cache": "العناصر في التخزين المؤقت"
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"x-b": "٪1 بايت",
|
||||
"x-mb": "٪1 ميقا بايت",
|
||||
"x-gb": "٪1 قيقا بايت",
|
||||
"uptime-seconds": "مدة التشغيل بالثواني",
|
||||
"uptime-days": "مدة التشغيل بالأيام",
|
||||
|
||||
"mongo": "MongoDB",
|
||||
"mongo.version": "اصدار MongoDB",
|
||||
"mongo.storage-engine": "محرك التخزين",
|
||||
"mongo.collections": "التجميعات",
|
||||
"mongo.objects": "العناصر",
|
||||
"mongo.avg-object-size": "معدل حجم العناصر",
|
||||
"mongo.data-size": "حجم البيانات",
|
||||
"mongo.storage-size": "حجم التخرين",
|
||||
"mongo.index-size": "حجم الفهرس",
|
||||
"mongo.file-size": "حجم الملف",
|
||||
"mongo.resident-memory": "الذاكرة الساكنة",
|
||||
"mongo.virtual-memory": "الذاكرة الإفتراضية",
|
||||
"mongo.mapped-memory": "Mapped Memory",
|
||||
"mongo.bytes-in": "البايتات الواردة",
|
||||
"mongo.bytes-out": "البايتات الصادرة",
|
||||
"mongo.num-requests": "عدد الطلبات",
|
||||
"mongo.raw-info": "معلومات MongoDB الأولية",
|
||||
"mongo.unauthorized": "لم يستطع NodeBB من الاستعلام عن احصاءات قواعد البيانات ل MongoDB. الرجاء التأكد من أن المستخدم في NodeBB يحتوي على دور "clusterMonitor" ال "admin" لقواعد البيانات.",
|
||||
|
||||
"redis": "Redis",
|
||||
"redis.version": "إصدار Redis",
|
||||
"redis.keys": "المفاتيح",
|
||||
"redis.expires": "ينتهي ",
|
||||
"redis.avg-ttl": "متوسط وقت الانعاش",
|
||||
"redis.connected-clients": "العميل المتصل",
|
||||
"redis.connected-slaves": "البدلاء المتصلين",
|
||||
"redis.blocked-clients": "العملاء المحظورون",
|
||||
"redis.used-memory": "الذاكرة المستخدمة",
|
||||
"redis.memory-frag-ratio": "نسبة تجزئة الذاكرة",
|
||||
"redis.total-connections-recieved": "إجمالي الاتصالات المستلمة",
|
||||
"redis.total-commands-processed": "إجمالي الأوامر التي تمت معالجتها",
|
||||
"redis.iops": "العمليات اللحظية في الثانية",
|
||||
"redis.iinput": "الإدخال الفوري في الثانية",
|
||||
"redis.ioutput": "المخرجات الفورية في الثانية",
|
||||
"redis.total-input": "مجموع المدخلات",
|
||||
"redis.total-output": "مجموع المخرجات",
|
||||
|
||||
"redis.keyspace-hits": "المفاتيح المضغوطة",
|
||||
"redis.keyspace-misses": "المفاتيح المخفقة",
|
||||
"redis.raw-info": "معلومات Redis الأولية",
|
||||
|
||||
"postgres": "Postgres",
|
||||
"postgres.version": "إصدار PostgreSQL",
|
||||
"postgres.raw-info": "معلومات Postgres الأولية"
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"errors": "Errors",
|
||||
"figure-x": "شكل %1",
|
||||
"error-events-per-day": "<code>%1</code> حدث كل يوم ",
|
||||
"error.404": "404 لم يتم العثور",
|
||||
"error.503": "503 الخدمة غير متوفرة",
|
||||
"manage-error-log": "إدارة سجل الأخطاء",
|
||||
"export-error-log": "تصدير سجل الأخطاء (CSV)",
|
||||
"clear-error-log": "محو سجل الأخطاء",
|
||||
"route": "مسار",
|
||||
"count": "عدد",
|
||||
"no-routes-not-found": "لا توجد اخطاء 404!",
|
||||
"clear404-confirm": "هل تريد بالتأكيد محو سجلات الخطأ 404؟",
|
||||
"clear404-success": "أخطاء \"404 لم يتم العثور\" تم محوها بنجاح"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"events": "أحداث",
|
||||
"no-events": "لا توجد أحداث",
|
||||
"control-panel": "لوحة تحكم الأحداث",
|
||||
"delete-events": "حذف الاحداث",
|
||||
"confirm-delete-all-events": "هل أنت متاكد أنك تريد حذف كل سجل اﻻحداث؟",
|
||||
"filters": "تصفية",
|
||||
"filters-apply": "تطبيق التصفية",
|
||||
"filter-type": "نوع الحدث",
|
||||
"filter-start": "تاريخ البدء",
|
||||
"filter-end": "تاريخ الانتهاء",
|
||||
"filter-per-page": "لكل صفحة"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"logs": "السجلات",
|
||||
"control-panel": "لوحة تحكم السجلات",
|
||||
"reload": "إعادة تحميل السجلات",
|
||||
"clear": "محو السجلات",
|
||||
"clear-success": "تم محو السجلات!"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"customise": "Customise",
|
||||
"custom-css": "Custom CSS/SASS",
|
||||
"custom-css.description": "Enter your own CSS/SASS declarations here, which will be applied after all other styles.",
|
||||
"custom-css.enable": "Enable Custom CSS/SASS",
|
||||
|
||||
"custom-js": "Javascript مخصصة",
|
||||
"custom-js.description": "أدخل Javascript الخاص بك هنا. سيتم تنفيذها بعد تحميل الصفحة بالكامل.",
|
||||
"custom-js.enable": "تفعيل Javascript المخصصة",
|
||||
|
||||
"custom-header": "ترويسة مخصصة",
|
||||
"custom-header.description": "أدخل وسوم HTML المخصصة هنا (مثل: Meta Tags, وغيرها)، والتي سيتم تضمينها لجزئية <code><head></code> من ترميز المنتدى. يُسمح بعلامات البرمجة النصية، ولكن لا يُنصح بها ، نظرًا لأن علامة التبويب <a href=\"#custom-js\" data-toggle=\"tab\"> Custom Javascript </a> متاحة.",
|
||||
"custom-header.enable": "تفعيل الترويسة المخصصة",
|
||||
|
||||
"custom-css.livereload": "تفعيل إعادة التحميل المباشرة",
|
||||
"custom-css.livereload.description": "فعل هذا الخيار لإجبار جميع الجلسات في الأجهزة التي قمت بتسجيل الدخول فيها بحسابك على التحديث عند النقر على زر الحفظ",
|
||||
"bsvariables": "_variables.scss",
|
||||
"bsvariables.description": "Override bootstrap variables here. You can also use a tool like <a href=\"https://bootstrap.build/app\">bootstrap.build</a> and paste the output here.<br/>Changes require a rebuild & restart.",
|
||||
"bsvariables.enable": "Enable _variables.scss"
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"skins": "Skins",
|
||||
"bootswatch-skins": "Bootswatch Skins",
|
||||
"custom-skins": "Custom Skins",
|
||||
"add-skin": "Add Skin",
|
||||
"save-custom-skins": "Save Custom Skins",
|
||||
"save-custom-skins-success": "Custom skins saved successfully",
|
||||
"custom-skin-name": "Custom Skin Name",
|
||||
"custom-skin-variables": "Custom Skin Variables",
|
||||
"loading": "جاري تحميل السمات...",
|
||||
"homepage": "الصفحة الرئيسية",
|
||||
"select-skin": "إختيار السمة",
|
||||
"revert-skin": "Revert Skin",
|
||||
"current-skin": "السمة الحالية",
|
||||
"skin-updated": "تم تحديث السمة",
|
||||
"applied-success": "تم تطبيق السمة %1 بنجاح",
|
||||
"revert-success": "تم إستعادة الألوان الاساسية للسمة"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"themes": "Themes",
|
||||
"checking-for-installed": "جاري التحقق من القوالب المثبتة...",
|
||||
"homepage": "الصفحة الرئيسية",
|
||||
"select-theme": "إختيار القالب",
|
||||
"revert-theme": "Revert Theme",
|
||||
"current-theme": "القالب المستخدم حالياً",
|
||||
"no-themes": "لم يتم العثور على قوالب مثبتة",
|
||||
"revert-confirm": "هل أنت متأكد من أنك ترغب في استعادة قااب NodeBB الافتراضي؟",
|
||||
"theme-changed": "تم تغيير القالب",
|
||||
"revert-success": "لقد قمت بنجاح بإستعادة القالب الأساسي لـNodeBB",
|
||||
"restart-to-activate": "يرجى إعادة بناء وإعادة تشغيل NodeBB لتنشيط هذا الثيم."
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
{
|
||||
"forum-traffic": "حركة المنتدى",
|
||||
"page-views": "مشاهدات الصفحات",
|
||||
"unique-visitors": "زائرين فريدين",
|
||||
"logins": "عمليات تسجيل الدخول",
|
||||
"new-users": "مستخدم جديد",
|
||||
"posts": "مشاركات",
|
||||
"topics": "مواضيع",
|
||||
"page-views-seven": "آخر 7 ايام",
|
||||
"page-views-thirty": "آخر 30 يوماً",
|
||||
"page-views-last-day": "آخر 24 ساعة",
|
||||
"page-views-custom": "مدة زمنية مخصصة",
|
||||
"page-views-custom-start": "بداية المدة",
|
||||
"page-views-custom-end": "نهاية المده",
|
||||
"page-views-custom-help": "أدخل نطاقا زمنيا لمرات مشاهدة الصفحات التي ترغب في عرضها. إذا لم يظهر منتقي التاريخ، فإن التنسيق المقبول هو <code>YYYY-MM-DD</code>",
|
||||
"page-views-custom-error": "الرجاء إدخال نطاق تاريخ صالح بالتنسيق <code>YYYY-MM-DD</code>",
|
||||
|
||||
"stats.yesterday": "الأمس",
|
||||
"stats.today": "اليوم",
|
||||
"stats.last-week": "الاسبوع الماضي",
|
||||
"stats.this-week": "هذا الاسبوع",
|
||||
"stats.last-month": "الشهر الماضي",
|
||||
"stats.this-month": "هذا الشهر",
|
||||
"stats.all": "كل الوقت",
|
||||
|
||||
"updates": "تحديثات",
|
||||
"running-version": "المنتدى يعمل حاليا على <strong>NodeBB الإصدار<span id=\"version\">%1</span></strong>.",
|
||||
"keep-updated": "تأكد دائما من أن NodeBB يعمل على احدث إصدار للحصول على أحدث التصحيحات الأمنية وإصلاحات الأخطاء.",
|
||||
"up-to-date": "You are <strong>up-to-date</strong> <i class=\"fa fa-check\"></i>",
|
||||
"upgrade-available": "A new version (v%1) has been released. Consider <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">upgrading your NodeBB</a>.",
|
||||
"prerelease-upgrade-available": "This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">upgrading your NodeBB</a>.",
|
||||
"prerelease-warning": "This is a <strong>pre-release</strong> version of NodeBB. Unintended bugs may occur. <i class=\"fa fa-exclamation-triangle\"></i>",
|
||||
"fallback-emailer-not-found": "مرسل البريد الإلكتروني الاحتياطي غير موجود!",
|
||||
"running-in-development": "Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator",
|
||||
"latest-lookup-failed": "Failed to look up latest available version of NodeBB",
|
||||
|
||||
"notices": "إشعارات",
|
||||
"restart-not-required": "إعادة التشغيل غير مطلوب",
|
||||
"restart-required": "إعادة التشغيل مطلوبة",
|
||||
"search-plugin-installed": "إضافة البحث منصبة",
|
||||
"search-plugin-not-installed": "إضافة البحث غير منصبة",
|
||||
"search-plugin-tooltip": "نصب إضافة البحث من صفحة الإضافات البرمجية لتنشيط وظيفة البحث",
|
||||
|
||||
"control-panel": "التحكم بالنظام",
|
||||
"rebuild-and-restart": "أعد البناء & وأعد التشغيل",
|
||||
"restart": "أعد التشغيل",
|
||||
"restart-warning": "ستؤدي إعادة بناء أو إعادة تشغيل NodeBB إلى إنقطاع الاتصال بالمنتدى لبضع ثوان.",
|
||||
"restart-disabled": "تم تعطيل إعادة بناء NodeBB وإعادة تشغيله حيث لا يبدو أنك تقوم بتشغيله عبر البرنامج الخفي المناسب.",
|
||||
"maintenance-mode": "وضع الصيانة",
|
||||
"maintenance-mode-title": "انقر هنا لإعداد وضع الصيانة لـNodeBB",
|
||||
"dark-mode": "Dark Mode",
|
||||
"realtime-chart-updates": "التحديث الفوري للرسم البياني",
|
||||
|
||||
"active-users": "المستخدمين النشطين",
|
||||
"active-users.users": "الأعضاء",
|
||||
"active-users.guests": "الزوار",
|
||||
"active-users.total": "المجموع",
|
||||
"active-users.connections": "روابط الاتصال",
|
||||
|
||||
"guest-registered-users": "الزائر مقابل المستخدمين المسجلين",
|
||||
"guest": "الزائر",
|
||||
"registered": "مسجل",
|
||||
|
||||
"user-presence": "تواجد المستخدمين",
|
||||
"on-categories": "في قائمة الأقسام",
|
||||
"reading-posts": "قراءة المشاركات",
|
||||
"browsing-topics": "تصفح المواضيع",
|
||||
"recent": "الأخيرة",
|
||||
"unread": "غير مقروء",
|
||||
|
||||
"high-presence-topics": "مواضيع ذات حضور قوي",
|
||||
"popular-searches": "عمليات البحث الشائعة",
|
||||
|
||||
"graphs.page-views": "مشاهدات الصفحة",
|
||||
"graphs.page-views-registered": "زيارات الصفحات المسجلة",
|
||||
"graphs.page-views-guest": "زيارات الصفحات للزوار",
|
||||
"graphs.page-views-bot": "زيارات الصفحات الآلية",
|
||||
"graphs.unique-visitors": "زوار فريدين",
|
||||
"graphs.registered-users": "مستخدمين مسجلين",
|
||||
"graphs.guest-users": "المستخدمين الزوار",
|
||||
"last-restarted-by": "آخر إعادة تشغيل بواسطة",
|
||||
"no-users-browsing": "لا يوجد مستخدمين يتصفحون",
|
||||
|
||||
"back-to-dashboard": "العودة للوحة التحكم",
|
||||
"details.no-users": "لم ينضم أي مستخدم خلال الإطار الزمني المحدد",
|
||||
"details.no-topics": "لم يتم نشر أي مواضيع خلال الإطار الزمني المحدد",
|
||||
"details.no-searches": "لم يتم إجراء أي بحث خلال الإطار الزمني المحدد",
|
||||
"details.no-logins": "لم يوثق أي تسجيل دخول خلال الإطار الزمني المحدد",
|
||||
"details.logins-static": "يقوم NodeBB بحفظ بيانات الجلسة لمدة ٪1 يوم/أيام فقط، ولذا فإن هذا الجدول أدناه سيعرض فقط أحدث الجلسات النشطة",
|
||||
"details.logins-login-time": "وقت تسجيل الدخول",
|
||||
"start": "بدء",
|
||||
"end": "إنهاء",
|
||||
"filter": "تصفية",
|
||||
"view-as-json": "View as JSON",
|
||||
"expand-analytics": "Expand analytics",
|
||||
"clear-search-history": "Clear Search History",
|
||||
"clear-search-history-confirm": "Are you sure you want to clear entire search history?"
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"you-are-on": "أنت في <strong> %1:%2 </strong>",
|
||||
"ip": "رقم الآي بي <strong> %1 </strong>",
|
||||
"nodes-responded": "عدد %1 نقطة/نقاط استجابوا خلال %2 جزء من الثانية.",
|
||||
"host": "المضيف",
|
||||
"primary": "primary / run jobs",
|
||||
"pid": "pid",
|
||||
"nodejs": "nodejs",
|
||||
"online": "online",
|
||||
"git": "git",
|
||||
"process-memory": "process memory",
|
||||
"system-memory": "system memory",
|
||||
"used-memory-process": "Used memory by process",
|
||||
"used-memory-os": "Used system memory",
|
||||
"total-memory-os": "Total system memory",
|
||||
"load": "system load",
|
||||
"cpu-usage": "cpu usage",
|
||||
"uptime": "uptime",
|
||||
|
||||
"registered": "Registered",
|
||||
"sockets": "Sockets",
|
||||
"guests": "Guests",
|
||||
|
||||
"info": "Info"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"logger": "Logger",
|
||||
"logger-settings": "إعدادات المسجِّل",
|
||||
"description": "من خلال تمكين مربعات الاختيار ، ستتلقى سجلات إلى جهازك الطرفي. إذا حددت مسارًا ، فسيتم بعد ذلك حفظ السجلات في ملف بدلاً من ذلك. يعد تسجيل HTTP مفيدًا لجمع الإحصائيات حول من ومتى وماذا يصل الأشخاص في المنتدى. بالإضافة إلى تسجيل طلبات HTTP ، يمكننا أيضًا تسجيل أحداث socket.io. يمكن أن يكون تسجيل Socket.io ، جنبًا إلى جنب مع شاشة redis-cli ، مفيدًا جدًا في تعلم العناصر الداخلية لـ NodeBB.",
|
||||
"explanation": "ما عليك سوى تحديدأو/ إلغاء تحديد إعدادات التسجيل لتمكين أو تعطيل التسجيل أثناء التنقل. لا حاجة لإعادة التشغيل.",
|
||||
"enable-http": "تمكين سجلات HTTP",
|
||||
"enable-socket": "تفعيل تسجيل أحداث socket.io",
|
||||
"file-path": "مسار ملف السجل",
|
||||
"file-path-placeholder": "/path/to/log/file.log ::: اتركه فارغا لاظهار السجلات لنافذة الطرفية",
|
||||
|
||||
"control-panel": "لوحة تحكم المسجل",
|
||||
"update-settings": "تحديث إعدادات المسجل"
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
{
|
||||
"plugins": "Plugins",
|
||||
"trending": "Trending",
|
||||
"installed": "منصبة",
|
||||
"active": "مفعلة",
|
||||
"inactive": "معطلة",
|
||||
"out-of-date": "غير محدثة",
|
||||
"none-found": "لم يتم العثور على إضافات",
|
||||
"none-active": "لا توجد إضافات مفعلة",
|
||||
"find-plugins": "العثور على الإضافات",
|
||||
|
||||
"plugin-search": "البحث عن الإضافات",
|
||||
"plugin-search-placeholder": "جاري البحث عن الإضافات...",
|
||||
"submit-anonymous-usage": "Submit anonymous plugin usage data.",
|
||||
"reorder-plugins": "إعادة ترتيب الإضافات",
|
||||
"order-active": "ترتيب الإضافات المفعلة",
|
||||
"dev-interested": "هل انته مهتم ببرمجة إضافات لـNodeBB؟",
|
||||
"docs-info": "دليل كامل حول برمجة الإضافات بالإمكان العثور عليه في <a target=\"_blank\" href=\"https://docs.nodebb.org/development/plugins/\"> NodeBB Docs Portal</a>.",
|
||||
|
||||
"order.description": "بعض الإضافات تعمل بشكل مثالي عندما يتم تفعيلها قبل أو بعد الإضافات الأخرى.",
|
||||
"order.explanation": "يتم تحميل الإضافات حسب الترتيب المحدد هنا، من الأعلى إلى الأسفل",
|
||||
|
||||
"plugin-item.themes": "القوالب",
|
||||
"plugin-item.deactivate": "تعطيل",
|
||||
"plugin-item.activate": "تفعيل",
|
||||
"plugin-item.install": "تنصيب",
|
||||
"plugin-item.uninstall": "إلغاء التنصيب",
|
||||
"plugin-item.settings": "الإعدادات",
|
||||
"plugin-item.installed": "المنصبة",
|
||||
"plugin-item.latest": "الأحدث",
|
||||
"plugin-item.upgrade": "ترقية",
|
||||
"plugin-item.more-info": "لمزيد من المعلومات:",
|
||||
"plugin-item.unknown": "غير معروف",
|
||||
"plugin-item.unknown-explanation": "تعذر تحديد حالة هذه الإضافة، ربما بسبب خطأ في الإعدادات.",
|
||||
"plugin-item.compatible": "This plugin works on NodeBB %1",
|
||||
"plugin-item.not-compatible": "This plugin has no compatibility data, make sure it works before installing on your production environment.",
|
||||
|
||||
"alert.enabled": "الإضافة مفعلة",
|
||||
"alert.disabled": "الإضافة معطلة",
|
||||
"alert.upgraded": "الإضافة مرقاة",
|
||||
"alert.installed": "الإضافة منصبة",
|
||||
"alert.uninstalled": "تم إلغاء تنصيب الإضافة",
|
||||
"alert.activate-success": "Please rebuild and restart your NodeBB to fully activate this plugin",
|
||||
"alert.deactivate-success": "تم تعطيل الإضافة بنجاح",
|
||||
"alert.upgrade-success": "Please rebuild and restart your NodeBB to fully upgrade this plugin.",
|
||||
"alert.install-success": "تم تثبيت الإضافة بنجاح، يرجى تفعيلها.",
|
||||
"alert.uninstall-success": "تم تعطيل الإضافة وإلغاء تنصيبها بنجاح.",
|
||||
"alert.suggest-error": "<p>NodeBB could not reach the package manager, proceed with installation of latest version?</p><div class=\"alert alert-danger\"><strong>Server returned (%1)</strong>: %2</div>",
|
||||
"alert.package-manager-unreachable": "<p>NodeBB could not reach the package manager, an upgrade is not suggested at this time.</p>",
|
||||
"alert.incompatible": "<p>Your version of NodeBB (v%1) is only cleared to upgrade to v%2 of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.</p>",
|
||||
"alert.possibly-incompatible": "<div class=\"alert alert-warning\"><p><strong>No Compatibility Information Found</strong></p><p>This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.</p></div><p>In the event that NodeBB cannot boot properly:</p><pre><code>$ ./nodebb reset plugin=\"%1\"</code></pre><p>Continue installation of latest version of this plugin?</p>",
|
||||
"alert.reorder": "Plugins Re-ordered",
|
||||
"alert.reorder-success": "Please rebuild and restart your NodeBB to fully complete the process.",
|
||||
|
||||
"license.title": "معلومات ترخيص الإضافة",
|
||||
"license.intro": "The plugin <strong>%1</strong> is licensed under the %2. Please read and understand the license terms prior to activating this plugin.",
|
||||
"license.cta": "هل ترغب بالاستمرار في تفعيل هذه الإضافة؟"
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"rewards": "المكافآت",
|
||||
"add-reward": "Add reward",
|
||||
"condition-if-users": "إذا كان للمستخدم",
|
||||
"condition-is": "يكون: ",
|
||||
"condition-then": "عندئذ:",
|
||||
"max-claims": "عدد المرات التي يمكن فيها المطالبة بالمكافأة",
|
||||
"zero-infinite": "أدخل 0 للقيمة اللانهائية",
|
||||
"select-reward": "Select reward",
|
||||
"delete": "احذف",
|
||||
"enable": "مكّن",
|
||||
"disable": "عطّل",
|
||||
|
||||
"alert.delete-success": "المكافآت المحذوفة بنجاح",
|
||||
"alert.no-inputs-found": "مكافأة غير قانونية - لم يتم العثور على مدخلات!",
|
||||
"alert.save-success": "المكافآت المحفوظة بنجاح"
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"widgets": "Widgets",
|
||||
"available": "Available Widgets",
|
||||
"explanation": "Select a widget from the dropdown menu and then drag and drop it into a template's widget area on the left.",
|
||||
"none-installed": "No widgets found! Activate the widget essentials plugin in the <a href=\"%1\">plugins</a> control panel.",
|
||||
"clone-from": "Clone widgets from",
|
||||
"containers.available": "Available Containers",
|
||||
"containers.explanation": "Drag and drop on top of any widget",
|
||||
"containers.none": "None",
|
||||
"container.well": "Well",
|
||||
"container.jumbotron": "Jumbotron",
|
||||
"container.card": "Card",
|
||||
"container.card-header": "Card Header",
|
||||
"container.card-body": "Card Body",
|
||||
"container.alert": "Alert",
|
||||
|
||||
"alert.confirm-delete": "Are you sure you wish to delete this widget?",
|
||||
"alert.updated": "Widgets Updated",
|
||||
"alert.update-success": "Successfully updated widgets",
|
||||
"alert.clone-success": "Successfully cloned widgets",
|
||||
|
||||
"error.select-clone": "Please select a page to clone from",
|
||||
|
||||
"title": "Title",
|
||||
"title.placeholder": "Title (only shown on some containers)",
|
||||
"container": "Container",
|
||||
"container.placeholder": "Drag and drop a container or enter HTML here.",
|
||||
"show-to-groups": "Show to groups",
|
||||
"hide-from-groups": "Hide from groups",
|
||||
"start-date": "Start date",
|
||||
"end-date": "End date",
|
||||
"hide-on-mobile": "Hide on mobile",
|
||||
"hide-drafts": "Hide drafts",
|
||||
"show-drafts": "Show drafts"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"manage-admins-and-mods": "Manage Admins & Mods",
|
||||
"administrators": "Administrators",
|
||||
"global-moderators": "Global Moderators",
|
||||
"moderators": "Moderators",
|
||||
"no-global-moderators": "No Global Moderators",
|
||||
"no-sub-categories": "No subcategories",
|
||||
"view-children": "View children (%1)",
|
||||
"no-moderators": "No Moderators",
|
||||
"add-administrator": "Add Administrator",
|
||||
"add-global-moderator": "Add Global Moderator",
|
||||
"add-moderator": "Add Moderator"
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
"manage-categories": "Manage Categories",
|
||||
"add-category": "Add category",
|
||||
"jump-to": "Jump to...",
|
||||
"settings": "اعدادات القسم",
|
||||
"edit-category": "Edit Category",
|
||||
"privileges": "الصلاحيات",
|
||||
"back-to-categories": "Back to categories",
|
||||
"name": "Category Name",
|
||||
"description": "Category Description",
|
||||
"bg-color": "Background Colour",
|
||||
"text-color": "Text Colour",
|
||||
"bg-image-size": "Background Image Size",
|
||||
"custom-class": "Custom Class",
|
||||
"num-recent-replies": "# of Recent Replies",
|
||||
"ext-link": "External Link",
|
||||
"subcategories-per-page": "Subcategories per page",
|
||||
"is-section": "Treat this category as a section",
|
||||
"post-queue": "Post queue",
|
||||
"tag-whitelist": "Tag Whitelist",
|
||||
"upload-image": "Upload Image",
|
||||
"upload": "Upload",
|
||||
"delete-image": "Remove",
|
||||
"category-image": "Category Image",
|
||||
"image-and-icon": "Image & Icon",
|
||||
"parent-category": "Parent Category",
|
||||
"optional-parent-category": "(Optional) Parent Category",
|
||||
"top-level": "Top Level",
|
||||
"parent-category-none": "(None)",
|
||||
"copy-parent": "Copy Parent",
|
||||
"copy-settings": "Copy Settings From",
|
||||
"optional-clone-settings": "(Optional) Clone Settings From Category",
|
||||
"clone-children": "Clone Children Categories And Settings",
|
||||
"purge": "Purge Category",
|
||||
|
||||
"enable": "Enable",
|
||||
"disable": "Disable",
|
||||
"edit": "Edit",
|
||||
"analytics": "Analytics",
|
||||
|
||||
"view-category": "View category",
|
||||
"set-order": "Set order",
|
||||
"set-order-help": "Setting the order of the category will move this category to that order and update the order of other categories as necessary. Minimum order is 1 which puts the category at the top.",
|
||||
|
||||
"select-category": "Select Category",
|
||||
"set-parent-category": "Set Parent Category",
|
||||
|
||||
"privileges.description": "You can configure the access control privileges for portions of the site in this section. Privileges can be granted on a per-user or a per-group basis. Select the domain of effect from the dropdown below.",
|
||||
"privileges.category-selector": "Configuring privileges for ",
|
||||
"privileges.warning": "<strong>Note</strong>: Privilege settings take effect immediately. It is not necessary to save the category after adjusting these settings.",
|
||||
"privileges.section-viewing": "Viewing Privileges",
|
||||
"privileges.section-posting": "Posting Privileges",
|
||||
"privileges.section-moderation": "Moderation Privileges",
|
||||
"privileges.section-other": "Other",
|
||||
"privileges.section-user": "User",
|
||||
"privileges.search-user": "Add User",
|
||||
"privileges.no-users": "No user-specific privileges in this category.",
|
||||
"privileges.section-group": "Group",
|
||||
"privileges.group-private": "This group is private",
|
||||
"privileges.inheritance-exception": "This group does not inherit privileges from registered-users group",
|
||||
"privileges.banned-user-inheritance": "Banned users inherit privileges from banned-users group",
|
||||
"privileges.search-group": "Add Group",
|
||||
"privileges.copy-to-children": "Copy to Children",
|
||||
"privileges.copy-from-category": "Copy from Category",
|
||||
"privileges.copy-privileges-to-all-categories": "Copy to All Categories",
|
||||
"privileges.copy-group-privileges-to-children": "Copy this group's privileges to the children of this category.",
|
||||
"privileges.copy-group-privileges-to-all-categories": "Copy this group's privileges to all categories.",
|
||||
"privileges.copy-group-privileges-from": "Copy this group's privileges from another category.",
|
||||
"privileges.inherit": "If the <code>registered-users</code> group is granted a specific privilege, all other groups receive an <strong>implicit privilege</strong>, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the <code>registered-users</code> user group, and so, privileges for additional groups need not be explicitly granted.",
|
||||
"privileges.copy-success": "Privileges copied!",
|
||||
|
||||
"analytics.back": "Back to Categories List",
|
||||
"analytics.title": "Analytics for \"%1\" category",
|
||||
"analytics.pageviews-hourly": "<strong>Figure 1</strong> – Hourly page views for this category</small>",
|
||||
"analytics.pageviews-daily": "<strong>Figure 2</strong> – Daily page views for this category</small>",
|
||||
"analytics.topics-daily": "<strong>Figure 3</strong> – Daily topics created in this category</small>",
|
||||
"analytics.posts-daily": "<strong>Figure 4</strong> – Daily posts made in this category</small>",
|
||||
|
||||
"alert.created": "Created",
|
||||
"alert.create-success": "Category successfully created!",
|
||||
"alert.none-active": "You have no active categories.",
|
||||
"alert.create": "Create a Category",
|
||||
"alert.confirm-purge": "<p class=\"lead\">Do you really want to purge this category \"%1\"?</p><h5><strong class=\"text-danger\">Warning!</strong> All topics and posts in this category will be purged!</h5> <p class=\"help-block\">Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category <em>temporarily</em>, you'll want to \"disable\" the category instead.</p>",
|
||||
"alert.purge-success": "Category purged!",
|
||||
"alert.copy-success": "Settings Copied!",
|
||||
"alert.set-parent-category": "Set Parent Category",
|
||||
"alert.updated": "Updated Categories",
|
||||
"alert.updated-success": "Category IDs %1 successfully updated.",
|
||||
"alert.upload-image": "Upload category image",
|
||||
"alert.find-user": "Find a User",
|
||||
"alert.user-search": "Search for a user here...",
|
||||
"alert.find-group": "Find a Group",
|
||||
"alert.group-search": "Search for a group here...",
|
||||
"alert.not-enough-whitelisted-tags": "Whitelisted tags are less than minimum tags, you need to create more whitelisted tags!",
|
||||
"collapse-all": "Collapse All",
|
||||
"expand-all": "Expand All",
|
||||
"disable-on-create": "Disable on create",
|
||||
"no-matches": "No matches"
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"lead": "A listing of digest delivery stats and times is displayed below.",
|
||||
"disclaimer": "Please be advised that email delivery is not guaranteed, due to the nature of email technology. Many variables factor into whether an email sent to the recipient server is ultimately delivered into the user's inbox, including server reputation, blacklisted IP addresses, and whether DKIM/SPF/DMARC is configured.",
|
||||
"disclaimer-continued": "A successful delivery means the message was sent successfully by NodeBB and acknowledged by the recipient server. It does not mean the email landed in the inbox. For best results, we recommend using a third-party email delivery service such as <a href=\"https://sendgrid.com/why-sendgrid/\">SendGrid</a>.",
|
||||
|
||||
"user": "User",
|
||||
"subscription": "Subscription Type",
|
||||
"last-delivery": "Last successful delivery",
|
||||
"default": "System default",
|
||||
"default-help": "<em>System default</em> means the user has not explicitly overridden the global forum setting for digests, which is currently: "<strong>%1</strong>"",
|
||||
"resend": "Resend Digest",
|
||||
"resend-all-confirm": "Are you sure you wish to manually execute this digest run?",
|
||||
"resent-single": "Manual digest resend completed",
|
||||
"resent-day": "Daily digest resent",
|
||||
"resent-week": "Weekly digest resent",
|
||||
"resent-biweek": "Bi-Weekly digest resent",
|
||||
"resent-month": "Monthly digest resent",
|
||||
"null": "<em>Never</em>",
|
||||
"manual-run": "Manual digest run:",
|
||||
|
||||
"no-delivery-data": "No delivery data found"
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"manage-groups": "Manage Groups",
|
||||
"add-group": "Add group",
|
||||
"edit-group": "Edit Group",
|
||||
"back-to-groups": "Back to groups",
|
||||
"view-group": "View group",
|
||||
"icon-and-title": "Icon & Title",
|
||||
"name": "اسم المجموعة",
|
||||
"badge": "Badge",
|
||||
"properties": "Properties",
|
||||
"description": "Group Description",
|
||||
"member-count": "Member Count",
|
||||
"system": "System",
|
||||
"hidden": "Hidden",
|
||||
"private": "Private",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"privileges": "Privileges",
|
||||
"members-csv": "Members (CSV)",
|
||||
"search-placeholder": "Search",
|
||||
"create": "Create Group",
|
||||
"description-placeholder": "A short description about your group",
|
||||
"create-button": "Create",
|
||||
|
||||
"alerts.create-failure": "<strong>Uh-Oh</strong><p>There was a problem creating your group. Please try again later!</p>",
|
||||
"alerts.confirm-delete": "Are you sure you wish to delete this group?",
|
||||
|
||||
"edit.name": "Name",
|
||||
"edit.description": "Description",
|
||||
"edit.user-title": "Title of Members",
|
||||
"edit.icon": "Group Icon",
|
||||
"edit.label-color": "Group Label Color",
|
||||
"edit.text-color": "Group Text Color",
|
||||
"edit.show-badge": "Show Badge",
|
||||
"edit.private-details": "If enabled, joining of groups requires approval from a group owner.",
|
||||
"edit.private-override": "Warning: Private groups is disabled at system level, which overrides this option.",
|
||||
"edit.disable-join": "Disable join requests",
|
||||
"edit.disable-leave": "Disallow users from leaving the group",
|
||||
"edit.hidden": "Hidden",
|
||||
"edit.hidden-details": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually",
|
||||
"edit.add-user": "Add User to Group",
|
||||
"edit.add-user-search": "Search Users",
|
||||
"edit.members": "Member List",
|
||||
"control-panel": "Groups Control Panel",
|
||||
"revert": "Revert",
|
||||
|
||||
"edit.no-users-found": "No Users Found",
|
||||
"edit.confirm-remove-user": "Are you sure you want to remove this user?"
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
{
|
||||
"manage-privileges": "Manage Privileges",
|
||||
"discard-changes": "Discard changes",
|
||||
"global": "Global",
|
||||
"admin": "Admin",
|
||||
"group-privileges": "Group Privileges",
|
||||
"user-privileges": "User Privileges",
|
||||
"edit-privileges": "Edit Privileges",
|
||||
"select-clear-all": "Select/Clear All",
|
||||
"chat": "Chat",
|
||||
"chat-with-privileged": "Chat with Privileged",
|
||||
"upload-images": "Upload Images",
|
||||
"upload-files": "Upload Files",
|
||||
"signature": "Signature",
|
||||
"ban": "Ban",
|
||||
"mute": "Mute",
|
||||
"invite": "Invite",
|
||||
"search-content": "Search Content",
|
||||
"search-users": "Search Users",
|
||||
"search-tags": "Search Tags",
|
||||
"view-users": "View Users",
|
||||
"view-tags": "View Tags",
|
||||
"view-groups": "View Groups",
|
||||
"allow-local-login": "Local Login",
|
||||
"allow-group-creation": "Group Create",
|
||||
"view-users-info": "View Users Info",
|
||||
"find-category": "Find Category",
|
||||
"access-category": "Access Category",
|
||||
"access-topics": "Access Topics",
|
||||
"create-topics": "Create Topics",
|
||||
"reply-to-topics": "Reply to Topics",
|
||||
"schedule-topics": "Schedule Topics",
|
||||
"tag-topics": "Tag Topics",
|
||||
"edit-posts": "Edit Posts",
|
||||
"view-edit-history": "View Edit History",
|
||||
"delete-posts": "Delete Posts",
|
||||
"view-deleted": "View Deleted Posts",
|
||||
"upvote-posts": "Upvote Posts",
|
||||
"downvote-posts": "Downvote Posts",
|
||||
"delete-topics": "Delete Topics",
|
||||
"purge": "Purge",
|
||||
"moderate": "Moderate",
|
||||
"admin-dashboard": "Dashboard",
|
||||
"admin-categories": "Categories",
|
||||
"admin-privileges": "Privileges",
|
||||
"admin-users": "Users",
|
||||
"admin-admins-mods": "Admins & Mods",
|
||||
"admin-groups": "Groups",
|
||||
"admin-tags": "Tags",
|
||||
"admin-settings": "Settings",
|
||||
|
||||
"alert.confirm-moderate": "<strong>Are you sure you wish to grant the moderation privilege to this user group?</strong> This group is public, and any users can join at will.",
|
||||
"alert.confirm-admins-mods": "<strong>Are you sure you wish to grant the "Admins & Mods" privilege to this user/group?</strong> Users with this privilege are able to promote and demote other users into privileged positions, <em>including super administrator</em>",
|
||||
"alert.confirm-save": "Please confirm your intention to save these privileges",
|
||||
"alert.confirm-discard": "Are you sure you wish to discard your privilege changes?",
|
||||
"alert.discarded": "Privilege changes discarded",
|
||||
"alert.confirm-copyToAll": "Are you sure you wish to apply this set of <strong>%1</strong> to <strong>all categories</strong>?",
|
||||
"alert.confirm-copyToAllGroup": "Are you sure you wish to apply this group's set of <strong>%1</strong> to <strong>all categories</strong>?",
|
||||
"alert.confirm-copyToChildren": "Are you sure you wish to apply this set of <strong>%1</strong> to <strong>all descendant (child) categories</strong>?",
|
||||
"alert.confirm-copyToChildrenGroup": "Are you sure you wish to apply this group's set of <strong>%1</strong> to <strong>all descendant (child) categories</strong>?",
|
||||
"alert.no-undo": "<em>This action cannot be undone.</em>",
|
||||
"alert.admin-warning": "Administrators implicitly get all privileges",
|
||||
"alert.copyPrivilegesFrom-title": "Select a category to copy from",
|
||||
"alert.copyPrivilegesFrom-warning": "This will copy <strong>%1</strong> from the selected category.",
|
||||
"alert.copyPrivilegesFromGroup-warning": "This will copy this group's set of <strong>%1</strong> from the selected category."
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"queue": "الطابور",
|
||||
"description": "There are no users in the registration queue. <br> To enable this feature, go to <a href=\"%1\">Settings → User → User Registration</a> and set <strong>Registration Type</strong> to \"Admin Approval\".",
|
||||
|
||||
"list.name": "اﻹسم",
|
||||
"list.email": "البريد الإلكتروني",
|
||||
"list.ip": "IP",
|
||||
"list.time": "التوقيت",
|
||||
"list.username-spam": "Frequency: %1 Appears: %2 Confidence: %3",
|
||||
"list.email-spam": "Frequency: %1 Appears: %2",
|
||||
"list.ip-spam": "Frequency: %1 Appears: %2",
|
||||
|
||||
"invitations": "الدعوات",
|
||||
"invitations.description": "Below is a complete list of invitations sent. Use ctrl-f to search through the list by email or username. <br><br>The username will be displayed to the right of the emails for users who have redeemed their invitations.",
|
||||
"invitations.inviter-username": "أسم المستخدم الداعي",
|
||||
"invitations.invitee-email": "البريد اﻻلكتروني للمدعو",
|
||||
"invitations.invitee-username": "اسم المستخم للمدعو (اذا كان مسجل)",
|
||||
|
||||
"invitations.confirm-delete": "هل أنت متأكد من أنك تريد حذف هذه الدعوة؟"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"manage-tags": "Manage Tags",
|
||||
"none": "Your forum does not have any topics with tags yet.",
|
||||
"bg-color": "Background Colour",
|
||||
"text-color": "Text Colour",
|
||||
"description": "Select tags by clicking or dragging, use <code>CTRL</code> to select multiple tags.",
|
||||
"create": "Create Tag",
|
||||
"add-tag": "Add tag",
|
||||
"modify": "Modify Tags",
|
||||
"rename": "Rename Tags",
|
||||
"delete": "Delete Selected Tags",
|
||||
"search": "Search for tags...",
|
||||
"settings": "Tags Settings",
|
||||
"name": "Tag Name",
|
||||
|
||||
"alerts.editing": "Editing tag(s)",
|
||||
"alerts.confirm-delete": "Do you want to delete the selected tags?",
|
||||
"alerts.update-success": "Tag Updated!",
|
||||
"reset-colors": "Reset colors"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"manage-uploads": "Manage Uploads",
|
||||
"upload-file": "Upload File",
|
||||
"filename": "Filename",
|
||||
"usage": "Post Usage",
|
||||
"orphaned": "Orphaned",
|
||||
"size/filecount": "Size / Filecount",
|
||||
"confirm-delete": "Do you really want to delete this file?",
|
||||
"filecount": "%1 files",
|
||||
"new-folder": "New Folder",
|
||||
"name-new-folder": "Enter a name for new the folder"
|
||||
}
|
||||