From 5d701069e9800c000643308f6f04149515788125 Mon Sep 17 00:00:00 2001
From: Bad Manners <me@badmanners.xyz>
Date: Thu, 21 Nov 2024 10:10:49 -0300
Subject: [PATCH] Add fediverse:creator tag to pages

---
 README.md                                | 11 -----
 package-lock.json                        | 60 ++--------------------
 package.json                             |  6 +--
 scripts/deploy-lftp.ts                   | 63 ------------------------
 src/data/licenses.toml                   |  9 ++--
 src/layouts/BlogPostLayout.astro         |  1 +
 src/layouts/GameLayout.astro             |  1 +
 src/layouts/PublishedContentLayout.astro |  7 +++
 src/layouts/StoryLayout.astro            |  1 +
 9 files changed, 22 insertions(+), 137 deletions(-)
 delete mode 100644 scripts/deploy-lftp.ts

diff --git a/README.md b/README.md
index 3d94ae1..96127e7 100644
--- a/README.md
+++ b/README.md
@@ -56,14 +56,3 @@ Then, if you're using rsync, after configuring the `gallerybm` host (or the name
 ```bash
 rsync --delete-after -acP dist/ gallerybm:/home/public
 ```
-
-Or if you prefer LFTP, create a `.env` file at the root of the project:
-
-```env
-DEPLOY_LFTP_HOST=https://example-webdav-server.com
-DEPLOY_LFTP_USER=example_user
-DEPLOY_LFTP_PASSWORD=sup3r_s3cr3t_password
-DEPLOY_LFTP_TARGETFOLDER=sites/gallery.badmanners.xyz/
-```
-
-Then run `npm run deploy-lftp`.
diff --git a/package-lock.json b/package-lock.json
index 06d416b..71d2bd7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "gallery.badmanners.xyz",
-  "version": "1.13.0",
+  "version": "1.13.1",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "gallery.badmanners.xyz",
-      "version": "1.13.0",
+      "version": "1.13.1",
       "hasInstallScript": true,
       "dependencies": {
         "@astrojs/alpinejs": "^0.4.0",
@@ -39,7 +39,6 @@
         "@types/node": "^22.7.4",
         "@types/sanitize-html": "^2.13.0",
         "commander": "^12.1.0",
-        "dotenv-cli": "^7.4.2",
         "fetch-retry": "^6.0.0",
         "prettier": "^3.3.3",
         "prettier-plugin-astro": "^0.14.1",
@@ -3107,9 +3106,9 @@
       }
     },
     "node_modules/cross-spawn": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
-      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
       "license": "MIT",
       "dependencies": {
         "path-key": "^3.1.0",
@@ -3297,45 +3296,6 @@
         "url": "https://github.com/fb55/domutils?sponsor=1"
       }
     },
-    "node_modules/dotenv": {
-      "version": "16.4.5",
-      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
-      "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://dotenvx.com"
-      }
-    },
-    "node_modules/dotenv-cli": {
-      "version": "7.4.2",
-      "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.4.2.tgz",
-      "integrity": "sha512-SbUj8l61zIbzyhIbg0FwPJq6+wjbzdn9oEtozQpZ6kW2ihCcapKVZj49oCT3oPM+mgQm+itgvUQcG5szxVrZTA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "cross-spawn": "^7.0.3",
-        "dotenv": "^16.3.0",
-        "dotenv-expand": "^10.0.0",
-        "minimist": "^1.2.6"
-      },
-      "bin": {
-        "dotenv": "cli.js"
-      }
-    },
-    "node_modules/dotenv-expand": {
-      "version": "10.0.0",
-      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz",
-      "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=12"
-      }
-    },
     "node_modules/dset": {
       "version": "3.1.4",
       "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz",
@@ -5843,16 +5803,6 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
-    "node_modules/minimist": {
-      "version": "1.2.8",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
-      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/minipass": {
       "version": "7.1.2",
       "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
diff --git a/package.json b/package.json
index 2ba5a52..738c499 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "gallery.badmanners.xyz",
   "type": "module",
-  "version": "1.13.0",
+  "version": "1.13.1",
   "scripts": {
     "postinstall": "astro sync",
     "dev": "astro dev",
@@ -12,8 +12,7 @@
     "check": "astro check",
     "astro": "astro",
     "prettier": "prettier --write .",
-    "export-story": "tsx scripts/export-story.ts",
-    "deploy-lftp": "dotenv tsx scripts/deploy-lftp.ts --"
+    "export-story": "tsx scripts/export-story.ts"
   },
   "dependencies": {
     "@astrojs/alpinejs": "^0.4.0",
@@ -46,7 +45,6 @@
     "@types/node": "^22.7.4",
     "@types/sanitize-html": "^2.13.0",
     "commander": "^12.1.0",
-    "dotenv-cli": "^7.4.2",
     "fetch-retry": "^6.0.0",
     "prettier": "^3.3.3",
     "prettier-plugin-astro": "^0.14.1",
diff --git a/scripts/deploy-lftp.ts b/scripts/deploy-lftp.ts
deleted file mode 100644
index c6c5914..0000000
--- a/scripts/deploy-lftp.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { spawn } from "node:child_process";
-import { join, normalize as n } from "node:path/posix";
-import { program, Option } from "commander";
-
-interface DeployLftpOptions {
-  host: string;
-  user: string;
-  password: string;
-  targetFolder: string;
-  sourceFolder: string;
-  assetsFolder: string;
-}
-
-async function deployLftp({ host, user, password, targetFolder, sourceFolder, assetsFolder }: DeployLftpOptions) {
-  const process = spawn(
-    "lftp",
-    [
-      "-c",
-      [
-        `open -u ${user},${password.replaceAll(/([ \t.,:;?!`'"^|*+#&$\(\)\[\]{}<>\\/-])/, "\\$1")} ${host}`,
-        `mirror --reverse --include-glob ${join(assetsFolder, "*")} --delete --only-missing --no-perms --verbose ${n(sourceFolder)} ${n(targetFolder)}`,
-        `mirror --reverse --exclude-glob ${join(assetsFolder, "*")} --delete                --no-perms --verbose ${n(sourceFolder)} ${n(targetFolder)}`,
-        `bye`,
-      ].join("\n"),
-    ],
-    {
-      stdio: "inherit",
-    },
-  );
-  await new Promise((resolve, reject) => {
-    process.on("close", (code) => (code === 0 ? resolve(0) : reject(`lftp failed with code ${code}`)));
-  });
-}
-
-await program
-  .name("deploy-lftp")
-  .description("Deploy files to remote server with LFTP")
-  .addOption(
-    new Option("-h, --host <hostname>", "Hostname of the LFTP (i.e. WebDav, SCP, SFTP...) remote.").env(
-      "DEPLOY_LFTP_HOST",
-    ),
-  )
-  .addOption(new Option("-u, --user <username>", "Username portion of the LFTP credentials").env("DEPLOY_LFTP_USER"))
-  .addOption(
-    new Option("-p, --password <pass>", "Password portion of the LFTP credentials").env("DEPLOY_LFTP_PASSWORD"),
-  )
-  .addOption(
-    new Option("-t, --target-folder <remoteDir>", "Folder to mirror files to in the LFTP remote").env(
-      "DEPLOY_LFTP_TARGETFOLDER",
-    ),
-  )
-  .addOption(
-    new Option("-s, --source-folder <localDir>", "Folder to read files from in the local machine")
-      .env("DEPLOY_LFTP_SOURCEFOLDER")
-      .default("dist/"),
-  )
-  .addOption(
-    new Option("-a, --assets-folder <localDir>", "Directory inside of --source-folder of assets with hash-based names")
-      .env("DEPLOY_LFTP_ASSETSFOLDER")
-      .default("assets/"),
-  )
-  .action(deployLftp)
-  .parseAsync();
diff --git a/src/data/licenses.toml b/src/data/licenses.toml
index 15b7b14..26bc3ef 100644
--- a/src/data/licenses.toml
+++ b/src/data/licenses.toml
@@ -1,11 +1,12 @@
-# licenses.toml
+file = "licenses.toml"
+url = "https://gallery.badmanners.xyz/licenses.toml"
 
 [copyright]
 title = "gallery.badmanners.xyz"
 description = "Bad Manners's self-hosted gallery."
 type = "website"
 date = "2024"
-author = { name = "Bad Manners", url = "https://badmanners.xyz", email = "me@badmanners.xyz>" }
+author = { name = "Bad Manners", url = "https://badmanners.xyz", email = "me@badmanners.xyz" }
 source = "https://git.badmanners.xyz/badmanners/gallery.badmanners.xyz"
 license = { name = "MIT", url = "https://opensource.org/license/mit" }
 notes = """All rights reserved. \
@@ -24,7 +25,7 @@ notes = """My characters, whether directly attributed to me or unattributed, are
 type = "content"
 description = "Content hosted on this website, i.e. the stories, games, and blog posts."
 date = "2022-2024"
-author = { name = "Bad Manners", url = "https://badmanners.xyz", email = "me@badmanners.xyz>" }
+author = { name = "Bad Manners", url = "https://badmanners.xyz", email = "me@badmanners.xyz" }
 source = "https://git.badmanners.xyz/badmanners/gallery.badmanners.xyz/src/branch/main/src/content"
 license = { name = "CC-BY-NC-ND-4.0", url = "https://creativecommons.org/licenses/by-nc-nd/4.0/" }
 notes = "All rights reserved."
@@ -60,7 +61,7 @@ notes = """All third-party copyrights and trademarks belong to their respective
 [[attributions]]
 description = "Edited icons for other websites."
 type = "icons"
-author = { name = "Bad Manners", url = "https://badmanners.xyz", email = "me@badmanners.xyz>" }
+author = { name = "Bad Manners", url = "https://badmanners.xyz", email = "me@badmanners.xyz" }
 source = "https://git.badmanners.xyz/badmanners/badmanners.xyz/src/branch/main/src/components/icons/brands"
 license = { name = "CC0 1.0 Universal", url = "https://creativecommons.org/publicdomain/zero/1.0/" }
 items = [
diff --git a/src/layouts/BlogPostLayout.astro b/src/layouts/BlogPostLayout.astro
index fad4c47..faaceff 100644
--- a/src/layouts/BlogPostLayout.astro
+++ b/src/layouts/BlogPostLayout.astro
@@ -47,6 +47,7 @@ const relatedBlogPosts = (await getEntries(props.relatedBlogPosts)).filter((post
   relatedGames={relatedGames}
   relatedBlogPosts={relatedBlogPosts}
   posts={props.posts}
+  authors={authorsList}
   labelReturnTo={{ title: t(props.lang, "blog/return_to_blog_posts"), link: "/blog" }}
   labelPreviousContent={t(props.lang, "blog/previous_post_aria_label")}
   labelNextContent={t(props.lang, "blog/next_post_aria_label")}
diff --git a/src/layouts/GameLayout.astro b/src/layouts/GameLayout.astro
index d26e6af..5a029aa 100644
--- a/src/layouts/GameLayout.astro
+++ b/src/layouts/GameLayout.astro
@@ -44,6 +44,7 @@ const metaDescription = t(props.lang, "game/warnings", props.platforms, props.co
   relatedGames={relatedGames}
   relatedBlogPosts={relatedBlogPosts}
   posts={props.posts}
+  authors={authorsList}
   labelReturnTo={{ title: t(props.lang, "game/return_to_games"), link: "/games" }}
   labelPreviousContent={t(props.lang, "game/previous_game_aria_label")}
   labelNextContent={t(props.lang, "game/next_game_aria_label")}
diff --git a/src/layouts/PublishedContentLayout.astro b/src/layouts/PublishedContentLayout.astro
index 79e500e..c720a19 100644
--- a/src/layouts/PublishedContentLayout.astro
+++ b/src/layouts/PublishedContentLayout.astro
@@ -49,6 +49,7 @@ type Props = {
   relatedGames?: CollectionEntry<"games">[];
   relatedBlogPosts?: CollectionEntry<"blog">[];
   posts: Posts;
+  authors: CollectionEntry<"users">[];
 
   /* Layout attributes */
   publishedContentType: "story" | "game" | "blog post";
@@ -126,6 +127,12 @@ const returnTo = series
         </Fragment>
       ) : null
     }
+    {
+      props.authors
+        .map((user) => user.data.links.mastodon?.username)
+        .filter((value) => value)
+        .map((username) => <meta name="fediverse:creator" content={`@${username}`} />)
+    }
     <slot name="head" />
   </Fragment>
   <div
diff --git a/src/layouts/StoryLayout.astro b/src/layouts/StoryLayout.astro
index ddcca0b..cede7c6 100644
--- a/src/layouts/StoryLayout.astro
+++ b/src/layouts/StoryLayout.astro
@@ -55,6 +55,7 @@ const metaDescription = t(props.lang, "story/warnings", wordCount, props.content
   relatedGames={relatedGames}
   relatedBlogPosts={relatedBlogPosts}
   posts={props.posts}
+  authors={authorsList}
   labelReturnTo={{ title: t(props.lang, "story/return_to_stories"), link: "/stories" }}
   labelPreviousContent={t(props.lang, "story/previous_story_aria_label")}
   labelNextContent={t(props.lang, "story/next_story_aria_label")}