nodebb-plugin-anti-account-sharing 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.gitattributes ADDED
@@ -0,0 +1,22 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
3
+
4
+ # Custom for Visual Studio
5
+ *.cs diff=csharp
6
+ *.sln merge=union
7
+ *.csproj merge=union
8
+ *.vbproj merge=union
9
+ *.fsproj merge=union
10
+ *.dbproj merge=union
11
+
12
+ # Standard to msysgit
13
+ *.doc diff=astextplain
14
+ *.DOC diff=astextplain
15
+ *.docx diff=astextplain
16
+ *.DOCX diff=astextplain
17
+ *.dot diff=astextplain
18
+ *.DOT diff=astextplain
19
+ *.pdf diff=astextplain
20
+ *.PDF diff=astextplain
21
+ *.rtf diff=astextplain
22
+ *.RTF diff=astextplain
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 NodeBB Inc. <sales@nodebb.org>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # Quickstart Plugin for NodeBB
2
+
3
+ A starter kit for quickly creating NodeBB plugins. Comes with a pre-setup SCSS file, server side JS script with an `static:app.load` hook, and a client-side script. Most plugins need at least one of the above, so this ought to save you some time. For a full list of hooks have a look at our [wiki page](https://github.com/NodeBB/NodeBB/wiki/Hooks), and for more information about creating plugins please visit our [documentation portal](https://docs.nodebb.org/).
4
+
5
+ Fork this or copy it, and using your favourite text editor find and replace all instances of `nodebb-plugin-quickstart` with `nodebb-plugin-your-plugins-name`. Change the author's name in the LICENSE and package.json files.
6
+
7
+ ## Hello World
8
+
9
+ Really simple, just edit `public/lib/main.js` and paste in `console.log('hello world');`, and that's it!
10
+
11
+ ## Installation
12
+
13
+ npm install nodebb-plugin-quickstart
14
+
15
+ ## Screenshots
16
+
17
+ Don't forget to add screenshots!
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ extends: ['@commitlint/config-angular'],
5
+ rules: {
6
+ 'header-max-length': [1, 'always', 72],
7
+ 'type-enum': [
8
+ 2,
9
+ 'always',
10
+ [
11
+ 'breaking',
12
+ 'build',
13
+ 'chore',
14
+ 'ci',
15
+ 'docs',
16
+ 'feat',
17
+ 'fix',
18
+ 'perf',
19
+ 'refactor',
20
+ 'revert',
21
+ 'style',
22
+ 'test',
23
+ ],
24
+ ],
25
+ },
26
+ };
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ import serverConfig from 'eslint-config-nodebb';
4
+ import publicConfig from 'eslint-config-nodebb/public';
5
+
6
+ export default [
7
+ ...publicConfig,
8
+ ...serverConfig,
9
+ ];
10
+
@@ -0,0 +1,3 @@
1
+ {
2
+ "info": "Dies ist eine Übersetzungszeichenkette, sie wird in die Sprache des Benutzers übersetzt. Siehe den Ordner 'languages' im Stammverzeichnis dieses Plugins."
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "info": "This is a translation string, it gets translated to the users language. See the languages folder in the root of this plugin."
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "info": "This is a translation string, it gets translated to the users language. See the languages folder in the root of this plugin."
3
+ }
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const Controllers = module.exports;
4
+
5
+ Controllers.renderAdminPage = function (req, res/* , next */) {
6
+ /*
7
+ Make sure the route matches your path to template exactly.
8
+
9
+ If your route was:
10
+ myforum.com/some/complex/route
11
+ your template should be:
12
+ templates/some/complex/route.tpl
13
+ and you would render it like so:
14
+ res.render('some/complex/route');
15
+ */
16
+
17
+ res.render('admin/plugins/quickstart', {
18
+ title: 'Quick Start',
19
+ });
20
+ };
package/library.js ADDED
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ const db = require.main.require('./src/database');
4
+ const user = require.main.require('./src/user');
5
+
6
+ const Plugin = {};
7
+
8
+ // --- AYARLAR ---
9
+ const MAX_DEVICES = 1; // Kaç bilgisayara izin verilsin?
10
+
11
+ // Cihazın Mobil olup olmadığını anlayan fonksiyon
12
+ function isMobile(req) {
13
+ const ua = req.headers['user-agent'];
14
+ return /Mobile|Android|iPhone|iPad|iPod/i.test(ua);
15
+ }
16
+
17
+ Plugin.init = async function (params) {
18
+ console.log(`[Anti-Share] Güvenlik Aktif (Limit: ${MAX_DEVICES} Bilgisayar) 🛡️`);
19
+ };
20
+
21
+ // 1. GİRİŞ YAPILDIĞINDA (Listeye Ekle)
22
+ Plugin.recordSession = async function (data) {
23
+ // Sadece Bilgisayar girişlerini takip ediyoruz
24
+ if (!isMobile(data.req)) {
25
+ if (data.uid && data.req.sessionID) {
26
+ const key = `antishare:sessions:${data.uid}`;
27
+
28
+ // 1. Yeni oturumu listenin SONUNA ekle
29
+ await db.listAppend(key, data.req.sessionID);
30
+
31
+ // 2. Listeyi kırp (Sadece son 5 taneyi tut)
32
+ // (Eğer 6. kişi girerse, listenin başındaki 1. kişi silinir)
33
+ await db.listTrim(key, -MAX_DEVICES, -1);
34
+ }
35
+ }
36
+ };
37
+
38
+ // 2. HER SAYFA GEZİNTİSİNDE (Kontrol Et)
39
+ Plugin.checkSession = async function (data) {
40
+ const req = data.req;
41
+ const res = data.res;
42
+
43
+ // Giriş yapmamışsa veya Mobilden giriyorsa KARIŞMA
44
+ if (!req.uid || req.uid <= 0 || isMobile(req)) {
45
+ return data;
46
+ }
47
+
48
+ const isAdmin = await user.isAdministrator(req.uid);
49
+ if (isAdmin) return data;
50
+
51
+ // --- BİLGİSAYAR KONTROLÜ ---
52
+ const key = `antishare:sessions:${req.uid}`;
53
+
54
+ // Veritabanındaki "Geçerli Oturum Listesi"ni çek
55
+ const validSessions = await db.getListRange(key, 0, -1);
56
+
57
+ // Eğer şu anki oturum ID'si, izin verilenler listesinde YOKSA -> AT!
58
+ if (validSessions && !validSessions.includes(req.sessionID)) {
59
+
60
+ // Oturumu Öldür
61
+ req.logout();
62
+ if (req.session) req.session.destroy();
63
+
64
+ if (req.xhr || (req.headers.accept && req.headers.accept.indexOf('json') > -1)) {
65
+ return res.status(401).json({
66
+ error: 'session_terminated',
67
+ message: 'Maksimum cihaz sınırına ulaşıldı.'
68
+ });
69
+ } else {
70
+ return res.redirect('/login?error=session-conflict');
71
+ }
72
+ }
73
+
74
+ return data;
75
+ };
76
+
77
+ // Client scriptini ekle
78
+ Plugin.addScripts = async function (scripts) {
79
+ scripts.push('plugins/nodebb-plugin-anti-account-sharing/static/security.js');
80
+ return scripts;
81
+ };
82
+
83
+ module.exports = Plugin;
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "nodebb-plugin-anti-account-sharing",
3
+ "version": "1.0.0",
4
+ "description": "Prevents account sharing by enforcing a single active session policy for desktop devices.",
5
+ "main": "library.js",
6
+ "keywords": [
7
+ "nodebb",
8
+ "plugin",
9
+ "security",
10
+ "anti-share",
11
+ "session-limit",
12
+ "account-protection"
13
+ ],
14
+ "author": {
15
+ "name": "IEU Forum Team",
16
+ "email": "forum@ieu.app"
17
+ },
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/sucreistaken/nodebb-plugin-anti-account-sharing"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/sucreistaken/nodebb-plugin-anti-account-sharing/issues"
25
+ },
26
+ "nbbpm": {
27
+ "compatibility": "^4.0.0"
28
+ },
29
+ "dependencies": {},
30
+ "devDependencies": {
31
+ "eslint": "9.39.2",
32
+ "eslint-config-nodebb": "1.1.11"
33
+ },
34
+ "scripts": {
35
+ "lint": "eslint ."
36
+ }
37
+ }
package/plugin.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "id": "nodebb-plugin-niki-antishare",
3
+ "name": "Niki Anti-Share Security",
4
+ "description": "Bilgisayar oturumlarını tekilleştirerek hesap paylaşımını engeller (Mobil hariç).",
5
+ "url": "https://forum.ieu.app",
6
+ "library": "./library.js",
7
+ "hooks": [
8
+ { "hook": "static:app.load", "method": "init" },
9
+ { "hook": "action:user.loggedIn", "method": "recordSession" },
10
+ { "hook": "filter:router.page", "method": "checkSession" },
11
+ { "hook": "filter:scripts.get", "method": "addScripts" }
12
+ ],
13
+ "scripts": [
14
+ "static/security.js"
15
+ ],
16
+ "staticDirs": {
17
+ "static": "./static"
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ $(document).ready(function () {
4
+ /*
5
+ This file shows how admin page client-side javascript can be included via a plugin.
6
+ If you check `plugin.json`, you'll see that this file is listed under "acpScripts".
7
+ That array tells NodeBB which files to bundle into the minified javascript
8
+ that is served to the end user.
9
+
10
+ Some events you can elect to listen for:
11
+
12
+ $(document).ready(); Fired when the DOM is ready
13
+ $(window).on('action:ajaxify.end', function(data) { ... }); "data" contains "url"
14
+ */
15
+
16
+ console.log('nodebb-plugin-quickstart: acp-loaded');
17
+ // Note how this is shown in the console on the first load of every page in the ACP
18
+ });
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ /*
4
+ This file is located in the "modules" block of plugin.json
5
+ It is only loaded when the user navigates to /admin/plugins/quickstart page
6
+ It is not bundled into the min file that is served on the first load of the page.
7
+ */
8
+
9
+ import { save, load } from 'settings';
10
+ import * as uploader from 'uploader';
11
+
12
+ export function init() {
13
+ handleSettingsForm();
14
+ setupUploader();
15
+ };
16
+
17
+ function handleSettingsForm() {
18
+ load('quickstart', $('.quickstart-settings'), function () {
19
+ setupColorInputs();
20
+ });
21
+
22
+ $('#save').on('click', () => {
23
+ save('quickstart', $('.quickstart-settings')); // pass in a function in the 3rd parameter to override the default success/failure handler
24
+ });
25
+ }
26
+
27
+ function setupColorInputs() {
28
+ var colorInputs = $('[data-settings="colorpicker"]');
29
+ colorInputs.on('change', updateColors);
30
+ updateColors();
31
+ }
32
+
33
+ function updateColors() {
34
+ $('#preview').css({
35
+ color: $('#color').val(),
36
+ 'background-color': $('#bgColor').val(),
37
+ });
38
+ }
39
+
40
+ function setupUploader() {
41
+ $('#content input[data-action="upload"]').each(function () {
42
+ var uploadBtn = $(this);
43
+ uploadBtn.on('click', function () {
44
+ uploader.show({
45
+ route: config.relative_path + '/api/admin/upload/file',
46
+ params: {
47
+ folder: 'quickstart',
48
+ },
49
+ accept: 'image/*',
50
+ }, function (image) {
51
+ $('#' + uploadBtn.attr('data-target')).val(image);
52
+ });
53
+ });
54
+ });
55
+ }
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * This file shows how client-side javascript can be included via a plugin.
5
+ * If you check `plugin.json`, you'll see that this file is listed under "scripts".
6
+ * That array tells NodeBB which files to bundle into the minified javascript
7
+ * that is served to the end user.
8
+ *
9
+ * There are two (standard) ways to wait for when NodeBB is ready.
10
+ * This one below executes when NodeBB reports it is ready...
11
+ */
12
+
13
+ (async () => {
14
+ const hooks = await app.require('hooks');
15
+
16
+ hooks.on('action:app.load', () => {
17
+ // called once when nbb has loaded
18
+ });
19
+
20
+ hooks.on('action:ajaxify.end', (/* data */) => {
21
+ // called everytime user navigates between pages including first load
22
+ });
23
+ })();
24
+
25
+ /**
26
+ * ... and this one reports when the DOM is loaded (but NodeBB might not be fully ready yet).
27
+ * For most cases, you'll want the one above.
28
+ */
29
+
30
+ $(document).ready(function () {
31
+ // ...
32
+ });
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+
3
+ /*
4
+ This file is located in the "modules" block of plugin.json
5
+ It is only loaded when the user navigates to /quickstart page
6
+ It is not bundled into the min file that is served on the first load of the page.
7
+ */
8
+
9
+ define('forum/quickstart', function () {
10
+ var module = {};
11
+ module.init = function () {
12
+ $('#last-p').text('quickstart.js loaded!');
13
+ };
14
+ return module;
15
+ });
package/renovate.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": [
3
+ "config:recommended"
4
+ ]
5
+ }
@@ -0,0 +1 @@
1
+ /* Place any SASS in here */
@@ -0,0 +1,5 @@
1
+ <h1>Hello!</h1>
2
+
3
+ <p>This file is served by nodebb at domain.com/assets/plugins/nodebb-plugin-quickstart/static/samplefile.html</p>
4
+
5
+ Check plugin.json for the "staticDirs" property if you want to change the path.
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ $(document).ready(function() {
4
+ // 1. URL Kontrolü (Sayfa yenileyince atıldıysa)
5
+ const urlParams = new URLSearchParams(window.location.search);
6
+ if (urlParams.get('error') === 'session-conflict') {
7
+ showSecurityModal();
8
+ }
9
+
10
+ // 2. AJAX Kontrolü (Sayfada gezerken atıldıysa)
11
+ $(document).ajaxError(function(event, jqxhr) {
12
+ if (jqxhr.status === 401 && jqxhr.responseJSON && jqxhr.responseJSON.error === 'session_terminated') {
13
+ showSecurityModal();
14
+ }
15
+ });
16
+
17
+ function showSecurityModal() {
18
+ // Varsa eski modalları temizle
19
+ $('#antishare-modal').remove();
20
+
21
+ // Niki'den bağımsız, genel ve şık bir tasarım
22
+ const modalHtml = `
23
+ <div id="antishare-modal" style="
24
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
25
+ background: rgba(0,0,0,0.92); z-index: 999999;
26
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
27
+ text-align: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
28
+ animation: fadeIn 0.3s ease;
29
+ ">
30
+ <div style="
31
+ background: #ffffff; padding: 40px; border-radius: 16px;
32
+ max-width: 90%; width: 400px; box-shadow: 0 10px 40px rgba(0,0,0,0.5);
33
+ position: relative; overflow: hidden;
34
+ ">
35
+ <div style="position: absolute; top:0; left:0; width:100%; height:6px; background:#d9534f;"></div>
36
+
37
+ <div style="
38
+ width: 70px; height: 70px; background: #fdf2f2; border-radius: 50%;
39
+ display: flex; align-items: center; justify-content: center; margin: 0 auto 20px;
40
+ ">
41
+ <i class="fa fa-shield" style="font-size: 32px; color: #d9534f;"></i>
42
+ </div>
43
+
44
+ <h2 style="color: #333; margin: 0 0 10px; font-size: 22px; font-weight: 700;">Oturum Sonlandırıldı</h2>
45
+
46
+ <p style="color: #666; font-size: 14px; line-height: 1.6; margin: 0 0 30px;">
47
+ Hesabınıza <strong>başka bir bilgisayardan</strong> giriş yapıldığı tespit edildi.<br><br>
48
+ Hesap güvenliğiniz için önceki cihazdaki oturum otomatik olarak kapatılmıştır.
49
+ </p>
50
+
51
+ <a href="/login" style="
52
+ display: block; width: 100%; padding: 14px 0;
53
+ background: #d9534f; color: #fff; text-decoration: none;
54
+ font-weight: 600; border-radius: 8px; font-size: 15px;
55
+ transition: background 0.2s;
56
+ " onmouseover="this.style.background='#c9302c'" onmouseout="this.style.background='#d9534f'">
57
+ TEKRAR GİRİŞ YAP
58
+ </a>
59
+
60
+ <div style="margin-top: 20px; font-size: 12px; color: #999;">
61
+ Mobil cihazlarınız bu durumdan etkilenmez.
62
+ </div>
63
+ </div>
64
+ </div>
65
+ <style>@keyframes fadeIn { from { opacity:0; transform:scale(0.95); } to { opacity:1; transform:scale(1); } }</style>
66
+ `;
67
+
68
+ $('body').append(modalHtml);
69
+ $('body').css('overflow', 'hidden'); // Scrollu kilitle
70
+ }
71
+ });
@@ -0,0 +1,10 @@
1
+ <form>
2
+ <div class="mb-3">
3
+ <label class="form-label" for="name">Name</label>
4
+ <input type="text" id="name" name="name" class="form-control" placeholder="Name" />
5
+ </div>
6
+ <div class="mb-3">
7
+ <label class="form-label" for="description">Description</label>
8
+ <input type="text" id="description" name="description" class="form-control" placeholder="Description" />
9
+ </div>
10
+ </form>
@@ -0,0 +1,12 @@
1
+ <li data-type="item" class="list-group-item">
2
+ <div class="d-flex gap-2 justify-content-between align-items-start">
3
+ <div class="flex-grow-1">
4
+ <strong>{name}</strong><br />
5
+ <small>{description}</small>
6
+ </div>
7
+ <div class="d-flex gap-1 flex-nowrap">
8
+ <button type="button" data-type="edit" class="btn btn-sm btn-info">Edit</button>
9
+ <button type="button" data-type="remove" class="btn btn-sm btn-danger">Delete</button>
10
+ </div>
11
+ </div>
12
+ </li>
@@ -0,0 +1,68 @@
1
+ <div class="acp-page-container">
2
+ <!-- IMPORT admin/partials/settings/header.tpl -->
3
+
4
+ <div class="row m-0">
5
+ <div id="spy-container" class="col-12 col-md-8 px-0 mb-4" tabindex="0">
6
+ <form role="form" class="quickstart-settings">
7
+ <div class="mb-4">
8
+ <h5 class="fw-bold tracking-tight settings-header">General</h5>
9
+
10
+ <p class="lead">
11
+ Adjust these settings. You can then retrieve these settings in code via:
12
+ <br/><code>await meta.settings.get('quickstart');</code>
13
+ </p>
14
+ <div class="mb-3">
15
+ <label class="form-label" for="setting-1">Setting 1</label>
16
+ <input type="text" id="setting-1" name="setting-1" title="Setting 1" class="form-control" placeholder="Setting 1">
17
+ </div>
18
+ <div class="mb-3">
19
+ <label class="form-label" for="setting-2">Setting 2</label>
20
+ <input type="text" id="setting-2" name="setting-2" title="Setting 2" class="form-control" placeholder="Setting 2">
21
+ </div>
22
+
23
+ <div class="form-check form-switch">
24
+ <input type="checkbox" class="form-check-input" id="setting-3" name="setting-3">
25
+ <label for="setting-3" class="form-check-label">Setting 3</label>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="mb-4">
30
+ <h5 class="fw-bold tracking-tight settings-header">Colors</h5>
31
+
32
+ <p class="alert" id="preview">
33
+ Here is some preview text. Use the inputs below to modify this alert's appearance.
34
+ </p>
35
+ <div class="mb-3 d-flex gap-2">
36
+ <label class="form-label" for="color">Foreground</label>
37
+ <input data-settings="colorpicker" type="color" id="color" name="color" title="Background Color" class="form-control p-1" placeholder="#ffffff" value="#ffffff" style="width: 64px;"/>
38
+ </div>
39
+ <div class="mb-3 d-flex gap-2">
40
+ <label class="form-label" for="bgColor">Background</label>
41
+ <input data-settings="colorpicker" type="color" id="bgColor" name="bgColor" title="Background Color" class="form-control p-1" placeholder="#000000" value="#000000" style="width: 64px;" />
42
+ </div>
43
+ </div>
44
+
45
+ <div class="mb-4">
46
+ <h5 class="fw-bold tracking-tight settings-header">Sorted List</h5>
47
+
48
+ <div class="mb-3" data-type="sorted-list" data-sorted-list="sample-list" data-item-template="admin/plugins/quickstart/partials/sorted-list/item" data-form-template="admin/plugins/quickstart/partials/sorted-list/form">
49
+ <ul data-type="list" class="list-group mb-2"></ul>
50
+ <button type="button" data-type="add" class="btn btn-info">Add Item</button>
51
+ </div>
52
+ </div>
53
+
54
+ <div>
55
+ <h5 class="fw-bold tracking-tight settings-header">Uploads</h5>
56
+
57
+ <label class="form-label" for="uploadedImage">Upload Image</label>
58
+ <div class="d-flex gap-1">
59
+ <input id="uploadedImage" name="uploadedImage" type="text" class="form-control" />
60
+ <input value="Upload" data-action="upload" data-target="uploadedImage" type="button" class="btn btn-light" />
61
+ </div>
62
+ </div>
63
+ </form>
64
+ </div>
65
+
66
+ <!-- IMPORT admin/partials/settings/toc.tpl -->
67
+ </div>
68
+ </div>
@@ -0,0 +1,7 @@
1
+ <div class="card card-body text-bg-light">
2
+ <p>This is a custom page.</p>
3
+ <p>Your uid is {uid}!</p>
4
+ <p id="last-p"></p>
5
+ <hr/>
6
+ <p>[[quickstart:info]]</p>
7
+ </div>
package/test/.eslintrc ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "env": {
3
+ "mocha": true
4
+ },
5
+ "rules": {
6
+ "no-unused-vars": "off"
7
+ }
8
+ }
9
+
package/test/index.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * You can run these tests by executing `npx mocha test/plugins-installed.js`
3
+ * from the NodeBB root folder. The regular test runner will also run these
4
+ * tests.
5
+ *
6
+ * Keep in mind tests do not activate all plugins, so if you are testing
7
+ * hook listeners, socket.io, or mounted routes, you will need to add your
8
+ * plugin to `config.json`, e.g.
9
+ *
10
+ * {
11
+ * "test_plugins": [
12
+ * "nodebb-plugin-quickstart"
13
+ * ]
14
+ * }
15
+ */
16
+
17
+ 'use strict';
18
+
19
+ /* globals describe, it, before */
20
+
21
+ const assert = require('assert');
22
+
23
+ const db = require.main.require('./test/mocks/databasemock');
24
+
25
+ describe('nodebb-plugin-quickstart', () => {
26
+ before(() => {
27
+ // Prepare for tests here
28
+ });
29
+
30
+ it('should pass', (done) => {
31
+ const actual = 'value';
32
+ const expected = 'value';
33
+ assert.strictEqual(actual, expected);
34
+ done();
35
+ });
36
+
37
+ it('should load config object', async () => { // Tests can be async functions too
38
+ const config = await db.getObject('config');
39
+ assert(config);
40
+ });
41
+ });