وبلاگ نویسی بهتر در Dev.to با Vrite – CMS بدون سر برای محتوای فنی

با محبوبیت روزافزون نوشتن فنی – تا حدی به لطف پلتفرم هایی مانند DEV یا Hashnode، برای من جالب بود که ابزار در این طاقچه هنوز وجود ندارد. شما اغلب باید Markdown خام بنویسید، بین ویرایشگرهای مختلف بپرید و از ابزارهای زیادی برای پشتیبانی از فرآیند تولید محتوا استفاده کنید.
به همین دلیل است که تصمیم گرفتم Vrite را ایجاد کنم – نوع جدیدی از CMS بدون هد که به طور خاص برای نوشتن فنی طراحی شده است، با تجربه توسعه دهنده خوب در ذهن. از توکار مدیریت کانبان داشبورد به ویرایشگر پیشرفته WYSIWYG با پشتیبانی از مارک داون، همکاری بلادرنگ، تعبیه شده است ویرایشگر کدو ادغام زیباتر – Vrite یک فروشگاه برای همه محتوای فنی شما است.
با انتشار نسخه بتا عمومی در اوایل این هفته، Vrite اکنون منبع باز و در دسترس برای همه است – برای کمک به هدایت نقشه راه آینده و ایجاد بهترین ابزار برای همه نویسندگان فنی!
API DEV
یک CMS – مخصوصاً یک سیستم بدون هد – فقط میتواند کارهای زیادی را بدون یک نقطه پایانی انتشار متصل انجام دهد. در مورد Vrite، به لطف API و قالب محتوای انعطافپذیر، میتوان آن را به راحتی به هر چیزی از وبلاگ شخصی گرفته تا مخزن GitHub یا پلتفرمی مانند Dev.to متصل کرد.
Dev.to یک گزینه به خصوص جالب است، زیرا API پلتفرم اصلی – Forem – به خوبی مستند شده و به راحتی در دسترس است. بنابراین، بیایید ببینیم چگونه آن را با Vrite وصل کنیم!
شروع کار با Vrite
با توجه به اینکه Vrite منبع باز است، به زودی می توانید آن را خود میزبانی کنید. با این حال، من هنوز در حال کار بر روی اسناد و پشتیبانی مناسب برای این فرآیند هستم. در حال حاضر، بهترین راه برای آزمایش Vrite از طریق نسخه رایگان «ابر» در app.vrite.io است.
با ثبت نام برای یک حساب شروع کنید – مستقیم یا از طریق GitHub:
وقتی وارد می شوید، با داشبورد کانبان مواجه می شوید. اینجاست که میتوانید تمام محتوای خود را مدیریت کنید:
در این مرحله، ارزش توضیح چگونگی ساختار چیزها در Vrite را دارد:
- فضای کار – این بالاترین واحد سازمانی در Vrite است. اینجا جایی است که همه گروههای محتوای شما، اعضای تیم، تنظیمات ویرایش و دسترسی API کنترل میشوند. یک پیشفرض برای شما ایجاد شده است، اگرچه میتوانید هر تعداد که میخواهید ایجاد کنید و به آنها دعوت شوید.
- گروه های محتوا – معادل ستون ها در داشبورد Kanban؛ آنها اساساً تمام قطعات محتوا را تحت یک برچسب گروه بندی می کنند، به عنوان مثال ایده ها، پیش نویس، منتشر شده.
- قطعات محتوا – جایی که محتوای واقعی شما و ابرداده های آن – مانند توضیحات، برچسب ها و غیره زندگی می کنند.
فرض کنید می خواهید یک فضای کاری کاملاً جدید برای وبلاگ Dev.to خود داشته باشید زیرا قصد دارید محتوای منحصر به فردی را در آنجا منتشر کنید. برای ایجاد یکی، روی فضای کاری را تغییر دهید دکمه در گوشه پایین سمت چپ (شش ضلعی) و سپس فضای کاری جدید.
شما باید یک نام و به صورت اختیاری – یک توضیحات و آرم ارائه دهید. سپس کلیک کنید ایجاد فضای کاری و فضای کاری جدیدی که ایجاد کرده اید را از لیست انتخاب کنید:
در داشبورد، اکنون می توانید چند گروه محتوا ایجاد کنید تا با کلیک کردن، محتوای خود را سازماندهی کنید گروه جدید. وقتی این کار انجام شد، در نهایت می توانید با کلیک کردن، یک قطعه محتوای جدید ایجاد کنید قطعه محتوای جدید در پایین ستون انتخابی شما
با یک قطعه محتوای جدید، می توانید تمام ابرداده های آن را در قسمت مشاهده و پیکربندی کنید پنل کناری. در Vrite، تقریباً همه چیز، به غیر از ایجاد و مدیریت محتوا، در این نمای قابل تغییر اندازه اتفاق میافتد. به این ترتیب همیشه می توانید در حین ویرایش متادیتا یا پیکربندی تنظیمات، مراقب محتوا باشید.
اکنون، کلیک کنید در ویرایشگر باز کنید یا در پانل کناری یا در کارت قطعه محتوا در Kanban (شما همچنین می توانید از دکمه منوی کناری استفاده کنید) برای باز کردن قطعه محتوای انتخابی در ویرایشگر.
این جایی است که سحر و جادو اتفاق می افتد! در حین نوشتن قطعه عالی بعدی خود با خیال راحت به بررسی ویرایشگر بپردازید. Vrite تمام تغییرات را در زمان واقعی همگام می کند و از بسیاری از گزینه های قالب بندی موجود در ویرایشگرهای WYSIWYG مدرن پشتیبانی می کند. علاوه بر این، شما همچنین یک ویرایشگر کد پیشرفته برای همه قطعه های خود با ویژگی هایی مانند تکمیل خودکار و قالب بندی برای زبان های پشتیبانی شده دریافت می کنید:
اتصال با DEV
وقتی نوشتن قطعه بعدی خود را به پایان رساندید، زمان انتشار آن فرا رسیده است! برای راحتی، ویرایشگر Vrite یک را ارائه می دهد صادرات منویی که در آن می توانید محتویات ویرایشگر خود را در JSON، HTML یا GitHub Flavored Markdown (GFM) برای کپی کردن آسان دریافت کنید. با این حال، برای به دست آوردن یک تجربه انتشار خودکار مناسب تر، احتمالاً می خواهید از Vrite API و Webhooks استفاده کنید.
گردش کار مورد نظر به شکل زیر است:
- قطعات محتوا را به ستون انتشار بکشید و رها کنید.
- ارسال پیام به سرور از طریق Webhooks.
- بازیابی و پردازش محتوا از طریق Vrite API و JS SDK.
- انتشار/بهروزرسانی یک پست وبلاگ در Dev.to؛
برای این آموزش، من از Cloudflare Workers استفاده خواهم کرد، زیرا آنها واقعاً سریع و آسان تنظیم می شوند، اما شما می توانید تقریباً از هر ارائه دهنده بدون سرور دیگری با پشتیبانی از JS استفاده کنید.
با ایجاد یک پروژه CF Worker جدید شروع کنید:
npm create cloudflare
سپس، cd
به پروژه داربست به wrangler login
و Vrite JS SDK را نصب کنید:
wrangler login
npm i @vrite/sdk
برای تعامل با SDK، باید یک توکن API داشته باشید. برای دریافت آن از Vrite، به تنظیمات → API → نشانه API جدید:
توصیه می شود مجوزهای توکن API را در حداقل لازم نگه دارید، که در این مورد فقط به این معنی است نوشتن دسترسی به قطعات محتوا (از آنجایی که ما در واقع بعداً متادیتای قطعه محتوا را به روز خواهیم کرد). بعد از کلیک کردن توکن جدید ایجاد کنید توکن جدید ایجاد شده به شما ارائه می شود. آن را ایمن و ایمن نگه دارید – شما فقط یک بار آن را خواهید دید!
علاوه بر این، برای انتشار محتوا در Dev.to از طریق API آن، باید یک کلید API نیز از آن دریافت کنید. برای انجام این کار، به پایین تنظیمات در حساب DEV خود بروید و کلیک کنید کلید API را ایجاد کنید:
اکنون هر دو توکن را به عنوان متغیرهای محیطی از طریق Worker اضافه کنید wrangler.toml
:
name = "autopublishing"
main = "src/worker.ts"
compatibility_date = "2023-05-18"
[vars]
VRITE_API_TOKEN = "[YOUR_VRITE_API_TOKEN]"
DEV_API_KEY="[YOUR_DEV_API_KEY]"
پس از رویداد، Vrite یک را ارسال می کند POST
درخواست به URL هدف پیکربندی شده وب هوک با بار اضافی JSON. برای مورد استفاده ما، مهمترین بخش این بار، شناسه یک قطعه محتوایی است که به تازگی به گروه محتوای داده شده اضافه شده است (چه با کشیدن و رها کردن یا با ایجاد مستقیم)
بیایید بالاخره Worker خود را بسازیم (در داخل src/worker.ts
):
import { JSONContent, createClient } from '@vrite/sdk/api';
import { createContentTransformer, gfmTransformer } from '@vrite/sdk/transformers';
const processContent = (content: JSONContent): string => {
// ...
};
export interface Env {
VRITE_API_TOKEN: string;
DEV_API_KEY: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const payload: { id: string } = await request.json();
const client = createClient({ token: env.VRITE_API_TOKEN });
const contentPiece = await client.contentPieces.get({
id: payload.id,
content: true,
description: 'text',
});
const article = {
title: contentPiece.title,
body_markdown: processContent(contentPiece.content),
description: contentPiece.description || undefined,
tags: contentPiece.tags.map((tag) => tag.label?.toLowerCase().replace(/\s/g, '')).filter(Boolean),
canonical_url: contentPiece.canonicalLink || undefined,
published: true,
series: contentPiece.customData?.devSeries || undefined,
main_image: contentPiece.coverUrl || undefined,
};
if (contentPiece.customData?.devId) {
try {
const response = await fetch(`https://dev.to/api/articles/${contentPiece.customData.devId}`, {
method: 'PUT',
headers: {
'api-key': env.DEV_API_KEY,
Accept: 'application/json',
'content-type': 'application/json',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)',
},
body: JSON.stringify({
article,
}),
});
const data: { error?: string } = await response.json();
if (data.error) {
console.error('Error from DEV: ', data.error);
}
} catch (error) {
console.error(error);
}
} else {
try {
const response = await fetch(`https://dev.to/api/articles`, {
method: 'POST',
body: JSON.stringify({ article }),
headers: {
'api-key': env.DEV_API_KEY,
Accept: 'application/json',
'content-type': 'application/json',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)',
},
});
const data: { error?: string; id?: string } = await response.json();
if (data.error) {
console.error(data.error);
} else if (data.id) {
await client.contentPieces.update({
id: contentPiece.id,
customData: {
...contentPiece.customData,
devId: data.id,
},
});
}
} catch (error) {
console.error(error);
}
}
return new Response();
},
};
اینجا چه خبره؟ ما با راهاندازی سرویس گیرنده Vrite API و واکشی ابرداده و محتوای مربوط به محتوایی که رویداد را راهاندازی کرده است، شروع میکنیم. سپس، از این داده ها برای ایجاد یک article
شیء مورد انتظار DEV API و استفاده از آن برای درخواست.
در Vrite، علاوه بر ابرداده های کاملاً تعریف شده مانند برچسب ها یا پیوندهای متعارف، می توانید مبتنی بر JSON نیز ارائه دهید. داده های سفارشی. هم از طریق داشبورد و هم از طریق API قابل تنظیم است، و آن را به یک ذخیرهسازی عالی برای دادههایی مانند شناسه مقاله DEV تبدیل میکند، که به ما امکان میدهد تصمیم بگیریم مقاله جدیدی منتشر کنیم یا مقاله موجود را بهروزرسانی کنیم (با استفاده از یک سفارشی). devId
ویژگی). مکانیسم مشابهی برای بازیابی نام سری که مقاله باید به آن اختصاص داده شود در DEV اعمال می شود، که می تواند از داشبورد Vrite با استفاده از یک سفارشی پیکربندی شود. devSeries
ویژگی.
شایان ذکر است که، برای درخواستهای DEV API، ما یک مورد عمومی را ارائه میکنیم User-Agent
هدر – لازم است یک درخواست موفقیت آمیز بدون 403
خطای تشخیص ربات
تبدیل کننده های محتوا
شاید متوجه شده باشید که body_markdown
ویژگی به نتیجه تنظیم شده است processContent()
زنگ زدن. دلیل آن این است که Vrite API محتوای خود را در قالب JSON ارائه می دهد. این قالب برگرفته از کتابخانه ProseMirror که ویرایشگر Vrite را تامین میکند، امکان ارائه محتوای همه کاره را فراهم میکند، زیرا میتواند به راحتی با نیازهای مختلف سازگار شود.
Vrite JS SDK ابزارهای داخلی برای تبدیل این فرمت به نام دارد تبدیل کننده های محتوا. آنها به شما این امکان را می دهند که به راحتی JSON را به یک فرمت مبتنی بر رشته، مانند HTML یا GFM (که هر دو دارای ترانسفورماتورهای اختصاصی در SDK هستند) پردازش کنید.
برای DEV، استفاده از ترانسفورماتور GFM در بیشتر موارد خوب است. با این حال، این ترانسفورماتور جاسازیهایی را که توسط ویرایشگر Vrite و DEV (به عنوان مثال CodePen، CodeSandbox و YouTube) پشتیبانی میشوند، نادیده میگیرد، زیرا در مشخصات GFM پشتیبانی نمیشوند. بنابراین، بیایید یک ترانسفورماتور سفارشی بسازیم که گسترش می دهد gfmTransformer
برای افزودن پشتیبانی برای این تعبیهها:
import { JSONContent, createClient } from '@vrite/sdk/api';
import { createContentTransformer, gfmTransformer } from '@vrite/sdk/transformers';
const processContent = (content: JSONContent): string => {
const devTransformer = createContentTransformer({
applyInlineFormatting(type, attrs, content) {
return gfmTransformer({
type,
attrs,
content: [
{
type: 'text',
marks: [{ type, attrs }],
text: content,
},
],
});
},
transformNode(type, attrs, content) {
switch (type) {
case 'embed':
return `\n{% embed ${attrs?.src || ''} %}\n`;
case 'taskList':
return '';
default:
return gfmTransformer({
type,
attrs,
content: [
{
type: 'text',
attrs: {},
text: content,
},
],
});
}
},
});
return devTransformer(content);
};
// ...
یک Content Transform از درخت JSON – از پایینترین تا بالاترین سطح – میگذرد و هر گره را پردازش میکند و همیشه در نتیجه عبور میکند. content
رشته تولید شده از گره های فرزند.
در processContent()
تابع بالا، ما پردازش گزینه های قالب بندی درون خطی (مانند پررنگ، ایتالیک و غیره) را به gfmTransformer
، زیرا GFM و DEV Markdown از گزینه های قالب بندی یکسانی پشتیبانی می کنند. در مورد گره ها (مانند پاراگراف ها، تصاویر، لیست ها و غیره) ما در حال “فیلتر کردن” هستیم. taskList
s (چون DEV از آنها پشتیبانی نمی کند) و پردازش برای embeds
، با استفاده از تگ های مایع DEV و جاسازی URL موجود به عنوان ویژگی گره — src
.
اکنون Worker آماده استقرار از طریق Wrangler CLI است:
wrangler deploy
هنگام استقرار، باید URL تماس با Worker را در ترمینال خود دریافت کنید. اکنون می توانید از آن برای ایجاد یک Webhook جدید در Vrite استفاده کنید:
رفتن به تنظیمات → Webhooks → Webhook جدید (همه در پانل کناری)
برای یک رویداد را انتخاب کنید New content piece added
– هر بار که یک محتوای جدید مستقیماً در گروه محتوای داده شده ایجاد می شود، این کار باعث می شود (در این مورد منتشر شده) یا کشیده و داخل آن انداخته است.
اکنون باید بتوانید فقط محتوای آماده خود را بکشید و رها کنید و ببینید که به طور خودکار در DEV منتشر می شود! 🎉
مراحل بعدی
در حال حاضر، حتی در حال حاضر، کارهای زیادی می توانید با Vrite انجام دهید، که من در این مقاله به آنها پرداخته ام. در اینجا چند نمونه هستند:
- فقط محتوایی که به تازگی به گروه محتوا اضافه شده و منتشر/به روز می شود. شاید بخواهید در نظر بگیرید “قفل کردن” این گروه محتوا به طوری که ویرایش این قطعات محتوا مستلزم این است که ابتدا مقاله را به قسمت قبلی برگردانید پیش نویس یا ویرایش ستون در صورت لزوم، میتوانید Webhookهای اختصاصی را برای آن گروهها راهاندازی کنید تا قطعات محتوا بهطور خودکار در DEV منتشر نشود.
- از زمان معرفی Workspace ها، Vrite از Teams و همکاری در زمان واقعی مانند Google Docs. این آن را از یک CMS استاندارد به یک ویرایشگر واقعا خوب ارتقا می دهد و به شما امکان می دهد بدون نیاز به کپی پیست دستی، تحویل محتوای خود را سرعت بخشید. بنابراین با خیال راحت از سایر همکاران دعوت کنید تا به فضای کاری شما بپیوندند و سطح دسترسی آنها را از طریق آن کنترل کنید نقش ها و مجوزها.
- با پشتیبانی Vrite از گزینههای قالببندی مختلف و بلوکهای محتوا – ممکن است بخواهید ویژگیهای موجود را برای تناسب بهتر با سبک نوشتاری خود محدود کنید – به خصوص زمانی که در یک تیم کار میکنید. سعی کنید خود را تنظیم کنید تجربه ویرایش در تنظیمات شامل گزینه های ذکر شده و الف پیکربندی زیباتر برای قالب بندی کد
- در نهایت، از آنجایی که Vrite یک CMS خارجی است، میتوانید آزادانه آن را با هر صفحه اصلی تحویل محتوا (مانند وبلاگ شخصی یا سایر پلتفرمها) متصل کنید و به راحتی محتوای خود را ارسال کنید.
خط پایین
اکنون، لازم به یادآوری است که Vrite هنوز وارد است بتا. این بدان معنی است که هنوز همه ویژگی ها پیاده سازی نشده اند و احتمالاً با باگ ها و سایر مشکلات روبرو خواهید شد. اما این تنها بخشی از فرآیند است و امیدوارم که با من در حال تکامل چشمانداز نوشتاری فنی باشیم!