Workflow is an iOS app that lets you build a simple program by
dragging blocks around, similar to Apple’s Automator app that ships with macOS.
A recent update makes it possible to send a wider variety of
HTTP requests, which allows you to interact with web APIs that aren’t otherwise
supported.
Or, if you have a web server, write your own API.
Upload images workflow
Here is a workflow to take images from my phone and upload them to my server.
It makes one request per image.
It sets the custom header Grigg-Authentication to make sure that random people
aren’t uploading images.
It puts a file into the POST request with field name image.
The responses will be HTML image tags, which are collected and then
copied to the clipboard.
Flask
Flask is a Python web framework.
It makes it very easy to map URLs to Python functions.
The first thing I wrote was a private decorator, that would check the
HTTP headers for my authentication key. It doesn’t have to be a decorator, but
that makes it easier to reuse in the future.
If you are not using a secure (HTTPS) connection, somebody could read your
authentication key and pretend to be you.
You can set this up directly with Flask, but since I’m already
running nginx, I used that.
(I will share the details in a future post.)
Next, there is some basic Flask setup.
I changed the response MIME type to plain text
and registered an error handler that will report any exceptions
in the response, rather than logging an error where I won’t see it.
Then, there is the routing code. This function is called
every time someone visits /blog/upload-image,
as specified in the route decorator.
1
2
3
4
5
6
7
8
9
10
11
@app.route('/blog/upload-image',methods=['POST'])@privatedefblog_upload_image():try:fh=flask.request.files['image']exceptKeyError:flask.abort(400,'Expected a file with key "image", not found')_,extension=os.path.splitext(fh.filename)filename=upload_image(fh,extension)return'<img src="{}" class="centered">\n'.format(filename)
Finally, the actual work is done by the upload_image function.
I save the image into a dated directory with a random filename, then
run a bunch of git commands.
classError(Exception):passdefrandom_chars(size):returnbase64.b32encode(uuid.uuid4().bytes).decode('ascii').lower().rstrip('=')[:size]defupload_image(fh,extension):"""Upload image to blog and return filename, relative to site root."""subdir='images/{:%Y}'.format(datetime.datetime.today())try:os.mkdir(os.path.join(PATH,subdir))exceptFileExistsError:passbasename=''.join((random_chars(8),extension))filename=os.path.join(subdir,basename)fh.save(os.path.join(PATH,filename))output=[]defrun(args):output.append(' '.join(args))output.append(subprocess.check_output(args,cwd=PATH,stderr=subprocess.STDOUT))try:run(['git','pull','--ff-only'])run(['git','add',filename])run(['git','commit','-m','Add image'])run(['git','push'])exceptsubprocess.CalledProcessError:raiseError('Git operation failed. Output:\n{}'.format('\n'.join(output)))returnfilename