From 685ad21fcc19d2ebb073ce08cd59c8e65d97920e Mon Sep 17 00:00:00 2001 From: Anuj Kumar Jha Date: Mon, 23 Jan 2023 01:20:41 +0530 Subject: [PATCH] init --- .env | 18 ++++ .gitignore | 2 + .markdownlint.json | 8 ++ .vscode/launch.json | 42 ++++++++ .vscode/settings.json | 52 +++++++++ .vscode/tasks.json | 129 +++++++++++++++++++++++ deno.jsonc | 13 +++ deno.lock | 163 ++++++++++++++++++++++++++++ readme/readme.cli.md | 65 ++++++++++++ readme/readme.configFile.md | 70 +++++++++++++ readme/readme.env.md | 51 +++++++++ readme/readme.logger.md | 128 ++++++++++++++++++++++ src/deps.ts | 17 +++ src/global/global.emitter.ts | 18 ++++ src/global/global.ioredis.ts | 198 +++++++++++++++++++++++++++++++++++ src/global/global.logger.ts | 71 +++++++++++++ src/global/global.redlock.ts | 29 +++++ src/global/global.ts | 27 +++++ src/global/global.util.ts | 19 ++++ src/main.ts | 10 ++ src/utils/utils.ts | 0 src/utils/utils.utilities.ts | 4 + 22 files changed, 1134 insertions(+) create mode 100644 .env create mode 100644 .gitignore create mode 100644 .markdownlint.json create mode 100644 .vscode/launch.json create mode 100755 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 deno.jsonc create mode 100644 deno.lock create mode 100644 readme/readme.cli.md create mode 100644 readme/readme.configFile.md create mode 100644 readme/readme.env.md create mode 100644 readme/readme.logger.md create mode 100644 src/deps.ts create mode 100644 src/global/global.emitter.ts create mode 100644 src/global/global.ioredis.ts create mode 100644 src/global/global.logger.ts create mode 100644 src/global/global.redlock.ts create mode 100644 src/global/global.ts create mode 100644 src/global/global.util.ts create mode 100644 src/main.ts create mode 100644 src/utils/utils.ts create mode 100644 src/utils/utils.utilities.ts diff --git a/.env b/.env new file mode 100644 index 0000000..3e8cc18 --- /dev/null +++ b/.env @@ -0,0 +1,18 @@ +DENO_ENV = 'dev' +PORT='4000' +HOST='127.0.0.1' +DB_USER='' +DB_PORT= '27017' +DB_PASSWORD='' +DB_URL='mongodb+srv://epiko:epiko_2022@cluster0.wgotto0.mongodb.net/beta?retryWrites=true&w=majority' +REDIS_DB='0' +# REDIS_HOST='127.0.0.1' +# REDIS_PORT='6379' +# REDIS_USERNAME='' +# REDIS_PASSWORD='' +REDIS_HOST='redis-18015.c301.ap-south-1-1.ec2.cloud.redislabs.com' +REDIS_PORT='18015' +REDIS_PASSWORD='k39y54ZB6vf6ne4lMFrR3dW6vt58vBWD' + +LOG_LEVEL='silly' +JWT_SECRET='jwt-secret'; \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..258a65c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.local +.logs \ No newline at end of file diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..54c932d --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,8 @@ +{ + "default": true, + "MD003": { "style": "atx" }, + "MD007": { "indent": 2 }, + "MD013": false, + "MD028": false, + "no-hard-tabs": false +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..9a042fa --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,42 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Attach", + "port": 9229, + "request": "attach", + "skipFiles": [ + "/**" + ], + "type": "node" + }, + { + "name": "Attach by Process ID", + "processId": "${command:PickProcess}", + "request": "attach", + "skipFiles": [ + "/**" + ], + "type": "node" + }, + { + "request": "launch", + "name": "Launch Program", + "type": "node", + "program": "${workspaceFolder}/src/main.ts", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "deno", // "/home/evil/.deno/bin/deno", + "runtimeArgs": [ + "run", + "--inspect-wait", + "--allow-all" + ], + "attachSimplePort": 9229, + // "outputCapture": "std" + // "console": "integratedTerminal" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100755 index 0000000..a40323d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,52 @@ +{ + "deno.enable": true, + "deno.lint": true, + "markdownlint.run": "onSave", + + /* inlayHints */ + "deno.inlayHints.enumMemberValues.enabled": false, + "deno.inlayHints.functionLikeReturnTypes.enabled": false, + "deno.inlayHints.parameterNames.enabled": "none", + "deno.inlayHints.propertyDeclarationTypes.enabled": false, + "deno.inlayHints.variableTypes.enabled": false, + /* ----- */ + + "deno.suggest.imports.autoDiscover": true, + "debug.javascript.usePreview": false, + + /* editor */ + "editor.formatOnSave": true, + "[typescript]": { + "editor.defaultFormatter": "denoland.vscode-deno" + }, + /* ----- */ + + /* files */ + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + "deno.jsonc": false, + "deno.lock": false, + ".markdownlint.json": false + }, + /* ----- */ + + /* explorerExclude */ + "explorerExclude.backup": {}, + /* ----- */ + + /* cSpell */ + "cSpell.words": [ + "bullmq", + "datetime", + "deno", + "NOTSET", + "Redlock", + "testdata" + ] + /* ----- */ +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..38e8117 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,129 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "runAndInspect:shell", + "type": "shell", + "command": "deno run --allow-net --allow-write --allow-read --allow-env --inspect ./src/main.ts", + "isBackground": true, + "problemMatcher": ["$deno"] + }, + { + "label": "runAndInspect", + "type": "deno", + "command": "run", + "args": [ + "--allow-net", + "--allow-write", + "--allow-read", + "--allow-env", + "--inspect", + "./src/main.ts" + ], + "problemMatcher": ["$deno"] + }, + { + "label": "runAndInspectWait", + "type": "deno", + "command": "run", + "args": [ + "--allow-net", + "--allow-write", + "--allow-read", + "--allow-env", + "--inspect-wait", + "./src/main.ts" + ], + "problemMatcher": ["$deno"] + }, + { + "label": "run", + "type": "deno", + "command": "run", + "args": [ + "--allow-net", + "--allow-write", + "--allow-read", + "--allow-env", + "./src/main.ts" + ], + "problemMatcher": ["$deno"] + }, + { + "label": "runAndWatch", + "type": "deno", + "command": "run", + "args": [ + "--allow-net", + "--allow-write", + "--allow-read", + "--allow-env", + "--watch", + "./src/main.ts" + ], + "problemMatcher": ["$deno"] + }, + { + "label": "fmt", + "type": "deno", + "command": "fmt", + "args": [], + "problemMatcher": ["$deno"] + }, + { + "label": "fmtAndWatch", + "type": "deno", + "command": "fmt", + "args": [ + "--watch" + ], + "problemMatcher": ["$deno"] + }, + { + "label": "lint", + "type": "deno", + "command": "lint", + "args": [], + "problemMatcher": ["$deno"] + }, + { + "label": "lintAndWatch", + "type": "deno", + "command": "lint", + "args": [ + "--watch" + ], + "problemMatcher": ["$deno"] + }, + { + "label": "lintAndWatch", + "type": "deno", + "command": "lint", + "args": [ + "--watch" + ], + "problemMatcher": ["$deno"] + }, + { + "label": "check", + "type": "deno", + "command": "check", + "args": ["./src/main.ts"], + "problemMatcher": ["$deno"] + }, + { + "label": "checkWithReload", + "type": "deno", + "command": "check", + "args": ["-r", "./src/main.ts"], + "problemMatcher": ["$deno"] + }, + { + "label": "checkAllWithRemoveAndNPM", + "type": "deno", + "command": "check", + "args": ["--all", "./src/main.ts"], + "problemMatcher": ["$deno"] + } + ] +} diff --git a/deno.jsonc b/deno.jsonc new file mode 100644 index 0000000..fa3feb6 --- /dev/null +++ b/deno.jsonc @@ -0,0 +1,13 @@ +{ + "fmt": { + "options": { + "useTabs": true, + "lineWidth": 180, + "indentWidth": 2, + "singleQuote": true, + "proseWrap": "preserve" + } + }, + "tasks": { + } +} diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..cc68088 --- /dev/null +++ b/deno.lock @@ -0,0 +1,163 @@ +{ + "version": "2", + "remote": { + "https://deno.land/std@0.173.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", + "https://deno.land/std@0.173.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.173.0/async/abortable.ts": "73acfb3ed7261ce0d930dbe89e43db8d34e017b063cf0eaa7d215477bf53442e", + "https://deno.land/std@0.173.0/async/deadline.ts": "b98e50d2c42399af03ad13bbb8cf59dadb9f0cd5d70648cc0c3b9202d75ab565", + "https://deno.land/std@0.173.0/async/debounce.ts": "adab11d04ca38d699444ac8a9d9856b4155e8dda2afd07ce78276c01ea5a4332", + "https://deno.land/std@0.173.0/async/deferred.ts": "42790112f36a75a57db4a96d33974a936deb7b04d25c6084a9fa8a49f135def8", + "https://deno.land/std@0.173.0/async/delay.ts": "73aa04cec034c84fc748c7be49bb15cac3dd43a57174bfdb7a4aec22c248f0dd", + "https://deno.land/std@0.173.0/async/mod.ts": "f04344fa21738e5ad6bea37a6bfffd57c617c2d372bb9f9dcfd118a1b622e576", + "https://deno.land/std@0.173.0/async/mux_async_iterator.ts": "70c7f2ee4e9466161350473ad61cac0b9f115cff4c552eaa7ef9d50c4cbb4cc9", + "https://deno.land/std@0.173.0/async/pool.ts": "fd082bd4aaf26445909889435a5c74334c017847842ec035739b4ae637ae8260", + "https://deno.land/std@0.173.0/async/retry.ts": "5efa3ba450ac0c07a40a82e2df296287b5013755d232049efd7ea2244f15b20f", + "https://deno.land/std@0.173.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757", + "https://deno.land/std@0.173.0/bytes/copy.ts": "939d89e302a9761dcf1d9c937c7711174ed74c59eef40a1e4569a05c9de88219", + "https://deno.land/std@0.173.0/collections/filter_values.ts": "5b9feaf17b9a6e5ffccdd36cf6f38fa4ffa94cff2602d381c2ad0c2a97929652", + "https://deno.land/std@0.173.0/collections/without_all.ts": "a89f5da0b5830defed4f59666e188df411d8fece35a5f6ca69be6ca71a95c185", + "https://deno.land/std@0.173.0/dotenv/mod.ts": "6e1dbc4737e3f62a5c1f3d2a26a0e5626fb168a198375bef574fedbe86a79f20", + "https://deno.land/std@0.173.0/fmt/colors.ts": "938c5d44d889fb82eff6c358bea8baa7e85950a16c9f6dae3ec3a7a729164471", + "https://deno.land/std@0.173.0/fs/exists.ts": "b8c8a457b71e9d7f29b9d2f87aad8dba2739cbe637e8926d6ba6e92567875f8e", + "https://deno.land/std@0.173.0/http/server.ts": "cbb17b594651215ba95c01a395700684e569c165a567e4e04bba327f41197433", + "https://deno.land/std@0.173.0/io/buf_writer.ts": "759c69d304b04d2909976f2a03a24a107276fbd81ed13593c5c2d43d104b52f3", + "https://deno.land/std@0.173.0/log/handlers.ts": "38871ecbfa67b0d39dc3384210439ac9a13cba6118b912236f9011b5989b9a4d", + "https://deno.land/std@0.173.0/log/levels.ts": "6309147664e9e008cd6671610f2505c4c95f181f6bae4816a84b33e0aec66859", + "https://deno.land/std@0.173.0/log/logger.ts": "257ceb47e3f5f872068073de9809b015a7400e8d86dd40563c1d80169e578f71", + "https://deno.land/std@0.173.0/log/mod.ts": "36d156ad18de3f1806c6ddafa4965129be99ccafc27f1813de528d65b6c528bf", + "https://deno.land/std@0.173.0/node/_core.ts": "8667bf9129cdcbaac6f5c14f4def45ca954928baaa697a0ffdb0ee1556d4c644", + "https://deno.land/std@0.173.0/node/_events.d.ts": "1347437fd6b084d7c9a4e16b9fe7435f00b030970086482edeeb3b179d0775af", + "https://deno.land/std@0.173.0/node/_events.mjs": "d4ba4e629abe3db9f1b14659fd5c282b7da8b2b95eaf13238eee4ebb142a2448", + "https://deno.land/std@0.173.0/node/_utils.ts": "7fd55872a0cf9275e3c080a60e2fa6d45b8de9e956ebcde9053e72a344185884", + "https://deno.land/std@0.173.0/node/events.ts": "d2de352d509de11a375e2cb397d6b98f5fed4e562fc1d41be33214903a38e6b0", + "https://deno.land/std@0.173.0/node/internal/crypto/_keys.ts": "8f3c3b5a141aa0331a53c205e9338655f1b3b307a08085fd6ff6dda6f7c4190b", + "https://deno.land/std@0.173.0/node/internal/crypto/constants.ts": "544d605703053218499b08214f2e25cf4310651d535b7ab995891c4b7a217693", + "https://deno.land/std@0.173.0/node/internal/error_codes.ts": "8495e33f448a484518d76fa3d41d34fc20fe03c14b30130ad8e936b0035d4b8b", + "https://deno.land/std@0.173.0/node/internal/errors.ts": "1c699b8a3cb93174f697a348c004b1c6d576b66688eac8a48ebb78e65c720aae", + "https://deno.land/std@0.173.0/node/internal/hide_stack_frames.ts": "9dd1bad0a6e62a1042ce3a51eb1b1ecee2f246907bff44835f86e8f021de679a", + "https://deno.land/std@0.173.0/node/internal/normalize_encoding.mjs": "fd1d9df61c44d7196432f6e8244621468715131d18cc79cd299fc78ac549f707", + "https://deno.land/std@0.173.0/node/internal/util/inspect.mjs": "11d7c9cab514b8e485acc3978c74b837263ff9c08ae4537fa18ad56bae633259", + "https://deno.land/std@0.173.0/node/internal/util/types.ts": "4f3625ea39111eaae1443c834e769b0c5ce9ea33b31d5a853b02af6a78105178", + "https://deno.land/std@0.173.0/node/internal/validators.mjs": "e02f2b02dd072a5d623970292588d541204dc82207b4c58985d933a5f4b382e6", + "https://deno.land/std@0.173.0/node/internal_binding/_libuv_winerror.ts": "30c9569603d4b97a1f1a034d88a3f74800d5ea1f12fcc3d225c9899d4e1a518b", + "https://deno.land/std@0.173.0/node/internal_binding/_winerror.ts": "3e8cfdfe22e89f13d2b28529bab35155e6b1730c0221ec5a6fc7077dc037be13", + "https://deno.land/std@0.173.0/node/internal_binding/constants.ts": "21ff9d1ee71d0a2086541083a7711842fc6ae25e264dbf45c73815aadce06f4c", + "https://deno.land/std@0.173.0/node/internal_binding/types.ts": "5a658bf08975af30d0fad6fa6247274379be26ba3f023425bec03e61c74083ef", + "https://deno.land/std@0.173.0/node/internal_binding/util.ts": "808ff3b92740284184ab824adfc420e75398c88c8bccf5111f0c24ac18c48f10", + "https://deno.land/std@0.173.0/node/internal_binding/uv.ts": "eb0048e30af4db407fb3f95563e30d70efd6187051c033713b0a5b768593a3a3", + "https://deno.land/std@0.173.0/types.d.ts": "220ed56662a0bd393ba5d124aa6ae2ad36a00d2fcbc0e8666a65f4606aaa9784" + }, + "npm": { + "specifiers": { + "@types/redlock@^4.0.4": "@types/redlock@4.0.4", + "ioredis": "ioredis@5.2.5", + "ioredis@5.2.4": "ioredis@5.2.4", + "ioredis@5.2.5": "ioredis@5.2.5", + "redlock@5.0.0-beta.2": "redlock@5.0.0-beta.2" + }, + "packages": { + "@ioredis/commands@1.2.0": { + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==", + "dependencies": {} + }, + "@types/bluebird@3.5.38": { + "integrity": "sha512-yR/Kxc0dd4FfwtEoLZMoqJbM/VE/W7hXn/MIjb+axcwag0iFmSPK7OBUZq1YWLynJUoWQkfUrI7T0HDqGApNSg==", + "dependencies": {} + }, + "@types/ioredis@4.28.10": { + "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", + "dependencies": { "@types/node": "@types/node@18.11.18" } + }, + "@types/node@18.11.18": { + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", + "dependencies": {} + }, + "@types/redis@2.8.32": { + "integrity": "sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==", + "dependencies": { "@types/node": "@types/node@18.11.18" } + }, + "@types/redlock@4.0.4": { + "integrity": "sha512-1Q2cbSmuGQzqowWDVXkSDsj96muMSs1RiDBDHqSCiycyrpvNcMkmGZRaVrDgnbiL/YWXh3DAlKplAtIO4rzV7g==", + "dependencies": { + "@types/bluebird": "@types/bluebird@3.5.38", + "@types/ioredis": "@types/ioredis@4.28.10", + "@types/redis": "@types/redis@2.8.32" + } + }, + "cluster-key-slot@1.1.2": { + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "dependencies": {} + }, + "debug@4.3.4": { + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { "ms": "ms@2.1.2" } + }, + "denque@2.1.0": { + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "dependencies": {} + }, + "ioredis@5.2.4": { + "integrity": "sha512-qIpuAEt32lZJQ0XyrloCRdlEdUUNGG9i0UOk6zgzK6igyudNWqEBxfH6OlbnOOoBBvr1WB02mm8fR55CnikRng==", + "dependencies": { + "@ioredis/commands": "@ioredis/commands@1.2.0", + "cluster-key-slot": "cluster-key-slot@1.1.2", + "debug": "debug@4.3.4", + "denque": "denque@2.1.0", + "lodash.defaults": "lodash.defaults@4.2.0", + "lodash.isarguments": "lodash.isarguments@3.1.0", + "redis-errors": "redis-errors@1.2.0", + "redis-parser": "redis-parser@3.0.0", + "standard-as-callback": "standard-as-callback@2.1.0" + } + }, + "ioredis@5.2.5": { + "integrity": "sha512-7HKo/ClM2DGLRXdFq8ruS3Uuadensz4A76wPOU0adqlOqd1qkhoLPDaBhmVhUhNGpB+J65/bhLmNB8DDY99HJQ==", + "dependencies": { + "@ioredis/commands": "@ioredis/commands@1.2.0", + "cluster-key-slot": "cluster-key-slot@1.1.2", + "debug": "debug@4.3.4", + "denque": "denque@2.1.0", + "lodash.defaults": "lodash.defaults@4.2.0", + "lodash.isarguments": "lodash.isarguments@3.1.0", + "redis-errors": "redis-errors@1.2.0", + "redis-parser": "redis-parser@3.0.0", + "standard-as-callback": "standard-as-callback@2.1.0" + } + }, + "lodash.defaults@4.2.0": { + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dependencies": {} + }, + "lodash.isarguments@3.1.0": { + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "dependencies": {} + }, + "ms@2.1.2": { + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dependencies": {} + }, + "node-abort-controller@3.0.1": { + "integrity": "sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw==", + "dependencies": {} + }, + "redis-errors@1.2.0": { + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "dependencies": {} + }, + "redis-parser@3.0.0": { + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { "redis-errors": "redis-errors@1.2.0" } + }, + "redlock@5.0.0-beta.2": { + "integrity": "sha512-2RDWXg5jgRptDrB1w9O/JgSZC0j7y4SlaXnor93H/UJm/QyDiFgBKNtrh0TI6oCXqYSaSoXxFh6Sd3VtYfhRXw==", + "dependencies": { + "node-abort-controller": "node-abort-controller@3.0.1" + } + }, + "standard-as-callback@2.1.0": { + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "dependencies": {} + } + } + } +} diff --git a/readme/readme.cli.md b/readme/readme.cli.md new file mode 100644 index 0000000..409a708 --- /dev/null +++ b/readme/readme.cli.md @@ -0,0 +1,65 @@ +# Deno CLI + +reference :=> [doc](https://deno.land/manual@v1.29.4/getting_started/command_line_interface) + +## some useful commands + +```bash +deno help bundle +deno bundle -h +deno bundle --help +``` + +## Watch mode + +You can supply the --watch flag to deno run, deno test, deno bundle, and deno fmt to enable the built-in file watcher. The files that are watched depend on the subcommand used: + +for deno run, deno test, and deno bundle the entrypoint, and all local files the entrypoint(s) statically import(s) will be watched. +for deno fmt all local files and directories specified as command line arguments (or the working directory if no specific files/directories is passed) are watched. +Whenever one of the watched files is changed on disk, the program will automatically be restarted / formatted / tested / bundled. + +```bash +deno run --watch main.ts +deno test --watch +deno fmt --watch + +# +deno run --allow-net --allow-write --allow-read --allow-env --watch ./src/main.ts +``` + +## Script arguments + +Separately from the Deno runtime flags, you can pass user-space arguments to the script you are running by specifying them after the script name: + +```bash +deno run main.ts a b -c --quiet +``` + +```typescript +// main.ts +console.log(Deno.args); // [ "a", "b", "-c", "--quiet" ] +``` + +_Note that anything passed after the script name will be passed as a script argument and not consumed as a Deno runtime flag. This leads to the following pitfall:_ + +### _Good. We grant net permission to net_client.ts_ + +```bash +deno run --allow-net net_client.ts +``` + +### _Bad! --allow-net was passed to Deno.args, throws a net permission error_ + +```bash +deno run net_client.ts --allow-net +``` + +Some see it as unconventional that: + +- a non-positional flag is parsed differently depending on its position. + +However: + +- This is the most logical and ergonomic way of distinguishing between runtime flags and script arguments. +- This is, in fact, the same behavior as that of any other popular runtime. +- Try node -c index.js and node index.js -c. The first will only do a syntax check on index.js as per Node's -c flag. The second will execute index.js with -c passed to require("process").argv. diff --git a/readme/readme.configFile.md b/readme/readme.configFile.md new file mode 100644 index 0000000..9eee3eb --- /dev/null +++ b/readme/readme.configFile.md @@ -0,0 +1,70 @@ +# Deno CLI + +reference :=> [doc](https://deno.land/manual@v1.29.4/getting_started/configuration_file) + +Deno supports configuration file that allows to customize built-in TypeScript compiler, formatter and linter. + +The configuration file supports .json and .jsonc extensions. Since v1.18, Deno will automatically detect deno.json or deno.jsonc configuration file if it's in your current working directory (or parent directories). To manually tell Deno to use a specific configuration file pass --config path/to/file.json flag. + +⚠️ Starting with Deno v1.22 you can disable automatic detection of the configuration file, by passing --no-config. + +Note that using a configuration file is not required now, and will not be required in the future. Deno still works best with the default options and no configuration file. All options specified in the configuration file can also be set using command line flags (for example --options-use-tabs for deno fmt). Using the configuration file should be considered an "as needed" feature, not something every user should be reaching to as the first thing when setting up a project. + +```json +{ + "compilerOptions": { + "allowJs": true, + "lib": ["deno.window"], + "strict": true + }, + "importMap": "import_map.json", + "lint": { + "files": { + "include": ["src/"], + "exclude": ["src/testdata/"] + }, + "rules": { + "tags": ["recommended"], + "include": ["ban-untagged-todo"], + "exclude": ["no-unused-vars"] + } + }, + "fmt": { + "files": { + "include": ["src/"], + "exclude": ["src/testdata/"] + }, + "options": { + "useTabs": true, + "lineWidth": 80, + "indentWidth": 4, + "singleQuote": true, + "proseWrap": "preserve" + } + }, + "test": { + "files": { + "include": ["src/"], + "exclude": ["src/testdata/"] + } + } +} +``` + +## Example + +file: deno.jsonc + +```json +{ + "fmt": { + "options": { + "useTabs": true, + "lineWidth": 80, + "indentWidth": 2, + "singleQuote": true, + "proseWrap": "preserve" + } + } +} +``` diff --git a/readme/readme.env.md b/readme/readme.env.md new file mode 100644 index 0000000..e2cd042 --- /dev/null +++ b/readme/readme.env.md @@ -0,0 +1,51 @@ +# Deno ENV + +reference :=> [doc](https://deno.land/manual@v1.29.4/basics/env_variables) + +## Environment variables + +There are a few ways to use environment variables in Deno: + +### [](https://deno.land/manual@v1.29.4/basics/env_variables#built-in-denoenv)Built-in `Deno.env` + +The Deno runtime offers built-in support for environment variables with [`Deno.env`](https://deno.land/api@v1.25.3?s=Deno.env). + +`Deno.env` has getter and setter methods. Here is example usage: + +Deno.env.set("FIREBASE_API_KEY", "examplekey123"); +Deno.env.set("FIREBASE_AUTH_DOMAIN", "firebasedomain.com"); + +console.log(Deno.env.get("FIREBASE_API_KEY")); // examplekey123 +console.log(Deno.env.get("FIREBASE_AUTH_DOMAIN")); // firebasedomain.com + +### [](https://deno.land/manual@v1.29.4/basics/env_variables#env-file) `.env` file + +You can also put environment variables in a `.env` file and retrieve them using `dotenv` in the standard library. + +Let's say you have an `.env` file that looks like this: + +PASSWORD=Geheimnis + +To access the environment variables in the `.env` file, import the config function from the standard library. Then, import the configuration using the `config` function. + +import { config } from "https://deno.land/std/dotenv/mod.ts"; + +const configData = await config(); +const password = configData["PASSWORD"]; + +console.log(password); +// "Geheimnis" + +### [](https://deno.land/manual@v1.29.4/basics/env_variables#stdflags) `std/flags` + +The Deno standard library has a [`std/flags` module](https://deno.land/std@0.173.0/flags/README.md?source=) for parsing command line arguments. + +```typescript +import { load } from 'https://deno.land/std@0.173.0/dotenv/mod.ts'; /* DOTENV */ +Deno.env.set('SOME_VAR', 'Value'); +Deno.env.get('SOME_VAR'); // outputs "Value" +Deno.env.delete('SOME_VAR'); // outputs "undefined" +Deno.env.get('SOME_VAR'); // outputs "undefined" + +const env = Deno.env.toObject(); // Record // will contain all envs +``` diff --git a/readme/readme.logger.md b/readme/readme.logger.md new file mode 100644 index 0000000..7688652 --- /dev/null +++ b/readme/readme.logger.md @@ -0,0 +1,128 @@ +# Logger + +## Levels + +There are five standard logging levels (these are the most commonly used levels in industry) + +```typescript +import * as log from 'https://deno.land/std@0.173.0/log/mod.ts'; + +enum LogLevels { + NOTSET = 0, + DEBUG = 10, + INFO = 20, + WARNING = 30, + ERROR = 40, + CRITICAL = 50, +} +``` + +## Logging Functions + +Corresponding to levels, there are logging functions. For example: logger.debug function is used for logging at least at debug level. + +_The logging functions are:_ + +> **debug**: Log if level is at least debug or higher +> +> **info**: Log if level is at least info or higher +> +> **warning**: Log if level is at least warning or higher +> +> **error**: Log if level is at least error or higher +> +> **critical**: Log if level is at least critical or higher + +```typescript +logger.debug('abcd'); +logger.info('abcd'); +logger.warning('abcd'); +logger.error('abcd'); +logger.critical('abcd'); +``` + +## Default settings + +Unless customized, the logger comes with three default settings: + +> Default log level is set to INFO +> +> Default logging destination is set to console +> +> Default logging format is: `DEFAULT_FORMATTER = "{levelName} {msg}"` + +the default format is very simple: LEVEL MESSAGE. The default settings are good for development work, but not suitable for production work or background apps. + +That’s where the custom settings come in. + +_The custom settings can be applied in two steps_: + +- custom handler +- custom formatter + +Let’s see them one by one. + +## Log Handlers + +The logger has a concept of handlers. If unspecified, the default handler is set to console. + +All the log handlers take the log level as input, to specify the minimum log level the handler would consider. Depending on the type of handler, there would be additional options like file name, max size, number of backups, etc. + +Here is the complete list of handlers: + +### ConsoleHandler + +This handler writes log message to the console (also the default handler). In addition to log level, it can take a custom formatter. + +```typescript +new ConsoleHandler(levelName, formatter); +``` + +### FileHandler + +This handler writers log message to a fixed file. In additional to log level, it can take the output file name and a custom formatter. + +```typescript +new FileHandler(levelName, { filename: '/var/tmp/a.log' }); +``` + +### RotatingFileHandler + +This writes log messages to a rotating set of files with a max size (in bytes) and max number of backups. + +```typescript +new RotatingFileHandler(levelName, { filename: '/var/tmp/a.log', maxBytes: 10485760, maxBackupCount: 5 }); // mode?: 'a|w|x' // default: 'a' +``` + +#### mode + +- `'a'` Default mode. As above, this will pick up where the logs left off in rotation, or create a new log file if it doesn't exist. +- `'w'` in addition to starting with a clean `filename`, this mode will also cause any existing backups (up to `maxBackupCount`) to be deleted on setup giving a fully clean slate. +- `'x'` requires that neither `filename`, nor any backups (up to `maxBackupCount`), exist before setup. + + > The log line boundaries would be preserved when rotating files. + > In other words, if a log line is longer than maxBytes, it’d still be written in the same file. + + > **For development work, ConsoleHandler and FileHandler would be good. For production use, RotatingFileHandler would be the most useful as it’d keep the file size and number of backups in a limit. A normal FileHandler would grow the file as much as it could**. + +### Formatter + +#### A formatter could be a string or a function + +#### _string_ + +This would contain the keys present in LogRecord to output in the desired format + +```typescript +formatter: +'{datetime} {levelName} {msg}'; +``` + +#### _function_ + +This is a callback function that would take a LogRecord as input and returns a string that would be logged directly. JSON logging can be done using a function formatter. + +```typescript +formatter: +((rec) => JSON.stringify({ ts: rec.datetime, level: rec.levelName, data: rec.msg })); +``` diff --git a/src/deps.ts b/src/deps.ts new file mode 100644 index 0000000..c99d01d --- /dev/null +++ b/src/deps.ts @@ -0,0 +1,17 @@ +/* -------------- deno standard module -------------- */ +export { EventEmitter } from 'https://deno.land/std@0.173.0/node/events.ts'; +export { serve } from 'https://deno.land/std@0.173.0/http/server.ts'; /* HTTP SERVER */ +export { load } from 'https://deno.land/std@0.173.0/dotenv/mod.ts'; /* DOTENV */ +export * as Logger from 'https://deno.land/std@0.173.0/log/mod.ts'; /* LOGGER */ + +/* ------------- deno third party module ------------- */ + +/* ----------------------- npm ----------------------- */ +export { Redis } from 'npm:ioredis@5.2.5'; /* IOREDIS */ +export type { RedisOptions } from 'npm:ioredis@5.2.5'; +// +import * as redlock from 'npm:redlock@5.0.0-beta.2'; /* REDLOCK */ +export const Redlock = redlock.default; +export const Redlock_ExecutionError = redlock.ExecutionError; +export const Redlock_ResourceLockedError = redlock.ResourceLockedError; +// diff --git a/src/global/global.emitter.ts b/src/global/global.emitter.ts new file mode 100644 index 0000000..8f0f6f4 --- /dev/null +++ b/src/global/global.emitter.ts @@ -0,0 +1,18 @@ +import { EventEmitter } from '../deps.ts'; +// import type { Job } from 'bullmq'; + +class Emitter extends EventEmitter { + // deno-lint-ignore no-explicit-any + asyncEmit = (channel: string, data: any) => { + return new Promise((resolve) => _emitter.emit(channel, data, resolve)); + }; + + // /** + // * @param {string} jobName + // * @param {Job} job + // */ + // asyncBullEmit = (jobName: string, job: Job) => { + // return new Promise((resolve) => _emitter.emit(jobName, job, resolve)); + // }; +} +export const _emitter = new Emitter(); diff --git a/src/global/global.ioredis.ts b/src/global/global.ioredis.ts new file mode 100644 index 0000000..fac5084 --- /dev/null +++ b/src/global/global.ioredis.ts @@ -0,0 +1,198 @@ +// deno-lint-ignore-file no-explicit-any +import { EventEmitter, Redis, RedisOptions } from '../deps.ts'; +import initializeRedlock from './global.redlock.ts'; + +export default class RedisClient { + private options: RedisOptions; + + private isClientConnected = false; + private isPublisherConnected = false; + private isSubscriberConnected = false; + private isRedLockConnected = false; + private isBullConnected = false; + + private isResolved = false; + private isRejected = false; + + public client!: InstanceType & { on(eventName: string | symbol, listener: (...args: any[]) => void): EventEmitter }; + public publisher!: InstanceType & { on(eventName: string | symbol, listener: (...args: any[]) => void): EventEmitter }; + public subscriber!: InstanceType & { on(eventName: string | symbol, listener: (...args: any[]) => void): EventEmitter }; + public redLock!: InstanceType & { on(eventName: string | symbol, listener: (...args: any[]) => void): EventEmitter }; + public bull!: InstanceType & { on(eventName: string | symbol, listener: (...args: any[]) => void): EventEmitter }; + + public json = { + /** + * @ref: https://redis.io/commands/set + */ + set: async (key: string, path: string, value: any, options?: { mode?: 'NX' | 'XX'; EX?: number }): Promise<'OK' | null> => { + try { + // - JSON.SET key path value [NX | XX] + + if (typeof options?.EX === 'number' && !isNaN(options.EX)) { + // prettier-ignore + const txnRes = options?.mode + ? await this.client + .multi() + .call('JSON.SET', key, path, h.stringify(value), options.mode) + .expire(key, options.EX) + .exec() + : await this.client + .multi() + .call('JSON.SET', key, path, h.stringify(value)) + .expire(key, options.EX) + .exec(); // [ [err, res], [ err, res], .. ] + + if (!txnRes || txnRes[0][0] || txnRes[1][0]) return null; + const jsonRes = txnRes[0][1]; + const expireRes = txnRes[1][1]; // 1 if the timeout was set. 0 if the timeout was not set. + if (jsonRes && !expireRes) log.warning(`[Redis] failed to set expiry wit [JSON.SET] ${key} ${path} ${value} ${options?.EX} ${expireRes}`); + return jsonRes as 'OK' | null; + } + + const res = options?.mode + ? await this.client.call('JSON.SET', key, path, h.stringify(value), options.mode) + : await this.client.call('JSON.SET', key, path, h.stringify(value)); + if (res === null) return res; + return res as 'OK' | null; + } catch (err: any) { + log.error(`[Redis] failed to set ${key} ${path} ${value} ${options?.EX} ${err.message}`); + return null; + } + }, + + /** + * @ref: https://redis.io/commands/get + */ + get: async (key: string, path?: string): Promise | null> => { + // - JSON.GET key path + const res = await this.client.call('JSON.GET', key, path ?? '$'); + if (res === null) return null; + const parsedRes = h.parse(res); + return Array.isArray(parsedRes) ? parsedRes[0] : parsedRes; + }, + }; + + constructor() { + this.options = { + host: Deno.env.get('REDIS_HOST'), + port: parseInt(Deno.env.get('REDIS_PORT') ?? '', 10), + // username: process.env.REDIS_USERNAME, + password: Deno.env.get('REDIS_PASSWORD'), + enableAutoPipelining: true, + commandQueue: true, + enableOfflineQueue: true, + enableReadyCheck: true, + reconnectOnError: () => true, + maxRetriesPerRequest: null, + }; + } + + private updateConnection(client?: 'GAME' | 'PUBLISHER' | 'SUBSCRIBER' | 'REDLOCK' | 'BULL', resolve = (_x: unknown) => {}) { + if (client === 'GAME') this.isClientConnected = true; + else if (client === 'PUBLISHER') this.isPublisherConnected = true; + else if (client === 'SUBSCRIBER') this.isSubscriberConnected = true; + else if (client === 'REDLOCK') this.isRedLockConnected = true; + else if (client === 'BULL') this.isBullConnected = true; + + log.debug(`Redis client Connected '${client} '⚡`); + + if (this.isClientConnected && this.isPublisherConnected && this.isSubscriberConnected && this.isRedLockConnected && this.isBullConnected && !this.isResolved) { + this.isResolved = true; + log.debug(`IoRedis Initialized ✅`); + resolve(null); + initializeRedlock(); + } + } + + private errorHandler(err: any, client?: 'GAME' | 'PUBLISHER' | 'SUBSCRIBER' | 'REDLOCK' | 'BULL', rej = (_x: unknown) => {}) { + log.error(`Redis Error on client '${client}' ☠ \n ${err.message}`); + if (this.isRejected) return; + this.isRejected = true; + rej(null); + } + + private async setupConfig() { + try { + await this.subscriber.subscribe('__keyevent@0__:expired', 'redisEvent', () => ( this.subscriber).on('message', this.onMessage)); + if (Deno.env.get('DENO_ENV') !== 'prod') await this.client.config('SET', 'notify-keyspace-events', 'Ex'); + } catch (err: any) { + log.error('RedisClient error on method setupConfig() ☠', +' ' + err?.message); + } + } + + private async onMessage(channel: string, message: any) { + log.debug(`IORedisClient onMessage() message: ${message} channel: ${channel}`); + let _channel, _message; + + if (channel === '__keyevent@0__:expired') { + const [type] = message.split(':'); // 'sch:fqr6dlI_2Gg2TcH3_YTfj:assignBot::127.0.0.1' | redisEvent:uid1:cleanKick:kingdomId:kid_1:127.0.0.1' + let channelId, taskName, userId, ip, customIdType, customId, key, val; + switch (type) { + // game schedular + case 'sch': + [, channelId, taskName, userId, ip] = message.split(':'); // 'sch:fqr6dlI_2Gg2TcH3_YTfj:assignBot::127.0.0.1' + _channel = type; // 'sch' + _message = { taskName, channelId, userId }; + break; + // custom schedular + case 'schCustom': + [, userId, taskName, customIdType, customId, ip] = message.split(':'); // 'schCustom:uid1:cleanKick:kingdomId:kid_1:127.0.0.1' + _channel = type; // 'schCustom' 'schCustom:uid_1:removeKickedKingdomData:kingdom_id:kid_1:127.0.0.1/dev' + _message = { taskName, userId, customIdType, customId }; + break; + case 'changeSeason': + [, key, val, ip] = message.split(':'); + _channel = type; + _message = { key, val }; + break; + default: + return false; + } + if (ip !== Deno.env.get('HOST')) return false; + } else return; + + let parsedMessage = {}; + try { + parsedMessage = h.parse(_message); + } catch (err: any) { + log.error(`can not parse message ☠ -> ${_message} ${{ reason: err.message, stack: err.stack }}`); + parsedMessage = _message; + } + await emitter.asyncEmit(_channel, parsedMessage); // ch : redisEvent | sch + } + + public initialize() { + return new Promise((res, rej) => { + try { + this.client = new Redis(this.options) as typeof this.client; + this.publisher = new Redis(this.options) as typeof this.publisher; + this.subscriber = new Redis(this.options) as typeof this.subscriber; + this.redLock = new Redis(this.options) as typeof this.redLock; + this.bull = new Redis(this.options) as typeof this.bull; + + this.client.on('error', (error) => this.errorHandler(error, 'GAME', rej)); + this.publisher.on('error', (error) => this.errorHandler(error, 'PUBLISHER', rej)); + this.subscriber.on('error', (error) => this.errorHandler(error, 'SUBSCRIBER', rej)); + this.redLock.on('error', (error) => this.errorHandler(error, 'REDLOCK', rej)); + this.bull.on('error', (error) => this.errorHandler(error, 'BULL', rej)); + this.setupConfig(); + + /* + this.client.on('connect', () => log.silly(`Redis connected 'GAME' ⚡`)); + this.publisher.on('connect', () => log.silly(`Redis connected 'PUBLISHER' ⚡`)); + this.subscriber.on('connect', () => log.silly(`Redis connected 'SUBSCRIBER' ⚡`)); + this.redLock.on('connect', () => log.silly(`Redis connected 'REDLOCK' ⚡`)); + this.bull.on('connect', () => log.silly(`Redis connected 'BULL' ⚡`)); + */ + + this.client.on('ready', () => this.updateConnection('GAME', res)); + this.publisher.on('ready', () => this.updateConnection('PUBLISHER', res)); + this.subscriber.on('ready', () => this.updateConnection('SUBSCRIBER', res)); + this.redLock.on('ready', () => this.updateConnection('REDLOCK', res)); + this.bull.on('ready', () => this.updateConnection('BULL', res)); + } catch (err: any) { + log.error(` Redis Error on initialize() ☠ ${err.message}`); + } + }); + } +} diff --git a/src/global/global.logger.ts b/src/global/global.logger.ts new file mode 100644 index 0000000..c66f815 --- /dev/null +++ b/src/global/global.logger.ts @@ -0,0 +1,71 @@ +// deno-lint-ignore-file no-explicit-any + +import { Logger } from '../deps.ts'; + +const formatter = { + default: '{datetime} {levelName} {msg}', + custom: (rec: any) => { + // + // access args + const args = rec.args; + const id = args[args.length - 1] ?? ''; + const flag = args[args.length - 2] ?? ''; + const track = args[args.length - 3] ?? ''; + return `${rec.datetime} ${rec.levelName} id: ${id}, flag: ${flag}, track: ${track}, data: \n${rec.msg}\n`; + }, + json: (rec: any) => JSON.stringify({ ts: rec.datetime, level: rec.levelName, data: rec.msg }), + jsonWithRegion: (rec: any) => JSON.stringify({ region: rec.loggerName, ts: rec.datetime, level: rec.levelName, data: rec.msg }), + jsonWithArgs: (rec: any) => { + // + // access args + const args = rec.args; + const id = args[args.length - 1] ?? ''; + const flag = args[args.length - 2] ?? ''; + const track = args[args.length - 3] ?? ''; + // + return JSON.stringify({ ts: rec.datetime, level: rec.levelName, id, flag, track, data: rec.msg }); + }, + jsonWithArgsAndRegion: (rec: any) => { + // + // access args + const args = rec.args; + const id = args[args.length - 1] ?? ''; + const flag = args[args.length - 2] ?? ''; + const track = args[args.length - 3] ?? ''; + // + return JSON.stringify({ region: rec.loggerName, ts: rec.datetime, level: rec.levelName, id, flag, track, data: rec.msg }); + }, +}; + +const handlers = { + console: new Logger.handlers.ConsoleHandler('DEBUG', { formatter: formatter.custom }), + file: (filename: string) => new Logger.handlers.FileHandler('INFO', { filename, formatter: formatter.jsonWithArgs, mode: 'a' }), + rotatingFile: (filename: string) => + new Logger.handlers.RotatingFileHandler('INFO', { filename, maxBytes: (2 ** 10) * (2 ** 10), maxBackupCount: 5, formatter: formatter.jsonWithArgs, mode: 'a' }), +}; + +Logger.setup({ + /* define handlers */ + handlers: { + console: handlers.console, + file: handlers.rotatingFile('./.logs/a.log'), + }, + + /** + * - assign handlers to loggers. + * - level should not be less than level specified in handler + * - level would not have any effect if level specified here is less than what specified in handler. + * - in that case level of handler will be used + */ + loggers: { + default: { level: 'DEBUG', handlers: ['console'] }, + file: { level: 'INFO', handlers: ['file'] }, + consoleAndFile: { level: 'DEBUG', handlers: ['console', 'file'] }, + }, +}); + +const lg = Logger.getLogger('consoleAndFile'); + +export default lg; + +// lg.debug({a: 1, b: {c: 1}}, 'track:status', 'flag:req_id', 'id:user_id'); diff --git a/src/global/global.redlock.ts b/src/global/global.redlock.ts new file mode 100644 index 0000000..2309e86 --- /dev/null +++ b/src/global/global.redlock.ts @@ -0,0 +1,29 @@ +// deno-lint-ignore-file no-explicit-any +import type { EventEmitter } from '../deps.ts'; +import { Redlock, Redlock_ResourceLockedError } from '../deps.ts'; + +function errorHandler(error: any) { + // Ignore cases where a resource is explicitly marked as locked on a client. + if (error instanceof Redlock_ResourceLockedError) return; + // Log all other errors. + log.error(`A error has occurred. [[redlock]] ☠. reason: ${error?.message}`); +} + +function initializeRedlock() { + try { + const redlock = new Redlock([( redis).redLock], { + driftFactor: 0.01, // multiplied by lock ttl to determine drift time + retryCount: -1, + retryDelay: 200, // time in ms + retryJitter: 200, // time in ms + automaticExtensionThreshold: 500, // time in ms + }); + ( redlock).on('error', errorHandler); + globalThis.Lock = redlock; + log.debug('RedLock initialized 🔐'); + } catch (err: any) { + log.critical(`RedLock initialization failed ❌ \nreason: ${err.message}`); + } +} + +export default initializeRedlock; diff --git a/src/global/global.ts b/src/global/global.ts new file mode 100644 index 0000000..a1b7f33 --- /dev/null +++ b/src/global/global.ts @@ -0,0 +1,27 @@ +// deno-lint-ignore-file no-var +import { load } from '../deps.ts'; +import type { Redlock } from '../deps.ts'; +import Utils from './global.util.ts'; +import { _emitter } from './global.emitter.ts'; +import Logger from './global.logger.ts'; +import IOREDIS from './global.ioredis.ts'; + +/* SETUP envs */ +const envs = await load({ allowEmptyValues: false, envPath: '.env', export: true }); /* loading envs from '.env' files and export to `Deno.env` */ +Logger.debug(envs, 'env_loaded', '', ''); +// + +declare global { + var redis: IOREDIS; + var h: typeof Utils; + var log: typeof Logger; + var emitter: typeof _emitter; + var Lock: InstanceType; +} + +globalThis.h = Utils; +globalThis.log = Logger; +globalThis.emitter = _emitter; +globalThis.redis = new IOREDIS(); + +export {}; diff --git a/src/global/global.util.ts b/src/global/global.util.ts new file mode 100644 index 0000000..adfba49 --- /dev/null +++ b/src/global/global.util.ts @@ -0,0 +1,19 @@ +// deno-lint-ignore-file no-explicit-any +export default { + stringify: (x: unknown) => { + try { + return JSON.stringify(x); + } catch (error: any) { + log.error(error); + return x; + } + }, + parse: (x: any) => { + try { + return JSON.parse(x); + } catch (error: any) { + log.error(error); + return x; + } + }, +}; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..eaacec5 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,10 @@ +import { serve } from './deps.ts'; +import './global/global.ts'; + +await redis.initialize(); + +serve(async () => { + log.debug('req'); + const x = await redis.client.get('a'); + return new Response('pong: ' + x); +}); diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/utils.utilities.ts b/src/utils/utils.utilities.ts new file mode 100644 index 0000000..a91510a --- /dev/null +++ b/src/utils/utils.utilities.ts @@ -0,0 +1,4 @@ +export function appendId(resp: Response, id: string) { + if (!resp) return; + resp.headers.set('x-tracking-id', id); +}