Deploy client-side (static) web app built by Flet

I edited width, alignment, etc. of my Flet code originally written as a desktop app to make it work as a web app. In this post I’m going to introduce how to build and deploy your Flet app as a static website (client-side, HTML + JavaScript). This deployment should work on an ordinary web hosting server, and you don’t need to know about web server side technology. You can learn how to add Google AdSense advertisement to your Flet web app as well.

Preparation

Please refer to my previous post and build your environment. It’s mainly targeting macOS. The sample Flet app code is used in this post.

How to build Python GUI app on macOS with Flet

In case you’re looking for server side deployment

My another post below introduces how to depoloy your web app on an Apache web server. Should you have a Nginx web server, refer to the official guide.

Host Flet web app behind Apache web server by Reverse Proxy

Build as a web app

Complete the preparation steps 1 through 12 in the other post. Build options won’t make much difference for web, so simply run the command below. It takes some time to complete.

flet build web

Test locally

Built files are placed in build/web. Let’s test locally before pushing to a server. Execute the command and open the URL (http://localhost:8000) in your web browser.

python -m http.server --directory build/web
# Press Ctrl + C to exit.
On Chrome it works as expected.

Little more steps to upload

Specify the directory name

In this example, the web app will be deployed to https://blog.peddals.com/fletpassgen, so change the path in index.html. (By adding a build option --base-dir "/directoryname/" you can avoid this step, but you cannot test locally.) Edit index.html like the below. Make sure you have the directory name between slashes (/).

  <base href="/fletpassgen/">

Compress the entire folder

Change the folder name from web to the directory name, and compress it as a single file. You get fletpassgen.tar.gz as a result of these commands:

cd build
mv web fletpassgen
tar cvzf fletpassgen.tar.gz fletpassgen

Upload and extract

Upload the compressed file

To upload the compressed file to a hosting server, this example uses the scp command in Terminal.app. Replace username, hostname and upload directory based on your account details.

scp fletpassgen.tar.gz username@hostname:~/public_html

Login server and extract the file

If ssh is allowed, login your server and extract the file like the below. The directory has to be extracted in the correct location. In this example the web app will be in the subfolder /fletpassgen/ so it’s extracted in the document root of the website.

ssh username@hostname
cd ~/public_html
tar xvf fletpassgen.tar.gz
rm fletpassgen.tar.gz

Access the site by web browser

Your web app is ready now. URL should be like this: https://blog.peddals.com/fletpassgen/

After showing an icon for some moments then your web app will start working.

Few things to check if it’s not working

With this building method (static web app), the total size of files tends to be big. My example resulted 28MB in total. As the first access will take some time to download all required files, you have to be patient and wait until the app to be ready to start.

In case you don’t even see the icon after several seconds, take a look into the directory name in index.html, actual name of the extracted directory, user/group ownership and access permissions.

Tips and notes

Use same code for desktop and web apps

You may notice the layout of contents is broken when opening your app in web browser (I did!) Use ft.Container to place contents and width= property having the same value as page.window_width= so the horizontal layout will be kept in a wider window. For a simple app, having the below parameters keeps your app at the top center even in a web browser.

    page.vertical_alignment = ft.MainAxisAlignment.START
    page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

File size is big

As I wrote, even this small app (Python code is approx. 3.9KB) becomes 28MB in total after a build. You need to put your eye on the available disk space.

App keeps running once loaded

Since this deployment method does not require a code to be running on the web server, your app keeps running in a web browser even when the network is down. For a simple tool it can be an advantage (I don’t know who needs a new password when offline, though).

Copy button won’t work on Safari (macOS and iOS)

This is a known issue. Hopefully it will be resolved in the near future, but at this moment copy works on Chrome but not on Safari. I added a code to hide the copy button based on the user agent, but it does not work. flet build web deployment won’t be able to get user agent unfortunately. You need to deploy as a server-side app if you want to add browser specific features.

Bonus: add Google AdSense advertisement

You can find this type of information for Flutter quite easily, but some of the ways I found didn’t work for my Flet app. If you’re looking for a solution, try this:

Get AdSense strings

Login your Google AdSense account, create new ad or click on the Get code < > icon of an existing ad to get strings.

Google AdSense > Ads > By ad unit > Display ads > Give it a name and Create > take note of the below two lines.

             data-ad-client="xxxxxxxx"
             data-ad-slot="yyyyyyyy"

Add style to index.html

Edit the index.html file in the Flet web app directory and add the below CSS code, right above the </style> tag. Line numbers are just reference (Flet ver. 0.19.0).

    footer{
        width: 100%;
        height: 100px;
        text-align: center;
        padding: 0;
        position: absolute;
        bottom: 0;
        z-index: 100;
    }

Add <footer></footer> block to index.html

Right above the last line </body></html> of the index.html, add the below code. Replace highlighted xxxxxxxx and yyyyyyyy with strings you copied.

  <footer>
    <style>
    .example_responsive_1 { width: 320px; height: 100px; }
    @media(min-width: 500px) { .example_responsive_1 { width: 468px; height: 60px; } }
    @media(min-width: 800px) { .example_responsive_1 { width: 728px; height: 90px; } } 
    </style>
        <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
        <!-- Home_Page -->
        <ins class="adsbygoogle"
             style="display:inline-block"
             data-ad-client="xxxxxxxx"
             data-ad-slot="yyyyyyyy">
        </ins>
        <script>
    (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
  </footer>

Save the file. You’ll see a horizontal ad at the bottom of the web page. If you just created a new ad, it may take some time for it to appear. Enjoy!

Image by Stable Diffusion

Date:
2024-Jan-29 0:04:44

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
masterpiece, best quality, retro future, successful upload of application

Exclude from Image:

Seed:
3400661084

Steps:
20

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

How to build Python GUI app on macOS with Flet

Flet version 1.18.0 released on the last day of year 2023 introduced a newer way of building apps that run on other computer, smartphone or website without setting up Python and Flet environment. I tried to build a macOS app and encountered a weird issue — a plain window opened with a text “Hello, Flet!” and I could not figure out how to fix it. A few days later, newer version 1.19.0 was released, and it had multiple fixes for build related issues. In this post I’d like to share how I built macOS app in Python with Flet. Once you set up your development environment, building process is really straight forward.

Official announcement in discord: https://discord.com/channels/981374556059086931/981375816489402408

Official GitHub change log: https://github.com/flet-dev/flet/blob/main/CHANGELOG.md

Official flet build guide: https://flet.dev/docs/guides/python/packaging-app-for-distribution

Quick introduction of my sample code

As an example I’m going to use my simple Password Generator app. The main purpose of this post is to introduce how to build GUI app with Flet, so I’m not going to write about too much of this sample code. As you can see in the screenshot below, you can edit number of characters and special characters to use. Copy button copies the password to clipboard. It’s fun to see how strong passwords the app generates by checking with online services like this one (bitwarden).

If you’re interested you can grab the code from this GitHub: https://github.com/tokyohandsome/passgen.py/blob/main/fletpassgen.py

Sample app you can build by following this guide. Number and special character set are editable.

Environment

  • macOS: 14.2.1
  • Python: 3.11.6
  • Python virtual environment: pipenv (version: 2023.10.24)
  • Modules: flet (version: 0.19.0)
  • Rosetta
  • Xcode: 15.2
  • Git: 2.29.2
  • cocoapods: 1.14.3_1
  • Flutter: 3.16.7

High level steps

  1. Build a virtual environment.
  2. Install Flet (version 0.18.0 or newer. 0.19.0 used in this post)
  3. Write a Flet app code or grab one.
  4. Test the code in your environment.
  5. Install requirements such as Flutter to build Flet app.
  6. Create a folder and work in there.
  7. Create assets folder and place an icon image.
  8. Copy your Flet code as main.py.
  9. Change the last part of main.py to ft.app(main).
  10. Create requirements.txt and add required modules.
  11. Clone build template from official GitHub.
  12. Edit copyright in the template.
  13. Build your app.

Little more detailed initial steps (#1~4)

Use your favorite virtual environment tool. Python version has to be 3.8 or newer. Install flet by executing pip install flet without specifying the version or flet==1.19.0. If you have not written your Flet app code yet, grab one from somewhere like the official site or from my GitHub if you’d like. In this post I’m going to use the fletpassgen.py as an example. Confirm it works by python3 fletpassgen.py then move forward.

Install requirements such as Flutter to build Flet app. (step #5)

To build as a desktop/smartphone/web app, you need to install Flutter, Dart, and a few other stuff based on the requirements. Once this process is done, you don’t need to redo unless anything goes wrong. Below steps are for Apple Silicon macs specifically. Skip any step if you’re already done.

1. Rosetta: Execute below to install.

sudo softwareupdate --install-rosetta --agree-to-license

2. Xcode 15: Download Xcode 15 from Apple’s website. Click on Download at the top of the page then double click on the installer.

3. Cocoapods: Execute below to install.

brew install cocoapods

4. Git: Execute below to install.

brew install git

5. Flutter: Follow the steps at the middle of the page and download Flutter SDK for your CPU (Intel or Apple Silicon such as M1, M2, M3…). Move the zip file to somewhere like ~/development/, unzip (double click), and add path to the commands to your PATH variable (rename handsome to your username, add below line to your ~/.zshrc then execute source ~/.zshrc to load the setting).

export PATH="/Users/handsome/development/Flutter/flutter/bin:$PATH"

Preparation for build (steps #6~12)

When you’re done all the above steps, create an app folder, go inside and perform the remaining steps. In this example the app name is fletpassgen.

mkdir fletpassgen
cd fletpassgen
mkdir assets
open assets

The above last command opens the assets folder in Finder where you can place a 512×512 pixel icon image named like icon.png (supported formats include .bmp, .jpg, and .webp). I don’t have anything else, but you can place other resources such as audio and text files used by our code.

Side note about icon: Flet can build app without an icon provided - it uses its own icon. I used Mochi Diffusion, a desktop app version of the AI image generator Stable Diffusion to generate the app icon which is also the top of this post. Prompts, model, etc. can be found at the last part of this post.

Next, copy your Flet app code as main.py.

cp ../fletpassgen.py main.py

If the last line of the code to call the main function is not ft.app(main), change so.

#if __name__ == "__main__":ft.app(target=main) <-- this needs to be changed to the below:
ft.app(main)

requirements.txt has to have needed Python module to run your Flet app, but pip freeze > requirements.txt caused several errors for me. In this example, flet was only needed (other imported modules are Python built-in). See the official guide for details especially when you’re building iOS or Android app.

flet

You can clone the build template from the official GitHub to your local folder and edit copyright which can be shown by Command + I (macOS). Use your favorite editor if you are not good at the vi (vim) editor.

git clone https://github.com/flet-dev/flet-build-template
vi flet-build-template/cookiecutter.json

I edited highlighted lines 7-9 of cookiecutter.json in the flet-build-template folder.

{
    "out_dir": "",
    "python_module_name": "main",
    "project_name": "",
    "project_description": "",
    "product_name": "{{ cookiecutter.project_name }}",
    "org_name": "com.peddals",
    "company_name": "Peddals.com",
    "copyright": "Copyright (c) 2024 Peddals.com",
    "sep": "/",
    "kotlin_dir": "{{ cookiecutter.org_name.replace('.', cookiecutter.sep) }}{{ cookiecutter.sep }}{{ cookiecutter.project_name }}{{ cookiecutter.sep }}",
    "hide_loading_animation": true,
    "team_id": "",
    "base_url": "/",
    "route_url_strategy": "path",
    "web_renderer": "canvaskit",
    "use_color_emoji": "false"
}

Build your app (final step #13)

If you don’t care of options, simply execute flet build macos and you get an executable app. Below longer command adds version number and the above edited copyright. (Correction on Jan 25, 2024, thanks to the issue answered by the author.) To specify the build template location, use --template option followed by a relative path.

flet build macos --build-version "1.0.1" --template flet-build-template

Give it some time to complete and when you see Success! congrats, your app is built! Under build/macos/ you can find your app fletpassgen.app. It took around 3 mins and 10 secs to build it on my M1 mac mini. Output of the successful build process log is like below:

Creating Flutter bootstrap project...OK
Customizing app icons and splash images...OK
Generating app icons...OK
Packaging Python app...OK
Building macOS bundle...OK
Copying build to build/macos directory...OK
Success!
Screenshot in color

App is Universal

Type: Application (Universal). See version 1.0.1 and Copyright (by Cmd + I) as well.

Just like other mac apps, you can move the app to your Applications folder and double-click to launch. I was not asked to approve in the Privacy and Security. Not tested yet, but it should run on an Intel mac as well since it’s a Universal app. It opens and works just like the Python code. My sample app opens the Flet default size window momentarily then resizes as specified – I suppose there’s a better way to code.

Do I like it?

I love it! I used to use tkinter and pysimplegui to build desktop apps, but Flet is much easier with better looking/modern interface. Building process is straight forward, and app works great. Just like 3D printer changed productivity of nonprofessional DIYers in the real world, Flet lets you make your ideas real on your computer and share with your family, friends, colleagues and others. Hope you find it useful and enjoy as well!

Image by Stable Diffusion

Date:
2024-Jan-15 23:05:04

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
masterpiece, best quality, retro future, cyber, disco computer, password generator

Exclude from Image:

Seed:
3224310018

Steps:
20

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

(solved) macOS Finder didn’t open Documents and subfolders directly.

Not sure when it started, but Finder didn’t open ~/Documents and its subfolders directly. Somehow I managed to get it fixed. If you have the same issue, it can be fixed.

Decided to troubleshoot (finally).

Current running macOS version is Sonoma 14.2.1. I might have first noticed this issue when the macOS version was Big Sur 11.0 — Finder didn’t open folder by executing open ~/Documents/Python/fuga in Terminal.app, but it was not a big deal at that time. Today, I was playing with GitHub Desktop app and once again noticed the issue — the Show in Finder button didn’t open the local repo but my home directory instead. Something was (still) wrong.

↑ this button opened the home directory ↓ instead of the local repo folder (my UI is Japanese.)
Local repo located a couple folders under the Documents folder

Symptom

Open command in Terminal didn’t open any folder under ~/Documents in Finder. I was able to navigate to lower levels folders in the same Finder window, so I had the right permissions. Subfolders in ~/Music, ~/Downloads, etc. had no problem to be opend. Go menu > Recent Folders and right-click > Open in New Tab resulted the exact same issue. Only Documents and its subfolders didn’t open directly in Finder in any way.

How I fixed it.

I tried multiple things for a couple hours and nothing helped. Finally, changing the folder view to List from Column seemed to have worked. Little more details follow: Open the Documents folder in Finder then select List from the View icon or drop-down.

or List from drop-down

From the window’s action menu or Finder’s View menu, select Show View Options to open the settings window. Command + J shortcut does the same thing.

Check “Always open in list view” then click on “Use as Defaults” at the bottom and close the window. Now the Documents and its subfolders open in the list view directly by the open command in Terminal.

I won.

Finder still changes view of window somehow whether you like it or not. So, I moved around/opened new windows/changed views/closed windows several times to reproduce the issue, but it never happened again (so far). I don’t know if the above steps was the right way, but the issue is gone.

Things that I tried but didn’t work/help, just for your info.

  • Search in the internet — could not find the exact same issue.
  • Reboot
  • Boot in safe mode.
  • Run Disk First Aid
  • Remove all app’s Documents folder access permissions, reboot and granted again.
  • Compare permissions of Documents and other folders.
  • Check Console.app

Image by Stable Diffusion

Date:
2024-Jan-3 17:56:25

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
comicbook-style, gray hair guy looking for a missing folder in a book library

Exclude from Image:

Seed:
2520942867

Steps:
20

Guidance Scale:
20.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

Host Flet web app behind Apache web server by Reverse Proxy

Flet, desktop and web app framework is really useful for Python developers. With only little modifications, your standalone desktop app can be hosted on a web server. In this post you can find how to self-host your Flet app on an Apache web server. This is not covered in the Flet official website.

Quick introduction of Flet.

Flet is a Python framework to build desktop or web application without having knowledges of GUI or web frontend. Flet is developed based on Flutter mobile app framework developed by Google for the Dart language. You’ll find word “Flutter” when you writing codes and getting errors with Flet. I’m not going to provide more information around Flet or Flutter in this post.

What I’m going to demonstrate in this post

The goal is publish a Flet app on an Apache web server using TCP port based reverse proxy. In my case the web app is only accessible within the LAN. Should you have a publicly accessible Apache server (and admin privileges), you can publish your app to the public. The Flet official webpage Self Hosting introduces the process to publish on an NGINX web server. You can do pretty much the same thing on an Apache web server by following this post.

Environment

  • Ubuntu 20.04 LTS
  • Apache 2.4.41

High-level steps

  1. Install requirements on Ubuntu server.
  2. Build a Python virtual environment and install Flet.
  3. Prepare a Flet app code.
  4. Enable Apache modules required for reverse proxy.
  5. Write an Apache configuration file.
  6. Write an auto-start configuration file.

Detailed steps

Install requirements on Ubuntu server.

As introduced in the official website, you need to install GStreamer to execute Flet app on a Linux server. Simply follow the steps and install requirements.

sudo apt-get update
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio

Build a Python virtual environment and install Flet.

I use pipenv as below. Use your preferred virtual env and install Flet. Flet supports Python 3.8 and above.

pipenv --python 3.11
pipenv shell
pip install flet

Prepare a Flet app code.

Just for testing, let’s use a code posted on the official website. To check simple interaction, I copied Counter app and saved as counter.py. Change the last line as below.

ft.app(target=main, view=None, port=8501)

Quick explanation: view=None won’t open GUI or web browser window, and port=8501 sets the TCP port to listen to. As long as it does not conflict on your server, any port number works fine. By executing python3 counter.py, you can see the app on web browser if installed on your web server by opening http://localhost:8501. Next step is publishing to external access.

Enable Apache modules required for reverse proxy.

At least following 4 modules are required to configure Apache as a reverse proxy. As Flet uses web socket, wstunnel is also required. Following commands enable modules, load modules and check Apache status respectively.

sudo a2enmod proxy proxy_http proxy_wstunnel headers
sudo systemctl restart apache2
sudo systemctl status apache2

Write an Apache configuration file.

In this example, accessing flet.dev.peddals.com will open the Flet web app. In my environment, access to the subdomain uses always HTTPS as posted separately. So, listening port is 443, and reverse proxy port is 8501 that the Flet app is listening to. Please edit these based on your environment.

Line 13-14 for wss:// may not be required.

<VirtualHost *:443>
	ServerName flet.dev.peddals.com

	SSLEngine on
	SSLCertificateFile /etc/letsencrypt/live/dev.peddals.com/fullchain.pem	
	SSLCertificateKeyFile /etc/letsencrypt/live/dev.peddals.com/privkey.pem

	ProxyRequests Off
	ProxyPreserveHost On

	ProxyPass /ws ws://localhost:8501/ws
	ProxyPassReverse /ws ws://localhost:8501/ws
	ProxyPass /ws wss://localhost:8501/ws
	ProxyPassReverse /ws wss://localhost:8501/ws
	ProxyPass / http://localhost:8501/
	ProxyPassReverse / http://localhost:8501/

	ErrorLog ${APACHE_LOG_DIR}/flet.error.log
	CustomLog ${APACHE_LOG_DIR}/flet.log combined

</VirtualHost>

Load Apache configuration (check syntax, load config, and check status).

sudo apachectl configtest
sudo systemctl reload apache2
sudo systemctl status apache2

Now, let’s execute python3 counter.py and check if the web app opens from a client PC. If you removed the lines for wss:// and the app kept loading, add them, reload, and try again.

Write an auto-start configuration file.

Let’s follow the Flet official page and write an auto-start configuration file. Below is an example in my server. Save this as fletcounter.service.

[Unit]
Description=Flet Counter Service
After=network.target

[Service]
User=handsome
Group=handsome
WorkingDirectory=/home/handsome/codes/flet
Environment="PATH=/home/handsome/.local/share/virtualenvs/flet-xuR7EMBP/bin/"
ExecStart=/home/handsome/.local/share/virtualenvs/flet-xuR7EMBP/bin/python3 /home/handsome/codes/flet/counter.py

[Install]
WantedBy=multi-user.target

Modifications:

  • Description= as you like
  • User= and Group= your own username (whoami)
  • WorkingDirectory= is the full path to the directory of counter.py.
  • Environment="PATH= is the full path to the directory of python3 (output of which python3 up to bin/)
  • ExecStart= first arg is full path to Python3 (output of which python3), and the second arg is the full path to the Flet app.

Lastly, start and enable it as a service by following the official page. The target of the symbolic link (#2) is the file created in the previous step.

cd /etc/systemd/system
sudo ln -s /home/handsome/codes/flet/fletcounter.service
sudo systemctl start fletcounter
sudo systemctl enable fletcounter
sudo systemctl status fletcounter

That’s all. Access your app from a client PC and confirm the counter opens. When possible, reboot your server and confirm the service starts automatically.

Trouble that I encountered.

In my environment, loading of the app kept forever initially. I finally figured out that the reverse proxy settings needed wss:// as well as ws:// (the NGINX config on the official page does not have wss:// either). It took me some time to figure out that wss stood for Web Socket Secured, just like https stood for http Secured. However, another Apache server doesn’t require wss — my SSD for the web server (Raspberry Pi) died after reverse proxy setup, and needed to build another. I’m still not sure why wss was required…

Image by Stable Diffusion

Date:
2023-Nov-25 23:02:10

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
cartoon, clolorful,
modern ladies working at post office classifying letters

Exclude from Image:

Seed:
4084494267

Steps:
23

Guidance Scale:
11.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

Install Let’s Encrypt certificate in a Ubuntu development web server in the closed LAN

Once HSTS preload was approved, my dev sites became HTTPS enforced.

I was happy with the results of HSTS preload submitted and approved for websites that I manage for business at HSTS preload. So, I submitted the same request for my personal domain, peddals.com. Once it was approved, my dev-sites/services running in my home office LAN became inaccessible as they did not hire secured http (HTTPS). In this article I’m going to share how I installed Let’s Encrypt certificate in a web server running on Apache/Ubuntu inside closed LAN.

My environment and requirements

  • Raspberry Pi 3 model B
  • Ubuntu 20.04 LTS
  • Apache 2.4.41
  • Domain name
  • Access rights to manage DNS records (TXT)

High level step

  1. Install certbot.
  2. Run certbot command (with DNS challenge option).
  3. Add TXT record to DNS as requested.
  4. Let Let’s Encrypt complete certificate installation.
  5. Enable SSL in Apache.
  6. Change TCP port in Apache config file to 443 and add other settings for SSL.

Detailed step

Obtain certificate (CLI + DNS record)

First, you need to install certbot to install a certificate. In the first line below you execute elevated bash so you don’t need to add sudo every time. $ is normal user shell prompt and # is administrator level shell prompt. Others are output examples.

$ sudo bash
# apt-get update
# apt-get install certbot

In my case, I submitted requests for *.dev.peddals.com for all dev sites and the top level domain — I may post another article, but on my mac DNSmasq is running as a local DNS server, and access to *.dev.peddals.com is diverted to my Raspberry Pi Apache server.

# certbot certonly --manual --preferred-challenges dns-01 -m mail@example.com -d '*.dev.peddals.com' -d peddals.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

After the above the command froze for some reason. Aborted by ctrl + C and rerunthe certbot to proceed the process.

I think sharing email address (above) is NOT required however, sharing IP address IS required, so answer Y(es) to the below question.

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for dev.peddals.com
dns-01 challenge for peddals.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.dev.peddals.com with the following value:

(your value here)

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

Add the hostname and value as a TXT record in your DNS server. Sorry for the Japanese characters but the below reads from the top to the bottom: Host name, Type, Value, TTL, Priority.

When you copy-paste, don’t include the TLD if it’s pre-populated.

Once added, go back to the CLI and hit the enter key. In my case I added the top level domain well, so there’s another challenge.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.peddals.com with the following value:

(your value here)

Before continuing, verify the record is deployed.
(This must be set up in addition to the previous challenges; do not remove,
replace, or undo the previous challenge tasks yet. Note that you might be
asked to create multiple distinct TXT records with the same name. This is
permitted by DNS standards.)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

Do the similar thing on your DNS server then hit the enter key to complete the process as below.

Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/dev.peddals.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/dev.peddals.com/privkey.pem
   Your cert will expire on 2024-02-10. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Path to your certs and key are written under IMPORTANT NOTES (if you forget the paths, search them in /etc/letsencrypt/live/). Let’s encrypt certs expire every 90 days, and you have to renew. Files are symbolic link to actual certs and key to avoid tasks to update Apache config file.

# ll /etc/letsencrypt/live/dev.peddals.com/
total 12
drwxr-xr-x 2 root root 4096 Nov 12 19:00 ./
drwx------ 3 root root 4096 Nov 12 19:00 ../
-rw-r--r-- 1 root root  692 Nov 12 19:00 README
lrwxrwxrwx 1 root root   39 Nov 12 19:00 cert.pem -> ../../archive/dev.peddals.com/cert1.pem
lrwxrwxrwx 1 root root   40 Nov 12 19:00 chain.pem -> ../../archive/dev.peddals.com/chain1.pem
lrwxrwxrwx 1 root root   44 Nov 12 19:00 fullchain.pem -> ../../archive/dev.peddals.com/fullchain1.pem
lrwxrwxrwx 1 root root   42 Nov 12 19:00 privkey.pem -> ../../archive/dev.peddals.com/privkey1.pem

Enable SSL in Apache and edit site config file

Enable SSL in Apache by the following command Apache. The last 2 commands are to restart and check the status of Apache.

# a2enmod ssl
Considering dependency setenvif for ssl:
Module setenvif already enabled
Considering dependency mime for ssl:
Module mime already enabled
Considering dependency socache_shmcb for ssl:
Enabling module socache_shmcb.
Enabling module ssl.
See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates.
To activate the new configuration, you need to run:
  systemctl restart apache2
# systemctl restart apache2
# systemctl status apache2

A change and additions to a http (port: 80) site config file are as below. Change the port from 80 to 443 and add SSL related settings. As this (my case) is only for dev sites in a closed environment, no other security settings such as http headers are added. Since HTTPS preload and includeSubDomains are included in my HSTS policy, there’s no need to have redirection from port 80.

<VirtualHost *:443>
	SSLEngine on
	SSLCertificateFile /etc/letsencrypt/live/dev.peddals.com/fullchain.pem	
	SSLCertificateKeyFile /etc/letsencrypt/live/dev.peddals.com/privkey.pem

Get the config syntax checked and reload the apache configurations.

# apachectl configtest
Syntax OK
# systemctl reload apache2

Test in web browser

Now you can open your site in web browsers. You don’t need to add https://.

Google Chrome

Let’s Encrypt certs expire in 90 days.

You should, even for your dev env., renew the cert before expired. You can renew in 30 days until the expiration. As this case is for test sites manual renewal is fine for now. Run the command with the --dry-run option then without it if no error reported.

# certbot renew --dry-run
# certbot renew

When you run the command when renewal is not needed, you can see the expiration date.

# certbot renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/dev.peddals.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not yet due for renewal

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

The following certs are not due for renewal yet:
  /etc/letsencrypt/live/dev.peddals.com/fullchain.pem expires on 2024-02-10 (skipped)
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Use the crontab command to automate the renewal process. It’s not covered in this post.

HSTS policy and where to add

As this website is on hosting servce, I have the .htaccess file which has the below line in the document root directory. includeSubDomains to apply the configuration to all sub-domains and preload to force access over HTTPS.

Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

Conclusion

There are multiple reasons why you want/need to install certificate in a closed environment. The steps above let you install commercial level certificate.

Image by Stable Diffusion

Date:
2023-Nov-12 17:59:44

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
cartoon, let’s encrypt, apache, success

Exclude from Image:

Seed:
356871963

Steps:
30

Guidance Scale:
11.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

Run ssh on Pythonista 3.4

I managed to run ssh.py written in Python 2 on StaSh for Pythonista 3 (ver. 3.4). Here I share what I did.

Note

The StaSh ssh command is not the best ssh client as it makes iPhone warmer than usual, and StaSh does not have dedicated control or escape virtual key which are well used in Linux/Unix systems. So, I would suggest a SSH client app such as Termius if you’r looking for a serious SSH client. This post is rather for those of you who are interested in running (converting) Python 2 scripts on Pythonista 3.4.

Pythonista 3.4 does not include Python 2.7 any longer. Scripts written in Python 2 need to be converted to Python 3 scripts. The script ssh.py comes with StaSh was written in Python 2. Converting it by the Python 2 to 3 tool was not enough.

“Python 2 to 3” tool can be found under the tool menu.

Version info (StaSh version command)

$ version
StaSh v0.8.0
Python 3.10.4 (CPython)
UI stash.system.shui.pythonista_ui
root: ~/Documents/site-packages/stash
core.py: 2023-05-05 18:00:40
SELFUPDATE_TARGET: master
Pythonista 3.4 (340012)
iOS 16.4.1 (64-bit iPhone10,2)
Platform iOS-16.4.1-iPhone10,2-64bit
BIN_PATH:
  ~/Documents/bin
  ~/Documents/stash_extensions/bin
  ~/Documents/site-packages/stash/bin

StaSh is the latest dev version as of May 12, 2023 (Please refer to another post for the StaSh installation.)

List of things I did

I restarted Pythonista 3 as needed. (You can find how to generate SSH keys in my other post. It has a tip to adjust number of rows.)

  1. Copy ~/Documents/site-packages/stash/bin/ssh.py to ~/Documents/stash_extensions/bin as ssh3.py
  2. Open ssh3.py and execute the “Python 2 to 3” tool. Apply all suggestions.
  3. Edit function vk_tapped() and treat vk.name as int (details below)
  4. Open ~/Documents/site-packages/stash/system/shscreens.py and execute the “Python 2 to 3” tool. Apply all suggestions.
  5. Replace / with // on lines 541 and 576 of shscreens.py. They are now like this: idx_line, idx_column = idx // (ncolumns + 1), idx % (ncolumns + 1)
  6. Execute the command ssh3 in StaSh

Actual code after the changes of the vk_tapped() function is below. Lines 242-262. Rename vk.name to vk, and replace'k_tab' with relative integer. By this change, virtual keys like Tab, Up, CC work on the SSH server as tab, up arrow and control+C respectively.

    def vk_tapped(self, vk):
        if vk == 7:
            self.send('t')
        elif vk == 0:
            self.kc_pressed('C', CTRL_KEY_FLAG)
        elif vk == 1:
            self.kc_pressed('D', CTRL_KEY_FLAG)
        elif vk == 6:
            self.kc_pressed('U', CTRL_KEY_FLAG)
        elif vk == 9:
            self.kc_pressed('Z', CTRL_KEY_FLAG)
        elif vk == 2:
            self.kc_pressed('UIKeyInputUpArrow', 0)
        elif vk == 3:
            self.kc_pressed('UIKeyInputDownArrow', 0)

        elif vk == 10:
            if _stash.terminal.is_editing:
                _stash.terminal.end_editing()
            else:
                _stash.terminal.begin_editing()

Actual errors and helpful info to fix

I got the below error after converting shscreens.py to a Python 3 script (step #4):

system/shscreens.py", line 578, in load_pyte_screen
    c = pyte_screen.buffer[idx_line][idx_column]
TypeError: list indices must be integers or slices, not float

Somehow adding int(idx_line) and int(idx_column) before line 578 didn’t resolve this issue. Helpful info (or answer) was found in this old issue:

My guess would be that stash uses / division for line/column indices. On Python 3 / always produces a float. The fix is simple: replace it with flooring division //.

https://github.com/selectel/pyte/issues/123

Another error that wast the reason of the change step #3 was this:

  File "stash_extensions/bin/ssh3.py", line 230, in vk_tapped
    if vk.name == 'k_tab':
AttributeError: 'int' object has no attribute 'name'

I am still not sure how this worked in Python 2. Without fixing this you can establish a VPN connection, but virtual keys won’t function. To fix this issue, I added print(vk) at the top of the def block. After connecting an SSH session, I pressed each virtual key (Tab, Up, CC, etc.), opened Console to confirm the number and replaced with the name. There should be better way but this worked.

Lastly this was the reason of my motivation to make the ssh command work.

StaSh works with both Pythonista 2 and 3, though not all commands support python3.

https://github.com/ywangd/stash

Image by Stable Diffusion

Off topic: The eye-catch image was generated by Mochi Diffusion, a Stable Diffusion client for macOS. Added “SSH” on the AI generated image using a graphic editor. Below is the details:

Date:
2023/5/6/ 22:31:24

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
cartoon, a young man waring glasses, super happy

Exclude from Image:


Seed:
3826992198

Steps:
20

Guidance Scale:
11.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

Pythonista 3.4 is out now (supporting Python 3.10)

I thought the development discontinued…, but I was wrong!

Running on iPad. Output of Stash version command. See Python is 3.10.4.

Python is now 3.10.4. Python 2 is no longer included.

After 3 years of silence, the great Python IDE for iOS/iPadOS, Pythonista 3 is finally released. You can now execute your Python 3.10 codes.

Pythonista 3 ← Link to the App Store

For details (not very much, tough), visit the official website below:

https://omz-software.com/pythonista/docs-3.4/py3/ios/new.html

Since Python 2.7 is no longer included, you cannot run codes written in Python 2.x directly. E.g. SSH command in StaSh does not run unless you make a few changes (I’ll post another article how you can make the ssh command work).

StaSh, a bash-like shell environment for Pythonista is not fully compatible yet. Installer works, and pip command is able to install packages, but somehow entries in the .stashrc file is not fully loaded – only the fist line becomes available.

StaSh installation

It is recommended to cleaninstall Pythonista 3 to install StaSh. Even in the last few days the installation process changed, so I recommend to visit the official Github constantly. For me the dev version works better (mainly ‘ls -l’). You can copy the command below and execute in Console to install the dev version.

url = 'https://raw.githubusercontent.com/ywangd/stash/dev/getstash.py'; import requests as r; exec(r.get(url).text.replace('master', 'dev'))

As advised, exit and relaunch Pythonista 3, and run launch_stash.py located in “This iPhone” to execute the StaSh shell.

Let’s install Django 4.0 (latest 4.2.1 won’t work.)

As far as I testd, Django version 4.0 can build the test page. Latest version 4.2.1 will be installed if you do not specify the version number, however it won’t run with an error regarding openssl_md5 when you launch django-admin. Copy and execute the below in StaSh.

pip install django==4.0

In my case, StaSh installation logs and pip show Django reads version 4.2.1 but in Console, import django then print(django.__version__) shows “4.0” correctly.

Anyways, after a successful installation of Django, restart Pythonista 3, launch StaSh and execute the below:

django-admin startproject mysite

You can now add an argument below to manage.py in the editor window (press and hold ▷ button then add the arg) and run.

runserver --noreload

If you see an error message “CommandError: You must set settings. ALLOWED_HOSTS if DEBUG is False“, simply ignore for now and restart Pythonista 3 then run manage.py again. Also, allow network access if asked by iOS.

If all goes well you see the URL http://127.0.0.1:8000/ . Either tap to open in Pythonista built-in browser or copy-paste in a web browser to open the page. Congrats! A rocket GIF image means your Django site is working! I have a few Django in StaSh/Pythonista articles in my website for little more detailed instructions.

I never expected an update so I’m happy.

Recently I was playing anotehr iOS app, a-Shell which is a Unix/Linux-like shell environment where you can write and run Python 3.11 codes. I like it as it’s more like a standard CLI shell with multiple programming languages, you can edit code in vim editor, etc. One thing I was disappointed about is the behavior of Django and Flask web apps — you need to open web browser and a-Shell back and forth to process the code. Pythonista 3 is a great IDE and StaSh is a nice tiny shell to play with. I expect StaSh will catch up soon. I’m back to Pythonista 3 and will post more articles.

Image by Stable Diffusion

This is totally off topic — I start adding details of Mochi Diffusion generated image when I add one as an eye-catching image. This one was generated with only 20 steps so looks bit scary, kinda typical AI generated image, but when I increased to the max 50 steps of Mochi Diffusion, people didn’t that look happy. Decided to go with more passionated image. And it should be more suitable than a free-of-use beautiful photograph of nature which isn’t related to the article at all.

Date:
2023年5月6日 14:35:09

Model:
realisticVision-v20_split-einsum

Size:
512 x 512

Include in Image:
cartoon, people happy with a new release of software

Exclude from Image:


Seed:
3343127351

Steps:
20

Guidance Scale:
11.0

Scheduler:
DPM-Solver++

ML Compute Unit:
CPU & Neural Engine

You may need to install tkinter separately if it’s gone.

Recently I started working on some small Python GUI programs. Last year on an Intel Mac I found tkinter was not good as it had some issues with Japanese input method so I gave up. It’s been more than a year and I have a M1 (arm) Mac so there might be some improvements with tkinter. Upgrading Python3 (and all programs) by brew was the first step I tried, then tkinter module was unable to be loaded any longer…

You always better read install log which gives you necessary info.

I didn’t really pay attention to the brew log when upgraded Python from 3.9.1 to 3.9.6 as it was successful and must be a minor update… Soon after the upgrade my Python scripts that import tkinter in a pipenv shell started giving me error “No module named '_tkinter'“. Lots of trials and errors referring to several web pages, tech threads, etc. didn’t help. Tried setting up pipenv from scratch, installed tcl-tk by brew, added PATH and some other tcl-tk variables to .zshrc to no avail. I finally decided to give up and went to bed – worst case scenario I need to uninstall and install all Python related things including brew… On the day-2, I was calmer than last night and started identifying the scope of the problem – ok, tkinter cannot be found even out of my pipenv. It’s really gone from macOS Big Sur at some point. Reviewed steps taken on the day-1 and it didn’t take much time to find the below:

% brew info python3
python@3.9: stable 3.9.6 (bottled)
Interpreted, interactive, object-oriented programming language
https://www.python.org/
...snip...
tkinter is no longer included with this formula, but it is available separately:
  brew install python-tk@3.9
...snip...

Aha! If I read the brew install log once completed, I didn’t need to waste 6 hours and would have a sweet dream. Anyways, if your tkinter is gone after upgrading Python by brew, just execute another command brew install python-tk@3.9 and install it as well. My scripts finally open tkinter GUI windows with no error.

Most of Python documents or webpages suggest you to try tkinter for GUI development as it’s installed by default, but it’s not true anymore especially for those of you who install Python by brew on Mac.

Example of the error in my case

% python3 -m tkinter
Traceback (most recent call last):
File "/opt/homebrew/Cellar/python@3.9/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 188, in _run_module_as_main
mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
File "/opt/homebrew/Cellar/python@3.9/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 147, in _get_module_details
return _get_module_details(pkg_main_name, error)
File "/opt/homebrew/Cellar/python@3.9/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/runpy.py", line 111, in _get_module_details
__import__(pkg_name)
File "/opt/homebrew/Cellar/python@3.9/3.9.6/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tkinter/__init__.py", line 37, in
import _tkinter # If this fails your Python may not be configured for Tk
ModuleNotFoundError: No module named '_tkinter'

Select the Python version (2.x or 3.x) per a file basis in Pythonista 3

There are two Python versions available in Pythonista 3

In Pythonista3, you can select Python version to execute your code – tap and hold the right triangle next to the file name and select 2.7 or 3.6. Usually 3.6 which is default should be fine, but in case you need to run a code written in Python 2.x you’ll need this feature.

6DF76B93-53C3-4D19-B1AD-37FD314A1BD5.jpeg

Some commands on StaSh are written in Python 2.x

Some commands on StaSh, the shell-like powerful CLI add-on to Pythonista3, are written in Python 2. So, if you run it without specifying the version, you get the warning message below.

StaSh v0.7.2 on python 3.6.1
Warning: you are running StaSh in python3. Some commands may not work correctly in python3.
Please help us improving StaSh by reporting bugs on github.

In case you need to install Python 2 packages, you have to run StaSh in Python 2.7 and the warning message won’t appear.

StaSh v0.7.2 on python 2.7.12

Selecting the version everytime you launch StaSh is not smart, is it? 

Specify the Python version to execute your code

When you write a code, it is usually meant to be executed by a specific version, either 2.x or 3.x. Just like macOS or Linux, well, even simpler, you can specify the Python version at the first line – this method is called “Shebang”.

#! python2
# coding: utf-8
# Write your Python 2 codes below

Now you got the idea – yes, add the Shebang to a copy of the launch_stash.py and add it as a shortcut along with the original code so that you can quickly launch StaSh in your desired Python version.

There are lots of useful legacy codes/packages written in Python 2 you can find in pip, in the Internet or books. Don’t spend too much time to hand-code and convert into Python 3. Simply run as-is and/or have Pythonista3 convert it to version 3 – you can find Python 2 to 3 tool in the spanner menu: 

IMG_9338.jpeg

Enjoy!

iOS Pythonista 3: Using SSH

Use SSH in Pythonista 3 (StaSh)

SSH in StaSh seems not to work if a passphrase is given to create keys. I found some hosting servers do not accept passphrase (password) to connect using SSH. In this post you can find a way to careate SSH keys in Pythonista 3 (SSH command in StaSh) with no passphrase.

Generate SSH keys in StaSh

$ ssh-keygen -t rsa -b 2048

Secret key and Public key are generated in the following path.

~/Documents/site-packages/stash/.ssh
$ ls
id_rsa id_rsa.pub

Copy the public key “id_rsa.pub” to your SSH server’s appropriate location.

SSH command to connect to your server

$ ssh username@username.xsrv.jp -p 10022

Above is an example to connect to XServer hosting server that I use. Change username, host name (“username.xsrv.jp” in this example) and port number (10022 in this case).

When you first run the ssh command in Pythonista 3, a terminal emulator “pyte” will be installed automatically. In this case you’ll see global name 'pyte' is not defined error message and need to quit and relaunch Pythonista 3.

Additional Notes – Set number of lines

Once the above is complete, you will want to add an alias (shortcut command) to the .stashrc file (Reference), copy the SSH keys to another SSH app, iOS device or computer. Make sure that you do not share “ids_rsa” the secret key file with anyone. Create .ssh folder in the same path if you use Pythonista 3 in another iOS device and place the ids_rsa file.

When connecting to a host, you will notice at least 3 lines from the top will be hidden as StaSh’s shortcut keys and iOS’s software keyboard will use 3 lines of the bottom of your screen. This is very annoying especially when you use ‘vi’ command to edit a file. To avoid this issue, one manual way is to use “stty” command to set your screen size (number of rows) every time you SSH to a server. Refer to examples below and find the best number for your environment.

$ stty rows 51 # Works best for iPad mini 2 + Bluetooth keyboard
$ stty rows 27 # Works best for iPhone 8 Plus with iOS software keyboard
Copyright (C) Peddals.com