Compare commits
56 commits
Author | SHA1 | Date | |
---|---|---|---|
split / May | b59d1b24ff | ||
35616abb48 | |||
1d6ac68c7d | |||
split / May | 467180b189 | ||
split / May | 633dc45687 | ||
split / May | 2a5a1f75f5 | ||
split / May | 9523664f74 | ||
split / May | 9489a6dd13 | ||
split / May | 8d843169d9 | ||
split / May | 2510f31a58 | ||
split / May | 89b8b2c7b1 | ||
196b661b52 | |||
split / May | 58aa0aac7e | ||
cbb9152529 | |||
split / May | a84400c903 | ||
ab073db311 | |||
f5ffe42fb4 | |||
939f6ad0aa | |||
split / May | ead3b59917 | ||
split / May | 85f1238897 | ||
e617a9464e | |||
667acf54f2 | |||
2f043d5eb9 | |||
1beb4a9632 | |||
split / May | 693f453833 | ||
split / May | dca41242e1 | ||
split / May | 6dfb18a042 | ||
split / May | 0bf53801ed | ||
split / May | 783d6be808 | ||
split / May | 052d51abfe | ||
1ca2ea28ae | |||
bf2b398a1a | |||
split / May | 3163ad649b | ||
split / May | 937363c9ee | ||
split / May | 9f480cbc87 | ||
aa840e176f | |||
split / May | 0071c17fe5 | ||
split / May | 5e15b20c10 | ||
split / May | 8bec8a4360 | ||
split / May | af9c3233d8 | ||
split / May | 6742bf724f | ||
split / May | 2006fa1cca | ||
split / May | fad320d7fb | ||
split / May | a04cc9a376 | ||
split / May | b3efd8ca29 | ||
split / May | 8a4dfd361c | ||
split / May | 8f6a5b5474 | ||
split / May | 261b81ee04 | ||
split / May | 11602b5556 | ||
split / May | e54f6a5b8f | ||
split / May | 76119811ff | ||
split / May | c8e486630e | ||
split / May | a5d3131180 | ||
split / May | 14d4261858 | ||
split / May | 64dd66dc03 | ||
split / May | 1dfd095563 |
4
.vscode/tasks.json
vendored
|
@ -3,7 +3,7 @@
|
|||
"tasks": [
|
||||
{
|
||||
"type": "shell",
|
||||
"command":"tsc\nsass src/style:out/style\nrollup -c",
|
||||
"command":"npm run build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
|
@ -12,7 +12,7 @@
|
|||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"command":"tsc\nsass src/style:out/style\nrollup -c\nnode ./out/server/index.js\ndel ./out/* -Recurse",
|
||||
"command":"npm run build\nnode ./out/server/index.js\ndel ./out/* -Recurse",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
|
|
11
README.md
|
@ -1,6 +1,5 @@
|
|||
# monofile
|
||||
The open-source, Discord-based file sharing service.
|
||||
[Live instance](https://fyle.uk)
|
||||
<img src="https://fyle.uk/fiAb3" alt="monofile: 'File sharing over Discord', topped with the monofile logo. Blue gradient with an image of monofile 1.4.0-dev on the side.">
|
||||
<p align="center">The open-source, Discord-based file sharing service.<br><a href="https://fyle.uk">Flagship instance</a> — <a href="https://beta.fyle.uk">Live test instance</a></p>
|
||||
|
||||
<br>
|
||||
|
||||
|
@ -66,10 +65,10 @@ monofile should now be running on either `env.MONOFILE_PORT` or port `3000`.
|
|||
|
||||
## Disclaimer
|
||||
|
||||
Although we believe monofile is not against Discord's developer terms of service, monofile's contributors are not liable if Discord takes action against you for running an instance.
|
||||
Although we believe monofile is not against Discord's developer terms of service, Etcetera is not liable if Discord takes action against you for running an instance.
|
||||
|
||||
## License
|
||||
|
||||
Code written by monofile's contributors is currently licensed under [Unlicense](https://github.com/nbitzz/monofile/blob/main/LICENSE).
|
||||
Code written by Etcetera is currently licensed under [Unlicense](./LICENSE).
|
||||
|
||||
Icons under `/assets/icons` were created by Microsoft, and as such are licensed under [different terms](https://github.com/nbitzz/monofile/blob/1.3.0/assets/icons/README.md) (MIT).
|
||||
Icons under `/assets/icons` were created by Microsoft, and as such are licensed under [different terms](./assets/icons/README.md) (MIT).
|
||||
|
|
BIN
assets/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 448 KiB After Width: | Height: | Size: 401 KiB |
BIN
assets/banner.xcf
Normal file
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m11.256 13 .238-1.5h1.481l-.237 1.5h-1.482Z" fill="#8aadf4"/><path d="M17.75 2.001a2.25 2.25 0 0 1 2.245 2.096L20 4.25v15.498a2.25 2.25 0 0 1-2.096 2.245l-.154.005H6.25a2.25 2.25 0 0 1-2.245-2.096L4 19.75V4.251a2.25 2.25 0 0 1 2.096-2.245l.154-.005h11.5Zm-5.355 13.16a.75.75 0 1 0 1.482.234l.142-.895h.731a.75.75 0 0 0 0-1.5h-.494l.238-1.5h.756a.75.75 0 0 0 0-1.5h-.519l.162-1.025a.75.75 0 1 0-1.481-.234l-.2 1.259h-1.48l.161-1.025a.75.75 0 1 0-1.481-.234l-.2 1.259H9.25a.75.75 0 1 0 0 1.5h.725L9.738 13H8.75a.75.75 0 1 0 0 1.5h.75l-.105.66a.75.75 0 0 0 1.482.235l.142-.895H12.5l-.105.66Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#8aadf4" d="m11.256 13 .238-1.5h1.481l-.237 1.5h-1.482Z"/><path fill="#DDD" d="M17.75 2.001a2.25 2.25 0 0 1 2.245 2.096L20 4.25v15.498a2.25 2.25 0 0 1-2.096 2.245l-.154.005H6.25a2.25 2.25 0 0 1-2.245-2.096L4 19.75V4.251a2.25 2.25 0 0 1 2.096-2.245l.154-.005h11.5Zm-5.355 13.16a.75.75 0 1 0 1.482.234l.142-.895h.731a.75.75 0 0 0 0-1.5h-.494l.238-1.5h.756a.75.75 0 0 0 0-1.5h-.519l.162-1.025a.75.75 0 1 0-1.481-.234l-.2 1.259h-1.48l.161-1.025a.75.75 0 1 0-1.481-.234l-.2 1.259H9.25a.75.75 0 1 0 0 1.5h.725L9.738 13H8.75a.75.75 0 1 0 0 1.5h.75l-.105.66a.75.75 0 0 0 1.482.235l.142-.895H12.5l-.105.66Z"/></svg>
|
Before Width: | Height: | Size: 716 B After Width: | Height: | Size: 713 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2v6a2 2 0 0 0 2 2h6v10a2 2 0 0 1-2 2h-6.81A6.5 6.5 0 0 0 4 11.498V4a2 2 0 0 1 2-2h6Z" fill="#DDDDDD"/><path d="M13.5 2.5V8a.5.5 0 0 0 .5.5h5.5l-6-6ZM6.5 12a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11Zm2.478 3.731-1.77 1.77 1.77 1.769a.5.5 0 1 1-.707.707l-1.77-1.77-1.769 1.768a.5.5 0 1 1-.707-.708L5.794 17.5 4.025 15.73a.5.5 0 1 1 .707-.707l1.77 1.769 1.77-1.769a.5.5 0 0 1 .706.707Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M12 2v6a2 2 0 0 0 2 2h6v10a2 2 0 0 1-2 2h-6.81A6.5 6.5 0 0 0 4 11.498V4a2 2 0 0 1 2-2h6Z"/><path fill="#DDD" d="M13.5 2.5V8a.5.5 0 0 0 .5.5h5.5l-6-6ZM6.5 12a5.5 5.5 0 1 0 0 11 5.5 5.5 0 0 0 0-11Zm2.478 3.731-1.77 1.77 1.77 1.769a.5.5 0 1 1-.707.707l-1.77-1.77-1.769 1.768a.5.5 0 1 1-.707-.708L5.794 17.5 4.025 15.73a.5.5 0 1 1 .707-.707l1.77 1.769 1.77-1.769a.5.5 0 0 1 .706.707Z"/></svg>
|
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 504 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M14 14.05V14H4.253a2.249 2.249 0 0 0-2.25 2.25v.919c0 .572.18 1.13.511 1.596C4.056 20.929 6.58 22 10 22c.715 0 1.39-.046 2.026-.14A2.51 2.51 0 0 1 12 21.5v-5a2.5 2.5 0 0 1 2-2.45ZM10 2.005a5 5 0 1 1 0 10 5 5 0 0 1 0-10ZM15 15v-1a2.5 2.5 0 0 1 5 0v1h.5a1.5 1.5 0 0 1 1.5 1.5v5a1.5 1.5 0 0 1-1.5 1.5h-6a1.5 1.5 0 0 1-1.5-1.5v-5a1.5 1.5 0 0 1 1.5-1.5h.5Zm1.5-1v1h2v-1a1 1 0 1 0-2 0Zm2 5a1 1 0 1 0-2 0 1 1 0 0 0 2 0Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M14 14.05V14H4.253a2.249 2.249 0 0 0-2.25 2.25v.919c0 .572.18 1.13.511 1.596C4.056 20.929 6.58 22 10 22c.715 0 1.39-.046 2.026-.14A2.51 2.51 0 0 1 12 21.5v-5a2.5 2.5 0 0 1 2-2.45ZM10 2.005a5 5 0 1 1 0 10 5 5 0 0 1 0-10ZM15 15v-1a2.5 2.5 0 0 1 5 0v1h.5a1.5 1.5 0 0 1 1.5 1.5v5a1.5 1.5 0 0 1-1.5 1.5h-6a1.5 1.5 0 0 1-1.5-1.5v-5a1.5 1.5 0 0 1 1.5-1.5h.5Zm1.5-1v1h2v-1a1 1 0 1 0-2 0Zm2 5a1 1 0 1 0-2 0 1 1 0 0 0 2 0Z"/></svg>
|
Before Width: | Height: | Size: 540 B After Width: | Height: | Size: 537 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.022 14A6.47 6.47 0 0 0 11 17.5c0 1.63.6 3.12 1.592 4.261-.796.16-1.66.24-2.592.24-3.42 0-5.944-1.072-7.486-3.237a2.75 2.75 0 0 1-.51-1.595v-.92a2.249 2.249 0 0 1 2.249-2.25h7.77Zm5.478-2a5.5 5.5 0 1 1 0 11 5.5 5.5 0 0 1 0-11Zm0 7.75a.625.625 0 1 0 0 1.25.625.625 0 0 0 0-1.25Zm0-5.876c-1.048 0-1.864.817-1.853 1.954a.5.5 0 1 0 1-.01c-.006-.579.36-.944.853-.944.473 0 .854.392.854.95 0 .192-.056.342-.224.56l-.094.117-.1.113-.265.29-.136.157c-.384.457-.535.793-.535 1.31a.5.5 0 1 0 1 0c0-.203.059-.359.239-.59l.085-.104.1-.116.267-.29.134-.155c.378-.45.529-.783.529-1.293 0-1.103-.823-1.95-1.854-1.95ZM10 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M12.022 14A6.47 6.47 0 0 0 11 17.5c0 1.63.6 3.12 1.592 4.261-.796.16-1.66.24-2.592.24-3.42 0-5.944-1.072-7.486-3.237a2.75 2.75 0 0 1-.51-1.595v-.92a2.249 2.249 0 0 1 2.249-2.25h7.77Zm5.478-2a5.5 5.5 0 1 1 0 11 5.5 5.5 0 0 1 0-11Zm0 7.75a.625.625 0 1 0 0 1.25.625.625 0 0 0 0-1.25Zm0-5.876c-1.048 0-1.864.817-1.853 1.954a.5.5 0 1 0 1-.01c-.006-.579.36-.944.853-.944.473 0 .854.392.854.95 0 .192-.056.342-.224.56l-.094.117-.1.113-.265.29-.136.157c-.384.457-.535.793-.535 1.31a.5.5 0 1 0 1 0c0-.203.059-.359.239-.59l.085-.104.1-.116.267-.29.134-.155c.378-.45.529-.783.529-1.293 0-1.103-.823-1.95-1.854-1.95ZM10 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z"/></svg>
|
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 769 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M23 6.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0ZM18 7l.001 2.504a.5.5 0 1 1-1 0V7h-2.505a.5.5 0 0 1 0-1H17V3.5a.5.5 0 0 1 1 0V6h2.497a.5.5 0 0 1 0 1H18Zm-.5 6a6.478 6.478 0 0 0 4.5-1.81v5.56a3.25 3.25 0 0 1-3.066 3.245L18.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75V8.608l9.652 5.056a.75.75 0 0 0 .696 0l2.417-1.266A6.477 6.477 0 0 0 17.5 13ZM5.25 4h6.248A6.479 6.479 0 0 0 11 6.5c0 1.993.897 3.776 2.308 4.968L12 12.153l-9.984-5.23a3.25 3.25 0 0 1 3.048-2.918L5.25 4Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M23 6.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0ZM18 7l.001 2.504a.5.5 0 1 1-1 0V7h-2.505a.5.5 0 0 1 0-1H17V3.5a.5.5 0 0 1 1 0V6h2.497a.5.5 0 0 1 0 1H18Zm-.5 6a6.478 6.478 0 0 0 4.5-1.81v5.56a3.25 3.25 0 0 1-3.066 3.245L18.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75V8.608l9.652 5.056a.75.75 0 0 0 .696 0l2.417-1.266A6.477 6.477 0 0 0 17.5 13ZM5.25 4h6.248A6.479 6.479 0 0 0 11 6.5c0 1.993.897 3.776 2.308 4.968L12 12.153l-9.984-5.23a3.25 3.25 0 0 1 3.048-2.918L5.25 4Z"/></svg>
|
Before Width: | Height: | Size: 597 B After Width: | Height: | Size: 594 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.95 8.6a6.554 6.554 0 0 1 6.55-6.55c3.596 0 6.55 2.819 6.55 6.45a6.554 6.554 0 0 1-6.55 6.55c-.531 0-1.055-.076-1.552-.204A1.25 1.25 0 0 1 12.7 16.05h-1.75v1.75c0 .69-.56 1.25-1.25 1.25H7.95v1.25a1.75 1.75 0 0 1-1.75 1.75H3.7a1.75 1.75 0 0 1-1.75-1.75v-2.172c0-.73.29-1.429.806-1.944L8.99 9.948a.275.275 0 0 0 .07-.244A6.386 6.386 0 0 1 8.95 8.6Zm9.3-1.6a1.25 1.25 0 1 0-2.5 0 1.25 1.25 0 0 0 2.5 0Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M8.95 8.6a6.554 6.554 0 0 1 6.55-6.55c3.596 0 6.55 2.819 6.55 6.45a6.554 6.554 0 0 1-6.55 6.55c-.531 0-1.055-.076-1.552-.204A1.25 1.25 0 0 1 12.7 16.05h-1.75v1.75c0 .69-.56 1.25-1.25 1.25H7.95v1.25a1.75 1.75 0 0 1-1.75 1.75H3.7a1.75 1.75 0 0 1-1.75-1.75v-2.172c0-.73.29-1.429.806-1.944L8.99 9.948a.275.275 0 0 0 .07-.244A6.386 6.386 0 0 1 8.95 8.6Zm9.3-1.6a1.25 1.25 0 1 0-2.5 0 1.25 1.25 0 0 0 2.5 0Z"/></svg>
|
Before Width: | Height: | Size: 529 B After Width: | Height: | Size: 526 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M11 15c0-.35.06-.687.17-1H4.253a2.249 2.249 0 0 0-2.249 2.249v.92c0 .572.179 1.13.51 1.596C4.057 20.929 6.58 22 10 22c.397 0 .783-.014 1.156-.043A2.997 2.997 0 0 1 11 21v-6ZM10 2.005a5 5 0 1 1 0 10 5 5 0 0 1 0-10ZM12 15a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2v-6Zm2.5 1a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6Zm0 3a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M11 15c0-.35.06-.687.17-1H4.253a2.249 2.249 0 0 0-2.249 2.249v.92c0 .572.179 1.13.51 1.596C4.057 20.929 6.58 22 10 22c.397 0 .783-.014 1.156-.043A2.997 2.997 0 0 1 11 21v-6ZM10 2.005a5 5 0 1 1 0 10 5 5 0 0 1 0-10ZM12 15a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-7a2 2 0 0 1-2-2v-6Zm2.5 1a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6Zm0 3a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1h-6Z"/></svg>
|
Before Width: | Height: | Size: 496 B After Width: | Height: | Size: 493 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m4.21 4.387.083-.094a1 1 0 0 1 1.32-.083l.094.083L12 10.585l6.293-6.292a1 1 0 1 1 1.414 1.414L13.415 12l6.292 6.293a1 1 0 0 1 .083 1.32l-.083.094a1 1 0 0 1-1.32.083l-.094-.083L12 13.415l-6.293 6.292a1 1 0 0 1-1.414-1.414L10.585 12 4.293 5.707a1 1 0 0 1-.083-1.32l.083-.094-.083.094Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="m4.21 4.387.083-.094a1 1 0 0 1 1.32-.083l.094.083L12 10.585l6.293-6.292a1 1 0 1 1 1.414 1.414L13.415 12l6.292 6.293a1 1 0 0 1 .083 1.32l-.083.094a1 1 0 0 1-1.32.083l-.094-.083L12 13.415l-6.293 6.292a1 1 0 0 1-1.414-1.414L10.585 12 4.293 5.707a1 1 0 0 1-.083-1.32l.083-.094-.083.094Z"/></svg>
|
Before Width: | Height: | Size: 410 B After Width: | Height: | Size: 407 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M17.5 12a5.5 5.5 0 1 1 0 11 5.5 5.5 0 0 1 0-11Zm-5.478 2A6.47 6.47 0 0 0 11 17.5c0 1.644.61 3.145 1.617 4.29-.802.141-1.675.21-2.617.21-2.89 0-5.128-.656-6.691-2a3.75 3.75 0 0 1-1.305-2.843v-.907A2.25 2.25 0 0 1 4.254 14h7.768Zm3.071.966-.07.058-.057.07a.5.5 0 0 0 0 .568l.058.069 1.77 1.77-1.768 1.766-.057.07a.5.5 0 0 0 0 .568l.058.07.069.057a.5.5 0 0 0 .568 0l.07-.058 1.766-1.767 1.77 1.77.069.058a.5.5 0 0 0 .568 0l.07-.058.058-.07a.5.5 0 0 0 0-.568l-.058-.07-1.77-1.768 1.772-1.77.058-.07a.5.5 0 0 0 0-.568l-.058-.069-.069-.058a.5.5 0 0 0-.569 0l-.069.058-1.771 1.77-1.77-1.77-.07-.058a.5.5 0 0 0-.492-.043l-.076.043ZM10 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M17.5 12a5.5 5.5 0 1 1 0 11 5.5 5.5 0 0 1 0-11Zm-5.478 2A6.47 6.47 0 0 0 11 17.5c0 1.644.61 3.145 1.617 4.29-.802.141-1.675.21-2.617.21-2.89 0-5.128-.656-6.691-2a3.75 3.75 0 0 1-1.305-2.843v-.907A2.25 2.25 0 0 1 4.254 14h7.768Zm3.071.966-.07.058-.057.07a.5.5 0 0 0 0 .568l.058.069 1.77 1.77-1.768 1.766-.057.07a.5.5 0 0 0 0 .568l.058.07.069.057a.5.5 0 0 0 .568 0l.07-.058 1.766-1.767 1.77 1.77.069.058a.5.5 0 0 0 .568 0l.07-.058.058-.07a.5.5 0 0 0 0-.568l-.058-.07-1.77-1.768 1.772-1.77.058-.07a.5.5 0 0 0 0-.568l-.058-.069-.069-.058a.5.5 0 0 0-.569 0l-.069.058-1.771 1.77-1.77-1.77-.07-.058a.5.5 0 0 0-.492-.043l-.076.043ZM10 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z"/></svg>
|
Before Width: | Height: | Size: 791 B After Width: | Height: | Size: 788 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M23 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0Zm-7.146-2.354a.5.5 0 0 0-.708.708L16.793 6.5l-1.647 1.646a.5.5 0 0 0 .708.708L17.5 7.207l1.646 1.647a.5.5 0 0 0 .708-.708L18.207 6.5l1.647-1.646a.5.5 0 0 0-.708-.708L17.5 5.793l-1.646-1.647ZM17.5 13a6.478 6.478 0 0 0 4.5-1.81v5.56a3.25 3.25 0 0 1-3.066 3.245L18.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75V8.608l9.652 5.056a.75.75 0 0 0 .696 0l2.417-1.266A6.477 6.477 0 0 0 17.5 13ZM5.25 4h6.248A6.479 6.479 0 0 0 11 6.5c0 1.993.897 3.776 2.308 4.968L12 12.153l-9.984-5.23a3.25 3.25 0 0 1 3.048-2.918L5.25 4Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M23 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0Zm-7.146-2.354a.5.5 0 0 0-.708.708L16.793 6.5l-1.647 1.646a.5.5 0 0 0 .708.708L17.5 7.207l1.646 1.647a.5.5 0 0 0 .708-.708L18.207 6.5l1.647-1.646a.5.5 0 0 0-.708-.708L17.5 5.793l-1.646-1.647ZM17.5 13a6.478 6.478 0 0 0 4.5-1.81v5.56a3.25 3.25 0 0 1-3.066 3.245L18.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75V8.608l9.652 5.056a.75.75 0 0 0 .696 0l2.417-1.266A6.477 6.477 0 0 0 17.5 13ZM5.25 4h6.248A6.479 6.479 0 0 0 11 6.5c0 1.993.897 3.776 2.308 4.968L12 12.153l-9.984-5.23a3.25 3.25 0 0 1 3.048-2.918L5.25 4Z"/></svg>
|
Before Width: | Height: | Size: 685 B After Width: | Height: | Size: 682 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2v6a2 2 0 0 0 2 2h6v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h6Z" fill="#DDDDDD"/><path d="M13.5 2.5V8a.5.5 0 0 0 .5.5h5.5l-6-6Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M12 2v6a2 2 0 0 0 2 2h6v10a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h6Z"/><path fill="#DDD" d="M13.5 2.5V8a.5.5 0 0 0 .5.5h5.5l-6-6Z"/></svg>
|
Before Width: | Height: | Size: 267 B After Width: | Height: | Size: 261 B |
1
assets/icons/icon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="15.009" version="1.1" viewBox="0 0 3.175 3.971"><defs><linearGradient id="gradient"><stop offset="0" stop-color="#fff" stop-opacity="0"/><stop offset=".5" stop-color="#fff"/></linearGradient></defs><title>monofile</title><path fill="#fff" d="m0 3.442"/><path fill="url(#gradient)" d="m0 3.442v.52917h3.175v-.52917z"/><path fill="#fff" d="m1.3219 0-0.56845 0.34727v0.53228l0.5054-0.30231h0.013436v1.6464l-0.55605-0.55605c-0.04961-0.0496-0.11679-0.077516-0.18707-0.077516s-0.13746 0.027916-0.18707 0.077516c-0.10335 0.10336-0.10335 0.27079 0 0.37414l1.0584 1.0578c0.10335 0.10335 0.27078 0.10335 0.37414 0l1.0584-1.0578c0.10335-0.10335 0.10335-0.27078 0-0.37414-0.10335-0.10335-0.27078-0.10335-0.37414 0l-0.55967 0.55967v-2.2273h-0.57724z"/></svg>
|
After Width: | Height: | Size: 847 B |
|
@ -1 +0,0 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m11.256 13 .238-1.5h1.481l-.237 1.5h-1.482Z" fill="#8aadf4"/><path d="M17.75 2.001a2.25 2.25 0 0 1 2.245 2.096L20 4.25v15.498a2.25 2.25 0 0 1-2.096 2.245l-.154.005H6.25a2.25 2.25 0 0 1-2.245-2.096L4 19.75V4.251a2.25 2.25 0 0 1 2.096-2.245l.154-.005h11.5Zm-5.355 13.16a.75.75 0 1 0 1.482.234l.142-.895h.731a.75.75 0 0 0 0-1.5h-.494l.238-1.5h.756a.75.75 0 0 0 0-1.5h-.519l.162-1.025a.75.75 0 1 0-1.481-.234l-.2 1.259h-1.48l.161-1.025a.75.75 0 1 0-1.481-.234l-.2 1.259H9.25a.75.75 0 1 0 0 1.5h.725L9.738 13H8.75a.75.75 0 1 0 0 1.5h.75l-.105.66a.75.75 0 0 0 1.482.235l.142-.895H12.5l-.105.66Z" fill="#8aadf4"/></svg>
|
Before Width: | Height: | Size: 716 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m11.475 13.718.083-.071a.75.75 0 0 1 .874-.007l.093.078 6.928 6.8A3.235 3.235 0 0 1 17.75 21H6.25a3.235 3.235 0 0 1-1.703-.481l6.928-6.801.083-.071-.083.07ZM17.75 3A3.25 3.25 0 0 1 21 6.25v11.5c0 .627-.178 1.213-.485 1.71l-6.939-6.813-.128-.116a2.25 2.25 0 0 0-2.889-.006l-.135.123-6.939 6.811A3.235 3.235 0 0 1 3 17.75V6.25A3.25 3.25 0 0 1 6.25 3h11.5Zm-1.998 3a2.252 2.252 0 1 0 0 4.504 2.252 2.252 0 0 0 0-4.504Zm0 1.5a.752.752 0 1 1 0 1.504.752.752 0 0 1 0-1.504Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="m11.475 13.718.083-.071a.75.75 0 0 1 .874-.007l.093.078 6.928 6.8A3.235 3.235 0 0 1 17.75 21H6.25a3.235 3.235 0 0 1-1.703-.481l6.928-6.801.083-.071-.083.07ZM17.75 3A3.25 3.25 0 0 1 21 6.25v11.5c0 .627-.178 1.213-.485 1.71l-6.939-6.813-.128-.116a2.25 2.25 0 0 0-2.889-.006l-.135.123-6.939 6.811A3.235 3.235 0 0 1 3 17.75V6.25A3.25 3.25 0 0 1 6.25 3h11.5Zm-1.998 3a2.252 2.252 0 1 0 0 4.504 2.252 2.252 0 0 0 0-4.504Zm0 1.5a.752.752 0 1 1 0 1.504.752.752 0 0 1 0-1.504Z"/></svg>
|
Before Width: | Height: | Size: 595 B After Width: | Height: | Size: 592 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.502 3.003a3.5 3.5 0 0 0-3.5 3.5v6a3.5 3.5 0 0 0 3.5 3.5H7v-2h-.497a1.5 1.5 0 0 1-1.5-1.5v-6a1.5 1.5 0 0 1 1.5-1.5h6a1.5 1.5 0 0 1 1.5 1.5v6a1.5 1.5 0 0 1-1.5 1.5h-1.506v2h1.506a3.5 3.5 0 0 0 3.5-3.5v-6a3.5 3.5 0 0 0-3.5-3.5h-6Z" fill="#DDDDDD"/><path d="M10 11.5a1.5 1.5 0 0 1 1.5-1.5h1.499V8H11.5A3.5 3.5 0 0 0 8 11.5v6a3.5 3.5 0 0 0 3.5 3.5h6a3.5 3.5 0 0 0 3.5-3.5v-6A3.5 3.5 0 0 0 17.5 8h-.495v2h.495a1.5 1.5 0 0 1 1.5 1.5v6a1.5 1.5 0 0 1-1.5 1.5h-6a1.5 1.5 0 0 1-1.5-1.5v-6Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M6.502 3.003a3.5 3.5 0 0 0-3.5 3.5v6a3.5 3.5 0 0 0 3.5 3.5H7v-2h-.497a1.5 1.5 0 0 1-1.5-1.5v-6a1.5 1.5 0 0 1 1.5-1.5h6a1.5 1.5 0 0 1 1.5 1.5v6a1.5 1.5 0 0 1-1.5 1.5h-1.506v2h1.506a3.5 3.5 0 0 0 3.5-3.5v-6a3.5 3.5 0 0 0-3.5-3.5h-6Z"/><path fill="#DDD" d="M10 11.5a1.5 1.5 0 0 1 1.5-1.5h1.499V8H11.5A3.5 3.5 0 0 0 8 11.5v6a3.5 3.5 0 0 0 3.5 3.5h6a3.5 3.5 0 0 0 3.5-3.5v-6A3.5 3.5 0 0 0 17.5 8h-.495v2h.495a1.5 1.5 0 0 1 1.5 1.5v6a1.5 1.5 0 0 1-1.5 1.5h-6a1.5 1.5 0 0 1-1.5-1.5v-6Z"/></svg>
|
Before Width: | Height: | Size: 609 B After Width: | Height: | Size: 603 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.25 2.75a1.5 1.5 0 0 0-1.5 1.5v15.5a1.5 1.5 0 0 0 1.5 1.5h5.94a6.5 6.5 0 0 1 7.06-10.012V4.25a1.5 1.5 0 0 0-1.5-1.5H6.25Zm2.25 10.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm9 9.75a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11Zm3.5-5.5a.5.5 0 0 1-.5.5h-4.793l1.647 1.646a.5.5 0 0 1-.708.708l-2.5-2.5a.5.5 0 0 1 0-.708l2.5-2.5a.5.5 0 0 1 .708.708L15.707 17H20.5a.5.5 0 0 1 .5.5Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M6.25 2.75a1.5 1.5 0 0 0-1.5 1.5v15.5a1.5 1.5 0 0 0 1.5 1.5h5.94a6.5 6.5 0 0 1 7.06-10.012V4.25a1.5 1.5 0 0 0-1.5-1.5H6.25Zm2.25 10.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3Zm9 9.75a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11Zm3.5-5.5a.5.5 0 0 1-.5.5h-4.793l1.647 1.646a.5.5 0 0 1-.708.708l-2.5-2.5a.5.5 0 0 1 0-.708l2.5-2.5a.5.5 0 0 1 .708.708L15.707 17H20.5a.5.5 0 0 1 .5.5Z"/></svg>
|
Before Width: | Height: | Size: 494 B After Width: | Height: | Size: 491 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M7.207 2.543a1 1 0 0 1 0 1.414L5.414 5.75h7.836a8 8 0 1 1-8 8 1 1 0 1 1 2 0 6 6 0 1 0 6-6H5.414l1.793 1.793a1 1 0 0 1-1.414 1.414l-3.5-3.5a1 1 0 0 1 0-1.414l3.5-3.5a1 1 0 0 1 1.414 0Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M7.207 2.543a1 1 0 0 1 0 1.414L5.414 5.75h7.836a8 8 0 1 1-8 8 1 1 0 1 1 2 0 6 6 0 1 0 6-6H5.414l1.793 1.793a1 1 0 0 1-1.414 1.414l-3.5-3.5a1 1 0 0 1 0-1.414l3.5-3.5a1 1 0 0 1 1.414 0Z"/></svg>
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 308 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M22 8.608v8.142a3.25 3.25 0 0 1-3.066 3.245L18.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75V8.608l9.652 5.056a.75.75 0 0 0 .696 0L22 8.608ZM5.25 4h13.5a3.25 3.25 0 0 1 3.234 2.924L12 12.154l-9.984-5.23a3.25 3.25 0 0 1 3.048-2.919L5.25 4h13.5-13.5Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M22 8.608v8.142a3.25 3.25 0 0 1-3.066 3.245L18.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75V8.608l9.652 5.056a.75.75 0 0 0 .696 0L22 8.608ZM5.25 4h13.5a3.25 3.25 0 0 1 3.234 2.924L12 12.154l-9.984-5.23a3.25 3.25 0 0 1 3.048-2.919L5.25 4h13.5-13.5Z"/></svg>
|
Before Width: | Height: | Size: 377 B After Width: | Height: | Size: 374 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8 12a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM14 12a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM18 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M8 12a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM14 12a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM18 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z"/></svg>
|
Before Width: | Height: | Size: 232 B After Width: | Height: | Size: 229 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6.707 3.293a1 1 0 0 0-1.414 0L4 4.586l-.293-.293a1 1 0 0 0-1.414 1.414l1 1a1 1 0 0 0 1.414 0l2-2a1 1 0 0 0 0-1.414ZM10 16.993h11.003a1 1 0 0 1 .117 1.994l-.117.006H10A1 1 0 0 1 9.883 17l.117-.007ZM10 11h11.003a1 1 0 0 1 .117 1.993l-.117.007H10a1 1 0 0 1-.117-1.993L10 11Zm0-6h11.003a1 1 0 0 1 .117 1.993L21.003 7H10a1 1 0 0 1-.117-1.993L10 5ZM5.293 16.293a1 1 0 0 1 1.414 1.414l-2 2a1 1 0 0 1-1.414 0l-1-1a1 1 0 1 1 1.414-1.414l.293.293 1.293-1.293Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M6.707 3.293a1 1 0 0 0-1.414 0L4 4.586l-.293-.293a1 1 0 0 0-1.414 1.414l1 1a1 1 0 0 0 1.414 0l2-2a1 1 0 0 0 0-1.414ZM10 16.993h11.003a1 1 0 0 1 .117 1.994l-.117.006H10A1 1 0 0 1 9.883 17l.117-.007ZM10 11h11.003a1 1 0 0 1 .117 1.993l-.117.007H10a1 1 0 0 1-.117-1.993L10 11Zm0-6h11.003a1 1 0 0 1 .117 1.993L21.003 7H10a1 1 0 0 1-.117-1.993L10 5ZM5.293 16.293a1 1 0 0 1 1.414 1.414l-2 2a1 1 0 0 1-1.414 0l-1-1a1 1 0 1 1 1.414-1.414l.293.293 1.293-1.293Z"/></svg>
|
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 575 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2.25a.75.75 0 0 0-1.5 0V3.5a2.24 2.24 0 0 0-.841.53L2.78 10.91a2.25 2.25 0 0 0 0 3.182L7.66 18.97a2.25 2.25 0 0 0 3.182 0l6.879-6.879a2.25 2.25 0 0 0 0-3.182L12.84 4.03A2.24 2.24 0 0 0 12 3.5V2.25Zm-1.5 3.06v1.44a.75.75 0 0 0 1.5 0V5.31l4.659 4.66a.75.75 0 0 1 0 1.06l-.97.97H3.812l.029-.03L10.5 5.31ZM19.521 13.602a.874.874 0 0 0-1.542 0l-2.008 3.766C14.85 19.466 16.372 22 18.75 22s3.898-2.534 2.78-4.632l-2.009-3.766Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M12 2.25a.75.75 0 0 0-1.5 0V3.5a2.24 2.24 0 0 0-.841.53L2.78 10.91a2.25 2.25 0 0 0 0 3.182L7.66 18.97a2.25 2.25 0 0 0 3.182 0l6.879-6.879a2.25 2.25 0 0 0 0-3.182L12.84 4.03A2.24 2.24 0 0 0 12 3.5V2.25Zm-1.5 3.06v1.44a.75.75 0 0 0 1.5 0V5.31l4.659 4.66a.75.75 0 0 1 0 1.06l-.97.97H3.812l.029-.03L10.5 5.31ZM19.521 13.602a.874.874 0 0 0-1.542 0l-2.008 3.766C14.85 19.466 16.372 22 18.75 22s3.898-2.534 2.78-4.632l-2.009-3.766Z"/></svg>
|
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 549 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M17.754 14a2.249 2.249 0 0 1 2.25 2.249v.918a2.75 2.75 0 0 1-.513 1.599C17.945 20.929 15.42 22 12 22c-3.422 0-5.945-1.072-7.487-3.237a2.75 2.75 0 0 1-.51-1.595v-.92a2.249 2.249 0 0 1 2.249-2.25h11.501ZM12 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M17.754 14a2.249 2.249 0 0 1 2.25 2.249v.918a2.75 2.75 0 0 1-.513 1.599C17.945 20.929 15.42 22 12 22c-3.422 0-5.945-1.072-7.487-3.237a2.75 2.75 0 0 1-.51-1.595v-.92a2.249 2.249 0 0 1 2.249-2.25h11.501ZM12 2.004a5 5 0 1 1 0 10 5 5 0 0 1 0-10Z"/></svg>
|
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 366 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M10.985 3.165a1 1 0 0 0-1.973-.33l-.86 5.163L3.998 8a1 1 0 1 0 .002 2l3.817-.002-.667 4L3 14a1 1 0 1 0 0 2l3.817-.002-.807 4.838a1 1 0 1 0 1.973.329l.862-5.167 4.975-.003-.806 4.84a1 1 0 1 0 1.972.33l.862-5.17L20 15.992a1 1 0 0 0 0-2l-3.819.001.667-4.001L21 9.99a1 1 0 0 0 0-2l-3.818.002.804-4.827a1 1 0 1 0-1.972-.33l-.86 5.159-4.975.003.806-4.832Zm-1.14 6.832 4.976-.003-.667 4.001-4.976.002.667-4Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M10.985 3.165a1 1 0 0 0-1.973-.33l-.86 5.163L3.998 8a1 1 0 1 0 .002 2l3.817-.002-.667 4L3 14a1 1 0 1 0 0 2l3.817-.002-.807 4.838a1 1 0 1 0 1.973.329l.862-5.167 4.975-.003-.806 4.84a1 1 0 1 0 1.972.33l.862-5.17L20 15.992a1 1 0 0 0 0-2l-3.819.001.667-4.001L21 9.99a1 1 0 0 0 0-2l-3.818.002.804-4.827a1 1 0 1 0-1.972-.33l-.86 5.159-4.975.003.806-4.832Zm-1.14 6.832 4.976-.003-.667 4.001-4.976.002.667-4Z"/></svg>
|
Before Width: | Height: | Size: 528 B After Width: | Height: | Size: 525 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M2.22 2.22a.75.75 0 0 0-.073.976l.073.084 4.034 4.035a9.986 9.986 0 0 0-3.955 5.75.75.75 0 0 0 1.455.364 8.49 8.49 0 0 1 3.58-5.034l1.81 1.81A4 4 0 0 0 14.8 15.86l5.919 5.92a.75.75 0 0 0 1.133-.977l-.073-.084-6.113-6.114.001-.002-6.95-6.946.002-.002-1.133-1.13L3.28 2.22a.75.75 0 0 0-1.06 0ZM12 5.5c-1 0-1.97.148-2.889.425l1.237 1.236a8.503 8.503 0 0 1 9.899 6.272.75.75 0 0 0 1.455-.363A10.003 10.003 0 0 0 12 5.5Zm.195 3.51 3.801 3.8a4.003 4.003 0 0 0-3.801-3.8Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M2.22 2.22a.75.75 0 0 0-.073.976l.073.084 4.034 4.035a9.986 9.986 0 0 0-3.955 5.75.75.75 0 0 0 1.455.364 8.49 8.49 0 0 1 3.58-5.034l1.81 1.81A4 4 0 0 0 14.8 15.86l5.919 5.92a.75.75 0 0 0 1.133-.977l-.073-.084-6.113-6.114.001-.002-6.95-6.946.002-.002-1.133-1.13L3.28 2.22a.75.75 0 0 0-1.06 0ZM12 5.5c-1 0-1.97.148-2.889.425l1.237 1.236a8.503 8.503 0 0 1 9.899 6.272.75.75 0 0 0 1.455-.363A10.003 10.003 0 0 0 12 5.5Zm.195 3.51 3.801 3.8a4.003 4.003 0 0 0-3.801-3.8Z"/></svg>
|
Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 589 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 9.005a4 4 0 1 1 0 8 4 4 0 0 1 0-8ZM12 5.5c4.613 0 8.596 3.15 9.701 7.564a.75.75 0 1 1-1.455.365 8.503 8.503 0 0 0-16.493.004.75.75 0 0 1-1.455-.363A10.003 10.003 0 0 1 12 5.5Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M12 9.005a4 4 0 1 1 0 8 4 4 0 0 1 0-8ZM12 5.5c4.613 0 8.596 3.15 9.701 7.564a.75.75 0 1 1-1.455.365 8.503 8.503 0 0 0-16.493.004.75.75 0 0 1-1.455-.363A10.003 10.003 0 0 1 12 5.5Z"/></svg>
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 304 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10Zm3.27-11.25H14a.75.75 0 0 0 0 1.5h2.75a.75.75 0 0 0 .75-.75V8.25a.75.75 0 0 0-1.5 0V9a4.991 4.991 0 0 0-4-2c-1.537 0-2.904.66-3.827 1.77a.75.75 0 0 0 1.154.96C9.963 8.963 10.907 8.5 12 8.5c1.492 0 2.767.934 3.27 2.25Zm-7.27 5V15a5.013 5.013 0 0 0 7.821.237.75.75 0 1 0-1.142-.972 3.513 3.513 0 0 1-5.842-.765H10a.75.75 0 0 0 0-1.5H7.25a.75.75 0 0 0-.75.75v3a.75.75 0 0 0 1.5 0Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10Zm3.27-11.25H14a.75.75 0 0 0 0 1.5h2.75a.75.75 0 0 0 .75-.75V8.25a.75.75 0 0 0-1.5 0V9a4.991 4.991 0 0 0-4-2c-1.537 0-2.904.66-3.827 1.77a.75.75 0 0 0 1.154.96C9.963 8.963 10.907 8.5 12 8.5c1.492 0 2.767.934 3.27 2.25Zm-7.27 5V15a5.013 5.013 0 0 0 7.821.237.75.75 0 1 0-1.142-.972 3.513 3.513 0 0 1-5.842-.765H10a.75.75 0 0 0 0-1.5H7.25a.75.75 0 0 0-.75.75v3a.75.75 0 0 0 1.5 0Z"/></svg>
|
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 575 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M21.25 13a.75.75 0 0 1 .743.648l.007.102v5a3.25 3.25 0 0 1-3.066 3.245L18.75 22h-4.668c.536-.385.973-.9 1.265-1.499l3.403-.001a1.75 1.75 0 0 0 1.744-1.607l.006-.143v-5a.75.75 0 0 1 .75-.75ZM9.447 17.165l.114.103 4.085 4.086.023.019A3.235 3.235 0 0 1 11.75 22h-6.5a3.235 3.235 0 0 1-1.92-.627l.024-.02 4.085-4.085.114-.103a1.5 1.5 0 0 1 1.894 0ZM11.75 9A3.25 3.25 0 0 1 15 12.25v6.5c0 .718-.233 1.382-.627 1.92l-.02-.024-4.085-4.085-.13-.122a2.5 2.5 0 0 0-3.269-.006l-.137.128-4.086 4.085-.019.023A3.235 3.235 0 0 1 2 18.75v-6.5A3.25 3.25 0 0 1 5.25 9h6.5ZM11 12a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm7.75-10a3.25 3.25 0 0 1 3.245 3.066L22 5.25v5a.75.75 0 0 1-1.493.102l-.007-.102v-5a1.75 1.75 0 0 0-1.606-1.744L18.75 3.5h-5a.75.75 0 0 1-.102-1.493L13.75 2h5Zm-8.5 0a.75.75 0 0 1 .102 1.493l-.102.007h-5a1.75 1.75 0 0 0-1.744 1.606L3.5 5.25v3.402c-.6.292-1.114.73-1.5 1.266V5.25a3.25 3.25 0 0 1 3.066-3.245L5.25 2h5Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M21.25 13a.75.75 0 0 1 .743.648l.007.102v5a3.25 3.25 0 0 1-3.066 3.245L18.75 22h-4.668c.536-.385.973-.9 1.265-1.499l3.403-.001a1.75 1.75 0 0 0 1.744-1.607l.006-.143v-5a.75.75 0 0 1 .75-.75ZM9.447 17.165l.114.103 4.085 4.086.023.019A3.235 3.235 0 0 1 11.75 22h-6.5a3.235 3.235 0 0 1-1.92-.627l.024-.02 4.085-4.085.114-.103a1.5 1.5 0 0 1 1.894 0ZM11.75 9A3.25 3.25 0 0 1 15 12.25v6.5c0 .718-.233 1.382-.627 1.92l-.02-.024-4.085-4.085-.13-.122a2.5 2.5 0 0 0-3.269-.006l-.137.128-4.086 4.085-.019.023A3.235 3.235 0 0 1 2 18.75v-6.5A3.25 3.25 0 0 1 5.25 9h6.5ZM11 12a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm7.75-10a3.25 3.25 0 0 1 3.245 3.066L22 5.25v5a.75.75 0 0 1-1.493.102l-.007-.102v-5a1.75 1.75 0 0 0-1.606-1.744L18.75 3.5h-5a.75.75 0 0 1-.102-1.493L13.75 2h5Zm-8.5 0a.75.75 0 0 1 .102 1.493l-.102.007h-5a1.75 1.75 0 0 0-1.744 1.606L3.5 5.25v3.402c-.6.292-1.114.73-1.5 1.266V5.25a3.25 3.25 0 0 1 3.066-3.245L5.25 2h5Z"/></svg>
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M19.75 2A2.25 2.25 0 0 1 22 4.25v5.462a3.25 3.25 0 0 1-.952 2.298l-8.5 8.503a3.255 3.255 0 0 1-4.597.001L3.489 16.06a3.25 3.25 0 0 1-.003-4.596l8.5-8.51A3.25 3.25 0 0 1 14.284 2h5.465ZM17 5.502a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M19.75 2A2.25 2.25 0 0 1 22 4.25v5.462a3.25 3.25 0 0 1-.952 2.298l-8.5 8.503a3.255 3.255 0 0 1-4.597.001L3.489 16.06a3.25 3.25 0 0 1-.003-4.596l8.5-8.51A3.25 3.25 0 0 1 14.284 2h5.465ZM17 5.502a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"/></svg>
|
Before Width: | Height: | Size: 358 B After Width: | Height: | Size: 355 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M19.75 2A2.25 2.25 0 0 1 22 4.25v5.462a3.25 3.25 0 0 1-.952 2.298l-.026.026a6.5 6.5 0 0 0-9.028 8.92 3.256 3.256 0 0 1-4.043-.442L3.489 16.06a3.25 3.25 0 0 1-.004-4.596l8.5-8.51a3.25 3.25 0 0 1 2.3-.953h5.465ZM17 5.502a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3ZM23 17.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0Zm-7.147-2.354a.5.5 0 0 0-.707.708l1.647 1.646-1.647 1.646a.5.5 0 0 0 .707.708l1.647-1.647 1.646 1.647a.5.5 0 0 0 .707-.708L18.207 17.5l1.646-1.646a.5.5 0 0 0-.707-.708L17.5 16.793l-1.647-1.647Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M19.75 2A2.25 2.25 0 0 1 22 4.25v5.462a3.25 3.25 0 0 1-.952 2.298l-.026.026a6.5 6.5 0 0 0-9.028 8.92 3.256 3.256 0 0 1-4.043-.442L3.489 16.06a3.25 3.25 0 0 1-.004-4.596l8.5-8.51a3.25 3.25 0 0 1 2.3-.953h5.465ZM17 5.502a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3ZM23 17.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0Zm-7.147-2.354a.5.5 0 0 0-.707.708l1.647 1.646-1.647 1.646a.5.5 0 0 0 .707.708l1.647-1.647 1.646 1.647a.5.5 0 0 0 .707-.708L18.207 17.5l1.646-1.646a.5.5 0 0 0-.707-.708L17.5 16.793l-1.647-1.647Z"/></svg>
|
Before Width: | Height: | Size: 623 B After Width: | Height: | Size: 620 B |
|
@ -1 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M22 12.001c0-5.523-4.476-10-10-10-5.522 0-10 4.477-10 10s4.478 10 10 10c5.524 0 10-4.477 10-10Zm-14.53.28a.75.75 0 0 1-.073-.976l.073-.085 4-4a.75.75 0 0 1 .977-.073l.085.073 4 4.001a.75.75 0 0 1-.977 1.133l-.084-.072-2.72-2.722v6.691a.75.75 0 0 1-.649.744L12 17a.75.75 0 0 1-.743-.648l-.007-.102v-6.69l-2.72 2.72a.75.75 0 0 1-.976.073l-.084-.073Z" fill="#DDDDDD"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><path fill="#DDD" d="M22 12.001c0-5.523-4.476-10-10-10-5.522 0-10 4.477-10 10s4.478 10 10 10c5.524 0 10-4.477 10-10Zm-14.53.28a.75.75 0 0 1-.073-.976l.073-.085 4-4a.75.75 0 0 1 .977-.073l.085.073 4 4.001a.75.75 0 0 1-.977 1.133l-.084-.072-2.72-2.722v6.691a.75.75 0 0 1-.649.744L12 17a.75.75 0 0 1-.743-.648l-.007-.102v-6.69l-2.72 2.72a.75.75 0 0 1-.976.073l-.084-.073Z"/></svg>
|
Before Width: | Height: | Size: 475 B After Width: | Height: | Size: 472 B |
Before Width: | Height: | Size: 216 KiB |
1
assets/monofileLogo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="15.009" version="1.1" viewBox="0 0 3.175 3.971"><defs><linearGradient id="gradient"><stop offset="0" stop-color="#fff" stop-opacity="0"/><stop offset=".5" stop-color="#fff"/></linearGradient></defs><title>monofile</title><path fill="#fff" d="m0 3.442"/><path fill="url(#gradient)" d="m0 3.442v.52917h3.175v-.52917z"/><path fill="#fff" d="m1.3219 0-0.56845 0.34727v0.53228l0.5054-0.30231h0.013436v1.6464l-0.55605-0.55605c-0.04961-0.0496-0.11679-0.077516-0.18707-0.077516s-0.13746 0.027916-0.18707 0.077516c-0.10335 0.10336-0.10335 0.27079 0 0.37414l1.0584 1.0578c0.10335 0.10335 0.27078 0.10335 0.37414 0l1.0584-1.0578c0.10335-0.10335 0.10335-0.27078 0-0.37414-0.10335-0.10335-0.27078-0.10335-0.37414 0l-0.55967 0.55967v-2.2273h-0.57724z"/></svg>
|
After Width: | Height: | Size: 847 B |
|
@ -11,10 +11,6 @@
|
|||
"requiredForUpload": false
|
||||
},
|
||||
|
||||
"webdrop": {
|
||||
"accountRequired": false
|
||||
},
|
||||
|
||||
"mail": {
|
||||
"transport": {
|
||||
"host": "smtp.fastmail.com",
|
||||
|
@ -28,4 +24,4 @@
|
|||
|
||||
"trustProxy": true,
|
||||
"forceSSL": true
|
||||
}
|
||||
}
|
||||
|
|
39
package-lock.json
generated
|
@ -1,17 +1,14 @@
|
|||
{
|
||||
"name": "monofile",
|
||||
"version": "1.3.0-beta",
|
||||
"version": "1.4.0-dev",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "monofile",
|
||||
"version": "1.3.0-beta",
|
||||
"version": "1.4.0-dev",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@fontsource/fira-code": "^5.0.8",
|
||||
"@fontsource/inconsolata": "^5.0.8",
|
||||
"@fontsource/source-sans-pro": "^5.0.8",
|
||||
"@types/body-parser": "^1.19.2",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/multer": "^1.4.7",
|
||||
|
@ -37,7 +34,7 @@
|
|||
"svelte": "^3.55.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=v18"
|
||||
"node": ">=v16.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/builders": {
|
||||
|
@ -90,21 +87,6 @@
|
|||
"node": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource/fira-code": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/fira-code/-/fira-code-5.0.8.tgz",
|
||||
"integrity": "sha512-kp/tJUVnjaZeLHENMBFTTSgP2B7+/rIboeofuMfoGB40s2U0DKXNqQcOqIF5PtDhJ5QTG1LcviYXMnc1yG6oYQ=="
|
||||
},
|
||||
"node_modules/@fontsource/inconsolata": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/inconsolata/-/inconsolata-5.0.8.tgz",
|
||||
"integrity": "sha512-KpBU6q1yCovfycaFprVEauh8U5RsWty3konFfUukyRRxZBK4Sf73XmGQc8iJ4CPrOP4dplGfdX2kjbRgdymajA=="
|
||||
},
|
||||
"node_modules/@fontsource/source-sans-pro": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/source-sans-pro/-/source-sans-pro-5.0.8.tgz",
|
||||
"integrity": "sha512-5U2UvIYRkCMozZ388gCE73PEpa2MFgN/0t9O4a1FF7bGT/MIneQWSL1XpWZ8iMVYdh6ntxRf3iFA6slCIuFgkg=="
|
||||
},
|
||||
"node_modules/@rollup/plugin-node-resolve": {
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz",
|
||||
|
@ -1750,21 +1732,6 @@
|
|||
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz",
|
||||
"integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ=="
|
||||
},
|
||||
"@fontsource/fira-code": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/fira-code/-/fira-code-5.0.8.tgz",
|
||||
"integrity": "sha512-kp/tJUVnjaZeLHENMBFTTSgP2B7+/rIboeofuMfoGB40s2U0DKXNqQcOqIF5PtDhJ5QTG1LcviYXMnc1yG6oYQ=="
|
||||
},
|
||||
"@fontsource/inconsolata": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/inconsolata/-/inconsolata-5.0.8.tgz",
|
||||
"integrity": "sha512-KpBU6q1yCovfycaFprVEauh8U5RsWty3konFfUukyRRxZBK4Sf73XmGQc8iJ4CPrOP4dplGfdX2kjbRgdymajA=="
|
||||
},
|
||||
"@fontsource/source-sans-pro": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/source-sans-pro/-/source-sans-pro-5.0.8.tgz",
|
||||
"integrity": "sha512-5U2UvIYRkCMozZ388gCE73PEpa2MFgN/0t9O4a1FF7bGT/MIneQWSL1XpWZ8iMVYdh6ntxRf3iFA6slCIuFgkg=="
|
||||
},
|
||||
"@rollup/plugin-node-resolve": {
|
||||
"version": "15.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz",
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
{
|
||||
"name": "monofile",
|
||||
"version": "1.3.4",
|
||||
"version": "2.0.0-dev",
|
||||
"description": "Discord-based file sharing",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node ./out/server/index.js",
|
||||
"build": "tsc\nsass src/style:out/style\nrollup -c",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "nbitzz",
|
||||
"author": "Etcetera (https://cetera.uk)",
|
||||
"license": "Unlicense",
|
||||
"engines": {
|
||||
"node": ">=v16.11"
|
||||
|
|
|
@ -14,10 +14,15 @@
|
|||
href="/static/style/app.css"
|
||||
>
|
||||
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
href="/static/assets/apple-touch-icon.png"
|
||||
>
|
||||
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/svg"
|
||||
href="/static/assets/icons/icon_temp.svg"
|
||||
href="/static/assets/icons/icon.svg"
|
||||
>
|
||||
|
||||
<link
|
||||
|
@ -37,6 +42,9 @@
|
|||
<meta name="title" content="monofile">
|
||||
<meta name="description" content="The open-source Discord-based file sharing service">
|
||||
<meta name="theme-color" content="rgb(30, 33, 36)">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="image" content="/static/assets/banner.png">
|
||||
<meta name="og:image" content="/static/assets/banner.png">
|
||||
|
||||
</head>
|
||||
|
||||
|
@ -44,4 +52,4 @@
|
|||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -13,6 +13,7 @@ import * as authRoutes from "./routes/authRoutes";
|
|||
import * as fileApiRoutes from "./routes/fileApiRoutes";
|
||||
import * as adminRoutes from "./routes/adminRoutes";
|
||||
import * as primaryApi from "./routes/primaryApi";
|
||||
import { getAccount } from "./lib/middleware";
|
||||
|
||||
require("dotenv").config()
|
||||
|
||||
|
@ -82,11 +83,14 @@ app.get("/", function(req,res) {
|
|||
|
||||
// serve download page
|
||||
|
||||
app.get("/download/:fileId",(req,res) => {
|
||||
app.get("/download/:fileId", getAccount, (req,res) => {
|
||||
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (files.getFilePointer(req.params.fileId)) {
|
||||
let file = files.getFilePointer(req.params.fileId)
|
||||
|
||||
if (file.visibility == "private" && Accounts.getFromToken(req.cookies.auth)?.id != file.owner) {
|
||||
if (file.visibility == "private" && acc?.id != file.owner) {
|
||||
ServeError(res,403,"you do not own this file")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -27,6 +27,14 @@ export interface Account {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create a new account.
|
||||
* @param username New account's username
|
||||
* @param pwd New account's password
|
||||
* @param admin Whether or not the account should have administrative rights
|
||||
* @returns A Promise which returns the new account's ID
|
||||
*/
|
||||
|
||||
export function create(username:string,pwd:string,admin:boolean=false):Promise<string> {
|
||||
return new Promise((resolve,reject) => {
|
||||
let accId = crypto.randomBytes(12).toString("hex")
|
||||
|
@ -46,26 +54,52 @@ export function create(username:string,pwd:string,admin:boolean=false):Promise<s
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Gets an account from its username.
|
||||
* @param id The target account's username
|
||||
* @returns An Account, if it exists
|
||||
*/
|
||||
export function getFromUsername(username:string) {
|
||||
return Accounts.find(e => e.username == username)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Gets an account from its ID.
|
||||
* @param id The target account's ID
|
||||
* @returns An Account, if it exists
|
||||
*/
|
||||
export function getFromId(id:string) {
|
||||
return Accounts.find(e => e.id == id)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Gets an account from an AuthToken. Equivalent to getFromId(auth.validate(token)).
|
||||
* @param token A valid AuthToken
|
||||
* @returns An Account, if the token is valid
|
||||
*/
|
||||
export function getFromToken(token:string) {
|
||||
let accId = auth.validate(token)
|
||||
if (!accId) return
|
||||
return getFromId(accId)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Deletes an account.
|
||||
* @param id The target account's ID
|
||||
*/
|
||||
export function deleteAccount(id:string) {
|
||||
Accounts.splice(Accounts.findIndex(e => e.id == id),1)
|
||||
return save()
|
||||
}
|
||||
|
||||
export namespace password {
|
||||
|
||||
/**
|
||||
* @description Generates a hashed and salted version of an input password.
|
||||
* @param password Target password.
|
||||
* @param _salt Designated password salt. Use to validate a password.
|
||||
*/
|
||||
|
||||
export function hash(password:string,_salt?:string) {
|
||||
let salt = _salt || crypto.randomBytes(12).toString('base64')
|
||||
let hash = crypto.createHash('sha256').update(`${salt}${password}`).digest('hex')
|
||||
|
@ -75,6 +109,12 @@ export namespace password {
|
|||
hash:hash
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Sets an account's password.
|
||||
* @param id The target account's ID
|
||||
* @param password New password
|
||||
*/
|
||||
|
||||
export function set(id:string,password:string) {
|
||||
let acc = Accounts.find(e => e.id == id)
|
||||
|
@ -84,6 +124,12 @@ export namespace password {
|
|||
return save()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description Tests a password against an account.
|
||||
* @param id The target account's ID
|
||||
* @param password Password to check
|
||||
*/
|
||||
export function check(id:string,password:string) {
|
||||
let acc = Accounts.find(e => e.id == id)
|
||||
if (!acc) return
|
||||
|
@ -93,6 +139,12 @@ export namespace password {
|
|||
}
|
||||
|
||||
export namespace files {
|
||||
/**
|
||||
* @description Adds a file to an account's file index
|
||||
* @param accountId The target account's ID
|
||||
* @param fileId The target file's ID
|
||||
* @returns Promise that resolves after accounts.json finishes writing
|
||||
*/
|
||||
export function index(accountId:string,fileId:string) {
|
||||
// maybe replace with a obj like
|
||||
// { x:true }
|
||||
|
@ -105,17 +157,28 @@ export namespace files {
|
|||
return save()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Removes a file from an account's file index
|
||||
* @param accountId The target account's ID
|
||||
* @param fileId The target file's ID
|
||||
* @param noWrite Whether or not accounts.json should save
|
||||
* @returns A Promise which resolves when accounts.json finishes writing, if `noWrite` is `false`
|
||||
*/
|
||||
export function deindex(accountId:string,fileId:string, noWrite:boolean=false) {
|
||||
let acc = Accounts.find(e => e.id == accountId)
|
||||
if (!acc) return
|
||||
let fi = acc.files.findIndex(e => e == fileId)
|
||||
if (fi) {
|
||||
if (fi >= 0) {
|
||||
acc.files.splice(fi,1)
|
||||
if (!noWrite) return save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Saves accounts.json
|
||||
* @returns A promise which resolves when accounts.json finishes writing
|
||||
*/
|
||||
export function save() {
|
||||
return writeFile(`${process.cwd()}/.data/accounts.json`,JSON.stringify(Accounts))
|
||||
.catch((err) => console.error(err))
|
||||
|
|
|
@ -1,19 +1,46 @@
|
|||
import crypto from "crypto"
|
||||
import express from "express"
|
||||
import { readFile, writeFile } from "fs/promises"
|
||||
export let AuthTokens: AuthToken[] = []
|
||||
export let AuthTokenTO:{[key:string]:NodeJS.Timeout} = {}
|
||||
|
||||
export const ValidTokenPermissions = [
|
||||
"user", // permissions to /auth/me, with email docked
|
||||
"email", // adds email back to /auth/me
|
||||
"private", // allows app to read private files
|
||||
"upload", // allows an app to upload under an account
|
||||
"manage", // allows an app to manage an account's files
|
||||
"customize", // allows an app to change customization settings
|
||||
"admin" // only available for accounts with admin
|
||||
// gives an app access to all admin tools
|
||||
] as const
|
||||
|
||||
export type TokenType = "User" | "App"
|
||||
export type TokenPermission = typeof ValidTokenPermissions[number]
|
||||
|
||||
export interface AuthToken {
|
||||
account: string,
|
||||
token: string,
|
||||
expire: number
|
||||
expire: number,
|
||||
|
||||
type?: TokenType, // if !type, assume User
|
||||
tokenPermissions?: TokenPermission[] // default to user if type is App,
|
||||
// give full permissions if type is User
|
||||
}
|
||||
|
||||
export function create(id:string,expire:number=(24*60*60*1000)) {
|
||||
export function create(
|
||||
id:string,
|
||||
expire:number=(24*60*60*1000),
|
||||
type:TokenType="User",
|
||||
tokenPermissions?:TokenPermission[]
|
||||
) {
|
||||
let token = {
|
||||
account:id,
|
||||
token:crypto.randomBytes(12).toString('hex'),
|
||||
expire:Date.now()+expire
|
||||
token:crypto.randomBytes(36).toString('hex'),
|
||||
expire: expire ? Date.now()+expire : 0,
|
||||
|
||||
type,
|
||||
tokenPermissions: type == "App" ? tokenPermissions || ["user"] : undefined
|
||||
}
|
||||
|
||||
AuthTokens.push(token)
|
||||
|
@ -24,11 +51,32 @@ export function create(id:string,expire:number=(24*60*60*1000)) {
|
|||
return token.token
|
||||
}
|
||||
|
||||
export function tokenFor(req: express.Request) {
|
||||
return req.cookies.auth || (
|
||||
req.header("authorization")?.startsWith("Bearer ")
|
||||
? req.header("authorization")?.split(" ")[1]
|
||||
: undefined
|
||||
)
|
||||
}
|
||||
|
||||
function getToken(token:string) {
|
||||
return AuthTokens.find(e => e.token == token && (e.expire == 0 || Date.now() < e.expire))
|
||||
}
|
||||
|
||||
export function validate(token:string) {
|
||||
return AuthTokens.find(e => e.token == token && Date.now() < e.expire)?.account
|
||||
return getToken(token)?.account
|
||||
}
|
||||
|
||||
export function getType(token:string): TokenType | undefined {
|
||||
return getToken(token)?.type
|
||||
}
|
||||
|
||||
export function getPermissions(token:string): TokenPermission[] | undefined {
|
||||
return getToken(token)?.tokenPermissions
|
||||
}
|
||||
|
||||
export function tokenTimer(token:AuthToken) {
|
||||
if (!token.expire) return // justincase
|
||||
if (Date.now() >= token.expire) {
|
||||
invalidate(token.token)
|
||||
return
|
||||
|
|
|
@ -3,6 +3,12 @@ import { readFile } from "fs/promises"
|
|||
|
||||
let errorPage:string
|
||||
|
||||
/**
|
||||
* @description Serves an error as a response to a request with an error page attached
|
||||
* @param res Express response object
|
||||
* @param code Error code
|
||||
* @param reason Error reason
|
||||
*/
|
||||
export default async function ServeError(
|
||||
res:Response,
|
||||
code:number,
|
||||
|
@ -29,7 +35,12 @@ export default async function ServeError(
|
|||
.replace(/\$text/g,reason)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Redirects a user to another page.
|
||||
* @param res Express response object
|
||||
* @param url Target URL
|
||||
* @deprecated Use `res.redirect` instead.
|
||||
*/
|
||||
export function Redirect(res:Response,url:string) {
|
||||
res.status(302)
|
||||
res.header("Location",url)
|
||||
|
|
|
@ -2,21 +2,27 @@ import axios from "axios";
|
|||
import Discord, { Client, TextBasedChannel } from "discord.js";
|
||||
import { readFile, writeFile } from "fs";
|
||||
import { Readable } from "node:stream";
|
||||
import crypto from "node:crypto";
|
||||
import { files } from "./accounts";
|
||||
|
||||
import * as Accounts from "./accounts";
|
||||
|
||||
export let id_check_regex = /[A-Za-z0-9_\-\.\!\=\:]+/
|
||||
export let id_check_regex = /[A-Za-z0-9_\-\.\!\=\:\&\$\,\+\;\@\~\*\(\)\']+/
|
||||
export let alphanum = Array.from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
|
||||
|
||||
// bad solution but whatever
|
||||
|
||||
export type FileVisibility = "public" | "anonymous" | "private"
|
||||
|
||||
/**
|
||||
* @description Generates an alphanumeric string, used for files
|
||||
* @param length Length of the ID
|
||||
* @returns a random alphanumeric string
|
||||
*/
|
||||
export function generateFileId(length:number=5) {
|
||||
let fid = ""
|
||||
for (let i = 0; i < length; i++) {
|
||||
fid += alphanum[Math.floor(Math.random()*alphanum.length)]
|
||||
fid += alphanum[crypto.randomInt(0,alphanum.length)]
|
||||
}
|
||||
return fid
|
||||
}
|
||||
|
@ -39,7 +45,10 @@ export interface Configuration {
|
|||
accounts: {
|
||||
registrationEnabled: boolean,
|
||||
requiredForUpload: boolean
|
||||
}
|
||||
},
|
||||
|
||||
trustProxy: boolean,
|
||||
forceSSL: boolean
|
||||
}
|
||||
|
||||
export interface FilePointer {
|
||||
|
@ -92,6 +101,12 @@ export default class Files {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Uploads a new file
|
||||
* @param settings Settings for your new upload
|
||||
* @param fBuffer Buffer containing file content
|
||||
* @returns Promise which resolves to the ID of the new file
|
||||
*/
|
||||
uploadFile(settings:FileUploadSettings,fBuffer:Buffer):Promise<string|StatusCodeError> {
|
||||
return new Promise<string>(async (resolve,reject) => {
|
||||
if (!this.uploadChannel) {
|
||||
|
@ -243,6 +258,12 @@ export default class Files {
|
|||
|
||||
// fs
|
||||
|
||||
/**
|
||||
* @description Writes a file to disk
|
||||
* @param uploadId New file's ID
|
||||
* @param file FilePointer representing the new file
|
||||
* @returns Promise which resolves to the file's ID
|
||||
*/
|
||||
writeFile(uploadId: string, file: FilePointer):Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
|
@ -263,8 +284,12 @@ export default class Files {
|
|||
})
|
||||
}
|
||||
|
||||
// todo: move read code here
|
||||
|
||||
/**
|
||||
* @description Read a file
|
||||
* @param uploadId Target file's ID
|
||||
* @param range Byte range to get
|
||||
* @returns A `Readable` containing the file's contents
|
||||
*/
|
||||
readFileStream(uploadId: string, range?: {start:number, end:number}):Promise<Readable> {
|
||||
return new Promise(async (resolve,reject) => {
|
||||
if (!this.uploadChannel) {
|
||||
|
@ -400,6 +425,11 @@ export default class Files {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Deletes a file
|
||||
* @param uploadId Target file's ID
|
||||
* @param noWrite Whether or not the change should be written to disk. Enable for bulk deletes
|
||||
*/
|
||||
unlink(uploadId:string, noWrite: boolean = false):Promise<void> {
|
||||
return new Promise(async (resolve,reject) => {
|
||||
let tmp = this.files[uploadId];
|
||||
|
@ -430,6 +460,11 @@ export default class Files {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get a file's FilePointer
|
||||
* @param uploadId Target file's ID
|
||||
* @returns FilePointer for the file
|
||||
*/
|
||||
getFilePointer(uploadId:string):FilePointer {
|
||||
return this.files[uploadId]
|
||||
}
|
||||
|
|
|
@ -19,6 +19,13 @@ transport =
|
|||
|
||||
// lazy but
|
||||
|
||||
/**
|
||||
* @description Sends an email
|
||||
* @param to Target email address
|
||||
* @param subject Email subject
|
||||
* @param content Email content
|
||||
* @returns Promise which resolves to the output from nodemailer.transport.sendMail
|
||||
*/
|
||||
export function sendMail(to: string, subject: string, content: string) {
|
||||
return new Promise((resolve,reject) => {
|
||||
transport.sendMail({
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
import * as Accounts from "./accounts";
|
||||
import express, { type RequestHandler } from "express"
|
||||
import ServeError from "../lib/errors";
|
||||
import * as auth from "./auth";
|
||||
|
||||
export let getAccount: RequestHandler = function(req, res, next) {
|
||||
res.locals.acc = Accounts.getFromToken(req.cookies.auth)
|
||||
/**
|
||||
* @description Middleware which adds an account, if any, to res.locals.acc
|
||||
*/
|
||||
export const getAccount: RequestHandler = function(req, res, next) {
|
||||
res.locals.acc = Accounts.getFromToken(auth.tokenFor(req))
|
||||
next()
|
||||
}
|
||||
|
||||
export let requiresAccount: RequestHandler = function(_req, res, next) {
|
||||
/**
|
||||
* @description Middleware which blocks requests which do not have res.locals.acc set
|
||||
*/
|
||||
export const requiresAccount: RequestHandler = function(_req, res, next) {
|
||||
if (!res.locals.acc) {
|
||||
ServeError(res, 401, "not logged in")
|
||||
return
|
||||
|
@ -15,10 +22,52 @@ export let requiresAccount: RequestHandler = function(_req, res, next) {
|
|||
next()
|
||||
}
|
||||
|
||||
export let requiresAdmin: RequestHandler = function(_req, res, next) {
|
||||
/**
|
||||
* @description Middleware which blocks requests that have res.locals.acc.admin set to a falsy value
|
||||
*/
|
||||
export const requiresAdmin: RequestHandler = function(_req, res, next) {
|
||||
if (!res.locals.acc.admin) {
|
||||
ServeError(res, 403, "you are not an administrator")
|
||||
return
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Blocks requests based on the permissions which a token has. Does not apply to routes being accessed with a token of type `User`
|
||||
* @param tokenPermissions Permissions which your route requires.
|
||||
* @returns Express middleware
|
||||
*/
|
||||
|
||||
export const requiresPermissions = function(...tokenPermissions: auth.TokenPermission[]): RequestHandler {
|
||||
return function(req, res, next) {
|
||||
let token = auth.tokenFor(req)
|
||||
let type = auth.getType(token)
|
||||
|
||||
if (type == "App") {
|
||||
let permissions = auth.getPermissions(token)
|
||||
|
||||
if (!permissions) ServeError(res, 403, "insufficient permissions")
|
||||
else {
|
||||
|
||||
for (let v of tokenPermissions) {
|
||||
if (!permissions.includes(v as auth.TokenPermission)) {
|
||||
ServeError(res,403,"insufficient permissions")
|
||||
return
|
||||
}
|
||||
}
|
||||
next()
|
||||
|
||||
}
|
||||
} else next()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Blocks requests based on whether or not the token being used to access the route is of type `User`.
|
||||
*/
|
||||
|
||||
export const noAPIAccess: RequestHandler = function(req, res, next) {
|
||||
if (auth.getType(auth.tokenFor(req)) == "App") ServeError(res, 403, "apps are not allowed to access this endpoint")
|
||||
else next()
|
||||
}
|
|
@ -2,14 +2,19 @@ import { RequestHandler } from "express"
|
|||
import { type Account } from "./accounts"
|
||||
import ServeError from "./errors"
|
||||
|
||||
interface ratelimitSettings {
|
||||
interface RatelimitSettings {
|
||||
|
||||
requests: number
|
||||
per: number
|
||||
|
||||
}
|
||||
|
||||
export function accountRatelimit( settings: ratelimitSettings ): RequestHandler {
|
||||
/**
|
||||
* @description Ratelimits a route based on res.locals.acc
|
||||
* @param settings Ratelimit settings
|
||||
* @returns Express middleware
|
||||
*/
|
||||
export function accountRatelimit( settings: RatelimitSettings ): RequestHandler {
|
||||
let activeLimits: {
|
||||
[ key: string ]: {
|
||||
requests: number,
|
||||
|
|
|
@ -5,7 +5,7 @@ import * as auth from "../lib/auth";
|
|||
import bytes from "bytes"
|
||||
import {writeFile} from "fs";
|
||||
import { sendMail } from "../lib/mail";
|
||||
import { getAccount, requiresAccount, requiresAdmin } from "../lib/middleware"
|
||||
import { getAccount, requiresAccount, requiresAdmin, requiresPermissions } from "../lib/middleware"
|
||||
|
||||
import ServeError from "../lib/errors";
|
||||
import Files from "../lib/files";
|
||||
|
@ -19,6 +19,7 @@ adminRoutes
|
|||
.use(getAccount)
|
||||
.use(requiresAccount)
|
||||
.use(requiresAdmin)
|
||||
.use(requiresPermissions("admin"))
|
||||
let files:Files
|
||||
|
||||
export function setFilesObj(newFiles:Files) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Router } from "express";
|
|||
import * as Accounts from "../lib/accounts";
|
||||
import * as auth from "../lib/auth";
|
||||
import { sendMail } from "../lib/mail";
|
||||
import { getAccount, requiresAccount } from "../lib/middleware"
|
||||
import { getAccount, noAPIAccess, requiresAccount, requiresPermissions } from "../lib/middleware"
|
||||
import { accountRatelimit } from "../lib/ratelimit"
|
||||
|
||||
import ServeError from "../lib/errors";
|
||||
|
@ -123,7 +123,7 @@ authRoutes.post("/logout", (req,res) => {
|
|||
res.send("logged out")
|
||||
})
|
||||
|
||||
authRoutes.post("/dfv", requiresAccount, parser, (req,res) => {
|
||||
authRoutes.post("/dfv", requiresAccount, requiresPermissions("manage"), parser, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (['public','private','anonymous'].includes(req.body.defaultFileVisibility)) {
|
||||
|
@ -136,7 +136,7 @@ authRoutes.post("/dfv", requiresAccount, parser, (req,res) => {
|
|||
}
|
||||
})
|
||||
|
||||
authRoutes.post("/customcss", requiresAccount, parser, (req,res) => {
|
||||
authRoutes.post("/customcss", requiresAccount, requiresPermissions("customize"), parser, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (typeof req.body.fileId != "string") req.body.fileId = undefined;
|
||||
|
@ -158,7 +158,7 @@ authRoutes.post("/customcss", requiresAccount, parser, (req,res) => {
|
|||
}
|
||||
})
|
||||
|
||||
authRoutes.post("/embedcolor", requiresAccount, parser, (req,res) => {
|
||||
authRoutes.post("/embedcolor", requiresAccount, requiresPermissions("customize"), parser, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (typeof req.body.color != "string") req.body.color = undefined;
|
||||
|
@ -181,7 +181,7 @@ authRoutes.post("/embedcolor", requiresAccount, parser, (req,res) => {
|
|||
}
|
||||
})
|
||||
|
||||
authRoutes.post("/embedsize", requiresAccount, parser, (req,res) => {
|
||||
authRoutes.post("/embedsize", requiresAccount, requiresPermissions("customize"), parser, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (typeof req.body.largeImage != "boolean") req.body.color = false;
|
||||
|
@ -193,7 +193,7 @@ authRoutes.post("/embedsize", requiresAccount, parser, (req,res) => {
|
|||
res.send(`custom embed image size saved`)
|
||||
})
|
||||
|
||||
authRoutes.post("/delete_account", requiresAccount, parser, async (req,res) => {
|
||||
authRoutes.post("/delete_account", requiresAccount, noAPIAccess, parser, async (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
let accId = acc.id
|
||||
|
@ -217,7 +217,7 @@ authRoutes.post("/delete_account", requiresAccount, parser, async (req,res) => {
|
|||
} else cpl()
|
||||
})
|
||||
|
||||
authRoutes.post("/change_username", requiresAccount, parser, (req,res) => {
|
||||
authRoutes.post("/change_username", requiresAccount, noAPIAccess, parser, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (typeof req.body.username != "string" || req.body.username.length < 3 || req.body.username.length > 20) {
|
||||
|
@ -253,7 +253,7 @@ authRoutes.post("/change_username", requiresAccount, parser, (req,res) => {
|
|||
|
||||
let verificationCodes = new Map<string, {code: string, email: string, expiry: NodeJS.Timeout}>()
|
||||
|
||||
authRoutes.post("/request_email_change", requiresAccount, accountRatelimit({ requests: 4, per: 60*60*1000 }), parser, (req,res) => {
|
||||
authRoutes.post("/request_email_change", requiresAccount, noAPIAccess, accountRatelimit({ requests: 4, per: 60*60*1000 }), parser, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
|
||||
|
@ -292,7 +292,7 @@ authRoutes.post("/request_email_change", requiresAccount, accountRatelimit({ req
|
|||
})
|
||||
})
|
||||
|
||||
authRoutes.get("/confirm_email/:code", requiresAccount, (req,res) => {
|
||||
authRoutes.get("/confirm_email/:code", requiresAccount, noAPIAccess, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
|
||||
|
@ -314,7 +314,7 @@ authRoutes.get("/confirm_email/:code", requiresAccount, (req,res) => {
|
|||
}
|
||||
})
|
||||
|
||||
authRoutes.post("/remove_email", requiresAccount, (req,res) => {
|
||||
authRoutes.post("/remove_email", requiresAccount, noAPIAccess, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (acc.email) {
|
||||
|
@ -404,7 +404,7 @@ authRoutes.get("/emergency_login/:code", (req,res) => {
|
|||
}
|
||||
})
|
||||
|
||||
authRoutes.post("/change_password", requiresAccount, parser, (req,res) => {
|
||||
authRoutes.post("/change_password", requiresAccount, noAPIAccess, parser, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (typeof req.body.password != "string" || req.body.password.length < 8) {
|
||||
|
@ -429,7 +429,7 @@ authRoutes.post("/change_password", requiresAccount, parser, (req,res) => {
|
|||
res.send("password changed - logged out all sessions")
|
||||
})
|
||||
|
||||
authRoutes.post("/logout_sessions", requiresAccount, (req,res) => {
|
||||
authRoutes.post("/logout_sessions", requiresAccount, noAPIAccess, (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
let accId = acc.id
|
||||
|
@ -441,32 +441,25 @@ authRoutes.post("/logout_sessions", requiresAccount, (req,res) => {
|
|||
res.send("logged out all sessions")
|
||||
})
|
||||
|
||||
authRoutes.get("/me", requiresAccount, (req,res) => {
|
||||
authRoutes.get("/me", requiresAccount, requiresPermissions("user"), (req,res) => {
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
|
||||
let sessionToken = auth.tokenFor(req)
|
||||
let accId = acc.id
|
||||
res.send({
|
||||
...acc,
|
||||
sessionCount: auth.AuthTokens.filter(e => e.account == accId && e.expire > Date.now()).length,
|
||||
sessionExpires: auth.AuthTokens.find(e => e.token == req.cookies.auth)?.expire,
|
||||
password: undefined
|
||||
sessionCount: auth.AuthTokens.filter(e => e.type != "App" && e.account == accId && (e.expire > Date.now() || !e.expire)).length,
|
||||
sessionExpires: auth.AuthTokens.find(e => e.token == sessionToken)?.expire,
|
||||
password: undefined,
|
||||
email:
|
||||
auth.getType(sessionToken) == "User" || auth.getPermissions(sessionToken)?.includes("email")
|
||||
? acc.email
|
||||
: undefined
|
||||
})
|
||||
})
|
||||
|
||||
authRoutes.get("/customCSS", (req,res) => {
|
||||
if (!auth.validate(req.cookies.auth)) {
|
||||
ServeError(res, 401, "not logged in")
|
||||
return
|
||||
}
|
||||
|
||||
// lazy rn so
|
||||
|
||||
let acc = Accounts.getFromToken(req.cookies.auth)
|
||||
if (acc) {
|
||||
if (acc.customCSS) {
|
||||
res.redirect(`/file/${acc.customCSS}`)
|
||||
} else {
|
||||
res.send("")
|
||||
}
|
||||
} else res.send("")
|
||||
let acc = res.locals.acc
|
||||
if (acc?.customCSS) res.redirect(`/file/${acc.customCSS}`)
|
||||
else res.send("")
|
||||
})
|
||||
|
|
|
@ -7,6 +7,7 @@ import {writeFile} from "fs";
|
|||
|
||||
import ServeError from "../lib/errors";
|
||||
import Files from "../lib/files";
|
||||
import { getAccount, requiresAccount, requiresPermissions } from "../lib/middleware";
|
||||
|
||||
let parser = bodyParser.json({
|
||||
type: ["text/plain","application/json"]
|
||||
|
@ -21,14 +22,11 @@ export function setFilesObj(newFiles:Files) {
|
|||
|
||||
let config = require(`${process.cwd()}/config.json`)
|
||||
|
||||
fileApiRoutes.get("/list", (req,res) => {
|
||||
fileApiRoutes.use(getAccount);
|
||||
|
||||
if (!auth.validate(req.cookies.auth)) {
|
||||
ServeError(res, 401, "not logged in")
|
||||
return
|
||||
}
|
||||
fileApiRoutes.get("/list", requiresAccount, requiresPermissions("user"), (req,res) => {
|
||||
|
||||
let acc = Accounts.getFromToken(req.cookies.auth)
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (!acc) return
|
||||
let accId = acc.id
|
||||
|
@ -46,14 +44,9 @@ fileApiRoutes.get("/list", (req,res) => {
|
|||
|
||||
})
|
||||
|
||||
fileApiRoutes.post("/manage", parser, (req,res) => {
|
||||
fileApiRoutes.post("/manage", parser, requiresPermissions("manage"), (req,res) => {
|
||||
|
||||
if (!auth.validate(req.cookies.auth)) {
|
||||
ServeError(res, 401, "not logged in")
|
||||
return
|
||||
}
|
||||
|
||||
let acc = Accounts.getFromToken(req.cookies.auth) as Accounts.Account
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (!acc) return
|
||||
if (!req.body.target || !(typeof req.body.target == "object") || req.body.target.length < 1) return
|
||||
|
|
|
@ -8,6 +8,7 @@ import multer, {memoryStorage} from "multer"
|
|||
|
||||
import ServeError from "../lib/errors";
|
||||
import Files from "../lib/files";
|
||||
import { getAccount, requiresPermissions } from "../lib/middleware";
|
||||
|
||||
let parser = bodyParser.json({
|
||||
type: ["text/plain","application/json"]
|
||||
|
@ -24,9 +25,12 @@ const multerSetup = multer({storage:memoryStorage()})
|
|||
|
||||
let config = require(`${process.cwd()}/config.json`)
|
||||
|
||||
primaryApi.use(getAccount);
|
||||
|
||||
primaryApi.get(["/file/:fileId", "/cpt/:fileId/*", "/:fileId"], async (req:express.Request,res:express.Response) => {
|
||||
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
let file = files.getFilePointer(req.params.fileId)
|
||||
res.setHeader("Access-Control-Allow-Origin", "*")
|
||||
res.setHeader("Content-Security-Policy","sandbox allow-scripts")
|
||||
|
@ -34,7 +38,7 @@ primaryApi.get(["/file/:fileId", "/cpt/:fileId/*", "/:fileId"], async (req:expre
|
|||
|
||||
if (file) {
|
||||
|
||||
if (file.visibility == "private" && Accounts.getFromToken(req.cookies.auth)?.id != file.owner) {
|
||||
if (file.visibility == "private" && acc?.id != file.owner) {
|
||||
ServeError(res,403,"you do not own this file")
|
||||
return
|
||||
}
|
||||
|
@ -111,7 +115,10 @@ primaryApi.head(["/file/:fileId", "/cpt/:fileId/*", "/:fileId"], (req: express.R
|
|||
|
||||
// upload handlers
|
||||
|
||||
primaryApi.post("/upload",multerSetup.single('file'),async (req,res) => {
|
||||
primaryApi.post("/upload", requiresPermissions("upload"), multerSetup.single('file'), async (req,res) => {
|
||||
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
if (req.file) {
|
||||
try {
|
||||
let prm = req.header("monofile-params")
|
||||
|
@ -121,7 +128,7 @@ primaryApi.post("/upload",multerSetup.single('file'),async (req,res) => {
|
|||
}
|
||||
|
||||
files.uploadFile({
|
||||
owner: auth.validate(req.cookies.auth),
|
||||
owner: acc?.id,
|
||||
|
||||
uploadId:params.uploadId,
|
||||
name:req.file.originalname,
|
||||
|
@ -142,12 +149,15 @@ primaryApi.post("/upload",multerSetup.single('file'),async (req,res) => {
|
|||
}
|
||||
})
|
||||
|
||||
primaryApi.post("/clone", bodyParser.json({type: ["text/plain","application/json"]}) ,(req,res) => {
|
||||
primaryApi.post("/clone", requiresPermissions("upload"), bodyParser.json({type: ["text/plain","application/json"]}) ,(req,res) => {
|
||||
|
||||
let acc = res.locals.acc as Accounts.Account
|
||||
|
||||
try {
|
||||
axios.get(req.body.url,{responseType:"arraybuffer"}).then((data:AxiosResponse) => {
|
||||
|
||||
files.uploadFile({
|
||||
owner: auth.validate(req.cookies.auth),
|
||||
owner: acc?.id,
|
||||
|
||||
name:req.body.url.split("/")[req.body.url.split("/").length-1] || "generic",
|
||||
mime:data.headers["content-type"],
|
||||
|
|
|
@ -70,7 +70,7 @@ body {
|
|||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color:#191919;
|
||||
background-color:#222222;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
position: absolute;
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
background-color: #191919;
|
||||
background-color: #222222;
|
||||
color: #dddddd;
|
||||
|
||||
top:0px;
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
.searchBar {
|
||||
transition-duration:150ms;
|
||||
background-color:#171717;
|
||||
background-color:#212121;
|
||||
width:100%;
|
||||
padding:8px;
|
||||
color:#dddddd;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
position:relative;
|
||||
width:100%;
|
||||
height:50px;
|
||||
background-color: #191919;
|
||||
background-color: #222222;
|
||||
border:none;
|
||||
border-bottom:1px solid #AAAAAA;
|
||||
transition-duration:150ms;
|
||||
|
@ -106,7 +106,7 @@
|
|||
|
||||
.modal {
|
||||
position:absolute;
|
||||
background-color:#191919;
|
||||
background-color:#222222;
|
||||
width:100%;
|
||||
transform:translateY(-100%);
|
||||
top:100%;
|
||||
|
|
|
@ -56,4 +56,4 @@
|
|||
width:100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,20 @@
|
|||
font-size: 14px;
|
||||
}
|
||||
|
||||
h1 > button {
|
||||
background-color: #0000 !important;
|
||||
padding: 0;
|
||||
margin-top: 5px;
|
||||
|
||||
img {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
img { color: #DDD; }
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color:#999;
|
||||
}
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
<script>
|
||||
import { _void } from "./transition/_void.js";
|
||||
import { _void } from "./transition/_void.js"
|
||||
import { padding_scaleY } from "./transition/padding_scaleY.js"
|
||||
import { fade } from "svelte/transition";
|
||||
import { circIn, circOut } from "svelte/easing";
|
||||
import { serverStats, refresh_stats, account } from "./stores.mjs";
|
||||
import { fade } from "svelte/transition"
|
||||
import { circIn, circOut } from "svelte/easing"
|
||||
import { serverStats, refresh_stats, account } from "./stores.mjs"
|
||||
|
||||
import AttachmentZone from "./uploader/AttachmentZone.svelte";
|
||||
import AttachmentZone from "./uploader/AttachmentZone.svelte"
|
||||
|
||||
// stats
|
||||
|
||||
refresh_stats()
|
||||
|
||||
|
||||
// uploads
|
||||
|
||||
let attachmentZone;
|
||||
let uploads = {};
|
||||
let uploadInProgress = false;
|
||||
|
||||
let attachmentZone
|
||||
let uploads = {}
|
||||
let uploadInProgress = false
|
||||
let notificationPermission =
|
||||
globalThis?.Notification?.permission ?? "denied"
|
||||
let handle_file_upload = (ev) => {
|
||||
if (ev.detail.type == "clone") {
|
||||
uploads[Math.random().toString().slice(2)] = {
|
||||
|
@ -25,31 +26,31 @@
|
|||
url: ev.detail.url,
|
||||
|
||||
params: {
|
||||
uploadId: ""
|
||||
uploadId: "",
|
||||
},
|
||||
|
||||
uploadStatus:{
|
||||
uploadStatus: {
|
||||
fileId: null,
|
||||
error: null,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
uploads = uploads
|
||||
} else if (ev.detail.type == "upload") {
|
||||
ev.detail.files.forEach((v,x) => {
|
||||
ev.detail.files.forEach((v, x) => {
|
||||
uploads[Math.random().toString().slice(2)] = {
|
||||
type: "upload",
|
||||
name: v.name,
|
||||
file: v,
|
||||
|
||||
params: {
|
||||
uploadId: ""
|
||||
uploadId: "",
|
||||
},
|
||||
|
||||
uploadStatus:{
|
||||
uploadStatus: {
|
||||
fileId: null,
|
||||
error: null,
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -57,18 +58,48 @@
|
|||
}
|
||||
}
|
||||
|
||||
let handle_fetch_promise = (x,prom) => {
|
||||
return prom.then(async (res) => {
|
||||
let txt = await res.text()
|
||||
if (txt.startsWith("[err]")) uploads[x].uploadStatus.error = txt;
|
||||
else {
|
||||
uploads[x].uploadStatus.fileId = txt;
|
||||
|
||||
refresh_stats();
|
||||
}
|
||||
}).catch((err) => {
|
||||
uploads[x].uploadStatus.error = err.toString();
|
||||
})
|
||||
let handle_fetch_promise = (x, prom) => {
|
||||
return prom
|
||||
.then(async (res) => {
|
||||
let txt = await res.text()
|
||||
if (txt.startsWith("[err]")) uploads[x].uploadStatus.error = txt
|
||||
else {
|
||||
uploads[x].uploadStatus.fileId = txt
|
||||
try {
|
||||
new Notification("Upload complete", {
|
||||
body: `View at ${location.origin}/${uploads[x].uploadStatus.fileId}`,
|
||||
actions: [
|
||||
{
|
||||
action: "open",
|
||||
title: "Open",
|
||||
},
|
||||
{
|
||||
action: "copy",
|
||||
title: "Copy",
|
||||
},
|
||||
],
|
||||
}).addEventListener(
|
||||
"notificationclick",
|
||||
({ action }) => {
|
||||
if (action === "open") {
|
||||
open(
|
||||
"/download/" +
|
||||
uploads[x].uploadStatus.fileId
|
||||
)
|
||||
} else {
|
||||
navigator.clipboard.writeText(
|
||||
`${location.origin}/${uploads[x].uploadStatus.fileId}`
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
} catch (_) {}
|
||||
refresh_stats()
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
uploads[x].uploadStatus.error = err.toString()
|
||||
})
|
||||
}
|
||||
|
||||
let upload_files = async () => {
|
||||
|
@ -77,112 +108,224 @@
|
|||
let sequential = localStorage.getItem("sequentialMode") == "true"
|
||||
|
||||
// go through all files
|
||||
for (let [x,v] of Object.entries(uploads)) {
|
||||
for (let [x, v] of Object.entries(uploads)) {
|
||||
// quick patch-in to allow for a switch to have everything upload sequentially
|
||||
// switch will have a proper menu option later, for now i'm lazy so it's just gonna be a Secret
|
||||
let hdl = () => {
|
||||
switch(v.type) {
|
||||
switch (v.type) {
|
||||
case "upload":
|
||||
let fd = new FormData()
|
||||
fd.append("file",v.file)
|
||||
fd.append("file", v.file)
|
||||
|
||||
return handle_fetch_promise(x,fetch("/upload",{
|
||||
headers: {
|
||||
"monofile-params": JSON.stringify(v.params)
|
||||
},
|
||||
method: "POST",
|
||||
body: fd
|
||||
}))
|
||||
break
|
||||
case "clone":
|
||||
return handle_fetch_promise(x,fetch("/clone",{
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
url: v.url,
|
||||
...v.params
|
||||
return handle_fetch_promise(
|
||||
x,
|
||||
fetch("/upload", {
|
||||
headers: {
|
||||
"monofile-params": JSON.stringify(v.params),
|
||||
},
|
||||
method: "POST",
|
||||
body: fd,
|
||||
})
|
||||
}))
|
||||
break
|
||||
)
|
||||
break
|
||||
case "clone":
|
||||
return handle_fetch_promise(
|
||||
x,
|
||||
fetch("/clone", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
url: v.url,
|
||||
...v.params,
|
||||
}),
|
||||
})
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (sequential) await hdl();
|
||||
else hdl();
|
||||
|
||||
if (sequential) await hdl()
|
||||
else hdl()
|
||||
}
|
||||
}
|
||||
|
||||
// animation
|
||||
|
||||
|
||||
function fileTransition(node) {
|
||||
return {
|
||||
duration: 300,
|
||||
css: t => {
|
||||
css: (t) => {
|
||||
let eased = circOut(t)
|
||||
|
||||
return `
|
||||
height: ${eased*(node.offsetHeight-22)}px;
|
||||
padding: ${eased*10}px 10px;
|
||||
height: ${eased * (node.offsetHeight - 22)}px;
|
||||
padding: ${eased * 10}px 10px;
|
||||
`
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div id="uploadWindow">
|
||||
<h1>monofile</h1>
|
||||
<h1>
|
||||
monofile
|
||||
{#if notificationPermission === "default"}
|
||||
<button
|
||||
on:click={() => {
|
||||
Notification.requestPermission().then(
|
||||
(permission) => (notificationPermission = permission)
|
||||
)
|
||||
}}
|
||||
style="float:right"
|
||||
title="Notify me when the upload finishes"
|
||||
>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="Notify me when the upload finishes"
|
||||
><path
|
||||
d="M9.042 19.003h5.916a3 3 0 0 1-5.916 0Zm2.958-17a7.5 7.5 0 0 1 7.5 7.5v4l1.418 3.16A.95.95 0 0 1 20.052 18h-16.1a.95.95 0 0 1-.867-1.338l1.415-3.16V9.49l.005-.25A7.5 7.5 0 0 1 12 2.004Z"
|
||||
fill="currentColor"
|
||||
/></svg
|
||||
>
|
||||
</button>
|
||||
{/if}
|
||||
</h1>
|
||||
<p style:color="#999999">
|
||||
<span class="number">{$serverStats.version ? `v${$serverStats.version}` : "•••"}</span> — Discord based file sharing
|
||||
<span class="number"
|
||||
>{$serverStats.version ? `v${$serverStats.version}` : "•••"}</span
|
||||
> — Discord based file sharing
|
||||
</p>
|
||||
|
||||
<div style:min-height="10px" />
|
||||
|
||||
|
||||
<!-- consider splitting the file thing into a separate element maybe -->
|
||||
|
||||
<div class="uploadContainer">
|
||||
{#each Object.entries(uploads) as upload (upload[0])}
|
||||
<!-- container to allow for animate directive -->
|
||||
<div>
|
||||
<div class="file" transition:fileTransition style:border={upload[1].uploadStatus.error ? "1px solid #BB7070" : ""}>
|
||||
<h2>{upload[1].name} <span style:color="#999999" style:font-weight="400">{upload[1].type}{@html upload[1].type == "upload" ? ` (${Math.round(upload[1].file.size/1048576)}MiB)` : ""}</span></h2>
|
||||
|
||||
<div
|
||||
class="file"
|
||||
transition:fileTransition
|
||||
style:border={upload[1].uploadStatus.error
|
||||
? "1px solid #BB7070"
|
||||
: ""}
|
||||
>
|
||||
<h2>
|
||||
{upload[1].name}
|
||||
<span style:color="#999999" style:font-weight="400"
|
||||
>{upload[1].type}{@html upload[1].type == "upload"
|
||||
? ` (${Math.round(
|
||||
upload[1].file.size / 1048576
|
||||
)}MiB)`
|
||||
: ""}</span
|
||||
>
|
||||
</h2>
|
||||
|
||||
{#if upload[1].maximized && !uploadInProgress}
|
||||
<div transition:padding_scaleY|local>
|
||||
<div style:height="10px" />
|
||||
<input placeholder="custom id" type="text" bind:value={ uploads[upload[0]].params.uploadId }>
|
||||
<input
|
||||
placeholder="custom id"
|
||||
type="text"
|
||||
bind:value={uploads[upload[0]].params.uploadId}
|
||||
/>
|
||||
<div style:height="10px" />
|
||||
<div class="buttonContainer">
|
||||
<button on:click={() => {delete uploads[upload[0]];uploads=uploads;}}>
|
||||
<button
|
||||
on:click={() => {
|
||||
delete uploads[upload[0]]
|
||||
uploads = uploads
|
||||
}}
|
||||
>
|
||||
delete
|
||||
</button>
|
||||
<button on:click={() => uploads[upload[0]].maximized = false}>
|
||||
<button
|
||||
on:click={() =>
|
||||
(uploads[upload[0]].maximized = false)}
|
||||
>
|
||||
minimize
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else if !uploadInProgress}
|
||||
<button on:click={() => uploads[upload[0]].maximized = true} class="hitbox"></button>
|
||||
<button
|
||||
on:click={() =>
|
||||
(uploads[upload[0]].maximized = true)}
|
||||
class="hitbox"
|
||||
/>
|
||||
{:else}
|
||||
<div transition:padding_scaleY|local class="uploadingContainer">
|
||||
<div
|
||||
transition:padding_scaleY|local
|
||||
class="uploadingContainer"
|
||||
>
|
||||
{#if !upload[1].uploadStatus.fileId}
|
||||
<p in:fade={{duration:300, delay:400, easingFunc:circOut}} out:padding_scaleY={{easingFunc:circIn,op:true}}>{upload[1].uploadStatus.error ?? "Uploading..."}</p>
|
||||
<p
|
||||
in:fade={{
|
||||
duration: 300,
|
||||
delay: 400,
|
||||
easingFunc: circOut,
|
||||
}}
|
||||
out:padding_scaleY={{
|
||||
easingFunc: circIn,
|
||||
op: true,
|
||||
}}
|
||||
>
|
||||
{upload[1].uploadStatus.error ??
|
||||
"Uploading..."}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
{#if upload[1].uploadStatus.fileId}
|
||||
<div style:height="10px" transition:padding_scaleY />
|
||||
<div
|
||||
style:height="10px"
|
||||
transition:padding_scaleY
|
||||
/>
|
||||
{#if !upload[1].viewingUrl}
|
||||
<div class="buttonContainer" out:_void in:_void={{easingFunc:circOut}}>
|
||||
<button on:click={() => uploads[upload[0]].viewingUrl = true}>
|
||||
<div
|
||||
class="buttonContainer"
|
||||
out:_void
|
||||
in:_void={{ easingFunc: circOut }}
|
||||
>
|
||||
<button
|
||||
on:click={() =>
|
||||
(uploads[
|
||||
upload[0]
|
||||
].viewingUrl = true)}
|
||||
>
|
||||
view url
|
||||
</button>
|
||||
<button on:click={() => navigator.clipboard.writeText(`https://${window.location.host}/download/${upload[1].uploadStatus.fileId}`)}>
|
||||
<button
|
||||
on:click={() =>
|
||||
navigator.clipboard.writeText(
|
||||
`https://${window.location.host}/download/${upload[1].uploadStatus.fileId}`
|
||||
)}
|
||||
>
|
||||
copy url
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="buttonContainer" out:_void in:_void={{easingFunc:circOut}}>
|
||||
<input type="text" readonly value={`https://${window.location.host}/download/${upload[1].uploadStatus.fileId}`} style:flex-basis="80%">
|
||||
<button on:click={() => uploads[upload[0]].viewingUrl = false} style:flex-basis="20%">
|
||||
<div
|
||||
class="buttonContainer"
|
||||
out:_void
|
||||
in:_void={{ easingFunc: circOut }}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value={`https://${window.location.host}/download/${upload[1].uploadStatus.fileId}`}
|
||||
style:flex-basis="80%"
|
||||
/>
|
||||
<button
|
||||
on:click={() =>
|
||||
(uploads[
|
||||
upload[0]
|
||||
].viewingUrl = false)}
|
||||
style:flex-basis="20%"
|
||||
>
|
||||
ok
|
||||
</button>
|
||||
</div>
|
||||
|
@ -195,37 +338,73 @@
|
|||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
||||
{#if uploadInProgress == false}
|
||||
|
||||
<!-- if required for upload, check if logged in -->
|
||||
{#if ($serverStats.accounts||{}).requiredForUpload ? !!$account.username : true}
|
||||
|
||||
<AttachmentZone bind:this={attachmentZone} on:addFiles={handle_file_upload}/>
|
||||
<div style:min-height="10px" transition:_void={{rTarg:"height",prop:"min-height"}} />
|
||||
{#if ($serverStats.accounts || {}).requiredForUpload ? !!$account.username : true}
|
||||
<AttachmentZone
|
||||
bind:this={attachmentZone}
|
||||
on:addFiles={handle_file_upload}
|
||||
/>
|
||||
<div
|
||||
style:min-height="10px"
|
||||
transition:_void={{ rTarg: "height", prop: "min-height" }}
|
||||
/>
|
||||
{#if Object.keys(uploads).length > 0}
|
||||
<button in:padding_scaleY={{easingFunc:circOut}} out:_void on:click={upload_files}>upload</button>
|
||||
<div transition:_void={{rTarg:"height",prop:"min-height"}} style:min-height="10px" />
|
||||
<button
|
||||
in:padding_scaleY={{ easingFunc: circOut }}
|
||||
out:_void
|
||||
on:click={upload_files}>upload</button
|
||||
>
|
||||
<div
|
||||
transition:_void={{ rTarg: "height", prop: "min-height" }}
|
||||
style:min-height="10px"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{:else}
|
||||
|
||||
<p transition:_void style:color="#999999" style:text-align="center">Please log in to upload files.</p>
|
||||
<div transition:_void={{rTarg:"height",prop:"min-height"}} style:min-height="10px" />
|
||||
|
||||
<p transition:_void style:color="#999999" style:text-align="center">
|
||||
Please log in to upload files.
|
||||
</p>
|
||||
<div
|
||||
transition:_void={{ rTarg: "height", prop: "min-height" }}
|
||||
style:min-height="10px"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{/if}
|
||||
|
||||
|
||||
<p style:color="#999999" style:text-align="center">
|
||||
Hosting <span class="number" style:font-weight="600">{$serverStats.files || "•••"}</span> files
|
||||
—
|
||||
Maximum filesize is <span class="number" style:font-weight="600">{(($serverStats.maxDiscordFileSize || 0)*($serverStats.maxDiscordFiles || 0))/1048576 || "•••"}MiB</span>
|
||||
Hosting <span class="number" style:font-weight="600"
|
||||
>{$serverStats.files || "•••"}</span
|
||||
>
|
||||
files — Maximum filesize is
|
||||
<span class="number" style:font-weight="600"
|
||||
>{(($serverStats.maxDiscordFileSize || 0) *
|
||||
($serverStats.maxDiscordFiles || 0)) /
|
||||
1048576 || "•••"}MiB</span
|
||||
>
|
||||
<br />
|
||||
</p>
|
||||
|
||||
<p style:color="#999999" style:text-align="center" style:font-size="12px">
|
||||
Made with {Math.floor(Math.random()*10)==0 ? "🐟" : "❤"} by <a href="https://github.com/nbitzz" style:font-size="12px">@nbitzz</a> — <a href="https://github.com/nbitzz/monofile" style:font-size="12px">source</a>
|
||||
Made with {Math.floor(Math.random() * 10) == 0 ? "🐟" : "❤"} by
|
||||
<a href="https://cetera.uk" style:font-size="12px"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 173.8 275.72"
|
||||
height="16"
|
||||
style="vertical-align:middle"
|
||||
fill="currentColor"
|
||||
>
|
||||
<circle cx="37.13" cy="26.43" r="21.6" />
|
||||
<circle cx="34.62" cy="117.87" r="34.62" class="middle" />
|
||||
<circle cx="119.78" cy="130.68" r="21.6" class="middle" />
|
||||
<circle cx="127.16" cy="46.64" r="46.65" />
|
||||
<circle cx="102.68" cy="219.58" r="56.14" class="bottom" />
|
||||
</svg> Etcetera</a
|
||||
>
|
||||
—
|
||||
<a href="https://github.com/mollersuite/monofile" style:font-size="12px"
|
||||
>source</a
|
||||
>
|
||||
</p>
|
||||
<div style:height="10px" />
|
||||
</div>
|
||||
</div>
|
||||
|
|