diceline-chartmagnifiermouse-upquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

How to Configure Traefik to Use Existing TLS Certificates

  1. Create a new file tls.yml
tls:
  stores:
    default:
      defaultCertificate:
        certFile: /etc/traefik/certs/your-domain.dev/cert.pem
        keyFile: /etc/traefik/certs/your-domain.dev/privkey.pem
  1. Make sure you're copying this file in your Dockerfile

COPY .docker/traefik/conf.d/tls.yml /etc/traefik/tls.yml

  1. Mount the certs folder in your docker-compose.yml file
volumes:
  ...
	- .docker/traefik/certs:/etc/traefik/certs
  ...
  1. Update the traefik.yml config to support the file provider:
providers:
		...
    file:
      filename: "/etc/traefik/tls.yml
    ...

How To Keep Your Github Actions Workflows Private in an Open Source Repository

If you want to make your repository open source, but keep the workflows private, you can do the following:

  1. Create a separate repository for your workflows and make it private.
  2. In the open source repository, include only the necessary configuration files (e.g. .github/actions) and reference the private repository as a submodule.
  3. In the private repository, configure the GitHub Actions workflows as you normally would.
  4. When someone clones or forks the open source repository, the submodule reference to the private repository will not be included.

This way, you can keep your workflows private, while still making your repository open source.

Meteor.js powered React hook for retrieving articles with error and loading state handling

The custom hook created uses Meteor's useTracker hook and the subscribe method to subscribe to the article publication, as well as the ArticlesCollection to find the article with the matching _id (which is the same as the slug in this case).

const useArticle = (slug: string): any => {
    const [error, setError] = useState<Error | null>(null)

    return useTracker(() => {
        const sub = Meteor.subscribe('article', { _id: slug })
        if (sub.ready()) {
            const article = ArticlesCollection.findOne({ _id: slug })
            if (article) {
                return {
                    loading: false,
                    error: null,
                    article,
                }
            } else {
                setError(new Error(`Article with slug "${slug}" not found`))
                return {
                    loading: false,
                    error,
                    article: undefined,
                }
            }
        } else {
            return {
                loading: true,
                error: null,
                article: undefined,
            }
        }
    }, [slug, setError])
}

This hook can be used in a React component to display the article, its loading state and error:

const Article = ({ slug }: { slug: string }) => {
    const { loading, error, article } = useArticle(slug)

    if (loading) {
        return <div>Loading...</div>
    }

    if (error) {
        return <div>Error: {error.message}</div>
    }

    return (
        <div>
            <h1>{article.title}</h1>
            <p>{article.body}</p>
        </div>
    )
}

Eager Loading with Subqueries in Laravel Eloquent

Eager loading with subqueries allows you to load related models using a subquery instead of joining the tables, which can improve performance in certain situations. For example, if you have a User model and a Post model, you can count the number of posts created by each user in the last month using a subquery like this:

$users = User::withCount(['posts as post_count' => function ($query) {
    $query->select(DB::raw('count(*)'))
          ->where('created_at', '>', now()->subMonth());
}])->get();

Laravel Request Authorization: Understanding the Differences between Request Classes and Policies

In Laravel, both request classes and policies can handle authorization.

Request classes have an authorize method that is called automatically by the framework before the request is handled. It is a convenient way to perform authorization checks on a specific request.

Policies provide a more flexible and reusable way to handle authorization across multiple request classes. They are classes that organize authorization logic around a particular model or resource. They allow you to define granular abilities for your application's users and groups.

When both the request's authorize method and a policy's __call method (or method with the same name as the action) return false for the same action, the framework will consider the user as unauthorized and will return a 403 Forbidden response.

In such scenarios, the priority is given to the authorize method of the request class. If it returns true, the framework will continue to process the request and will not check against any policy. However, if it returns false or throws an exception, Laravel will check the policy's method with the same name as the action, and if it returns false, the user will be considered unauthorized and a 403 Forbidden response will be returned.

In summary, both request classes and policies can handle authorization in Laravel, with priority given to the authorize method of request classes.

Use TouchID on MacBook Pro for Terminal sudo prompts

In order to use TouchID on MacBook Pro for Terminal sudo prompts, we need to enable Apple's Touch ID PAM module pam_tid.so (https://opensource.apple.com/source/pam_modules/pam_modules-173.1.1/modules/pam_tid/pam_tid.c.auto.html).

Just edit /etc/pam.d/sudo and add

auth sufficient pam_tid.so

Heads up, when you do a system update this change will be most probably overwritten. In order to make it persistent, you need to create a launchd daemon.

Create a new file called pam-tid.sh in a shared path

vim /Users/Shared/pam-tid.sh
#!/bin/bash

if ! grep 'pam_tid.so' /etc/pam.d/sudo --silent; then
  sed -i -e '1s;^;auth       sufficient     pam_tid.so\n;' /etc/pam.d/sudo
fi

Create a new com.graffino.pam.plist file:

vim /Users/Shared/com.graffino.pam.plist`

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.graffino.pam</string>

    <key>KeepAlive</key>
    <false/>

    <key>LaunchOnlyOnce</key>
    <true/>

    <key>RunAtLoad</key>
    <true/>

    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/pam-tid.sh</string>
    </array>
</dict>
</plist>

Start the daemon

Improving mapping performance with web workers

We can use web workers to execute the computationally intensive mapping operation in a separate thread. This can be done by following these steps:

  1. Create a new TS file for the worker
// worker.ts

// define the input and output types
type WorkerInput<T, U> = { data: T, mapFn: (x: T) => U };
type WorkerOutput<U> = { data: U[] };

// function for the actual work on the data
function doWork<T, U>(input: WorkerInput<T, U>): Promise<WorkerOutput<U>> {
  // Apply the mapping function to the input data.
  const mappedData = input.data.map(input.mapFn);

  // Return the mapped data as a promise.
  return Promise.resolve({ data: mappedData });
}
  1. Use the worker in the main.ts file
// main.ts
const worker = new Worker('worker.ts');

// Define the mapping function.
const mapFn = (x: number) => x * x;

// Send data and the mapping function to the worker for processing.
worker.postMessage({ data: [1, 2, 3], mapFn });

// Listen for the message with the result from the worker
worker.addEventListener('message', (event) => {
    console.log(`Received message from worker: ${event.data}`)
});

Matching dates within a monthly Interval that crosses over multiple years with PHP and Carbon

function getDatesInRange($dates, $start, $end) {
    $datesInRange = $dates->filter(function ($date) use ($start, $end) {
        if ((int) $start->format('Y') === (int) $end->format('Y')) {
            $start->year((int)$date->format('Y'));
            $end-> year((int)$date->format('Y'));
        } elseif ((int) $start->format('Y') < (int) $end->format('Y')) {
            if ((int) $date->format('m') >= (int) $start->format('m')) {
                $start->year((int) $date->format('Y'));
                $end->year((int) $date->format('Y'))->addYears(1);
            } else {
                $end->year((int) $date->format('Y'));
                $start->year((int) $date->format('Y'))->subYears(1);
            }
        }

        return Carbon::parse($date)->isBetween($start, $end);
    });

    return $datesInRange;
}