Compare commits
34 Commits
dev-pretti
...
master
Author | SHA1 | Date | |
---|---|---|---|
58363c2c1d | |||
86d0b37713 | |||
de2c82617f | |||
f6e2278c81 | |||
f36f893e70 | |||
|
63abd46364 | ||
6f465d79a3 | |||
78d2ec5ce6 | |||
6cecbec592 | |||
ed9559efe5 | |||
4d17c3bcff | |||
d0e5faa040 | |||
4f76cce8d3 | |||
e7ad775573 | |||
5d9ca61ecc | |||
29480105c5 | |||
|
caca4f3409 | ||
|
4b78256ae2 | ||
|
c6e74cca90 | ||
|
1ca64bc925 | ||
|
b772c83c53 | ||
76f1c43858 | |||
a2d344ceb4 | |||
50d07820dc | |||
7a41437121 | |||
049e90540d | |||
c93df5d573 | |||
e8d1ba9c96 | |||
c0a310b5ff | |||
d9014d2a54 | |||
feb5e5b4ca | |||
01931cb238 | |||
cc453e2337 | |||
fcb5780ef9 |
@ -8,3 +8,4 @@ deployment.yaml
|
|||||||
Dockerfile
|
Dockerfile
|
||||||
readme.md
|
readme.md
|
||||||
.git
|
.git
|
||||||
|
lib
|
35
.drone.yml
35
.drone.yml
@ -1,35 +0,0 @@
|
|||||||
kind: pipeline
|
|
||||||
type: kubernetes
|
|
||||||
name: build
|
|
||||||
steps:
|
|
||||||
- name: docker
|
|
||||||
image: harbor.k-space.ee/k-space/drone-kaniko
|
|
||||||
settings:
|
|
||||||
repo: ${DRONE_REPO}
|
|
||||||
tags: ${DRONE_BRANCH}
|
|
||||||
registry: harbor.codemowers.eu
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
|
|
||||||
- name: test
|
|
||||||
image: harbor.codemowers.eu/msergo/walias:${DRONE_COMMIT_SHA}
|
|
||||||
settings:
|
|
||||||
registry: harbor.codemowers.eu
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
commands:
|
|
||||||
- npm test
|
|
||||||
|
|
||||||
# services:
|
|
||||||
# - name: mysql
|
|
||||||
# image: mysql:8
|
|
||||||
# environment:
|
|
||||||
# MYSQL_ROOT_PASSWORD: dev
|
|
||||||
# MYSQL_DATABASE: sky
|
|
||||||
|
|
||||||
image_pull_secrets:
|
|
||||||
- dockerconfigjson
|
|
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"printWidth": 120,
|
"printWidth": 120,
|
||||||
"quoteProps": "as-needed",
|
"quoteProps": "as-needed",
|
||||||
"arrowParens": "avoid"
|
"arrowParens": "avoid"
|
||||||
}
|
}
|
@ -1,13 +0,0 @@
|
|||||||
pipeline:
|
|
||||||
build:
|
|
||||||
image: plugins/kaniko
|
|
||||||
settings:
|
|
||||||
repo: harbor.k-space.ee/${CI_REPO}
|
|
||||||
tags: latest
|
|
||||||
registry: harbor.k-space.ee
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
when:
|
|
||||||
branch: master
|
|
26
.woodpecker/build.yaml
Normal file
26
.woodpecker/build.yaml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
matrix:
|
||||||
|
ARCH:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: woodpeckerci/plugin-kaniko
|
||||||
|
backend_options:
|
||||||
|
kubernetes:
|
||||||
|
nodeSelector:
|
||||||
|
kubernetes.io/arch: ${ARCH}
|
||||||
|
tolerations:
|
||||||
|
- key: arch
|
||||||
|
operator: Equal
|
||||||
|
value: ${ARCH}
|
||||||
|
effect: NoSchedule
|
||||||
|
settings:
|
||||||
|
repo: ${CI_REPO}
|
||||||
|
registry: harbor.k-space.ee
|
||||||
|
tags: latest-${ARCH}
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
31
.woodpecker/manifest.yaml
Normal file
31
.woodpecker/manifest.yaml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
skip_clone: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: manifest
|
||||||
|
image: mirror.gcr.io/mplatform/manifest-tool:alpine-v2.1.6
|
||||||
|
secrets:
|
||||||
|
- docker_username
|
||||||
|
- docker_password
|
||||||
|
commands:
|
||||||
|
- set -u
|
||||||
|
- ls -lash
|
||||||
|
- env
|
||||||
|
- |
|
||||||
|
cat << EOF > spec.yaml
|
||||||
|
image: "harbor.k-space.ee/${CI_REPO}:latest"
|
||||||
|
manifests:
|
||||||
|
- image: "harbor.k-space.ee/${CI_REPO}:latest-amd64"
|
||||||
|
platform:
|
||||||
|
architecture: amd64
|
||||||
|
os: linux
|
||||||
|
- image: "harbor.k-space.ee/${CI_REPO}:latest-arm64"
|
||||||
|
platform:
|
||||||
|
architecture: arm64
|
||||||
|
os: linux
|
||||||
|
EOF
|
||||||
|
- /manifest-tool --username $docker_username --password $docker_password push from-spec spec.yaml > stdout
|
||||||
|
- cat stdout
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- build
|
@ -1,4 +1,4 @@
|
|||||||
FROM node:18-alpine as dev
|
FROM mirror.gcr.io/library/node:18-alpine as dev
|
||||||
RUN apk add netcat-openbsd
|
RUN apk add netcat-openbsd
|
||||||
|
|
||||||
RUN npm config set update-notifier false
|
RUN npm config set update-notifier false
|
||||||
@ -12,7 +12,7 @@ RUN npm run compile
|
|||||||
|
|
||||||
ENTRYPOINT npm run start
|
ENTRYPOINT npm run start
|
||||||
|
|
||||||
FROM node:18-alpine AS prod
|
FROM mirror.gcr.io/node:18-alpine AS prod
|
||||||
RUN npm config set update-notifier false
|
RUN npm config set update-notifier false
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"origins": [
|
"origins": [
|
||||||
"http://localhost:3030"
|
"http://localhost:3030"
|
||||||
],
|
],
|
||||||
|
"sessionSecret": "test",
|
||||||
"paginate": {
|
"paginate": {
|
||||||
"default": 10,
|
"default": 10,
|
||||||
"max": 50
|
"max": 50
|
||||||
@ -14,5 +15,8 @@
|
|||||||
"token": "aaaaa",
|
"token": "aaaaa",
|
||||||
"domain": "test-codemowers.eu",
|
"domain": "test-codemowers.eu",
|
||||||
"preferredDomain": "k-space.ee"
|
"preferredDomain": "k-space.ee"
|
||||||
|
},
|
||||||
|
"redis": {
|
||||||
|
"url": "redis://localhost:6379"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,14 +1,27 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
clientUrl: process.env.CLIENT_URL,
|
clientUrl: process.env.CLIENT_URL,
|
||||||
|
sessionSecret: process.env.SESSION_SECRET,
|
||||||
oidc: {
|
oidc: {
|
||||||
gatewayUri: process.env.OIDC_GATEWAY_URI,
|
gatewayUri: process.env.OIDC_GATEWAY_URI,
|
||||||
|
gatewayAuthUri: process.env.OIDC_GATEWAY_AUTH_URI,
|
||||||
|
gatewayTokenUri: process.env.OIDC_GATEWAY_TOKEN_URI,
|
||||||
|
gatewayUserinfoUri: process.env.OIDC_GATEWAY_USERINFO_URI,
|
||||||
clientId: process.env.OIDC_CLIENT_ID,
|
clientId: process.env.OIDC_CLIENT_ID,
|
||||||
clientSecret: process.env.OIDC_CLIENT_SECRET,
|
clientSecret: process.env.OIDC_CLIENT_SECRET,
|
||||||
redirectUris: process.env.OIDC_REDIRECT_URIS
|
redirectUris: process.env.OIDC_REDIRECT_URIS,
|
||||||
|
scopes: 'openid profile',
|
||||||
|
grantTypes: process.env.OIDC_GRANT_TYPES,
|
||||||
|
signedResponseAlg: process.env.OIDC_ID_TOKEN_SIGNED_RESPONSE_ALG,
|
||||||
|
codeChallengeMethod: 'S256',
|
||||||
|
responseTypes: process.env.OIDC_RESPONSE_TYPES,
|
||||||
|
authMethod: process.env.OIDC_TOKEN_ENDPOINT_AUTH_METHOD,
|
||||||
},
|
},
|
||||||
wildDuck: {
|
wildDuck: {
|
||||||
url: process.env.WILDDUCK_URL,
|
url: process.env.WILDDUCK_URL,
|
||||||
token: process.env.WILDDUCK_TOKEN,
|
token: process.env.WILDDUCK_TOKEN,
|
||||||
domain: process.env.WILDDUCK_DOMAIN
|
domain: process.env.WILDDUCK_DOMAIN,
|
||||||
}
|
},
|
||||||
|
redis: {
|
||||||
|
url: process.env.REDIS_URL,
|
||||||
|
},
|
||||||
};
|
};
|
@ -98,6 +98,11 @@ spec:
|
|||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: walias-secrets
|
name: walias-secrets
|
||||||
key: WILDDUCK_DOMAIN
|
key: WILDDUCK_DOMAIN
|
||||||
|
- name: REDIS_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: redis-walias-cache-owner-secrets
|
||||||
|
key: REDIS_MASTER_URI
|
||||||
envFrom:
|
envFrom:
|
||||||
- secretRef:
|
- secretRef:
|
||||||
name: oidc-client-walias-owner-secrets
|
name: oidc-client-walias-owner-secrets
|
||||||
|
334
package-lock.json
generated
334
package-lock.json
generated
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "walias",
|
"name": "wildflock",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "walias",
|
"name": "wildflock",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@faker-js/faker": "^8.0.2",
|
"@faker-js/faker": "^8.0.2",
|
||||||
@ -22,9 +22,12 @@
|
|||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"config": "^3.3.9",
|
"config": "^3.3.9",
|
||||||
|
"connect-redis": "^7.1.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"express-session": "^1.17.3",
|
"express-session": "^1.17.3",
|
||||||
"openid-client": "^5.4.3",
|
"ioredis": "^5.3.2",
|
||||||
|
"openid-client": "^5.6.5",
|
||||||
|
"redis": "^4.6.7",
|
||||||
"winston": "^3.10.0"
|
"winston": "^3.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -34,6 +37,7 @@
|
|||||||
"@types/express-session": "^1.17.7",
|
"@types/express-session": "^1.17.7",
|
||||||
"@types/mocha": "^10.0.1",
|
"@types/mocha": "^10.0.1",
|
||||||
"@types/node": "^20.4.5",
|
"@types/node": "^20.4.5",
|
||||||
|
"@types/redis": "^4.0.11",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^13.2.3",
|
"lint-staged": "^13.2.3",
|
||||||
@ -413,6 +417,11 @@
|
|||||||
"url": "https://github.com/sponsors/daffl"
|
"url": "https://github.com/sponsors/daffl"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@ioredis/commands": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/resolve-uri": {
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||||
@ -438,6 +447,59 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@redis/bloom": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/client": {
|
||||||
|
"version": "1.5.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.8.tgz",
|
||||||
|
"integrity": "sha512-xzElwHIO6rBAqzPeVnCzgvrnBEcFL1P0w8P65VNLRkdVW8rOE58f52hdj0BDgmsdOm4f1EoXPZtH4Fh7M/qUpw==",
|
||||||
|
"dependencies": {
|
||||||
|
"cluster-key-slot": "1.1.2",
|
||||||
|
"generic-pool": "3.9.0",
|
||||||
|
"yallist": "4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/graph": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/json": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/search": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@redis/time-series": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@redis/client": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@socket.io/component-emitter": {
|
"node_modules/@socket.io/component-emitter": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||||
@ -610,6 +672,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
|
||||||
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
|
"integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/redis": {
|
||||||
|
"version": "4.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/redis/-/redis-4.0.11.tgz",
|
||||||
|
"integrity": "sha512-bI+gth8La8Wg/QCR1+V1fhrL9+LZUSWfcqpOj2Kc80ZQ4ffbdL173vQd5wovmoV9i071FU9oP2g6etLuEwb6Rg==",
|
||||||
|
"deprecated": "This is a stub types definition. redis provides its own type definitions, so you do not need this installed.",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"redis": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/send": {
|
"node_modules/@types/send": {
|
||||||
"version": "0.17.1",
|
"version": "0.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
|
||||||
@ -867,11 +939,11 @@
|
|||||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.4.0",
|
"version": "1.6.7",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
|
||||||
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
|
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.0",
|
"follow-redirects": "^1.15.4",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"proxy-from-env": "^1.1.0"
|
"proxy-from-env": "^1.1.0"
|
||||||
}
|
}
|
||||||
@ -931,12 +1003,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "1.20.1",
|
"version": "1.20.2",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
"content-type": "~1.0.4",
|
"content-type": "~1.0.5",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
"destroy": "1.2.0",
|
"destroy": "1.2.0",
|
||||||
@ -944,7 +1016,7 @@
|
|||||||
"iconv-lite": "0.4.24",
|
"iconv-lite": "0.4.24",
|
||||||
"on-finished": "2.4.1",
|
"on-finished": "2.4.1",
|
||||||
"qs": "6.11.0",
|
"qs": "6.11.0",
|
||||||
"raw-body": "2.5.1",
|
"raw-body": "2.5.2",
|
||||||
"type-is": "~1.6.18",
|
"type-is": "~1.6.18",
|
||||||
"unpipe": "1.0.0"
|
"unpipe": "1.0.0"
|
||||||
},
|
},
|
||||||
@ -971,12 +1043,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/braces": {
|
"node_modules/braces": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fill-range": "^7.0.1"
|
"fill-range": "^7.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@ -1229,6 +1301,14 @@
|
|||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cluster-key-slot": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color": {
|
"node_modules/color": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||||
@ -1340,6 +1420,17 @@
|
|||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/connect-redis": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-UaqO1EirWjON2ENsyau7N5lbkrdYBpS6mYlXSeff/OYXsd6EGZ+SXSmNPoljL2PSua8fgjAEaldSA73PMZQ9Eg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"express-session": ">=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
@ -1379,9 +1470,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cookie": {
|
"node_modules/cookie": {
|
||||||
"version": "0.5.0",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@ -1501,6 +1592,14 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/denque": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
@ -1566,9 +1665,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/engine.io": {
|
"node_modules/engine.io": {
|
||||||
"version": "6.5.1",
|
"version": "6.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
|
||||||
"integrity": "sha512-mGqhI+D7YxS9KJMppR6Iuo37Ed3abhU8NdfgSvJSDUafQutrN+sPTncJYTyM9+tkhSmWodKtVYGPPHyXJEwEQA==",
|
"integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/cookie": "^0.4.1",
|
"@types/cookie": "^0.4.1",
|
||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
@ -1578,17 +1677,17 @@
|
|||||||
"cookie": "~0.4.1",
|
"cookie": "~0.4.1",
|
||||||
"cors": "~2.8.5",
|
"cors": "~2.8.5",
|
||||||
"debug": "~4.3.1",
|
"debug": "~4.3.1",
|
||||||
"engine.io-parser": "~5.1.0",
|
"engine.io-parser": "~5.2.1",
|
||||||
"ws": "~8.11.0"
|
"ws": "~8.17.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/engine.io-parser": {
|
"node_modules/engine.io-parser": {
|
||||||
"version": "5.1.0",
|
"version": "5.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||||
"integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==",
|
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
@ -1724,16 +1823,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.18.2",
|
"version": "4.19.2",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
"body-parser": "1.20.1",
|
"body-parser": "1.20.2",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"content-type": "~1.0.4",
|
"content-type": "~1.0.4",
|
||||||
"cookie": "0.5.0",
|
"cookie": "0.6.0",
|
||||||
"cookie-signature": "1.0.6",
|
"cookie-signature": "1.0.6",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
@ -1868,9 +1967,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
@ -1927,9 +2026,9 @@
|
|||||||
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.2",
|
"version": "1.15.6",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@ -1999,6 +2098,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/generic-pool": {
|
||||||
|
"version": "3.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
|
||||||
|
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-caller-file": {
|
"node_modules/get-caller-file": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
@ -2274,6 +2381,50 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ioredis": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@ioredis/commands": "^1.1.1",
|
||||||
|
"cluster-key-slot": "^1.1.0",
|
||||||
|
"debug": "^4.3.4",
|
||||||
|
"denque": "^2.1.0",
|
||||||
|
"lodash.defaults": "^4.2.0",
|
||||||
|
"lodash.isarguments": "^3.1.0",
|
||||||
|
"redis-errors": "^1.2.0",
|
||||||
|
"redis-parser": "^3.0.0",
|
||||||
|
"standard-as-callback": "^2.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.22.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/ioredis"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ioredis/node_modules/debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ioredis/node_modules/ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
@ -2398,9 +2549,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/jose": {
|
"node_modules/jose": {
|
||||||
"version": "4.14.4",
|
"version": "4.15.5",
|
||||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz",
|
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.5.tgz",
|
||||||
"integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==",
|
"integrity": "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/panva"
|
"url": "https://github.com/sponsors/panva"
|
||||||
}
|
}
|
||||||
@ -2650,6 +2801,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.defaults": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
|
||||||
|
},
|
||||||
|
"node_modules/lodash.isarguments": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="
|
||||||
|
},
|
||||||
"node_modules/log-symbols": {
|
"node_modules/log-symbols": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
|
||||||
@ -3232,11 +3393,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/openid-client": {
|
"node_modules/openid-client": {
|
||||||
"version": "5.4.3",
|
"version": "5.6.5",
|
||||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz",
|
||||||
"integrity": "sha512-sVQOvjsT/sbSfYsQI/9liWQGVZH/Pp3rrtlGEwgk/bbHfrUDZ24DN57lAagIwFtuEu+FM9Ev7r85s8S/yPjimQ==",
|
"integrity": "sha512-5P4qO9nGJzB5PI0LFlhj4Dzg3m4odt0qsJTfyEtZyOlkgpILwEioOhVVJOrS1iVH494S4Ee5OCjjg6Bf5WOj3w==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jose": "^4.14.4",
|
"jose": "^4.15.5",
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^6.0.0",
|
||||||
"object-hash": "^2.2.0",
|
"object-hash": "^2.2.0",
|
||||||
"oidc-token-hash": "^5.0.3"
|
"oidc-token-hash": "^5.0.3"
|
||||||
@ -3478,9 +3639,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/raw-body": {
|
"node_modules/raw-body": {
|
||||||
"version": "2.5.1",
|
"version": "2.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
@ -3536,6 +3697,38 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/redis": {
|
||||||
|
"version": "4.6.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis/-/redis-4.6.7.tgz",
|
||||||
|
"integrity": "sha512-KrkuNJNpCwRm5vFJh0tteMxW8SaUzkm5fBH7eL5hd/D0fAkzvapxbfGPP/r+4JAXdQuX7nebsBkBqA2RHB7Usw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@redis/bloom": "1.2.0",
|
||||||
|
"@redis/client": "1.5.8",
|
||||||
|
"@redis/graph": "1.1.0",
|
||||||
|
"@redis/json": "1.0.4",
|
||||||
|
"@redis/search": "1.1.3",
|
||||||
|
"@redis/time-series": "1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/redis-errors": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/redis-parser": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
|
||||||
|
"dependencies": {
|
||||||
|
"redis-errors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/regenerator-runtime": {
|
"node_modules/regenerator-runtime": {
|
||||||
"version": "0.13.11",
|
"version": "0.13.11",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||||
@ -3851,13 +4044,35 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/socket.io-adapter": {
|
"node_modules/socket.io-adapter": {
|
||||||
"version": "2.5.2",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
|
||||||
"integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
|
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ws": "~8.11.0"
|
"debug": "~4.3.4",
|
||||||
|
"ws": "~8.17.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/socket.io-adapter/node_modules/debug": {
|
||||||
|
"version": "4.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
|
||||||
|
"integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-adapter/node_modules/ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
"node_modules/socket.io-parser": {
|
"node_modules/socket.io-parser": {
|
||||||
"version": "4.2.4",
|
"version": "4.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
@ -3920,6 +4135,11 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/standard-as-callback": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="
|
||||||
|
},
|
||||||
"node_modules/statuses": {
|
"node_modules/statuses": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
@ -4357,15 +4577,15 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.11.0",
|
"version": "8.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"bufferutil": "^4.0.1",
|
"bufferutil": "^4.0.1",
|
||||||
"utf-8-validate": "^5.0.2"
|
"utf-8-validate": ">=5.0.2"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"bufferutil": {
|
"bufferutil": {
|
||||||
|
21
package.json
21
package.json
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "walias",
|
"name": "wildflock",
|
||||||
"description": "Aliases for Wild Duck",
|
"description": "Aliases for Wild Duck",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
@ -7,9 +7,16 @@
|
|||||||
"keywords": [
|
"keywords": [
|
||||||
"feathers"
|
"feathers"
|
||||||
],
|
],
|
||||||
"author": {
|
"authors": [
|
||||||
"url": "github.com/msergo"
|
{
|
||||||
},
|
"name": "Sergii",
|
||||||
|
"url": "https://github.com/msergo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Erki",
|
||||||
|
"url": "https://github.com/veebkolm"
|
||||||
|
}
|
||||||
|
],
|
||||||
"contributors": [],
|
"contributors": [],
|
||||||
"bugs": {},
|
"bugs": {},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -55,9 +62,12 @@
|
|||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"config": "^3.3.9",
|
"config": "^3.3.9",
|
||||||
|
"connect-redis": "^7.1.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"express-session": "^1.17.3",
|
"express-session": "^1.17.3",
|
||||||
"openid-client": "^5.4.3",
|
"ioredis": "^5.3.2",
|
||||||
|
"openid-client": "^5.6.5",
|
||||||
|
"redis": "^4.6.7",
|
||||||
"winston": "^3.10.0"
|
"winston": "^3.10.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -67,6 +77,7 @@
|
|||||||
"@types/express-session": "^1.17.7",
|
"@types/express-session": "^1.17.7",
|
||||||
"@types/mocha": "^10.0.1",
|
"@types/mocha": "^10.0.1",
|
||||||
"@types/node": "^20.4.5",
|
"@types/node": "^20.4.5",
|
||||||
|
"@types/redis": "^4.0.11",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^13.2.3",
|
"lint-staged": "^13.2.3",
|
||||||
|
@ -2,15 +2,16 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>walias</title>
|
<title>WildDuck Aliases</title>
|
||||||
<script src="index.js" async></script>
|
|
||||||
<meta name="description" content="Aliases for Wild Duck">
|
<meta name="description" content="Aliases for Wild Duck">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet"
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
|
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
||||||
integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm"
|
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
|
||||||
|
<script src="index.js" async></script>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -50,11 +51,11 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body >
|
<body>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<img class="logo" src="logo.jpg">
|
<img class="logo" src="logo.jpg">
|
||||||
<h1>Wildduck aliases</h1>
|
<h1>WildDuck Aliases</h1>
|
||||||
</div>
|
</div>
|
||||||
<div id="container"></div>
|
<div id="container"></div>
|
||||||
</div>
|
</div>
|
||||||
|
167
public/index.js
167
public/index.js
@ -1,93 +1,98 @@
|
|||||||
function renderAliases(aliases) {
|
function renderAliases(data) {
|
||||||
const dataContainer = document.getElementById('container')
|
const dataContainer = document.getElementById('container');
|
||||||
// Create table element ant populate with items
|
const template = window.innerWidth > 768 ? Handlebars.compile(defaultTemplate) : Handlebars.compile(responsiveCardTemplate);
|
||||||
const table = document.createElement('table')
|
const html = template({
|
||||||
table.classList.add('table')
|
data: data
|
||||||
const thead = document.createElement('thead')
|
.sort((a, b) => new Date(a.created) - new Date(b.created))
|
||||||
const tr = document.createElement('tr')
|
.map(alias => ({ ...alias, created: new Date(alias.created).toLocaleString() }))
|
||||||
|
});
|
||||||
|
|
||||||
for (const value of ['Address', 'Created']) {
|
dataContainer.innerHTML = html;
|
||||||
const th = document.createElement('th')
|
|
||||||
th.setAttribute('scope', 'col')
|
|
||||||
th.innerText = value
|
|
||||||
tr.appendChild(th)
|
|
||||||
}
|
|
||||||
|
|
||||||
thead.appendChild(tr)
|
|
||||||
table.appendChild(thead)
|
|
||||||
|
|
||||||
const tbody = document.createElement('tbody')
|
|
||||||
// sort by field created
|
|
||||||
aliases
|
|
||||||
.sort((a, b) => new Date(a.created) - new Date(b.created))
|
|
||||||
.forEach(alias => {
|
|
||||||
const tr = document.createElement('tr')
|
|
||||||
const tdAddress = document.createElement('td')
|
|
||||||
// On click copy to clipboard
|
|
||||||
tdAddress.addEventListener('click', () => {
|
|
||||||
navigator.clipboard.writeText(alias.address)
|
|
||||||
})
|
|
||||||
tdAddress.innerText = alias.address
|
|
||||||
const tdCreated = document.createElement('td')
|
|
||||||
tdCreated.innerText = alias.created
|
|
||||||
const tdActions = document.createElement('td')
|
|
||||||
const aDelete = document.createElement('a')
|
|
||||||
|
|
||||||
aDelete.addEventListener('click', async () => {
|
|
||||||
const res = await fetch(`/aliases/${alias.id}`, {
|
|
||||||
method: 'DELETE'
|
|
||||||
})
|
|
||||||
|
|
||||||
if (res.ok) {
|
|
||||||
const data = await res.json()
|
|
||||||
renderAliases(data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
aDelete.classList.add('btn', 'btn-danger')
|
|
||||||
aDelete.innerText = 'Delete'
|
|
||||||
tdActions.appendChild(aDelete)
|
|
||||||
|
|
||||||
tr.appendChild(tdAddress)
|
|
||||||
tr.appendChild(tdCreated)
|
|
||||||
tr.appendChild(tdActions)
|
|
||||||
tbody.appendChild(tr)
|
|
||||||
})
|
|
||||||
table.appendChild(tbody)
|
|
||||||
|
|
||||||
dataContainer.innerHTML = ''
|
|
||||||
dataContainer.appendChild(table)
|
|
||||||
|
|
||||||
renderCreateButton();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCreateButton() {
|
function createAlias() {
|
||||||
const dataContainer = document.getElementById('container')
|
const tags = document.getElementById('tags').value?.split(',') || [];
|
||||||
const button = document.createElement('a')
|
|
||||||
button.classList.add('btn')
|
|
||||||
button.classList.add('btn-primary')
|
|
||||||
|
|
||||||
button.addEventListener('click', async () => {
|
fetch('/aliases', {
|
||||||
button.innerText = 'Creating, please wait...'
|
method: 'POST',
|
||||||
const res = await fetch('/aliases', {
|
body: JSON.stringify({ tags }),
|
||||||
method: 'POST'
|
headers: {
|
||||||
})
|
'Content-Type': 'application/json'
|
||||||
|
|
||||||
if (res.ok) {
|
|
||||||
const data = await res.json()
|
|
||||||
renderAliases(data)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.then(res => res.json())
|
||||||
button.innerText = 'Create new alias'
|
.then(data => {
|
||||||
dataContainer.appendChild(button)
|
renderAliases(data)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteAlias(id) {
|
||||||
|
fetch(`/aliases/${id}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
renderAliases(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyToClipboard(text) {
|
||||||
|
navigator.clipboard.writeText(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultTemplate = `
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Address</th>
|
||||||
|
<th scope="col">Tags</th>
|
||||||
|
<th scope="col">Created</th>
|
||||||
|
<th scope="col">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each data}}
|
||||||
|
<tr>
|
||||||
|
<td onclick="copyToClipboard('{{address}}')" style="cursor: pointer;">{{address}}</td>
|
||||||
|
<td> {{#each tags}}{{this}} {{/each}}</td>
|
||||||
|
<td>{{created}}</td>
|
||||||
|
<td>
|
||||||
|
<div onclick="deleteAlias('{{id}}')" class="btn btn-danger {{#unless id}}disabled{{/unless}}">Delete</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<input type="text" id="tags" placeholder="Add tags (optional)">
|
||||||
|
<div class="btn btn-primary" onclick="createAlias()">Create alias</div class="btn btn-primary">
|
||||||
|
`
|
||||||
|
// Responsive card template for mobile
|
||||||
|
const responsiveCardTemplate = `
|
||||||
|
{{#each data}}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title
|
||||||
|
{{#if tags.length}}
|
||||||
|
text-primary
|
||||||
|
{{/if}}
|
||||||
|
">
|
||||||
|
{{#each tags}}
|
||||||
|
<span class="badge bg-secondary">{{this}}</span>
|
||||||
|
{{/each}}
|
||||||
|
</h5>
|
||||||
|
<h5 class="card-title" onclick="copyToClipboard('{{address}}')" style="cursor: pointer;">{{address}}</h5>
|
||||||
|
<p class="card-text">{{created}}</p>
|
||||||
|
<div onclick="deleteAlias('{{id}}')" class="btn btn-sm btn-danger {{#unless id}}disabled{{/unless}}">Delete</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
<input type="text" id="tags" placeholder="Add tags (optional)">
|
||||||
|
<button class="btn btn-primary btn-sm" onclick="createAlias()">Create alias</button">
|
||||||
|
`
|
||||||
|
|
||||||
fetch('/aliases').then(async res => {
|
fetch('/aliases').then(async res => {
|
||||||
const data = await res.json()
|
const dataContainer = document.getElementById('container');
|
||||||
const dataContainer = document.getElementById('container')
|
const data = await res.json();
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
dataContainer.innerHTML = `
|
dataContainer.innerHTML = `
|
||||||
|
BIN
public/screenshot.png
Normal file
BIN
public/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
25
readme.md
25
readme.md
@ -1,18 +1,19 @@
|
|||||||
# walias
|
# Wildflock
|
||||||
|
|
||||||
> Manage email aliases for Wild Duck mail server
|
Headless service for managing address aliases in [Wild Duck](https://wildduck.email/) mail server.
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
This project was build for [K-Space Hackathon2023](https://wiki.k-space.ee/en/hackathon/2023)
|
This project was built for [K-Space Hackathon 2023](https://wiki.k-space.ee/en/hackathon/2023) and is supposed to live in K-Space infrastructure; however, it can be seamlessly deployed elsewhere.
|
||||||
|
The main motivation behind the development of this headless web UI was to provide users with a convenient tool for creating and deleting email aliases in the Wild Duck mail server.
|
||||||
|
|
||||||
## Features
|
The Wild Duck server has a nice and functional [API](https://docs.wildduck.email/api/), but only offers an admin API token, limiting regular users' ability to manage their own email aliases.
|
||||||
* auth with oidc
|
|
||||||
* headless only, no DB
|
User authentication is implemented via OIDC.
|
||||||
* develop/debug with skaffold on k8s cluster
|
|
||||||
|
No persistent storage, Redis is used for keeping express sessions.
|
||||||
|
|
||||||
|
Deployed on Kubernetes, developed with Skaffold.
|
||||||
|
|
||||||
|
![Screenshot](./public/screenshot.png)
|
||||||
|
|
||||||
## TODO
|
|
||||||
* remove TS-related hacks
|
|
||||||
* add tests
|
|
||||||
* make nicer UI
|
|
||||||
* implement persistent sessions for multi-docker deployment (redis, db, etc)
|
|
28
src/app.ts
28
src/app.ts
@ -1,19 +1,22 @@
|
|||||||
|
import { randomUUID } from 'crypto';
|
||||||
import { feathers } from '@feathersjs/feathers';
|
import { feathers } from '@feathersjs/feathers';
|
||||||
import express, { rest, json, urlencoded, cors, serveStatic, notFound, errorHandler } from '@feathersjs/express';
|
import express, { rest, json, urlencoded, cors, serveStatic, notFound, errorHandler } from '@feathersjs/express';
|
||||||
import configuration from '@feathersjs/configuration';
|
import configuration from '@feathersjs/configuration';
|
||||||
import socketio from '@feathersjs/socketio';
|
import socketio from '@feathersjs/socketio';
|
||||||
import session from 'express-session';
|
import session from 'express-session';
|
||||||
import cookieParser from 'cookie-parser';
|
import cookieParser from 'cookie-parser';
|
||||||
|
import RedisStore from 'connect-redis';
|
||||||
|
import { createClient } from 'redis';
|
||||||
|
import config from 'config';
|
||||||
import type { Application } from './declarations';
|
import type { Application } from './declarations';
|
||||||
|
|
||||||
import { logger } from './logger';
|
import { logger } from './logger';
|
||||||
import { logError } from './hooks/log-error';
|
import { logError } from './hooks/log-error';
|
||||||
import { services } from './services/index';
|
import { services } from './services/index';
|
||||||
import { channels } from './channels';
|
import { channels } from './channels';
|
||||||
import { randomUUID } from 'crypto';
|
import { Env, getEnv } from './helpers/get-env';
|
||||||
|
|
||||||
const app: Application = express(feathers());
|
const app: Application = express(feathers());
|
||||||
|
let sessionStore;
|
||||||
|
|
||||||
// Load app configuration
|
// Load app configuration
|
||||||
app.configure(configuration());
|
app.configure(configuration());
|
||||||
@ -25,11 +28,26 @@ app.use(
|
|||||||
);
|
);
|
||||||
|
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
|
|
||||||
|
if (getEnv() === Env.prod) {
|
||||||
|
const redisClient = createClient({
|
||||||
|
url: config.get('redis.url'),
|
||||||
|
});
|
||||||
|
|
||||||
|
sessionStore = new RedisStore({
|
||||||
|
prefix: 'walias:',
|
||||||
|
client: redisClient,
|
||||||
|
});
|
||||||
|
|
||||||
|
redisClient.connect().catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
session({
|
session({
|
||||||
secret: randomUUID(),
|
store: sessionStore,
|
||||||
|
secret: config.get('sessionSecret') || randomUUID(),
|
||||||
resave: false,
|
resave: false,
|
||||||
saveUninitialized: true,
|
saveUninitialized: false,
|
||||||
cookie: { secure: false },
|
cookie: { secure: false },
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
16
src/helpers/get-env.ts
Normal file
16
src/helpers/get-env.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export enum Env {
|
||||||
|
dev = 'dev',
|
||||||
|
prod = 'prod',
|
||||||
|
test = 'test',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getEnv = (): Env => {
|
||||||
|
const env = process.env.NODE_ENV;
|
||||||
|
if (env === 'prod') {
|
||||||
|
return Env.prod;
|
||||||
|
} else if (env === 'test') {
|
||||||
|
return Env.test;
|
||||||
|
} else {
|
||||||
|
return Env.dev;
|
||||||
|
}
|
||||||
|
};
|
@ -1,10 +1,7 @@
|
|||||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html
|
|
||||||
import { createLogger, format, transports } from 'winston';
|
import { createLogger, format, transports } from 'winston';
|
||||||
|
|
||||||
// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston
|
|
||||||
export const logger = createLogger({
|
export const logger = createLogger({
|
||||||
// To see more detailed errors, change this to 'debug'
|
|
||||||
level: 'info',
|
level: 'info',
|
||||||
format: format.combine(format.splat(), format.simple()),
|
format: format.combine(format.splat(), format.json()),
|
||||||
transports: [new transports.Console()],
|
transports: [new transports.Console()],
|
||||||
});
|
});
|
||||||
|
@ -6,7 +6,7 @@ import { faker } from '@faker-js/faker';
|
|||||||
import { BadRequest } from '@feathersjs/errors';
|
import { BadRequest } from '@feathersjs/errors';
|
||||||
import config from 'config';
|
import config from 'config';
|
||||||
|
|
||||||
interface Alias {
|
interface WildDuckAddress {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
id: string;
|
id: string;
|
||||||
address: string;
|
address: string;
|
||||||
@ -16,21 +16,27 @@ interface Alias {
|
|||||||
created: string;
|
created: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetAddressInfoResponse {
|
interface GetWildDuckAddressInfoResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
results: Alias[];
|
results: WildDuckAddress[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateAddressResponse {
|
interface AliasApiResponse {
|
||||||
|
id: string | null;
|
||||||
|
address: string;
|
||||||
|
tags: string[];
|
||||||
|
created: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateWildDuckAddressResponse {
|
||||||
success: boolean;
|
success: boolean;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
type AliasesData = { tags?: string[] }
|
||||||
type AliasesData = any;
|
|
||||||
type AliasesPatch = any;
|
type AliasesPatch = any;
|
||||||
type AliasesQuery = any;
|
type AliasesQuery = any;
|
||||||
|
|
||||||
export type { Alias as Aliases, AliasesData, AliasesPatch, AliasesQuery };
|
export type { WildDuckAddress as Aliases, AliasesData, AliasesPatch, AliasesQuery };
|
||||||
|
|
||||||
export interface AliasesServiceOptions {
|
export interface AliasesServiceOptions {
|
||||||
app: Application;
|
app: Application;
|
||||||
@ -41,18 +47,18 @@ export interface AliasesParams extends Params<AliasesQuery> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
||||||
implements ServiceInterface<Alias, AliasesData, ServiceParams, AliasesPatch>
|
implements ServiceInterface<AliasApiResponse, AliasesData, ServiceParams, AliasesPatch>
|
||||||
{
|
{
|
||||||
constructor(public options: AliasesServiceOptions) {}
|
constructor(public options: AliasesServiceOptions) { }
|
||||||
|
|
||||||
async find(params: ServiceParams): Promise<Alias[]> {
|
async find(params: ServiceParams): Promise<AliasApiResponse[]> {
|
||||||
const userId = await this.getUserIdByEmailAddress(params);
|
const userId = await this.getUserIdByEmailAddress(params);
|
||||||
|
|
||||||
return this.getUserAddresses(userId);
|
return this.getUserAddresses(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: AliasesData, params: ServiceParams): Promise<Alias>;
|
async create(data: AliasesData, params: ServiceParams): Promise<AliasApiResponse>;
|
||||||
async create(data: AliasesData, params: ServiceParams): Promise<Alias | Alias[]> {
|
async create(data: AliasesData, params: ServiceParams): Promise<AliasApiResponse | AliasApiResponse[]> {
|
||||||
const userId = await this.getUserIdByEmailAddress(params);
|
const userId = await this.getUserIdByEmailAddress(params);
|
||||||
|
|
||||||
const randomString = faker.git.commitSha({ length: 4 });
|
const randomString = faker.git.commitSha({ length: 4 });
|
||||||
@ -65,8 +71,9 @@ export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
|||||||
|
|
||||||
const emailDomain = config.get('wildDuck.domain');
|
const emailDomain = config.get('wildDuck.domain');
|
||||||
|
|
||||||
const createResult = await wildDuckClient.post<CreateAddressResponse>(`/users/${userId}/addresses`, {
|
const createResult = await wildDuckClient.post<CreateWildDuckAddressResponse>(`/users/${userId}/addresses`, {
|
||||||
address: `${alias}@${emailDomain}`,
|
address: `${alias}@${emailDomain}`,
|
||||||
|
tags: data.tags,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!createResult.data.success) {
|
if (!createResult.data.success) {
|
||||||
@ -79,25 +86,31 @@ export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
|||||||
private async getUserIdByEmailAddress(params: ServiceParams): Promise<string> {
|
private async getUserIdByEmailAddress(params: ServiceParams): Promise<string> {
|
||||||
const emails = params.session?.user?.emails;
|
const emails = params.session?.user?.emails;
|
||||||
|
|
||||||
|
const preferredDomain = config.get('wildDuck.preferredDomain');
|
||||||
|
|
||||||
|
if (!emails.length || !preferredDomain) {
|
||||||
|
throw new BadRequest('Unable to find user');
|
||||||
|
}
|
||||||
|
|
||||||
const addressInfoResponse = await Promise.any(
|
const addressInfoResponse = await Promise.any(
|
||||||
emails
|
emails
|
||||||
.filter((email: string) => email.endsWith(config.get('wildDuck.preferredDomain')))
|
.filter((email: string) => email.endsWith(config.get('wildDuck.preferredDomain')))
|
||||||
.map((email: string) => wildDuckClient.get<Alias>(`addresses/resolve/${email}`))
|
.map((email: string) => wildDuckClient.get<WildDuckAddress>(`addresses/resolve/${email}`))
|
||||||
);
|
);
|
||||||
|
|
||||||
return addressInfoResponse.data.user;
|
return addressInfoResponse.data.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getUserAddresses(userId: string): Promise<Alias[]> {
|
private async getUserAddresses(userId: string): Promise<AliasApiResponse[]> {
|
||||||
const { data: userAddressesResponse } = await wildDuckClient.get<GetAddressInfoResponse>(
|
const { data: userAddressesResponse } = await wildDuckClient.get<GetWildDuckAddressInfoResponse>(
|
||||||
`/users/${userId}/addresses`
|
`/users/${userId}/addresses`
|
||||||
);
|
);
|
||||||
|
|
||||||
return userAddressesResponse.results;
|
return userAddressesResponse.results.map(this.sanitizeAliasResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(id: NullableId, params: ServiceParams): Promise<Alias[]> {
|
async remove(id: NullableId, params: ServiceParams): Promise<AliasApiResponse[]> {
|
||||||
const { data: addressInfoResponse } = await wildDuckClient.get<Alias>(`addresses/resolve/${id}`);
|
const { data: addressInfoResponse } = await wildDuckClient.get<WildDuckAddress>(`addresses/resolve/${id}`);
|
||||||
const allowedDomain: string = config.get('wildDuck.domain');
|
const allowedDomain: string = config.get('wildDuck.domain');
|
||||||
|
|
||||||
// If address does not match the allowed domain, throw an error
|
// If address does not match the allowed domain, throw an error
|
||||||
@ -106,10 +119,23 @@ export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
|||||||
}
|
}
|
||||||
const userId = await this.getUserIdByEmailAddress(params);
|
const userId = await this.getUserIdByEmailAddress(params);
|
||||||
|
|
||||||
await wildDuckClient.delete<Alias>(`users/${userId}/addresses/${id}`);
|
await wildDuckClient.delete<WildDuckAddress>(`users/${userId}/addresses/${id}`);
|
||||||
|
|
||||||
return this.getUserAddresses(userId);
|
return this.getUserAddresses(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sanitizeAliasResponse(alias: WildDuckAddress): AliasApiResponse {
|
||||||
|
// Prevent the user from deleting their main address or any address that does not end with the preferred domain
|
||||||
|
const isRemovable = !alias.main && alias.address.endsWith(config.get('wildDuck.domain'));
|
||||||
|
|
||||||
|
// Hide the id if the alias is not removable
|
||||||
|
return {
|
||||||
|
id: isRemovable ? alias.id : null,
|
||||||
|
address: alias.address,
|
||||||
|
tags: alias.tags,
|
||||||
|
created: alias.created,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getOptions = (app: Application) => {
|
export const getOptions = (app: Application) => {
|
||||||
|
@ -29,17 +29,19 @@ export class AuthOidcService<ServiceParams extends AuthOidcParams = AuthOidcPara
|
|||||||
client_id: config.get('oidc.clientId'),
|
client_id: config.get('oidc.clientId'),
|
||||||
client_secret: config.get('oidc.clientSecret'),
|
client_secret: config.get('oidc.clientSecret'),
|
||||||
redirect_uris: [config.get('oidc.redirectUris')],
|
redirect_uris: [config.get('oidc.redirectUris')],
|
||||||
response_types: ['code'],
|
response_types: [config.get('oidc.responseTypes')],
|
||||||
|
id_token_signed_response_alg: config.get('oidc.signedResponseAlg'),
|
||||||
|
token_endpoint_auth_method: config.get('oidc.authMethod'),
|
||||||
});
|
});
|
||||||
const codeVerifier = generators.codeVerifier();
|
const codeVerifier = generators.codeVerifier();
|
||||||
const codeChallenge = generators.codeChallenge(codeVerifier);
|
const codeChallenge = generators.codeChallenge(codeVerifier);
|
||||||
|
|
||||||
const url = client.authorizationUrl({
|
const url = client.authorizationUrl({
|
||||||
redirect_uri: config.get('clientUrl') + '/auth-oidc/callback',
|
redirect_uri: config.get('clientUrl') + '/auth-oidc/callback',
|
||||||
scope: 'openid profile offline_access',
|
scope: config.get('oidc.scopes'),
|
||||||
response_type: 'code',
|
response_type: config.get('oidc.responseTypes'),
|
||||||
code_challenge: codeChallenge,
|
code_challenge: codeChallenge,
|
||||||
code_challenge_method: 'S256',
|
code_challenge_method: config.get('oidc.codeChallengeMethod'),
|
||||||
});
|
});
|
||||||
|
|
||||||
params.session.codeVerifier = codeVerifier;
|
params.session.codeVerifier = codeVerifier;
|
||||||
|
Loading…
Reference in New Issue
Block a user