Compare commits

..

22 Commits

Author SHA1 Message Date
ge
71e1d87d32 feat: Improve and make scripts POSIX compliant 2022-09-29 10:47:48 +03:00
ge
921dd5d29d feat: Add dark theme via prefers-color-scheme 2022-09-29 10:46:51 +03:00
ge
cde343d159 update README 2022-09-29 10:44:41 +03:00
ge
cd258011be fix: Update link 2022-09-29 08:31:36 +03:00
aed21e14a3 Merge pull request #2 from gechandesu/dependabot/pip/bottle-0.12.20
build(deps): Bump bottle from 0.12.19 to 0.12.20
2022-07-27 08:40:34 +03:00
85b2664483 build(deps): Bump bottle from 0.12.19 to 0.12.20
Bumps [bottle](https://github.com/bottlepy/bottle) from 0.12.19 to 0.12.20.
- [Release notes](https://github.com/bottlepy/bottle/releases)
- [Changelog](https://github.com/bottlepy/bottle/blob/master/docs/changelog.rst)
- [Commits](https://github.com/bottlepy/bottle/compare/0.12.19...0.12.20)

---
updated-dependencies:
- dependency-name: bottle
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-03 22:50:24 +00:00
ge
dbdbf2a3d9 feat: Remove WSGI server from requirements 2022-03-17 21:42:47 +03:00
ge
3a8bef4ddc feat: Reduce image size from ~930M to ~85M 2022-03-17 21:42:06 +03:00
ge
8122de2c52 fix: Naitilus script fixed 2022-01-29 02:43:50 +03:00
ge
2eea050bf8 fix: Fix typo in README 2022-01-23 16:47:49 +03:00
gd
2f7fc76573 fix: Fix Nginx vhost example 2022-01-05 13:22:13 +03:00
gd
dd570f7f93 feat: Add Dockerfile 2022-01-05 13:12:51 +03:00
gd
312efc9047 feat: Update requirements 2022-01-05 11:18:10 +03:00
gd
fc80d9e661 fix: Fixed WSGI app startup 2022-01-05 09:31:27 +03:00
gd
721fda8b22 feat: Update README 2022-01-05 08:33:43 +03:00
gd
331a377c6b feat: Update imgs.ini 2022-01-05 08:33:18 +03:00
gd
12f4433e8e feat: Add shell-scripts 2022-01-05 08:32:46 +03:00
gd
0898b1f593 feat: Update version 2022-01-05 08:18:54 +03:00
gd
4ff6362c64 feat: Remove run() options 2022-01-05 07:52:05 +03:00
gd
8e4ab793f9 feat: Refactored uplocad_image(), MIME check skipping added 2022-01-05 07:16:24 +03:00
gd
87f6c3a048 feat: Add favicon 2022-01-05 07:14:47 +03:00
gd
397aeed123 feat: Update LICENSE 2022-01-05 07:13:50 +03:00
11 changed files with 286 additions and 78 deletions

9
Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM alpine:latest
RUN apk update && apk add python3 py3-pip
ADD . /opt/imgs
RUN mkdir -p /opt/imgs/uploads
WORKDIR /opt/imgs
RUN pip install --upgrade pip && pip install --requirement requirements.txt
RUN pip install gunicorn
EXPOSE 5000
CMD gunicorn imgs:app --bind :5000

View File

@ -1,4 +1,4 @@
Copyright (c) 2021 gd <gechandev@gmail.com>
Copyright (c) 2022 ge <gechandev@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -5,8 +5,74 @@ imgs is a minimalictic image sharing web app written with Bottle framework.
No database. No image compression. No time limits. No additional dependencies.
Features:
* Upload images via Drag&Drop
* Easy copy share link
* MIME type detecting
See deployment options in Bottle documentation: https://bottlepy.org/docs/dev/deployment.html
# Run imgs in Docker
Clone repository and edit **imgs.ini**.
Build Docker image:
```shell
docker build --tag imgs .
```
Run container from image. Replace **/path/to/your/uploads/dir** with path to directory where you want to store images:
```shell
docker run -d \
--name imgs \
--publish 127.0.0.1:5000:5000 \
--volume /path/to/your/uploads/dir:/opt/imgs/uploads \
imgs
```
imgs will launched on `127.0.0.1:5000`. Set up reverse proxy server. I recommed to use basic authentication to prevent abuses. Nginx virtual host example:
```nginx
server {
listen 80;
server_name yourdomain.tld;
root /path/to/imgs/root;
location / {
auth_basic "Authentication required";
auth_basic_user_file /path/to/.htpasswd;
proxy_pass http://127.0.0.1:5000;
}
location ~* ^/favicon.ico$ {
try_files $uri $uri/ =404;
}
location ~* \..* {
auth_basic off;
proxy_pass http://127.0.0.1:5000;
}
}
```
# Additional
## imgs client with CLI
imgs has a simple CLI tool based on curl. Copy **imgs** script to your PATH.
```shell
sudo cp imgs /usr/bin/imgs
```
## Nautilus integration
Push files to your imgs instance via GNOME Files (former name: Nautilus). Depends on: curl, libnotify (notify-send utility).
Just place **Upload to imgs** script into **~/.local/share/nautilus/scripts/** directory.
```shell
DIR=~/.local/share/nautilus/scripts/; mkdir -p $DIR && cp Upload\ to\ imgs $DIR
```

32
Upload to imgs Executable file
View File

@ -0,0 +1,32 @@
#!/bin/sh
# This is an imgs https://git.nxhs.cloud/ge/imgs "integration" for Nautilus.
# Place this script into path: $HOME/.local/share/nautilus/scripts
# See more info at: <https://help.ubuntu.com/community/NautilusScriptsHowto>
IMGSLOG="${IMGSLOG:-$HOME/imgs_debug.log}"
[ -n "$IMGSREMOTE" ] && return 0 # exit from func if variable is set
if [ -f "$HOME"/.imgsremote ]; then
# shellcheck source=/dev/null
. "$HOME"/.imgsremote
fi
if [ -z "$IMGSREMOTE" ]; then
echo "$0: Error: IMGSREMOTE variable is not set." >&2; exit 1
fi
[ -n "$IMGSDEBUG" ] && date +"[%d %b %Y %H:%M:%S] Started" >> "$IMGSLOG"
echo "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" | while read -r file; do
[ -z "$file" ] && break
if [ -n "$IMGSDEBUG" ]; then
image="$(curl -v -L -F "image=@$file" "$IMGSREMOTE" 2>&1 | tee -a "$IMGSLOG")"
image="$(echo "$image" | tail -n 1)"
else
image="$(curl -L -F "image=@$file" "$IMGSREMOTE")"
fi
[ -n "$IMGSDEBUG" ] && echo "$(date +"[%d %b %Y %H:%M:%S]") $file --> $image" >> "$IMGSLOG"
notify-send "File uploaded to imgs!" "$image"
done
[ -n "$IMGSDEBUG" ] && date +"[%d %b %Y %H:%M:%S] Finished" >> "$IMGSLOG"

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

74
imgs Executable file
View File

@ -0,0 +1,74 @@
#!/bin/sh
# imgs CLI https://git.nxhs.cloud/ge/imgs
imgs_usage() {
cat <<- EOF
Upload images to remote imgs server.
Usage: imgs [-rvh] <file>...
Options:
-r, --remote remote imgs instance URI e.g. https://user:password@example.org
-v, --version print version and exit.
-h, --help print this help message and exit.
Environment variables:
IMGSREMOTE remote imgs instance URI.
IMGSDEBUG enables verbose mode and logging.
IMGSLOG path to logfile. Default: ~/imgs_debug.log
You can set variables in ~/.imgsremote file instead of ~/.bashrc
See <https://git.nxhs.cloud/ge/imgs> for more info.
EOF
}
[ "$#" -eq 0 ] && { imgs_usage; exit 1; }
# Transform long options to short ones
for arg in "$@"; do
shift
case "$arg" in
--remote) set -- "$@" "-r";;
--help) set -- "$@" "-h";;
--version) set -- "$@" "-v";;
*) set -- "$@" "$arg";;
esac
done
while getopts r:vh OPT; do
case "$OPT" in
r) IMGSREMOTE="$OPTARG";;
v) echo 'imgs CLI 1.1'; exit 0;;
h) imgs_usage; exit 0;;
*) echo "$0: Unknown option: $OPT" >&2; exit 1;;
esac
done
shift $((OPTIND - 1)) # shift for parse positional args
# Check variables
IMGSLOG="${IMGSLOG:-$HOME/imgs_debug.log}"
[ -n "$IMGSREMOTE" ] && return 0 # exit from func if variable is set
if [ -f "$HOME"/.imgsremote ]; then
# shellcheck source=/dev/null
. "$HOME"/.imgsremote
fi
if [ -z "$IMGSREMOTE" ]; then
echo "$0: Error: IMGSREMOTE variable is not set." >&2; exit 1
fi
[ -n "$IMGSDEBUG" ] && date +"[%d %b %Y %H:%M:%S] Started" | tee -a "$IMGSLOG"
for file in "$@"; do
filepath="$(realpath "$file")"
if [ -n "$IMGSDEBUG" ]; then
echo "Uploading $filepath ..." | tee -a "$IMGSLOG"
curl -v -L -F "image=@/$filepath" "$IMGSREMOTE" 2>&1 | tee -a "$IMGSLOG"
else
curl -L -F "image=@/$filepath" "$IMGSREMOTE"
fi
done
[ -n "$IMGSDEBUG" ] && date +"[%d %b %Y %H:%M:%S] Finished" | tee -a "$IMGSLOG"

View File

@ -1,6 +1,6 @@
[imgs]
debug = False
base_url = http://localhost:5000/
image_name_lenght = 3
uploads_dir = uploads
#allowed_mime_types = * # allow any MIME type
allowed_mime_types = ['image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/tiff', 'image/webp']

38
imgs.py
View File

@ -1,3 +1,5 @@
__version__ = '1.1'
import os
import random
import string
@ -48,30 +50,38 @@ def upload_image():
# Handle request from CLI
if request.files.get('image'):
file = request.files.get('image')
if file.content_type in config['imgs.allowed_mime_types']:
image_name = upload_file(file)
return get_image_url(image_name) + '\n'
else:
# Prevent recource leek. Force close buffered file
request.body.close()
response.status = 415
return 'Error: bad file MIME type\n'
rq = 'cli'
# Handle request from web-browser
elif request.files.get('image_web'):
file = request.files.get('image_web')
if file.content_type in config['imgs.allowed_mime_types']:
rq = 'web'
if config['imgs.allowed_mime_types'] == '*':
# Skip MIME checking.
image_name = upload_file(file)
return template('index.tpl',
uploaded = True, not_found = False, bad_mime_type = False,
base_url = get_base_url(), image_url = get_image_url(image_name))
else:
if file.content_type in config['imgs.allowed_mime_types']:
# Upload file!
image_name = upload_file(file)
else:
# Show MIME type error!
# Prevent recource leek. Force close buffered file
request.body.close()
response.status = 415
if rq == 'cli':
return 'Error: bad file MIME type\n'
else:
return template('index.tpl',
uploaded = False, not_found = False, bad_mime_type = True,
allowed_mime_types = config['imgs.allowed_mime_types'],
base_url = get_base_url(), image_url = 'None')
# Return 200 OK
if rq == 'cli':
return get_image_url(image_name) + '\n'
else:
return template('index.tpl',
uploaded = True, not_found = False, bad_mime_type = False,
base_url = get_base_url(), image_url = get_image_url(image_name))
@get('/<image_name>')
def send_image(image_name):
@ -81,5 +91,7 @@ def send_image(image_name):
def send_style():
return static_file('style.css', root = './')
app = app() # Create WSGI application
if __name__ == '__main__':
run(debug = config['imgs.debug'])
run()

View File

@ -77,7 +77,7 @@
<div class="logo">
<pre> __<br>|__| _____ ____ ______<br>| |/ \ / ___\/ ___/<br>| | Y Y / /_/ \___ \<br>|__|__|_| \___ /____ ><br> \/_____/ \/</pre>
</div>
<p><a href="https://gitea.gch.icu/gd/imgs" target="_blank">v1.0</a></p>
<p><a href="https://git.nxhs.cloud/ge/imgs" target="_blank">v1.1</a></p>
</main>
</body>

View File

@ -1 +1 @@
bottle==0.12.19
bottle==0.12.20

View File

@ -1,5 +1,18 @@
:root {
--b: #000;
--w: #fff;
}
@media (prefers-color-scheme: dark) {
:root {
--b: #fff;
--w: #000;
}
}
body {
background-color: #fff;
color: var(--b);
background-color: var(--w);
font-family: 'Ubuntu Mono', monospace;
max-width: 720px;
margin: 0 auto;
@ -7,11 +20,14 @@ body {
}
main { margin: 4rem 2rem; }
.not-found, .bad-mime-type { margin-bottom: 2rem; }
a, a:visited { color: #000; }
a, a:visited { color: var(--b); }
img { width: 100%; }
.not-found, .bad-mime-type { margin-bottom: 2rem; }
.logo pre {
display: flex;
justify-content: center;
text-align: left;
}
/* Drag and Drop */
.drop-area {
@ -23,9 +39,7 @@ img { width: 100%; }
padding: 25px;
border: 3px dashed #e1e1e1;
}
.drop-area.dragover { border-color: #000; }
.drop-area.dragover { border-color: var(--b); }
.file-input {
position: absolute;
left: 0;
@ -36,7 +50,6 @@ img { width: 100%; }
align-items: center;
opacity: 0;
}
.file-input-label {
display: block;
margin-top: 1rem;
@ -46,17 +59,17 @@ img { width: 100%; }
.copy-to-clipboard {
display: flex;
margin: 2rem 0;
border: 1px solid #000000;
border: 1px solid var(--b);
}
.copy-to-clipboard input[type=text] {
flex: 50%;
width: 100%;
padding: 12px 20px;
border: none;
outline: none;
background-color: var(--w);
color: var(--b);
}
.copy-to-clipboard button {
padding: 12px 20px;
margin: 0;
@ -64,8 +77,8 @@ img { width: 100%; }
cursor: pointer;
text-align: center;
border: none;
background-color: #000000;
color: #ffffff;
background-color: var(--b);
color: var(--w);
}
/* cURL command */
@ -73,13 +86,15 @@ img { width: 100%; }
text-align: left;
padding: 12px 20px;
font-size: 14px;
background: #000000;
color: #ffffff;
background: var(--b);
color: var(--w);
overflow-x: auto;
}
.logo pre {
display: flex;
justify-content: center;
text-align: left;
/* SVG */
svg {
color: var(--b);
}
svg path {
stroke: currentcolor;
}