Fendera.com Great affordable WordPress Hosting

Web development stack

I’ve built quite a few websites for clients, friends, family and for myself too in the last few years, some examples are: kunstwinder.com, southcitysmile.com or this very blog. Over time, I’ve polished the tool and techniques, to speed up development, and to increase the quality. At this point my default and preferred architecture that I recommend to most clients is WordPress + Enfold theme. There are powerful reasons behind this choice:

  • WordPress is great. Used by ~27% of the top 10M websites, with a very rich plugin ecosystem, and a visual, intuitive, web based builder. It’s built on technologies that have passed the test of time: PHP and MySQL. The web interface allows non technical customers to make tweaks and additions if they are needed after the project is finished.

 

  • Enfold by Kriesi is one of the most popular WordPress themes out there, and rightfully so. Their intuitive UI makes it straightforward to create visually compelling websites. It comes with dozens of UI elements, including contact forms, google maps, and image sliders, and they are all personalizable.

I think the quality of the examples above prove the point. The clients were extremely happy with the result, and after 2-3 years, the websites still look sleek and responsive.

Hosting….

GoDaddy

This is where the headaches begin. I started recommending my first clients to go with GoDaddy, the had made a name for themselves, their support was always available, and their cPanel web management, although far from perfect, got the job done. Finally, about the price, they weren’t so bad at the beginning, and they included some freebies such as Google Ads credit.

GoDaddy has however let me down in multiple occasions, so I have progressively pushed them down the list of recommended hosting solutions. My first problem with them were their continuous and unwarned price rises, we were paying no more than 20$ for hosting on the first year, but by the third, the bill was over 100$. They also have a very aggressive up-sale and cross-sale model, constantly and annoyingly suggesting you to buy their SSL certificate for 70$+ or to get a faster hosting (read next paragraph) for another 100$.

Secondly comes the speed, in case you don’t know, speed loading in a website is critical in this world of instant gratification, visitors don’t like waits; according to Kissmetrics, every second that passes before the web loads leads to a 7% decrease in conversions.  GoDaddy, in an attempt to cut costs, makes you share a server with hundreds if not thousands of other websites, reducing speed and responsiveness.

Lastly, they recently changed their support policy, you can’t email and are forced to call them. Their support team is helpful, but unfortunately they’ve been trained to try and sell you other services…

Getflywheel and WPEngine

These guys understand WordPress hosting: great infrastructure, free SSL certificates (they are free for them, so that makes sense), free backups and flawless user experience. I have tried both services, and really, they are good.

They have only one inconvenient: they are both alarmingly expensive. Their basic plans can set you back at 250$ per year, and it only goes up from there. Unfortunately, most people can’t justify that expense for a website with some dozen visitors per day.

Fendera

This is where fendera.com comes into the game. I’ve developed Fendera using the latest cloud computing technologies: Docker + Kubernetes. Kubernetes is legendary; created by Google as a way to ensure services such as YouTube or Gmail were available 99.99999% of the time and later released to the community. It’s not by chance that it integrates very nicely with Google Cloud, arguably one of the best cloud computing provider.

I wanted to break apart from how GoDaddy did things, and I wanted to stay close to Getflywheel or WPEngine but without forgetting about affordability. Our current plan sets you back at only 34$ per year, yet it comes with free SSL certificates, free weekly backups,  6Gb of SSD and a lot of horse power under Google Cloud’s hood. Moreover, thanks to Kubernetes we can guarantee great up times SLAs and ensure a safe and reliable service.

If you want to give it a try, please register at dashboard.fendera.com. Questions or feedback are welcome!

WordPress Enfold YouTube video only displaying link

I had a problem recently on a website using WordPress and the Enfold theme. A YouTube video that was initially marked as private, but later updated to public (embeddable) was still only showing the link, and not the actual video player.

A previously private video but later publicly released was still showing as a link on the website.

Other videos however in the same page, where showing properly.

Other videos however, as this one in the image where showing properly.
Other videos, however, as this one in the image where showing properly.

It turns out that from reading here that WordPress does some caching and is not pulling the latest youtube change.

To fix the problem, connect to the MySQL client, and retrieve all pages with an embedded video:

mysql> SELECT * FROM wp_postmeta WHERE meta_key like '%_oembed%';

My results were something like this:

+---------+---------+-----------------------------+------------------------------------------+
| meta_id | post_id | meta_key | meta_value |
+---------+---------+-----------------------------+------------------------------------------+
| 306 | 86 | _oembed_5c3aff6d73f219 | {{unknown}} |
| 336 | 10 | _oembed_39378045d82af3 | {{unknown}} |
| 337 | 10 | _oembed_a26684b605680e | {{unknown}} |
| 354 | 86 | _oembed_af60fe80d2fe16 | <iframe src="yt.com/S3edckDk_qo></iframe>|
| 355 | 86 | _oembed_time_af60fe80d2fe16 | 1503662521 |
| 356 | 10 | _oembed_f4d0e4e5251dea | <iframe src="yt.com/S3edckDk_qo</iframe> |
| 357 | 10 | _oembed_time_f4d0e4e5251dea | 1503662579 |
| 359 | 71 | _oembed_time_a26684b605680e | 1503662615 |
| 385 | 138 | _oembed_f4d0e4e5251dea | <iframe src="yt.com/S3edckDk_qo</iframe> |
| 386 | 138 | _oembed_time_f4d0e4e5251dea | 1503777685 |
| 387 | 138 | _oembed_4c76e997d8b21a | {{unknown}} |
| 389 | 138 | _oembed_268d95c8d907aa | {{unknown}} |
| 391 | 138 | _oembed_time_a26684b605680e | 1505355944 |
+---------+---------+-----------------------------------------------+------------------------+

I tried removing the offending meta_id, by issuing:

mysql> DELETE from wp_postmeta WHERE meta_key like '%_oembed%' and meta_value like '%OeiZNr24o7g%';

However that didn’t work when I refreshed the page, so I decided on the more radical solution:

mysql> DELETE from wp_postmeta WHERE meta_key like '%_oembed%';

That solved all my problems!

Python Stripe Signature Verification Error

Building a Django + Stripe integration today I kept hitting the SignatureVerificationError.

According to their webhook’s documentation, they recommend something like this:

@csrf_exempt
def my_webhook_view(request):
  payload = request.body
  sig_header = request.META['HTTP_STRIPE_SIGNATURE']
  event = None

  try:
    event = stripe.Webhook.construct_event(
      payload, sig_header, endpoint_secret
    )
  except ValueError as e:
    # Invalid payload
    return HttpResponse(status=400)
  except stripe.error.SignatureVerificationError as e:
    # Invalid signature
    return HttpResponse(status=400)

  # Do something with event

  return HttpResponse(status=200)

I think would work fine with python2, but in my case the signature being generated never matched the expected signature hence triggering the exception stripe.error.SignatureVerificationError

The solution is to utf8 decode the payload, on line 3, change:

payload = request.body
payload = request.body.decode('utf-8')

That did the trick, hope it can save someone some time.

Repair experience OnePlus 3t

After many months of use without a case, I finally managed to crack the screen of my One Plus 3t. I researched online to try a repair on my own, but eventually, and from my previous experiences in which self-repairs don’t end up as nicely as you might think, I decided to reach out to their support team.

The experience was surprisingly pleasant, relatively fast and not as expensive as I had initially estimated. I will describe more or less the process and dates so you get an idea of what to expect.

To start the process you need to submit a ticket through the official Oneplus customer center website,  explaining what repairs your phone needs, in my case my cracked screen. They will reply within a few hours and give you a cost estimate. If you agree to the repairs, they will send you an address in Texas, assuming you are in the US, to where you need to ship the phone.

Once the phone has been received, they will give you a final bill, you need to pay online for the repair bill and then they will proceed with the repairs and mail the phone back to your address. My phone also needed a case replacing, apparently, upon impact, the framing body had been deformed to the point where the new screen wouldn’t fit, so that made the repairs a little more expensive.

Timeline:

  • May 12: opened a ticket with support explaining my screen was cracked
  • May 13: Support responded with an estimate of 62.81$ (not including labor and fees). I agree to the repairs.
  • May 13: Received an email from Acer (who’s in charge of the repairs for One Plus) with mailing instructions.
  • May 15: I mailed my phone using USPS to their repair center in Temple, TX.
  • May 18: I get a message informing that the phone has been received.
  • May 23: I get an email asking me to pay online for the repairs: screen + case: 133.16$. I paid a couple of hours after.
  • May 24: Repairs are completed.
  • May 25: Got a FedEx tracking number.
  • May 27: I get the repaired phone!

One last thing, in my case they did a complete phone reset, even though it wasn’t necessary, so before sending it to repair, I suggest you take off everything that want to preserve that is not backed up already. Also, my phone was rooted and they didn’t care about that.

Finally, the repair looks great, screen+case makes the phone look brand new, and they included a free phone case so that this won’t happen again.

Android RNE App

Spanish Radio and TV (RTVE) is a government funded entity that since 1956 has been broadcasting several TV and Radio channels.

Living abroad from home is thought but the radio programs from RTVE are a great companion for those missing home moments. I use the official app from Google Play Store, but unfortunately, the quality of the app is nowhere close to that of the content, so I decided to build my own.

https://play.google.com/store/apps/details?id=pandatech.rneunofficial

My app is not based on a web view as the official one, but rather on native Android elements that create a better user experience and are more visually appealing. Also, it plays audio as per the official Android documentation, i.e. requesting Audio focus before playing, and pausing if things like an alarm or a phone call come through.

I am planning to add more features in the next few days, ideally, the podcasts, and not just the live audio. Finally, and probably best, no adds!

Docker Alpine+Python3+Django+uWGSI+Nginx

Django works great when you are running in local, but the moment you decide to move onto production, the headaches begin…

Since a few months ago I deploy everything using Docker, the result is something much cleaner and easier to maintain, so when moving my Django app to production, I choose Docker for it. As expected someone had already taken the time to build a Docker image that using ubuntu as is base image will run your Django site with uWGSI&Nginx, a marriage made in heaven.

My problem with that image is that it uses Ubuntu, don’t get me wrong, I love Ubuntu for something like my laptop, but for a Django app in an EC2 instance, it just feels too heavy. So I created an equivalent Dockerfile, this time with the much lighter Alpine.

The Dockerfile is available in Github with instructions on how to deploy your app.
Enjoy!

Redshift UDF Phone Number to Country

Redshift’s UDFs (User Defined Functions) permit to execute, with some limitations, certain Python libraries and custom code.

In my case, I wanted to find a way to extract the country code from a phone number in E.164 format. UDFs are a perfect fit for this, the implementation in SQL would most certainly require creating custom views, and hacking your way around, while in python, we can use the library phone-iso3166

>>> from phone_iso3166.country import *
>>> phone_country('+1 202-111-1111')
'US'
>>> phone_country('+34645678901')
'ES'

To upload a library to Redshift, we first need to check it follows the structure:

directory/
    __init__.py
    extra_files.py
    subdirectory/
        __init__.py
        other_files.py

In our case, phone-iso3166 is already in that structure. Now we need to zip the library:

tar -xvzf phone-iso3166.tar.gz
zip -r phone-iso3166.zip phone-iso3166

With our zipped library, we need to upload it to S3. I did this part manually into a bucket named s3://redshift/custom-udfs/

Now, connect to Redshift and issue:

CREATE LIBRARY phone_iso3166 LANGUAGE plpythonu 
FROM 's3://redshift/custom-udfs/phone_iso3166.zip' 
CREDENTIALS 'aws_access_key_id=<your-aws-key>;aws_secret_access_key=<your-aws-pass>' 
region as '<your-region>';

Last step:

CREATE OR REPLACE FUNCTION udf_phone_country (phone_number VARCHAR(64)) RETURNS VARCHAR(64) IMMUTABLE as $$ 
from phone_iso3166.country import phone_country, InvalidPhone
try:
    return phone_country(phone_number)
except:
    return None
$$ LANGUAGE plpythonu;

You should be all set to use this in your queries:

SELECT 
analytics.udf_phone_country('+14151111975'),
analytics.udf_phone_country('+7652112311'),
analytics.udf_phone_country('+34626472918')

That returns:

"US","RU","ES"

DD-WRT Remote SSH Access behind VPN

SSH access doesn’t work when OpenVPN client is enabled on DD-WRT.
Packages do arrive at the router if you try to SSH against the WAN IP, however, because all OUTPUT  traffic is diverted through the VPN (interface tun0) SSH won’t succeed.

What’s missing is an OUTPUT rule on iptables  to route traffic on port 22 through the vlan2 interface (that’s the interface connected directly to the internet).

First, create table 202 via the Gateway Ip on the Interface VLAN2:

$ ip route add default via $(nvram get wan_gateway) dev vlan2 table 202

Then apply the rule on table 202 to packages marked with 22.

$ ip rule add fwmark 22 table 202

Finally, tag with 22 every output package on port 22 not coming from any machine on the local network.

$ iptables -t mangle -I OUTPUT -p tcp --sport 22 -d ! 192.168.1.0/24 -j MARK --set-mark 22

Note that the last command skips packages from the local network in my case 192.168.1.0/24, reason being that when SSHing from a host in local, the packages should be routed through br0 and not vlan2.

First issue these commands in the command line of your router to ensure they work with you, if somehow they break your routing, a restart will clear them. Once you have made sure they work, you can add them to the firewall script of your router

Note that my config IP and port is different because I am not using the default values.

 

Redshift Force Drop User

Ever tried dropping a user in Redshift only to discover that user “user_1” cannot be dropped because the user has a privilege on some object .

That’s not of great help Redshift. Luckily for us, there is a solution. Kamlesh Gallani posted a response about revoking permissions for tables and schemas that I user might still be assigned to, along with changing the ownership of tables that the user might have created. After that, dropping the user is straight forward.

I created a python snippet with his response that might save you a few minutes. You will need the view v_get_obj_priv_by_user from amazon-utils.

import psycopg2
 
# Connect to Redshift
conn = psycopg2.connect(host="", dbname="", user="", password="", port="5439")
conn.autocommit = True
cursor = conn.cursor()
 
# List of users to drop
users = ['user_to_drop_1', 'user_to_drop_2']
 
# New owner, used when changing ownership
new_owner = 'new_user'
 
# Templates to change ownership, revoke permissions, and drop users
change_ownership = "select 'alter table '+schemaname+'.'+tablename+' owner to %s;' from pg_tables where tableowner like '%s'"
revoke_schema_permissions = "select distinct 'revoke all on schema '+schemaname+' from %s;' from admin.v_get_obj_priv_by_user where usename like '%s'"
revoke_table_permissions = "select distinct 'revoke all on all tables in schema '+schemaname+' from %s;' from admin.v_get_obj_priv_by_user where usename like '%s'"
drop_user = "drop user %s;"
 
for user in users:
    # Change ownership
    cursor.execute(change_ownership % (new_owner, user))
    for r in cursor.fetchall():
        print("Executing: %s" % r[0])
        cursor.execute(r[0])
    # Revoke schema permissions
    cursor.execute(revoke_schema_permissions % (user, user))
    for r in cursor.fetchall():
        print("Executing: %s" % r[0])
        cursor.execute(r[0])
    # Revoke table permissions
    cursor.execute(revoke_table_permissions % (user, user))
    for r in cursor.fetchall():
        print("Executing: %s" % r[0])
        cursor.execute(r[0])
    # Drop user
    cursor.execute(drop_user % (user))

Miele W 1065 – Wiring for 110 Volts

Miele W 1065 Back

I got yesterday one of these Miele beauties. She is over 20 years old, and my guess is that she weights around 250 pounds. Yes, it was though carrying it downstairs from is previous owner to home with just 2 guys. 

When I brought her home, I saw that it required 220V which it’s a problem where I live, BUT if you are willing to sacrifice the second heater,  you can adapt can change the wires to make it work with the regular 110V.

Miele W 1065 Back
Miele W 1065 Back

First and foremost unplug the washer, unscrew the white plastic box and that will expose the connections box. You will see we have Ground, Neutral, phase 2 and phase 1 when reading from right to left. The washer will work for with just having phase 2 connected to 110V, but as said above, the heater won’t work, so you are stuck with washing with cold water. Which anyways is the best way to preserve your clothes.

I bought a piece of cord and cut it to expose the wiring, then just connect ground to ground, neutral to neutral and phase 2 to the remaining. I used a multimeter to make sure the wiring was correct.

Try this at your own risk. Mine is working like a champ.