@inner-dj/server 0.0.3

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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/assets/css/styles.css +3 -0
  3. package/assets/js/entry.js +1 -0
  4. package/assets/js/index.global.js +1 -0
  5. package/assets/js/otplib.js +1 -0
  6. package/assets/js/qr-creator.min.js +1 -0
  7. package/assets/js/qr.js +1 -0
  8. package/assets/js/song-entry.js +1 -0
  9. package/assets/js/totp.js +1 -0
  10. package/assets/js/urls.js +1 -0
  11. package/lib/src/browser/entry.d.ts +1 -0
  12. package/lib/src/browser/entry.js +62 -0
  13. package/lib/src/browser/entry.js.map +1 -0
  14. package/lib/src/browser/index.global.js +10 -0
  15. package/lib/src/browser/otplib.js +10 -0
  16. package/lib/src/browser/qr-creator.min.js +28 -0
  17. package/lib/src/browser/qr.js +1636 -0
  18. package/lib/src/browser/song-entry.d.ts +1 -0
  19. package/lib/src/browser/song-entry.js +44 -0
  20. package/lib/src/browser/song-entry.js.map +1 -0
  21. package/lib/src/browser/totp.d.ts +1 -0
  22. package/lib/src/browser/totp.js +46 -0
  23. package/lib/src/browser/totp.js.map +1 -0
  24. package/lib/src/browser/urls.d.ts +1 -0
  25. package/lib/src/browser/urls.js +15 -0
  26. package/lib/src/browser/urls.js.map +1 -0
  27. package/lib/src/config.d.ts +18 -0
  28. package/lib/src/config.js +51 -0
  29. package/lib/src/config.js.map +1 -0
  30. package/lib/src/index.d.ts +3 -0
  31. package/lib/src/index.js +26 -0
  32. package/lib/src/index.js.map +1 -0
  33. package/lib/src/main.d.ts +2 -0
  34. package/lib/src/main.js +5 -0
  35. package/lib/src/main.js.map +1 -0
  36. package/lib/src/receivers/ireceiver.d.ts +17 -0
  37. package/lib/src/receivers/ireceiver.js +18 -0
  38. package/lib/src/receivers/ireceiver.js.map +1 -0
  39. package/lib/src/receivers/smtp-receiver.d.ts +10 -0
  40. package/lib/src/receivers/smtp-receiver.js +38 -0
  41. package/lib/src/receivers/smtp-receiver.js.map +1 -0
  42. package/lib/src/session-store-migrations.d.ts +2 -0
  43. package/lib/src/session-store-migrations.js +17 -0
  44. package/lib/src/session-store-migrations.js.map +1 -0
  45. package/lib/src/session-store.d.ts +16 -0
  46. package/lib/src/session-store.js +40 -0
  47. package/lib/src/session-store.js.map +1 -0
  48. package/lib/src/web.d.ts +21 -0
  49. package/lib/src/web.js +282 -0
  50. package/lib/src/web.js.map +1 -0
  51. package/package.json +61 -0
  52. package/views/admin.ejs +29 -0
  53. package/views/footer.ejs +7 -0
  54. package/views/head.ejs +24 -0
  55. package/views/index.ejs +28 -0
  56. package/views/instructions.ejs +6 -0
  57. package/views/landing.ejs +30 -0
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ document.addEventListener("DOMContentLoaded", function () {
2
+ const urlTxt = document.getElementById('url');
3
+ const submitBtn = document.getElementById('submit');
4
+ async function submit(_) {
5
+ if (!urlTxt?.value || !submitBtn) {
6
+ return;
7
+ }
8
+ const url = urlTxt?.value;
9
+ urlTxt.disabled = submitBtn.disabled = true;
10
+ submitBtn?.classList?.remove('is-danger');
11
+ submitBtn?.classList?.add('is-loading', 'is-primary');
12
+ try {
13
+ const resp = await fetch('/landing', {
14
+ method: 'POST',
15
+ headers: {
16
+ 'Content-Type': 'application/json',
17
+ },
18
+ body: JSON.stringify({ url })
19
+ });
20
+ if (!resp.ok) {
21
+ throw new Error(`Response status: ${resp.status}`);
22
+ }
23
+ urlTxt.disabled = submitBtn.disabled = false;
24
+ submitBtn?.classList?.remove('is-loading');
25
+ urlTxt.value = '';
26
+ urlTxt.focus();
27
+ }
28
+ catch (ex) {
29
+ urlTxt.disabled = submitBtn.disabled = false;
30
+ submitBtn?.classList?.remove('is-loading', 'is-primary');
31
+ submitBtn?.classList?.add('is-danger');
32
+ console.log(ex);
33
+ }
34
+ }
35
+ submitBtn?.addEventListener('click', submit);
36
+ urlTxt?.addEventListener('keydown', e => {
37
+ if (e.key === 'Enter') {
38
+ submit(e);
39
+ }
40
+ });
41
+ urlTxt?.focus();
42
+ });
43
+ export {};
44
+ //# sourceMappingURL=song-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"song-entry.js","sourceRoot":"","sources":["../../../src/browser/song-entry.ts"],"names":[],"mappings":"AAAA,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE;IAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,cAAc,CAAC,KAAK,CAA4B,CAAC;IACzE,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAA6B,CAAC;IAEhF,KAAK,UAAU,MAAM,CAAC,CAAS;QAC3B,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;YAC/B,OAAO;QACX,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,EAAE,KAAK,CAAC;QAC1B,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;QAC5C,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;QAC1C,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QACtD,IAAI,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE;gBACjC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACL,cAAc,EAAE,kBAAkB;iBACrC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;aAChC,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACvD,CAAC;YACD,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC,QAAQ,GAAG,KAAK,CAAC;YAC7C,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;YAC3C,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC;YAClB,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;QACD,OAAO,EAAE,EAAE,CAAC;YACR,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC,QAAQ,GAAG,KAAK,CAAC;YAC7C,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;YACzD,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;YACvC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC;IACL,CAAC;IAED,SAAS,EAAE,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7C,MAAM,EAAE,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE;QACpC,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,CAAC,CAAC,CAAC;QACd,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,EAAE,KAAK,EAAE,CAAC;AACpB,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ document.addEventListener("DOMContentLoaded", function () {
2
+ const qrContainer = document.getElementById('qr-container');
3
+ const tokenSpan = document.getElementById('token');
4
+ function emptyElement(el) {
5
+ while (el?.firstChild) {
6
+ el.removeChild(el.lastChild);
7
+ }
8
+ }
9
+ fetch('/admin/token', {
10
+ method: 'GET'
11
+ }).then(async (res) => {
12
+ const token = (await res.json())?.token;
13
+ if (!token || typeof token !== 'string') {
14
+ setTimeout(() => location.reload(), 3000);
15
+ return;
16
+ }
17
+ const OtpLib = window.otplib;
18
+ const QR = window.QrCreator;
19
+ let lastOtp = '';
20
+ async function genOtp() {
21
+ const otp = await OtpLib.generate({
22
+ secret: token
23
+ });
24
+ if (lastOtp === otp) {
25
+ return;
26
+ }
27
+ lastOtp = otp;
28
+ emptyElement(tokenSpan);
29
+ tokenSpan?.appendChild(document.createTextNode(otp));
30
+ const authUrl = `${window.location.origin}/#${encodeURIComponent(`{"code":"${otp}"}`)}`;
31
+ emptyElement(qrContainer);
32
+ QR.render({
33
+ text: authUrl,
34
+ radius: 0,
35
+ ecLevel: 'L',
36
+ fill: '#000000',
37
+ background: '#ffffff',
38
+ size: 256
39
+ }, qrContainer);
40
+ }
41
+ genOtp();
42
+ setInterval(genOtp, 5000);
43
+ });
44
+ });
45
+ export {};
46
+ //# sourceMappingURL=totp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"totp.js","sourceRoot":"","sources":["../../../src/browser/totp.ts"],"names":[],"mappings":"AAAA,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE;IAC1C,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAEnD,SAAS,YAAY,CAAC,EAAsB;QACxC,OAAO,EAAE,EAAE,UAAU,EAAE,CAAC;YACpB,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,SAAU,CAAC,CAAC;QAClC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,cAAc,EAAE;QAClB,MAAM,EAAE,KAAK;KAChB,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAClB,MAAM,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAwB,CAAA,EAAE,KAAK,CAAC;QAC7D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACtC,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;YAC1C,OAAO;QACX,CAAC;QACD,MAAM,MAAM,GAA6B,MAAc,CAAC,MAAM,CAAC;QAC/D,MAAM,EAAE,GAA2C,MAAc,CAAC,SAAS,CAAC;QAC5E,IAAI,OAAO,GAAG,EAAE,CAAC;QAEjB,KAAK,UAAU,MAAM;YACjB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC;gBAC9B,MAAM,EAAE,KAAK;aAChB,CAAC,CAAC;YACH,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;gBAClB,OAAO;YACX,CAAC;YACD,OAAO,GAAG,GAAG,CAAC;YACd,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,SAAS,EAAE,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;YACrD,MAAM,OAAO,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,kBAAkB,CAAC,YAAY,GAAG,IAAI,CAAC,EAAE,CAAC;YACxF,YAAY,CAAC,WAAW,CAAC,CAAC;YAC1B,EAAE,CAAC,MAAM,CAAC;gBACN,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,CAAC;gBACT,OAAO,EAAE,GAAG;gBACZ,IAAI,EAAE,SAAS;gBACf,UAAU,EAAE,SAAS;gBACrB,IAAI,EAAE,GAAG;aACZ,EAAE,WAAY,CAAC,CAAC;QACrB,CAAC;QAED,MAAM,EAAE,CAAC;QACT,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ document.addEventListener("DOMContentLoaded", function () {
2
+ function emptyElement(el) {
3
+ while (el?.firstChild) {
4
+ el.removeChild(el.lastChild);
5
+ }
6
+ }
7
+ const elements = document.getElementsByClassName('populate-url');
8
+ for (let el of elements) {
9
+ emptyElement(el);
10
+ const suffix = el.classList.contains('with-trailing-slash') ? '/' : '';
11
+ el.appendChild(document.createTextNode(`${window.location.origin}${suffix}`));
12
+ }
13
+ });
14
+ export {};
15
+ //# sourceMappingURL=urls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../../src/browser/urls.ts"],"names":[],"mappings":"AAAA,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE;IAC1C,SAAS,YAAY,CAAC,EAAkB;QACpC,OAAO,EAAE,EAAE,UAAU,EAAE,CAAC;YACpB,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,SAAU,CAAC,CAAC;QAClC,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC;IACjE,KAAK,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;QACtB,YAAY,CAAC,EAAE,CAAC,CAAC;QACjB,MAAM,MAAM,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;IAClF,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type DBConfig } from './session-store.js';
2
+ import { type InnerDJDiscordBotConfig } from '@inner-dj/discord-receiver';
3
+ import { type TwilioReceiverConfig } from '@inner-dj/twilio-receiver';
4
+ export interface ServerConfig {
5
+ port: number;
6
+ trustProxy: boolean | string | number;
7
+ secure: boolean;
8
+ public: boolean;
9
+ sessionSecret: string;
10
+ userOtp: string;
11
+ adminOtp: string;
12
+ cookieTTL: number;
13
+ jwtSecret: string;
14
+ db: DBConfig;
15
+ discord: InnerDJDiscordBotConfig;
16
+ twilio: TwilioReceiverConfig;
17
+ }
18
+ export declare function getConfig(cfgPath?: string): Promise<ServerConfig>;
@@ -0,0 +1,51 @@
1
+ import path from 'path';
2
+ import fsp from 'fs/promises';
3
+ import { parse } from 'yaml';
4
+ import { generateSecret } from "otplib";
5
+ import { notStupidParseInt, getBoolean, throwNewError } from '@inner-dj/common';
6
+ import { SessionStore } from './session-store.js';
7
+ import { InnerDJDiscordBot } from '@inner-dj/discord-receiver';
8
+ import { TwilioReceiver } from '@inner-dj/twilio-receiver';
9
+ function joinIfArray(x) {
10
+ if (typeof x === 'undefined') {
11
+ return false;
12
+ }
13
+ if (typeof x === 'object') {
14
+ return x.join(', ');
15
+ }
16
+ return x;
17
+ }
18
+ async function parseConfig(cfgPath) {
19
+ try {
20
+ return parse(await fsp.readFile(cfgPath, { encoding: 'utf-8' }));
21
+ }
22
+ catch (ex) {
23
+ console.warn(`failed to read ${cfgPath}:\n`, ex, '\nusing default config.');
24
+ return {};
25
+ }
26
+ }
27
+ function generateTotpSecret(field) {
28
+ const secret = generateSecret();
29
+ console.log(`TOTP secret for ${field} is ${secret}\nSave the secret in your configuration if you want to keep it.`);
30
+ return secret;
31
+ }
32
+ export async function getConfig(cfgPath) {
33
+ const p = process.env['CONFIG'] || cfgPath || path.join('config', 'config.yaml');
34
+ const cfg = await parseConfig(p);
35
+ const pub = getBoolean('PUBLIC', cfg, 'public', false);
36
+ return {
37
+ port: notStupidParseInt(process.env['PORT'] ?? cfg.port ?? 8080),
38
+ trustProxy: joinIfArray(process.env['TRUSTPROXY'] ?? cfg.trustProxy),
39
+ secure: getBoolean('SECURE', cfg, 'secure', true),
40
+ public: pub,
41
+ sessionSecret: pub ? '' : process.env['SESSIONSECRET'] ?? cfg.sessionSecret ?? throwNewError('sessionSecret is required if server is not public'),
42
+ userOtp: pub ? '' : process.env['USEROTP'] ?? cfg.userOtp ?? generateTotpSecret('userOtp'),
43
+ adminOtp: pub ? '' : process.env['ADMINOTP'] ?? cfg.adminOtp ?? generateTotpSecret('adminOtp'),
44
+ cookieTTL: notStupidParseInt(process.env['COOKIETTL'] ?? cfg.cookieTTL ?? 1000 * 60 * 60 * 24 * 30),
45
+ jwtSecret: process.env['JWTSECRET'] ?? cfg.jwtSecret ?? throwNewError('jwtSecret is required'),
46
+ db: SessionStore.getConfig(path.dirname(p), cfg.db),
47
+ discord: InnerDJDiscordBot.getConfig(cfg.discord),
48
+ twilio: TwilioReceiver.getConfig(cfg.twilio),
49
+ };
50
+ }
51
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Web } from './web.js';
2
+ export declare function main(cfgPath?: string): Promise<void>;
3
+ export { Web };
@@ -0,0 +1,26 @@
1
+ import { getConfig } from './config.js';
2
+ import { Web } from './web.js';
3
+ import { InnerDJDiscordBot } from '@inner-dj/discord-receiver';
4
+ import { TwilioReceiver } from '@inner-dj/twilio-receiver';
5
+ export async function main(cfgPath) {
6
+ const cfg = await getConfig(cfgPath);
7
+ const web = new Web(cfg);
8
+ await web.initialize();
9
+ const receivers = [];
10
+ if (cfg.twilio.enabled) {
11
+ const twilio = new TwilioReceiver(cfg.twilio, web);
12
+ receivers.push(twilio);
13
+ twilio.start();
14
+ }
15
+ if (cfg.discord.enabled) {
16
+ const discord = new InnerDJDiscordBot(cfg.discord, web);
17
+ receivers.push(discord);
18
+ discord.start();
19
+ }
20
+ process.on('SIGTERM', () => {
21
+ web.close();
22
+ receivers.forEach(r => r.close());
23
+ });
24
+ }
25
+ export { Web };
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAgB;IACvC,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,GAAG,CAAC,UAAU,EAAE,CAAC;IAEvB,MAAM,SAAS,GAAiC,EAAE,CAAC;IAEnD,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACnD,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,MAAM,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;IACD,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACxD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,OAAO,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;IAED,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACvB,GAAG,CAAC,KAAK,EAAE,CAAC;QACZ,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACP,CAAC;AAED,OAAO,EAAE,GAAG,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { main } from './index.js';
3
+ console.log('inner-dj-server - Developed by Cory Sanin - 2026');
4
+ await main();
5
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;AAChE,MAAM,IAAI,EAAE,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { type ServerConfig } from '../config.js';
2
+ import { type Web } from '../web.js';
3
+ export interface iReceiverConfig {
4
+ enabled: boolean;
5
+ }
6
+ export interface iReceiverStatic<T extends iReceiverConfig> {
7
+ new (config: ServerConfig, web: Web): iReceiver;
8
+ getConfig(cfg?: Partial<T>): T;
9
+ }
10
+ export declare abstract class iReceiver {
11
+ protected config: ServerConfig;
12
+ private web;
13
+ constructor(config: ServerConfig, web: Web);
14
+ request: (url: string) => Promise<boolean>;
15
+ start: () => void;
16
+ close: () => void;
17
+ }
@@ -0,0 +1,18 @@
1
+ ;
2
+ export class iReceiver {
3
+ config;
4
+ web;
5
+ constructor(config, web) {
6
+ this.config = config;
7
+ this.web = web;
8
+ }
9
+ request = (url) => {
10
+ return this.web.sendPayload({
11
+ type: 'queue',
12
+ body: url
13
+ });
14
+ };
15
+ start = () => { };
16
+ close = () => { };
17
+ }
18
+ //# sourceMappingURL=ireceiver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ireceiver.js","sourceRoot":"","sources":["../../../src/receivers/ireceiver.ts"],"names":[],"mappings":"AAKC,CAAC;AAOF,MAAM,OAAgB,SAAS;IACjB,MAAM,CAAe;IACvB,GAAG,CAAM;IAEjB,YAAY,MAAoB,EAAE,GAAQ;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACnB,CAAC;IAED,OAAO,GAAG,CAAC,GAAW,EAAE,EAAE;QACtB,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC;YACxB,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,GAAG;SACZ,CAAC,CAAC;IACP,CAAC,CAAA;IAED,KAAK,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;IACjB,KAAK,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;CACpB"}
@@ -0,0 +1,10 @@
1
+ import { iReceiver, type iReceiverConfig } from './ireceiver.js';
2
+ export interface SmtpReceiverConfig extends iReceiverConfig {
3
+ port: number;
4
+ }
5
+ export declare class SmtpReceiver extends iReceiver {
6
+ private smtp;
7
+ static getConfig(cfg?: Partial<SmtpReceiverConfig>): SmtpReceiverConfig;
8
+ start: () => void;
9
+ close: () => void;
10
+ }
@@ -0,0 +1,38 @@
1
+ import { SMTPServer } from "smtp-server";
2
+ import { notStupidParseInt } from 'inner-dj-common';
3
+ import { iReceiver } from './ireceiver.js';
4
+ import { getBoolean } from '../config.js';
5
+ export class SmtpReceiver extends iReceiver {
6
+ smtp = null;
7
+ static getConfig(cfg) {
8
+ return {
9
+ enabled: getBoolean('SMTPENABLED', cfg, 'enabled', false),
10
+ port: notStupidParseInt(process.env['SMTPPORT'] ?? cfg?.port ?? 4650),
11
+ };
12
+ }
13
+ start = () => {
14
+ if (this.smtp) {
15
+ return;
16
+ }
17
+ const server = this.smtp = new SMTPServer({
18
+ secure: true,
19
+ name: 'Inner DJ request inbox',
20
+ size: 255,
21
+ authOptional: false,
22
+ disabledCommands: ['AUTH'],
23
+ onData: (stream, _session, callback) => {
24
+ const body = stream.read();
25
+ console.log(body);
26
+ callback(null, "Message queued");
27
+ }
28
+ });
29
+ server.listen(this.config.smtpConfig.port);
30
+ console.log(`SmtpReceiver listening on port ${this.config.smtpConfig.port}`);
31
+ };
32
+ close = () => {
33
+ this.smtp?.close();
34
+ this.smtp = null;
35
+ };
36
+ }
37
+ SmtpReceiver;
38
+ //# sourceMappingURL=smtp-receiver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smtp-receiver.js","sourceRoot":"","sources":["../../../src/receivers/smtp-receiver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,SAAS,EAA8C,MAAM,gBAAgB,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM1C,MAAM,OAAO,YAAa,SAAQ,SAAS;IAC/B,IAAI,GAAsB,IAAI,CAAC;IAEvC,MAAM,CAAC,SAAS,CAAC,GAAiC;QAC9C,OAAO;YACH,OAAO,EAAE,UAAU,CAAC,aAAa,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC;YACzD,IAAI,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,GAAG,EAAE,IAAI,IAAI,IAAI,CAAC;SACxE,CAAA;IACL,CAAC;IAEQ,KAAK,GAAG,GAAG,EAAE;QAClB,IAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YACX,OAAO;QACX,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC;YACtC,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,wBAAwB;YAC9B,IAAI,EAAE,GAAG;YACT,YAAY,EAAE,KAAK;YACnB,gBAAgB,EAAE,CAAC,MAAM,CAAC;YAC1B,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;gBACnC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAClB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAA;YACpC,CAAC;SACJ,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,kCAAkC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;IACjF,CAAC,CAAA;IAEQ,KAAK,GAAG,GAAG,EAAE;QAClB,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACrB,CAAC,CAAA;CACJ;AAED,YAA0D,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type Database } from 'better-sqlite3';
2
+ export declare function migrate(db: Database): void;
@@ -0,0 +1,17 @@
1
+ const migrations = [
2
+ ['CREATE TABLE sessions (sid VARCHAR(128) PRIMARY KEY, data TEXT);'],
3
+ ];
4
+ export function migrate(db) {
5
+ db.pragma('journal_mode = WAL');
6
+ const version = db.pragma('user_version')[0]?.user_version ?? 0;
7
+ const migrationsTodo = migrations.slice(version);
8
+ const runMigration = db.transaction((migrations) => {
9
+ migrations.forEach(m => db.exec(m));
10
+ });
11
+ migrationsTodo.forEach((m, i) => {
12
+ console.log(`Running DB migration ${i}`);
13
+ runMigration(m);
14
+ });
15
+ db.pragma(`user_version=${migrations.length}`);
16
+ }
17
+ //# sourceMappingURL=session-store-migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store-migrations.js","sourceRoot":"","sources":["../../src/session-store-migrations.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,GAAe;IAC3B,CAAC,kEAAkE,CAAC;CACvE,CAAC;AAEF,MAAM,UAAU,OAAO,CAAC,EAAY;IAChC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,MAAM,OAAO,GAAI,EAAE,CAAC,MAAM,CAAC,cAAc,CAAgC,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,CAAC,CAAC;IAChG,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,YAAY,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,UAAoB,EAAE,EAAE;QACzD,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IACH,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC5B,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC;QACzC,YAAY,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,MAAM,CAAC,gBAAgB,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { Store, type SessionData } from 'express-session';
2
+ export interface DBConfig {
3
+ path: string;
4
+ }
5
+ export declare class SessionStore extends Store {
6
+ private getQuery;
7
+ private setQuery;
8
+ private destroyQuery;
9
+ constructor(config: DBConfig);
10
+ static getConfig: (cfgDir: string, cfg?: Partial<DBConfig>) => {
11
+ path: string;
12
+ };
13
+ get(sid: string, callback: (err: any, session?: SessionData | null) => void): SessionData | null;
14
+ set(sid: string, session: SessionData, callback?: (err?: any) => void): void;
15
+ destroy(sid: string, callback?: (err?: any) => void): void;
16
+ }
@@ -0,0 +1,40 @@
1
+ import path from 'path';
2
+ import { Store } from 'express-session';
3
+ import sqlite from 'better-sqlite3';
4
+ import { migrate } from './session-store-migrations.js';
5
+ export class SessionStore extends Store {
6
+ getQuery;
7
+ setQuery;
8
+ destroyQuery;
9
+ constructor(config) {
10
+ super();
11
+ const db = sqlite(config.path);
12
+ migrate(db);
13
+ this.getQuery = db.prepare('SELECT data FROM sessions WHERE sid = ?');
14
+ this.setQuery = db.prepare('INSERT INTO sessions (sid, data) VALUES(@sid, @data) ON CONFLICT(sid) DO UPDATE SET data=excluded.data');
15
+ this.destroyQuery = db.prepare('DELETE FROM sessions WHERE sid = ?');
16
+ }
17
+ static getConfig = (cfgDir, cfg) => {
18
+ return {
19
+ path: process.env['DBPATH'] ?? cfg?.path ?? path.join(cfgDir, 'sessions.db'),
20
+ };
21
+ };
22
+ get(sid, callback) {
23
+ const data = this.getQuery.get(sid)?.data;
24
+ const result = (data && JSON.parse(data)) || null;
25
+ callback(null, result);
26
+ return result;
27
+ }
28
+ set(sid, session, callback) {
29
+ this.setQuery.run({
30
+ sid,
31
+ data: JSON.stringify(session)
32
+ });
33
+ callback?.(null);
34
+ }
35
+ destroy(sid, callback) {
36
+ this.destroyQuery.run(sid);
37
+ callback?.(null);
38
+ }
39
+ }
40
+ //# sourceMappingURL=session-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/session-store.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,KAAK,EAAoB,MAAM,iBAAiB,CAAC;AAC1D,OAAO,MAAM,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAgBxD,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC3B,QAAQ,CAA4B;IACpC,QAAQ,CAA2B;IACnC,YAAY,CAA2B;IAE/C,YAAY,MAAgB;QACxB,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,CAAC,EAAE,CAAC,CAAC;QACZ,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC;QACtE,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC,wGAAwG,CAAC,CAAC;QACrI,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,CAAC,SAAS,GAAG,CAAC,MAAc,EAAE,GAAuB,EAAE,EAAE;QAC3D,OAAO;YACH,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,EAAE,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC;SAC/E,CAAA;IACL,CAAC,CAAA;IAEQ,GAAG,CAAC,GAAW,EAAE,QAA0D;QAChF,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;QAC1C,MAAM,MAAM,GAAG,CAAC,IAAI,IAAK,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,CAAC,IAAI,IAAI,CAAC;QACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvB,OAAO,MAAM,CAAC;IAClB,CAAC;IAEQ,GAAG,CAAC,GAAW,EAAE,OAAoB,EAAE,QAA8B;QAC1E,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YACd,GAAG;YACH,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;SAChC,CAAC,CAAC;QACH,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IAEQ,OAAO,CAAC,GAAW,EAAE,QAA8B;QACxD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3B,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { ServerConfig } from './config.js';
2
+ import express from 'express';
3
+ import { type Payload, type ClientInterface, Method } from '@inner-dj/common';
4
+ declare module 'express-session' {
5
+ interface SessionData {
6
+ admin?: boolean;
7
+ }
8
+ }
9
+ export declare class Web implements ClientInterface {
10
+ private config;
11
+ private _webserver;
12
+ private _clients;
13
+ private _app;
14
+ private _guides;
15
+ constructor(config: ServerConfig);
16
+ initialize: () => Promise<void>;
17
+ registerRoute: (method: Method, route: string, handler: (req: express.Request, res: express.Response, next?: express.NextFunction) => any) => void;
18
+ registerGuide: (html: string) => number;
19
+ sendPayload: (payload: Payload) => Promise<boolean>;
20
+ close: () => void;
21
+ }