Compare commits
No commits in common. "master" and "dev-prettier" have entirely different histories.
master
...
dev-pretti
@ -8,4 +8,3 @@ deployment.yaml
|
||||
Dockerfile
|
||||
readme.md
|
||||
.git
|
||||
lib
|
35
.drone.yml
Normal file
35
.drone.yml
Normal file
@ -0,0 +1,35 @@
|
||||
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
|
13
.woodpecker.yml
Normal file
13
.woodpecker.yml
Normal file
@ -0,0 +1,13 @@
|
||||
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
|
@ -1,26 +0,0 @@
|
||||
---
|
||||
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
|
@ -1,31 +0,0 @@
|
||||
---
|
||||
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 mirror.gcr.io/library/node:18-alpine as dev
|
||||
FROM node:18-alpine as dev
|
||||
RUN apk add netcat-openbsd
|
||||
|
||||
RUN npm config set update-notifier false
|
||||
@ -12,7 +12,7 @@ RUN npm run compile
|
||||
|
||||
ENTRYPOINT npm run start
|
||||
|
||||
FROM mirror.gcr.io/node:18-alpine AS prod
|
||||
FROM node:18-alpine AS prod
|
||||
RUN npm config set update-notifier false
|
||||
WORKDIR /app
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
"origins": [
|
||||
"http://localhost:3030"
|
||||
],
|
||||
"sessionSecret": "test",
|
||||
"paginate": {
|
||||
"default": 10,
|
||||
"max": 50
|
||||
@ -15,8 +14,5 @@
|
||||
"token": "aaaaa",
|
||||
"domain": "test-codemowers.eu",
|
||||
"preferredDomain": "k-space.ee"
|
||||
},
|
||||
"redis": {
|
||||
"url": "redis://localhost:6379"
|
||||
}
|
||||
}
|
@ -1,27 +1,14 @@
|
||||
module.exports = {
|
||||
clientUrl: process.env.CLIENT_URL,
|
||||
sessionSecret: process.env.SESSION_SECRET,
|
||||
oidc: {
|
||||
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,
|
||||
clientSecret: process.env.OIDC_CLIENT_SECRET,
|
||||
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,
|
||||
redirectUris: process.env.OIDC_REDIRECT_URIS
|
||||
},
|
||||
wildDuck: {
|
||||
url: process.env.WILDDUCK_URL,
|
||||
token: process.env.WILDDUCK_TOKEN,
|
||||
domain: process.env.WILDDUCK_DOMAIN,
|
||||
},
|
||||
redis: {
|
||||
url: process.env.REDIS_URL,
|
||||
},
|
||||
domain: process.env.WILDDUCK_DOMAIN
|
||||
}
|
||||
};
|
@ -98,11 +98,6 @@ spec:
|
||||
secretKeyRef:
|
||||
name: walias-secrets
|
||||
key: WILDDUCK_DOMAIN
|
||||
- name: REDIS_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: redis-walias-cache-owner-secrets
|
||||
key: REDIS_MASTER_URI
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: oidc-client-walias-owner-secrets
|
||||
|
334
package-lock.json
generated
334
package-lock.json
generated
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "wildflock",
|
||||
"name": "walias",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wildflock",
|
||||
"name": "walias",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
@ -22,12 +22,9 @@
|
||||
"axios": "^1.4.0",
|
||||
"compression": "^1.7.4",
|
||||
"config": "^3.3.9",
|
||||
"connect-redis": "^7.1.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"express-session": "^1.17.3",
|
||||
"ioredis": "^5.3.2",
|
||||
"openid-client": "^5.6.5",
|
||||
"redis": "^4.6.7",
|
||||
"openid-client": "^5.4.3",
|
||||
"winston": "^3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -37,7 +34,6 @@
|
||||
"@types/express-session": "^1.17.7",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/redis": "^4.0.11",
|
||||
"cross-env": "^7.0.3",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.2.3",
|
||||
@ -417,11 +413,6 @@
|
||||
"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": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||
@ -447,59 +438,6 @@
|
||||
"@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": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
@ -672,16 +610,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
|
||||
"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": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
|
||||
@ -939,11 +867,11 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
|
||||
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
|
||||
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.4",
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
@ -1003,12 +931,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
@ -1016,7 +944,7 @@
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.2",
|
||||
"raw-body": "2.5.1",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
@ -1043,12 +971,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
"fill-range": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@ -1301,14 +1229,6 @@
|
||||
"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": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||
@ -1420,17 +1340,6 @@
|
||||
"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": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
@ -1470,9 +1379,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@ -1592,14 +1501,6 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
@ -1665,9 +1566,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io": {
|
||||
"version": "6.5.5",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
|
||||
"integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.1.tgz",
|
||||
"integrity": "sha512-mGqhI+D7YxS9KJMppR6Iuo37Ed3abhU8NdfgSvJSDUafQutrN+sPTncJYTyM9+tkhSmWodKtVYGPPHyXJEwEQA==",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.4.1",
|
||||
"@types/cors": "^2.8.12",
|
||||
@ -1677,17 +1578,17 @@
|
||||
"cookie": "~0.4.1",
|
||||
"cors": "~2.8.5",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1"
|
||||
"engine.io-parser": "~5.1.0",
|
||||
"ws": "~8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.2.0"
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz",
|
||||
"integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
@ -1823,16 +1724,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.2",
|
||||
"body-parser": "1.20.1",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie": "0.5.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@ -1967,9 +1868,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@ -2026,9 +1927,9 @@
|
||||
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@ -2098,14 +1999,6 @@
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"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": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
@ -2381,50 +2274,6 @@
|
||||
"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": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
@ -2549,9 +2398,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "4.15.5",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.5.tgz",
|
||||
"integrity": "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==",
|
||||
"version": "4.14.4",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz",
|
||||
"integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
@ -2801,16 +2650,6 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"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": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
|
||||
@ -3393,11 +3232,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/openid-client": {
|
||||
"version": "5.6.5",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz",
|
||||
"integrity": "sha512-5P4qO9nGJzB5PI0LFlhj4Dzg3m4odt0qsJTfyEtZyOlkgpILwEioOhVVJOrS1iVH494S4Ee5OCjjg6Bf5WOj3w==",
|
||||
"version": "5.4.3",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.3.tgz",
|
||||
"integrity": "sha512-sVQOvjsT/sbSfYsQI/9liWQGVZH/Pp3rrtlGEwgk/bbHfrUDZ24DN57lAagIwFtuEu+FM9Ev7r85s8S/yPjimQ==",
|
||||
"dependencies": {
|
||||
"jose": "^4.15.5",
|
||||
"jose": "^4.14.4",
|
||||
"lru-cache": "^6.0.0",
|
||||
"object-hash": "^2.2.0",
|
||||
"oidc-token-hash": "^5.0.3"
|
||||
@ -3639,9 +3478,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
@ -3697,38 +3536,6 @@
|
||||
"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": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
@ -4044,35 +3851,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-adapter": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
|
||||
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
|
||||
"integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
|
||||
"dependencies": {
|
||||
"debug": "~4.3.4",
|
||||
"ws": "~8.17.1"
|
||||
"ws": "~8.11.0"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
@ -4135,11 +3920,6 @@
|
||||
"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": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
@ -4577,15 +4357,15 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
|
19
package.json
19
package.json
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "wildflock",
|
||||
"name": "walias",
|
||||
"description": "Aliases for Wild Duck",
|
||||
"version": "1.0.0",
|
||||
"homepage": "",
|
||||
@ -7,16 +7,9 @@
|
||||
"keywords": [
|
||||
"feathers"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sergii",
|
||||
"url": "https://github.com/msergo"
|
||||
"author": {
|
||||
"url": "github.com/msergo"
|
||||
},
|
||||
{
|
||||
"name": "Erki",
|
||||
"url": "https://github.com/veebkolm"
|
||||
}
|
||||
],
|
||||
"contributors": [],
|
||||
"bugs": {},
|
||||
"engines": {
|
||||
@ -62,12 +55,9 @@
|
||||
"axios": "^1.4.0",
|
||||
"compression": "^1.7.4",
|
||||
"config": "^3.3.9",
|
||||
"connect-redis": "^7.1.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"express-session": "^1.17.3",
|
||||
"ioredis": "^5.3.2",
|
||||
"openid-client": "^5.6.5",
|
||||
"redis": "^4.6.7",
|
||||
"openid-client": "^5.4.3",
|
||||
"winston": "^3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -77,7 +67,6 @@
|
||||
"@types/express-session": "^1.17.7",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node": "^20.4.5",
|
||||
"@types/redis": "^4.0.11",
|
||||
"cross-env": "^7.0.3",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.2.3",
|
||||
|
@ -2,16 +2,15 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>WildDuck Aliases</title>
|
||||
<title>walias</title>
|
||||
<script src="index.js" async></script>
|
||||
<meta name="description" content="Aliases for Wild Duck">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"
|
||||
integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm"
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.js"></script>
|
||||
<script src="index.js" async></script>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
@ -55,7 +54,7 @@
|
||||
<div class="wrapper">
|
||||
<div class="header">
|
||||
<img class="logo" src="logo.jpg">
|
||||
<h1>WildDuck Aliases</h1>
|
||||
<h1>Wildduck aliases</h1>
|
||||
</div>
|
||||
<div id="container"></div>
|
||||
</div>
|
||||
|
161
public/index.js
161
public/index.js
@ -1,98 +1,93 @@
|
||||
function renderAliases(data) {
|
||||
const dataContainer = document.getElementById('container');
|
||||
const template = window.innerWidth > 768 ? Handlebars.compile(defaultTemplate) : Handlebars.compile(responsiveCardTemplate);
|
||||
const html = template({
|
||||
data: data
|
||||
function renderAliases(aliases) {
|
||||
const dataContainer = document.getElementById('container')
|
||||
// Create table element ant populate with items
|
||||
const table = document.createElement('table')
|
||||
table.classList.add('table')
|
||||
const thead = document.createElement('thead')
|
||||
const tr = document.createElement('tr')
|
||||
|
||||
for (const value of ['Address', 'Created']) {
|
||||
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))
|
||||
.map(alias => ({ ...alias, created: new Date(alias.created).toLocaleString() }))
|
||||
});
|
||||
|
||||
dataContainer.innerHTML = html;
|
||||
}
|
||||
|
||||
function createAlias() {
|
||||
const tags = document.getElementById('tags').value?.split(',') || [];
|
||||
|
||||
fetch('/aliases', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ tags }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
.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)
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
renderAliases(data)
|
||||
})
|
||||
}
|
||||
tdAddress.innerText = alias.address
|
||||
const tdCreated = document.createElement('td')
|
||||
tdCreated.innerText = alias.created
|
||||
const tdActions = document.createElement('td')
|
||||
const aDelete = document.createElement('a')
|
||||
|
||||
function deleteAlias(id) {
|
||||
fetch(`/aliases/${id}`, {
|
||||
aDelete.addEventListener('click', async () => {
|
||||
const res = await fetch(`/aliases/${alias.id}`, {
|
||||
method: 'DELETE'
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
|
||||
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 copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text)
|
||||
}
|
||||
function renderCreateButton() {
|
||||
const dataContainer = document.getElementById('container')
|
||||
const button = document.createElement('a')
|
||||
button.classList.add('btn')
|
||||
button.classList.add('btn-primary')
|
||||
|
||||
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">
|
||||
`
|
||||
button.addEventListener('click', async () => {
|
||||
button.innerText = 'Creating, please wait...'
|
||||
const res = await fetch('/aliases', {
|
||||
method: 'POST'
|
||||
})
|
||||
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
renderAliases(data)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
button.innerText = 'Create new alias'
|
||||
dataContainer.appendChild(button)
|
||||
}
|
||||
|
||||
fetch('/aliases').then(async res => {
|
||||
const dataContainer = document.getElementById('container');
|
||||
const data = await res.json();
|
||||
const data = await res.json()
|
||||
const dataContainer = document.getElementById('container')
|
||||
|
||||
if (!res.ok) {
|
||||
dataContainer.innerHTML = `
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 108 KiB |
25
readme.md
25
readme.md
@ -1,19 +1,18 @@
|
||||
# Wildflock
|
||||
# walias
|
||||
|
||||
Headless service for managing address aliases in [Wild Duck](https://wildduck.email/) mail server.
|
||||
> Manage email aliases for Wild Duck mail server
|
||||
|
||||
## About
|
||||
|
||||
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.
|
||||
This project was build for [K-Space Hackathon2023](https://wiki.k-space.ee/en/hackathon/2023)
|
||||
|
||||
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.
|
||||
|
||||
User authentication is implemented via OIDC.
|
||||
|
||||
No persistent storage, Redis is used for keeping express sessions.
|
||||
|
||||
Deployed on Kubernetes, developed with Skaffold.
|
||||
|
||||
![Screenshot](./public/screenshot.png)
|
||||
## Features
|
||||
* auth with oidc
|
||||
* headless only, no DB
|
||||
* develop/debug with skaffold on k8s cluster
|
||||
|
||||
## 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,22 +1,19 @@
|
||||
import { randomUUID } from 'crypto';
|
||||
import { feathers } from '@feathersjs/feathers';
|
||||
import express, { rest, json, urlencoded, cors, serveStatic, notFound, errorHandler } from '@feathersjs/express';
|
||||
import configuration from '@feathersjs/configuration';
|
||||
import socketio from '@feathersjs/socketio';
|
||||
import session from 'express-session';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import RedisStore from 'connect-redis';
|
||||
import { createClient } from 'redis';
|
||||
import config from 'config';
|
||||
|
||||
import type { Application } from './declarations';
|
||||
|
||||
import { logger } from './logger';
|
||||
import { logError } from './hooks/log-error';
|
||||
import { services } from './services/index';
|
||||
import { channels } from './channels';
|
||||
import { Env, getEnv } from './helpers/get-env';
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
const app: Application = express(feathers());
|
||||
let sessionStore;
|
||||
|
||||
// Load app configuration
|
||||
app.configure(configuration());
|
||||
@ -28,26 +25,11 @@ app.use(
|
||||
);
|
||||
|
||||
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(
|
||||
session({
|
||||
store: sessionStore,
|
||||
secret: config.get('sessionSecret') || randomUUID(),
|
||||
secret: randomUUID(),
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
saveUninitialized: true,
|
||||
cookie: { secure: false },
|
||||
})
|
||||
);
|
||||
|
@ -1,16 +0,0 @@
|
||||
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,7 +1,10 @@
|
||||
// For more information about this file see https://dove.feathersjs.com/guides/cli/logging.html
|
||||
import { createLogger, format, transports } from 'winston';
|
||||
|
||||
// Configure the Winston logger. For the complete documentation see https://github.com/winstonjs/winston
|
||||
export const logger = createLogger({
|
||||
// To see more detailed errors, change this to 'debug'
|
||||
level: 'info',
|
||||
format: format.combine(format.splat(), format.json()),
|
||||
format: format.combine(format.splat(), format.simple()),
|
||||
transports: [new transports.Console()],
|
||||
});
|
||||
|
@ -6,7 +6,7 @@ import { faker } from '@faker-js/faker';
|
||||
import { BadRequest } from '@feathersjs/errors';
|
||||
import config from 'config';
|
||||
|
||||
interface WildDuckAddress {
|
||||
interface Alias {
|
||||
success: boolean;
|
||||
id: string;
|
||||
address: string;
|
||||
@ -16,27 +16,21 @@ interface WildDuckAddress {
|
||||
created: string;
|
||||
}
|
||||
|
||||
interface GetWildDuckAddressInfoResponse {
|
||||
interface GetAddressInfoResponse {
|
||||
success: boolean;
|
||||
results: WildDuckAddress[];
|
||||
results: Alias[];
|
||||
}
|
||||
|
||||
interface AliasApiResponse {
|
||||
id: string | null;
|
||||
address: string;
|
||||
tags: string[];
|
||||
created: string;
|
||||
}
|
||||
|
||||
interface CreateWildDuckAddressResponse {
|
||||
interface CreateAddressResponse {
|
||||
success: boolean;
|
||||
id: string;
|
||||
}
|
||||
type AliasesData = { tags?: string[] }
|
||||
|
||||
type AliasesData = any;
|
||||
type AliasesPatch = any;
|
||||
type AliasesQuery = any;
|
||||
|
||||
export type { WildDuckAddress as Aliases, AliasesData, AliasesPatch, AliasesQuery };
|
||||
export type { Alias as Aliases, AliasesData, AliasesPatch, AliasesQuery };
|
||||
|
||||
export interface AliasesServiceOptions {
|
||||
app: Application;
|
||||
@ -47,18 +41,18 @@ export interface AliasesParams extends Params<AliasesQuery> {
|
||||
}
|
||||
|
||||
export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
||||
implements ServiceInterface<AliasApiResponse, AliasesData, ServiceParams, AliasesPatch>
|
||||
implements ServiceInterface<Alias, AliasesData, ServiceParams, AliasesPatch>
|
||||
{
|
||||
constructor(public options: AliasesServiceOptions) {}
|
||||
|
||||
async find(params: ServiceParams): Promise<AliasApiResponse[]> {
|
||||
async find(params: ServiceParams): Promise<Alias[]> {
|
||||
const userId = await this.getUserIdByEmailAddress(params);
|
||||
|
||||
return this.getUserAddresses(userId);
|
||||
}
|
||||
|
||||
async create(data: AliasesData, params: ServiceParams): Promise<AliasApiResponse>;
|
||||
async create(data: AliasesData, params: ServiceParams): Promise<AliasApiResponse | AliasApiResponse[]> {
|
||||
async create(data: AliasesData, params: ServiceParams): Promise<Alias>;
|
||||
async create(data: AliasesData, params: ServiceParams): Promise<Alias | Alias[]> {
|
||||
const userId = await this.getUserIdByEmailAddress(params);
|
||||
|
||||
const randomString = faker.git.commitSha({ length: 4 });
|
||||
@ -71,9 +65,8 @@ export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
||||
|
||||
const emailDomain = config.get('wildDuck.domain');
|
||||
|
||||
const createResult = await wildDuckClient.post<CreateWildDuckAddressResponse>(`/users/${userId}/addresses`, {
|
||||
const createResult = await wildDuckClient.post<CreateAddressResponse>(`/users/${userId}/addresses`, {
|
||||
address: `${alias}@${emailDomain}`,
|
||||
tags: data.tags,
|
||||
});
|
||||
|
||||
if (!createResult.data.success) {
|
||||
@ -86,31 +79,25 @@ export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
||||
private async getUserIdByEmailAddress(params: ServiceParams): Promise<string> {
|
||||
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(
|
||||
emails
|
||||
.filter((email: string) => email.endsWith(config.get('wildDuck.preferredDomain')))
|
||||
.map((email: string) => wildDuckClient.get<WildDuckAddress>(`addresses/resolve/${email}`))
|
||||
.map((email: string) => wildDuckClient.get<Alias>(`addresses/resolve/${email}`))
|
||||
);
|
||||
|
||||
return addressInfoResponse.data.user;
|
||||
}
|
||||
|
||||
private async getUserAddresses(userId: string): Promise<AliasApiResponse[]> {
|
||||
const { data: userAddressesResponse } = await wildDuckClient.get<GetWildDuckAddressInfoResponse>(
|
||||
private async getUserAddresses(userId: string): Promise<Alias[]> {
|
||||
const { data: userAddressesResponse } = await wildDuckClient.get<GetAddressInfoResponse>(
|
||||
`/users/${userId}/addresses`
|
||||
);
|
||||
|
||||
return userAddressesResponse.results.map(this.sanitizeAliasResponse);
|
||||
return userAddressesResponse.results;
|
||||
}
|
||||
|
||||
async remove(id: NullableId, params: ServiceParams): Promise<AliasApiResponse[]> {
|
||||
const { data: addressInfoResponse } = await wildDuckClient.get<WildDuckAddress>(`addresses/resolve/${id}`);
|
||||
async remove(id: NullableId, params: ServiceParams): Promise<Alias[]> {
|
||||
const { data: addressInfoResponse } = await wildDuckClient.get<Alias>(`addresses/resolve/${id}`);
|
||||
const allowedDomain: string = config.get('wildDuck.domain');
|
||||
|
||||
// If address does not match the allowed domain, throw an error
|
||||
@ -119,23 +106,10 @@ export class AliasesService<ServiceParams extends AliasesParams = AliasesParams>
|
||||
}
|
||||
const userId = await this.getUserIdByEmailAddress(params);
|
||||
|
||||
await wildDuckClient.delete<WildDuckAddress>(`users/${userId}/addresses/${id}`);
|
||||
await wildDuckClient.delete<Alias>(`users/${userId}/addresses/${id}`);
|
||||
|
||||
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) => {
|
||||
|
@ -29,19 +29,17 @@ export class AuthOidcService<ServiceParams extends AuthOidcParams = AuthOidcPara
|
||||
client_id: config.get('oidc.clientId'),
|
||||
client_secret: config.get('oidc.clientSecret'),
|
||||
redirect_uris: [config.get('oidc.redirectUris')],
|
||||
response_types: [config.get('oidc.responseTypes')],
|
||||
id_token_signed_response_alg: config.get('oidc.signedResponseAlg'),
|
||||
token_endpoint_auth_method: config.get('oidc.authMethod'),
|
||||
response_types: ['code'],
|
||||
});
|
||||
const codeVerifier = generators.codeVerifier();
|
||||
const codeChallenge = generators.codeChallenge(codeVerifier);
|
||||
|
||||
const url = client.authorizationUrl({
|
||||
redirect_uri: config.get('clientUrl') + '/auth-oidc/callback',
|
||||
scope: config.get('oidc.scopes'),
|
||||
response_type: config.get('oidc.responseTypes'),
|
||||
scope: 'openid profile offline_access',
|
||||
response_type: 'code',
|
||||
code_challenge: codeChallenge,
|
||||
code_challenge_method: config.get('oidc.codeChallengeMethod'),
|
||||
code_challenge_method: 'S256',
|
||||
});
|
||||
|
||||
params.session.codeVerifier = codeVerifier;
|
||||
|
Loading…
Reference in New Issue
Block a user