mirror of
https://github.com/andwati/personal-website.git
synced 2026-10-18 02:32:54 +00:00
feat: add multiple new blog posts, remove old ones, and update core application and blog template pages.
This commit is contained in:
+13
-5
@@ -32,10 +32,10 @@ const navItems = computed<NavigationMenuItem[]>(() => [
|
||||
<UHeader>
|
||||
<template #title>
|
||||
<NuxtLink to="/" class="flex items-center gap-2">
|
||||
<span class="inline-flex items-center justify-center size-8 rounded-full bg-primary-600 text-white text-sm font-bold">IW</span>
|
||||
<span class="inline-flex items-center justify-center size-8 rounded-full bg-primary-600 text-white text-sm font-bold">IA</span>
|
||||
<div class="flex flex-col leading-tight">
|
||||
<span class="font-semibold text-sm">Ian Andwati</span>
|
||||
<span class="text-xs text-(--ui-text-muted)">Software Engineer</span>
|
||||
<span class="text-xs text-(--ui-text-muted)">0xmockinspectre</span>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
@@ -54,7 +54,7 @@ const navItems = computed<NavigationMenuItem[]>(() => [
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
to="https://x.com"
|
||||
to="https://x.com/andwati_"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-x"
|
||||
aria-label="X"
|
||||
@@ -78,7 +78,7 @@ const navItems = computed<NavigationMenuItem[]>(() => [
|
||||
<UFooter>
|
||||
<template #left>
|
||||
<p class="text-sm text-(--ui-text-muted)">
|
||||
© {{ new Date().getFullYear() }} Ian Andwati. Built with Nuxt.
|
||||
© 2022 - {{ new Date().getFullYear() }} 0xmockinspectre. Built with Nuxt.
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -94,11 +94,19 @@ const navItems = computed<NavigationMenuItem[]>(() => [
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
to="https://x.com"
|
||||
to="https://x.com/andwati_"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-x"
|
||||
aria-label="X"
|
||||
/>
|
||||
<UButton
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
to="https://linkedin.com/in/andwati"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-linkedin"
|
||||
aria-label="LinkedIn"
|
||||
/>
|
||||
</template>
|
||||
</UFooter>
|
||||
</UApp>
|
||||
|
||||
+30
-18
@@ -2,19 +2,20 @@
|
||||
useSeoMeta({
|
||||
title: "About — Ian Andwati",
|
||||
description:
|
||||
"Learn more about Ian Andwati — software engineer passionate about building great products.",
|
||||
"Learn more about Ian Andwati — software engineer interested in offensive security and reverse engineering.",
|
||||
});
|
||||
|
||||
const skills = [
|
||||
{ name: "C", icon: "i-simple-icons-c" },
|
||||
{ name: "Rust", icon: "i-simple-icons-rust" },
|
||||
{ name: "Go", icon: "i-simple-icons-go" },
|
||||
{ name: "Python", icon: "i-simple-icons-python" },
|
||||
{ name: "TypeScript", icon: "i-simple-icons-typescript" },
|
||||
{ name: "Vue.js", icon: "i-simple-icons-vuedotjs" },
|
||||
{ name: "Nuxt", icon: "i-simple-icons-nuxtdotjs" },
|
||||
{ name: "Node.js", icon: "i-simple-icons-nodedotjs" },
|
||||
{ name: "Go", icon: "i-simple-icons-go" },
|
||||
{ name: "Python", icon: "i-simple-icons-python" },
|
||||
{ name: "Docker", icon: "i-simple-icons-docker" },
|
||||
{ name: "Git", icon: "i-simple-icons-git" },
|
||||
{ name: "PostgreSQL", icon: "i-simple-icons-postgresql" },
|
||||
{ name: "Linux", icon: "i-simple-icons-linux" },
|
||||
];
|
||||
</script>
|
||||
|
||||
@@ -34,9 +35,10 @@ const skills = [
|
||||
<div class="space-y-4 text-center sm:text-left">
|
||||
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-(--ui-text-muted)">About</p>
|
||||
<h1 class="text-3xl sm:text-4xl font-bold">Ian Andwati</h1>
|
||||
<p class="text-sm font-mono text-primary-600 dark:text-primary-400">0xmockinspectre · offending binaries</p>
|
||||
<p class="text-lg text-(--ui-text-muted) leading-relaxed">
|
||||
I'm a software engineer who loves building tools and products that make a difference.
|
||||
My interests span full-stack development, open source, and exploring new technologies.
|
||||
I spend my free time working on hobby projects and sometimes I write about my experiences.
|
||||
In particular, I enjoy learning about offensive security and reverse engineering.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,9 +53,9 @@ const skills = [
|
||||
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">Focus</p>
|
||||
<h2 class="text-2xl font-bold mb-4">What I Do</h2>
|
||||
<p class="text-(--ui-text-muted) leading-relaxed">
|
||||
I specialize in building modern web applications using cutting-edge technologies.
|
||||
Whether it's crafting a seamless user experience on the frontend or architecting
|
||||
robust backend systems, I enjoy the entire spectrum of software development.
|
||||
I'm interested in low-level systems programming, security research, and understanding how things
|
||||
work under the hood. I build CPU emulators, CLI tools, bots, and web apps as hobby projects.
|
||||
I also participate in CTF competitions and write about what I learn.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -77,13 +79,12 @@ const skills = [
|
||||
|
||||
<USeparator />
|
||||
|
||||
<!-- Contact -->
|
||||
<!-- Connect -->
|
||||
<div>
|
||||
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">Connect</p>
|
||||
<h2 class="text-2xl font-bold mb-4">Get in Touch</h2>
|
||||
<h2 class="text-2xl font-bold mb-4">Find Me</h2>
|
||||
<p class="text-(--ui-text-muted) leading-relaxed mb-6">
|
||||
Feel free to reach out through any of the links below. Always happy to
|
||||
connect and discuss interesting projects or ideas.
|
||||
You can find me on the following platforms.
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<UButton
|
||||
@@ -96,16 +97,27 @@ const skills = [
|
||||
/>
|
||||
<UButton
|
||||
label="X / Twitter"
|
||||
to="https://x.com"
|
||||
to="https://x.com/andwati_"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-x"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
/>
|
||||
<UButton
|
||||
label="Email me"
|
||||
to="mailto:hello@ianwati.dev"
|
||||
icon="i-lucide-mail"
|
||||
label="LinkedIn"
|
||||
to="https://linkedin.com/in/andwati"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-linkedin"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
/>
|
||||
<UButton
|
||||
label="Stack Overflow"
|
||||
to="https://stackoverflow.com/users/12246264/ian-andwati"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-stackoverflow"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,34 +20,33 @@ useSeoMeta({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UPage v-if="post">
|
||||
<UPageHeader
|
||||
:title="post.title"
|
||||
:description="post.description"
|
||||
>
|
||||
<template #headline>
|
||||
<UBreadcrumb
|
||||
:items="[
|
||||
{ label: 'Blog', to: '/blog', icon: 'i-lucide-book-open' },
|
||||
{ label: post.title || '' }
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<UContainer v-if="post">
|
||||
<div class="max-w-3xl mx-auto py-8 lg:py-12">
|
||||
<UBreadcrumb
|
||||
:items="[
|
||||
{ label: 'Blog', to: '/blog', icon: 'i-lucide-book-open' },
|
||||
{ label: post.title || '' }
|
||||
]"
|
||||
class="mb-6"
|
||||
/>
|
||||
|
||||
<template #bottom>
|
||||
<div class="flex items-center gap-3 text-sm text-(--ui-text-muted)">
|
||||
<UIcon name="i-lucide-calendar" />
|
||||
<header class="mb-8 lg:mb-12">
|
||||
<h1 class="text-3xl lg:text-4xl font-bold tracking-tight text-(--ui-text-highlighted) mb-3">
|
||||
{{ post.title }}
|
||||
</h1>
|
||||
<p v-if="post.description" class="text-lg text-(--ui-text-muted) mb-4">
|
||||
{{ post.description }}
|
||||
</p>
|
||||
<div class="flex items-center gap-2 text-sm text-(--ui-text-muted)">
|
||||
<UIcon name="i-lucide-calendar" class="size-4" />
|
||||
<time v-if="post.date">{{ new Date(post.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) }}</time>
|
||||
<UBadge v-if="post.badge" :label="post.badge" variant="subtle" size="sm" class="ml-2" />
|
||||
</div>
|
||||
</template>
|
||||
</UPageHeader>
|
||||
</header>
|
||||
|
||||
<UPageBody>
|
||||
<UContainer>
|
||||
<div class="prose prose-primary dark:prose-invert max-w-none">
|
||||
<ContentRenderer :value="post" />
|
||||
</div>
|
||||
</UContainer>
|
||||
</UPageBody>
|
||||
</UPage>
|
||||
<div class="prose prose-primary dark:prose-invert max-w-none prose-headings:scroll-mt-20 prose-pre:rounded-xl prose-pre:border prose-pre:border-(--ui-border)">
|
||||
<ContentRenderer :value="post" />
|
||||
</div>
|
||||
</div>
|
||||
</UContainer>
|
||||
</template>
|
||||
|
||||
+169
-138
@@ -6,20 +6,73 @@ const { data: recentPosts } = await useAsyncData("recent-posts", () =>
|
||||
useSeoMeta({
|
||||
title: "Ian Andwati — Software Engineer",
|
||||
description:
|
||||
"Software engineer passionate about building great products. I write about development, technology, and the things I learn along the way.",
|
||||
"Software engineer interested in offensive security, reverse engineering, and building hobby projects.",
|
||||
});
|
||||
|
||||
const codeSnippet = `const softwareEngineer = {
|
||||
const codeSnippet = `const engineer = {
|
||||
name: 'Ian Andwati',
|
||||
role: 'Software Engineer',
|
||||
skills: [
|
||||
'TypeScript', 'Vue.js', 'Nuxt',
|
||||
'Node.js', 'Go', 'Python'
|
||||
handle: '0xmockinspectre',
|
||||
interests: [
|
||||
'Offensive Security',
|
||||
'Reverse Engineering',
|
||||
'Systems Programming'
|
||||
],
|
||||
languages: [
|
||||
'C', 'Rust', 'Go',
|
||||
'Python', 'TypeScript'
|
||||
],
|
||||
getProjects: async () => {
|
||||
return await fetchProjects();
|
||||
},
|
||||
};`;
|
||||
|
||||
const projects = [
|
||||
{
|
||||
name: "unsplash-bot",
|
||||
description: "Telegram bot to download images from Unsplash.",
|
||||
icon: "i-lucide-image",
|
||||
lang: "Python",
|
||||
stars: 8,
|
||||
url: "https://github.com/andwati/unsplash-bot",
|
||||
},
|
||||
{
|
||||
name: "mos-6502",
|
||||
description: "MOS 6502 CPU emulator — 8 bit lives!",
|
||||
icon: "i-lucide-cpu",
|
||||
lang: "C",
|
||||
stars: 4,
|
||||
url: "https://github.com/andwati/mos-6502",
|
||||
},
|
||||
{
|
||||
name: "url-shortener",
|
||||
description: "URL shortener service built in Go.",
|
||||
icon: "i-lucide-link",
|
||||
lang: "Go",
|
||||
stars: 1,
|
||||
url: "https://github.com/andwati/url-shortener",
|
||||
},
|
||||
{
|
||||
name: "ratatui-json-editor",
|
||||
description: "Terminal JSON editor built with Ratatui.",
|
||||
icon: "i-lucide-file-json",
|
||||
lang: "Rust",
|
||||
stars: 1,
|
||||
url: "https://github.com/andwati/ratatui-json-editor",
|
||||
},
|
||||
{
|
||||
name: "store-api",
|
||||
description: "Store inventory REST API.",
|
||||
icon: "i-lucide-database",
|
||||
lang: "Python",
|
||||
stars: 1,
|
||||
url: "https://github.com/andwati/store-api",
|
||||
},
|
||||
{
|
||||
name: "andwati.xyz",
|
||||
description: "This personal site, rebuilt with Nuxt.",
|
||||
icon: "i-lucide-globe",
|
||||
lang: "Vue",
|
||||
stars: 0,
|
||||
url: "https://github.com/andwati/andwati.xyz",
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -33,7 +86,7 @@ const codeSnippet = `const softwareEngineer = {
|
||||
<!-- Left: Text -->
|
||||
<div class="space-y-6">
|
||||
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-(--ui-text-muted)">
|
||||
Software Engineer
|
||||
0xmockinspectre
|
||||
</p>
|
||||
|
||||
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold leading-tight">
|
||||
@@ -41,8 +94,8 @@ const codeSnippet = `const softwareEngineer = {
|
||||
</h1>
|
||||
|
||||
<p class="text-lg sm:text-xl text-(--ui-text-muted) leading-relaxed max-w-xl">
|
||||
I build modern web applications and love crafting software that makes a real impact.
|
||||
Passionate about clean code, open source, and continuous learning.
|
||||
I spend my free time working on hobby projects and sometimes I write about my experiences.
|
||||
I enjoy learning about offensive security and reverse engineering.
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap gap-3 pt-2">
|
||||
@@ -65,12 +118,18 @@ const codeSnippet = `const softwareEngineer = {
|
||||
<div class="flex items-center gap-4 pt-2 text-sm text-(--ui-text-muted)">
|
||||
<span class="flex items-center gap-1.5">
|
||||
<UIcon name="i-lucide-map-pin" class="size-4" />
|
||||
Nairobi, Kenya
|
||||
</span>
|
||||
<span class="flex items-center gap-1.5">
|
||||
<UIcon name="i-lucide-mail" class="size-4" />
|
||||
[email protected]
|
||||
UTC +3
|
||||
</span>
|
||||
<UButton
|
||||
to="https://x.com/andwati_"
|
||||
target="_blank"
|
||||
variant="link"
|
||||
color="neutral"
|
||||
size="sm"
|
||||
icon="i-simple-icons-x"
|
||||
label="@andwati_"
|
||||
class="p-0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -79,20 +138,20 @@ const codeSnippet = `const softwareEngineer = {
|
||||
<!-- Floating stat cards -->
|
||||
<div class="absolute -top-4 -right-2 z-10">
|
||||
<div class="bg-primary-800 text-white rounded-xl px-4 py-3 shadow-lg flex items-center gap-3">
|
||||
<UIcon name="i-lucide-git-commit-horizontal" class="size-5 text-primary-300" />
|
||||
<UIcon name="i-lucide-shield" class="size-5 text-primary-300" />
|
||||
<div>
|
||||
<p class="font-bold text-sm">Open Source</p>
|
||||
<p class="text-xs text-primary-200">Active Contributor</p>
|
||||
<p class="font-bold text-sm">Security</p>
|
||||
<p class="text-xs text-primary-200">Offensive & RE</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="absolute -bottom-2 -right-4 z-10">
|
||||
<div class="bg-primary-700 text-white rounded-xl px-4 py-3 shadow-lg flex items-center gap-3">
|
||||
<UIcon name="i-lucide-code-2" class="size-5 text-primary-300" />
|
||||
<UIcon name="i-lucide-terminal" class="size-5 text-primary-300" />
|
||||
<div>
|
||||
<p class="font-bold text-sm">Full Stack</p>
|
||||
<p class="text-xs text-primary-200">Frontend & Backend</p>
|
||||
<p class="font-bold text-sm">Systems</p>
|
||||
<p class="text-xs text-primary-200">Low-level hacking</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,15 +164,15 @@ const codeSnippet = `const softwareEngineer = {
|
||||
<span class="size-3 rounded-full bg-yellow-500" />
|
||||
<span class="size-3 rounded-full bg-green-500" />
|
||||
</div>
|
||||
<span class="text-xs text-white/40 ml-2">portfolio.ts</span>
|
||||
<span class="text-xs text-white/40 ml-2">whoami.ts</span>
|
||||
</div>
|
||||
<pre class="p-5 text-sm font-mono leading-relaxed overflow-x-auto"><code class="text-green-400">{{ codeSnippet }}</code></pre>
|
||||
<div class="flex items-center justify-between px-4 py-2 border-t border-white/10 text-xs text-white/40">
|
||||
<span class="flex items-center gap-1.5">
|
||||
<span class="size-2 rounded-full bg-green-500" />
|
||||
Online and coding
|
||||
studying the blade
|
||||
</span>
|
||||
<span>Last commit: Today</span>
|
||||
<span>offending binaries</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -133,30 +192,82 @@ const codeSnippet = `const softwareEngineer = {
|
||||
<UCard>
|
||||
<div class="space-y-3">
|
||||
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
|
||||
<UIcon name="i-lucide-monitor" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
<UIcon name="i-lucide-shield-alert" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-lg">Frontend Development</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">Building responsive, accessible, and performant user interfaces with Vue.js, Nuxt, and modern CSS.</p>
|
||||
<h3 class="font-semibold text-lg">Offensive Security</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">Exploring vulnerabilities, CTF challenges, and security research. Learning to think like an attacker to build better defenses.</p>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="space-y-3">
|
||||
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
|
||||
<UIcon name="i-lucide-server" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
<UIcon name="i-lucide-binary" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-lg">Backend Development</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">Designing robust APIs and services with Node.js, Go, and Python. Focused on scalability and reliability.</p>
|
||||
<h3 class="font-semibold text-lg">Reverse Engineering</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">Taking binaries apart to understand how things work under the hood. From CPU emulators to malware analysis.</p>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="space-y-3">
|
||||
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
|
||||
<UIcon name="i-lucide-lightbulb" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
<UIcon name="i-lucide-code-2" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<h3 class="font-semibold text-lg">Systems Programming</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">Building things in C, Rust, and Go. CPU emulators, CLI tools, and low-level projects that make the machine do what you want.</p>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
</UContainer>
|
||||
</section>
|
||||
|
||||
<!-- Projects Section -->
|
||||
<section id="projects" class="py-16 lg:py-24 bg-(--ui-bg-elevated)">
|
||||
<UContainer>
|
||||
<div class="flex items-end justify-between mb-12">
|
||||
<div>
|
||||
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">Projects</p>
|
||||
<h2 class="text-3xl sm:text-4xl font-bold">Things I've Built</h2>
|
||||
<p class="mt-2 text-(--ui-text-muted)">Hobby projects, experiments, and tools.</p>
|
||||
</div>
|
||||
<UButton
|
||||
label="GitHub"
|
||||
to="https://github.com/andwati"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-github"
|
||||
variant="ghost"
|
||||
trailing-icon="i-lucide-arrow-up-right"
|
||||
class="hidden sm:inline-flex"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<UCard v-for="project in projects" :key="project.name">
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
|
||||
<UIcon :name="project.icon" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<UButton
|
||||
icon="i-simple-icons-github"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
:to="project.url"
|
||||
target="_blank"
|
||||
aria-label="View source"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="font-semibold text-lg">{{ project.name }}</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">{{ project.description }}</p>
|
||||
<div class="flex items-center gap-3 pt-1">
|
||||
<UBadge :label="project.lang" variant="subtle" size="sm" />
|
||||
<span v-if="project.stars" class="text-xs text-(--ui-text-muted) flex items-center gap-1">
|
||||
<UIcon name="i-lucide-star" class="size-3" />
|
||||
{{ project.stars }}
|
||||
</span>
|
||||
</div>
|
||||
<h3 class="font-semibold text-lg">Open Source</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">Contributing to open source projects and building tools that help the developer community grow.</p>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
@@ -164,13 +275,13 @@ const codeSnippet = `const softwareEngineer = {
|
||||
</section>
|
||||
|
||||
<!-- Recent Blog Posts Section -->
|
||||
<section class="py-16 lg:py-24 bg-(--ui-bg-elevated)">
|
||||
<section class="py-16 lg:py-24">
|
||||
<UContainer>
|
||||
<div class="flex items-end justify-between mb-12">
|
||||
<div>
|
||||
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">Blog</p>
|
||||
<h2 class="text-3xl sm:text-4xl font-bold">Latest Posts</h2>
|
||||
<p class="mt-2 text-(--ui-text-muted)">Thoughts on development, technology, and things I learn along the way.</p>
|
||||
<p class="mt-2 text-(--ui-text-muted)">CTF writeups, security research, and things I learn along the way.</p>
|
||||
</div>
|
||||
<UButton
|
||||
label="View All"
|
||||
@@ -204,126 +315,46 @@ const codeSnippet = `const softwareEngineer = {
|
||||
</UContainer>
|
||||
</section>
|
||||
|
||||
<!-- Projects Section -->
|
||||
<section id="projects" class="py-16 lg:py-24">
|
||||
<UContainer>
|
||||
<div class="text-center mb-12">
|
||||
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">Projects</p>
|
||||
<h2 class="text-3xl sm:text-4xl font-bold">Things I've Built</h2>
|
||||
<p class="mt-2 text-(--ui-text-muted)">A selection of projects I've worked on — from side experiments to production apps.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<UCard>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
|
||||
<UIcon name="i-lucide-layout-dashboard" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<UButton
|
||||
icon="i-simple-icons-github"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
to="https://github.com/andwati"
|
||||
target="_blank"
|
||||
aria-label="View source"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="font-semibold text-lg">Personal Website</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">This site! Built with Nuxt, Nuxt Content, and Nuxt UI. Features a blog, dark mode, and responsive design.</p>
|
||||
<div class="flex flex-wrap gap-2 pt-1">
|
||||
<UBadge label="Nuxt" variant="subtle" size="sm" />
|
||||
<UBadge label="Vue" variant="subtle" size="sm" />
|
||||
<UBadge label="Tailwind" variant="subtle" size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
|
||||
<UIcon name="i-lucide-terminal" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<UButton
|
||||
icon="i-simple-icons-github"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
to="https://github.com/andwati"
|
||||
target="_blank"
|
||||
aria-label="View source"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="font-semibold text-lg">CLI Toolkit</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">A collection of developer CLI tools for automating workflows, generating boilerplate, and managing projects.</p>
|
||||
<div class="flex flex-wrap gap-2 pt-1">
|
||||
<UBadge label="Go" variant="subtle" size="sm" />
|
||||
<UBadge label="CLI" variant="subtle" size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UCard>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
|
||||
<UIcon name="i-lucide-database" class="size-5 text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<UButton
|
||||
icon="i-simple-icons-github"
|
||||
color="neutral"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
to="https://github.com/andwati"
|
||||
target="_blank"
|
||||
aria-label="View source"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="font-semibold text-lg">API Starter</h3>
|
||||
<p class="text-sm text-(--ui-text-muted) leading-relaxed">A production-ready REST API boilerplate with authentication, rate limiting, and database migrations.</p>
|
||||
<div class="flex flex-wrap gap-2 pt-1">
|
||||
<UBadge label="Node.js" variant="subtle" size="sm" />
|
||||
<UBadge label="PostgreSQL" variant="subtle" size="sm" />
|
||||
<UBadge label="Docker" variant="subtle" size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</div>
|
||||
</UContainer>
|
||||
</section>
|
||||
|
||||
<!-- Contact Section -->
|
||||
<section id="contact" class="py-16 lg:py-24">
|
||||
<section id="contact" class="py-16 lg:py-24 bg-(--ui-bg-elevated)">
|
||||
<UContainer>
|
||||
<div class="max-w-2xl mx-auto text-center space-y-6">
|
||||
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400">Contact</p>
|
||||
<h2 class="text-3xl sm:text-4xl font-bold">Get in Touch</h2>
|
||||
<p class="text-lg text-(--ui-text-muted)">
|
||||
Have a project in mind or just want to connect? Feel free to reach out.
|
||||
Have something interesting to share? Feel free to reach out.
|
||||
</p>
|
||||
<div class="flex flex-wrap justify-center gap-4 pt-4">
|
||||
<UButton
|
||||
label="Email me"
|
||||
to="mailto:[email protected]"
|
||||
icon="i-lucide-mail"
|
||||
size="xl"
|
||||
/>
|
||||
<UButton
|
||||
label="GitHub"
|
||||
to="https://github.com/andwati"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-github"
|
||||
size="xl"
|
||||
/>
|
||||
<UButton
|
||||
label="X / Twitter"
|
||||
to="https://x.com/andwati_"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-x"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
size="xl"
|
||||
/>
|
||||
<UButton
|
||||
label="X / Twitter"
|
||||
to="https://x.com"
|
||||
label="LinkedIn"
|
||||
to="https://linkedin.com/in/andwati"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-x"
|
||||
icon="i-simple-icons-linkedin"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
size="xl"
|
||||
/>
|
||||
<UButton
|
||||
label="Stack Overflow"
|
||||
to="https://stackoverflow.com/users/12246264/ian-andwati"
|
||||
target="_blank"
|
||||
icon="i-simple-icons-stackoverflow"
|
||||
color="neutral"
|
||||
variant="outline"
|
||||
size="xl"
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "A Fresh Coat Of Paint"
|
||||
description: "Migrating to Zola"
|
||||
date: "2023-08-24"
|
||||
badge: "Blog"
|
||||
---
|
||||
|
||||
Using Zola as my preferred static site generator
|
||||
|
||||
After using Hugo for over eight months I got tired of it and looked for other alternatives. That's when I stumbled upon Zola. Zola is a static site generator (SSG), similar to [Hugo](https://gohugo.io/), [Pelican](https://blog.getpelican.com/), and [Jekyll](https://jekyllrb.com/).
|
||||
|
||||
It is written in Rust and uses the Tera template engine, similar to Jinja2, Django templates, Liquid, and Twig. SSGs use dynamic templates to transform content into static HTML pages. Static sites are thus very fast and require no databases, making them easy to host.
|
||||
|
||||
My previous Hugo site used a theme because I wasn't really ready to learn Go's template engine. I'm already familiar with Python Django and its template system so using Tera wasn't hard because it's similar to jinja2 and the Django template syntax.
|
||||
|
||||
Content is written in [CommonMark](https://commonmark.org/), a strongly defined, highly compatible specification of [Markdown](https://www.markdownguide.org/). Zola uses [pulldown-cmark](https://github.com/raphlinus/pulldown-cmark#pulldown-cmark) to parse markdown files. The goal of this library is 100% compliance with the CommonMark spec. It adds a few additional features such as parsing footnotes, Github-flavored tables, Github-flavored task lists, and strikethrough.
|
||||
|
||||
Zola comes with built-in syntax highlighting, but you must first enable it in the configuration file.
|
||||
|
||||
Zola is very minimalistic, it is installed only as a single binary, with no extra dependencies needed. It is configured in `toml` and you only use what is needed. For example here is the configuration for this site `config.toml`:
|
||||
|
||||
```toml
|
||||
title = "Ian Andwati"
|
||||
description = "Ian Andwati's personal website"
|
||||
base_url = "https://andwati.github.io"
|
||||
|
||||
generate_categories_pages = false
|
||||
generate_tags_pages = false
|
||||
generate_feed = true
|
||||
feed_filename = "rss.xml"
|
||||
minify_html = false
|
||||
taxonomies = [{ name = 'tags', feed = false }]
|
||||
compile_sass = true
|
||||
|
||||
[markdown]
|
||||
highlight_code = true
|
||||
highlight_theme = "gruvbox-dark"
|
||||
|
||||
[link_checker]
|
||||
|
||||
[extra]
|
||||
author = "Ian Andwati"
|
||||
```
|
||||
|
||||
## Inspiration
|
||||
|
||||
The motivation to use Zola also came from my decision to move from independently developed themes. I decided to write the HTML and CSS myself and endeavor to maintain the site individually. The base CSS is borrowed from [Reilly Tucker Siemens](https://tuckersiemens.com/) who had created a nice gruvbox palette for his site.
|
||||
|
||||
I improved on it by adding an archive page, pagination, a comment system, and Google Analytics (more of these in later posts). I also implemented article read time and word count.
|
||||
|
||||
## What next
|
||||
|
||||
The site is a work in progress and is far from polished. I plan on implementing a newsletter to notify readers of new posts. The site is primarily a dark theme, in the future I'll also add theme toggling and maybe different palettes.
|
||||
|
||||
You can subscribe to the site's RSS feed at [https://andwati.xyz/rss.xml](https://andwati.xyz/rss.xml).
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
title: "Building a Blog with Nuxt Content"
|
||||
description: "Learn how to use Nuxt Content v3 to create a powerful, file-based blog with markdown support."
|
||||
date: "2025-02-01"
|
||||
image: "https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=800&h=400&fit=crop"
|
||||
badge: "Tutorial"
|
||||
---
|
||||
|
||||
# Building a Blog with Nuxt Content
|
||||
|
||||
Nuxt Content v3 is a powerful module that turns your Nuxt application into a git-based CMS. In this post, we'll explore how to set up a blog using Nuxt Content.
|
||||
|
||||
## Why Nuxt Content?
|
||||
|
||||
Nuxt Content provides several advantages for content management:
|
||||
|
||||
- **Markdown support** — Write your content in Markdown with full MDC syntax support
|
||||
- **Collections** — Organize your content into typed collections with schema validation
|
||||
- **Querying** — Powerful query API to filter, sort, and paginate your content
|
||||
- **Vue components in Markdown** — Embed Vue components directly in your markdown files
|
||||
|
||||
## Setting Up Collections
|
||||
|
||||
Content collections are defined in your `content.config.ts` file:
|
||||
|
||||
```typescript
|
||||
import { defineContentConfig, defineCollection, z } from "@nuxt/content";
|
||||
|
||||
export default defineContentConfig({
|
||||
collections: {
|
||||
blog: defineCollection({
|
||||
type: "page",
|
||||
source: "blog/**",
|
||||
schema: z.object({
|
||||
date: z.string(),
|
||||
image: z.string().optional(),
|
||||
badge: z.string().optional(),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Querying Content
|
||||
|
||||
You can query your blog posts using the `queryCollection` composable:
|
||||
|
||||
```typescript
|
||||
const { data: posts } = await useAsyncData("posts", () =>
|
||||
queryCollection("blog").order("date", "DESC").all()
|
||||
);
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
Nuxt Content makes it incredibly easy to build a content-driven website. With collections, schema validation, and the powerful query API, you have all the tools you need to create a great blog experience.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: "File over app"
|
||||
description: "Take control of your artifacts"
|
||||
date: "2023-12-18"
|
||||
badge: "Thoughts"
|
||||
---
|
||||
|
||||
Take control of your artifacts
|
||||
|
||||
In my endless endeavours to find the perfect note taking app led me to obsidian, a note taking software that uses markdown files as the file format. All software is ephemeral in nature and your artifacts need to survive the decline of your favorite app. The files you create should be available to you and shouldn't require specialized software to access.
|
||||
|
||||
Your data shouldn't be locked behind a paywall or an authentication system that is not under your control.
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: "Django Secret Key Tutorial"
|
||||
description: "Managing secrets"
|
||||
date: "2022-11-11"
|
||||
badge: "Tutorial"
|
||||
---
|
||||
|
||||
Managing the Django SECRET_KEY variable.
|
||||
|
||||
The Django `SECRET_KEY` variable is very crucial to your Django application. The secret key must be a large random value and it must be kept secret. Leaking this value to unauthorized people could lead to a security breach. The SECRET_KEY is used in Django for cryptographic signing. It is used to generate tokens and hashes, they can be recreated using this variable. If it is not configured Django throws a `django.core.exceptions.ImproperlyConfigured: The SECRET_KEY setting must not be empty` error
|
||||
|
||||
# Using Environment Variables
|
||||
|
||||
The secret key should not be committed to version control. It is best practice to store the value in a .env file which is added to the .gitignore file to un-track its changes. The values can be loaded programmatically into your settings.py file.
|
||||
|
||||
# Generating A New Secret Key
|
||||
|
||||
This solution is using python's secrets lib on the back
|
||||
|
||||
```python
|
||||
from django.core.management.utils import get_random_secret_key
|
||||
# print new random secret key
|
||||
print(get_random_secret_key())
|
||||
```
|
||||
|
||||
This code can be run in the terminal as a command:
|
||||
|
||||
```python
|
||||
python -c 'from django.core.management.utils import get_random_secret_key; \
|
||||
print(get_random_secret_key())'
|
||||
```
|
||||
|
||||
Alternatively, If you are using python 3.6+ then you can use the `secrets.token_hex([nbytes=None])` function:
|
||||
|
||||
```python
|
||||
python3 -c 'import secrets; print(secrets.token_hex(100))'
|
||||
```
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
title: "Getting Started with Nuxt 4"
|
||||
description: "An introduction to Nuxt 4 and its new features that make web development even more enjoyable."
|
||||
date: "2025-01-15"
|
||||
image: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800&h=400&fit=crop"
|
||||
badge: "Nuxt"
|
||||
---
|
||||
|
||||
# Getting Started with Nuxt 4
|
||||
|
||||
Nuxt 4 represents a significant evolution in the Nuxt ecosystem. With a refined project structure, improved developer experience, and powerful new features, it's never been a better time to start building with Nuxt.
|
||||
|
||||
## What's New in Nuxt 4
|
||||
|
||||
Nuxt 4 brings several exciting improvements:
|
||||
|
||||
- **New directory structure** — The `app/` directory is now the default home for your Vue application code
|
||||
- **Improved TypeScript support** — Better type inference and auto-completion out of the box
|
||||
- **Enhanced performance** — Faster builds and smaller bundle sizes
|
||||
- **Better data fetching** — Reworked async data layer for more predictable behavior
|
||||
|
||||
## Getting Started
|
||||
|
||||
Setting up a new Nuxt 4 project is straightforward:
|
||||
|
||||
```bash
|
||||
npx nuxi@latest init my-app
|
||||
```
|
||||
|
||||
This creates a fresh project with all the latest defaults. From there, you can start building your application right away.
|
||||
|
||||
## Why Nuxt?
|
||||
|
||||
Nuxt takes the best of Vue.js and adds powerful conventions and features on top:
|
||||
|
||||
1. **File-based routing** — No manual route configuration needed
|
||||
2. **Auto-imports** — Components, composables, and utilities are automatically available
|
||||
3. **Server-side rendering** — Built-in SSR for better performance and SEO
|
||||
4. **Modules ecosystem** — Rich ecosystem of modules for common needs
|
||||
|
||||
Whether you're building a simple landing page or a complex full-stack application, Nuxt provides the tools and conventions to do it efficiently.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Nuxt 4 is a fantastic framework for modern web development. Give it a try and see how it can streamline your workflow!
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: "Hello World"
|
||||
description: "Hello world"
|
||||
date: "2022-11-04"
|
||||
badge: "Blog"
|
||||
---
|
||||
|
||||
How it begins
|
||||
|
||||
In this post-web2.0 age, people seem to blog less and less, and blogs seem to be dying slowly. People are separated in different Apps where the content is not searchable by search engines.
|
||||
|
||||
However, I still prefer to write using a blog.
|
||||
|
||||
# Why this?
|
||||
|
||||
The best way to learn something is to teach others the knowledge that you have gained. Regurgitating that information will solidify it more firmly in your mind.
|
||||
|
||||
# What I write?
|
||||
|
||||
Mostly random stuff and a few tutorials. I'll see where this goes. Although some of the blog posts may only be helpful to myself, but who knows, I hope they will be helpful to someone else in some day.
|
||||
|
||||
# Static blog generator
|
||||
|
||||
I use hugo as a static site generator. After trying a couple of blogging platforms and trying to build a few from scratch I decided to throw in the towel and opt in for a ready made solution, no need reinventing the wheel.
|
||||
|
||||
Hugo is one of the most popular open-source static site generators. With its amazing speed and flexibility, Hugo makes building websites fun again. Hugo provides a robust theming system that is easy to implement but capable of producing even the most complicated websites. Hugo's Go-based templates provide just the right amount of logic to build anything from the simple to complex. For this site I decided to go with the [even theme](https://github.com/olOwOlo/hugo-theme-even). The commenting system is implemented using [Utterances](https://utteranc.es/)
|
||||
|
||||
# Blog site hosting
|
||||
|
||||
The site is hosted on github pages as evident by the \*.github.io domain name. I prefer this mainly because it is free and I don't have to switch to a different hosting provider.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
title: "cognitive inertia"
|
||||
description: "inertia"
|
||||
date: "2025-02-26"
|
||||
badge: "Thoughts"
|
||||
---
|
||||
|
||||
Staring at the void
|
||||
|
||||
Cognitive inertia is the reason changing our habits can be difficult. If you want to overcome inertia you have to apply a force. I consider myself an eternal student, who is ready to learn anything, or so I thought. It happened but I had not noticed, I had stopped moving forward.
|
||||
|
||||
## Metawork
|
||||
|
||||
I will define this as the things we do before getting the "real" work done. Its things like choosing the best IDE or text editor, setting up your coding environment, choosing the best note taking app, or even the 'best' operating system. They are the prerequisites of the actual work.
|
||||
|
||||
I had been stuck on this loop, always trying to craft the perfect workflow. Setting up the best personalized linux distro, switching note taking apps, trying out multiple layouts, stylings and technologies for my blog, the list is endless.
|
||||
|
||||
All of these metawork took more time than the actual work; building, active learning and writing. Im going to stop all of these and start doing the important stuff.
|
||||
@@ -0,0 +1,151 @@
|
||||
---
|
||||
title: "Install PostgreSQL on Arch | Manjaro | Garuda Linux"
|
||||
description: "Getting PostgreSQL up and running on Arch Linux-based distros"
|
||||
date: "2023-02-16"
|
||||
badge: "Tutorial"
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
**PostgreSQL** is a powerful open-source relational database management system (RDBMS) that allows you to store and manage large amounts of data. It was first released in 1996 and has become one of the most popular RDBMSs in the world.
|
||||
|
||||
PostgreSQL is designed to handle high levels of **concurrency**, providing features such as **MVCC (multi-version concurrency control)** that allow multiple users to access the same data simultaneously without conflicts. It is also **highly extensible**, with a large number of third-party extensions and libraries available that can be used to add functionality to the core database.
|
||||
|
||||
Some of the key features of PostgreSQL include support for **complex SQL queries**, **advanced indexing** and **query optimization**, **transactional integrity**, and support for a **wide range of programming languages** and development frameworks.
|
||||
|
||||
In this tutorial, we'll look at how to install PostgreSQL on Arch Linux, Manjaro, Garuda Linux, or any other Arch-based distro out there.
|
||||
|
||||
# PostgreSQL use cases
|
||||
|
||||
- **E-commerce websites:** PostgreSQL is a popular choice for e-commerce websites because it can handle large amounts of data and supports complex queries.
|
||||
- **Financial institutions:** PostgreSQL is used in financial institutions to store and manage transactional data, such as stock trades, bank transfers, and credit card transactions.
|
||||
- **Healthcare industry:** PostgreSQL is used in the healthcare industry to store patient data, such as medical records and diagnostic test results.
|
||||
- **Government agencies:** PostgreSQL is used by government agencies for managing large datasets, storing and analyzing census data.
|
||||
- **Mobile and web applications:** PostgreSQL is often used as a back-end database for mobile and web applications.
|
||||
- **Geospatial applications:** PostgreSQL has built-in support for geospatial data and is often used for applications that require location-based data.
|
||||
|
||||
# Why PostgreSQL?
|
||||
|
||||
Postgres offers a lot of advantages that make it an excellent choice for highly transactional environments regardless of application size and data volume. Some of the top features are:
|
||||
|
||||
- **ACID Compliance:** Provides high reliability, consistency, and data integrity.
|
||||
- **Extensibility:** Highly extensible with a large extension framework.
|
||||
- **Concurrency:** Multi-Version Concurrency Control (MVCC) for fast and reliable access.
|
||||
- **Full-text search:** Powerful full-text search engine with advanced capabilities.
|
||||
- **Advanced indexing:** B-tree, Hash, GiST, SP-GiST, GIN, and BRIN indexing options.
|
||||
- **JSON support:** Native support for storing and querying JSON data.
|
||||
- **Security:** SSL support, client authentication, and role-based access control.
|
||||
- **Scalability:** Designed to scale horizontally and vertically.
|
||||
|
||||
# Install PostgreSQL on Arch | Manjaro | Garuda Linux
|
||||
|
||||
Now with all that fluff out of the way, let's get to business. The steps have been tested on Arch Linux with the KDE desktop.
|
||||
|
||||
> **Note:** Commands that should be run as the *postgres* user are prefixed by `[postgres]$` in this article.
|
||||
|
||||
## Step 1: Installing the PostgreSQL Package
|
||||
|
||||
```sh
|
||||
sudo pacman -Syu postgresql
|
||||
```
|
||||
|
||||
This command will update the package database and update all packages on the system then install the PostgreSQL package from your distribution mirrors. Never install a package without updating the system first. On a rolling release, this can lead to an unbootable system. Installing the package will also create a system user called *postgres*.
|
||||
|
||||
You can switch to the PostgreSQL user by executing the following command:
|
||||
|
||||
- If you have sudo and are in sudoers
|
||||
|
||||
```sh
|
||||
sudo -iu postgres
|
||||
```
|
||||
|
||||
- Otherwise using su:
|
||||
|
||||
```sh
|
||||
su
|
||||
su -l postgres
|
||||
```
|
||||
|
||||
## Step 2: Initial Configuration
|
||||
|
||||
Before PostgreSQL can function correctly, the database cluster must be initialized.
|
||||
|
||||
### 2.1 We can confirm the installed PostgreSQL version by running
|
||||
|
||||
```sh
|
||||
postgres --version
|
||||
postgres (PostgreSQL) 15.1
|
||||
```
|
||||
|
||||
### 2.2 Set applicable entries in /etc/locale.gen
|
||||
|
||||
```sh
|
||||
echo "en_US.UTF-8 UTF-8" | sudo tee /etc/locale.gen
|
||||
```
|
||||
|
||||
### 2.3 Then run locale-gen to generate locale settings
|
||||
|
||||
```sh
|
||||
sudo locale-gen
|
||||
Generating locales...
|
||||
en_US.UTF-8... done
|
||||
Generation complete.
|
||||
```
|
||||
|
||||
### 2.4 Initialize Postgres Data Directory
|
||||
|
||||
You must first log in as the **postgres** user using the following command before you can initialize PostgreSQL's data directory:
|
||||
|
||||
```sh
|
||||
sudo su - postgres
|
||||
```
|
||||
|
||||
### 2.5 With the following command, you can now initialize PostgreSQL's data directory
|
||||
|
||||
```sh
|
||||
[postgres]$ initdb -D /var/lib/postgres/data
|
||||
```
|
||||
|
||||
### 3.1 Check PostgreSQL status with the following command
|
||||
|
||||
```sh
|
||||
systemctl status postgresql
|
||||
```
|
||||
|
||||
### 3.2 Start the PostgreSQL service by running the following command
|
||||
|
||||
```sh
|
||||
sudo systemctl start postgresql
|
||||
```
|
||||
|
||||
### 3.3 Enable the PostgreSQL service to start automatically at boot
|
||||
|
||||
```sh
|
||||
sudo systemctl enable postgresql
|
||||
```
|
||||
|
||||
### 3.4 Verify that PostgreSQL is running
|
||||
|
||||
```sh
|
||||
sudo systemctl status postgresql
|
||||
```
|
||||
|
||||
### Change postgres user password
|
||||
|
||||
To change the postgres user password execute the following command:
|
||||
|
||||
```sh
|
||||
sudo -u postgres psql -c "ALTER USER postgres PASSWORD '$PGPASSWORD';"
|
||||
```
|
||||
|
||||
# Resources
|
||||
|
||||
- [Arch Linux Wiki](https://wiki.archlinux.org/title/PostgreSQL)
|
||||
- [Wikipedia](https://en.wikipedia.org/wiki/PostgreSQL)
|
||||
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
|
||||
|
||||
# Conclusion
|
||||
|
||||
Installing PostgreSQL on Arch Linux-based distribution is a straightforward process that can be completed in just a few steps. By following the steps outlined above, you can install, configure, and start the PostgreSQL database server on your Arch Linux system. Once installed, you can use PostgreSQL to store, manage, and query data for a wide range of applications and use cases.
|
||||
|
||||
You can connect to the PostgreSQL server using the psql command-line client or a graphical tool such as pgAdmin.
|
||||
@@ -0,0 +1,30 @@
|
||||
---
|
||||
title: "Integrating Netlify CMS"
|
||||
description: "Integrating netlify cms"
|
||||
date: "2022-11-24"
|
||||
badge: "Blog"
|
||||
---
|
||||
|
||||
Migrating to a better content management system through Netlify CMS
|
||||
|
||||
# Heavy Lifting
|
||||
|
||||
Setting up Netlify CMS with Hugo is a straightforward process that enhances your static site with a user-friendly content management interface. With Netlify CMS, you can manage your Hugo content directly from a web-based admin panel.
|
||||
|
||||
# Redeploying to Netlify
|
||||
|
||||
Moving from GitHub Pages to Netlify provides continuous deployment, form handling, and more. The migration process involves connecting your repository to Netlify and configuring the build settings.
|
||||
|
||||
# Setting up Netlify CMS
|
||||
|
||||
## Steps
|
||||
|
||||
1. Create an `admin` folder in your `static` directory
|
||||
2. Add `index.html` and `config.yml` files
|
||||
3. Configure the CMS to match your content structure
|
||||
4. Enable Netlify Identity for authentication
|
||||
5. Set up Git Gateway for content management
|
||||
|
||||
# Setting up a custom domain
|
||||
|
||||
Configure your custom domain in Netlify's domain settings and update your DNS records accordingly.
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: "Export default and module.exports in Javascript"
|
||||
description: "Modules are the building blocks of modern JavaScript applications, allowing for the encapsulation of code into reusable components."
|
||||
date: "2024-01-17"
|
||||
badge: "Tutorial"
|
||||
---
|
||||
|
||||
Modules are the building blocks of modern JavaScript applications, allowing for the encapsulation of code into reusable components. Two primary systems govern the exportation of these modules: export default in ES6 and module.exports in CommonJS.
|
||||
|
||||
Javascript is a popular programming language that can run both in the browser and on the server. However, different environments may have different ways of organizing and importing Javascript modules. In this article, we will compare two common methods of exporting and importing modules: **export default** and **module.exports**.
|
||||
|
||||
## What are modules?
|
||||
|
||||
Modules are reusable pieces of code that can be separated into different files. Modules can export values, functions, classes, or objects that can be imported by other modules. This way, modules can avoid polluting the global scope and enable code reuse and modularity.
|
||||
|
||||
## How to export modules in Node.js?
|
||||
|
||||
Node.js is a runtime environment that allows Javascript to run on the server. Node.js uses a module system called CommonJS, which is based on the require and module.exports syntax.
|
||||
|
||||
To export a module in Node.js, you can assign the value that you want to export to the module.exports object. For example:
|
||||
|
||||
```javascript
|
||||
// my-module.js
|
||||
// export a function
|
||||
module.exports = function add(a, b) {
|
||||
return a + b;
|
||||
};
|
||||
|
||||
// export an object
|
||||
module.exports = {
|
||||
name: "Alice",
|
||||
age: 25,
|
||||
};
|
||||
```
|
||||
|
||||
To import a module in Node.js, you can use the require function and pass the path of the module file as an argument. The require function will return the value of module.exports from the module file. For example:
|
||||
|
||||
```javascript
|
||||
// main.js
|
||||
// import a function
|
||||
const add = require("./my-module.js");
|
||||
console.log(add(2, 3)); // 5
|
||||
|
||||
// import an object
|
||||
const person = require("./my-module.js");
|
||||
console.log(person.name); // Alice
|
||||
console.log(person.age); // 25
|
||||
```
|
||||
|
||||
Note that Node.js will cache the modules that are imported with require, so if you import the same module multiple times, you will get the same instance of the module.
|
||||
|
||||
## How to export modules in ES6?
|
||||
|
||||
ES6 is the version of Javascript that introduced many new features, including a native module system. ES6 modules use the `import` and `export` syntax.
|
||||
|
||||
To export a module in ES6, you can use the `export` keyword followed by the declaration of the value that you want to export. You can also use the export keyword with curly braces to export a list of named values. For example:
|
||||
|
||||
```javascript
|
||||
// my-module.js
|
||||
// export a function
|
||||
export function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// export a class
|
||||
export class Person {
|
||||
constructor(name, age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
|
||||
// export an object
|
||||
export const person = {
|
||||
name: "Alice",
|
||||
age: 25,
|
||||
};
|
||||
```
|
||||
|
||||
To import a module in ES6, you can use the `import` keyword followed by the name of the value that you want to import and the path of the module file. You can also use the import keyword with curly braces to import a list of named values. For example:
|
||||
|
||||
```javascript
|
||||
// main.js
|
||||
// import a function
|
||||
import { add } from "./my-module.js";
|
||||
console.log(add(2, 3)); // 5
|
||||
|
||||
// import a class
|
||||
import { Person } from "./my-module.js";
|
||||
const person = new Person("Bob", 30);
|
||||
console.log(person.name); // Bob
|
||||
console.log(person.age); // 30
|
||||
|
||||
// import an object
|
||||
import { person as Alice } from "./my-module.js";
|
||||
console.log(Alice.name); // Alice
|
||||
console.log(Alice.age); // 25
|
||||
```
|
||||
|
||||
Note that ES6 modules are imported and executed only once, so if you import the same module multiple times, you will get the same instance of the module.
|
||||
|
||||
## What is the difference between export default and module.exports?
|
||||
|
||||
One of the main differences between export default and module.exports is that export default is an ES6 feature, while module.exports is a CommonJS feature. Therefore, they are not compatible with each other by default. However, some tools like Babel or Webpack can transpile ES6 modules to CommonJS modules, or vice versa, to make them work together.
|
||||
|
||||
Another difference is that export default can only export one value per module, while module.exports can export any value, including multiple values. For example:
|
||||
|
||||
```javascript
|
||||
// my-module.js
|
||||
// export default a function
|
||||
export default function add(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// export default an object
|
||||
export default {
|
||||
name: "Alice",
|
||||
age: 25,
|
||||
};
|
||||
|
||||
// export default a class
|
||||
export default class Person {
|
||||
constructor(name, age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
// my-module.js
|
||||
// export a function
|
||||
module.exports = function add(a, b) {
|
||||
return a + b;
|
||||
};
|
||||
|
||||
// export an object
|
||||
module.exports = {
|
||||
name: "Alice",
|
||||
age: 25,
|
||||
};
|
||||
|
||||
// export a class
|
||||
module.exports = class Person {
|
||||
constructor(name, age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Note that you can only have one export default statement per module, but you can have multiple module.exports statements. However, each module.exports statement will overwrite the previous one, so only the last one will be exported.
|
||||
|
||||
To import a default export, you can use the import keyword without curly braces and assign any name to the imported value. For example:
|
||||
|
||||
```javascript
|
||||
// main.js
|
||||
// import a default export
|
||||
import add from "./my-module.js";
|
||||
console.log(add(2, 3)); // 5
|
||||
```
|
||||
|
||||
To import a module.exports value, you can use the require function and assign any name to the imported value. For example:
|
||||
|
||||
```javascript
|
||||
// main.js
|
||||
// import a module.exports value
|
||||
const add = require("./my-module.js");
|
||||
console.log(add(2, 3)); // 5
|
||||
```
|
||||
|
||||
Node's JavaScript has diverged from that of the browser. Understanding both CommonJS modules and ES6 modules is your first step in becoming a 10X developer. Happy coding!
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: "mmuctf 2025 writeup"
|
||||
description: "mmuctf 2025 challenges writeup"
|
||||
date: "2025-08-03"
|
||||
badge: "CTF"
|
||||
---
|
||||
|
||||
Over the weekend, I had the opportunity to participate in the mmuctf 2025. It was an individual jeopardy style CTF targeted towards beginners and intermediate people in cybersecurity. This is a write-up to explore some challenges I did, or at least almost did.
|
||||
|
||||
## Welcome
|
||||
|
||||
> Through the shadows of the cave, at the door I had a zeal to get a welcome flag, but I couldn't see it though I could hear it calling for me to get it. Happy hacking
|
||||
|
||||
This was a sanity check flag. The challenge description talked about the `door`, my first instinct was to view the source of the front page. I got this html comment with what looked like a base64 encoded string `<!--bW11Y3Rme3dlbGNvbWVfZmxhZ19hZGlvc19NdWNoQGNob30= -->`
|
||||
|
||||
```zsh
|
||||
┌──(mockingspectre㉿blackout)-[~/ctf/mmuctf2025]
|
||||
└─$ echo -n "bW11Y3Rme3dlbGNvbWVfZmxhZ19hZGlvc19NdWNoQGNob30=" | base64 -d
|
||||
mmuctf{welcome_flag_adios_Much@cho}
|
||||
```
|
||||
|
||||
## My First App
|
||||
|
||||
> Our intern dev swears this app's secure...
|
||||
|
||||
This was a web challenge with an account creation form. Let's fire up Burp. The account creation request didn't seem suspicious for now, so I sent it to the repeater to abuse it some more. The response seemed interesting, the user id was in the query parameter: `GET /profile.php?id=5 HTTP/1.1`. This was a nice candidate for an Insecure Direct Object Reference(IDOR). I tried repeating the request with values from 0-4. The flag was at 1, the admin user's account.
|
||||
|
||||
```txt
|
||||
mmuctf{1d0r_4dm1n_4cc3ss_1s_c0mpr0m1s3d}
|
||||
```
|
||||
|
||||
## Ledilect
|
||||
|
||||
> Sometimes the path you take is just a jump away.
|
||||
|
||||
This was a simple one, I knew what the challenge wanted, but I couldn't get myself to exploit it, definitely a skill issue. The webapp had a landing page that had a button that made this request:
|
||||
|
||||
```txt
|
||||
GET /portal/jump.php?url=https://example.com HTTP/1.1
|
||||
Host: redacted
|
||||
```
|
||||
|
||||
Looking at that, I suspected an open redirect vulnerability, but I don't know anything about this bug class. I let it rest.
|
||||
|
||||
## Meet my X
|
||||
|
||||
This challenge involved homoglyphs — a character or a sequence of characters that looks very similar or identical to another character or sequence of characters, but has a different meaning, encoding, or origin.
|
||||
|
||||
A sentence might look normal, but if you copy it into a text editor that shows Unicode details or a specialized homoglyph detector, you'll see different underlying character codes.
|
||||
|
||||
I used these online decoders, https://wulfsige.com/crypto/ and https://holloway.nz/steg/ to get the flag:
|
||||
|
||||
```txt
|
||||
mmuctf{oops_twitter_steg_1s_fun}
|
||||
```
|
||||
|
||||
## Shadow
|
||||
|
||||
> Look beyond the appearance, perhaps their numerical essence will guide you to the flag. Can you reveal what lurks in the shadow?
|
||||
|
||||
This was another stego challenge; there is an image with what looks like a color palette. The description hints at 'numerical essence', so it might be something to deal with the hex values of the colors. I used https://imagecolorpicker.com/ to extract the hex values.
|
||||
|
||||
```txt
|
||||
#6d6d75
|
||||
#637466
|
||||
#7b346c
|
||||
#773479
|
||||
#355f68
|
||||
#316464
|
||||
#336e5f
|
||||
#316e35
|
||||
#64335f
|
||||
#6d337d
|
||||
```
|
||||
|
||||
Decoded becomes
|
||||
|
||||
```txt
|
||||
mmuctf{4lw4y5_h1dd3n_1n5d3_m3}
|
||||
```
|
||||
|
||||
## Revealme
|
||||
|
||||
> We found this mysterious executable on an old USB stick... Can you figure out how to get it to reveal the flag?
|
||||
|
||||
The provided file is an ELF executable binary.
|
||||
|
||||
```zsh
|
||||
┌──(mockingspectre㉿blackout)-[~/ctf/mmuctf2025]
|
||||
└─$ file revealme
|
||||
revealme: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d8546bb2c10c0226384fb2771684af9bc150b55e, for GNU/Linux 3.2.0, stripped
|
||||
```
|
||||
|
||||
```zsh
|
||||
┌──(mockingspectre㉿blackout)-[~/ctf/mmuctf2025]
|
||||
└─$ checksec --file=revealme
|
||||
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY
|
||||
Partial RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols No
|
||||
```
|
||||
|
||||
Opening it with Binary Ninja gives us a quick win — the flag is hardcoded in the binary:
|
||||
|
||||
```asm
|
||||
004011e9 4889c7 mov rdi, rax {data_402038, "Correct! The flag is: mmuctf{r3v3rs1ng_st4rt3r}"}
|
||||
```
|
||||
|
||||
Flag:
|
||||
|
||||
```txt
|
||||
mmuctf{r3v3rs1ng_st4rt3r}
|
||||
```
|
||||
|
||||
## Flicker
|
||||
|
||||
> An ordinary looking Android app... or is it?
|
||||
|
||||
The provided app is an Android APK. Let's fire up jadx-gui. The package name is `com.example.blink`. There is an interesting class `r2d2`. In the onCreate method, there is an image string variable that seems to contain base64 image data.
|
||||
|
||||
```zsh
|
||||
┌──(mockingspectre㉿blackout)-[~/ctf/mmuctf2025]
|
||||
└─$ cat output.txt | base64 -d > decoded_image.png
|
||||
```
|
||||
|
||||
Flag:
|
||||
|
||||
```txt
|
||||
CTF{PUCKMAN}
|
||||
```
|
||||
|
||||
## Open_Secrets
|
||||
|
||||
> Someone trusted the wrong site and network didn't forget. Can you piece together what was left behind? Some people still think locks are optional on digital doors.
|
||||
|
||||
The provided file was a network capture pcap file. The challenge title and filename hint at unencrypted traffic. Open up Wireshark and apply an HTTP filter. There is an HTTP stream that leaks the flag:
|
||||
|
||||
```txt
|
||||
mmuctf{plaintext_login_leak}
|
||||
```
|
||||
|
||||
## C-x C-s
|
||||
|
||||
> Ugh, I keep typing ^x ^s in my shell instead of saving, should've stuck with Emacs
|
||||
|
||||
This one made me mad. The provided file is a PCAP file. Another network forensics challenge, or so I thought.
|
||||
|
||||
I opened the file with Wireshark and noticed a new kind of communication — USB traffic. Each URB_INTERRUPT in the file corresponds to a key pressed, and the Leftover Capture Data field shows the hex value of the key in 8-byte format.
|
||||
|
||||
Creating a filter to list all interrupt communication with non-empty 8 bytes:
|
||||
|
||||
```txt
|
||||
usb.transfer_type == 0x01 && usb.dst == "host" && !(usb.capdata == 00:00:00:00:00:00:00:00)
|
||||
```
|
||||
|
||||
Crafting a tshark command to extract the data:
|
||||
|
||||
```zsh
|
||||
tshark -r thekey.pcapng -Tfields -Eseparator=, -e usb.capdata -Y 'usb.transfer_type == 0x01 && usb.dst == "host" && !(usb.capdata == 00:00:00:00:00:00:00:00)' | sed 's/://g' > usb.capdata
|
||||
```
|
||||
|
||||
I wrote a Python script to decode the hex values to key names, but the output appeared to be Vim motions. I wasn't able to fully decipher this to a correct flag.
|
||||
|
||||
These are the challenges I had the time to tackle. If you have any insights or suggestions, share them in the comments.
|
||||
@@ -0,0 +1,116 @@
|
||||
---
|
||||
title: "Setting up Obsidian sync in Windows"
|
||||
description: "Syncing Obsidian in Windows with Windows Task Scheduler with git"
|
||||
date: "2023-10-01"
|
||||
badge: "Tutorial"
|
||||
---
|
||||
|
||||
Syncing Obsidian in Windows with Windows Task Scheduler and git
|
||||
|
||||
[Obsidian](https://obsidian.md/) is a powerful note-taking and knowledge management tool that has gained a dedicated following among researchers, writers, and knowledge workers. To make the most of Obsidian, it's essential to keep your notes synchronized across devices and ensure your data is regularly backed up. Obsidian is the private and flexible writing app that adapts to the way you think.
|
||||
|
||||
Obsidian is free for personal use. It does not charge based on features or usage. You only pay if you use Obsidian commercially.
|
||||
|
||||
In this article, we'll guide you through the process of setting up automated synchronization for your Obsidian vault on Windows using Windows Task Scheduler and a PowerShell script.[^note]
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before we dive into setting up the synchronization task, ensure you have the following prerequisites in place:
|
||||
|
||||
1. **Obsidian Installed:** Make sure Obsidian is [installed](https://obsidian.md/download) on your Windows computer, and you have an existing Obsidian vault that you want to sync.
|
||||
|
||||
1. **Git Installed:** You'll need Git installed and configured with your Obsidian vault's repository. If you haven't already done this, you can download Git from the [official website](https://git-scm.com/downloads) and follow the installation instructions.
|
||||
|
||||
1. **PowerShell Script:** The provided PowerShell script, sync.ps1, should be located in your Obsidian vault folder.
|
||||
|
||||
## Understanding the PowerShell Script
|
||||
|
||||
The provided PowerShell script, sync.ps1, is the heart of our synchronization task. It automates the process of committing changes in your Obsidian vault's Git repository and pushing them to your remote repository (e.g., GitHub or GitLab). Here's a brief overview of the script:
|
||||
|
||||
```ps1
|
||||
# Define the folder path
|
||||
$FolderPath = "C:\path\to\obsidian-vault"
|
||||
|
||||
# Change directory to the repository folder
|
||||
Set-Location -Path $FolderPath
|
||||
|
||||
# Check if the working tree is clean
|
||||
$IsClean = (git diff-index --quiet HEAD --) -or (git diff-files --quiet)
|
||||
|
||||
if ($IsClean) {
|
||||
Write-Host "Working tree is clean. Skipping commit and pushing changes."
|
||||
git push
|
||||
} else {
|
||||
# Get the current date in the desired format (Day - Date)
|
||||
$CommitMessage = "$(Get-Date -Format 'dddd - yyyy-MM-dd HH:mm:ss')"
|
||||
|
||||
# Commit the changes
|
||||
git add .
|
||||
git commit -m $CommitMessage
|
||||
|
||||
# Push the changes
|
||||
git push
|
||||
}
|
||||
```
|
||||
|
||||
This script does the following:
|
||||
|
||||
- Sets the folder path to your Obsidian vault.
|
||||
- Checks if there are any changes to commit. If the vault is clean (no changes), it skips the commit and only pushes changes.
|
||||
- If there are changes, it commits them with a timestamped message and then pushes them to the remote repository.
|
||||
|
||||
## Setting up Windows Task Scheduler
|
||||
|
||||
Now that we have our PowerShell script ready, let's set up a scheduled task using Windows Task Scheduler to automate the synchronization process. Follow these steps:
|
||||
|
||||
1. **Open an Elevated PowerShell Prompt:** To run the necessary commands, open PowerShell as an administrator.
|
||||
|
||||
1. **Define Task Variables:** Execute the following commands to define variables for your task:
|
||||
|
||||
```ps1
|
||||
$ScriptPath = "C:\path\to\obsidian-vault\sync.ps1"
|
||||
$TaskName = "obsidian-sync"
|
||||
$UserAccount = "$env:USERDOMAIN\$env:USERNAME"
|
||||
```
|
||||
|
||||
Replace $ScriptPath with the actual path to your sync.ps1 script, $TaskName with a name for your task.
|
||||
|
||||
1. **Configure Task Trigger:** Set up a trigger for your task. In this example, we'll schedule the task to run daily at 9:00 PM:
|
||||
|
||||
```ps1
|
||||
$Trigger = New-ScheduledTaskTrigger -Daily -At '9:00 PM'
|
||||
```
|
||||
|
||||
You can adjust the schedule according to your preference.
|
||||
|
||||
1. **Define Task Principal and Settings:** Configure the task's principal (the user account under which the task will run) and settings:
|
||||
|
||||
```ps1
|
||||
$Principal = New-ScheduledTaskPrincipal -UserId $UserAccount -RunLevel Highest
|
||||
$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -RunOnlyIfNetworkAvailable -WakeToRun
|
||||
```
|
||||
|
||||
These settings ensure that the task runs with the necessary privileges and conditions.
|
||||
|
||||
1. **Define Task Action:** Specify the action that the task will perform, which is running the PowerShell script:
|
||||
|
||||
```ps1
|
||||
$Action = New-ScheduledTaskAction -Execute 'PowerShell.exe' -Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$ScriptPath`""
|
||||
```
|
||||
|
||||
This action runs PowerShell with the provided script.
|
||||
|
||||
1. **Create the Scheduled Task:** Finally, create the scheduled task and register it with Windows Task Scheduler:
|
||||
|
||||
```ps1
|
||||
$Task = New-ScheduledTask -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings
|
||||
Register-ScheduledTask -TaskName $TaskName -InputObject $Task
|
||||
```
|
||||
|
||||
The Obsidian synchronization task is now set up and ready to run automatically based on the schedule you defined.
|
||||
|
||||
# Inspiration
|
||||
|
||||
When looking up on how to implement this I found some breakthrough from this [stack overflow](https://stackoverflow.com/questions/7195503/setting-up-a-cron-job-in-windows) post. There are suggestions on how to do this using cron jobs in wsl. They are worth a look at. This article on [powershell](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/new-scheduledtask?view=windowsserver2022-ps) with windows server was also helpful.
|
||||
|
||||
[^note]: This method is only optimal for desktop sync. To sync across devices obsidian has a paid plan that has some extra features Such as Sync notes across devices, End-to-end encryption, Version history, Priority email support. Consider checking it out at [https://obsidian.md/pricing](https://obsidian.md/pricing)
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: "Semantic Versioning(SemVer) and Conventional Commits"
|
||||
description: "Adopting best practices in your git and build workflows."
|
||||
date: "2023-01-24"
|
||||
badge: "Workflow"
|
||||
---
|
||||
|
||||
Adopting best practices in your git and build workflows.
|
||||
|
||||
# What is Semver?
|
||||
|
||||
Semantic versioning or just SemVer is a dependency naming guideline that aims to bring some sanity into naming libraries and packages in software development. It is a standard versioning system created by **Tom Preston Werner** (co-founder of Github) that's used to communicate what changes are in a release.
|
||||
|
||||
An efficient and smooth versioning becomes crucial for efficient software delivery especially for projects that expose a public API. This saves the developers the chaos that is **dependency hell**. Dependency hell is a colloquial term for the frustration of some software users who have installed software packages which have dependencies on specific versions of other software packages.
|
||||
|
||||
The dependency issue arises when several packages have dependencies on the same shared packages or libraries, but they depend on different and incompatible versions of the shared packages. If the shared package or library can only be installed in a single version, the user may need to address the problem by obtaining newer or older versions of the dependent packages. This, in turn, may break other dependencies and push the problem to another set of packages.
|
||||
|
||||
## Semantic Versioning Specifications
|
||||
|
||||
### Summary
|
||||
|
||||
Semantic versioning is based on three numbers that indicate the versions of the software and the compatibilities. They are `X.Y.Z` corresponding to `Major.Minor.Patch`
|
||||
|
||||
- **Major Version (X):** Introduces major changes to the code and breaking changes toward backward compatibility
|
||||
- **Minor Version (Y):** These code changes don't introduce breaking changes. This could be adding a new feature that doesn't break changes but are not exactly bug fixes.
|
||||
- **Patches (Z):** Mainly correspond to bug fixes
|
||||
|
||||
### Examples in common packages
|
||||
|
||||
#### React(v18.2.0)
|
||||
|
||||
- **Major -> 18**
|
||||
- **Minor -> 2**
|
||||
- **Patch -> 0**
|
||||
|
||||
---
|
||||
|
||||
#### Pipx(v1.1.0)
|
||||
|
||||
- **Major -> 1**
|
||||
- **Minor -> 1**
|
||||
- **Patch -> 0**
|
||||
|
||||
---
|
||||
|
||||
## Start at v0.1.0 or v1.0.0?
|
||||
|
||||
Convention recommends that in the initial development phase the starting version should be 0.1.0 which makes sense since it's not in production yet.
|
||||
|
||||
The version should only be bumped to 1.0.0 only if it is being used in production and stable.
|
||||
|
||||
If your software is being used in production, it should probably already be 1.0.0. If you have a stable API on which users have come to depend, you should be 1.0.0. If you're worrying a lot about backwards compatibility, you should probably already be 1.0.0.
|
||||
|
||||
# Conventional Commits
|
||||
|
||||
Conventional Commits is a convention on top of commit messages that are used to automate version changes. It provides an easy set of rules for creating commit messages and works well with SemVer by describing features, fixes and breaking changes that correspond to SemVer standards.
|
||||
|
||||
## Summary
|
||||
|
||||
The commit message should be structured as follows:
|
||||
|
||||
```
|
||||
<type>[optional scope]: <description>
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer(s)]
|
||||
```
|
||||
|
||||
The commit contains the following structural elements, to communicate intent to the consumers of your library:
|
||||
|
||||
1. fix: a commit of the type fix patches a bug in your codebase (this correlates with PATCH in Semantic Versioning).
|
||||
2. feat: a commit of the type feat introduces a new feature to the codebase (this correlates with MINOR in Semantic Versioning).
|
||||
3. BREAKING CHANGE: a commit that has a footer BREAKING CHANGE:, or appends a ! after the type/scope, introduces a breaking API change (correlating with MAJOR in Semantic Versioning). A BREAKING CHANGE can be part of commits of any type.
|
||||
4. types other than fix: and feat: are allowed, for example @commitlint/config-conventional (based on the Angular convention) recommends build:, chore:, ci:, docs:, style:, refactor:, perf:, test:, and others.
|
||||
5. footers other than BREAKING CHANGE: may be provided and follow a convention similar to git trailer format.
|
||||
|
||||
# Resources
|
||||
|
||||
- [https://semver.org/](https://semver.org/)
|
||||
- [https://www.conventionalcommits.org/en/v1.0.0/](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "Subresource Integrity"
|
||||
description: "Understanding Subresource Integrity"
|
||||
date: "2023-04-29"
|
||||
badge: "Security"
|
||||
---
|
||||
|
||||
Understanding Subresource Integrity
|
||||
|
||||
In my junior year while I was learning HTML and CSS I got to use the Bootstrap css library. The easiest way to obtain the files was from a Content Delivery Network(CDN). I could see links such as:
|
||||
|
||||
```html
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
```
|
||||
|
||||
I didn't bother with the `integrity` attribute of the link(guess I wasn't curious enough). The integrity attribute has a cryptographic hash value that is important in CDNs and its called the Subresource Integrity.
|
||||
|
||||
Subresource Integrity (SRI) is a security feature in web browsers that helps prevent malicious or compromised scripts from being loaded and executed on a website. It is a security feature that enables browsers to verify that resources they fetch (for example, from a CDN) are delivered without unexpected manipulation. It works by allowing you to provide a cryptographic hash that a fetched resource must match.
|
||||
|
||||
When a resource, such as a script or stylesheet, fails the SRI check, the browser will block the resource from being loaded and displayed on the website.
|
||||
|
||||
## How Subresource Integrity Helps
|
||||
|
||||
SRI helps protect against supply chain attacks where a CDN or third-party host is compromised. By verifying the hash of the fetched resource, the browser ensures the content hasn't been tampered with.
|
||||
|
||||
## Using it
|
||||
|
||||
To use SRI, you add an `integrity` attribute to the `<script>` or `<link>` element containing a base64-encoded cryptographic hash of the resource.
|
||||
|
||||
## Tools for generating SRI hashes
|
||||
|
||||
You can generate SRI hashes using the `openssl` command-line tool or online tools like [srihash.org](https://www.srihash.org/).
|
||||
|
||||
```sh
|
||||
cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A
|
||||
```
|
||||
|
||||
## Cross-Origin Resource Sharing and Subresource Integrity
|
||||
|
||||
For SRI to work with resources loaded from a different origin, the server must include the `Access-Control-Allow-Origin` header. The `crossorigin="anonymous"` attribute is also required on the element.
|
||||
|
||||
## How browsers handle Subresource Integrity
|
||||
|
||||
When a browser encounters an element with an `integrity` attribute, it:
|
||||
1. Fetches the resource
|
||||
2. Computes the hash of the fetched resource
|
||||
3. Compares the computed hash with the one specified in the `integrity` attribute
|
||||
4. If they match, the resource is used; if not, it's blocked
|
||||
|
||||
## References
|
||||
|
||||
- [MDN Web Docs - Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
|
||||
- [W3C Subresource Integrity Specification](https://www.w3.org/TR/SRI/)
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: "What say you?"
|
||||
description: "Adding a comment system by utterances"
|
||||
date: "2023-08-24"
|
||||
badge: "Blog"
|
||||
---
|
||||
|
||||
Free speech enhanced
|
||||
|
||||
There is no point in writing online if your readers cannot provide feedback for your writings. You might as well be shouting at a void. My ideal commenting system has to be free, open source and should not be a hassle to setup. Luckily, [utterances 🔮](https://github.com/utterance/utterances) checks all of these conditions
|
||||
|
||||
It is a lightweight comments widget built on GitHub issues. Use GitHub issues for blog comments, wiki pages and more! The notable features are:
|
||||
|
||||
- Open source. 🙌
|
||||
- No tracking, no ads, always free. 📡🚫
|
||||
- No lock-in. All data stored in GitHub issues. 🔓
|
||||
- Styled with Primer, the css toolkit that powers GitHub. 💅
|
||||
- Dark theme. 🌘
|
||||
- Lightweight. Vanilla TypeScript. No font downloads, JavaScript frameworks or polyfills for evergreen browsers. 🐦🌲
|
||||
|
||||
## How it works
|
||||
|
||||
When Utterances loads, the GitHub [issue search API](https://developer.github.com/v3/search/#search-issues) is used to find the issue associated with the page based on url, pathname or title. If it cannot find an issue that matches the page, [utterances-bot](https://github.com/utterances-bot) will automatically create an issue the first time someone comments.
|
||||
|
||||
To comment, users must authorize the utterances app to post on their behalf using the GitHub [OAuth flow](https://developer.github.com/v3/oauth/#web-application-flow). Alternatively, users can comment on the GitHub issue directly.
|
||||
|
||||
# Setting up Utterances
|
||||
|
||||
### Repository
|
||||
|
||||
Choose the repository utterances will connect to.
|
||||
|
||||
Make sure the repo is public, otherwise your readers will not be able to view the issues/comments.
|
||||
Make sure the utterances app is installed on the repo, otherwise users will not be able to post comments.
|
||||
If your repo is a fork, navigate to its settings tab and confirm the issues feature is turned on.
|
||||
|
||||
```
|
||||
repo:
|
||||
username/repo
|
||||
```
|
||||
|
||||
A public GitHub repository. This is where the blog post issues and issue-comments will be posted.
|
||||
|
||||
### Theme
|
||||
|
||||
Choose an Utterances theme that matches your blog.
|
||||
|
||||
### Enable Utterances
|
||||
|
||||
Add the following script tag to your blog's template. Position it where you want the comments to appear. Customize the layout using the .utterances and .utterances-frame selectors.
|
||||
|
||||
```js
|
||||
<script
|
||||
src="https://utteranc.es/client.js"
|
||||
repo="[ENTER REPO HERE]"
|
||||
issue-term="title"
|
||||
theme="gruvbox-dark"
|
||||
crossorigin="anonymous"
|
||||
async
|
||||
></script>
|
||||
```
|
||||
|
||||
The complete documentation can be found at [https://utteranc.es](https://utteranc.es/)
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: "Configure Vue.js app With Tailwind CSS"
|
||||
description: "Tailwindcss in vuejs"
|
||||
date: "2021-06-12"
|
||||
badge: "Tutorial"
|
||||
---
|
||||
|
||||
A guide on how to use [Tailwind CSS](https://tailwindcss.com/) in your Vue project.
|
||||
|
||||
> **Note**
|
||||
> This article was originally published on my old medium blog. I moved it here for archival purposes.
|
||||
|
||||
Tailwind is a utility-first CSS framework for rapidly building custom user interfaces. It is an un-opinionated framework which means that it doesn't come with custom configurations like Bootstrap or Bulma(Yeah, I know, no one should tell you how to live your life).
|
||||
|
||||
Now let's get to the juicy part that brought us all here. I'll assume that we are using Vue 3 and the Vue CLI is already installed.
|
||||
|
||||
## Create a new Vue project
|
||||
|
||||
Open a terminal window and type the following commands to create a new Vue project in the current directory.
|
||||
|
||||
```sh
|
||||
vue create vue-app
|
||||
```
|
||||
|
||||
wait for the dependencies installation to finish then
|
||||
|
||||
```sh
|
||||
cd vue-app
|
||||
```
|
||||
|
||||
to change into the project directory.
|
||||
|
||||
## Install the Tailwind CSS and its peer dependencies as a dev dependency using npm
|
||||
|
||||
```sh
|
||||
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest
|
||||
```
|
||||
|
||||
## Create your configuration files
|
||||
|
||||
Next, generate your `tailwind.config.js` and `postcss.config.js` files:
|
||||
|
||||
```sh
|
||||
npx tailwindcss init -p
|
||||
```
|
||||
|
||||
This command will create a minimal `tailwind.config.js` file at the root of your project:
|
||||
|
||||
```js
|
||||
// tailwind.config.js
|
||||
module.exports = {
|
||||
purge: [],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
```
|
||||
|
||||
It will also create a `postcss.config.js` file that includes tailwindcss and autoprefixer already configured. Postcss is needed to process tailwind:
|
||||
|
||||
```js
|
||||
// postcss.config.js
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Configure Tailwind to remove unused styles in production
|
||||
|
||||
In your tailwind.config.js file, configure the purge option with the paths to all of your pages and components so Tailwind can tree-shake unused styles in production builds:
|
||||
|
||||
```js
|
||||
// tailwind.config.js
|
||||
module.exports = {
|
||||
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||
darkMode: false, // or 'media' or 'class'
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
variants: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
```
|
||||
|
||||
## Include Tailwind in your CSS
|
||||
|
||||
Navigate to `src/assets/css` and create a new `tailwind.css` file in the folder, and use the @tailwind directive to include Tailwind's base, components, and utilities styles in the file.
|
||||
|
||||
```css
|
||||
/* ./src/assets/css/tailwind.css */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
```
|
||||
|
||||
Tailwind will swap these directives out at build-time with all of the styles it generates based on your configured design system.
|
||||
|
||||
## Import Tailwind into your Vue app
|
||||
|
||||
Finally, ensure your CSS file is being imported in your `./src/main.js` file:
|
||||
|
||||
```js
|
||||
// src/main.js
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import './index.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
```
|
||||
|
||||
You're finished! Now when you run npm run dev, Tailwind CSS will be ready to use in your Vue app! You can now proceed to create awesome stuff
|
||||
@@ -1,41 +0,0 @@
|
||||
---
|
||||
title: "Why I Love Open Source"
|
||||
description: "Reflections on the open source community and why contributing to open source projects is so rewarding."
|
||||
date: "2025-02-10"
|
||||
image: "https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=800&h=400&fit=crop"
|
||||
badge: "Thoughts"
|
||||
---
|
||||
|
||||
# Why I Love Open Source
|
||||
|
||||
Open source software has transformed the way we build technology. As a developer, contributing to open source projects has been one of the most rewarding aspects of my career.
|
||||
|
||||
## The Power of Community
|
||||
|
||||
The open source community is incredibly diverse and welcoming. Some of the best learning experiences come from:
|
||||
|
||||
- **Code reviews** — Getting feedback from experienced developers
|
||||
- **Issue discussions** — Understanding real-world problems and solutions
|
||||
- **Collaboration** — Working with people from around the world
|
||||
|
||||
## Giving Back
|
||||
|
||||
Contributing to open source isn't just about writing code. There are many ways to help:
|
||||
|
||||
1. **Documentation** — Good docs make projects accessible to everyone
|
||||
2. **Bug reports** — Detailed bug reports save maintainers hours of work
|
||||
3. **Testing** — Trying out new features and providing feedback
|
||||
4. **Mentoring** — Helping newcomers get started with contributions
|
||||
|
||||
## Building in the Open
|
||||
|
||||
One of the things I enjoy most is building projects in the open. Sharing your work publicly:
|
||||
|
||||
- Forces you to write cleaner, more maintainable code
|
||||
- Creates a portfolio of your work
|
||||
- Helps others learn from your approach
|
||||
- Opens doors to collaboration and feedback
|
||||
|
||||
## Final Thoughts
|
||||
|
||||
If you haven't contributed to open source yet, I encourage you to start. Find a project you use and love, and look for ways to help. Every contribution, no matter how small, makes a difference.
|
||||
Reference in New Issue
Block a user