generated from nhcarrigan/template
Compare commits
19 Commits
76749ba6b8
...
main
Author | SHA1 | Date | |
---|---|---|---|
4d73218d45
|
|||
96eb46a6ea
|
|||
94b481b9a2
|
|||
73618f9181
|
|||
5b2a7f6eea
|
|||
415c122a36
|
|||
b0031e35e0
|
|||
4f82486799
|
|||
4cc0240cf6
|
|||
9bd5956146
|
|||
06b817cd55
|
|||
f870bd28d8
|
|||
031406c95b
|
|||
aff99e119a
|
|||
166755a6e0
|
|||
b191f14a0b
|
|||
00dc40ba47
|
|||
e33df16e43
|
|||
e6f00559a9
|
20
README.md
20
README.md
@ -1,24 +1,14 @@
|
||||
# New Repository Template
|
||||
# Static Pages
|
||||
|
||||
This template contains all of our basic files for a new GitHub repository. There is also a handy workflow that will create an issue on a new repository made from this template, with a checklist for the steps we usually take in setting up a new repository.
|
||||
|
||||
If you're starting a Node.JS project with TypeScript, we have a [specific template](https://github.com/naomi-lgbt/nodejs-typescript-template) for that purpose.
|
||||
|
||||
## Readme
|
||||
|
||||
Delete all of the above text (including this line), and uncomment the below text to use our standard readme template.
|
||||
|
||||
<!-- # Project Name
|
||||
|
||||
Project Description
|
||||
This repository holds the files for all of our basic static site pages.
|
||||
|
||||
## Live Version
|
||||
|
||||
This page is currently deployed. [View the live website.]
|
||||
This page is currently deployed.
|
||||
|
||||
## Feedback and Bugs
|
||||
|
||||
If you have feedback or a bug report, please feel free to open a GitHub issue!
|
||||
If you have feedback or a bug report, please feel free to open an issue!
|
||||
|
||||
## Contributing
|
||||
|
||||
@ -36,4 +26,4 @@ Copyright held by Naomi Carrigan.
|
||||
|
||||
## Contact
|
||||
|
||||
We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via email at `contact@nhcarrigan.com`. -->
|
||||
We may be contacted through our [Chat Server](http://chat.nhcarrigan.com) or via email at `contact@nhcarrigan.com`.
|
||||
|
36
books.sh
Executable file
36
books.sh
Executable file
@ -0,0 +1,36 @@
|
||||
IFS=$'\n'
|
||||
|
||||
# Initialize an empty string to hold the list of books in JSON-like format
|
||||
books=""
|
||||
filecount=$(find /home/naomi/cloud/Books -type f | wc -l)
|
||||
echo "Found $filecount files."
|
||||
current=0
|
||||
|
||||
# Loop over each file found by find
|
||||
for file in $(find /home/naomi/cloud/Books -type f -print0 | tr '\0' '\n'); do
|
||||
current=$((current + 1))
|
||||
echo -ne "Processing $current/$filecount\r"
|
||||
title=$(exiftool "$file" | grep "^Title\s*:" | cut -d ":" -f 2 | sed -e 's/^[[:space:]]*//')
|
||||
author=$(exiftool "$file" | grep "^Creator\s*:" | cut -d ":" -f 2 | sed -e 's/^[[:space:]]*//')
|
||||
|
||||
if [ -z "$title" ]; then
|
||||
# remove .mp3 from the title
|
||||
title=$(basename "$file" | sed -e 's/\.*//g')
|
||||
fi
|
||||
if [ -z "$author" ]; then
|
||||
author=$(exiftool "$file" | grep "^Author\s*:" | cut -d ":" -f 2 | sed -e 's/^[[:space:]]*//')
|
||||
fi
|
||||
if [ -z "$author" ]; then
|
||||
author="Unknown Author"
|
||||
fi
|
||||
|
||||
# use jq to add the book to the list
|
||||
books="$books$(jq -n --arg title "$title" --arg author "$author" '{title: $title, author: $author}'),"
|
||||
done
|
||||
|
||||
# Remove trailing comma and add square brackets to complete the list
|
||||
books="[${books%,}]"
|
||||
|
||||
# Write to ./books/books.json
|
||||
echo "$books" > ./books/books.json
|
||||
echo -ne "Done!\r"
|
1210
books/books.json
Normal file
1210
books/books.json
Normal file
File diff suppressed because it is too large
Load Diff
117
books/index.html
Normal file
117
books/index.html
Normal file
@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Naomi's Book Library</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="An interactive explorer for the books Naomi reads." />
|
||||
<script src="https://cdn.nhcarrigan.com/headers/index.js" async defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Naomi's Book Library</h1>
|
||||
<section>
|
||||
<p>An interactive explorer for the books Naomi reads.</p>
|
||||
<p id="count">Loading library...</p>
|
||||
</section>
|
||||
<div style="display: none;">
|
||||
<span>Search Authors: </span>
|
||||
<input type="text" id="author" />
|
||||
</div>
|
||||
<div style="display: none;">
|
||||
<span>Search Titles: </span>
|
||||
<input type="text" id="title" />
|
||||
</div>
|
||||
<div style="display: none;">
|
||||
<button type="button" id="clear">Clear Filters</button>
|
||||
</div>
|
||||
<table id="books">
|
||||
|
||||
</table>
|
||||
</main>
|
||||
</body>
|
||||
<script>
|
||||
const authorQuery = document.getElementById('author');
|
||||
const titleQuery = document.getElementById('title');
|
||||
const resetButton = document.getElementById('clear');
|
||||
const bookTable = document.getElementById('books');
|
||||
const filterBooks = (author, title) => {
|
||||
let result = [...bookList];
|
||||
if(author) {
|
||||
result = result.filter(book => book.author.toLowerCase().includes(author.toLowerCase()));
|
||||
}
|
||||
if(title) {
|
||||
result = result.filter(book => book.title.toLowerCase().includes(title.toLowerCase()));
|
||||
}
|
||||
resetButton.parentElement.style.display = author || title ? "block" : "none";
|
||||
document.getElementById('count').innerText = author || title ? `Filtered to ${result.length} books from ${bookList.length}.` : `Naomi currently has ${bookList.length} books.`;
|
||||
updateTable(result);
|
||||
}
|
||||
const loadBooks = (books) => {
|
||||
bookList.push(...books);
|
||||
authorQuery.value = "";
|
||||
titleQuery.value = "";
|
||||
authorQuery.parentElement.style.display = "block";
|
||||
titleQuery.parentElement.style.display = "block";
|
||||
document.getElementById('count').innerText = `Naomi currently has ${books.length} books.`;
|
||||
updateTable(books);
|
||||
}
|
||||
const updateTable = (books) => {
|
||||
books = books.sort((a, b) => a.title.localeCompare(b.title));
|
||||
bookTable.innerHTML = "";
|
||||
const header = document.createElement('tr');
|
||||
const authorHeader = document.createElement('th');
|
||||
authorHeader.innerText = "Author";
|
||||
const titleHeader = document.createElement('th');
|
||||
titleHeader.innerText = "Title";
|
||||
header.appendChild(titleHeader);
|
||||
header.appendChild(authorHeader);
|
||||
bookTable.appendChild(header);
|
||||
books.forEach(book => {
|
||||
const row = document.createElement('tr');
|
||||
const author = document.createElement('td');
|
||||
author.innerText = book.author;
|
||||
const title = document.createElement('td');
|
||||
title.innerText = book.title;
|
||||
row.appendChild(title);
|
||||
row.appendChild(author);
|
||||
bookTable.appendChild(row);
|
||||
});
|
||||
}
|
||||
const bookList = [];
|
||||
fetch("./books.json").then(res => res.json()).then(data => loadBooks(data))
|
||||
|
||||
authorQuery?.addEventListener("input", (e) => filterBooks(e.target.value, titleQuery.value));
|
||||
titleQuery?.addEventListener("input", (e) => filterBooks(authorQuery.value, e.target.value));
|
||||
resetButton?.addEventListener("click", () => {
|
||||
authorQuery.value = "";
|
||||
titleQuery.value = "";
|
||||
filterBooks("", "");
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
tr:nth-of-type(even) {
|
||||
background-color: #db7093dd;
|
||||
color: #ffefef;
|
||||
}
|
||||
input {
|
||||
background:var(--foreground);
|
||||
color:var(--background);
|
||||
border:1px solid white;
|
||||
border-radius:10px;
|
||||
padding:.25rem
|
||||
}
|
||||
button {
|
||||
background:var(--foreground);
|
||||
color:var(--background);
|
||||
border:1px solid white;
|
||||
border-radius:10px;
|
||||
padding:.25rem;
|
||||
cursor:url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer;
|
||||
}
|
||||
</style>
|
||||
</html>
|
@ -22,6 +22,9 @@
|
||||
<span>Search Titles: </span>
|
||||
<input type="text" id="title" />
|
||||
</div>
|
||||
<div style="display: none;">
|
||||
<button type="button" id="clear">Clear Filters</button>
|
||||
</div>
|
||||
<table id="songs">
|
||||
|
||||
</table>
|
||||
@ -30,6 +33,7 @@
|
||||
<script>
|
||||
const artistQuery = document.getElementById('artist');
|
||||
const titleQuery = document.getElementById('title');
|
||||
const resetButton = document.getElementById('clear');
|
||||
const songTable = document.getElementById('songs');
|
||||
const filterSongs = (artist, title) => {
|
||||
let result = [...songList];
|
||||
@ -39,10 +43,14 @@
|
||||
if(title) {
|
||||
result = result.filter(song => song.title.toLowerCase().includes(title.toLowerCase()));
|
||||
}
|
||||
resetButton.parentElement.style.display = artist || title ? "block" : "none";
|
||||
document.getElementById('count').innerText = artist || title ? `Filtered to ${result.length} songs from ${songList.length}.` : `Naomi currently has ${songList.length} songs.`;
|
||||
updateTable(result);
|
||||
}
|
||||
const loadSongs = (songs) => {
|
||||
songList.push(...songs);
|
||||
artistQuery.value = "";
|
||||
titleQuery.value = "";
|
||||
artistQuery.parentElement.style.display = "block";
|
||||
titleQuery.parentElement.style.display = "block";
|
||||
document.getElementById('count').innerText = `Naomi currently has ${songs.length} songs.`;
|
||||
@ -75,15 +83,22 @@
|
||||
|
||||
artistQuery?.addEventListener("input", (e) => filterSongs(e.target.value, titleQuery.value));
|
||||
titleQuery?.addEventListener("input", (e) => filterSongs(artistQuery.value, e.target.value));
|
||||
resetButton?.addEventListener("click", () => {
|
||||
artistQuery.value = "";
|
||||
titleQuery.value = "";
|
||||
filterSongs("", "");
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
table {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
}
|
||||
tr:nth-of-type(even) {
|
||||
background-color: #db7093dd;
|
||||
color: #ffefef;
|
||||
background-color: var(--foreground);
|
||||
color: var(--background);
|
||||
}
|
||||
input {
|
||||
background:var(--foreground);
|
||||
@ -92,5 +107,20 @@
|
||||
border-radius:10px;
|
||||
padding:.25rem
|
||||
}
|
||||
button {
|
||||
background:var(--foreground);
|
||||
color:var(--background);
|
||||
border:1px solid white;
|
||||
border-radius:10px;
|
||||
padding:.25rem;
|
||||
cursor:url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer;
|
||||
}
|
||||
tr {
|
||||
max-width: 100%;
|
||||
}
|
||||
td {
|
||||
max-width: 50%;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
</html>
|
93156
music/songs.json
93156
music/songs.json
File diff suppressed because it is too large
Load Diff
57
products.sh
Executable file
57
products.sh
Executable file
@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set your Gitea instance base URL and API token
|
||||
GITEA_URL="https://git.nhcarrigan.com/api/v1"
|
||||
TOKEN=$GITEA_TOKEN # Pass as CLI arg
|
||||
|
||||
# Output file
|
||||
OUTPUT_FILE="./products/data.json"
|
||||
|
||||
# Organizations and their categories
|
||||
declare -A orgs=(
|
||||
["nhcarrigan"]="public"
|
||||
["nhcarrigan-games"]="games"
|
||||
["nhcarrigan-private"]="private"
|
||||
["nhcarrigan-archive"]="archived"
|
||||
)
|
||||
|
||||
# Initialize the JSON array
|
||||
echo "[" > "$OUTPUT_FILE"
|
||||
|
||||
first=true
|
||||
|
||||
for org in "${!orgs[@]}"; do
|
||||
category="${orgs[$org]}"
|
||||
echo "Fetching repos for $org..."
|
||||
|
||||
# Fetch repos from the API
|
||||
response=$(curl -s -H "Authorization: token $TOKEN" "$GITEA_URL/orgs/$org/repos")
|
||||
|
||||
# Parse each repo
|
||||
echo "$response" | jq -c '.[]' | while read -r repo; do
|
||||
name=$(echo "$repo" | jq -r '.name')
|
||||
description=$(echo "$repo" | jq -r '.description // ""')
|
||||
url=$(echo "$repo" | jq -r '.website')
|
||||
|
||||
# Skip ".profile" repos
|
||||
if [[ "$name" == ".profile" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Add comma if not the first item
|
||||
if [ "$first" = true ]; then
|
||||
first=false
|
||||
else
|
||||
echo "," >> "$OUTPUT_FILE"
|
||||
fi
|
||||
|
||||
# Write the repo object to the file
|
||||
jq -n --arg name "$name" --arg description "$description" --arg url "$url" --arg category "$category" \
|
||||
'{name: $name, description: $description, url: $url, category: $category}' >> "$OUTPUT_FILE"
|
||||
done
|
||||
done
|
||||
|
||||
# Close the JSON array
|
||||
echo "]" >> "$OUTPUT_FILE"
|
||||
|
||||
echo "✨ Done! Your product data is in $OUTPUT_FILE 💖"
|
260
products/data.json
Normal file
260
products/data.json
Normal file
@ -0,0 +1,260 @@
|
||||
[
|
||||
{
|
||||
"name": "mod-logs",
|
||||
"description": "Logs for moderation actions taken on our platforms.",
|
||||
"url": "",
|
||||
"category": "archived"
|
||||
},
|
||||
{
|
||||
"name": "tingle-bot",
|
||||
"description": "Bot for my friend Ruu's server",
|
||||
"url": "",
|
||||
"category": "archived"
|
||||
},
|
||||
{
|
||||
"name": "announcements",
|
||||
"description": "Repository for our announcements page.",
|
||||
"url": "",
|
||||
"category": "archived"
|
||||
},
|
||||
{
|
||||
"name": "forms",
|
||||
"description": "Client and server monorepo for our various webforms",
|
||||
"url": "",
|
||||
"category": "archived"
|
||||
},
|
||||
{
|
||||
"name": "notes",
|
||||
"description": "",
|
||||
"url": "",
|
||||
"category": "private"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"description": "Status updates for our client work.",
|
||||
"url": "",
|
||||
"category": "private"
|
||||
},
|
||||
{
|
||||
"name": "beaver-twitch",
|
||||
"description": "Twitch bot for BigBadBeaver",
|
||||
"url": "",
|
||||
"category": "private"
|
||||
},
|
||||
{
|
||||
"name": "obsidian",
|
||||
"description": "",
|
||||
"url": "",
|
||||
"category": "private"
|
||||
},
|
||||
{
|
||||
"name": "insomnium",
|
||||
"description": "Our Insomnium requests",
|
||||
"url": "",
|
||||
"category": "private"
|
||||
},
|
||||
{
|
||||
"name": "life-of-a-naomi",
|
||||
"description": "A little game",
|
||||
"url": "",
|
||||
"category": "games"
|
||||
},
|
||||
{
|
||||
"name": "naomis-adventure-1",
|
||||
"description": "Our first full-length, paid game!",
|
||||
"url": "",
|
||||
"category": "games"
|
||||
},
|
||||
{
|
||||
"name": "beccalia-origins",
|
||||
"description": "The story of how Becca and Rosalia first met, and how they came to be. Never moved past a demo state.",
|
||||
"url": "https://beccalia.nhcarrigan.com/origins",
|
||||
"category": "games"
|
||||
},
|
||||
{
|
||||
"name": "beccalia-prologue",
|
||||
"description": "A short adventure to introduce our characters Becca and Rosalia. Our first attempt at game development!",
|
||||
"url": "https://beccalia.nhcarrigan.com/prologue",
|
||||
"category": "games"
|
||||
},
|
||||
{
|
||||
"name": "ruu-goblin-quest",
|
||||
"description": "A quick game we made about our friend Ruu, as part of a game jam she was hosting.",
|
||||
"url": "https://goblin.nhcarrigan.com",
|
||||
"category": "games"
|
||||
},
|
||||
{
|
||||
"name": "template",
|
||||
"description": "",
|
||||
"url": "",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "a4p-bot",
|
||||
"description": "Bot for the Art 4 Palestine charity initiative.",
|
||||
"url": "https://a4p.nhcarrigan.com",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "boost-monitor",
|
||||
"description": "Discord bot that monitors boost status in the Caylus Crew server.",
|
||||
"url": "https://oogie.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "docs",
|
||||
"description": "Our documentation site.",
|
||||
"url": "https://docs.nhcarrigan.com",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "eslint-config",
|
||||
"description": "Our custom linter rules for our various TypeScript products.",
|
||||
"url": "https://www.npmjs.com/package/@nhcarrigan/eslint-config",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "espanso",
|
||||
"description": "Our shortcuts for Espanso.",
|
||||
"url": "",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "celestine",
|
||||
"description": "Moderation bot for Discord.",
|
||||
"url": "https://hooks.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "portfolio",
|
||||
"description": "Our main homepage",
|
||||
"url": "https://nhcarrigan.com",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "rig-task-bot",
|
||||
"description": "Task bot for a friend's organisation.",
|
||||
"url": "",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "security",
|
||||
"description": "A quick tool to scan our projects for security concerns.",
|
||||
"url": "https://security.nhcarrigan.com",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "website-headers",
|
||||
"description": "Our global styling and scripts for all of our pages.",
|
||||
"url": "https://cdn.nhcarrigan.com/headers/index.js",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "typescript-config",
|
||||
"description": "Our global TypeScript configuration.",
|
||||
"url": "https://www.npmjs.com/package/@nhcarrigan/typescript-config",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "blog",
|
||||
"description": "Naomi's personal musings.",
|
||||
"url": "https://blog.nhcarrigan.com",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "nginx-configs",
|
||||
"description": "A version controlled backup of our servers' NGINX configurations.",
|
||||
"url": "",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "vscode-themes",
|
||||
"description": "Custom colour schemes for VSCode.",
|
||||
"url": "https://marketplace.visualstudio.com/items?itemName=nhcarrigan.naomis-themes",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": ".gitea",
|
||||
"description": "This repository contains the files that customise our Gitea instance!",
|
||||
"url": "https://git.nhcarrigan.com",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "aria-iuvo",
|
||||
"description": "A user-installable translation application for Discord. Now you can translate messages directly on the platform!",
|
||||
"url": "https://trans-bot.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "cordelia-taryne",
|
||||
"description": "AI-powered virtual assistant for Discord",
|
||||
"url": "https://assistant.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "rosalia-nightsong",
|
||||
"description": "A webserver to handle alerting us to application logs and errors.",
|
||||
"url": "https://alerts.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "logger",
|
||||
"description": "Our custom logging package, which pipes logs to our alerts server.",
|
||||
"url": "https://www.npmjs.com/package/@nhcarrigan/logger",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "melody-iuvo",
|
||||
"description": "Task management bot for Discord",
|
||||
"url": "https://tasks.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "static-pages",
|
||||
"description": "The raw HTML pages served via our production box.",
|
||||
"url": "",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "becca-lyria",
|
||||
"description": "An AI-powered Discord bot that allows you to play an RPG without any friends!",
|
||||
"url": "https://becca.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "maylin-taryne",
|
||||
"description": "An AI-powered companion to help you through the tough times.",
|
||||
"url": "https://maylin.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "gwen-abalise",
|
||||
"description": "A ticket system for Discord!",
|
||||
"url": "https://gwen.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "nails",
|
||||
"description": "Nail polish tracker for my sister",
|
||||
"url": "",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "maribelle",
|
||||
"description": "",
|
||||
"url": "",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "mommy",
|
||||
"description": "Mommy loves you~!",
|
||||
"url": "https://mommy.nhcarrigan.com",
|
||||
"category": "public"
|
||||
},
|
||||
{
|
||||
"name": "mommy-bot",
|
||||
"description": "Mommy loves you everywhere~!",
|
||||
"url": "https://mommy-bot.nhcarrigan.com/",
|
||||
"category": "public"
|
||||
}
|
||||
]
|
63
products/index.html
Normal file
63
products/index.html
Normal file
@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>NHCarrigan Product Directory</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="An automated list of the products offered by NHCarrigan." />
|
||||
<script src="https://cdn.nhcarrigan.com/headers/index.js" async defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>NHCarrigan Product Directory</h1>
|
||||
<section>
|
||||
<p>An automated list of the products offered by NHCarrigan.</p>
|
||||
<p>Products without a link to a public page are either not hosted or still under active development and not shipped yet. </p>
|
||||
<p id="count">Loading directory...</p>
|
||||
</section>
|
||||
<section id="data">
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
<script>
|
||||
const titleCase = (name) => {
|
||||
// Repository names are in kebab-case, so we need to convert them to Title Case.
|
||||
return name
|
||||
.split("-")
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(" ");
|
||||
}
|
||||
const loadProducts = (products) => {
|
||||
const public = products.filter(product => product.category === "public").sort((a, b) => a.name.localeCompare(b.name));
|
||||
const private = products.filter(product => product.category === "private").sort((a, b) => a.name.localeCompare(b.name));
|
||||
const games = products.filter(product => product.category === "games").sort((a, b) => a.name.localeCompare(b.name));
|
||||
const archived = products.filter(product => product.category === "archived").sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
const html = `
|
||||
<h2>Public Products</h2>
|
||||
<p>These are our products which are open source and completely available to the public.</p>
|
||||
${public.map(product => `<h3>${titleCase(product.name)}</h3><p>${product.description}</p>${product.url ? `<p class="italic"><a href="${product.url}" target="_blank">View Product</a></p>` : `<p class="italic">Not hosted.</p>`}`).join("")}
|
||||
<h2>Public Games</h2>
|
||||
<p>The games we have developed are available to the public, but due to licensing we cannot open source them.</p>
|
||||
${games.map(product => `<h3>${titleCase(product.name)}</h3><p>${product.description}</p>${product.url ? `<p class="italic"><a href="${product.url}" target="_blank">Play Game</a></p>` : `<p class="italic">Not hosted.</p>`}`).join("")}
|
||||
<h2>Private Products</h2>
|
||||
<p>These are our products which are closed-source, either due to proprietary information or a client request. However, a hosted version may be available.</p>
|
||||
${private.map(product => `<h3>${titleCase(product.name)}</h3><p>${product.description}</p>${product.url ? `<p class="italic"><a href="${product.url}" target="_blank">View Product</a></p>` : `<p class="italic">Not hosted.</p>`}`).join("")}
|
||||
<h2>Archived Products</h2>
|
||||
<p>These are our products which are no longer maintained, but are still available to the public.</p>
|
||||
${archived.map(product => `<h3>${titleCase(product.name)}</h3><p>${product.description}</p>${product.url ? `<p class="italic"><a href="${product.url}" target="_blank">View Product</a></p>` : `<p class="italic">Not hosted.</p>`}`).join("")}
|
||||
`;
|
||||
|
||||
const count = document.getElementById("count");
|
||||
const data = document.getElementById("data");
|
||||
count.innerText = `Found ${products.length} products.`;
|
||||
data.innerHTML = html;
|
||||
}
|
||||
fetch("./data.json").then(res => res.json()).then(data => loadProducts(data.filter(el => el.name !== "template")))
|
||||
</script>
|
||||
<style>
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
</html>
|
@ -1,364 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Naomi Carrigan</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="This is Naomi's full work history!" />
|
||||
<script
|
||||
src="https://cdn.nhcarrigan.com/headers/index.js"
|
||||
async
|
||||
defer
|
||||
></script>
|
||||
<style>
|
||||
hr {
|
||||
border: 1px solid var(--background);
|
||||
}
|
||||
.title {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
.company {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.type {
|
||||
font-style: italic;
|
||||
}
|
||||
.type::before {
|
||||
content: " - ";
|
||||
}
|
||||
.date {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.info {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
@media screen {
|
||||
.card {
|
||||
background: var(--foreground);
|
||||
color: var(--background);
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
margin: auto;
|
||||
border-radius: 10px;
|
||||
box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.7);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
:root {
|
||||
font-size: 12px;
|
||||
}
|
||||
* {
|
||||
color: black;
|
||||
font-family: "Times New Roman", serif;
|
||||
}
|
||||
video,
|
||||
footer,
|
||||
hr {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Naomi Carrigan</h1>
|
||||
<p class="info">
|
||||
Washington, USA | contact@nhcarrigan.com | https://nhcarrigan.com
|
||||
</p>
|
||||
<p>
|
||||
Passionate technologist dedicated to building inclusive tech communities
|
||||
and empowering individuals to break into the field. With a rich
|
||||
background in community management, software engineering, and developer
|
||||
experience, I strive to create accessible pathways for diverse talent.
|
||||
</p>
|
||||
<p>
|
||||
Interested in hiring me? <a href="https://testimonials.nhcarrigan.com" target="_blank">See what past clients have to say</a>.
|
||||
</p>
|
||||
<section>
|
||||
<h2>Employment</h2>
|
||||
<!-- MARK: Current
|
||||
-->
|
||||
<div class="card">
|
||||
<p class="title">Developer Experience Consultant</p>
|
||||
<div>
|
||||
<span class="company">Deepgram</span>
|
||||
<span class="type">Contract</span>
|
||||
</div>
|
||||
<span class="date">June 2024 - present</span>
|
||||
<hr />
|
||||
<p class="subtitle">Community Bot Engineer</p>
|
||||
<p class="date">July 2023 - June 2024</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Educational Web Developer and Community Manager</p>
|
||||
<div>
|
||||
<span class="company">freeCodeCamp</span>
|
||||
<span class="type">Contract</span>
|
||||
</div>
|
||||
<span class="date">Dec 2020 - present</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Technomancer</p>
|
||||
<div>
|
||||
<span class="company">nhcarrigan</span>
|
||||
<span class="type">Founder</span>
|
||||
</div>
|
||||
<span class="date">Dec 2020 - present</span>
|
||||
</div>
|
||||
<!-- MARK: Prior
|
||||
-->
|
||||
<div class="card">
|
||||
<p class="title">Community Manager and Infrastructure Engineer</p>
|
||||
<div>
|
||||
<span class="company">Streamcord</span>
|
||||
<span class="type">Contract</span>
|
||||
</div>
|
||||
<span class="date">Aug 2021 - Dec 2024</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Senior Integrations Engineer</p>
|
||||
<div>
|
||||
<span class="company">Rythm</span>
|
||||
<span class="type">Contract</span>
|
||||
</div>
|
||||
<span class="date">Apr 2022 - Oct 2024</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Twitch Integration Engineer</p>
|
||||
<div>
|
||||
<span class="company">BigBadBeaver TV</span>
|
||||
<span class="type">Freelance</span>
|
||||
</div>
|
||||
<span class="date">Oct 2022 - Jan 2024</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Community Manager</p>
|
||||
<div>
|
||||
<span class="company">Tweetshift</span>
|
||||
<span class="type">Contract</span>
|
||||
</div>
|
||||
<span class="date">Jan 2022 - May 2023</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Community Manager</p>
|
||||
<div>
|
||||
<span class="company">4C</span>
|
||||
<span class="type">Contract</span>
|
||||
</div>
|
||||
<span class="date">May 2022 - Nov 2022</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Community Manager and Open Source Engineer</p>
|
||||
<div>
|
||||
<span class="company">Sema</span>
|
||||
<span class="type">Contract</span>
|
||||
</div>
|
||||
<span class="date">May 2022 - Sep 2022</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Safeway</p>
|
||||
<div>
|
||||
<span class="company">Service Operations Manager</span>
|
||||
<span class="type">Full-time</span>
|
||||
</div>
|
||||
<span class="date">Nov 2016 - Apr 2020</span>
|
||||
<hr />
|
||||
<p class="subtitle">Person-in-Charge</p>
|
||||
<p class="date">Aug 2013 - Nov 2016</p>
|
||||
<hr />
|
||||
<p class="subtitle">Produce Clerk</p>
|
||||
<p class="date">Feb 2010 - Aug 2013</p>
|
||||
<hr />
|
||||
<p class="subtitle">Courtesy Clerk</p>
|
||||
<p class="date">Aug 2009 - Feb 2010</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Volunteer</h2>
|
||||
<!-- MARK: Volun. Current
|
||||
-->
|
||||
<div class="card">
|
||||
<p class="title">Discord Moderator</p>
|
||||
<div>
|
||||
<span class="company">Virtual Insanity</span>
|
||||
</div>
|
||||
<span class="date">May 2024 - present</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Discord Moderator</p>
|
||||
<div>
|
||||
<span class="company">FruitPursuits</span>
|
||||
</div>
|
||||
<span class="date">Mar 2024 - present</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Development Lead</p>
|
||||
<div>
|
||||
<span class="company">Artists for Palestine</span>
|
||||
</div>
|
||||
<span class="date">Nov 2023 - present</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Discord Moderator</p>
|
||||
<div>
|
||||
<span class="company">Angel Rose</span>
|
||||
</div>
|
||||
<span class="date">Sep 2023 - present</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">
|
||||
Discord Moderator and Platform Engineering Manager
|
||||
</p>
|
||||
<div>
|
||||
<span class="company">Caylus Crew</span>
|
||||
</div>
|
||||
<span class="date">Jun 2021 - present</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">
|
||||
Discord Administrator and Lead Integrations Engineer
|
||||
</p>
|
||||
<div>
|
||||
<span class="company">Commit Your Code</span>
|
||||
</div>
|
||||
<span class="date">Dec 2020 - present</span>
|
||||
</div>
|
||||
</section>
|
||||
<!-- MARK: Volun. Prior
|
||||
-->
|
||||
<div class="card">
|
||||
<p class="title">Hacktoberfest Community Moderator</p>
|
||||
<div>
|
||||
<span class="company">DigitalOcean</span>
|
||||
</div>
|
||||
<span class="date">Apr 2021 - Oct 2024</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Discord Administrator and Integrations Engineer</p>
|
||||
<div>
|
||||
<span class="company">Azuliah</span>
|
||||
</div>
|
||||
<span class="date">Dec 2023 - Apr 2024</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Discord Moderator</p>
|
||||
<div>
|
||||
<span class="company">Rion Kuroko</span>
|
||||
</div>
|
||||
<span class="date">Nov 2023 - Jan 2024</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Senior Discord Moderator</p>
|
||||
<div>
|
||||
<span class="company">Rythm</span>
|
||||
</div>
|
||||
<span class="date">Feb 2022 - Jul 2022</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Technical Support Staff</p>
|
||||
<div>
|
||||
<span class="company">TweetShift</span>
|
||||
</div>
|
||||
<span class="date">Sep 2021 - Feb 2022</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Discord Moderator</p>
|
||||
<div>
|
||||
<span class="company">Rythm</span>
|
||||
</div>
|
||||
<span class="date">Sep 2021 - Feb 2022</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Community Moderator</p>
|
||||
<div>
|
||||
<span class="company">Battlesnake</span>
|
||||
</div>
|
||||
<span class="date">Jun 2021 - Nov 2022</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Integrations Engineer</p>
|
||||
<div>
|
||||
<span class="company">XCentric Collective</span>
|
||||
</div>
|
||||
<span class="date">Apr 2021 - Jul 2023</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Technical Support Staff</p>
|
||||
<div>
|
||||
<span class="company">Streamcord</span>
|
||||
</div>
|
||||
<span class="date">Mar 2021 - Aug 2021</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Discord Administrator</p>
|
||||
<div>
|
||||
<span class="company">EddieHub</span>
|
||||
</div>
|
||||
<span class="date">Jan 2021 - May 2023</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Community Moderator</p>
|
||||
<div>
|
||||
<span class="company">freeCodeCamp</span>
|
||||
</div>
|
||||
<span class="date">Jun 2020 - Dec 2020</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Shop Steward</p>
|
||||
<div>
|
||||
<span class="company">United Food and Commercial Workers</span>
|
||||
</div>
|
||||
<span class="date">Sep 2013 - Mar 2016</span>
|
||||
</div>
|
||||
<div class="card">
|
||||
<p class="title">Instructional Assistant</p>
|
||||
<div>
|
||||
<span class="company">Vancouver Public Schools</span>
|
||||
</div>
|
||||
<span class="date">Sep 2010 - Jun 2014</span>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
<script>
|
||||
const dates = document.querySelectorAll(".date");
|
||||
const today = new Date();
|
||||
for (const date of dates) {
|
||||
const start = new Date("5" + date.textContent.split(" - ")[0]);
|
||||
const end =
|
||||
date.textContent.split(" - ")[1] === "present"
|
||||
? "present"
|
||||
: new Date("5" + date.textContent.split(" - ")[1]);
|
||||
const diff =
|
||||
(end === "present" ? today.getTime() : end.getTime()) - start.getTime();
|
||||
const diffYears = Math.floor(diff / (1000 * 60 * 60 * 24 * 365.25));
|
||||
const diffMonths = Math.floor(
|
||||
(diff % (1000 * 60 * 60 * 24 * 365.25)) / (1000 * 60 * 60 * 24 * 30.44)
|
||||
);
|
||||
const diffString = [];
|
||||
if (diffYears > 0) {
|
||||
diffString.push(`${diffYears} year${diffYears === 1 ? "" : "s"}`);
|
||||
}
|
||||
if (diffMonths > 0) {
|
||||
diffString.push(`${diffMonths} month${diffMonths === 1 ? "" : "s"}`);
|
||||
}
|
||||
if (end === "present") {
|
||||
date.textContent = `${start.toLocaleDateString("en-GB", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
})} - present (${diffString.join(", ")})`;
|
||||
continue;
|
||||
}
|
||||
date.textContent = `${start.toLocaleDateString("en-GB", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
})} - ${end.toLocaleDateString("en-GB", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
})} (${diffString.join(", ")})`;
|
||||
}
|
||||
</script>
|
||||
</html>
|
@ -18,6 +18,7 @@
|
||||
<section>
|
||||
<p>A complete listing of all of our various pages.</p>
|
||||
<p>All subdomains listed here are under our top level <code>nhcarrigan.com</code> domain.</p>
|
||||
<p>Additionally, every one of these apps is available under our <code>naomi.lgbt</code> domain thanks to a wildcard DNS redirect.</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>APIs</h2>
|
||||
@ -58,28 +59,22 @@
|
||||
<ul>
|
||||
<li><a href="https://analytics.nhcarrigan.com" target="_blank"><code>analytics</code> - Analytics</a></li>
|
||||
<li><a href="https://blog.nhcarrigan.com" target="_blank"><code>blog</code> - Our Blog</a></li>
|
||||
<li><a href="https://chat.nhcarrigan.com" target="_blank"><code>chat</code> - Social Media Landing Page</a></li>
|
||||
<li><a href="https://books.nhcarrigan.com" target="_blank"><code>books</code> - Naomi's E-Book Library</a></li>
|
||||
<li><a href="https://cloud.nhcarrigan.com" target="_blank"><code>cloud</code> - NextCloud</a></li>
|
||||
<li><a href="https://deepgram.nhcarrigan.com" target="_blank"><code>deepgram</code> - Deepgram Weekly Standup</a></li>
|
||||
<li><a href="https://docs.nhcarrigan.com" target="_blank"><code>docs</code> - Documentation</a></li>
|
||||
<li><a href="https://fcc.nhcarrigan.com" target="_blank"><code>fcc</code> - freeCodeCamp Weekly Standup</a></li>
|
||||
<li><a href="https://fedi.nhcarrigan.com" target="_blank"><code>fedi</code> - Sharkey Instance</a></li>
|
||||
<li><a href="https://forms.nhcarrigan.com" target="_blank"><code>forms</code> - Web Form Collection</a></li>
|
||||
<li><a href="https://forum.nhcarrigan.com" target="_blank"><code>forum</code> - Discourse Forum</a></li>
|
||||
<li><a href="https://git.nhcarrigan.com" target="_blank"><code>git</code> - Gitea Instance</a></li>
|
||||
<li><a href="https://incidents.nhcarrigan.com" target="_blank"><code>incidents</code> - Uptime Kuma Instance</a></li>
|
||||
<li><a href="https://irc-admin.nhcarrigan.com" target="_blank"><code>irc-admin</code> - UninspIRCd Admin Panel</a></li>
|
||||
<li><a href="https://irc.nhcarrigan.com" target="_blank"><code>irc</code> - IRC Server and public web client</a></li>
|
||||
<li><a href="https://irc-private.nhcarrigan.com" target="_blank"><code>irc-private</code> - Private IRC client for Naomi</a></li>
|
||||
<li><a href="https://manual.nhcarrigan.com" target="_blank"><code>manual</code> - User Manual</a></li>
|
||||
<li><a href="https://matrix-admin.nhcarrigan.com" target="_blank"><code>matrix-admin</code> - Synapse Admin Panel</a></li>
|
||||
<li><a href="https://moderation.nhcarrigan.com" target="_blank"><code>moderation</code> - Mod Logs</a></li>
|
||||
<li><a href="https://music.nhcarrigan.com" target="_blank"><code>music</code> - Music Library</a></li>
|
||||
<li><a href="https://nails.nhcarrigan.com" target="_blank"><code>nails</code> - Nail Polish Tracker</a></li>
|
||||
<li><a href="https://notes.nhcarrigan.com" target="_blank"><code>notes</code> - Private Notes for Sponsors</a></li>
|
||||
<li><a href="https://quality.nhcarrigan.com" target="_blank"><code>quality</code> - SonarQube Instance</a></li>
|
||||
<li><a href="https://resume.nhcarrigan.com" target="_blank"><code>resume</code> - Web-based Resume</a></li>
|
||||
<li><a href="https://oogie.nhcarrigan.com" target="_blank"><code>analytics</code> - Analytics</a></li>
|
||||
<li><a href="https://security.nhcarrigan.com" target="_blank"><code>security</code> - Automated Code Scanning Results</a></li>
|
||||
<li><a href="https://sitemap.nhcarrigan.com" target="_blank"><code>sitemap</code> - This page!</a></li>
|
||||
<li><a href="https://testimonials.nhcarrigan.com" target="_blank"><code>testimonials</code> - Client Reviews</a></li>
|
||||
@ -91,9 +86,9 @@
|
||||
<h2>Redirects</h2>
|
||||
<ul>
|
||||
<li><a href="https://announcements.nhcarrigan.com" target="_blank"><code>announcements</code> - Redirects to our forum</a></li>
|
||||
<li><a href="https://chat.nhcarrigan.com" target="_blank"><code>chat</code> - Redirects to our forum</a></li>
|
||||
<li><a href="https://contact.nhcarrigan.com" target="_blank"><code>contact</code> - Redirects to our docs</a></li>
|
||||
<li><a href="https://donate.nhcarrigan.com" target="_blank"><code>donate</code> - Redirects to our docs</a></li>
|
||||
<li><a href="https://matrix.nhcarrigan.com" target="_blank"><code>matrix</code> - Redirects to matrix.to page</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
|
13
songs.sh
13
songs.sh
@ -10,15 +10,8 @@ current=0
|
||||
for file in $(find /home/naomi/music -type f -print0 | tr '\0' '\n'); do
|
||||
current=$((current + 1))
|
||||
echo -ne "Processing $current/$filecount\r"
|
||||
title=$(id3v2 -l "$file" | grep "TIT2" | cut -d ":" -f 2 | sed -e 's/^[[:space:]]*//')
|
||||
artist=$(id3v2 -l "$file" | grep "TPE1" | cut -d ":" -f 2 | sed -e 's/^[[:space:]]*//')
|
||||
|
||||
if [ -z "$title" ]; then
|
||||
title=$(id3v2 -l "$file" | grep "TT2" | cut -d ":" -f 2 | sed -e 's/^[[:space:]]*//')
|
||||
fi
|
||||
if [ -z "$artist" ]; then
|
||||
artist=$(id3v2 -l "$file" | grep "TP1" | cut -d ":" -f 2 | sed -e 's/^[[:space:]]*//')
|
||||
fi
|
||||
title=$(mid3v2 -l "$file" | grep "TIT2\|TT2" | cut -d "=" -f 2)
|
||||
artist=$(mid3v2 -l "$file" | grep "TPE1\|TP1" | cut -d "=" -f 2)
|
||||
if [ -z "$title" ]; then
|
||||
# remove .mp3 from the title
|
||||
title=$(basename "$file" | sed -e 's/\.mp3//g')
|
||||
@ -29,8 +22,6 @@ for file in $(find /home/naomi/music -type f -print0 | tr '\0' '\n'); do
|
||||
|
||||
# use jq to add the song to the list
|
||||
songs="$songs$(jq -n --arg title "$title" --arg artist "$artist" '{title: $title, artist: $artist}'),"
|
||||
|
||||
# songs="$songs{\"title\":\"$(echo "$title" | sed -e 's/[\\"\/]/\\&/g' | sed -e 's/[\x01-\x1f\x7f]//g')\",\"artist\":\"$(echo "$artist" | sed -e 's/[\\"\/]/\\&/g' | sed -e 's/[\x01-\x1f\x7f]//g')\"},"
|
||||
done
|
||||
|
||||
# Remove trailing comma and add square brackets to complete the list
|
||||
|
2
sync.sh
2
sync.sh
@ -1,6 +1,6 @@
|
||||
#! /usr/bin/bash
|
||||
|
||||
dirs=("bsky" "chat" "games" "link-redirector" "resume" "testimonials" "manual" "sitemap" "music");
|
||||
dirs=("bsky" "chat" "games" "link-redirector" "testimonials" "manual" "sitemap" "music" "books" "products");
|
||||
|
||||
for dir in "${dirs[@]}"; do
|
||||
rsync -av $dir prod:/home/nhcarrigan
|
||||
|
@ -54,7 +54,7 @@
|
||||
<main>
|
||||
<h1>Testimonials</h1>
|
||||
<p>See what our past clients have to say about our work!</p>
|
||||
<p>Want to submit your own? <a href="https://forms.nhcarrigan.com/testimonial">Use our web form</a>.</p>
|
||||
<p>Want to submit your own? <a href="https://forms.nhcarrigan.com/form/M_GrmqASymmO744axMOmu2LaMAaT5F0LmdVcU2c8-gQ">Use our web form</a>.</p>
|
||||
<section>
|
||||
<div class="card">
|
||||
<p class="title">Alexis Madsen</p>
|
||||
|
Reference in New Issue
Block a user