Compare commits
11 Commits
v1.0.0
...
fee1475779
Author | SHA1 | Date | |
---|---|---|---|
fee1475779
|
|||
a3fb47a329
|
|||
2409fa2b8d
|
|||
4d689a86ab
|
|||
d723d4264a
|
|||
a3751c985b
|
|||
5dd20cd6a0
|
|||
8ed804a922
|
|||
02a352a122
|
|||
e8e864bdfc
|
|||
2eddb77b74
|
@ -1,6 +1,9 @@
|
|||||||
HASHTAG_FILTER = ichlausche,music,musik,nowplaying,tunetuesday
|
HASHTAG_FILTER = ichlausche,music,musik,nowplaying,tunetuesday,nowlistening
|
||||||
URL_FILTER = song.link,album.link,spotify.com,music.apple.com,bandcamp.com
|
URL_FILTER = song.link,album.link,spotify.com,music.apple.com,bandcamp.com
|
||||||
YOUTUBE_API_KEY = CHANGE_ME
|
YOUTUBE_API_KEY = CHANGE_ME
|
||||||
|
MASTODON_INSTANCE = 'metalhead.club'
|
||||||
|
BASE_URL = 'https://moshingmammut.phlaym.net'
|
||||||
VERBOSE = false
|
VERBOSE = false
|
||||||
|
|
||||||
PUBLIC_REFRESH_INTERVAL = 10000
|
PUBLIC_REFRESH_INTERVAL = 10000
|
||||||
|
PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME = 'Metalhead.club'
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
*.db
|
*.db
|
||||||
|
feed.xml
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
/build
|
/build
|
||||||
|
46
README.md
46
README.md
@ -46,32 +46,50 @@ This might not be the ideal setup to run this, but here's how I am doing it. Ins
|
|||||||
or Debian derivative, using Apache as HTTP Proxy. Other setups are possible, but not covered here.
|
or Debian derivative, using Apache as HTTP Proxy. Other setups are possible, but not covered here.
|
||||||
By default, NVM is used to install NodeJS, but you can install it any way you want.
|
By default, NVM is used to install NodeJS, but you can install it any way you want.
|
||||||
|
|
||||||
This is based on [SvelteKit's instructions](https://kit.svelte.dev/docs/adapter-node#deploying)
|
This is based on [SvelteKit's instructions](https://kit.svelte.dev/docs/adapter-node#deploying) and [How To Deploy Node.js Applications Using Systemd and Nginx](https://www.digitalocean.com/community/tutorials/how-to-deploy-node-js-applications-using-systemd-and-nginx)
|
||||||
|
|
||||||
On your server, install the requirements:
|
|
||||||
- Apache2 HTTP Server
|
|
||||||
- NodeJS (via [NVM](https://github.com/nvm-sh/nvm))
|
|
||||||
|
|
||||||
#### On your server
|
#### On your server
|
||||||
|
|
||||||
Create a directory for the app. This will be called `$APP_DIR` from now on.
|
Install Apache2 if not already installed.
|
||||||
|
|
||||||
Place `package.json`, `apache2.conf.EXAMPLE`, `moshing-mammut.service.EXAMPLE` and `start.sh.EXAMPLE` in this directory.
|
Copy `apache2.conf.EXAMPLE` and `moshing-mammut.service.EXAMPLE` to your server.
|
||||||
|
|
||||||
|
Set up a user for the app: `useradd -mrU moshing-mammut`
|
||||||
|
|
||||||
|
Switch to your newly created user: `su moshing-mammut`
|
||||||
|
|
||||||
|
Set up NVM:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd
|
||||||
|
$ curl https://raw.github.com/creationix/nvm/master/install.sh | sh
|
||||||
|
$ source ~/.nvm/nvm.sh
|
||||||
|
$ nvm install --lts
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a directory for the app. This will be called `$APP_DIR` from now on. I use `/home/moshing-mammut/app`.
|
||||||
|
|
||||||
|
Enter `$APP_DIR`.
|
||||||
|
|
||||||
|
Place `package-lock.json` and `start.sh.EXAMPLE` in this directory.
|
||||||
|
Run `npm ci --omit dev` to install the dependencies.
|
||||||
|
|
||||||
|
|
||||||
|
Rename `start.sh.EXAMPLE` to `start.sh` and set the path to your NVM.
|
||||||
|
|
||||||
|
If you do not have NVM installed, simply remove the line and make sure your node executable can be found either by
|
||||||
|
specifying the full path or by adding it to your $PATH.
|
||||||
|
|
||||||
|
Exit out of your `moshing-mammut` user shell.
|
||||||
|
|
||||||
Copy `apache2.conf.EXAMPLE` to `/etc/apache2/sites-available/moshingmammut.conf` and replace `ServerName` with your
|
Copy `apache2.conf.EXAMPLE` to `/etc/apache2/sites-available/moshingmammut.conf` and replace `ServerName` with your
|
||||||
Domain. If you do not need or want SSL support, remove the whole `<IfModule mod_ssl.c>` block.
|
Domain. If you do not need or want SSL support, remove the whole `<IfModule mod_ssl.c>` block.
|
||||||
If you do, add the path to your SSLCertificateFile and SSLCertificateKeyFile.
|
If you do, add the path to your SSLCertificateFile and SSLCertificateKeyFile.
|
||||||
|
|
||||||
Copy `moshing-mammut.service.EXAMPLE` to `/etc/systemd/system/moshing-mammut.service`
|
Copy `moshing-mammut.service.EXAMPLE` to `/etc/systemd/system/moshing-mammut.service`
|
||||||
and replace `/PATH_TO_MOSHING_MAMMUT` with your `$APP_DIR`. Also replace `MOSHING_MAMMUT_USER` with the user you want
|
and set your `User`, `Group`, `ExecStart` and `WorkingDirectory` accordingly.
|
||||||
to run the app as.
|
|
||||||
|
|
||||||
Rename `start.sh.EXAMPLE` to `start.sh` and replace `/PATH_TO_YOUR_NVM/.nvm/nvm.sh` with the path to your NVM
|
|
||||||
installation.
|
|
||||||
If you do not have NVM installed, simply remove the line and make sure your node executable can be found either by
|
|
||||||
specifying the full path or by adding it to your $PATH.
|
|
||||||
|
|
||||||
Run `npm ci --omit dev` to install the dependencies.
|
|
||||||
|
|
||||||
#### On your development machine
|
#### On your development machine
|
||||||
|
|
||||||
|
@ -2,14 +2,13 @@
|
|||||||
Description=Moshing Mammut
|
Description=Moshing Mammut
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/PATH_TO_MOSHING_MAMMUT/start.sh
|
ExecStart=/home/moshing-mammut/app/start.sh
|
||||||
Restart=always
|
Restart=always
|
||||||
User=MOSHING_MAMMUT_USER
|
User=moshing-mammut
|
||||||
Group=nogroup
|
Group=moshing-mammut
|
||||||
Environment=PATH=/usr/bin:/usr/local/bin
|
Environment=PATH=/usr/bin:/usr/local/bin
|
||||||
Environment=NODE_ENV=production
|
Environment=NODE_ENV=production
|
||||||
WorkingDirectory=/PATH_TO_MOSHING_MAMMUT/
|
WorkingDirectory=/home/moshing-mammut/app
|
||||||
KillMode=process
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
349
package-lock.json
generated
349
package-lock.json
generated
@ -8,15 +8,17 @@
|
|||||||
"name": "moshing-mammut",
|
"name": "moshing-mammut",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/sqlite3": "^3.1.8",
|
|
||||||
"@types/ws": "^8.5.4",
|
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
|
"feed": "^4.2.2",
|
||||||
|
"feeder": "git+ssh://git@phlaym.net:phlaym/feeder.git",
|
||||||
"sqlite3": "^5.1.6",
|
"sqlite3": "^5.1.6",
|
||||||
"ws": "^8.13.0"
|
"ws": "^8.13.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-node": "^1.2.3",
|
"@sveltejs/adapter-node": "^1.2.3",
|
||||||
"@sveltejs/kit": "^1.5.0",
|
"@sveltejs/kit": "^1.5.0",
|
||||||
|
"@types/sqlite3": "^3.1.8",
|
||||||
|
"@types/ws": "^8.5.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.28.0",
|
||||||
@ -31,6 +33,102 @@
|
|||||||
"vite": "^4.0.0"
|
"vite": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/code-frame": {
|
||||||
|
"version": "7.21.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
|
||||||
|
"integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/highlight": "^7.18.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.19.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
|
||||||
|
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight": {
|
||||||
|
"version": "7.18.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
|
||||||
|
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.18.6",
|
||||||
|
"chalk": "^2.0.0",
|
||||||
|
"js-tokens": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/highlight/node_modules/supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.17.14",
|
"version": "0.17.14",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.14.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.14.tgz",
|
||||||
@ -841,7 +939,8 @@
|
|||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.15.11",
|
"version": "18.15.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
|
||||||
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q=="
|
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/pug": {
|
"node_modules/@types/pug": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
@ -865,6 +964,7 @@
|
|||||||
"version": "3.1.8",
|
"version": "3.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/sqlite3/-/sqlite3-3.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/@types/sqlite3/-/sqlite3-3.1.8.tgz",
|
||||||
"integrity": "sha512-sQMt/qnyUWnqiTcJXm5ZfNPIBeJ/DVvJDwxw+0tAxPJvadzfiP1QhryO1JOR6t1yfb8NpzQb/Rud06mob5laIA==",
|
"integrity": "sha512-sQMt/qnyUWnqiTcJXm5ZfNPIBeJ/DVvJDwxw+0tAxPJvadzfiP1QhryO1JOR6t1yfb8NpzQb/Rud06mob5laIA==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@ -873,6 +973,7 @@
|
|||||||
"version": "8.5.4",
|
"version": "8.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
|
||||||
"integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
|
"integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@ -1429,6 +1530,11 @@
|
|||||||
"color-support": "bin.js"
|
"color-support": "bin.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||||
|
},
|
||||||
"node_modules/commondir": {
|
"node_modules/commondir": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
@ -1536,6 +1642,14 @@
|
|||||||
"integrity": "sha512-n94yQo4LI3w7erwf84mhRUkUJfhLoCZiLyoOZ/QFsDbcWNZePrLwbQpvZBUG2TNxwV3VjCKPxkiiQA6pe3TrTA==",
|
"integrity": "sha512-n94yQo4LI3w7erwf84mhRUkUJfhLoCZiLyoOZ/QFsDbcWNZePrLwbQpvZBUG2TNxwV3VjCKPxkiiQA6pe3TrTA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/diff": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/dir-glob": {
|
"node_modules/dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||||
@ -1801,6 +1915,18 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/esprima": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||||
|
"bin": {
|
||||||
|
"esparse": "bin/esparse.js",
|
||||||
|
"esvalidate": "bin/esvalidate.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/esquery": {
|
"node_modules/esquery": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
|
||||||
@ -1922,6 +2048,38 @@
|
|||||||
"reusify": "^1.0.4"
|
"reusify": "^1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/feed": {
|
||||||
|
"version": "4.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz",
|
||||||
|
"integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"xml-js": "^1.6.11"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/feeder": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "git+ssh://git@phlaym.net:phlaym/feeder.git#9543c65bd79b056202cb5649b08858e90085da41",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"tslint": "^6.1.3",
|
||||||
|
"typescript": "^5.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/feeder/node_modules/typescript": {
|
||||||
|
"version": "5.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.3.tgz",
|
||||||
|
"integrity": "sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/file-entry-cache": {
|
"node_modules/file-entry-cache": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
||||||
@ -2014,8 +2172,7 @@
|
|||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"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=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/gauge": {
|
"node_modules/gauge": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
@ -2130,7 +2287,6 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.1"
|
"function-bind": "^1.1.1"
|
||||||
},
|
},
|
||||||
@ -2305,7 +2461,6 @@
|
|||||||
"version": "2.11.0",
|
"version": "2.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
|
||||||
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has": "^1.0.3"
|
"has": "^1.0.3"
|
||||||
},
|
},
|
||||||
@ -2397,6 +2552,11 @@
|
|||||||
"url": "https://opencollective.com/js-sdsl"
|
"url": "https://opencollective.com/js-sdsl"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-tokens": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||||
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
@ -2594,7 +2754,6 @@
|
|||||||
"version": "1.2.8",
|
"version": "1.2.8",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
"dev": true,
|
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
@ -2691,7 +2850,6 @@
|
|||||||
"version": "0.5.6",
|
"version": "0.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
},
|
},
|
||||||
@ -3009,8 +3167,7 @@
|
|||||||
"node_modules/path-parse": {
|
"node_modules/path-parse": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/path-type": {
|
"node_modules/path-type": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@ -3174,7 +3331,6 @@
|
|||||||
"version": "1.22.1",
|
"version": "1.22.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-core-module": "^2.9.0",
|
"is-core-module": "^2.9.0",
|
||||||
"path-parse": "^1.0.7",
|
"path-parse": "^1.0.7",
|
||||||
@ -3329,6 +3485,11 @@
|
|||||||
"rimraf": "bin.js"
|
"rimraf": "bin.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||||
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.3.8",
|
"version": "7.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
||||||
@ -3465,6 +3626,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sprintf-js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
||||||
|
},
|
||||||
"node_modules/sqlite3": {
|
"node_modules/sqlite3": {
|
||||||
"version": "5.1.6",
|
"version": "5.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.6.tgz",
|
||||||
@ -3580,7 +3746,6 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
@ -3788,6 +3953,152 @@
|
|||||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==",
|
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tslint": {
|
||||||
|
"version": "6.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
|
||||||
|
"integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
|
||||||
|
"deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": "^7.0.0",
|
||||||
|
"builtin-modules": "^1.1.1",
|
||||||
|
"chalk": "^2.3.0",
|
||||||
|
"commander": "^2.12.1",
|
||||||
|
"diff": "^4.0.1",
|
||||||
|
"glob": "^7.1.1",
|
||||||
|
"js-yaml": "^3.13.1",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"mkdirp": "^0.5.3",
|
||||||
|
"resolve": "^1.3.2",
|
||||||
|
"semver": "^5.3.0",
|
||||||
|
"tslib": "^1.13.0",
|
||||||
|
"tsutils": "^2.29.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"tslint": "bin/tslint"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.8.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/argparse": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||||
|
"dependencies": {
|
||||||
|
"sprintf-js": "~1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/builtin-modules": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/js-yaml": {
|
||||||
|
"version": "3.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||||
|
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^1.0.7",
|
||||||
|
"esprima": "^4.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"js-yaml": "bin/js-yaml.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/semver": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
|
},
|
||||||
|
"node_modules/tslint/node_modules/tsutils": {
|
||||||
|
"version": "2.29.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
|
||||||
|
"integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^1.8.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tsutils": {
|
"node_modules/tsutils": {
|
||||||
"version": "3.21.0",
|
"version": "3.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz",
|
||||||
@ -3837,7 +4148,6 @@
|
|||||||
"version": "4.9.5",
|
"version": "4.9.5",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@ -4024,6 +4334,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xml-js": {
|
||||||
|
"version": "1.6.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
|
||||||
|
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": "^1.2.4"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"xml-js": "bin/cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-node": "^1.2.3",
|
"@sveltejs/adapter-node": "^1.2.3",
|
||||||
"@sveltejs/kit": "^1.5.0",
|
"@sveltejs/kit": "^1.5.0",
|
||||||
|
"@types/sqlite3": "^3.1.8",
|
||||||
|
"@types/ws": "^8.5.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^5.45.0",
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.28.0",
|
||||||
@ -29,9 +31,8 @@
|
|||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/sqlite3": "^3.1.8",
|
|
||||||
"@types/ws": "^8.5.4",
|
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
|
"feed": "^4.2.2",
|
||||||
"sqlite3": "^5.1.6",
|
"sqlite3": "^5.1.6",
|
||||||
"ws": "^8.13.0"
|
"ws": "^8.13.0"
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<meta name="theme-color" content="#17063B" media="(prefers-color-scheme: dark)" />
|
<meta name="theme-color" content="#17063B" media="(prefers-color-scheme: dark)" />
|
||||||
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
|
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" />
|
||||||
|
<link rel="alternate" type="application/atom+xml" href="/feed.xml" title="Atom Feed" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { TimelineReader } from '$lib/server/timeline';
|
import { TimelineReader } from '$lib/server/timeline';
|
||||||
import type { HandleServerError } from '@sveltejs/kit';
|
import type { HandleServerError } from '@sveltejs/kit';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
|
||||||
TimelineReader.init();
|
TimelineReader.init();
|
||||||
|
|
||||||
@ -13,3 +14,18 @@ export const handleError = (({ error }) => {
|
|||||||
code: (error as any)?.code ?? 'UNKNOWN'
|
code: (error as any)?.code ?? 'UNKNOWN'
|
||||||
};
|
};
|
||||||
}) satisfies HandleServerError;
|
}) satisfies HandleServerError;
|
||||||
|
|
||||||
|
import type { Handle } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const handle = (async ({ event, resolve }) => {
|
||||||
|
if (event.url.pathname === '/feed.xml') {
|
||||||
|
const f = await fs.readFile('feed.xml', { encoding: 'utf8' });
|
||||||
|
return new Response(
|
||||||
|
f,
|
||||||
|
{ headers: [['Content-Type', 'application/atom+xml']] }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await resolve(event);
|
||||||
|
return response;
|
||||||
|
}) satisfies Handle;
|
1
src/lib/assets/rss-fill.svg
Normal file
1
src/lib/assets/rss-fill.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path d="M3 3C12.9411 3 21 11.0589 21 21H18C18 12.7157 11.2843 6 3 6V3ZM3 10C9.07513 10 14 14.9249 14 21H11C11 16.5817 7.41828 13 3 13V10ZM3 17C5.20914 17 7 18.7909 7 21H3V17Z" fill="#000"></path></svg>
|
After Width: | Height: | Size: 285 B |
@ -1,5 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import git from '$lib/assets/git-branch-fill.svg';
|
import git from '$lib/assets/git-branch-fill.svg';
|
||||||
|
import rss from '$lib/assets/rss-fill.svg';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
@ -11,7 +12,14 @@
|
|||||||
<div>
|
<div>
|
||||||
<a href="https://phlaym.net/git/phlaym/moshing-mammut">
|
<a href="https://phlaym.net/git/phlaym/moshing-mammut">
|
||||||
<img alt="Git branch" src={git} class="icon" />
|
<img alt="Git branch" src={git} class="icon" />
|
||||||
Source Code
|
Source Code
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
<div>
|
||||||
|
<a href="/feed.xml">
|
||||||
|
<img alt="RSS" src={rss} class="icon" />
|
||||||
|
RSS Feed
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -26,7 +34,7 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
-webkit-backdrop-filter: blur(10px);
|
-webkit-backdrop-filter: blur(10px);
|
||||||
background-color: #54546788;
|
background-color: #bbbbcd73;
|
||||||
padding: 0.3em 1em;
|
padding: 0.3em 1em;
|
||||||
margin: 0 -8px;
|
margin: 0 -8px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
40
src/lib/components/LoadMoreComponent.svelte
Normal file
40
src/lib/components/LoadMoreComponent.svelte
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
|
|
||||||
|
export let moreAvailable: boolean = false;
|
||||||
|
export let isLoading: boolean = false;
|
||||||
|
let displayText = '';
|
||||||
|
let title = '';
|
||||||
|
let disabled: boolean;
|
||||||
|
|
||||||
|
$: displayText = isLoading ? 'Loading...' : 'Load More';
|
||||||
|
$: disabled = !moreAvailable || isLoading;
|
||||||
|
$: title = moreAvailable ? 'Load More' : 'No more posts available';
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
function loadOlderPosts() {
|
||||||
|
dispatch('loadOlderPosts');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button on:click={loadOlderPosts} {disabled} {title}>{displayText}</button>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
button {
|
||||||
|
padding: 0.75em;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: none;
|
||||||
|
background-color: var(--color-link);
|
||||||
|
color: white;
|
||||||
|
cursor: grab;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
button:hover:not(:disabled) {
|
||||||
|
background-color: var(--color-link-visited);
|
||||||
|
}
|
||||||
|
button:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: grey;
|
||||||
|
}
|
||||||
|
</style>
|
@ -92,7 +92,8 @@ function getMigrations(): Migration[] {
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function savePost(post: Post): void {
|
export async function savePost(post: Post): Promise<undefined> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
console.debug(`Saving post ${post.url}`);
|
console.debug(`Saving post ${post.url}`);
|
||||||
const account = post.account;
|
const account = post.account;
|
||||||
db.run(`
|
db.run(`
|
||||||
@ -118,6 +119,7 @@ export function savePost(post: Post): void {
|
|||||||
(err) => {
|
(err) => {
|
||||||
if (err !== null) {
|
if (err !== null) {
|
||||||
console.error(`Could not insert/update account ${account.id}`, err);
|
console.error(`Could not insert/update account ${account.id}`, err);
|
||||||
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
db.run(`
|
db.run(`
|
||||||
@ -137,10 +139,12 @@ export function savePost(post: Post): void {
|
|||||||
(postErr) => {
|
(postErr) => {
|
||||||
if (postErr !== null) {
|
if (postErr !== null) {
|
||||||
console.error(`Could not insert post ${post.url}`, postErr);
|
console.error(`Could not insert post ${post.url}`, postErr);
|
||||||
|
reject(postErr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.parallelize(() => {
|
db.parallelize(() => {
|
||||||
|
let remaining = post.tags.length;
|
||||||
for (let tag of post.tags) {
|
for (let tag of post.tags) {
|
||||||
db.run(`
|
db.run(`
|
||||||
INSERT INTO tags (url, tag) VALUES (?, ?)
|
INSERT INTO tags (url, tag) VALUES (?, ?)
|
||||||
@ -153,6 +157,7 @@ export function savePost(post: Post): void {
|
|||||||
(tagErr) => {
|
(tagErr) => {
|
||||||
if (tagErr !== null) {
|
if (tagErr !== null) {
|
||||||
console.error(`Could not insert/update tag ${tag.url}`, tagErr);
|
console.error(`Could not insert/update tag ${tag.url}`, tagErr);
|
||||||
|
reject(tagErr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
db.run('INSERT INTO poststags (post_id, tag_url) VALUES (?, ?)',
|
db.run('INSERT INTO poststags (post_id, tag_url) VALUES (?, ?)',
|
||||||
@ -160,8 +165,15 @@ export function savePost(post: Post): void {
|
|||||||
(posttagserr) => {
|
(posttagserr) => {
|
||||||
if (posttagserr !== null) {
|
if (posttagserr !== null) {
|
||||||
console.error(`Could not insert poststags ${tag.url}, ${post.url}`, posttagserr);
|
console.error(`Could not insert poststags ${tag.url}, ${post.url}`, posttagserr);
|
||||||
|
reject(posttagserr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Don't decrease on fail
|
||||||
|
remaining--;
|
||||||
|
// Only resolve after all have been inserted
|
||||||
|
if (remaining === 0) {
|
||||||
|
resolve(undefined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -170,17 +182,22 @@ export function savePost(post: Post): void {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPosts(since: string | null, limit: number) {
|
export async function getPosts(since: string | null, before: string | null, limit: number) {
|
||||||
let promise = await new Promise<Post[]>((resolve, reject) => {
|
let promise = await new Promise<Post[]>((resolve, reject) => {
|
||||||
let filter_query;
|
let filter_query;
|
||||||
let params: any = { $limit: limit };
|
let params: any = { $limit: limit };
|
||||||
if (since === null) {
|
if (since === null && before === null) {
|
||||||
filter_query = '';
|
filter_query = '';
|
||||||
} else {
|
} else if (since !== null) {
|
||||||
filter_query = 'WHERE posts.created_at > $since';
|
filter_query = 'WHERE posts.created_at > $since';
|
||||||
params.$since = since;
|
params.$since = since;
|
||||||
|
} else if (before !== null) {
|
||||||
|
// Setting both, before and since doesn't make sense, so this case is not explicitly handled
|
||||||
|
filter_query = 'WHERE posts.created_at < $before';
|
||||||
|
params.$before = before;
|
||||||
}
|
}
|
||||||
const sql = `SELECT posts.id, posts.content, posts.created_at, posts.url,
|
const sql = `SELECT posts.id, posts.content, posts.created_at, posts.url,
|
||||||
accounts.id AS account_id, accounts.acct, accounts.username, accounts.display_name,
|
accounts.id AS account_id, accounts.acct, accounts.username, accounts.display_name,
|
||||||
|
45
src/lib/server/rss.ts
Normal file
45
src/lib/server/rss.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { BASE_URL } from '$env/static/private';
|
||||||
|
import { PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME } from '$env/static/public';
|
||||||
|
import type { Post } from '$lib//mastodon/response';
|
||||||
|
import { Feed } from 'feed';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
|
||||||
|
export function createFeed(posts: Post[]): Feed {
|
||||||
|
const baseUrl = BASE_URL.endsWith('/') ? BASE_URL : BASE_URL + '/';
|
||||||
|
const feed = new Feed({
|
||||||
|
title: `${PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME} music feed`,
|
||||||
|
description: `Posts about music on ${PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME}`,
|
||||||
|
id: baseUrl,
|
||||||
|
link: baseUrl,
|
||||||
|
language: 'en',
|
||||||
|
//image: "http://example.com/image.png",
|
||||||
|
//favicon: "http://example.com/favicon.ico",
|
||||||
|
copyright: '',
|
||||||
|
generator: 'moshing-mamut',
|
||||||
|
feedLinks: {
|
||||||
|
atom: `${BASE_URL}/feed.xml`
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
name: '@aymm',
|
||||||
|
link: 'https://metalhead.club/@aymm'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
posts.forEach(p => {
|
||||||
|
feed.addItem({
|
||||||
|
title: p.content,
|
||||||
|
id: p.url,
|
||||||
|
link: p.url,
|
||||||
|
content: p.content,
|
||||||
|
author: [{
|
||||||
|
name: p.account.acct,
|
||||||
|
link: p.account.url
|
||||||
|
}],
|
||||||
|
date: new Date(p.created_at)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
feed.addCategory('Music');
|
||||||
|
return feed;
|
||||||
|
}
|
||||||
|
export async function saveAtomFeed(feed: Feed) {
|
||||||
|
await fs.writeFile('feed.xml', feed.atom1(), { encoding: 'utf8' });
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
import { HASHTAG_FILTER, URL_FILTER, YOUTUBE_API_KEY } from '$env/static/private';
|
import { HASHTAG_FILTER, MASTODON_INSTANCE, URL_FILTER, YOUTUBE_API_KEY } from '$env/static/private';
|
||||||
import type { Post, Tag, TimelineEvent } from '$lib/mastodon/response';
|
import type { Post, Tag, TimelineEvent } from '$lib/mastodon/response';
|
||||||
import { savePost } from '$lib/server/db';
|
import { getPosts, savePost } from '$lib/server/db';
|
||||||
|
import { createFeed, saveAtomFeed } from '$lib/server/rss';
|
||||||
import { WebSocket } from "ws";
|
import { WebSocket } from "ws";
|
||||||
|
|
||||||
const YOUTUBE_REGEX = new RegExp(/https?:\/\/(www\.)?youtu((be.com\/.*v=)|(\.be\/))(?<videoId>[a-zA-Z_0-9-]+)/gm);
|
const YOUTUBE_REGEX = new RegExp(/https?:\/\/(www\.)?youtu((be.com\/.*?v=)|(\.be\/))(?<videoId>[a-zA-Z_0-9-]+)/gm);
|
||||||
|
|
||||||
export class TimelineReader {
|
export class TimelineReader {
|
||||||
private static _instance: TimelineReader;
|
private static _instance: TimelineReader;
|
||||||
@ -55,7 +56,7 @@ export class TimelineReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
const socket = new WebSocket("wss://metalhead.club/api/v1/streaming")
|
const socket = new WebSocket(`wss://${MASTODON_INSTANCE}/api/v1/streaming`);
|
||||||
socket.onopen = (_event) => {
|
socket.onopen = (_event) => {
|
||||||
socket.send('{ "type": "subscribe", "stream": "public:local"}');
|
socket.send('{ "type": "subscribe", "stream": "public:local"}');
|
||||||
};
|
};
|
||||||
@ -79,7 +80,9 @@ export class TimelineReader {
|
|||||||
!await TimelineReader.checkYoutubeMatches(post.content)) {
|
!await TimelineReader.checkYoutubeMatches(post.content)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
savePost(post);
|
await savePost(post);
|
||||||
|
const posts = await getPosts(null, null, 100);
|
||||||
|
await saveAtomFeed(createFeed(posts));
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("error message", event, event.data, e)
|
console.error("error message", event, event.data, e)
|
||||||
|
@ -2,52 +2,129 @@
|
|||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
import type { Post } from '$lib/mastodon/response';
|
import type { Post } from '$lib/mastodon/response';
|
||||||
import { PUBLIC_REFRESH_INTERVAL } from '$env/static/public';
|
import { PUBLIC_REFRESH_INTERVAL, PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME } from '$env/static/public';
|
||||||
import PostComponent from '$lib/components/PostComponent.svelte';
|
import PostComponent from '$lib/components/PostComponent.svelte';
|
||||||
|
import LoadMoreComponent from '$lib/components/LoadMoreComponent.svelte';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
|
||||||
|
const refreshInterval = parseInt(PUBLIC_REFRESH_INTERVAL);
|
||||||
let interval: NodeJS.Timer | null = null;
|
let interval: NodeJS.Timer | null = null;
|
||||||
|
let moreOlderPostsAvailable = true;
|
||||||
|
let loadingOlderPosts = false;
|
||||||
|
|
||||||
onMount(async () => {
|
interface FetchOptions {
|
||||||
interval = setInterval(async () => {
|
since?: string,
|
||||||
|
before?: string,
|
||||||
|
count?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchPosts(options: FetchOptions): Promise<Post[]> {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (data.posts.length > 0) {
|
if (options?.since !== undefined) {
|
||||||
params.set('since', data.posts[0].created_at);
|
params.set('since', options.since);
|
||||||
}
|
}
|
||||||
await fetch(`/api/posts?${params}`)
|
if (options?.before !== undefined) {
|
||||||
.then(r => r.json())
|
params.set('before', options.before);
|
||||||
.then((resp: Post[]) => {
|
}
|
||||||
|
if (options?.count !== undefined) {
|
||||||
|
params.set('count', options.count.toFixed(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/api/posts?${params}`);
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterDuplicates(posts: Post[]): Post[] {
|
||||||
|
return posts.filter((obj, index, arr) => {
|
||||||
|
return arr.map(mapObj => mapObj.url).indexOf(obj.url) === index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
let filter: FetchOptions = {};
|
||||||
|
if (data.posts.length > 0) {
|
||||||
|
filter = { since: data.posts[0].created_at };
|
||||||
|
}
|
||||||
|
fetchPosts(filter).then(resp => {
|
||||||
if (resp.length > 0) {
|
if (resp.length > 0) {
|
||||||
data.posts = resp.concat(data.posts);
|
// Prepend new posts, filter dupes
|
||||||
console.log('updated data', resp, data.posts);
|
// There shouldn't be any duplicates, but better be safe than sorry
|
||||||
|
data.posts = filterDuplicates(resp.concat(data.posts));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
// TODO: Show error in UI
|
// TODO: Show error in UI
|
||||||
console.error('Error loading newest posts', e);
|
console.error('Error loading newest posts', e);
|
||||||
});
|
});
|
||||||
}, parseInt(PUBLIC_REFRESH_INTERVAL));
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
interval = setInterval(refresh, refreshInterval);
|
||||||
|
|
||||||
|
// - If the page is hidden, slow down refresh rate
|
||||||
|
// - If the page is shown, bump up refresh rate
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
const delay = document.hidden ? refreshInterval * 10 : refreshInterval;
|
||||||
|
if (interval) {
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
interval = setInterval(refresh, delay);
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (interval !== null) {
|
if (interval !== null) {
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
function loadOlderPosts() {
|
||||||
|
loadingOlderPosts = true;
|
||||||
|
const filter: FetchOptions = { count: 20 };
|
||||||
|
if (data.posts.length > 0) {
|
||||||
|
filter.before = data.posts[data.posts.length - 1].created_at;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fetchPosts(filter).then(resp => {
|
||||||
|
if (resp.length > 0) {
|
||||||
|
// Append old posts, filter dupes
|
||||||
|
// There shouldn't be any duplicates, but better be safe than sorry
|
||||||
|
data.posts = filterDuplicates(data.posts.concat(resp));
|
||||||
|
// If we got less than we expected, there are no older posts available
|
||||||
|
moreOlderPostsAvailable = resp.length < (filter.count ?? 20);
|
||||||
|
} else {
|
||||||
|
moreOlderPostsAvailable = false;
|
||||||
|
}
|
||||||
|
loadingOlderPosts = false;
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
loadingOlderPosts = false;
|
||||||
|
// TODO: Show error in UI
|
||||||
|
console.error('Error loading older posts', e);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Metalhead.club music list</title>
|
<title>{PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME} music list</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
<h2>Metalhead.club music list</h2>
|
<h2>{PUBLIC_MASTODON_INSTANCE_DISPLAY_NAME} music list</h2>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div></div>
|
<div></div>
|
||||||
<div class="posts">
|
<div class="posts">
|
||||||
{#if data.posts.length === 0}
|
{#if data.posts.length === 0}
|
||||||
Sorry, no posts recommending music aave been found yet
|
Sorry, no posts recommending music aave been found yet
|
||||||
{/if}
|
{/if}
|
||||||
{#each data.posts as post (post.id)}
|
{#each data.posts as post (post.url)}
|
||||||
<div class="post"><PostComponent {post} /></div>
|
<div class="post"><PostComponent {post} /></div>
|
||||||
{/each}
|
{/each}
|
||||||
|
<LoadMoreComponent
|
||||||
|
on:loadOlderPosts={loadOlderPosts}
|
||||||
|
moreAvailable={moreOlderPostsAvailable}
|
||||||
|
isLoading={loadingOlderPosts}/>
|
||||||
</div>
|
</div>
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
@ -55,6 +132,7 @@ onMount(async () => {
|
|||||||
.posts {
|
.posts {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
.post {
|
.post {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -5,11 +5,12 @@ import type { RequestHandler } from './$types';
|
|||||||
|
|
||||||
export const GET = (async ({ url }) => {
|
export const GET = (async ({ url }) => {
|
||||||
const since = url.searchParams.get('since');
|
const since = url.searchParams.get('since');
|
||||||
|
const before = url.searchParams.get('before');
|
||||||
let count = Number.parseInt(url.searchParams.get('count') || '');
|
let count = Number.parseInt(url.searchParams.get('count') || '');
|
||||||
if (isNaN(count)) {
|
if (isNaN(count)) {
|
||||||
count = 20;
|
count = 20;
|
||||||
}
|
}
|
||||||
count = Math.min(count, 100);
|
count = Math.min(count, 100);
|
||||||
const posts = await getPosts(since, count);
|
const posts = await getPosts(since, before, count);
|
||||||
return json(posts);
|
return json(posts);
|
||||||
}) satisfies RequestHandler;
|
}) satisfies RequestHandler;
|
@ -1,3 +1,3 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
. /PATH_TO_YOUR_NVM/.nvm/nvm.sh
|
. /home/moshing-mammut/.nvm/nvm.sh
|
||||||
node -r dotenv/config build
|
node -r dotenv/config build
|
Reference in New Issue
Block a user