Skip to content

Commit 3e8cda5

Browse files
committed
Merge #177: feat: new article listing Rust BitTorrent tracker implementations
b95cc90 fix: update Torrust-Actix link to new website torrust-actix.com (Jose Celano) 144fa13 feat: add banner image for trackers-implemented-in-rust post (Jose Celano) 51cfb92 chore: fix Markdown formatting and improve AGENTS.md docs (Jose Celano) e6c0f93 feat: add blog post listing Rust BitTorrent tracker implementations (Jose Celano) Pull request description: Closes #155 ## Summary Adds a new blog post: **"BitTorrent Trackers Implemented in Rust"** — a comprehensive overview of all known Rust-based BitTorrent tracker implementations. ## What's included ### New blog post (`src/routes/blog/trackers-implemented-in-rust/`) - Covers 4 actively maintained trackers: aquatic, Torrust Tracker, Torrust-Actix, UNIT3D-Announce - Covers 5 historical/abandoned trackers: UDPT, Tyto, Rotten, bittorrent-client-tracker, Torshare - Each entry includes: repository, stars, license, status, key features, and implemented BEPs - Comparison table with columns: Project, Protocols, BEPs, License, Stars, Status - Trackers ordered by GitHub star count within each section - Neutral conclusion with no promotional language ### Banner image - `static/images/posts/trackers-implemented-in-rust/banner-rust-bitorrent-trackers.webp` ### Bug fix - Updated Torrust-Actix link in `src/lib/constants/constants.ts` and the blog post from `gbitt.info` to `https://www.torrust-actix.com/` ### Tooling / docs - `AGENTS.md`: improved blog post authoring instructions, fixed Markdown lint violations (MD010, MD036, MD001) - `.prettierrc.cjs`: added `*.md` override to prevent hard tabs in Markdown files ACKs for top commit: josecelano: ACK b95cc90 Tree-SHA512: 1e433945a6571a7ffa4fa8fa1f59fc890ad407aec1e62ad0bf453697541eacb7d47c03998a541366dc479ca47173f069c309a659445ad38127d493e112f60bea
2 parents c9d5426 + b95cc90 commit 3e8cda5

9 files changed

Lines changed: 986 additions & 98 deletions

File tree

.prettierrc.cjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ module.exports = {
44
trailingComma: 'none',
55
printWidth: 100,
66
plugins: [require('prettier-plugin-svelte')],
7-
overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }]
7+
overrides: [
8+
{ files: '*.svelte', options: { parser: 'svelte' } },
9+
{ files: '*.md', options: { useTabs: false, tabWidth: 2 } }
10+
]
811
};

AGENTS.md

Lines changed: 137 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -138,49 +138,137 @@ scripts/
138138

139139
**File Structure:**
140140

141-
- Blog posts are in `src/routes/blog/[post-slug]/` directories
142-
- Each post has:
143-
- `+page.svelte` - Main content (MDsveX)
144-
- `+page.server.ts` - Server-side data loading
145-
- `metadata.ts` - Post metadata export
146-
147-
**Front Matter (YAML):**
148-
Required fields:
149-
150-
```yaml
151-
---
152-
title: 'Post Title'
153-
date: '2024-01-15'
154-
excerpt: 'Brief description for listings and SEO'
155-
categories: ['Development', 'BitTorrent']
156-
cover_image: '/images/posts/post-slug/cover.webp'
157-
author: 'Author Name'
158-
---
141+
Blog posts live in `src/routes/blog/[post-slug]/`. Each post directory contains exactly three files:
142+
143+
| File | Purpose |
144+
| ----------------- | -------------------------------------------------------------- |
145+
| `metadata.ts` | Typed post metadata (title, date, tags, etc.) |
146+
| `+page.server.ts` | Server-side data loader (identical boilerplate for every post) |
147+
| `+page.svelte` | Post content written as a Svelte component |
148+
149+
**`metadata.ts` format:**
150+
151+
```typescript
152+
export const metadata = {
153+
title: 'Post Title',
154+
slug: 'post-slug', // must match the directory name
155+
contributor: 'Author Name',
156+
contributorSlug: 'author-slug', // matches a directory under src/routes/contributor/
157+
date: '2024-01-15T12:00:00.000Z', // ISO 8601
158+
coverImage: '/images/posts/post-slug/cover.webp',
159+
excerpt: 'Brief description for listings and SEO.',
160+
tags: ['Rust', 'BitTorrent'] // title-case tags
161+
};
159162
```
160163

161-
Optional fields:
164+
**`+page.server.ts` boilerplate** (copy verbatim for every post):
162165

163-
- `updated`: "2024-01-20" - Last update date
164-
- `tags`: ["rust", "tracker"] - Additional tags
166+
```typescript
167+
import { getMetadata } from '$lib/data/metadata';
168+
import type { PageServerLoad } from './$types';
165169

166-
**Workflow:**
170+
export const load: PageServerLoad = async ({ url }) => {
171+
const slug = url.pathname.split('/').filter(Boolean).pop();
172+
if (!slug) throw new Error('Slug could not be determined.');
167173

168-
1. Use Front Matter VS Code extension for easier management
169-
2. Create post directory: `src/routes/blog/my-new-post/`
170-
3. Add front matter and content
171-
4. Place images in `static/images/posts/my-new-post/`
172-
5. Reference images: `/images/posts/my-new-post/image.png`
173-
6. Use `<Image />` component for automatic optimization
174-
7. Run `npm run dev` to preview
175-
8. Metadata is auto-generated during build
174+
const metadata = await getMetadata();
175+
const currentPost = metadata.find((post) => post.slug === slug);
176176

177-
**Supported Content:**
177+
if (!currentPost) throw new Error(`Post not found: ${slug}`);
178178

179-
- Standard Markdown syntax
180-
- Svelte components inline
181-
- Code blocks with syntax highlighting
182-
- Images with automatic optimization
183-
- Links with automatic external link handling
179+
return { currentPost, allPosts: metadata };
180+
};
181+
```
182+
183+
**`+page.svelte` structure:**
184+
185+
```svelte
186+
<script lang="ts">
187+
import BlogPreview from '$lib/components/molecules/BlogPreview.svelte';
188+
import Toc from '$lib/components/atoms/Toc.svelte';
189+
import Post from '$lib/components/organisms/Post.svelte';
190+
import PagesWrapper from '$lib/components/atoms/PagesWrapper.svelte';
191+
import PrevNextPost from '$lib/components/singletons/PrevNextPost.svelte';
192+
import Callout from '$lib/components/molecules/Callout.svelte';
193+
194+
let { data } = $props();
195+
let currentPost = $derived(data.currentPost);
196+
let allPosts = $derived(data.allPosts);
197+
</script>
198+
199+
<Post
200+
title={currentPost.title}
201+
slug={currentPost.slug}
202+
coverImage={currentPost.coverImage}
203+
date={currentPost.date}
204+
tags={currentPost.tags}
205+
excerpt={currentPost.excerpt}
206+
contributor={currentPost.contributor}
207+
contributorSlug={currentPost.contributorSlug}
208+
>
209+
<PagesWrapper>
210+
<div class="wrapper">
211+
<Toc class="toc" />
212+
<div id="toc-contents" class="content-preview">
213+
<!-- Post content goes here -->
214+
<h2 id="introduction">Introduction</h2>
215+
<p>...</p>
216+
</div>
217+
</div>
218+
</PagesWrapper>
219+
<PrevNextPost currentPage={currentPost.slug} {allPosts} />
220+
<div class="related-posts-container">
221+
<h2>Related Posts:</h2>
222+
<div class="grid">
223+
{#each data.allPosts.slice(0, 3) as post}
224+
<a href="/blog/{post.slug}">
225+
<BlogPreview post_data={post} />
226+
</a>
227+
{/each}
228+
</div>
229+
</div>
230+
</Post>
231+
232+
<style lang="scss">
233+
@use '$lib/scss/breakpoints.scss' as bp;
234+
/* styles here */
235+
</style>
236+
```
237+
238+
Look at an existing post (e.g. `src/routes/blog/vortex-rust-bittorrent-client-review/`) as a full reference implementation.
239+
240+
**Heading IDs for the Table of Contents:**
241+
242+
The `<Toc />` component auto-generates a table of contents from `<h2>` and `<h3>` elements inside the `id="toc-contents"` div. Every heading must have a matching `id` attribute:
243+
244+
```svelte
245+
<h2 id="my-section">My Section</h2>
246+
```
247+
248+
**Supported content components:**
249+
250+
| Component | Import | Usage |
251+
| ------------- | -------------------------------------------- | ------------------------------------------------------------------------- |
252+
| `<Callout>` | `$lib/components/molecules/Callout.svelte` | `<Callout type="info">...</Callout>` (types: `info`, `warning`, `danger`) |
253+
| `<CodeBlock>` | `$lib/components/molecules/CodeBlock.svelte` | Fenced code with syntax highlighting |
254+
| `<Image>` | `$lib/components/atoms/Image.svelte` | Optimised images (preferred over `<img>`) |
255+
256+
**Images:**
257+
258+
- Place images in `static/images/posts/my-new-post/`
259+
- Reference them as `/images/posts/my-new-post/image.png`
260+
- Use `<Image src="..." alt="..." />` for automatic WebP/AVIF optimisation
261+
- Cover image should be named `cover.webp` and placed in the same folder
262+
263+
### ⚠️ Critical: regenerate metadata during development
264+
265+
`static/blogMetadata.json` drives the blog listing page and search. It is generated automatically by `npm run build`, but during development you must run it manually after adding or modifying a post's `metadata.ts`:
266+
267+
```bash
268+
npx tsx scripts/generateMetadata.ts
269+
```
270+
271+
Without this step the new post will not appear at `/blog`.
184272

185273
## Managing Contributors List
186274

@@ -333,11 +421,19 @@ This metadata is used for:
333421

334422
### Adding a new blog post
335423

336-
1. Create a new `.md` file in `src/routes/blog/`
337-
2. Add required front matter (title, date, excerpt, etc.)
338-
3. Write content using Markdown and Svelte components as needed
339-
4. Add cover image to `static/images/blog/`
340-
5. Test locally with `npm run dev`
424+
1. Create a new directory: `src/routes/blog/my-new-post/`
425+
2. Add `metadata.ts` with required fields (see **Managing Blog Posts** above)
426+
3. Add `+page.server.ts` (copy boilerplate verbatim from any existing post)
427+
4. Add `+page.svelte` with post content (use an existing post as a reference)
428+
5. Place cover image at `static/images/posts/my-new-post/cover.webp`
429+
6. Regenerate metadata so the post appears in the listing:
430+
431+
```bash
432+
npx tsx scripts/generateMetadata.ts
433+
```
434+
435+
7. Run `npm run dev` and verify the post appears at `http://localhost:5173/blog`
436+
8. Run `npm run check` and `npm run lint` before committing
341437

342438
### Adding a new component
343439

project-words.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ bencoded
1111
Bencoding
1212
binstall
1313
bitmagnet
14+
bitorrent
1415
Bragilevsky
1516
Bram
1617
buildtime
@@ -83,6 +84,8 @@ nojekyll
8384
nologin
8485
Nuxt
8586
opentracker
87+
optimisation
88+
Optimised
8689
passcode
8790
pataquets
8891
pcarles
@@ -118,10 +121,12 @@ TMDB
118121
tornnab
119122
torrenting
120123
Torrust
124+
Torshare
121125
Torznab
122126
Troubleshoting
123127
Ttorrent
124128
Tzou
129+
UDPT
125130
usermod
126131
valgrind
127132
Verstappen

src/lib/constants/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ export const trackerTableData = [
409409
language: 'Rust',
410410
os: 'Linux,MacOs,Windows',
411411
repo: 'https://github.com/Power2All/torrust-actix',
412-
demo: 'https://www.gbitt.info'
412+
demo: 'https://www.torrust-actix.com/'
413413
},
414414
{
415415
name: 'opentracker',
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { getMetadata } from '$lib/data/metadata';
2+
import type { PageServerLoad } from './$types';
3+
4+
export const load: PageServerLoad = async ({ url }) => {
5+
const slug = url.pathname.split('/').filter(Boolean).pop();
6+
if (!slug) throw new Error('Slug could not be determined.');
7+
8+
const metadata = await getMetadata();
9+
const currentPost = metadata.find((post) => post.slug === slug);
10+
11+
if (!currentPost) throw new Error(`Post not found: ${slug}`);
12+
13+
return { currentPost, allPosts: metadata };
14+
};

0 commit comments

Comments
 (0)