Compare commits
3 commits
c90a4c141e
...
59350d61f3
| Author | SHA1 | Date | |
|---|---|---|---|
| 59350d61f3 | |||
| eb97ade514 | |||
| 5b5e21e614 |
10 changed files with 1059 additions and 895 deletions
48
.forgejo/workflows/devel.yml
Normal file
48
.forgejo/workflows/devel.yml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
name: Build Docker Image
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [devel]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: node:21-alpine
|
||||||
|
steps:
|
||||||
|
- name: Setup Node
|
||||||
|
run: |
|
||||||
|
apk add --no-cache libc6-compat
|
||||||
|
apk update
|
||||||
|
yarn set version canary
|
||||||
|
yarn config set nodeLinker node-modules
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Run Node CI Stuff
|
||||||
|
run: |
|
||||||
|
yarn install --immutable
|
||||||
|
yarn lint
|
||||||
|
- name: Docker Metadata
|
||||||
|
id: metadata
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: git.thornbush.dev/rose/fixbluesky
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=sha,prefix=
|
||||||
|
type=edge,branch=devel
|
||||||
|
- name: Docker Buildx
|
||||||
|
uses: https://code.forgejo.org/docker/setup-buildx-action@v3
|
||||||
|
- name: Login to Docker Registry
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: https://code.forgejo.org/docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: git.thornbush.dev
|
||||||
|
username: ${{github.actor}}
|
||||||
|
password: ${{secrets.OAUTH_TOKEN}}
|
||||||
|
- name: Build Image
|
||||||
|
uses: https://code.forgejo.org/docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
|
@ -4,5 +4,9 @@
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll": "explicit",
|
"source.fixAll": "explicit",
|
||||||
"source.organizeImports": "explicit"
|
"source.organizeImports": "explicit"
|
||||||
|
},
|
||||||
|
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "biomejs.biome"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
893
.yarn/releases/yarn-4.1.1.cjs
vendored
893
.yarn/releases/yarn-4.1.1.cjs
vendored
File diff suppressed because one or more lines are too long
925
.yarn/releases/yarn-4.5.0.cjs
vendored
Normal file
925
.yarn/releases/yarn-4.5.0.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
||||||
yarnPath: .yarn/releases/yarn-4.1.1.cjs
|
yarnPath: .yarn/releases/yarn-4.5.0.cjs
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "fixbluesky",
|
"name": "fixbluesky",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "yarn@4.1.1",
|
"packageManager": "yarn@4.5.0",
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "21.7.3",
|
"node": "21.7.3",
|
||||||
"yarn": "4.1.1"
|
"yarn": "4.1.1"
|
||||||
|
|
|
||||||
34
src/components/Profile.tsx
Normal file
34
src/components/Profile.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import type { AppBskyActorDefs } from "@atproto/api";
|
||||||
|
|
||||||
|
import { OEmbedTypes } from "../routes/getOEmbed.ts";
|
||||||
|
import { Layout } from "./Layout.tsx";
|
||||||
|
|
||||||
|
interface ProfileProps {
|
||||||
|
profile: AppBskyActorDefs.ProfileViewDetailed;
|
||||||
|
url: string;
|
||||||
|
appDomain: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Profile = ({ profile, url, appDomain }: ProfileProps) => (
|
||||||
|
<Layout url={url}>
|
||||||
|
<meta name="twitter:creator" content={`@${profile.handle}`} />
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content={profile.description ?? ""}
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:title"
|
||||||
|
content={`${profile.displayName} (@${profile.handle})`}
|
||||||
|
/>
|
||||||
|
<meta property="og:image" content={profile.avatar} />
|
||||||
|
|
||||||
|
<link
|
||||||
|
type="application/json+oembed"
|
||||||
|
href={`https://${appDomain}/oembed?type=${
|
||||||
|
OEmbedTypes.Profile
|
||||||
|
}&follows=${profile.followsCount}&posts=${
|
||||||
|
profile.postsCount
|
||||||
|
}&avatar=${encodeURIComponent(profile.avatar ?? "")}`}
|
||||||
|
/>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
14
src/lib/fetchProfile.ts
Normal file
14
src/lib/fetchProfile.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import type { BskyAgent } from "@atproto/api";
|
||||||
|
|
||||||
|
export interface fetchProfileOptions {
|
||||||
|
user: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchProfile(
|
||||||
|
agent: BskyAgent,
|
||||||
|
{ user }: fetchProfileOptions,
|
||||||
|
) {
|
||||||
|
return agent.getProfile({
|
||||||
|
actor: user,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,9 @@ import { serve } from "@hono/node-server";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { HTTPException } from "hono/http-exception";
|
import { HTTPException } from "hono/http-exception";
|
||||||
import { Redis } from "ioredis";
|
import { Redis } from "ioredis";
|
||||||
|
import { getOEmbed } from "./routes/getOEmbed.ts";
|
||||||
import { getPost } from "./routes/getPost.tsx";
|
import { getPost } from "./routes/getPost.tsx";
|
||||||
|
import { getProfile } from "./routes/getProfile.tsx";
|
||||||
|
|
||||||
// biome-ignore lint/style/noNonNullAssertion: check is ran at app start
|
// biome-ignore lint/style/noNonNullAssertion: check is ran at app start
|
||||||
const redis = new Redis(6379, process.env.REDIS_HOSTNAME!);
|
const redis = new Redis(6379, process.env.REDIS_HOSTNAME!);
|
||||||
|
|
@ -58,6 +60,11 @@ app.use("*", async (c, next) => {
|
||||||
app.get("/profile/:user/post/:post", getPost);
|
app.get("/profile/:user/post/:post", getPost);
|
||||||
app.get("/https://bsky.app/profile/:user/post/:post", getPost);
|
app.get("/https://bsky.app/profile/:user/post/:post", getPost);
|
||||||
|
|
||||||
|
app.get("/profile/:user", getProfile);
|
||||||
|
app.get("/https://bsky.app/profile/:user", getProfile);
|
||||||
|
|
||||||
|
app.get("/oembed", getOEmbed);
|
||||||
|
|
||||||
serve(
|
serve(
|
||||||
{
|
{
|
||||||
...app,
|
...app,
|
||||||
|
|
|
||||||
25
src/routes/getProfile.tsx
Normal file
25
src/routes/getProfile.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import type { Handler } from "hono";
|
||||||
|
import { HTTPException } from "hono/http-exception";
|
||||||
|
import { Profile } from "../components/Profile.tsx";
|
||||||
|
import { fetchProfile } from "../lib/fetchProfile.ts";
|
||||||
|
|
||||||
|
export const getProfile: Handler<
|
||||||
|
HonoEnv,
|
||||||
|
"/profile/:user" | "/https://bsky.app/profile/:user"
|
||||||
|
> = async (c) => {
|
||||||
|
const { user } = c.req.param();
|
||||||
|
const agent = c.get("Agent");
|
||||||
|
const { data, success } = await fetchProfile(agent, { user });
|
||||||
|
if (!success) {
|
||||||
|
throw new HTTPException(500, {
|
||||||
|
message: "Failed to fetch the profile!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return c.html(
|
||||||
|
<Profile
|
||||||
|
profile={data}
|
||||||
|
url={c.req.path}
|
||||||
|
appDomain={process.env.FIXBLUESKY_APP_DOMAIN ?? "bsyy.app"}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue