Repository: Orbiter/tedimg
Branch: master
Commit: f1d5d7aea812
Files: 21
Total size: 13.0 KB
Directory structure:
gitextract_rj62ikr9/
├── .dockerignore
├── Dockerfile
├── LICENSE.md
├── README.md
├── docker/
│ ├── nginx.conf
│ └── supervisor.conf
├── package.json
├── requirements.txt
├── resources/
│ ├── main.css
│ └── main.js
├── run.py
├── tedimg/
│ ├── __init__.py
│ ├── images.py
│ ├── static/
│ │ └── empty
│ ├── templates/
│ │ ├── banner.html
│ │ ├── base.html
│ │ ├── error.html
│ │ ├── index.html
│ │ └── show.html
│ └── views.py
└── webpack.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
node_modules/
Dockerfile
================================================
FILE: Dockerfile
================================================
FROM python:3-alpine
RUN apk add --no-cache nginx supervisor
ADD . /app
WORKDIR /app
RUN apk add --no-cache nodejs \
&& npm install \
&& npm run build \
&& rm -rf node_modules \
&& apk del nodejs
RUN apk add --no-cache --virtual build-dep gcc linux-headers libc-dev \
&& apk add --no-cache jpeg-dev zlib-dev \
&& pip install -r /app/requirements.txt \
&& apk del build-dep
EXPOSE 80
CMD /usr/bin/supervisord -c /app/docker/supervisor.conf
================================================
FILE: LICENSE.md
================================================
"THE BEER-WARE LICENSE" (Revision 42):
kaiyou wrote this file. As long as you retain this notice you
can do whatever you want with this stuff. If we meet some day, and you think
this stuff is worth it, you can buy me a beer in return.
================================================
FILE: README.md
================================================
Image upload with Flask
=======================
No database, no additional features. Plain and simple image upload.

Use the Docker image
--------------------
Simply allocate a data directory and create the thumbnails
sub-directory:
mkdir -p /path/to/data/thumbnails
Then run the image server:
docker run --name=tedimg -d -v /path/to/data:/data kaiyou/tedimg
Build from source
-----------------
NodeJS and NPM are required to build from source :
git clone git@github.com:kaiyou/tedimg.git
cd tedimg
npm install
gulp
docker build
================================================
FILE: docker/nginx.conf
================================================
user nginx;
worker_processes 4;
pid /run/nginx.pid;
daemon off;
events {
worker_connections 768;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /dev/stdout;
error_log /dev/stderr;
gzip on;
gzip_disable "msie6";
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $http_x_forwarded_proto;
'' $scheme;
}
server {
listen 80;
client_max_body_size 20M;
add_header X-Frame-Options 'SAMEORIGIN';
add_header X-Content-Type-Options 'nosniff';
add_header X-Permitted-Cross-Domain-Policies 'none';
add_header X-XSS-Protection '1; mode=block';
add_header Referrer-Policy 'same-origin';
location /data {
root /;
}
location /static {
root /app/tedimg;
}
location / {
proxy_pass http://127.0.0.1:8000/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
}
}
}
================================================
FILE: docker/supervisor.conf
================================================
[supervisord]
pidfile=/var/run/supervisord.pid
nodaemon=true
loglevel=DEBUG
[program:nginx]
command=nginx -c /app/docker/nginx.conf
redirect_stderr=true
[program:flask]
directory=/app
command = gunicorn -w 4 -b 127.0.0.1:8000 tedimg:app
redirect_stderr=true
================================================
FILE: package.json
================================================
{
"name": "tedimg",
"scripts": {
"watch": "webpack -w",
"build": "webpack -p"
},
"dependencies": {
"css-loader": "^0.28.11",
"file-loader": "^1.1.11",
"jquery": "^3.3.1",
"js-loader": "^0.1.1",
"materialize-css": "^0.100.2",
"resolve-url-loader": "^2.3.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack": "^4.9.1",
"webpack-cli": "^2.1.4",
"webpack-dev-server": "^3.1.4"
}
}
================================================
FILE: requirements.txt
================================================
Flask==1.0.2
Pillow==5.1.0
requests==2.18.4
gunicorn==19.8.1
Werkzeug==0.14.1
================================================
FILE: resources/main.css
================================================
/* Browser specific (not valid) styles to make preformatted text wrap */
pre {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
input.snippet {
font-family: monospace;
}
::-webkit-input-placeholder {
color: #444;
}
:-moz-placeholder {
color: #444;
}
::-moz-placeholder {
color: #444;
}
:-ms-input-placeholder {
color: #444;
}
/* Materialize icons */
/* fallback */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url('https://fonts.gstatic.com/s/materialicons/v38/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2') format('woff2');
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-moz-font-feature-settings: 'liga';
-moz-osx-font-smoothing: grayscale;
}
================================================
FILE: resources/main.js
================================================
// Import general CSS
import 'materialize-css';
import 'materialize-css/dist/css/materialize.css';
// Import specific CSS
import './main.css';
// Javascript libs
import $ from 'jquery';
$(document).ready(function() {
$("form#upload input").change(function() {
$("form#upload").submit();
});
$("form#upload input[type=text]").on('paste', function() {
setTimeout(function () {
$("form#upload").submit();
}, 100);
});
$("input.snippet").click(function() {
this.select();
})
});
================================================
FILE: run.py
================================================
from tedimg import app
from flask import send_from_directory
import os
app.config.update(
SITE_NAME="TeDomum Images",
FULL_STORAGE="./tedimg/static/images",
THUMB_STORAGE="./tedimg/static/images/thumb",
FULL_WEB="static/images",
THUMB_WEB="static/images/thumb"
)
app.run(debug=True)
================================================
FILE: tedimg/__init__.py
================================================
from flask import Flask
import os
app = Flask(__name__)
app.debug = "FLASK_DEBUG" in os.environ
app.config.update(
SITE_NAME=os.environ.get("SITE_NAME", "TedImg"),
SOURCE_URL="https://git.tedomum.net/kaiyou/tedimg",
HELP_URL="https://git.tedomum.net/kaiyou/tedimg",
FULL_STORAGE=os.environ.get("FULL_STORAGE", "/data"),
THUMB_STORAGE=os.environ.get("THUMB_STORAGE", "/data/thumb"),
FULL_WEB=os.environ.get("FULL_WEB", "data"),
THUMB_WEB=os.environ.get("THUMB_WEB", "data/thumb"),
THUMB_SIZE=100
)
if (app.debug):
from werkzeug import debug
app.wsgi_app = debug.DebuggedApplication(app.wsgi_app, True)
import tedimg.views
================================================
FILE: tedimg/images.py
================================================
from tedimg import app
from PIL import Image, ImageSequence
import os
import binascii
import requests
import io
import urllib
def get_image(root, name):
""" Try and get basic image attributes.
"""
filename = urllib.parse.quote(os.path.basename(name))
return (os.path.join(root, app.config["FULL_WEB"], filename),
os.path.join(root, app.config["THUMB_WEB"], filename))
def image_from_file(file_storage):
""" Try and read the uploaded file.
"""
image = Image.open(file_storage)
return image
def image_from_url(url):
""" Try and download an image from the given url.
"""
response = requests.get(url)
image = Image.open(io.BytesIO(response.content))
return image
def save_with_thumbnail(image, filename):
dest = "."
while os.path.exists(os.path.join(app.config["FULL_STORAGE"], dest)):
filename, _ = os.path.splitext(filename)
ext = image.format.lower()
random = binascii.hexlify(os.urandom(3)).decode('utf8')
dest = "%s-%s.%s" % (filename, random, ext)
# Grab some configuration
full_file = os.path.join(app.config["FULL_STORAGE"], dest)
thumb_file = os.path.join(app.config["THUMB_STORAGE"], dest)
thumb_size = app.config["THUMB_SIZE"]
# Save the image and thumbnail
if image.format == 'GIF':
image.save(full_file, format=image.format, save_all=True)
else:
image.save(full_file, format=image.format)
image.thumbnail((thumb_size, thumb_size))
image.save(thumb_file, format=image.format)
return dest
================================================
FILE: tedimg/static/empty
================================================
================================================
FILE: tedimg/templates/banner.html
================================================
{% extends "base.html" %}
{% block content %}
<div class="section no-pad-bot" id="index-banner">
<div class="container">
{% block banner_content %}
{% endblock %}
<br><br>
</div>
</div>
<div class="container">
<div class="section">
{% block section_content %}
{% endblock %}
</div>
</div>
{% endblock %}
================================================
FILE: tedimg/templates/base.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>
<title>{{ config["SITE_NAME"] }}</title>
<script src="/static/app.js"></script>
</head>
<body>
<nav class="blue" role="navigation">
<div class="nav-wrapper container">
<i class="material-icons left">image</i><a href="/" class="brand-logo">{{ config["SITE_NAME"] }}</a>
</div>
</nav>
{% block content %}
{% endblock %}
<footer class="page-footer blue">
<div class="footer-copyright">
<div class="container">
<span class="right"><i class="material-icons tiny">call_split</i> on <a class="white-text" href="https://github.com/kaiyou/tedimg">Github</a>.</a>
</div>
</div>
</footer>
</body>
</html>
================================================
FILE: tedimg/templates/error.html
================================================
{% extends "base.html" %}
{% block content %}
<div class="section no-pad-bot" id="index-banner">
<div class="container">
<h1 class="red-text">Upload failed!</h1>
<h2>{{ message }}</h2>
<br><br>
</div>
</div>
{% endblock %}
================================================
FILE: tedimg/templates/index.html
================================================
{% extends "banner.html" %}
{% block banner_content %}
<h1 class="header center orange-text">Upload your image!</h1>
<form method="post" id="upload" action="{{ url_for('upload') }}" enctype="multipart/form-data">
<div class="file-field input-field">
<div>
<i class="material-icons left small">publish</i>
<input type="file" name="file" accept="image/*">
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text" placeholder="Select your file">
</div>
</div>
<div class="input-field">
<i class="material-icons prefix">public</i>
<input id="icon_prefix" type="text" class="validate" name="url" placeholder="Or paste your URL">
</div>
</form>
{% endblock %}
================================================
FILE: tedimg/templates/show.html
================================================
{% extends "banner.html" %}
{% block banner_content %}
<h1 class="header center orange-text">Upload successful!</h1>
<div class="row center">
<a href="{{ image }}" class="btn-large blue">Direct link</a>
<a href="{{ thumb }}" class="btn-large blue">Thumbnail</a>
</div>
<div class="row center">
<img src="{{ thumb }}">
</div>
{% endblock %}
{% block section_content %}
<div class="row">
<div class="col s6">
<h5 class="center">Full size</h5>
<div class="input-field">
<i class="material-icons prefix">image</i>
<input class="snippet" type=text value="{{ image }}">
</div>
<div class="input-field">
<i class="material-icons prefix">code</i>
<input class="snippet" type=text value="<img src="{{ image }}">">
</div>
<div class="input-field">
<i class="material-icons prefix">chat</i>
<input class="snippet" type=text value="[img]{{ image }}[/img]">
</div>
<div class="input-field">
<i class="material-icons prefix">subject</i>
<input class="snippet" type=text value="">
</div>
</div>
<div class="col s6">
<h5 class="center">Thumbnail</h5>
<div class="input-field">
<i class="material-icons prefix">image</i>
<input class="snippet" type=text value="{{ thumb }}">
</div>
<div class="input-field">
<i class="material-icons prefix">code</i>
<input class="snippet" type=text value="<a href="{{ image }}"><img src="{{ thumb }}"></a>">
</div>
<div class="input-field">
<i class="material-icons prefix">chat</i>
<input class="snippet" type=text value="[url={{ image }}][img]{{ thumb }}[/img][/url]">
</div>
<div class="input-field">
<i class="material-icons prefix">subject</i>
<input class="snippet" type=text value="[]({{ image }})">
</div>
</div>
</div>
{% endblock %}
================================================
FILE: tedimg/views.py
================================================
from tedimg import app, images
import flask
import urllib
import os
@app.route('/')
def index():
return flask.render_template("index.html")
@app.route('/show/<path:path>')
def show(path):
root = flask.url_for("index", _external=True)
image, thumb = images.get_image(root, path)
return flask.render_template("show.html", image=image, thumb=thumb)
@app.route('/upload', methods=['POST'])
def upload():
url = flask.request.form.get('url')
uploaded = flask.request.files.get('file')
# Get an image object from the uploaded image or URL
try:
if uploaded:
image = images.image_from_file(uploaded)
filename = os.path.basename(uploaded.filename)
elif url:
image = images.image_from_url(url)
parsed = urllib.parse.urlparse(url)
filename = os.path.basename(parsed.path)
else:
return flask.render_template("error.html", message="Missing image.")
except Exception as error:
__import__("traceback").print_exc()
return flask.render_template("error.html", message="Could not store your image.")
# Save the image to a local file
result = images.save_with_thumbnail(image, filename)
return flask.redirect(flask.url_for("show", path=result))
================================================
FILE: webpack.config.js
================================================
var webpack = require("webpack");
var path = require("path");
module.exports = {
mode: 'development',
entry: './resources/main.js',
output: {
filename: 'app.js',
path: path.resolve(__dirname, 'tedimg/static'),
publicPath: '/static/'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'resolve-url-loader']
},
{
test: /\.(png|woff2?)$/,
use: ['file-loader']
}
]
}
}
gitextract_rj62ikr9/ ├── .dockerignore ├── Dockerfile ├── LICENSE.md ├── README.md ├── docker/ │ ├── nginx.conf │ └── supervisor.conf ├── package.json ├── requirements.txt ├── resources/ │ ├── main.css │ └── main.js ├── run.py ├── tedimg/ │ ├── __init__.py │ ├── images.py │ ├── static/ │ │ └── empty │ ├── templates/ │ │ ├── banner.html │ │ ├── base.html │ │ ├── error.html │ │ ├── index.html │ │ └── show.html │ └── views.py └── webpack.config.js
SYMBOL INDEX (7 symbols across 2 files) FILE: tedimg/images.py function get_image (line 11) | def get_image(root, name): function image_from_file (line 19) | def image_from_file(file_storage): function image_from_url (line 26) | def image_from_url(url): function save_with_thumbnail (line 34) | def save_with_thumbnail(image, filename): FILE: tedimg/views.py function index (line 9) | def index(): function show (line 14) | def show(path): function upload (line 21) | def upload():
Condensed preview — 21 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (15K chars).
[
{
"path": ".dockerignore",
"chars": 25,
"preview": "node_modules/\nDockerfile\n"
},
{
"path": "Dockerfile",
"chars": 452,
"preview": "FROM python:3-alpine\n\nRUN apk add --no-cache nginx supervisor\n\nADD . /app\nWORKDIR /app\n\nRUN apk add --no-cache nodejs \\\n"
},
{
"path": "LICENSE.md",
"chars": 235,
"preview": "\"THE BEER-WARE LICENSE\" (Revision 42):\nkaiyou wrote this file. As long as you retain this notice you\ncan do whatever you"
},
{
"path": "README.md",
"chars": 596,
"preview": "Image upload with Flask\n=======================\n\nNo database, no additional features. Plain and simple image upload.\n\n!["
},
{
"path": "docker/nginx.conf",
"chars": 1174,
"preview": "user nginx;\nworker_processes 4;\npid /run/nginx.pid;\ndaemon off;\n\nevents {\n\tworker_connections 768;\n}\n\nhttp {\n\tsendfile o"
},
{
"path": "docker/supervisor.conf",
"chars": 260,
"preview": "[supervisord]\npidfile=/var/run/supervisord.pid\nnodaemon=true\nloglevel=DEBUG\n\n[program:nginx]\ncommand=nginx -c /app/docke"
},
{
"path": "package.json",
"chars": 451,
"preview": "{\n \"name\": \"tedimg\",\n \"scripts\": {\n \"watch\": \"webpack -w\",\n \"build\": \"webpack -p\"\n },\n \"dependencies\": {\n \""
},
{
"path": "requirements.txt",
"chars": 78,
"preview": "Flask==1.0.2\nPillow==5.1.0\nrequests==2.18.4\ngunicorn==19.8.1\nWerkzeug==0.14.1\n"
},
{
"path": "resources/main.css",
"chars": 1151,
"preview": "/* Browser specific (not valid) styles to make preformatted text wrap */\n\npre {\n white-space: pre-wrap; /* css-3 *"
},
{
"path": "resources/main.js",
"chars": 518,
"preview": "// Import general CSS\nimport 'materialize-css';\nimport 'materialize-css/dist/css/materialize.css';\n\n// Import specific C"
},
{
"path": "run.py",
"chars": 306,
"preview": "from tedimg import app\nfrom flask import send_from_directory\n\nimport os\n\napp.config.update(\n SITE_NAME=\"TeDomum Image"
},
{
"path": "tedimg/__init__.py",
"chars": 666,
"preview": "from flask import Flask\n\nimport os\n\napp = Flask(__name__)\napp.debug = \"FLASK_DEBUG\" in os.environ\n\napp.config.update(\n "
},
{
"path": "tedimg/images.py",
"chars": 1565,
"preview": "from tedimg import app\nfrom PIL import Image, ImageSequence\n\nimport os\nimport binascii\nimport requests\nimport io\nimport "
},
{
"path": "tedimg/static/empty",
"chars": 0,
"preview": ""
},
{
"path": "tedimg/templates/banner.html",
"chars": 334,
"preview": "{% extends \"base.html\" %}\n\n{% block content %}\n<div class=\"section no-pad-bot\" id=\"index-banner\">\n <div class=\"containe"
},
{
"path": "tedimg/templates/base.html",
"chars": 856,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n <meta n"
},
{
"path": "tedimg/templates/error.html",
"chars": 240,
"preview": "{% extends \"base.html\" %}\n\n{% block content %}\n<div class=\"section no-pad-bot\" id=\"index-banner\">\n <div class=\"containe"
},
{
"path": "tedimg/templates/index.html",
"chars": 738,
"preview": "{% extends \"banner.html\" %}\n\n{% block banner_content %}\n<h1 class=\"header center orange-text\">Upload your image!</h1>\n<f"
},
{
"path": "tedimg/templates/show.html",
"chars": 1940,
"preview": "{% extends \"banner.html\" %}\n\n{% block banner_content %}\n<h1 class=\"header center orange-text\">Upload successful!</h1>\n<d"
},
{
"path": "tedimg/views.py",
"chars": 1291,
"preview": "from tedimg import app, images\n\nimport flask\nimport urllib\nimport os\n\n\n@app.route('/')\ndef index():\n return flask.ren"
},
{
"path": "webpack.config.js",
"chars": 481,
"preview": "var webpack = require(\"webpack\");\nvar path = require(\"path\");\n\nmodule.exports = {\n mode: 'development',\n\n entry: './re"
}
]
About this extraction
This page contains the full source code of the Orbiter/tedimg GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 21 files (13.0 KB), approximately 4.1k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.