diff --git a/scripts/deploy-lftp.ts b/scripts/deploy-lftp.ts index fb41ffa..3e0a8fc 100644 --- a/scripts/deploy-lftp.ts +++ b/scripts/deploy-lftp.ts @@ -1,5 +1,5 @@ import { spawn } from "node:child_process"; -import { join } from "node:path"; +import { join, normalize as n } from "node:path"; import { program, Option } from "commander"; interface DeployLftpOptions { @@ -17,9 +17,9 @@ async function deployLftp({ host, user, password, targetFolder, sourceFolder, as [ "-c", [ - `open -u ${user},${password} ${host}`, - `mirror --reverse --include-glob ${join(assetsFolder, "*")} --delete --only-missing --no-perms --verbose ${sourceFolder} ${targetFolder}`, - `mirror --reverse --exclude-glob ${join(assetsFolder, "*")} --delete --no-perms --verbose ${sourceFolder} ${targetFolder}`, + `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"), ], @@ -27,14 +27,10 @@ async function deployLftp({ host, user, password, targetFolder, sourceFolder, as stdio: "inherit", }, ); - await new Promise((resolve, reject) => { - process.on("close", (code) => { - if (code === 0) { - resolve(); - } else { - reject(`deploy-lftp failed with code ${code}`); - } - }); + await new Promise((resolve, reject) => { + process.on("close", (code) => + (code === 0) ? resolve(0) : reject(`lftp failed with code ${code}`), + ); }); } diff --git a/src/components/AgeRestrictedModal.astro b/src/components/AgeRestrictedModal.astro index 601b3bd..8267df0 100644 --- a/src/components/AgeRestrictedModal.astro +++ b/src/components/AgeRestrictedModal.astro @@ -86,6 +86,11 @@ import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro"; localStorage.ageVerified = ageVerified; document.body.style.overflow = "auto"; document.querySelectorAll("body > :not(#modal-age-restricted)").forEach((el) => el.removeAttribute("inert")); + document.body.querySelectorAll("a[href][data-age-restricted]").forEach((el) => { + let newHref = new URL(el.href); + newHref.searchParams.set("ageVerified", "true"); + el.href = newHref.href; + }); modal.style.display = "none"; }, { once: true }, diff --git a/src/components/AgeRestrictedScriptInline.astro b/src/components/AgeRestrictedScriptInline.astro index 504322c..ac1696a 100644 --- a/src/components/AgeRestrictedScriptInline.astro +++ b/src/components/AgeRestrictedScriptInline.astro @@ -1,4 +1,4 @@ --- --- - + diff --git a/src/components/icons/brands/IconSSH.astro b/src/components/icons/IconSSH.astro similarity index 98% rename from src/components/icons/brands/IconSSH.astro rename to src/components/icons/IconSSH.astro index b81383f..7a22f8b 100644 --- a/src/components/icons/brands/IconSSH.astro +++ b/src/components/icons/IconSSH.astro @@ -1,5 +1,5 @@ --- -import SVGIcon from "../SVGIcon.astro"; +import SVGIcon from "./SVGIcon.astro"; type Props = { width: string; diff --git a/src/components/icons/IconSSHPixel.astro b/src/components/icons/IconSSHPixel.astro new file mode 100644 index 0000000..deb5cd7 --- /dev/null +++ b/src/components/icons/IconSSHPixel.astro @@ -0,0 +1,15 @@ +--- +import SVGIcon from "./SVGIcon.astro"; + +type Props = { + width: string; + height: string; + class?: string; +}; +--- + + + + diff --git a/src/data/tos.ts b/src/data/tos.ts index 85453a9..d54c7b0 100644 --- a/src/data/tos.ts +++ b/src/data/tos.ts @@ -1,4 +1,21 @@ -import { parse as dateParse } from "date-fns"; +import { parse } from "date-fns"; -export const TOS_COMMISSION_STATUS: "CLOSED" | "OPEN" | "SEMI_OPEN" | "PRIVATE" = "OPEN"; -export const TOS_UPDATED_AT: Date = dateParse("2024-07-11", "yyyy-MM-dd", 0); +type CommissionStatus = "CLOSED" | "OPEN" | "SEMI_OPEN" | "PRIVATE"; + +interface TosFeedItem { + status: CommissionStatus; + updatedAt: Date; +} + +const parseISODate = (date: string) => parse(date, "yyyy-MM-dd", 0); + +export const TOS_FEED: TosFeedItem[] = ( + [ + { status: "CLOSED", updatedAt: parseISODate("2024-08-25") }, + { status: "OPEN", updatedAt: parseISODate("2024-07-11") }, + ] satisfies TosFeedItem[] +) + .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()) + .slice(0, 5); + +export const { status: TOS_COMMISSION_STATUS, updatedAt: TOS_UPDATED_AT } = TOS_FEED[0]; diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index fa69054..a472e55 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -75,6 +75,7 @@ const title = pageTitle ? `${pageTitle} | Bad Manners` : "Bad Manners"; Licenses diff --git a/src/pages/[...config].ts b/src/pages/[...config].ts index 9322473..0b93b13 100644 --- a/src/pages/[...config].ts +++ b/src/pages/[...config].ts @@ -5,37 +5,36 @@ const htaccess = String.raw` ErrorDocument 404 /404.html RedirectMatch 301 ^/tos(/(index.html)?)?$ /terms_of_service/ RedirectMatch 301 ^/contact(/(index.html)?)?$ / -RedirectMatch 301 ^/@/(aryion|ekas?)\b https://aryion.com/g4/user/BadManners -RedirectMatch 301 ^/@/(bluesky|bsky)\b https://bsky.app/profile/badmanners.xyz -RedirectMatch 301 ^/@/(buymeacoffee|bmac)\b https://www.buymeacoffee.com/BadMannersXYZ +RedirectMatch 301 ^/@/(aryion|ekas?(portal)?)\b https://aryion.com/g4/user/BadManners +RedirectMatch 301 ^/@/(blue[\ _-]?sky|bsky)\b https://bsky.app/profile/badmanners.xyz +RedirectMatch 301 ^/@/(buymeacoffee|buy\ me\ a\ coffee|buy_me_a_coffee|buy-me-a-coffee|bmac)\b https://www.buymeacoffee.com/BadMannersXYZ RedirectMatch 301 ^/@/carrd\b https://badmanners.carrd.co -RedirectMatch 301 ^/@/codeberg\b https://codeberg.org/BadManners -RedirectMatch 301 ^/@/cohost\b https://cohost.org/BadManners -RedirectMatch 301 ^/@/(fur[_-]?affinity|fa)\b https://www.furaffinity.net/user/badmanners -RedirectMatch 301 ^/@/gallery\b https://gallery.badmanners.xyz -RedirectMatch 301 ^/@/(github|gh)\b https://github.com/BadMannersXYZ -RedirectMatch 301 ^/@/gitlab\b https://gitlab.com/Bad_Manners -RedirectMatch 301 ^/@/gumroad\b https://badmanners.gumroad.com -RedirectMatch 301 ^/@/(inkbunny|ib)\b https://inkbunny.net/BadManners +RedirectMatch 301 ^/@/code[\ _-]?berg\b https://codeberg.org/BadManners +RedirectMatch 301 ^/@/co[\ _-]?host\b https://cohost.org/BadManners +RedirectMatch 301 ^/@/(fur[\ _-]?affinity|fa)\b https://www.furaffinity.net/user/badmanners +RedirectMatch 301 ^/@/(gallery|stor(y|ies)|games?)\b https://gallery.badmanners.xyz +RedirectMatch 301 ^/@/(git[\ _-]?hub|gh)\b https://github.com/BadMannersXYZ +RedirectMatch 301 ^/@/git[\ _-]?lab\b https://gitlab.com/Bad_Manners +RedirectMatch 301 ^/@/gum[\ _-]?road\b https://badmanners.gumroad.com +RedirectMatch 301 ^/@/(ink[\ _-]?bunny|ib)\b https://inkbunny.net/BadManners RedirectMatch 301 ^/@/itaku\b https://itaku.ee/profile/badmanners RedirectMatch 301 ^/@/itch\b https://bad-manners.itch.io RedirectMatch 301 ^/@/keybase\b https://keybase.io/badmanners -RedirectMatch 301 ^/@/ko[_-]?fi\b https://ko-fi.com/badmanners -RedirectMatch 301 ^/@/(mastodon|meow\.social|gulp\.cafe)\b https://meow.social/@BadManners -RedirectMatch 301 ^/@/neocities\b https://badmanners.neocities.org +RedirectMatch 301 ^/@/ko[\ ._-]?fi\b https://ko-fi.com/badmanners +RedirectMatch 301 ^/@/(mastodon|meow[\ ._-]?social|gulp[\ ._-]?cafe)\b https://meow.social/@BadManners +RedirectMatch 301 ^/@/neo[\ _-]?cities\b https://badmanners.neocities.org RedirectMatch 301 ^/@/picarto\b https://www.picarto.tv/BadManners -RedirectMatch 301 ^/@/pillowfort\b https://www.pillowfort.social/BadManners -RedirectMatch 301 ^/@/reddit\b https://www.reddit.com/user/BadManners_ +RedirectMatch 301 ^/@/pillow[\ _-]?fort\b https://www.pillowfort.social/BadManners +RedirectMatch 301 ^/@/redd\.?it\b https://www.reddit.com/user/BadManners_ RedirectMatch 301 ^/@/signal\b https://signal.me/#eu/ytt_rk0fFmAB2JAW-x2PbUiJyc_H3kYmfL_Pq4QNh5QIDsiFtjdFHaqFRs1D36tB -RedirectMatch 301 ^/@/(sofurry|sf)\b https://bad-manners.sofurry.com -RedirectMatch 301 ^/@/ssh\b https://badmanners.xyz/ssh.pub -RedirectMatch 301 ^/@/steam\b https://steamcommunity.com/id/badmanners_ -RedirectMatch 301 ^/@/subscribestar\b https://subscribestar.adult/bad-manners +RedirectMatch 301 ^/@/(so[\ _-]?furry|sf)\b https://bad-manners.sofurry.com +RedirectMatch 301 ^/@/steam([\ _-]?(community|powered))?\b https://steamcommunity.com/id/badmanners_ +RedirectMatch 301 ^/@/subscribe[\ _-]?star\b https://subscribestar.adult/bad-manners RedirectMatch 301 ^/@/(telegram|t\.me)\b https://t.me/bad_manners RedirectMatch 301 ^/@/twitch\b https://www.twitch.tv/bad__manners RedirectMatch 301 ^/@/weasyl\b https://www.weasyl.com/~badmanners RedirectMatch 301 ^/@/(x|twitter)\b https://x.com/BadManners__ -RedirectMatch 301 ^/@/(youtu\.?be|yt)\b https://www.youtube.com/@BadMannersXYZ +RedirectMatch 301 ^/@/(you[\ _-]?tube|youtu\.be|yt)\b https://www.youtube.com/@BadMannersXYZ `.trim(); export const getStaticPaths: GetStaticPaths = async () => { diff --git a/src/pages/about.astro b/src/pages/about.astro index 21d3f15..0a81897 100644 --- a/src/pages/about.astro +++ b/src/pages/about.astro @@ -46,6 +46,7 @@ import ImageSamStickerJuicebox from "../assets/images/sam_sticker_juicebox.webp" OliveCow diff --git a/src/pages/feed.xml.ts b/src/pages/feed.xml.ts index cf6e56f..082afc2 100644 --- a/src/pages/feed.xml.ts +++ b/src/pages/feed.xml.ts @@ -1,27 +1,22 @@ import rss from "@astrojs/rss"; import type { APIRoute } from "astro"; import { set as dateSet, subMinutes } from "date-fns"; -import { TOS_COMMISSION_STATUS, TOS_UPDATED_AT } from "../data/tos"; +import { TOS_FEED } from "../data/tos"; -export const GET: APIRoute = async ({ site }) => { +export const GET: APIRoute = ({ site }) => { return rss({ title: "Bad Manners", description: "Bad Manners status updates", site: site!, - items: [ - { - title: { - CLOSED: "Story commissions are closed.", - OPEN: "Story commissions are open!", - SEMI_OPEN: "Story commissions are semi-open, and I may accept them less frequently.", - PRIVATE: "Story commissions are private; they are only open to select commissioners.", - }[TOS_COMMISSION_STATUS], - link: "https://badmanners.xyz/terms_of_service", - pubDate: subMinutes( - dateSet(TOS_UPDATED_AT, { hours: 12, minutes: 0, seconds: 0 }), - TOS_UPDATED_AT.getTimezoneOffset(), - ), - }, - ], + items: TOS_FEED.map(({ status, updatedAt }) => ({ + title: { + CLOSED: "Story commissions are closed.", + OPEN: "Story commissions are open!", + SEMI_OPEN: "Story commissions are semi-open, and I may accept them less frequently.", + PRIVATE: "Story commissions are private; they are only open to select commissioners.", + }[status], + link: "https://badmanners.xyz/terms_of_service", + pubDate: subMinutes(dateSet(updatedAt, { hours: 12, minutes: 0, seconds: 0 }), updatedAt.getTimezoneOffset()), + })), }); }; diff --git a/src/pages/index.astro b/src/pages/index.astro index f21ef84..84bad0e 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,6 +1,7 @@ --- import BaseLayout from "../layouts/BaseLayout.astro"; import IconEnvelope from "../components/icons/IconEnvelope.astro"; +import IconBriefcase from "../components/icons/IconBriefcase.astro"; import IconBluesky from "../components/icons/brands/IconBluesky.astro"; import IconCodeberg from "../components/icons/brands/IconCodeberg.astro"; import IconCohost from "../components/icons/brands/IconCohost.astro"; @@ -21,6 +22,7 @@ import IconPicarto from "../components/icons/brands/IconPicarto.astro"; import IconReddit from "../components/icons/brands/IconReddit.astro"; import IconSignal from "../components/icons/brands/IconSignal.astro"; import IconSoFurry from "../components/icons/brands/IconSoFurry.astro"; +import IconSSH from "../components/icons/IconSSH.astro"; import IconSteam from "../components/icons/brands/IconSteam.astro"; import IconSubscribeStar from "../components/icons/brands/IconSubscribeStar.astro"; import IconTelegram from "../components/icons/brands/IconTelegram.astro"; @@ -28,7 +30,6 @@ import IconTwitch from "../components/icons/brands/IconTwitch.astro"; import IconWeasyl from "../components/icons/brands/IconWeasyl.astro"; import IconX from "../components/icons/brands/IconX.astro"; import IconYouTube from "../components/icons/brands/IconYouTube.astro"; -import IconSSH from "../components/icons/brands/IconSSH.astro"; --- @@ -39,7 +40,7 @@ import IconSSH from "../components/icons/brands/IconSSH.astro"; />

- Hi, I'm Bad Manners! + Hi, I'm Bad Manners!

-

I'm a safe vore enthusiast, and a furry who occasionally writes stuff.

+

I'm a safe vore enthusiast, a furry programmer, and occasionally a writer.

{ Astro.site ? (
@@ -78,6 +79,21 @@ import IconSSH from "../components/icons/brands/IconSSH.astro";

E-mail address

+
  • -

    SSH public key

  • diff --git a/src/pages/sam_brendan.astro b/src/pages/sam_brendan.astro index c3db21d..08e5736 100644 --- a/src/pages/sam_brendan.astro +++ b/src/pages/sam_brendan.astro @@ -25,6 +25,7 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp"; Rimmi @@ -77,6 +79,7 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp"; OliveCow diff --git a/src/pages/work.astro b/src/pages/work.astro index c2b6433..5d6cc0e 100644 --- a/src/pages/work.astro +++ b/src/pages/work.astro @@ -22,10 +22,13 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar"; I've been a lurker in the furry vore community for a long time before I decided to start writing safe vore storiessafe vore stories. Since there's a lot in furry endosoma that I enjoy, my stories tend to - have all sorts of themes and scenarioshave all sorts of themes and scenarios, including exotic ones. My content is all tagged with the appropriate content warnings, so if you enjoy safe vore, chances are that you'll find something in my gallery that is right up your alley.

    @@ -33,7 +36,8 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar"; I've also made a game called "Crossing Over""Crossing Over", as part of Strawberry Jam 8 (a game jam hosted by eevee on Feb. 2024). It's been my biggest project so far, and I'm very proud of it! Check it out if you have an hour to spare and the subject matters interest you.

    @@ -46,6 +50,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar"; gallery.badmanners.xyz @@ -210,6 +215,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar"; Wrangler