Local metadata link commit at 2025-06-06 15:40:57 — file:///home/mrhavens/git-local-repos/git-sigil.git
This commit is contained in:
commit
b548318725
134 changed files with 15789 additions and 0 deletions
25
src/utils/index.ts
Normal file
25
src/utils/index.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import path from "path";
|
||||
const MONTHS = [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
];
|
||||
|
||||
export const toTitleCase = (str: string) =>
|
||||
str.replace(/\w\S*/g, function (txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
|
||||
export const getMonthName = (date: Date) => MONTHS[new Date(date).getMonth()];
|
||||
|
||||
export const getSlugFromPathname = (pathname: string) =>
|
||||
path.basename(pathname, path.extname(pathname));
|
29
src/utils/mdoc/mdoc.config.ts
Normal file
29
src/utils/mdoc/mdoc.config.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { callout } from "./schema/callout.mdoc";
|
||||
import { link } from "./schema/link.mdoc";
|
||||
import { tweetEmbed } from "./schema/tweet-embed.mdoc";
|
||||
import { tabs } from "./schema/tabs.mdoc";
|
||||
import { ytEmbed } from "./schema/yt-embed.mdoc";
|
||||
|
||||
/** @type {import('@markdoc/markdoc').Config} */
|
||||
export const config = {
|
||||
tags: {
|
||||
callout,
|
||||
link,
|
||||
tweet: tweetEmbed,
|
||||
yt: ytEmbed,
|
||||
tabs,
|
||||
},
|
||||
functions: {
|
||||
getCountryEmoji: {
|
||||
transform(parameters) {
|
||||
const [country] = Object.values(parameters);
|
||||
const countryToEmojiMap = {
|
||||
japan: "🇯🇵",
|
||||
spain: "🇪🇸",
|
||||
france: "🇫🇷",
|
||||
};
|
||||
return countryToEmojiMap[country as string] ?? "🏳";
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
18
src/utils/mdoc/schema/callout.mdoc.ts
Normal file
18
src/utils/mdoc/schema/callout.mdoc.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { component } from "@astrojs/markdoc/config";
|
||||
|
||||
/** @type {import('@markdoc/markdoc').Schema} */
|
||||
export const callout = {
|
||||
render: component("./src/components/mdoc/Callout.astro"),
|
||||
children: ["paragraph", "tag", "list"],
|
||||
attributes: {
|
||||
type: {
|
||||
type: String,
|
||||
default: "note goes here...",
|
||||
matches: ["error", "check", "note", "warning"],
|
||||
errorLevel: "critical",
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
};
|
34
src/utils/mdoc/schema/link.mdoc.ts
Normal file
34
src/utils/mdoc/schema/link.mdoc.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { component } from "@astrojs/markdoc/config";
|
||||
|
||||
const SITE_DOMAIN = "astro-ink.vercel.app";
|
||||
function getHrefTarget(attributes) {
|
||||
const href = attributes.href;
|
||||
if (
|
||||
href.includes(SITE_DOMAIN) ||
|
||||
href.startsWith("/") ||
|
||||
href.startsWith("#") ||
|
||||
href.startsWith("?")
|
||||
) {
|
||||
return "_self";
|
||||
} else {
|
||||
return "_blank";
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {import('@markdoc/markdoc').Schema} */
|
||||
export const link = {
|
||||
render: component("./src/components/mdoc/Link.astro"),
|
||||
children: ["strong", "em", "s", "code", "text", "tag"],
|
||||
attributes: {
|
||||
href: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
target: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
};
|
15
src/utils/mdoc/schema/tabs.mdoc.ts
Normal file
15
src/utils/mdoc/schema/tabs.mdoc.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { component } from "@astrojs/markdoc/config";
|
||||
|
||||
/** @type {import('@markdoc/markdoc').Schema} */
|
||||
export const tabs = {
|
||||
render: component("./src/components/mdoc/Tabs/Tabs.astro"),
|
||||
children: ["paragraph", "tag", "list"],
|
||||
attributes: {
|
||||
tabs: {
|
||||
type: Array,
|
||||
},
|
||||
heading: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
};
|
12
src/utils/mdoc/schema/tweet-embed.mdoc.ts
Normal file
12
src/utils/mdoc/schema/tweet-embed.mdoc.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { component } from "@astrojs/markdoc/config";
|
||||
|
||||
/** @type {import('@markdoc/markdoc').Schema} */
|
||||
export const tweetEmbed = {
|
||||
render: component("./src/components/mdoc/TweetEmbed.astro"),
|
||||
attributes: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
16
src/utils/mdoc/schema/yt-embed.mdoc.ts
Normal file
16
src/utils/mdoc/schema/yt-embed.mdoc.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { component } from "@astrojs/markdoc/config";
|
||||
|
||||
/** @type {import('@markdoc/markdoc').Schema} */
|
||||
export const ytEmbed = {
|
||||
render: component("./src/components/mdoc/YTVideoEmbed.astro"),
|
||||
attributes: {
|
||||
url: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
};
|
34
src/utils/media-yt-feed.ts
Normal file
34
src/utils/media-yt-feed.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import type { MediaExternallyHostedVideo } from "./media";
|
||||
|
||||
export type Feed2JsonYtFeedItem = {
|
||||
guid: `yt:video:${string}`;
|
||||
url: string;
|
||||
title: string;
|
||||
date_published: string;
|
||||
author: {
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
|
||||
export function toFeedToJsonUrl(ytVideoChannelId: string) {
|
||||
return `https://feed2json.org/convert?url=https://www.youtube.com/feeds/videos.xml?channel_id=${ytVideoChannelId}`;
|
||||
}
|
||||
|
||||
export function toMediaFormatFromFeed2JsonUrl(posts: {
|
||||
items: Array<Feed2JsonYtFeedItem>;
|
||||
}): Array<MediaExternallyHostedVideo> {
|
||||
return posts?.items?.length
|
||||
? posts.items.map((post) => ({
|
||||
title: post.title,
|
||||
description: "",
|
||||
url: post.url,
|
||||
participants: [],
|
||||
date: post.date_published,
|
||||
host: post.author.name,
|
||||
thumbnail: `https://img.youtube.com/vi/${post.guid.substring(
|
||||
post.guid.lastIndexOf(":") + 1,
|
||||
post.guid.length,
|
||||
)}/0.jpg`,
|
||||
}))
|
||||
: [];
|
||||
}
|
15
src/utils/media.ts
Normal file
15
src/utils/media.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export type GithubContentURL =
|
||||
`https://api.github.com/repos/${string}/contents/${string}`;
|
||||
|
||||
export type MediaExternallyHostedVideo = {
|
||||
title: string;
|
||||
description: string;
|
||||
url: string;
|
||||
host: string;
|
||||
participants: Array<string>;
|
||||
date: string;
|
||||
thumbnail?: string;
|
||||
};
|
||||
|
||||
export const DEFAULT_MEDIA_URL: GithubContentURL =
|
||||
"https://api.github.com/repos/one-aalam/astro-ink/contents/src/data/astro-media.json";
|
16
src/utils/views/in-memory.ts
Normal file
16
src/utils/views/in-memory.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
const client = new Map<string, number>();
|
||||
|
||||
export const getViewsBySlug = async (slug: string) => {
|
||||
if (slug) {
|
||||
const prevValue = client.get(slug);
|
||||
let newValue = 1;
|
||||
if (prevValue) {
|
||||
newValue = parseInt(`${prevValue}`) + 1;
|
||||
client.set(slug, newValue);
|
||||
} else {
|
||||
client.set(slug, 1);
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
return 0;
|
||||
};
|
17
src/utils/views/ioredis.ts
Normal file
17
src/utils/views/ioredis.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import Redis from 'ioredis'
|
||||
const client = new Redis(import.meta.env.REDIS_URI)
|
||||
|
||||
export const getViewsBySlug = async (slug: string) => {
|
||||
if (slug) {
|
||||
const prevValue = await client.get(slug);
|
||||
let newValue = 1;
|
||||
if (prevValue) {
|
||||
newValue = parseInt(`${prevValue}`) + 1;
|
||||
await client.set(slug, newValue);
|
||||
} else {
|
||||
await client.set(slug, 1);
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
return 0;
|
||||
};
|
32
src/utils/views/turso.ts
Normal file
32
src/utils/views/turso.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { createClient } from "@libsql/client";
|
||||
|
||||
export const client = createClient({
|
||||
url: import.meta.env.TURSO_DB_URL,
|
||||
authToken: import.meta.env.TURSO_DB_AUTH_TOKEN
|
||||
});
|
||||
|
||||
export const getViewsBySlug = async (slug: string) => {
|
||||
if(!slug) return 0;
|
||||
try {
|
||||
const initialViewCount = 0
|
||||
const transaction = await client.transaction("write");
|
||||
const rsSelected = await transaction.execute({
|
||||
sql: 'SELECT * FROM post_stats WHERE slug = :slug',
|
||||
args: { slug }
|
||||
});
|
||||
const prevViewCount = rsSelected?.rows?.length ? rsSelected.rows[0].views as number : initialViewCount;
|
||||
const rsUpdated = await transaction.execute({
|
||||
sql: 'INSERT INTO post_stats (uid, slug, views) VALUES (:uid, :slug, :views) ON CONFLICT(slug) DO UPDATE SET views = :views RETURNING views',
|
||||
args: {
|
||||
uid: crypto.randomUUID(),
|
||||
slug,
|
||||
views: prevViewCount + 1
|
||||
}
|
||||
});
|
||||
await transaction.commit()
|
||||
return rsUpdated.rows[0].views as number
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return 0;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue