Using Laravel Vite with MAMP
As seen in other posts, I use MAMP quite a bit for my web development environment. I know I can run docker, or any of the other platforms out there but they use more memory and resources that I’d prefer to devote to my dev tools.
I started a new Laravel project and wanted to use MAMP but Vite was throwing errors due to the SSL not matching out of the box.
When adding
@vite('resources/js/app.js')
to my blade file, I’d get errors including:
This request has been blocked; the content must be served over HTTPS.
I found articles saying to add –https or –host to my package json command but then I got this error:
net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH
Load MAMP Pro, add your host and generate your SSL certificates. For this example, we’ll use set the host name to my-app.test, and assume you’re storing the SSL keys in the default location.
Open vite.config.js and add the following 2 lines:
import fs from 'fs'; const host = 'my-app.test';
Then add this to defineConfig section:
server: { host, hmr: { host }, https: { key: fs.readFileSync(`/Applications/MAMP/Library/OpenSSL/certs/${host}.key`), cert: fs.readFileSync(`/Applications/MAMP/Library/OpenSSL/certs/${host}.crt`), }, },
You should now be able to run npm run dev and have no issues.
Sample full vite.config.js file for easy reference:
import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; import fs from 'fs'; const host = 'my-app.text'; export default defineConfig({ server: { host, hmr: { host }, https: { key: fs.readFileSync(`/Applications/MAMP/Library/OpenSSL/certs/${host}.key`), cert: fs.readFileSync(`/Applications/MAMP/Library/OpenSSL/certs/${host}.crt`), }, }, plugins: [ laravel([ 'resources/sass/app.scss', 'resources/js/app.js', ]), ], });
How to deploy a React app to Amazon S3 using Gitlab CI/CD
I’ve been trying to build more CI/CD scripts using Gitlab to automate pipeline deployments for work. Here’s a useful one for building and deploying a React app to Amazon S3.
You’ll need to add a variable called S3_BUCKET_NAME to your repo or replace the variable with your bucket path.
stages:
- build
- deploy
build react-app:
#I'm using node:latest, but be sure to test or change to a version you know works. Sometimes node updates break the npm script.
image: node:latest
stage: build
only:
- master
script:
# Set PATH
- export PATH=$PATH:/usr/bin/npm
# Install dependencies
- npm install
# Build App
- CI=false npm run build
artifacts:
paths:
# Build folder
- build/
expire_in: 1 hour
deploy master:
image: python:latest
stage: deploy
only:
- master
script:
- pip3 install awscli
- aws s3 sync ./build s3://$S3_BUCKET_NAME --acl public-read
How to use Backblaze B2 with Laravel
I am working on a Laravel project and decided to use a Backblaze bucket as it’s cheaper for storage when compared to AWS S3. I couldn’t find a tutorial on how to get it working from scratch and I tested a bunch of Laravel B2 libraries that didn’t end up working. The good news is that you don’t need a special B2 plugin and instead can use the S3 package recommended by the Laravel docs.
If you haven’t added the flysystem-aws-s3 package, add it to your project using composer:
composer require league/flysystem-aws-s3-v3
Login to your B2 account and create your bucket with your required settings. Once created, you’ll want to create a new application key with the permissions you need for your app. You should get a confirmation once it’s generated:
Open your .env file and locate the settings for AWS. You’ll need to add one key that’s not there by default:
AWS_ENDPOINT=
Match the settings in your .env from the application key to the values below.
AWS_ACCESS_KEY_ID=keyID
AWS_SECRET_ACCESS_KEY=applicationKey
AWS_DEFAULT_REGION=us-west-000
AWS_BUCKET=bucket-name
AWS_ENDPOINT=S3 Endpoint
Now you should be able to call the Laravel storage system like normal:
\Storage::disk('s3')->put('test.txt', 'test');
How to Setup a CI/CD Pipeline for Storybook.js using Gitlab
I just spent a few hours setting up a Gitlab pipeline to deploy a Storybook.js site. Of course the end result ended up being much simpler than I made it out to be. Like everything else on my blog, I’m sharing in case anyone else can use the information to save time.
Just put this in your gitlab-ci.yml and it’ll take care of caching the node modules and building your static version of Storybook to deploy.
image: node:latest
cache:
paths:
- node_modules/
stages:
- build
- deploy
build:
stage: build
script:
- npm install
- npm run build-storybook -- -o storybook-static
artifacts:
paths:
- storybook-static
only:
- qa
- develop
- master
deploy:
stage: deploy_to_aws
# add your deploy code here
How to Automate Sending Emails through Outlook interop using C#
I was tasked with a tricky issue in sending emails. Due to security concerns, the client’s IT team was not willing to share SMTP information for their mail settings and was only willing to set up an account in Outlook directly on a dedicated machine without sharing the password with us to send the emails. The client’s ask was to send emails through Outlook without letting users see the emails or Outlook itself.
Installing Office Interop for Outlook
Sending emails through Outlook can be done using Microsoft.Office.Interop.Outlook but the documentation is really lacking. If you need to do the same, I hope this will save you the hours of time it took me to figure out what ends up not being complex code.
Create a new desktop application project in Visual Studio. Install the Microsoft Office Interop for Outlook. I used the NuGet package manager to install it since it wasn’t present on my system:
Install-Package Microsoft.Office.Interop.Outlook
Automating E-mails using C#
I created a static class to send the email through Outlook. Note that my error handling code was replaced with Debug.Writeline. Remember to modify it to handle errors or implement logging so it doesn’t fail silently.
Email.cs:
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Outlook;
using Exception = System.Exception;
namespace Email.classes
{
public class Email
{
public static void SendWithEmbeddedImages(string to, string subject, string htmlMessage)
{
var missing = Type.Missing;
Application oOutlook = null;
NameSpace oNS = null;
Folder oCtFolder = null;
Items oCts = null;
MailItem msg = null;
var sHeaderPath = Path.Combine(Environment.CurrentDirectory, "emails", "header.jpg");
var sLogoPath = Path.Combine(Environment.CurrentDirectory, "emails", "logo.jpg");
try
{
// Create an Outlook application.
oOutlook = new Application();
// Get the namespace.
oNS = oOutlook.GetNamespace("MAPI");
//Assumes MAPI profile name is Outlook
oNS.Logon("Outlook", missing, false, true);
msg = (MailItem) oOutlook.CreateItem(OlItemType.olMailItem);
var attachHeader = msg.Attachments.Add(sHeaderPath, OlAttachmentType.olEmbeddeditem);
var attachLogo = msg.Attachments.Add(sLogoPath, OlAttachmentType.olEmbeddeditem);
attachLogo.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E", "logo");
attachHeader.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E",
"header");
msg.Subject = subject;
msg.To = to;
msg.BodyFormat = OlBodyFormat.olFormatHTML;
msg.HTMLBody = htmlMessage;
//Show email
msg.Display();
//Send email
//((Outlook._MailItem)msg).Send();
oNS.Logoff();
}
catch (Exception ex)
{
Debug.WriteLine("Automate Outlook throws the error: {0}", ex.Message);
}
finally
{
// Manually clean up the explicit unmanaged Outlook COM resources by
// calling Marshal.FinalReleaseComObject on all accessor objects.
// See http://support.microsoft.com/kb/317109.
if (msg != null)
{
Marshal.FinalReleaseComObject(msg);
msg = null;
}
if (oCts != null)
{
Marshal.FinalReleaseComObject(oCts);
oCts = null;
}
if (oCtFolder != null)
{
Marshal.FinalReleaseComObject(oCtFolder);
oCtFolder = null;
}
if (oNS != null)
{
Marshal.FinalReleaseComObject(oNS);
oNS = null;
}
if (oOutlook != null)
{
Marshal.FinalReleaseComObject(oOutlook);
oOutlook = null;
}
}
}
}
}
Example on how to call the class:
var sEmailPath = Path.Combine(Environment.CurrentDirectory, "emails", "single.html");
var htmlMessage = "";
if (File.Exists(sEmailPath))
{
//Load HTML from file
htmlMessage = File.ReadAllText(sEmailPath);
}
Email.SendWithEmbeddedImages("toaddress@test.com", "Outlook Automation Test", htmlMessage);
email.html:
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
</head>
<body>
<table width="600" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td><img src="cid:header"></td>
</tr>
<tr>
<td>
Hello world!
</td>
</tr>
<tr>
<td><img src="cid:logo"></td>
</tr>
</table>
</body>
</html>
Important Notes and Gotchas
- CIDs need to be unique. I’ve seen them on all other posts with the format “file.extension@{random #}” but keeping it simple like in the code above worked for me with no issues with Outlook 2016. I did not test on older versions of Outlook to confirm as I no longer have access to them.
- Outlook ignores font rules in the HTML/CSS you code in the email and defaults to Times New Roman. I know Outlook uses the Word renderer but I have no idea why and the only solution I found was to update the default font in Microsoft Word. Yes, to change the font in Outlook, you’ll need to update the default font in Word. Here’s how to set it:
- Open Word
Go to Options -> Advanced -> Web Options
Change the default font in the Fonts tab
- Open Word
- Outlook only supports a subset of HTML so don’t forget to test and verify everything as most CSS formatting won’t work in Outlook.
How to Extract Text from image using Laravel and Amazon Rekognition API
I’m currently working on a project that requires extracting text from images of variable quality. I’m doing quick prototypes using PHP and Laravel, but I’ve found the documentation for accomplishing this a bit lacking. After working on it for a bit, I figured out a really simple solution and am sharing in case it helps anyone else.
This sample will send the image to the API as a blob instead of using a URL. I had tried Base64 encoding it without any luck. I discovered that using Imagick was the easiest way to make this work so the sample relies on that.
Fire up terminal and add a requirement for aws/aws-sdk-php:
composer require aws/aws-sdk-php
Add keys for your AWS access keys and region to your .ENV file:
AWS_REGION=YOUR_REGION
AWS_SECRET_ACCESS_KEY=ENTER_YOUR_KEY
AWS_ACCESS_KEY_ID=ENTER_YOUR_KEY
Create a controller, add the snippet below, and create a route to the controller. Check the URL and you should have a dump showing the array returned from AWS with all the text, bounding boxes, and information.
//Build config array for AWS variables
$config = array(
'region' => env('AWS_REGION'),
'version' => 'latest',
'credentials' => array(
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET')
)
);
//Replace with path to image or uploaded image.
$path = '{path to image.jpg}';
$image = new Imagick($path);
$imdata = $image->getImageBlob();
$client = new RekognitionClient($config);
$result = $client->detectText([
'Image' => ['Bytes' => $imdata],
]);
//Dump Result
dd($result);
How to get shape type in Visio using VBA?
I’m working with a Visio 2016 file with over 100 tabs and need to extract the data (mainly text, connector from/to, and shape) for data processing for a processing engine. I was trying to figure out how to get the shape type name in Visio using VBA. For example, in a flowchart, I’m trying to figure out how to tell if a shape is a process, decision, data, etc. The
visShape.Type
property seems to always return 3 which appears to be visTypeShape from https://docs.microsoft.com/en-us/office/vba/api/visio.visshapetypes. After hunting through all the available properties on the Shape object, I found that the shape.Master.Name property will return the shape name, but you need to check if it’s Nothing first in case it’s not a shape.
I didn’t do that and it kept breaking the script originally because some of the pages had text fields and the first few items on the first sheet I was working with were text boxes. Hopefully this snippet will save you the time I wasted figuring it out.
Public Sub GetShapeAndID()
Dim visShape As Shape
For Each visShape In ActivePage.Shapes
If Not visShape.Master Is Nothing Then
Debug.Print visShape.ID & " - " & visShape.Master.Name
End If
Next
End Sub
How to remove wrapping tags in PHP Storm
How often do you code something and need to delete a wrapping link or div? I was using PHPStorm and had grabbed some code from another file that had links in the tags which weren’t needed in the new file. I didn’t want to manually remove each link and after a quick look around PHPStorm’s menus, discovered that PHPStorm has a really useful command to remove the wrapping element for you.
Select the element, then from the menu, choose Code > Unwrap/Remove… or use the keyboard shortcut, Command + Shift + Backspace and then choose the appropriate wrapping element to remove.
Chrome Anchor Link Not Working Fix
One of my single page sites that had been live for months with no changes suddenly had the menu stop functioning in Chrome only. I can’t find an exact reason for the break, but I did find a cross-browser JavaScript solve that fixes the issue nicely. Just add the snippet to your site and the links should work automatically. Note that this fix requires jQuery but can be converted to VanillaJS.
/* Navigation Fix -----------------------------------------------------*/ $(function() { $('a[href*="#"]:not([href="#"])').click(function() { if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) { var target = $(this.hash); target = target.length ? target : $('[name=' + this.hash.slice(1) +']'); if (target.length) { $('html, body').animate({ scrollTop: target.offset().top }, 1000); return false; } } }); });
Someone reached out to me today for help as the fix broke their WordPress site. If you’re using WordPress, this version uses the compatibility version of jQuery they provide out of the box. Be sure to place it in a javascript file and not your functions.php. Rui was kind enough to confirm the fix also works for Drupal 7.
/* WordPress Navigation Fix -----------------------------------------------------*/ jQuery(function ($) { $('a[href*="#"]:not([href="#"])').click(function() { if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) { var target = $(this.hash); target = target.length ? target : $('[name=' + this.hash.slice(1) +']'); if (target.length) { $('html, body').animate({ scrollTop: target.offset().top }, 1000); return false; } } }); });
How to use Teensyduino to send cmd + enter
Recently we purchased a bunch of Teensyduino powered buttons for a trivia game we were building for a client’s convention as a fun learning experience. Now that the conference is through, the buttons are sitting unused, and my boss asked me if I could reprogram one of them so he could use it to send emails through Outlook on Mac.
It was a fun little exercise since I’ve never worked with programming Teensyduinos before. It turned out that this wasn’t so complicated to accomplish. I figured I’d share the script in case anyone else would find it useful.
int key1 = KEY_ENTER; int spacesAllowed = 1; void setup() { Serial.begin(9600); pinMode(10, INPUT); digitalWrite(10, HIGH); // C7 } void loop() { if (digitalRead(10) == LOW && spacesAllowed > 0){ Keyboard.set_modifier(MODIFIERKEY_GUI); Keyboard.set_key1(key1); Keyboard.send_now(); delay(5); spacesAllowed = 0; // no spaces allowed anymore } if (digitalRead(10) == HIGH){ Keyboard.set_modifier(0); Keyboard.set_key1(0); Keyboard.send_now(); delay(5); spacesAllowed = 1; // button is up again } }
If you need to send through Outlook on Windows, change the line
Keyboard.set_modifier(MODIFIERKEY_GUI);
to
Keyboard.set_modifier(MODIFIERKEY_CTRL);