generated from nhcarrigan/template
This commit is contained in:
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>
|
Reference in New Issue
Block a user