🔍 搜尋結果:作業

🔍 搜尋結果:作業

🏞️5 個可供學習並獲得靈感的開源網路應用程式🙇‍♀️💡

如標題所示,在這篇文章中,我們將介紹您可以學習並用作下一個專案起點的開源 Web 應用程式。堅持到最後,因為那裡有超酷的獎勵等著你! 在我們開始討論之前,先說幾句智慧之言(希望如此): ## (開源)榜樣的重要性 ![你很漂亮](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqf1wss7puysn3q0rhso.gif) **從頭開始一個新專案時,您可以做的最有幫助的事情之一就是選擇一個或多個角色模型。**例如,如果您正在建立一個新的生產力應用程式,您可能會關注Trello 等產品或體式。當然,您的應用程式不會相同,並且您可能會想到一些使您的應用程式獨一無二的核心差異,但仍然會有很多您不想重新發明的共享概念和機制。 即使您的角色模型是一個閉源應用程式,您仍然可以透過在野外觀察它來獲得很多價值 - 設計元素、UI、用戶旅程和使用的術語,... **但是現在想像一下,如果您決定學習的應用程式是開源的,並且您可以輕鬆地在 GitHub 上存取其完整原始程式碼 - 這將打開一個全新的可能性世界!** 接下來只需從“外面」並猜測幕後發生了什麼,現在您可以看到每一個細節並了解所做的每一個決定。架構、部署、API 設計、庫和使用的演算法 - 一切都在那裡供您查看! ## 注意規模(也就是不要過度設計) 另一件需要記住的事情是您的專案目前所處的階段。下面,我們將看到開源 SaaS 應用程式的不同範例,從獨立駭客、「週末建置」副專案到企業級 Web 平台。 **儘管您可能會發現擁有數百萬用戶的專案是一個令人驚嘆的學習資源,但請記住,並非他們所做的一切都是您必須嚴格遵循的。由於他們每天遇到的用戶規模和數量龐大**,他們的架構和設計決策通常會更加複雜。如果您剛開始,最好堅持使用最簡單(但仍然合理)的方法,直到您希望需要更高級的方法。 > 從現在開始,對於我們提到的每個應用程式,我們將使用“T 卹尺寸”方法(S、M、L...),讓您大致了解其尺寸和複雜性(無論是在功能還是功能方面)。使用者。 現在,前言結束了,讓我們一起來看看一些令人驚嘆的開源應用程式,您可以立即開始學習: ![樂趣現在開始](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7z4q2cxeiocgpgj3ofs.gif) ## [CoverLetterGPT](https://coverlettergpt.xyz/) - 人工智慧驅動的 SaaS 的完美起點 💾 **原始碼**:https://github.com/vincanger/coverlettergpt 👕 **尺寸**:S **🛠️ Stack**:Chakra UI、React、Node.js 和 Prisma,由 [Wasp](https://github.com/wasp-lang/wasp) 提供支持 [CoverLetterGPT.xyz](http://CoverLetterGPT.xyz) 是每個獨立駭客的夢想- **它是一個由GPT 支援的SaaS,完全開源,最重要的是,它是人們每天使用並付費的真實產品為了**!根據您的履歷和職位描述,該工具將產生一封專業撰寫的求職信。然後,您可以進一步調整每個段落的語氣或手動編輯。 它非常適合學習,因為它不太大,架構也很簡單,但它具有應用程式中可能需要的所有功能 - 社交身份驗證 (Google)、cron 作業、文件上傳、GPT 集成、透過 Stripe 進行支付集成,甚至可以透過比特幣付款! CoverLetterGPT 由 React、Node.js 和 Prisma 製成,由 [Wasp 框架](https://github.com/wasp-lang/wasp) 提供支持,它負責所有管道並刪除大量樣板檔案。 **最好的部分是,當您準備好時,可以透過執行單一 CLI 命令來免費部署應用程式**:「wasp deploy」。 <center><h3>🚨注意🚨</h3></center> > 提示:Wasp 團隊最近發布了 [OpenSaaS](https://kdta.io/github-wasp-lang-open_3),**一個完全免費且開源的 React 和 Node.js 樣板啟動器**。它包含提到的所有內容 + Tailwind、管理儀表板、登陸頁面、部落格等。 [在此處查看](https://kdta.io/github-wasp-lang-open_4) 以更快地開始使用。 ## Supabase Studio - 儀表板傑作🖼️ ![Supabase 工作室](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jc2pz1vhg1wysk7o9148.gif) 💾 **原始碼**:https://github.com/supabase/supabase/tree/master/apps/studio **👕 尺寸**:M/L **🛠️ 堆疊:** Next.js (React)、Tailwind [Supabase](https://supabase.com/) 是一個著名的開源專案,其核心是用 Elixir 編寫的。但是,由於我們在本文中專注於 Web 應用程式,因此我們將看一下 **Supabase Studio - 一個儀表板,您可以在其中查看和管理所有專案。它本身就是一部傑作,而且完全開源!** 該設計是使用 Tailwind 定制的,您可能希望在自己的專案中重複使用許多元素 - 用戶管理、表格、列表等。它還有自己的 AI 集成,用於編寫 SQL 查詢,效果出奇的好。 ## Papermark - 開源 DocSend 替代方案 ✉️ ![papermark_banner](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kt76y923xnpjhbglbvmn.png) 💾 **原始碼**:https://github.com/mfts/papermark 👕 **尺寸**:M **🛠️ 堆疊**:Next.js (React)、Tailwind、Prisma [Papermark](https://github.com/mfts/papermark) 最近受到社群的廣泛喜愛,尤其是其簡潔的設計和直覺的介面。雖然從外觀上看起來很簡單,但該應用程式包含許多功能,使一切順利執行:文件上傳、電子郵件發送、內建分析和自訂網域... **如果您正在建立涉及大量文件管理和使用者協作的專案**,這絕對是您應該考慮的專案。 ## [Crowd.dev](http://Crowd.dev) - 開發社群資料平台,使用 Vue 製作 📊👩‍💻 ![crowd_dev_banner](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ykd2vxomnrl02j46i7mm.png) 💾 **原始碼**:https://github.com/CrowdDotDev/crowd.dev **👕 尺寸**:M **🛠️ 堆疊:** Vue、Node.js [Crowd.dev](http://Crowd.dev) 是 GitHub 最新的後起之秀之一 - 它是一個用於監控社區活動的平台,無論是在 Slack 還是 Discord 上。如果您正在經營自己的開發者社區,那麼這樣的工具是必須具備的,以便了解正在發生的事情以及最活躍的成員是誰。 它在儀表板方面提供了很多功能,但它的另一個強項是**集成 - 如果您正在建置一個從外部源獲取和處理大量資料的應用程式,那麼這是您的首選角色模型**。如果你是 Vue 愛好者,那就加分了,因為這個專案就是用它製作的! ## Habitica - 作為角色扮演遊戲的習慣追蹤器🐲⚔️ ![habitica_banner](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g69lfe93k931fyoj1fzv.png) 💾 **原始碼**:https://github.com/HabitRPG/habitica **👕 尺寸**:L **🛠️ 堆疊:** Vue、Bootstrap、SAAS、Node.js、MongoDB [Habitica](https://habitica.com/) 是我見過的最酷的網頁應用程式之一(他們也有 iOS 和 Android 應用程式) - 它可以幫助您透過角色扮演遊戲!想像 Trello 這樣的看板,但對於您完成的每項任務,您都可以獲得 XP 和金幣,甚至可以與朋友組隊接受任務。 Habitica 已經存在 10 年了,它通過 Vue、Node.js/Express 和 MongoDB 的經典堆疊完美地經受住了時間的考驗。 **如果您想了解建立了多麼豐富的互動式 UI,以及執行這種規模的專案需要什麼樣的架構,那麼這個應用程式絕對值得一試。**誰知道,您甚至可能最終成為居住自己! ## 🏆 **獎勵** 🏆 Appflowy - Rust 和 Flutter 中的概念替代品 🤯 ![appflowy_banner](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tlzizemi22g9wkb48yfz.png) 💾 **原始碼**:https://github.com/AppFlowy-IO/AppFlowy **👕 尺寸**:M **🛠️ 堆疊:** Flutter、Rust 如果您走到這一步,您應該得到特別的待遇!這不是一個網絡應用程式,但它太酷了,我無法控制自己 - 它是**一個用 Rust 和 Flutter 建置的 Notion 替代品(因此可以做筆記)**!由於其本地優先的性質,用戶體驗非常流暢,並且它還將所有內容同步到雲端(如果您願意,您可以自行託管)。 **如果您一直在使用 Rust,但也在尋找一個可以每天使用的專案,Appflowy 可能是完美的選擇。** 它擁有從資料儲存到業務邏輯和 UI 的所有內容,全部都包含在其中一個包供您學習並查看您認為最有趣的內容。 ## 就是這樣!我很想聽聽你的訊息🫵 ![that_is_all](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ovyclq3iyi3d15kfxcaw.gif) 這就是我們今天的全部內容(*放下麥克風*),非常感謝您的閱讀!我希望您發現它有用和/或有趣。 我在撰寫本文時遇到了很多開源 Web 應用程式,很難只選擇其中 5 個。 **現在,我很想聽聽您的來信 - 您最喜歡的開源應用程式是什麼,以及您如何使用它們?寫在下面的評論👇** 謝謝您,下次再見! 👋 --- 原文出處:https://dev.to/matijasos/5-beautiful-open-source-web-apps-to-learn-from-and-get-inspired-280f

使用 Next.js、Resend 和 Trigger.dev 建立後台電子郵件通知

## 您會在本文中找到什麼? 電子郵件通知是讓使用者了解應用程式所執行操作的最常用方法。典型的通知包括:有人追蹤您、有人喜歡您的貼文、有人查看了您的內容。在這篇文章中,我們將探索如何使用 Next.js、Resend 和 Trigger.dev 建立一個簡單的非同步電子郵件通知系統。 我們將使用 Next.js 作為框架來建立我們的應用程式。我們將使用 Resend 發送電子郵件,並使用 Trigger.dev 非同步卸載和發送電子郵件。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwlb0t41kg2s3djcb072.gif) ## Papermark - 開源 DocSend 替代品。 在我們開始之前,讓我與您分享 Papermark。它是 DocSend 的開源替代方案,可幫助您安全地共享文件並從查看者那裡獲取即時的逐頁分析。全部都是開源的! 如果您能給我們一顆星星,我會非常高興!別忘了在留言區分享你的想法❤️ [https://github.com/mfts/papermark](https://github.com/mfts/papermark) [![Papermark 應用程式](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/igzk8cdssbmla9uf1544.png)](https://github.com/mfts/papermark) ## 設定專案 讓我們繼續為我們的電子郵件後台通知系統設定專案環境。我們將建立一個 Next.js 應用程式,並設定為重新發送,最重要的是,設定觸發器來處理非同步電子郵件通知。 ### 使用 TypeScript 和 Tailwindcss 設定 Next.js 我們將使用「create-next-app」產生一個新的 Next.js 專案。我們還將使用 TypeScript 和 Tailwind CSS,因此請確保在出現提示時選擇這些選項。 ``` npx create-next-app # --- # you'll be asked the following prompts What is your project named? my-app Would you like to add TypeScript with this project? Y/N # select `Y` for typescript Would you like to use ESLint with this project? Y/N # select `Y` for ESLint Would you like to use Tailwind CSS with this project? Y/N # select `Y` for Tailwind CSS Would you like to use the `src/ directory` with this project? Y/N # select `N` for `src/` directory What import alias would you like configured? `@/*` # enter `@/*` for import alias ``` ### 安裝重新傳送和 React-Email Resend 是開發人員優先的事務性電子郵件服務。我們將使用它向我們的用戶發送電子郵件。 `react-email` 是一個 React 元件庫,可以輕鬆建立漂亮的電子郵件。 ``` npm install resend react-email ``` ### 安裝觸發器 Trigger 是 TypeScript 的後台作業框架。它允許您從主應用程式中卸載長時間執行的任務並非同步執行它們。我們將使用它非同步發送電子郵件。 觸發器 CLI 是在新的或現有的 Next.js 專案中設定觸發器的最簡單方法。有關更多訊息,請查看[他們的文件](https://trigger.dev/docs/documentation/quickstarts/nextjs)。 ``` npx @trigger.dev/cli@latest init ``` ## 建立應用程式 現在我們已經完成了設置,我們準備開始建立我們的應用程式。我們將介紹的主要功能是: - 設定重新發送電子郵件 - 編寫API路由來發送電子郵件 - 新增觸發器作業以使電子郵件發送非同步 ### #1 設定重新傳送電子郵件 首先,我們需要設定重新發送來發送電子郵件。我們將在專案中建立一個新檔案「resend-notification.ts」並新增以下程式碼。 ``` // lib/emails/resend-notification.ts import { Resend } from "resend"; import { NotificationEmail } from "@/components/emails/notification"; const resend = new Resend(process.env.RESEND_API_KEY!); export async function sendNotificationEmail({ name, email, }: { name: string | null | undefined; email: string | null | undefined; }) { const emailTemplate = NotificationEmail({ name }); try { // Send the email using the Resend API await resend.emails.send({ from: "Marc from Papermark <[email protected]>", to: email as string, subject: "You have a new view on your document!", react: emailTemplate, }); } catch (error) { // Log any errors and re-throw the error console.log({ error }); throw error; } } ``` 使用「react-email」的通知電子郵件範本將如下所示: ``` // components/emails/notification.tsx import React from "react"; import { Body, Button, Container, Head, Heading, Html, Preview, Section, Text, Tailwind, } from "@react-email/components"; export default function ViewedDocument({ name, }: { name: string | null | undefined; }) { return ( <Html> <Head /> <Preview>See who visited your document</Preview> <Tailwind> <Body className="bg-white my-auto mx-auto font-sans"> <Container className="my-10 mx-auto p-5 w-[465px]"> <Heading className="text-2xl font-normal text-center p-0 mt-4 mb-8 mx-0"> <span className="font-bold tracking-tighter">Papermark</span> </Heading> <Heading className="mx-0 my-7 p-0 text-center text-xl font-semibold text-black"> New Document Visitor </Heading> <Text className="text-sm leading-6 text-black"> Your document was just viewed by someone. </Text> <Text className="text-sm leading-6 text-black"> You can get the detailed engagement insights like time-spent per page and total duration for this document on Papermark. </Text> <Section className="my-8 text-center"> <Button className="bg-black rounded text-white text-xs font-semibold no-underline text-center" href={`${process.env.NEXT_PUBLIC_BASE_URL}/documents`} style={{ padding: "12px 20px" }}> See my document insights </Button> </Section> <Text className="text-sm"> Cheers, <br /> The Papermark Team </Text> </Container> </Body> </Tailwind> </Html> ); } ``` ### #2 撰寫API路由發送電子郵件 現在,我們已經準備好了電子郵件範本。我們可以使用它向我們的用戶發送電子郵件。我們將建立一個無伺服器函數,該函數會取得使用者的“姓名”和“電子郵件”,並使用我們之前建立的“sendNotificationEmail”函數向他們發送電子郵件。 ``` // pages/api/send-notification.ts import { NextApiRequest, NextApiResponse } from "next"; import prisma from "@/lib/prisma"; import { sendViewedDocumentEmail } from "@/lib/emails/resend-notification"; export const config = { maxDuration: 60, }; export default async function handle( req: NextApiRequest, res: NextApiResponse ) { // We only allow POST requests if (req.method !== "POST") { res.status(405).json({ message: "Method Not Allowed" }); return; } // POST /api/send-notification try { const { viewId } = req.body as { viewId: string; }; // Fetch the link to verify the settings const view = await prisma.view.findUnique({ where: { id: viewId, }, select: { document: { select: { owner: { select: { email: true, name: true, }, }, }, }, }, }); if (!view) { res.status(404).json({ message: "View / Document not found." }); return; } // send email to document owner that document await sendViewedDocumentEmail({ email: view.document.owner.email as string, name: view.document.owner.name as string, }); res.status(200).json({ message: "Successfully sent notification", viewId }); return; } catch (error) { console.log("Error:", error); return res.status(500).json({ message: (error as Error).message }); } } ``` ### #3 新增觸發器作業,使電子郵件傳送非同步 我們的電子郵件發送功能已準備就緒,但我們不想同步發送電子郵件,因此要等到電子郵件發送後應用程式才會回應使用者。我們希望將電子郵件傳送任務轉移到後台作業。我們將使用觸發器來做到這一點。 在設定中,Trigger CLI 在我們的專案中建立了一個「jobs」目錄。我們將在該目錄中建立一個新檔案“notification-job.ts”並新增以下程式碼。 ``` // jobs/notification-job.ts import { client } from "@/trigger"; import { eventTrigger, retry } from "@trigger.dev/sdk"; import { z } from "zod"; client.defineJob({ id: "send-notification", name: "Send Notification", version: "0.0.1", trigger: eventTrigger({ name: "link.viewed", schema: z.object({ viewId: z.string(), }), }), run: async (payload, io, ctx) => { const { viewId } = payload; // get file url from document version const notification = await io.runTask( "send-notification", async () => { const response = await fetch( `${process.env.NEXT_PUBLIC_BASE_URL}/api/send-notification`, { method: "POST", body: JSON.stringify({ viewId }), headers: { "Content-Type": "application/json", }, } ); if (!response.ok) { await io.logger.error("Failed to send notification", { payload }); return; } const { message } = (await response.json()) as { message: string; }; await io.logger.info("Notification sent", { message, payload }); return { message }; }, { retry: retry.standardBackoff } ); return { success: true, message: "Successfully sent notification", }; }, }); ``` 將匯出新增至作業索引文件,否則觸發器將不知道該作業。雖然是小細節,但連我都忘記了這一點,並花了一個小時尋找錯誤。 ``` // jobs/index.ts export * from "./notification-job"; ``` ### 獎勵:防止惡意存取 API 路由 我們已準備好 API 路由,但我們不想允許任何人存取它。我們希望確保只有我們的應用程式可以存取它。我們將使用一個簡單的標頭身份驗證金鑰來做到這一點。 在觸發器作業中,我們將標頭加入到請求中: ``` // jobs/notification-job.ts .. ... const response = await fetch( `${process.env.NEXT_PUBLIC_BASE_URL}/api/jobs/send-notification`, { method: "POST", body: JSON.stringify({ viewId }), headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.INTERNAL_API_KEY}`, // <- add the authenication header with a local env variable }, }, ); ... .. ``` 在 API 路由中,我們將在「try {} catch {}」區塊之前檢查 API 金鑰是否符合: ``` // pages/api/send-notification.ts .. ... // Extract the API Key from the Authorization header const authHeader = req.headers.authorization; const token = authHeader?.split(" ")[1]; // Assuming the format is "Bearer [token]" // Check if the API Key matches if (token !== process.env.INTERNAL_API_KEY) { res.status(401).json({ message: "Unauthorized" }); return; } ... .. ``` 確保將“INTERNAL_API_KEY”新增至“.env”檔案中。 ``` # .env INTERNAL_API_KEY="YOUR_API_KEY" ``` ## 結論 瞧!我們已經準備好非同步電子郵件通知系統。我們現在可以非同步向用戶發送電子郵件,而不會影響用戶等待時間。我們還可以使用觸發器從主應用程式中卸載許多我們不希望用戶等待的其他任務。 感謝您的閱讀。我是 Marc,開源倡導者。我正在建立 [papermark.io](https://www.papermark.io) - DocSend 的開源替代品。 繼續編碼! ## 幫幫我! 如果您覺得這篇文章有幫助,並且對觸發器和後台任務有了更好的理解,如果您能給我們一顆星,我將非常高興!別忘了在評論中分享你的想法❤️ [https://github.com/mfts/papermark](https://github.com/mfts/papermark) ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nk9c8ktyv1tf3n6jgbxh.gif) --- 原文出處:https://dev.to/mfts/building-background-email-notifications-with-nextjs-resend-and-triggerdev-4cem

✨ 5 個被低估的開源專案 🫵🤐

## 簡介 本文列出了五個不太受歡迎的優秀專案,您應該嘗試一下。 🔥 這些工具旨在改進**資料處理**、**API 開發**、**後端測試**、**身份驗證**和**安全隧道**。 諸如此類的開源專案依賴社群支持🙏,因此請考慮探索並為這些儲存庫加註星標,以促進它們的發展。 ![擁抱 GIF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rxhja1odmmx414wrts5a.gif) *** ## 1. [集算器](http://scudata.com) **- 資料處理** > 💡 集算器是一種用於資料處理的腳本語言,具有豐富的函式庫函數和強大的語法。 ![集算器資料處理腳本語言](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z9dts1lgr1zy96k6zveu.jpg) 集算器是一個針對結構化和半結構化資料的計算和處理引擎。集算器既不是SQL系統,也不是NoSQL技術(如MongoDB),而是採用自創的SPL(結構化處理語言)語法,編碼更簡單,可以利用現有的資料處理技術建立高效的程式。 集算器是**純Java**編寫的,可以輕鬆為您的Java🍵應用程式加入強大的資料處理功能,但非Java應用程式可以透過RESTful API呼叫集算器。 ### 熱門常見問題解答🤔 > **⬇️集算器可以執行在哪些平台上?** 由於它純粹是用 Java 建置,因此可以在任何配備 JVM(Java 虛擬機)、雲端伺服器甚至容器的作業系統中流暢執行。 😎 > **⬇️集算器可以基於現有資料庫運作嗎?** 是的當然!集算器支援數十種資料來源,包括資料庫、文字、excel、json/xml、web服務等。 > **⬇️ 為什麼要放棄 SQL 而選擇集算器?** 簡化的逐步程式碼,易於編寫和除錯。相較於SQL降低N倍的開發、硬體、維運成本。 > 🟢我最近寫了一篇關於這個工具的文章,重點介紹了它的強大功能。看看吧👇。 https://dev.to/shricodev/one-must-have-tool-for-anyone-in-data-field-2jek > 如果你想更深入地了解這個工具的潛力,**[jbx1279](https://dev.to/jbx1279)**分享了一些關於集算器和SPL本身的富有洞察力的文章。請務必也檢查一下它們。 https://github.com/SPLWare/esProc *** ## 2. [Firecamp](https://firecamp.dev/) **- 郵差替代方案** > 💡 API 開發平台,幫助開發人員輕鬆設計、開發、測試和記錄他們的 API。 ![Firecamp 工具 Postman 替代品](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uigr65jz5z6yh731x9gm.jpg) Firecamp 是開放原始碼 Postman 的替代方案,具有 VScode DX,這是一個優先考慮開發人員體驗的 API 開發平台,並為設計、測試和記錄 API 提供無縫環境。 🎯 借助 Firecamp,跨工作區和團隊就 API 集合進行協作,並更快地建立 API,而無需在工具和應用程式之間切換。文件、CLI、CI/CD 一站式提供。 > **⬇️ 從 Postman 切換到 Firecamp 對我來說有挑戰性嗎?** 您可以將 Postman 腳本和資料(例如 **API Collection** 和 **環境變數**)無縫傳輸到 Firecamp,沒有任何問題。 ![Firecamp Postman 替代方案](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q74wl17yc9b7clse6m3h.png) https://github.com/firecamp-dev/firecamp *** ## 3. [Keploy](https://keploy.io/) **- 後端測試** > 💡 為您的應用程式產生實際有效的測試和存根! ![Keploy 產生後端測試](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ry5awt5wtk5qyiqccbwp.jpg) Keploy 是您的開源、以開發人員為中心的後端測試工具。它使工程團隊的後端測試變得簡單且有效率。使用 Keploy,我們不必編寫手動測試用例。 它記錄 API 互動和預期回應,並產生測試案例和資料模擬,使我們的工作變得輕鬆高效,顯著加快發布速度並增強可靠性。 📈 > **⬇️ 它是一個單元測試框架嗎?還是它完全取代了單元測試?** Keploy 與「go-test」、「Pytest」或「Jest」等單元測試框架配合得很好,可簡化測試流程並節省高達 80% 的工作。雖然它涵蓋了大多數情況,但您仍然可以選擇為非 API 可呼叫方法編寫測試。 > **⬇️ 我需要更改程式碼才能將 Keploy 整合到我的應用程式中嗎?** 不需要。Keploy 可以很好地與您現有的程式碼庫配合,無需更改程式碼。 ![Keploy 後端測試示範](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdsnkmq2efgxltzplfq9.gif) https://github.com/keploy/keploy *** ## 4. [Hanko](https://hanko.io) **- 金鑰驗證** > 💡 支援 FIDO2 和 WebAuthn 標準的無密碼身份驗證伺服器。 ![Hanko 金鑰驗證](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqifvf1i536y0afh7nhe.jpg) Hanko 是一款輕量級開源用戶身份驗證解決方案,可帶您踏上超越密碼的旅程。它支援 FIDO2 和 WebAuthn 標準,提供安全、無縫的使用者身份驗證體驗。 > **⬇️ Hanko 如何運作?** Hanko 的工作原理是使用使用者自己的裝置(例如智慧型手機、筆記型電腦或安全金鑰)註冊和驗證使用者。這些裝置可作為加密令牌,無需密碼或其他憑證即可證明使用者的身分。 Hanko 還支援各種身份驗證方法,例如行動應用程式中的生物辨識或 OAuth 登入。 > **⬇️ 我該如何開始使用 Hanko?** 您可以透過註冊免費帳戶並按照文件和教學課程開始使用 Hanko。對於生產用途,請選擇 Hanko Cloud。 > 🟢 我最近使用 Hanko Passkeys 身份驗證建立了一個專案。查看**[此處](https://github.com/shricodev/pdfwhisper-openai)**。 ![Hanko 登陸頁](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/emte4gfglhdft8g8dlhh.png) https://github.com/teamhanko/hanko *** ## 5. [Zrok](https://zrok.io/) **- Ngrok 類固醇** > 💡 Ngrok 的替代品,提供增強的功能和免費的 SaaS 型號。 ![Zrok ngrok 替代方案](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4n31emowwxoamfqzawm.jpg) Zrok 是一個建立在 **OpenZiti** 之上的工具,有助於共享正在執行的服務,例如 Web 伺服器或網路套接字,或安全地將靜態檔案目錄共享到網際網路。它是 Ngrok 的替代品,但具有一些增強功能和**免費 SaaS** 型號。 借助 Zrok,您可以為應用程式建立安全隧道,從而更輕鬆地共享和協作專案。 > **⬇️ 使用 Zrok 相對於 Ngrok 有什麼好處?** Zrok 擁有內建的身份驗證系統、用於管理隧道的 Web 儀表板以及免費的 SaaS 模型。它也是完全**可自我託管**。 > **⬇️ 我該如何開始使用 Zrok?** 若要開始使用 Zrok,請下載適合您平台的 Zrok 用戶端或使用 Web 介面建立隧道。您也可以使用 Zrok CLI 從命令列建立和管理隧道。 ![Zrok 安全隧道](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bp8bguor0wj3i8h6ail1.png) https://github.com/openziti/zrok *** > 如果您認為您使用的任何其他方便的專案沒有應有的受歡迎,請在下面的評論部分分享。 👇 非常感謝您的閱讀! 🎉🫡 --- 原文出處:https://dev.to/shricodev/top-5-underrated-open-source-projects-that-no-one-talks-about-2gki

2024 年 7 個學習 Python 的最佳場所 [網站 + 平台]

--- 標題:2024 年學習 Python 的 7 個最佳地點 [網站 + 平台] 發表:真實 描述:如果您想知道 2024 年在哪裡學習 Python,那麼請查看這 8 個排名前列的網站和免費教程,以在 2024 年免費在線學習 Python 編程。 標籤: python, 程式設計, 編碼, 開發 封面圖片:https://thepracticaldev.s3.amazonaws.com/i/60y4jb5e98udezhppwt8.png --- *披露:這篇文章包含附屬連結;如果您透過本文提供的不同連結購買產品或服務,我可能會收到補償。* 不管你相信與否,Python 已經激勵了許多人學習編碼,而且它還在不斷激勵他們。我認識一些人因為不同的原因學習 Python,從 [web 開發](https://javarevisited.blogspot.com/2019/04/top-5-python-web-development-frameworks.html) 到[機器學習](https://javarevisited.blogspot.com/2019/08/top-5-python-books-for-data-science-and-machine-learning.html)。 我看過新人學習 Python,使用 Django 編寫 Web 應用程式,使用 Python 建立機器學習模型,然後編寫一些方便的腳本來自動執行無聊的工作。 Python 目前是世界上**#1 的程式語言**,並且由於資料科學和機器學習以及出色的 [Python 庫](https://javarevisited.blogspot.com/2018/10/ top-8-python- libraries-for-data-science-machine-learning.html)如Pandas、NumPy 和[TensorFlow](https://hackernoon.com/top-5-tensorflow-and-ml-courses-對於程式設計師-8b30111cad2c)。 因此,如果您也考慮在 2024 年學習 Python,或者已經開始使用 Python 進行編碼,但仍在尋找一些免費資源,那麼您來對地方了。 過去,我分享了許多有用的免費Python資源,例如[書籍](https://javarevisited.blogspot.com/2019/07/top-5-books-to-learn-python-in-2019.html )和[免費課程](https://javarevisited.blogspot.com/2018/12/10-free-python-courses-for-programmers.html)。今天,我將分享一些可以免費學習 Python 的網站、免費教學和入口網站。 從免費資源中學習真是太棒了,因為您不需要信用卡,也不需要支付課程費用。您所需要的只是時間和學習的渴望。 然而,這並不容易,因為有很多[免費的Python資源](https://medium.com/swlh/5-free-python-courses-for-beginners-to-learn-online-e1ca90687caf)可用,選擇正確的一個是一項艱鉅的任務。這就像大海撈針一樣,這就是本文將為您提供幫助的地方。 順便說一句,如果你不介意花幾塊錢來學習像Python 這樣有價值和有用的東西,那麼我還建議你看看Josh Portilla 的**[完整的Python 3 Bootcamp](http://bit. ly/complete Udemy 上的 -python3-bootcamp)**。您將以更結構化的方式快速學習 Python,並且您可以在 Udemy 的促銷活動中僅花費 10 美元購買本課程。 ##2024 年 8 個初學者的熱門平台和免費 Python 教程 在這裡,您將找到一些免費學習 Python 的最佳地點,我與幾位 Python 專家一起精心挑選了這些資源。我有目的地選擇盡可能少的資源,但我仍然有一些選擇。如果您有任何其他免費教授 Python 開發的有用 Python 網站,請隨時推薦它們。 ###**1\. [Coursera](https://coursera.pxf.io/c/3294490/1164545/14726?u=https%3A%2F%2Fwww.coursera.org%2F)** 如果您想在不支付一分錢的情況下向世界一流大學學習,那麼 Coursera 就是您的最佳選擇。它提供史丹佛大學、歐洲工商管理學院、新加坡國立大學(新加坡國立大學)等知名大學教授的線上課程。 最重要的是,它還有最受歡迎的免費課程之一來學習*Python - 適合所有人的程式設計*(Python 入門)。 本課程將從零開始教您 Python 3。您不需要任何程式設計經驗,因為您將在課程中學習。超過 1250,000 名學生已經註冊了這門課程並學習如何編程,現在是您從中受益的機會。 課程也是 [**Python forEverybody 專業化**](https://coursera.pxf.io/c/3294490/1164545/14726?u=https%3A%2F%2Fwww.coursera.org% Coursera 上的2Fspecializations %2Fpython),其中包含另外4 個深入學習Python 的課程,例如: 1.【Python資料結構】( https://coursera.pxf.io/c/3294490/1164545/14726?u=https%3A%2F%2Fwww.coursera.org%2Flearn%2Fpython-data%3Fspecialization%3Dpython) 2.【使用Python存取Web資料】( https://coursera.pxf.io/c/3294490/1164545/14726?u=https%3A%2F%2Fwww.coursera.org%2Flearn%2Fpython-network-data%3Fspecialization%3Dpython) 3. [透過Python使用資料庫]( https://coursera.pxf.io/c/3294490/1164545/14726?u=https%3A%2F%2Fwww.coursera.org%2Flearn%2Fpython-databases%3Fspecialization%3Dpython) 4. [Capstone 專案:使用 Python 檢索、處理和視覺化資料]( https://coursera.pxf.io/c/3294490/1164545/14726?u=https%3A%2F%2Fwww.coursera.org%2Flearn%2Fpython-data-visualization) 所有課程都可以免費旁聽,這意味著您可以免費加入並學習。但是,您無法參加作業和測驗,並且在付款之前不會獲得任何認證。 [![Python 適合所有人Coursera 免費課程](https://1.bp.blogspot.com/-9iz43SzwsfY/XX4691bTMjI/AAAAAAAAaYE/Xk960951xbALk2Y7eCIqfIgL94pOq5vDQCA51xbALk207eCIqfIgL94pOq5vDQA2057%/57%/A5%/B5%/B55%/B5/M5%/B5%/M5%/AM5%/B5%/Mr. 2Bfree。 .jpg) ]( https://dev.to/javinpaul/7-python-online-courses-for-beginners-and-intermediate-programmers-1h4k) 如果您想要所有這些和認證,那麼您需要註冊不是免費的專業化課程。如果您負擔得起並欣賞該課程,無論如何,您應該訂閱,它完全值得您的時間和金錢。 我建議加入 [Coursera Plus],這是 Coursera 的訂閱計劃,可以無限制地存取許多課程、認證和專案。如果您想參加 Coursera 中的多個課程或認證,這可能是最好的學習方式,不僅包括 Python,還包括資料科學和雲端。 ------ ###**2\. [Udemy](https://click.linksynergy.com/deeplink?id=JVFxdTr9V80&mid=39197&murl=https%3A%2F%2Fwww.udemy.com%2F)** 這是另一個流行的線上課程平台,它可能擁有地球上最大的線上課程集合。我喜歡 Udemy,因為你幾乎可以找到任何你想學的課程,而且還是免費的。 與 [Codecademy](https://bit.ly/codecademypro) 不同,您不需要任何訂閱,只需建立免費帳戶,然後您就可以註冊免費的 Python 課程。大多數講師在首次推出課程時都會免費提供課程,以便獲得一些關注、評論和社會認同。 但是,也有一些完全免費的優質 Python 課程。您可以加入他們來學習 Python 3。以下是我最喜歡的一些免費課程,可幫助您深入學習 Python。 Udemy 的優點是您可以向專家學習,但它的互動性不如 CodeCademy。不過,如果您喜歡透過影片學習,那麼沒有比 Udemy 更好的地方了。 如果你能負擔得起一些錢,你還可以以10 美元左右的一次性價格獲得很棒的訓練營風格的課程,例如**[The Complete Python 3 Bootcamp](http://bit.ly/complete -python3-bootcamp)**關於他們的閃購。 [![5 個免費學習 Python 的網站](https://1.bp.blogspot.com/-D0BNTzFrdGo/XX44gcl_gII/AAAAAAAAaXk/yIXkYvrcO60EB9lgyHTQiCDp8nUoaAzSQCLcBGAsYvrcO60EB9lgyHTQiCDp8nUoaAzSQCLcBGAsY7/s400/The% ://bit.ly/complete-python3-bootcamp) -------- ###**3 [透過 Educative 從頭開始學習 Python](https://www.educative.io/courses/learn-python-from-scratch?affiliate_id=5073518643380224)** 如果您不知道 Educative.io 是一個基於文字的互動式平台,可讓您透過瀏覽器進行學習和編碼。您可以學習概念並只需在下一行中編寫程式碼,而無需擔心下載必要的軟體和設定開發環境。對於任何學習任何程式語言的初學者來說,這是最大的優勢,因為他們中的大多數人都陷入了這個設定部分。 如果你想[在2024 年學習Python](https://www.java67.com/2020/05/top-5-courses-to-learn-python-in-depth.html) 那麼這門課是完美的地方開始。本課程首先探索基本建置塊,然後再討論函數和循環等更高層次的概念。有趣的測驗和程式設計挑戰將伴隨您一路前行,幫助您強化課程中涵蓋的所有概念。 在課程結束時,您將熟悉[資料結構](https://becoming human.ai/6-courses-python-programmers-can-join-to-learn-data-structs-and -algorithms-c1a37284938e) 和Python中的函數式程式設計。這是一門免費課程,因此您無需支付任何費用即可加入,您只需建立 Educative.io 帳戶即可存取課程。 [![深入 Python 的免費教學](https://thepracticaldev.s3.amazonaws.com/i/2bwr9gfk1yxgn89j3kjy.png)](https://www.educative.io/courses/learn-python-from-從頭開始?affiliate_id=5073518643380224) ---- ### 4\. [DataCamp 的免費 Python 入門課程](https://datacamp.pxf.io/c/1193463/1012793/13294?u=https%3A%2F%2Fwww.datacamp.com%2Fcourses%2Fintro-to-python-for -資料科學) DataCamp 的「Python 程式設計簡介」課程對於想要踏上 Python 世界之旅的初學者來說是一個極好的資源。此免費課程具有用戶友好的介面和全面的內容,適合剛接觸程式設計或希望鞏固基礎 Python 技能的學習者。 它涵蓋了變數、資料類型、控制結構、函數等關鍵概念,確保學生充分掌握 Python 基礎知識。透過實踐方法,參與者參與實際練習和編碼挑戰,以增強他們的理解。 無論您是想進入資料科學、Web 開發或任何 Python 相關領域,本課程都會對該語言的語法和功能進行紮實的介紹,為更高級的學習奠定基礎。 說到社會認同,超過 500 萬人參加了這門課程,這是任何線上 Python 課程的記錄,它的平均評分為 4.7,簡直令人驚嘆。 順便說一下,如果您喜歡Datacamp的線上學習平台及其課程,可以考慮付費訂閱。他們有不同的計劃,如標準、專業和高級,允許存取所有專案。我推薦 **[標準計劃](https://datacamp.pxf.io/c/1193463/1012793/13294?u=https%3A%2F%2Fwww.datacamp.com%2Fpricing)** 因為它是正確的-價格合理,您可以獲得提高資料技能的所有必需品。 [![適合初學者的最佳免費Python課程](https://blogger.googleusercontent.com/img/a/AVvXsEhZ1TxSJhyjGvc8q5E6AE4d8YXovCdy4RuU75uZWx2ISudjW64QLyZgNCiERoA0Ee3HYjmW64QLyZgNCiERoA0Ee3HYjmW64QLDgNCiERo YAx yjX5tcmFxfSsiOOFfnIG6ta66ZtpgGFbs-m2KQpxFHdNxvlFyLbwk0hBfD-MhIWXo0fDjmbXsyli9EzQ=w395-h250)](https://datacampc. /c/1193463 /1012793/13294?u=https%3A%2F%2Fwww.datacamp.com%2Fcourses%2Fintro-to-python-for-data-science) -------- ###**5\. [CodeCademy](https://bit.ly/codecademypro)** 如果您喜歡互動式學習,那麼沒有比 CodeCademy 更好的地方了。他們首先用盡可能少的單字教你理論,然後要求你使用該概念在線上編寫程式碼。最好的事情是您不需要進行任何設置,例如在電腦上安裝 Python。 您可以直接從瀏覽器執行 Python 程式碼。另一個好處是,在準備好之前,您不需要編寫完整的程式。您需要進行一些小的更改並執行它們。這是學習 Python 程式設計的一種很棒且有趣的方式。 我使用他們的互動式平台學習了 JavaScript、Java、Python 和 Linux。早些時候,他們是完全免費的,但現在他們有免費增值模式,其中一些課程或課程僅對付費會員開放。 目前,他們的[**學習Python 2**](https://bit.ly/learnpython2withcodecademy)課程是免費的,而[**Python 3課程**](https://bit.ly/learnpython3codecademy)只是免費的可供付費會員使用。如果您負擔得起並欣賞 CodeCademy,請務必訂閱,但如果您無法從他們的 Python 2 課程開始,那麼它非常適合沒有編碼經驗的初學者。 [![線上學習Python的免費互動課程](https://1.bp.blogspot.com/-lSHI8IMKqGA/XX46dg1vjwI/AAAAAAAAaX8/4O1-n9jcl5YT47zh02TOYIdGA87j3AOxQCLcBGAsYhon/T47zh02TOYIdGA87j3AOxQCLcBGAsYhon/s4007% PG )](https://bit.ly/codecademypro) ----- ###**6\. [Google 的 Python 課程](https://developers.google.com/edu/python/)** 如果你不知道,Google 也為初學者提供了一套優秀的 Python 教程,稱為 Google 的 Python 課程。這是一個免費課程,適合有一點程式設計經驗並且想要學習 Python 的人。課程包括書面教程、講座影片和大量練習 Python 編碼的程式碼練習。 第一個練習涉及字串和清單等基本 Python 概念,為下一個練習奠定基礎,下一個練習是處理文字檔案、進程和 http 連接的完整程序。 Google 本身的許多專案都使用 [Python](https://javarevisited.blogspot.com/2018/05/10-reasons-to-learn-python-programming.html)。而且,這些材料通常在 Google 內部用於向剛開始編碼或幾乎沒有程式設計經驗的人教授 Python。 本材料最好的部分是 YouTube 上提供講座影片。因此您不需要任何其他帳戶。它還教您設定自己的[Python開發環境](https://medium.com/better-programming/top-5-courses-to-learn-python-in-2018-best-of-lot-26644a99e7ec ),這確實帶來了最初的挑戰,但從長遠來看是很好的。 ------ ###**7\. [微軟的免費Python課程](https://www.awin1.com/cread.php?awinmid=6798&awinaffid=631878&clickref=&p=%5B%5Bhttps%3A%2F%2Fwww.edx.org%2Fcourse%2Fintroduction-to - python-absolute-beginner-4%5D%5D)** 如果谷歌也有Python課程,微軟怎麼會落後呢?嗯,它還在 Edx(另一個流行的免費教育線上入口網站)上提供免費的 Python 課程。本課程名為“Python 簡介:絕對初學者”,是一門學習 Python 的免費課程,由高級內容開發人員 Eric Camplin 教授。 本課程將在Jupyter Notebooks 中教您Python,這是一個基於瀏覽器的線上[Python] 編碼編輯器(https://hackernoon.com/top-5-courses-to-learn-python-in-2018- best-of- lot-26644a99e7ec),這表示你不需要安裝Python。這是一個為期 5 週的課程,每週學習 3 到 4 個小時。 本課程也是 Microsoft 入門級軟體開發專業計劃的一部分,該課程也是免費的。您只需在需要認證時付費。您可以將其新增至您的履歷或 LinkedIn 個人資料中,如下所示: [![免費最佳Python認證課程](https://1.bp.blogspot.com/-1U2amxNH280/XX47hDYf-3I/AAAAAAAAaYk/Xa8Se2JHrmca1AbXy81ILDQofQW4KyAzwCLcBGAsAsYHQ/s400/IntrodBPY free % 2B課程.png)](https://javarevisited.blogspot.com/2018/03/top-5-courses-to-learn-python-in-2018.html) ------ ###8。 [學習 Python - FreeCodeCamp 的初學者完整課程 [教程]](https://www.youtube.com/watch?v=rfscVS0vtbw) 本課程將向您全面介紹 Python 中的所有核心概念。跟著影片一起學習,您很快就會成為 Python 程式設計師!您可以在 YouTube 上免費觀看,這是目錄 ⭐️內容⭐ ⌨️ (0:00) 簡介 ⌨️ (1:45) 安裝 Python 和 PyCharm ⌨️ (6:40) 設定和 Hello World ⌨️ (10:23) 繪製形狀 ⌨️ (15:06) 變數和資料類型 ⌨️ (27:03) 使用字串 ⌨️ (38:18) 處理數字 ⌨️ (48:26) 取得使用者的意見 ⌨️ (52:37) 建構一個基本計算器 ⌨️ (58:27) 瘋狂自由遊戲 ⌨️ (1:03:10) 列表 ⌨️ (1:10:44) 列表函數 ⌨️ (1:18:57) 元組 ⌨️ (1:24:15) 功能 ⌨️ (1:34:11) 退貨聲明 ⌨️ (1:40:06) If 語句 ⌨️ (1:54:07) If 語句與比較 ⌨️ (2:00:37) 建構一個更好的計算器 ⌨️ (2:07:17) 字典 ⌨️ (2:14:13) While 循環 ⌨️ (2:20:21) 建構一個猜謎遊戲 ⌨️ (2:32:44) For 循環 ⌨️ (2:41:20) 指數函數 ⌨️ (2:47:13) 2D 清單和嵌套循環 ⌨️ (2:52:41) 建構翻譯器 ⌨️ (3:00:18) 評論 ⌨️ (3:04:17) 嘗試/例外 ⌨️ (3:12:41) 讀取文件 ⌨️ (3:21:26) 寫入文件 ⌨️ (3:28:13) 模組和 Pip ⌨️ (3:43:56) 類別與物件 ⌨️ (3:57:37) 建構多項選擇測驗 ⌨️ (4:08:28) 物件函數 ⌨️ (4:12:37) 繼承 ⌨️ (4:20:43) Python 解譯器 ![免費 Python 教學與平台](https://thepracticaldev.s3.amazonaws.com/i/zu01vymg8iberknzdeb8.png) ---- 這就是一些 **您可以免費學習 Python 的網站**。所有這些都是很棒的資源,您可以選擇您喜歡的資源。您不需要註冊所有這些,這將是荒謬且耗時的。相反,選擇最適合您的學習風格的一種。 例如,如果您喜歡互動式學習,請選擇[CodeCademy](https://javarevisited.blogspot.com/2019/09/codecademy-vs-udemy-vs-onemonth-which-is-better-for-learning-code . html),如果您喜歡非正規影片課程,那麼選擇 Udemy;如果您喜歡大學和學校等結構化教育,那麼選擇 Coursera。 而且,如果您喜歡基於文字的學習,請記住閱讀比觀看影片更快,那麼 Google 的 Python 課程是最好的。 除此之外,Scrimba 是另一個免費學習 Python 程式設計的好地方。 您可能喜歡的其他 **Python 文章和資源** - [2024年學習Python的10個原因](https://javarevisited.blogspot.com/2018/05/10-reasons-to-learn-python-programming.html) - [Python初學者學習Python的5大課程](https://javarevisited.blogspot.com/2018/03/top-5-courses-to-learn-python-in-2018.html) - [Python 開發者最喜歡的 5 個 Web 開發框架](https://javarevisited.blogspot.com/2019/04/top-5-python-web-development-frameworks.html) - [Python 與 JavaScript - 哪個更好開始?](https://javarevisited.blogspot.com/2019/05/python-vs-javascript-which-programming-language-beginners-should-learn.html) - [10門免費線上課程深入學習Python](https://javarevisited.blogspot.com/2018/12/10-free-python-courses-for-programmers.html) - [資料科學與機器學習的 8 個最佳 Python 函式庫](https://javarevisited.blogspot.com/2018/10/top-8-python-libraries-for-data-science-machine-learning.html) - [Python vs Java - 初學者應該學習哪種程式語言?](https://javarevisited.blogspot.com/2018/06/java-vs-python-which-programming-language-to-learn-first.html ) - [5 Python 資料科學與機器學習課程](https://javarevisited.blogspot.com/2018/03/top-5-data-science-and-machine-learning-online-courses-to-learn-online . html) - [完整的 Web 開發者路線圖](https://hackernoon.com/the-2019-web-developer-roadmap-ab89ac3c380e) - [10本針對程式設計師的免費Python程式設計書籍](http://www.java67.com/2017/05/top-7-free-python-programming-books-pdf-online-download.html) - [資料科學的 5 本 Python 書籍](https://javarevisited.blogspot.com/2019/08/top-5-python-books-for-data-science-and-machine-learning.html) 感謝您到目前為止閱讀這篇文章。如果您喜歡這些網站,請與您的朋友和同事分享。如果您有任何問題或回饋,請留言。 一切順利。 **P。 S. -** 如果你此刻只想做一件事來開啟你的 Python 程式設計之旅,那就加入 **[完整的 Python 3 Bootcamp](http://bit.ly/complete-python3-bootcamp ) ** Jose Portilla 在Udemy 上的課程。您將快速學習 Python 並且永遠不會後悔您的決定。 --- 原文出處:https://dev.to/javinpaul/top-5-places-to-learn-python-programming-for-free-m4c

全端工具箱🧰:Python🐍版

## 簡介 以下是專注於全端開發不同面向的 Python 函式庫;有些專注於 Web 應用程式開發,有些專注於後端,有些則兩者兼而有之。 ![Gif引言](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8eodxlxdlgr0xh6lrvhz.gif) --- ## 1. [Taipy](https://github.com/Avaiga/taipy) Taipy 是一個開源 Python 庫,用於建立生產就緒的應用程式前端和後端。 它旨在加快應用程式開發,從最初的原型到生產就緒的應用程式。 🔑特點: - 豐富的互動性 - 為您的佈局、樣式等提供更多自訂功能(無需 CSS) - 多頁和多用戶應用程式 - 圖形管道編輯器 - 與 Taipy 前端功能整合以實現端到端部署 - 日程安排 - 筆記型電腦相容 ![Taipy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1rk0w837hwaq0akybw6o.png) --- ![QueenB GIF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0un08vhstrk6zpst5yti.gif) https://github.com/Avaiga/taipy 您的支持意義重大🌱,並且在許多方面為我們帶來了很大的幫助,例如寫文章! 🙏 --- ## 2.[Prefect](https://github.com/PrefectHQ/prefect) Prefect 是一個提供資料管道開發的框架。 它透過注重簡單性和用戶友好性而與 Airflow 等競爭對手區分開來。 如果您想要一個具有各種功能但比 Airflow 更容易學習的成熟產品,Prefect 是一個很好的中間產品。 🔑特點: - 直覺且使用者友好的控制面板 - 快取等智慧功能 - 以流程為基礎的架構,可輕鬆融入工作流程組織 - 動態參數化與依賴管理 - 混合執行(本地/雲端) ![完美](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p94g40hwv04gkfmvklda.png) --- ## 3. [Streamlit](https://github.com/streamlit/streamlit) Streamlit 是一個遊戲規則改變者,可以快速、簡單地建立 Web 應用程式。 此資料應用程式框架將其從更完整、更複雜的解決方案轉變為快速、用戶友好的解決方案。 🔑特點: - Pythonic框架 - 互動式小部件 - 使用者友善性 ![Streamlit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddntytwa95tgp37kykcc.png) --- ## 4.[氣流](https://github.com/apache/airflow) Apache Airflow 是一個開源平台,專為調度和監控工作流程而設計。 它是編排複雜資料管道和 ETL 流程的絕佳選擇,因為它在過去十年中一直是管道領域的重要參與者。 該庫的完整性與陡峭的學習曲線有關。 🔑特點: - 基於DAG的工作流程定義 - 包括 DAG 視覺化的完整介面,可追蹤故障並管理重試。 - 各種集成 - 動態任務執行和調度 - 以Python為中心的設計 - 社區支持 ![氣流](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lm89iyieg07c2rgzm2jb.png) --- ## 5. [Brython](https://github.com/brython-dev/brython) Brython 將 Python 帶到了前端,因為這個名字重新組合了「瀏覽器」和「Python」。 它引入了直接在 Web 應用程式中執行 Python 程式碼的獨特概念。 這種創新方法可讓您使用 Python 建立互動式 Web 體驗。 🔑特點: - 易於集成,因為它將 Python 邏輯直接整合到瀏覽器中 - 相容於不同的網頁瀏覽器 ![Brython](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9a7w7lqd9b5rtpahnolr.png) --- ## 6. [達世幣](https://github.com/plotly/dash) Dash 由 Plotly 建立,是一個利用 Plotlys 元件完整性的 Web 應用程式框架。 眾所周知,它是一個完整的解決方案,因此需要更長的時間來掌握。 🔑特點: - 基於元件的架構 - 強大的互動式儀表板 - 即時資料更新 ![破折號](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yp5xw24ulak6mnwwos69.png) --- ## 7. [Dagster](https://github.com/dagster-io/dagster) Dagster 是本次編譯中較新的函式庫之一,它是一個雲端原生資料管道編排,旨在統一資料整合、工作流程編排和監控。 與其他工具相比,Dagster 強調工作流程建立和管理的 DataOps 面向。 🔑特點: - 聲明式管道設置 - 固執己見的結構 - 版本控制 - 與 Hadoop 集成 - 全面的元資料跟踪 ![Dagster](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hz78m7rein4asbyp9buf.png) --- ## 8. [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) SQLAlchemy 是一個用於處理資料庫互動的 Python 函式庫。 它是一個連接Python程式設計和關係資料庫的多功能工具包。 掌握這個庫的時間較長,但好處是實實在在的。 🔑特點: - SQL 表達式語言 - 物件關係映射(ORM) - 對資料庫的廣泛支持 - 複雜查詢支持 ![SQL](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/owtvt0kv5gt45r33ulra.png) --- ## 9. [芹菜](https://github.com/celery/celery) Celery 是一個 Python 函式庫,用作建立後端應用程式的框架。 它處理分散式任務處理、作業執行和工作流程。 它是一個完整的解決方案和廣泛的功能集,具有更陡峭的學習曲線。 🔑特點: - 平行處理 - 可擴展性 - 任務調度 - 非同步處理 ![芹菜](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p50my0xtbncfskb4y1ga.png) --- ## 10. [Peewee](https://github.com/coleifer/peewee) 與 SQAlchemy 一樣,Peewee 是一個簡化資料庫互動的 Python 函式庫。 它與競爭對手的不同之處在於用戶友好且簡單的設定。 該庫是中小型專案的最佳選擇。 🔑特點: - 輕量級框架 - 資料庫支持 - Python式文法 ![Peewee](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8tmvc7y9vkb2mdpj88jq.png) --- ## 11. [Kedro](https://github.com/kedro-org/kedro) Kedro 是一個開源 Python 框架。 它為生產就緒的資料科學管道提供了一個工具箱。 事實上,Kedro 可以輕鬆地與成熟的 Python ML 庫集成,並提供統一的方法來實現端到端框架。 🔑特點: - 資料目錄 - 筆記本集成 - 專案模板 - 有自己的觀點,因為它強制執行特定的約定 ![Kedro](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x2z12ofiibpd5c9ggs7s.png) --- ## 結論 Python 被認為是人工智慧和機器學習應用程式的首選語言。 為了簡化和自動化這些應用程式,Python 見證了框架庫的出現。 這些程式庫有助於 Web 應用程式開發、後端服務以及開源全端應用程式的建立。 --- ![新秀圖片](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/25oebh36ha622u74kpli.gif) 我剛開始我的內容創作之旅;如果您有任何問題或回饋,請隨時與我們聯繫! --- 原文出處:https://dev.to/taipy/full-stack-toolbox-python-edition-20h2

Docker 絕對初學者

Docker 是一個工具,允許開發人員將他們的應用程式及其所有依賴項打包到一個容器中。然後,這個容器就可以輕鬆地在任何安裝了 Docker 的機器上傳輸和執行,而不必擔心環境的差異。這就像是打包和執行軟體的標準化方式。 **容器是什麼?** ![Docker 容器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/imsgbstga86vnxjwgebr.png) 容器就像一個小包,其中包含程式執行所需的一切,可以輕鬆地在不同電腦上移動和執行,而不會造成任何麻煩。 最酷的部分是這個迷你電腦(容器)就像一個披著斗篷的超級英雄。它可以在任何電腦上執行,無論它們有多麼不同,因為它自帶特殊的環境。這是一種保持軟體井然有序的方式,並確保它無論在哪裡都能以相同的方式運作。 ![容器](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1z5zcued8ya2onerpzwt.png) **為什麼我們需要 Docker?** 1. **一致性:** Docker 確保軟體在您的電腦、您朋友的電腦或任何電腦上以相同的方式運作。它使事情保持一致。 2. **可移植性:** 您可以將您的軟體及其朋友打包到 Docker 容器中,並且它可以移動到任何地方。這就像將您的遊戲及其所有規則放在手提箱中並在朋友家中玩。 3. **隔離:** Docker 容器就像小氣泡。氣泡內發生的事只會留在氣泡內。這意味著容器中的一個程式不會幹擾容器外的另一個程式。 4. **效率:** Docker有助於節省電腦資源。您可以讓許多容器在同一台電腦上執行,而不會相互妨礙,而不是讓一整台電腦只用於一個程式。 5. **速度:** Docker 讓啟動、停止和共享軟體變得快速、輕鬆。這就像打開和關閉遊戲機一樣 - 快速而簡單。 **什麼是 Docker 映像?** ![Docker 映像](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/817t1rsad728snnighkj.png) Docker 映像像是程式及其運作所需的所有內容的快照。它是一個打包版本,包括程式碼、工具和設置,就像包含所有成分的餅乾食譜的快照一樣。 **圖像是配方,容器是當您按照該配方實際製作和執行程序時所得到的。** **一些基本的 Docker 命令。** ![基本 Docker 指令](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xq1pwt8896lvster3ppl.png) 1. **`docker執行nginx`** - 此命令告訴 Docker 使用「nginx」映像執行容器。這就像告訴 Docker 啟動一個預製程式的新實例(nginx,它是一個 Web 伺服器)。 2. **`docker ps`** - 顯示正在執行的容器的清單。這就像檢查當前正在執行哪些程式。 3. **`docker ps -a`** - 顯示所有容器的列表,包括已停止的容器。這就像檢查您執行過的所有程式的歷史記錄。 4. **`docker stopsilly_sammet'** - 停止名為「silly_sammet」的正在運作的容器。這就像關閉當前正在執行的程式。 5. **`docker rmsilly_sammet'** - 刪除名為「silly_sammet」的已停止容器。這就像丟掉你不再需要的程式的指令一樣。 6. **`docker 映像`** - 列出您擁有的所有 Docker 映像。這就像查看您可以執行的所有不同程式的選單一樣。 7. **`docker rmi nginx`** - 刪除“nginx”圖像。這就像刪除您不想再使用的程式的配方。 8. **`docker拉nginx`** - 從網路下載「nginx」映像。這就像從食譜中獲取新食譜一樣。 9. **`docker 執行 ubuntu sleep 5`** - 使用「ubuntu」映像檔執行容器並使其休眠 5 秒。這就像啟動一個程序,只是等待一小會兒,然後就停止了。 10. **`docker exectracted_mcclintock cat /etc/hosts`** - 在名為「distracted_mcclintock」的正在執行的容器內執行命令。這就像在食譜書中偷看特定頁面一樣。 11. **`docker run -d kodekloud/simple-webapp`** - 從「kodekloud/simple-webapp」鏡像以分離模式執行容器。這就像啟動一個程式並讓它在背景執行。 12. **`docker Attach a043d`** - 將您的終端附加到 ID 為「a043d」的正在執行的容器。這就像跳入正在執行的程式來查看發生了什麼。 **一些 Docker 概念:** 1. **使用標籤執行:** - 標籤就像程式的版本。它指定您要執行哪個版本。 - 範例程式碼:`docker run nginx:latest` - 這將執行最新版本的 Nginx 程式。 2. **使用標準輸入執行:** - STDIN 就像在鍵盤上打字一樣。有些程式需要您的輸入。 - 範例程式碼:`docker run -i -t ubuntu` - 這會在 Ubuntu 容器內執行互動終端,讓您可以鍵入命令。 3. **使用連接埠映射執行:** - 連接埠就像門。程式使用它們與外界進行通訊。 - 範例程式碼:`docker run -p 8080:80 nginx` - 這將執行 Nginx,並打開電腦連接埠 8080 上的門,將其連接到容器的連接埠 80。 4. **使用磁碟區映射執行:** - 磁碟區就像共用資料夾。它們讓您可以將東西存放在容器之外。 - 範例程式碼:`docker run -v /your/local/folder:/container/folder nginx` - 這將執行 Nginx 並將電腦上的資料夾連接到容器內的資料夾。 5. **檢查容器:** - 檢查就像仔細檢查正在執行的程式。 - 範例程式碼:`docker檢查container_name` - 這為您提供有關正在執行或已停止的容器的詳細資訊。 6. **容器日誌:** - 日誌就像日記。他們記錄程式正在做什麼。 - 範例程式碼:“docker 日誌容器名稱” - 這會向您顯示特定容器的日誌或活動。 ##環境變數 環境變數就像程式用來尋找重要資訊的便利筆記,有點像是程式可以理解和更好工作的秘密訊息! 1. **Python腳本(app.py)中的環境變數:** - 假設您有一個用 Python 寫的程式 (app.py)。您可能想要在不更改程式碼的情況下自訂它。您可以使用環境變數。 - 範例程式碼(app.py): ``` import os app_color = os.getenv("APP_COLOR", "default_color") print(f"The app color is {app_color}") ``` - 正常運作腳本:`python app.py` - 以特定顏色執行:`export APP_COLOR=blue; python 應用程式.py` 2. **在 Docker 中使用 ENV 變數:** - Docker 容器也可以使用環境變數。這就像是向容器內的程式發出指令。 - 範例程式碼: - `docker run -e APP_COLOR=green simple-webapp-color` - 這會執行 Docker 容器(`simple-webapp-color`)並將環境變數 `APP_COLOR` 設為「綠色」。 3. **檢查環境變數:** - 有時,您會想要檢查正在執行的容器正在使用哪些環境變數。 - 範例程式碼:`docker檢查blissful_hopper` - 此命令提供有關名為“blissful_hopper”的容器的詳細訊息,包括其環境變數。 簡單來說,環境變數就像程式(或 Docker 容器)可以讀取以了解如何行為的小註釋。您可以在執行程式之前設定這些註釋,程式將使用它們來自訂自身。第二個範例中的「export」指令就像在執行程式之前寫一條註釋,告訴它如何運作。 “docker Inspect”指令就像是在容器內部查看它有什麼註解。 ## Docker 映像 **Docker 檔案:** Dockerfile 就像是 Docker 建立映像的一組指令。這就像是烤蛋糕的食譜。 ``` # Use the Ubuntu base image FROM Ubuntu # Update apt repository RUN apt-get update # Install dependencies using apt RUN apt-get install -y python # Install Python dependencies using pip RUN pip install flask RUN pip install flask-mysql # Copy source code to /opt folder COPY . /opt/source-code # Set the working directory WORKDIR /opt/source-code # Specify entry point to run the web server ENTRYPOINT ["flask", "run"] ``` **建立自己的圖像的步驟:** 1. 使用上述內容建立一個名為「Dockerfile」的檔案。 2. 將其保存在與原始碼相同的目錄中。 **建置 Docker 映像:** 在終端機中執行以下命令: ``` docker build -t your-image-name . ``` 此命令告訴 Docker 使用目前目錄中的 Dockerfile (`.`) 建置映像,並使用您選擇的名稱對其進行標記 (`-t your-image-name`)。 **分層架構:** ![分層架構](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9i91e79dg61wnbbfp62m.png) - 將 Docker 映像視為一個分層蛋糕。 Dockerfile 中的每個指令都會在映像上新增一層。 - 圖層可重複使用。如果您變更程式碼中的某些內容,Docker 只會重建受影響的層,從而提高效率。 **Docker 建置輸出:** - 當您建置映像檔時,Docker 會顯示流程中的每個步驟。如果發生故障,它會給您錯誤訊息。 **你可以容器化什麼?** - 幾乎所有東西!應用程式、服務、資料庫、網站,基本上任何軟體都可以容器化。 - 這就像將您的軟體放入一個盒子中,以便它可以在任何地方執行而不會造成麻煩。 ## 什麼是 Docker CMD 與 ENTRYPOINT **Docker 中的`CMD`:** - 將 CMD 視為啟動容器時程式執行的預設操作。 - 這就像說,“嘿,當你執行這個容器時,默認執行此操作。” - 範例:`CMD ["flask", "run"]` 表示當容器啟動時,它會自動執行 Flask Web 伺服器。 **CMD 範例:** ``` FROM alpine CMD ["sleep", "5"] ``` 在此範例中,當您使用此映像執行容器時,它會自動休眠 5 秒。 **Docker 中的`ENTRYPOINT`:** - 將 ENTRYPOINT 視為容器所做的主要事情。就好像boss的命令一樣。 - 它設定一個預設應用程式在容器啟動時執行,但您仍然可以根據需要覆蓋它。 - 範例:`ENTRYPOINT ["flask", "run"]` 表示容器主要用於執行 Flask Web 伺服器,但如果需要,您仍可新增更多指令。 **入口點範例:** ``` FROM alpine ENTRYPOINT ["sleep"] CMD ["5"] ``` 在這裡,主要目的是睡眠,如果您願意,您仍然可以覆蓋睡眠持續時間。 在這兩種情況下,容器在啟動時只會休眠幾秒鐘。主要區別在於如何提供參數以及它們是否可以輕鬆覆蓋。 CMD 就像在說,“這是默認要做的事情”,而 ENTRYPOINT 就像在說,“這是主要要做的事情,但如果你願意,你可以稍微調整一下。”它們都有助於定義容器啟動時執行的操作。 ## Docker 中的網路: Docker 網路幫助容器(程式)相互通信,確保它們可以順利地協同工作。 **預設網路:** - Docker 建立預設網路供容器通訊。 - 範例程式碼:`docker run ubuntu --network=host` - 這使用主機網路執行 Ubuntu 容器,這意味著它與主機共享網路命名空間。 **使用者定義的網路:** - 您可以建立自己的網路以更好地組織和控制。 - 範例程式碼: ``` docker network create --driver=bridge --subnet=182.18.0.0/16 custom-isolated-network ``` - 這將建立一個名為「custom-isolated-network」的使用者定義的橋接網絡,具有特定的子網。 **上市網路:** - 您可以查看您擁有的所有網路。 - 範例程式碼:`docker network ls` **檢查網路:** - 您可以檢查特定網路的詳細資訊。 - 範例程式碼:`docker網路檢查blissful_hopper` - 這顯示有關名為「blissful_hopper」的網路的詳細資訊。 **嵌入式 DNS:** - Docker 有一個內建的 DNS 系統,供容器透過名稱相互查找。 - 範例程式碼:`mysql.connect(mysql)` - 這可能是程式碼中的一行,其中名為「mysql」的服務使用 Docker 的 DNS 連接到另一個名為「mysql」的服務。 ## Docker 儲存: ![Docker 儲存](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7i54a6m0o1tb0812xbjk.jpg) Docker 儲存就像使用容器時決定將資料保存在哪裡一樣。您可以將它們保留在容器內,使用磁碟區在容器之間共用它們,或將它們儲存在容器外部以妥善保管。 **Docker中的檔案系統:** - Docker 使用分層架構來建立映像。 Dockerfile 中的每個指令都會在檔案系統中新增一個新圖層。 ``` # Dockerfile FROM Ubuntu RUN apt-get update && apt-get install -y python RUN pip install flask flask-mysql COPY . /opt/source-code WORKDIR /opt/source-code ENTRYPOINT ["flask", "run"] ``` - Dockerfile 中的層: - 第 1 層:Ubuntu 基礎層 - 第 2 層:apt 軟體包的更改 - 第 3 層:pip 套件的變化 - 第 4 層:原始碼 - 第 5 層:使用「flask」指令更新入口點 - 第 6 層:容器層 **影像圖層:** - 當您建立 Docker 映像時,它由唯讀層組成。每一層代表影像的變化或加入。 - 第 1 層:Ubuntu 基礎層 - 第 2 層:apt 軟體包的更改 - 第 3 層:pip 套件的變化 - 第 4 層:原始碼 - 第 5 層:使用「flask」指令更新入口點 ``` # Build the Docker image docker build -t mmumshad/my-custom-app . ``` **容器層:** - 當您執行 Docker 容器時,會在唯讀映像層上方新增一個讀寫層。該層特定於正在執行的容器。 - 第 6 層. 容器層 ``` # Run the Docker container docker run mmumshad/my-custom-app ``` **數量:** - 卷是一種在容器外部保存資料的方法。它們就像外部記憶體。 ``` # Create a Docker volume docker volume create data_volume # Use the volume in a container docker run -v data_volume:/var/mysql mysql ``` - 您也可以使用「-v」將特定目錄從主機掛載到容器: ``` # Mount a host directory to a container directory docker run -v /path/on/host:/var/mysql/mysql -d mysql ``` - docker run --mount 指令用於將主機上的特定目錄或檔案掛載到正在執行的 Docker 容器中。 ``` docker run --mount type=bind,source=/mysql,target=/var/mysql mysql ``` **儲存驅動程式:** - Docker 使用儲存驅動程式來管理資料的儲存和存取方式。一些常見的儲存驅動程式包括 AUFS、ZFS、BTRFS、Device Mapper、Overlay 和 Overlay2。 [在 Docker 管理資料](https://docs.docker.com/storage/) [關於儲存驅動程式](https://docs.docker.com/storage/storagedriver/) [卷](https://docs.docker.com/storage/volumes/) ## Docker 組合 ![Docker Compose](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yjyro6o2844s2or1b83c.jpeg) Docker Compose 是一個方便的工具,可幫助您輕鬆執行和連接不同的軟體服務,就好像它們都是同一事件的一部分一樣。 **Docker Compose 基礎:** 1. **執行單一容器:** - 通常,您可以像這樣執行單獨的 Docker 容器: ``` docker run mmumshad/simple-webapp docker run mongodb docker run redis:alpine docker run ansible ``` 2. **Docker 撰寫文件(`docker-compose.yml`):** - Docker Compose 允許您在一個簡單的檔案中定義所有這些服務: ``` # docker-compose.yml version: '3' services: web: image: 'mmumshad/simple-webapp' database: image: 'mongodb' messaging: image: 'redis:alpine' orchestration: image: 'ansible' ``` - 此檔案描述您要執行的服務(「web」、「database」、「messaging」、「orchestration」)、它們各自的映像以及任何其他配置。 3. **使用 Docker Compose 執行:** - 要一起啟動所有這些服務: ``` docker-compose up ``` - Docker Compose 負責啟動「docker-compose.yml」檔案中定義的所有容器。 4. **使用 Docker Compose 建置:** - 您也可以使用 Docker Compose 建置映像: ``` docker-compose build ``` - 此指令建置「docker-compose.yml」檔案中指定的映像。 **執行連結容器:** - 如果您要透過連結執行單一容器: ``` docker run -d --name redis redis docker run --name voting-app -p 5000:80 --link redis:redis voting-app docker run --name result-app -p 5001:80 --link db:db result-app docker run -d --name worker --link db:db --link redis:redis worker ``` - 在 Docker 中撰寫: ``` # docker-compose.yml version: '3' services: vote: image: 'voting-app' ports: - '5000:80' links: - 'redis:redis' result: image: 'result-app' ports: - '5001:80' links: - 'db:db' worker: image: 'worker' links: - 'db:db' - 'redis:redis' db: image: 'db' redis: image: 'redis' ``` Docker Compose 可讓您在單一檔案中描述整個應用程式堆疊,從而輕鬆管理、執行和連接不同的服務。這就像在一份計劃中寫下活動的所有任務,然後 Docker Compose 為您處理設定。 [Docker Compose 概述](https://docs.docker.com/compose/) [Docker 撰寫文件](https://docs.docker.com/engine/reference/commandline/compose/) ## Docker 註冊表 ![Docker 註冊表](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bzflp82qyg36y8fcf8k8.png) Docker 註冊表是人們儲存和分享 Docker 映像的地方,使其他人可以輕鬆使用和執行他們的軟體。它就像一個大型線上程式庫,可以輕鬆下載並在不同電腦上使用。 **Docker 註冊表基礎知識:** 1. **公共登記處:** - Docker 映像可以在 Docker Hub 等公共註冊表中儲存和共用。 - 例: ``` docker pull nginx ``` 2. **私人登記處:** - 有時,您可能希望將圖像保存在您自己的私人註冊表中。 - 例: - 登入私人註冊表: ``` bash docker login private-registry.io ``` - 從私有註冊表中的映像執行容器: ``` docker run private-registry.io/apps/internal-app ``` 3. **部署您自己的私有註冊表:** - 您可以為您的團隊或公司部署自己的私人註冊表。 - 例: - 在您的電腦上執行私有註冊表: ``` docker run -d -p 5000:5000 --name registry registry:2 ``` - 為私人註冊表標記您的圖像: ``` bash docker image tag my-image localhost:5000/my-image ``` - 將映像推送到您的私人註冊表: ``` bash docker push localhost:5000/my-image ``` - 從您的私人註冊表中提取映像: ``` bash docker pull localhost:5000/my-image ``` 4. **從遠端私有註冊表中提取:** - 您也可以使用 IP 位址或網域從遠端私有註冊表中提取映像。 - 例: ``` docker pull 192.168.56.100:5000/my-image ``` Docker 註冊表就像一個儲存空間,人們在其中保存和共享 Docker 映像。您可以將公用註冊表用於廣泛使用的映像,也可以根據您的特定需求設定自己的私人註冊表。它就像一個用於共享和儲存軟體藍圖(圖像)的特殊庫。 ## Docker 引擎 ![Docker 引擎](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hc6r4dvo9kg9xqgwsrbb.jpg) 想像一下,你有一個魔盒(Docker Engine),可以為你執行和管理各種程式(容器)。 Docker Engine 就像是這個魔盒的大腦。 1. **Docker 守護程式:** - 守護程式就像魔法盒的看門人。它始終在那裡,隨時準備接受指示並確保一切順利進行。 2. **REST API:** - 將 REST API 視為一組允許您與魔盒對話的規則。它就像你和守護程式用來溝通的語言。你告訴守護程式要做什麼,它會理解,因為你們說的是同一種語言。 3. **Docker CLI(命令列介面):** - Docker CLI 就像是用來命令守護程式的魔杖。您輸入指令,守護程式就會按照您的指示進行操作。這就像說「Abracadabra」就能讓事情發生。 **連線到遠端 Docker 引擎:** 連接到遠端 Docker 引擎可讓您控制另一台機器上的容器,且設定約束可確保容器僅使用指定的資源。 1. **Docker主機IP:** - 您可以使用 IP 位址和連接埠連接到不同電腦上的 Docker 引擎。 - 例: ``` docker -H=remote-docker-engine:2375 run nginx ``` - 這告訴您的本機 Docker CLI 與遠端 Docker 引擎進行通訊。 2. **有約束地執行容器:** - Docker 允許您設定容器的資源限制,例如 CPU 和記憶體限制。 - 例: ``` docker run --cpus=0.5 ubuntu docker run --memory=100m ubuntu ``` - 這些指令限制容器僅使用半個 CPU 核心和 100 MB 記憶體。 當然,讓我們簡化一下PID命名空間的概念: **命名空間PID:** PID 命名空間可讓您為容器中的進程(如程式或任務)建立單獨的區域,因此它們有自己的一組「票號」(進程 ID),不會與容器外的進程發生衝突。 **範例程式碼:** 1. **使用主機 PID 命名空間執行容器:** - 這表示容器與主機共用相同的「票號」。 ``` docker run --pid=host ubuntu ``` 2. **執行具有隔離 PID 命名空間的容器:** - 這表示容器有自己的一組獨立於主機的「票號」。 ``` docker run --pid=container ubuntu ``` 在第一個範例中,容器與進程交互,就好像它與主機位於同一空間中一樣。在第二個範例中,容器有自己的進程隔離空間。這就像在大型活動中擁有一個私人區域,您的團隊有自己的一套票號,讓您可以獨立於活動的其餘部分進行操作。 **容器化概念:** 1. **進程 ID 命名空間:** - 容器有自己獨立的流程 ID (PID) 空間,因此容器內的流程與容器外的流程是分開的。 - 例: ``` docker run --pid=host ubuntu ``` - 此指令使用主機的 PID 命名空間來執行容器,因此它共用相同的程序。 2. **網路命名空間:** - 容器也有自己獨立的網路命名空間,這意味著它們可以有自己的網路配置。 - 例: ``` docker run --net=host nginx ``` - 此指令使用主機的網路命名空間來執行容器。 3. **Unix分時命名空間:** - 此命名空間允許容器擁有自己的時間視圖,與主機和其他容器分開。 - 例: ``` docker run --uts=host ubuntu ``` - 此指令使用主機的 Unix 時間共用命名空間來執行容器。 4. **進程間掛載命名空間:** - Mount命名空間隔離檔案系統,讓容器擁有自己的檔案系統視圖。 - 例: ``` docker run --mount=type=bind,source=/host/folder,target=/container/folder ubuntu ``` - 此指令將主機中的資料夾安裝到容器中。 當然!我們來簡化一下cgroup的概念: **C組:** cgroup(控制組的縮寫)可協助在不同進程或容器之間管理和分配系統資源,例如 CPU 和記憶體。它們確保沒有任何一個進程或容器耗盡所有可用資源,從而保持一切平衡。 **範例程式碼:** 1. **使用 Cgroup 設定 CPU 限制:** - 這就像說聚會上的每位客人只能吃一定數量的食物。 ``` docker run --cpus=0.5 ubuntu ``` - 這限制容器僅使用一半的 CPU 核心。 2. **使用 Cgroup 設定記憶體限制:** - 這就像說每位客人只能在舞池上佔據一定的空間。 ``` docker run --memory=100m ubuntu ``` - 這限制容器僅使用 100 MB 記憶體。 [Docker 引擎概述](https://docs.docker.com/engine/) [使用 Docker Engine API 進行開發](https://docs.docker.com/engine/api/) [執行時指標](https://docs.docker.com/config/containers/runmetrics/#control-groups) ## Linux容器與Windows容器的概念: **Linux 容器(預設):** Linux 容器是一種打包和執行軟體及其所需一切的方法,它們最適合執行 Linux 的電腦。 **Windows 容器:** Windows 容器是一種打包和執行軟體的方式,就像 Linux 容器一樣,但它們設計用於執行 Windows 的電腦。 **Windows 容器基礎:** 1. **集裝箱類型:** - Windows 容器有兩種主要類型:Windows Server Core 和 Nano Server。 - **Windows Server Core:** 將其視為功能更齊全的容器,適合各種應用程式。 - **Nano Server:** 將其視為一個輕量級容器,專為特定的、簡約的用例而設計。 2. **基礎鏡像:** - 基礎映像就像是建立容器時開始使用的空白畫布。 - 例: ``` docker pull mcr.microsoft.com/windows/servercore:ltsc2019 ``` - 此指令擷取 Windows Server Core 基礎映像。 - 例: ``` docker pull mcr.microsoft.com/windows/nanoserver:ltsc2019 ``` - 此命令提取 Nano Server 基礎映像。 3. **支援的環境:** - Windows 容器可以在特定版本的 Windows 作業系統上運作。 - 例: - 您可以在 Windows Server 2016 上執行 Windows 容器。 - 例: - 您可以在 Windows 10 專業版和企業版上執行 Windows 容器,並使用 Hyper-V 隔離容器進行額外隔離。 ## 容器編排 ![容器編排](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3lyleybojw3xmr2dgfep.png) 容器編排是一種管理和協調多個容器的方法,確保它們無縫協作來執行應用程式,就像一個超級智能的管理器確保所有機器人一起工作來建置完美的塔一樣。 **為什麼要編曲?** 1. **多項任務,一名經理:** - 想像一下您有許多機器人(容器)執行不同的工作。編排就像有一位超級聰明的經理(編排者),他告訴每個機器人該做什麼,並確保一切順利進行。 2. **一致性:** - 編排確保所有任務每次都以相同的方式完成。這就像為您的機器人提供了一套要遵循的指令,以確保其行為的一致性。 3. **效率:** - 編排有助於優化任務,確保資源(如時間和材料)有效利用。這就像經理確保所有機器人一起工作而不浪費能源。 4. **縮放比例:** - 當您需要完成更多工作時,編排可以輕鬆建立額外的機器人(容器)。這就像當有很多事情需要完成時神奇地召喚更多機器人來提供幫助。 5. **可靠性:** - 編排確保任務可靠地完成,即使機器人(容器)出現故障。這就像製定備份計劃來確保無論如何都能完成工作。 6. **協調:** - 編排協調任務,確保機器人無縫協作。這就像經理確保每個機器人都知道自己的角色並協作以實現總體目標。 **容器編排程式碼:** ``` # Create a Docker service with 100 replicas (instances) of a Node.js application docker service create --replicas 100 --name my-nodejs-app nodejs ``` 在這個例子中: - `docker service create`:該指令告訴 Docker 建立一個服務,該服務是一組正在執行的容器。 - `--replicas 100`:此標誌指定您需要 100 個服務實例(副本)。 - `--name my-nodejs-app`:此標誌為您的服務提供名稱,在本例中為「my-nodejs-app」。 - `nodejs`:這是 Node.js 應用程式的圖片或配方。這就像是烘焙紙杯蛋糕的藍圖。 因此,這段簡單的程式碼就像告訴您神奇的廚師助手 (Docker Swarm) 建立 Node.js 應用程式的 100 個副本,確保您有大量容器正在執行並準備好提供服務。 ## Docker 群 ![Docker Swarm](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z5noonjo2ikyrp90kc38.png) Docker Swarm 是一個工具,可以幫助協調和管理一組電腦(節點)作為一個機器人團隊一起工作,使它們能夠以協調的方式部署和執行多個容器。這就像有一個首席機器人經理,確保所有單一機器人一起建造出偉大而令人驚嘆的東西。 **設定 Docker Swarm:** 1. **群組管理器:** - 想像你有一個首席機器人(Swarm Manager)來領導團隊。主機器人決定需要做什麼,並指導其他機器人(節點)如何協同工作。 ``` # Initiate Docker Swarm on the Swarm Manager docker swarm init ``` 2. **節點工作人員:** - 現在,您的工作機器人(節點工作人員)已準備好加入團隊。 Swarm Manager 共享一個特殊的程式碼(令牌)來邀請他們一起工作。 ``` # Join a Node Worker to the Docker Swarm docker swarm join --token <token> <Swarm Manager IP> ``` **Docker Swarm 服務:** 現在您已經有了一個協調的團隊,您想要建立一項服務,例如與您的機器人團隊一起建造塔: ``` # Create a Docker service (a group of containers) with 3 replicas (instances) docker service create --replicas 3 --network frontend --name my-web-server my-web-image ``` - `--replicas 3`:此標誌告訴 Docker 建立服務的三個實例(副本)。 - `--network frontend`:此標誌指定您的服務屬於名為「frontend」的網路。 - `--name my-web-server`:這會為您的服務命名,在本例中為「my-web-server」。 - `my-web-image`:這是您的網頁伺服器的圖片或藍圖。這就像建造塔樓的配方。 您建立了一個由隊長(Swarm Manager)和工作機器人(Node Workers)組成的機器人團隊。然後,您指示他們建立一個執行您的 Web 伺服器應用程式的服務(容器群組)。主機器人確保建立 Web 伺服器的三個副本並將其連接到「前端」網路。這就像有一個首席機器人經理在工作機器人的幫助下監督多個塔(貨櫃)的建造。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ytitz2wt4jsufejox9yx.gif) **好的,這就是本文的內容。** 另外,如果您對此或其他任何問題有任何疑問,請隨時在下面的評論中或在 [Instagram](https://www.instagram.com/_abhixsh/) 、[Facebook](https://www.facebook.com/abhi.haththakage/) 或[Twitter](https://twitter.com/abhixsh)。 感謝您閱讀這篇文章,我們下一篇再見! ❤️ --- 原文出處:https://dev.to/abhixsh/docker-for-the-absolute-beginner-3h1p

在沒有伺服器的情況下在視窗之間共享狀態

最近,社群網路上流行一張 gif 動圖,展示了一件 [Bjorn Staal 製作的令人驚嘆的藝術品](https://twitter.com/_nonfigurativ_/status/1727322594570027343)。 ![Bjorn Staal 藝術作品](https://cdn-images-1.medium.com/max/2000/1*vCKb_XLed3eD9y4h-yjdKQ.gif) 我想重新建立它,但缺乏球體、粒子和物理的 3D 技能,我的目標是了解如何讓一個視窗對另一個視窗的位置做出反應。 本質上,在多個視窗之間共享狀態,我發現這是 Bjorn 專案中最酷的方面之一! 由於無法找到有關該主題的好文章或教程,我決定與您分享我的發現。 > 讓我們嘗試根據 Bjorn 的工作建立一個簡化的概念驗證 (POC)! ![我們將嘗試創造什麼(ofc 它比 Bjorn 的作品沒那麼性感)](https://cdn-images-1.medium.com/max/2000/1*KJHO9DmEDcTISWuCcvDpMQ.gif) 我做的第一件事就是列出我所知道的在多個客戶端之間共享資訊的所有方法: ## 呃:伺服器 顯然,擁有伺服器(帶有輪詢或 Websocket)可以簡化問題。然而,由於 Bjorn 在沒有使用伺服器的情況下實現了他的結果,所以這是不可能的。 ## 本機存儲 本地存儲本質上是瀏覽器鍵值存儲,通常用於在瀏覽器會話之間保存資訊。雖然通常用於儲存身份驗證令牌或重定向 URL,但它可以儲存任何可序列化的內容。 [您可以在這裡了解更多](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)。 我最近發現了一些有趣的本地儲存 API,包括 *`storage`* 事件,每當同一網站的另一個會話更改本地儲存時就會觸發該事件。 ![儲存事件如何運作(當然是簡化的)](https://cdn-images-1.medium.com/max/4048/1*otw7fDvd-XFjj9yVBxn5zg.png) 我們可以透過將每個視窗的狀態儲存在本地儲存中來利用這一點。每當一個視窗改變其狀態時,其他視窗將透過儲存事件進行更新。 這是我最初的想法,這似乎是Bjorn 選擇的解決方案,因為他分享了他的LocalStorage 管理器程式碼以及與ThreeJs 一起使用的範例[此處](https://github.com/bgstaal/multipleWindow3dScene) 。 但是當我發現有程式碼可以解決這個問題時,我想看看是否有其他方法…劇透警告:是的,有! ## 共享工作者 這個華而不實的術語背後是一個令人著迷的概念——WebWorkers 的概念。 簡單來說,工作執行緒本質上是在另一個執行緒上執行的第二個腳本。雖然它們無法存取 DOM(因為它們存在於 HTML 文件之外),但它們仍然可以與您的主腳本進行通訊。 它們主要用於透過處理背景作業來卸載主腳本,例如預取資訊或處理不太關鍵的任務(例如流日誌和輪詢)。 ![腳本與worker之間通訊機制的簡單解釋](https://cdn-images-1.medium.com/max/3548/1*izcnWc_p13m8pZy5d49mVw.png) 共享工作線程是一種特殊類型的 WebWorkers,它可以與同一腳本的多個實例進行通信,這使得它們對我們的用例很有趣!好吧,讓我們直接進入程式碼! ![共享工作人員可以將資訊傳送到同一腳本的多個會話](https://cdn-images-1.medium.com/max/5428/1*A7ObCM2OjojgfFP57ankyw.png) ### 設定工人 如前所述,工作人員是具有自己的入口點的「第二腳本」。根據您的設定(TypeScript、捆綁程式、開發伺服器),您可能需要調整 tsconfig、新增指令或使用特定的匯入語法。 我無法涵蓋所有使用 Web Worker 的可能方法,但您可以在 MDN 或網路上找到資訊。 如果需要,我很樂意為本文撰寫前傳,詳細介紹設定它們的所有方法! 就我而言,我使用的是 Vite 和 TypeScript,因此我需要一個「worker.ts」檔案並將「@types/sharedworker」安裝為開發依賴項。我們可以使用以下語法在主腳本中建立連結: ``` new SharedWorker(new URL("worker.ts", import.meta.url)); ``` 基本上,我們需要: * 辨識每個視窗 * 追蹤所有視窗狀態 * 一旦視窗改變狀態,提醒其他視窗重繪 我們的狀態將非常簡單: ``` type WindowState = { screenX: number; // window.screenX screenY: number; // window.screenY width: number; // window.innerWidth height: number; // window.innerHeight }; ``` 當然,最重要的訊息是“window.screenX”和“window.screenY”,因為它們告訴我們視窗相對於顯示器左上角的位置。 我們將有兩種類型的訊息: * 每個窗口,無論何時改變其狀態,都會發布一個帶有新狀態的“windowStateChangedmessage”。 * 工作人員將向所有其他視窗發送更新,以提醒他們其中一個視窗已更改。工作人員將發送包含所有視窗狀態的「syncmessage」。 我們可以從一個看起來有點像這樣的普通工人開始: ``` // worker.ts let windows: { windowState: WindowState; id: number; port: MessagePort }[] = []; onconnect = ({ ports }) => { const port = ports[0]; port.onmessage = function (event: MessageEvent<WorkerMessage>) { console.log("We'll do something"); }; }; ``` 我們與 SharedWorker 的基本連結如下所示。我有一些基本函數可以產生 id,並計算當前視窗狀態,我還對我們可以使用的稱為 WorkerMessage 的訊息類型進行了一些輸入: ``` // main.ts import { WorkerMessage } from "./types"; import { generateId, getCurrentWindowState, } from "./windowState"; const sharedWorker = new SharedWorker(new URL("worker.ts", import.meta.url)); let currentWindow = getCurrentWindowState(); let id = generateId(); ``` 一旦我們啟動應用程式,我們應該提醒工作人員有一個新窗口,因此我們立即發送一條訊息: ``` // main.ts sharedWorker.port.postMessage({ action: "windowStateChanged", payload: { id, newWindow: currentWindow, }, } satisfies WorkerMessage); ``` 我們可以在工作端監聽此訊息並相應地更改 onmessage。基本上,一旦工作人員收到 windowStateChanged 訊息,要么它是一個新窗口,我們將其附加到狀態,要么它是一個已更改的舊窗口。然後我們應該提醒大家狀態已經改變: ``` // worker.ts port.onmessage = function (event: MessageEvent<WorkerMessage>) { const msg = event.data; switch (msg.action) { case "windowStateChanged": { const { id, newWindow } = msg.payload; const oldWindowIndex = windows.findIndex((w) => w.id === id); if (oldWindowIndex !== -1) { // old one changed windows[oldWindowIndex].windowState = newWindow; } else { // new window windows.push({ id, windowState: newWindow, port }); } windows.forEach((w) => // send sync here ); break; } } }; ``` 為了發送同步,我實際上需要一些技巧,因為“port”屬性無法序列化,所以我將其字串化並解析回來。因為我很懶,所以我不只是將視窗映射到更可序列化的陣列: ``` w.port.postMessage({ action: "sync", payload: { allWindows: JSON.parse(JSON.stringify(windows)) }, } satisfies WorkerMessage); ``` 現在是時候畫東西了! ## 有趣的部分:繪畫! 當然,我們不會做複雜的 3D 球體:我們只會在每個視窗的中心畫一個圓,並在球體之間畫一條線! 我將使用 HTML Canvas 的基本 2D 上下文進行繪製,但您可以使用任何您想要的內容。畫一個圓,非常簡單: ``` const drawCenterCircle = (ctx: CanvasRenderingContext2D, center: Coordinates) => { const { x, y } = center; ctx.strokeStyle = "#eeeeee"; ctx.lineWidth = 10; ctx.beginPath(); ctx.arc(x, y, 100, 0, Math.PI * 2, false); ctx.stroke(); ctx.closePath(); }; ``` 為了繪製線條,我們需要做一些數學運算(我保證,這不是很多🤓),並將另一個視窗中心的相對位置轉換為目前視窗上的座標。 基本上,我們正在改變基地。我用一點數學來做到這一點。首先,我們將更改底座以在顯示器上具有座標,並透過目前視窗 screenX/screenY 進行偏移 ![基本上我們正在尋找鹼基變化後的目標位置](https://cdn-images-1.medium.com/max/5056/1*Zg_z1aZxUE1WP-uOk1owdw.png) ``` const baseChange = ({ currentWindowOffset, targetWindowOffset, targetPosition, }: { currentWindowOffset: Coordinates; targetWindowOffset: Coordinates; targetPosition: Coordinates; }) => { const monitorCoordinate = { x: targetPosition.x + targetWindowOffset.x, y: targetPosition.y + targetWindowOffset.y, }; const currentWindowCoordinate = { x: monitorCoordinate.x - currentWindowOffset.x, y: monitorCoordinate.y - currentWindowOffset.y, }; return currentWindowCoordinate; }; ``` 如您所知,現在我們在同一相對座標系上有兩個點,我們現在可以畫線了! ``` const drawConnectingLine = ({ ctx, hostWindow, targetWindow, }: { ctx: CanvasRenderingContext2D; hostWindow: WindowState; targetWindow: WindowState; }) => { ctx.strokeStyle = "#ff0000"; ctx.lineCap = "round"; const currentWindowOffset: Coordinates = { x: hostWindow.screenX, y: hostWindow.screenY, }; const targetWindowOffset: Coordinates = { x: targetWindow.screenX, y: targetWindow.screenY, }; const origin = getWindowCenter(hostWindow); const target = getWindowCenter(targetWindow); const targetWithBaseChange = baseChange({ currentWindowOffset, targetWindowOffset, targetPosition: target, }); ctx.strokeStyle = "#ff0000"; ctx.lineCap = "round"; ctx.beginPath(); ctx.moveTo(origin.x, origin.y); ctx.lineTo(targetWithBaseChange.x, targetWithBaseChange.y); ctx.stroke(); ctx.closePath(); }; ``` 現在,我們只需要對狀態變化做出反應。 ``` // main.ts sharedWorker.port.onmessage = (event: MessageEvent<WorkerMessage>) => { const msg = event.data; switch (msg.action) { case "sync": { const windows = msg.payload.allWindows; ctx.reset(); drawMainCircle(ctx, center); windows .forEach(({ windowState: targetWindow }) => { drawConnectingLine({ ctx, hostWindow: currentWindow, targetWindow, }); }); } } }; ``` 最後一步,我們只需要定期檢查視窗是否發生變化,如果發生變化則發送訊息 ``` setInterval(() => { const newWindow = getCurrentWindowState(); if ( didWindowChange({ newWindow, oldWindow: currentWindow, }) ) { sharedWorker.port.postMessage({ action: "windowStateChanged", payload: { id, newWindow, }, } satisfies WorkerMessage); currentWindow = newWindow; } }, 100); ``` [您可以在此儲存庫中找到完整的程式碼](https://github.com/achrafl0/multi-window-article)。實際上,我用它做了很多實驗,使它變得更加抽象,但其要點是相同的。 如果您在多個視窗上執行它,希望您能得到與此相同的結果! ![完整結果](https://cdn-images-1.medium.com/max/2000/1*KJHO9DmEDcTISWuCcvDpMQ.gif) 謝謝閱讀 ! 如果您發現這篇文章有幫助、有趣或只是有趣,您可以將其分享給您的朋友/同事/社區 [您也可以訂閱我的電子報](https://notachraf.substack.com/)它是免費的! --- 原文出處:https://dev.to/notachraf/sharing-a-state-between-windows-without-a-serve-23an

🎄24 個開源程式庫:供您假期休息時 Coding 一下使用🎅🏽🎁

這是開源的季節☃️ 假期編碼可以是一種幸福的經歷,沒有什麼比在休息時間建立一個很棒的專案更好的了。 我瀏覽了無數的儲存庫,找到了 24 個最適合聖誕節編碼的庫。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgcb0fm044iapyf30l34.gif) 不要忘記 STAR 🌟 這些儲存庫並保存本文以供日後使用。 --- #Web-Dev🌐: ### 1. [CopilotTextarea ](https://github.com/CopilotKit/CopilotKit) - React 應用程式中的 AI 驅動寫作 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m7yb6ie1oeed9d1xwixt.png) 具有 Github CopilotX 功能的任何 React `<textarea>` 的直接替代品。 自動完成、插入、編輯。 可以即時或由開發人員提前提供任何上下文。   ###2.[React Joyride](https://github.com/gilbarbara/react-joyride) - 建立產品演練   ###3.[NextAuthJS](https://github.com/nextauthjs/next-auth) - 輕鬆設定驗證   ###4.[Trigger.dev](https://github.com/triggerdotdev/trigger.dev) - 可靠地執行長時間作業而不會逾時 ---   #UI/UX🦋: ### 5. [Flowbite](https://github.com/themesberg/flowbite) - 頂級 CSS 元件庫 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98dwqyrhf1pbiqkpko8g.png) 最好、最受尊敬的 UI 元件庫之一。 基於實用優先的 CSS 框架。 易於使用,充滿重要的支援和模板。   ###6.[MaterialUI](https://github.com/mui/material-ui) - 使用 Google 的 Material Design 實現的基礎 React 元件   ###7。 [SwiperUI](https://github.com/nolimits4web/swiper) - 用於實現行動滑動 UI 的受人尊敬的庫   ###8.[ReactSpring](https://github.com/pmndrs/react-spring) - 在 React 中實現具有真實物理效果的動畫 ---   #GenAI🤖: ## 9. [CopilotPortal](https://github.com/RecursivelyAI/CopilotKit):在您的應用程式中嵌入可操作的 LLM 聊天機器人。 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szzmw5l8c9m4bce23bd3.png) 應用程式中的上下文感知 LLM 聊天機器人可以回答問題並採取行動。 只需幾行程式碼即可獲得一個可用的聊天機器人,然後根據需要進行自訂和嵌入。   ###10.[llamafile](https://github.com/Mozilla-Ocho/llamafile) - 將使用 LLM 的複雜性壓縮到單一檔案   ###11。 [Pezzo.ai](https://github.com/pezzolabs/pezzo) - 開發人員最喜歡的 LLM 操作和可觀察性   ###12.[Tavily](https://github.com/assafelovic/gpt-researcher) - 開發人員友善的自治 GPT 代理,可搜尋資料並撰寫報告 ---   #安全/隱私🔐: ### 13. [Wazuh](https://github.com/wazuh/wazuh) - 統一開源安全平台 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/46eta4l6zxt2a8rf61ea.jpg) 適用於端點和雲端工作負載的統一開源安全平台。 威脅偵測、預防和回應。 保護本地、容器化和雲端環境。   ###14.[Zeek](https://github.com/zeek/zeek) - 深入分析網路流量   ###15。 [SuperTokens](https://github.com/supertokens/supertokens-core) - 開源身分驗證提供者   ###16.[Sniffnet](https://github.com/GyulyVGC/sniffnet) - 本地監控您的網路流量 ---   #移動📱: ### 17. [Tamagui ](https://github.com/tamagui/tamagui) - 受人尊敬的 React Native UI 與最佳化函式庫 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3kuenic5tr8kmtzksn59.jpg) Tamagui 旨在快速設計 React 應用程式。 它包含一個可選的 UI 套件和一個用於增強效能的最佳化編譯器。 允許在 Web 和本機平台之間實現無縫程式碼共享,為每個環境最佳化樣式元件。   ###18.[EarlGrey](https://github.com/google/EarlGrey) - Google 進行的 iOS UI 測試   ###19。 [ReactNativeMaps](https://github.com/react-native-maps/react-native-maps) - 高度可自訂的應用程式地圖元件   ###20.[ReactNativePaper](https://github.com/callstack/react-native-paper) - 適用於 iOS 和 Android 的設計庫 ---   #其他🎅: ### 21. [LangChain ](https://github.com/CopilotKit/CopilotKit) - 使用 LLM 建立自訂操作鏈 ![圖片描述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pdk6rxdz8i4o28ratj7n.png) 一個著名的庫,但仍然包含在內,因為它是我最喜歡的庫之一,並且建置起來很有趣。 模組化元件可協助您將 LLM 整合到與許多應用程式和 API 整合的操作中。 用於建立 LLM 代理的強大框架。   ###22.[ReactAgent](https://github.com/eylonmiz/react-agent) - 實驗專案。將提示轉變為有效的反應元件   ###23.[Awesome for Beginners](https://github.com/MunGell/awesome-for-beginners) - Github 初學者友好專案列表   ###24.[Appwrite](https://github.com/appwrite/appwrite) - 適用於網路和行動應用程式的強大後端平台 --- 而且……就是這樣,夥計們! 希望您喜歡這些庫,並且它們可以幫助/激勵您在假期期間建立一些很酷的東西。 ##如果您喜歡本文,請不要忘記對儲存庫加 STAR🌟 並透過回饋來支持本文。 #聖誕節快樂! 🎅🏽☃️ --- [PS:本文附有我用 [Suno.ai](https://www.suno.ai) 產生的伴奏歌曲。他們很搞笑,很棒,也很令人毛骨悚然。告訴我你最喜歡哪一個: 1. [開源聖誕老人](https://app.suno.ai/song/e5d415a5-2914-4fc4-b3e5-136ea0a8ef5a/) 2.【聖誕老人的程式設計師聖誕魔法】(https://app.suno.ai/song/5794dcf0-e9cc-4e0d-a80f-f70ef4b73150/)] --- 原文出處:https://dev.to/copilotkit/24-must-try-open-source-projects-for-your-christmas-coding-3603

免費託管全端應用程式的 5 種最佳方式🚀✨

## 你好呀! ![Heroku 標誌](https://blog.4linux.com.br/wp-content/uploads/2018/01/Heroku.png) 首先,讓我給你一些背景資訊。 [直到最近](https://help.heroku.com/RSBRUH58/removal-of-heroku-free-product-plans-faq),**Heroku** 有一個很好的免費層,可以讓您部署和測試東西與他們的馬力機。使用起來很輕鬆。但隨著這一免費層的取消,開發人員不得不尋找新的平台來部署他們的專案。 幸運的是,有許多新的託管提供者不斷湧現。但**實際上選擇部署位置**可能與為專案編碼一樣具有挑戰性。 ![如此多的選項 gif](https://media.giphy.com/media/McD0cTjsFuxc7tjseu/giphy.gif) 這就是為什麼在本文中,**我將展示其中一些** **選項**,用於免費在線託管您的應用程式。我們將快速了解每個提供者最擅長的領域,以便您可以做出明智的決定,並讓您的應用程式上線。 --- ### 快速提醒 在開始部署我們的應用程式之前,請務必閱讀您選擇的框架提供的部署指南。 [React](https://create-react-app.dev/docs/deployment/)、[NextJS](https://nextjs.org/docs/pages/building-your-application/deploying)、[Wasp](https://wasp-lang.dev/docs/advanced/deployment/overview),而且大多數目前框架都有非常好的部署指南(甚至是讓這些部署更容易的平台)。 因此,您的框架選擇可能會對您的託管提供者產生很大影響! ![辦公室 GIF](https://media.giphy.com/media/BpGWitbFZflfSUYuZ9/giphy.gif) ## 支持我們! 🙏⭐️ 順便說一句,我是 Wasp 團隊的一員,如果您能檢查我們的全端 React/NodeJS/Prisma 框架並[給我們一顆星](https://kdta.io/github-黃蜂-lang-wasp_14 )。 我們正在努力幫助開發人員盡可能快速、輕鬆地從想法到部署(例如,單一命令、全端部署),感謝所有幫助! 好的。現在,讓我們探索一些在免費託管應用程式時提供絕佳選擇的雲端平台。 ### 韋爾塞爾 ![Vercel 標誌](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4nawjjv3zd1iea1rgfjw.png) 如果您正在部署前端應用程式,那麼這裡的體驗是難以比擬的。免費套餐**真的**很好。我有大約 20 個小型網站在 Hobby 計劃上執行,但無法減少使用量。 除此之外,部署的簡單性也令人驚訝。 ![Vercel 儀表板](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61giw9nmi9y1d933y6hk.png) 您所要做的就是選擇一個 github 儲存庫,如果他們檢測到您正在使用主要框架之一,您可能甚至不需要配置任何內容!他們會處理好這一切,您的應用程式將在幾秒鐘內執行。 ![Vercel 部署](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4xidcbvp9tpbeqr57d94.png) 不幸的是,對於後端和資料庫 [Vercel](https://vercel.com/) 仍然不是一個很好的選擇。如果你有一個非常小的後端(並且專注於NodeJS),你可以嘗試使用[Vercel 的函數](https://vercel.com/docs/functions/serverless-functions) 基礎設施在這裡部署,儘管它們在以下方面受到限制:執行時間和複雜性。 對於更複雜的後端,我們將不得不看看下面的一些提供者。 ### 鐵路 ![鐵路標誌](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnxai4q81n8u5uxlcz4h.png) [Railway](https://railway.app/) 是一個可讓您輕鬆部署 Web 應用程式和 API 的平台。當您註冊時,它提供每月**價值 5 美元的免費使用**的免費套餐。 只需幾個命令,您就可以部署應用程式並立即啟動並執行。它有一個非常漂亮的 UI,整體部署體驗也很好! 順便說一句,我真的很喜歡他們決定展示物品和容器的方式。這是一種非常聰明的方式來概覽事物,而不是數百個小應用程式。有時,[簡單地將事物分組](https://lawsofux.com/law-of-common-region/) 會對使用者體驗產生重大影響,如下所示: ![分組專案](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3f4y84fkv7sa3rxd0yrf.png) 它還支援 React、Next.js 和 Node.js 等流行框架。此外,它還提供一鍵式資料庫設定和[社區建立的模板](https://railway.app/templates),可以顯著簡化為專案設定 Dockerfile 的過程。 ![鐵路計畫](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t5zsr0oghlpyej2p335u.png) 以下是它的基本當前價格,每月 5 美元(他們每月給你使用)實際上足以讓你擁有一些專案(如 2 - 3 個小型網站、後端和資料庫)和如果您知道如何正確設置它們(例如,將它們設定為在不使用時自動睡眠),請執行。 ![設定頁](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e1nuvnb877w0zdb1kywt.png) 他們在應用程式設定頁面中有此選項。在那裡,您還可以找到其他可以更改的有趣的配置選項,例如網域、執行狀況檢查端點等。 ### 使成為 ![渲染標誌](https://images.g2crowd.com/uploads/product/image/large_detail/large_detail_477db83f729d63210139ec7cd29c1351/render-render.png) Render 是另一個用戶友好的雲端平台,提供**良好的免費套餐**並支援廣泛的應用程式。它簡化了部署和擴展應用程式的過程,並支援多種程式語言和框架。 Render 的免費套餐包括(每月): - 500 管道建設分鐘 - 100GB 頻寬 唯一的例外是他們的 **Cron Jobs 服務(您的情況可能不需要,特別是如果您正在利用 [Wasp 的內建作業](https://wasp-lang.dev/docs/advanced) /jobs),每月最低費用為1 美元。** ![渲染計畫](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sbqb08789v4soij9gp0s.png) ![渲染計畫 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n54c1fapvolqgzszzaaz.png) 它的功能真的很強大!我能夠部署資料庫、NestJS 應用程式和 NextJS 前端,所有這些都可以與遷移、Prisma ORM 等一起使用!它有一些很好的事件追蹤(儘管它可以呈現得更好——有時,螢幕上有太多元素)。 ![事件範例](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/essf0z1u1ows6snpswf6.png) 而且它還具有一些可觀察性功能!您可能認為這些不會在非常小的應用程式上使用,但是,當您在生產環境中偵錯事物時,您擁有的每個工具都可以產生有關問題的新見解。 ![可觀測範例](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m3fs623gtv5gu4hnjr3g.png) ![可觀測範例 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wasik5y01zuh5a5za8gk.png) ### 飛 ![飛標誌](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3v8jhqg988eorpcdn7hb.png) [Fly](https://fly.io/)是專注於全球邊緣運算的雲端平台。 Fly 專注於高效能託管,並提供全球邊緣站點網路。 Fly 以其可擴展性和性能優化而聞名。 ![飛行計畫](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/952omdoyh68fwq4lv4ik.png) 它支援各種程式語言和框架,讓您可以建立和部署功能強大的應用程式,並且在部署過程中,您只需下載[flyctl](https://fly.io/docs/hands-on/install-flyctl/)現在您就可以出發了! ![支援的架構](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/almuttwim73338hy7ech.png) 順便說一句,Wasp 框架支援使用 Fly! 的單一命令部署!您可以在[此處](https://wasp-lang.dev/docs/advanced/deployment/manually#flyio)查看有關它的更多資訊! ![黃蜂部署飛行](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ohqil6hslcacgnhr2128.jpeg) 它還可以容納一些小物品,但當我們查看實際價值時,成本會變得有點陡峭: ![飛行計畫定價](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f74v7dkf1p1i23jsckz3.png) 對於服務質量,它有一些不錯的監控儀表板和 Sentry 集成,這似乎非常有用。您也可以使用 SSH 輕鬆檢查應用程式日誌! ![飛行時記錄錯誤](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6bcj1fzf6xc64fzyugpu.png) ### 網路化 ![Netlify 標誌](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9zm251qd0ow6ja1yhrot.png) Netlify 以其出色的免費套餐而聞名,並提供各種功能來建立和部署網站。話雖如此,它更像是 Vercel 的直接競爭對手,因為它專注於前端和靜態內容。 與 Vercel 一樣,它並不適用於強大的後端,但它也可以選擇託管無伺服器功能,您只需將資料庫託管在其他地方即可。 它使用為每個框架量身定制的特定建置器,為主要框架提供出色的支援。他們的[部署文件](https://docs.netlify.com/configure-builds/overview/?_gl=1%2a10z7npm%2a_gcl_au%2aMTcyNzA3MDU2My4xNzAyNjUxODAx#basic-build-settings)非常全面且有幫助。 ![Netlify 方案](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xv8o94y0dltcvr0u2n4a.png) 透過 Netlify,您可以利用其內容交付網路 (CDN) 為使用者提供快速的靜態內容交付。它們提供了一系列功能來幫助您建立和優化您的網站。 Netlify 的免費方案包括: - 每月 300 分鐘建置時間 - 每月 100GB 頻寬 Netlify 因其易用性和強大的功能而受到開發人員的歡迎。它是託管靜態網站或 [Jamstack](https://jamstack.org/) 應用程式的絕佳選擇。 ![NextJS辨識](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e80nn5uz0il95xtogrr3.png) 他們還有一些主要的框架建置執行時,可以簡化您的流程! ### 結論 如果您還沒有為 [Wasp 的儲存庫](https://www.github.com/wasp-lang/wasp) 加註星標,我建議您這樣做!它可以幫助我們每週為您帶來這樣的免費內容。另外,它是最好的、沒有廢話的全端框架,可以減少開發時間並完成專案。 在免費託管您的應用程式時,重要的是要考慮每個平台的特定功能和限制。每個平台都有自己的優勢,選擇最適合您的需求和要求的平台非常重要。 以下是您的視覺回顧: ![複習圖片](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dd023j7tcv1cu23zedqm.png) 嘿!您想查看有關所有這些平台的更深入的文章嗎?留在評論區! --- 原文出處:https://dev.to/wasp/top-5-ways-to-host-your-full-stack-app-for-free-c2j

部署策略簡介:藍綠部署 vs 金絲雀部署 vs 其他部署

如今,軟體開發的最大變化是部署頻率。產品團隊更早(並且更頻繁)地將版本部署到生產中。長達數月或數年的發布週期變得越來越罕見,尤其是對於建立純軟體產品的人來說。 如今,使用服務導向的架構和微服務方法,開發人員可以將程式碼庫設計為模組化。這使他們能夠同時編寫程式碼庫的不同部分並將其部署到程式碼庫的不同部分。 縮短部署週期的商業優勢是顯而易見的: * 縮短上市時間 * 客戶在更短的時間內獲得產品價值 * 客戶回饋也更快回流到產品團隊,這意味著團隊可以更快地迭代功能並解決問題 * 開發人員整體士氣上升 然而,這種轉變也為營運或 DevOps 團隊帶來了新的挑戰。隨著部署更加頻繁,部署的程式碼更有可能對網站可靠性或客戶體驗產生負面影響。這就是為什麼制定程式碼部署策略以最大限度地降低產品和客戶的風險非常重要。 在本文中,我們將討論一些不同的部署策略、最佳實踐和工具,它們將使您的團隊更快*且*更可靠地工作。 --- ## 現代應用的挑戰 現代應用程式通常是分散式且基於雲端的。它們可以彈性擴展以滿足需求,並且由於高可用的架構,對故障的恢復能力更強。他們可以利用完全託管的服務,例如[AWS Lambda](https://aws.amazon.com/lambda/) 或[Elastic Container Service (ECS)](https://aws.amazon.com/ecs/) ,其中平台承擔一些營運責任。 這些應用程式幾乎總是有頻繁的部署。例如,行動應用程式或消費者 Web 應用程式可能在一個月內經歷多次變更。有些甚至每天多次部署到生產中。 他們經常使用微服務架構,其中多個元件協同工作以提供完整的功能。不同的元件可以有不同的發布週期,但它們都必須無縫地協同工作。 活動部件數量的增加意味著出現問題的可能性更大。由於多個開發團隊對整個程式碼庫進行更改,當問題不可避免地發生時,可能很難確定問題的根本原因。 另一個挑戰:基礎設施層的抽象,現在被視為程式碼。新應用程式的部署可能還需要部署新的基礎架構程式碼。 ## 流行的部署策略 為了應對這些挑戰,應用程式和基礎架構團隊應該設計並採用適合其用例的部署策略。 我們將回顧幾種不同的部署策略並討論幾種不同部署策略的優缺點,以便您可以選擇適合您的組織的策略。 ## “大爆炸”部署 顧名思義,「大爆炸」部署會一次更新應用程式的全部或大部分。這種策略可以追溯到軟體在實體媒體上發布並由客戶安裝的時代。 大爆炸部署要求企業在發布之前進行廣泛的開發和測試,通常與大型順序發布的[「瀑布模型」](https://en.wikipedia.org/wiki/Waterfall_model)相關。 現代應用程式的優點是可以在客戶端或伺服器端定期自動更新。對於現代團隊來說,這使得大爆炸方法變得更慢、更不靈活。 大爆炸部署的特點包括: * 所有主要部分都打包在一個部署中; * 用新版本很大程度上或完全取代現有軟體版本; * 部署通常會導致較長的開發和測試週期; * 假設失敗的可能性很小,因為回滾可能是不可能或不切實際的; * 完成時間通常很長,需要多個團隊的努力; * 要求客戶端採取行動更新客戶端安裝。 大爆炸部署不適合現代應用程式,因為對於面向公眾或關鍵業務應用程式來說,風險是不可接受的,因為中斷意味著巨大的財務損失。回滾通常成本高、耗時,甚至不可能。 大爆炸方法適用於非生產系統(例如,重新建立開發環境)或供應商打包的解決方案(例如桌面應用程式)。 ## 滾動部署 滾動、分階段或分步部署比大爆炸部署更好,因為它們最大限度地減少了許多相關風險,包括無法輕鬆回滾的用戶面臨的停機時間。 在滾動部署中,應用程式的新版本逐漸取代舊版本。實際部署需要一段時間。在此期間,新舊版本將共存,不會影響功能或使用者體驗。此過程可以更輕鬆地回滾與舊元件不相容的任何新元件。 下圖顯示了部署模式:叢集中每台伺服器的舊版本顯示為藍色,新版本顯示為綠色。 ![滾動部署](https://thepracticaldev.s3.amazonaws.com/i/divuxihkun2p186c9mye.png) 應用程式套件升級是滾動部署的一個範例。如果原始應用程式部署在容器中,則升級一次可以處理一個容器。每個容器都經過修改,可以從應用程式供應商的網站下載最新的映像。如果其中一個應用程式有相容性問題,舊映像可以重新建立容器。在這種情況下,套件應用程式的新舊版本會共存,直到每個應用程式都升級為止。 ## 藍綠、紅黑或 A/B 部署 這是另一個自動防故障過程。在這種方法中,兩個相同的生產環境並行工作。 一種是目前正在執行的生產環境,接收所有使用者流量(顯示為藍色)。另一個是它的克隆,但閒置(綠色)。兩者都使用相同的資料庫後端和應用程式配置: ![藍綠部署前](https://thepracticaldev.s3.amazonaws.com/i/78dk41w8qmuy9f9pvrf6.png) 新版本的應用程式部署在綠色環境中,並進行功能和效能測試。一旦測試結果成功,應用程式流量就會從藍色路由到綠色。綠色則成為新的產品。 ![藍綠部署後](https://thepracticaldev.s3.amazonaws.com/i/m664yyotixnqncprryf0.png) 如果綠色生效後出現問題,流量可以路由回藍色。 在藍綠部署中,兩個系統都使用相同的持久性層或資料庫後端。保持應用程式資料同步至關重要,但鏡像資料庫可以幫助實現這一目標。 您可以使用藍色的主資料庫進行寫入操作,並使用綠色的輔助資料庫進行讀取操作。從藍色切換到綠色期間,資料庫會從主資料庫故障轉移到輔助資料庫。如果測試時green也需要寫入資料,資料庫可以採用雙向複製。 一旦綠色變為可用,您可以關閉或回收舊的藍色實例。您可以在這些實例上部署較新的版本,並使它們成為下一個版本的新版本。 藍綠部署依賴流量路由。這可以透過更新主機的 DNS CNAMES 來完成。但是,長 TTL 值可能會延遲這些變更。或者,您可以變更負載平衡器設置,以便變更立即生效。 ELB 中的連線耗盡等功能可用於服務動態連線。 ## 金絲雀部署 金絲雀部署就像藍綠部署,但它更規避風險。您可以使用分階段的方法,而不是一步從藍色切換到綠色。 透過金絲雀部署,您可以在生產基礎架構的一小部分中部署新的應用程式程式碼。一旦應用程式被批准發布,只有少數用戶會被路由到它。這可以最大限度地減少任何影響。 如果沒有報告錯誤,新版本可以逐步推廣到基礎設施的其餘部分。下圖示範了金絲雀部署: ![金絲雀部署](https://thepracticaldev.s3.amazonaws.com/i/zvf9rbd1x38umph98zro.png) 金絲雀部署的主要挑戰是設計一種方法將某些使用者路由到新應用程式。此外,某些應用程式可能始終需要同一組使用者進行測試,而其他應用程式可能每次都需要不同的群組。 考慮透過探索多種技術來路由新用戶: * 在允許外部使用者存取之前,將內部使用者暴露給金絲雀部署; * 基於來源IP範圍的路由; * 在特定地理區域發布應用程式; * 使用應用程式邏輯為特定使用者和群組解鎖新功能。當應用程式對其他使用者上線時,此邏輯將被刪除。 ## 部署最佳實踐 現代應用程式團隊可以遵循許多最佳實踐,將部署風險降至最低: * **使用部署清單。** 例如,清單上的一項可能是「僅在應用程式服務停止後備份所有資料庫」以防止資料損壞。 * **採用持續整合 (CI)。** CI 確保簽入程式碼儲存庫功能分支的程式碼僅在*經過一系列相依性檢查、單元和整合測試以及成功建置。如果路徑中出現錯誤,建置將失敗並通知應用程式團隊。因此,使用 CI 意味著應用程式的每項變更都需要在部署之前進行測試。 CI 工具的範例包括:CircleCI、Jenkins。 * **採用持續交付 (CD)。** 透過 CD,CI 建置的程式碼工件被打包並隨時準備在一個或多個環境中部署。請閱讀我們的[低風險持續交付電子書](https://try.rollbar.com/low-risk-continuous-delivery-guide/) 以了解更多資訊。 * **使用標準作業環境 (SOE)** 以確保環境一致性。您可以使用 Vagrant 和 Packer 等工具來開發工作站和伺服器。 * **使用建置自動化工具來自動化環境建置。** 使用這些工具,通常只需單擊一個按鈕即可輕鬆拆除整個基礎架構堆疊並從頭開始重建。 CloudFormation 就是此類工具的一個範例。 * **在目標伺服器中使用 Puppet、Chef 或 Ansible 等組態管理工具**來自動套用作業系統設定、套用修補程式或安裝軟體 * **使用 Slack 等通訊管道**來自動通知不成功的建置和應用程式故障 * **建立一個流程,用於就部署失敗向負責團隊發出警報。** 理想情況下,您將在 CI 環境中捕獲這些情況,但如果部署了更改,您將需要一種方法來通知負責團隊 * **為執行狀況檢查失敗的部署啟用自動回滾**,無論是由於可用性還是錯誤率問題。 ## 部署後監控 即使您採用了所有這些最佳實踐,事情仍然可能失敗。因此,監控部署後立即發生的問題與規劃和執行完美部署同樣重要。 應用程式效能監控 (APM) 工具可以幫助您的團隊監控關鍵效能指標,包括部署後的伺服器回應時間。應用程式或系統架構的變化會極大地影響應用程式的效能。 像 [Rollbar](http://rollbar.com/) 這樣的錯誤監控解決方案同樣重要。它將快速通知您的團隊部署中的新錯誤或重新啟動的錯誤,這些錯誤可能會發現需要立即關注的重要錯誤。 如果沒有錯誤監控工具,這些錯誤可能永遠不會被發現。雖然少數遇到錯誤的用戶會花時間報告它們,但大多數其他用戶不會。隨著時間的推移,負面的客戶體驗可能會導致滿意度問題,或更糟的是,可能會導致業務交易無法進行。 錯誤監控工具還可以在營運/DevOps 團隊和開發人員之間建立所有部署後問題的共享可見性。這種共同的理解使團隊能夠更加協作和回應。 _原文發佈於 [rollbar.com](https://rollbar.com/blog/deployment-strategies/)_ --- 原文出處:https://dev.to/mostlyjason/intro-to-deployment-strategies-blue-green-canary-and-more-3a3

為什麼我不推薦 Robert C Martin 的《Clean Architecture》

*乾淨的架構*在許多方面都未能滿足我的期望。儘管 Martin 先生對這個主題表現出明顯的熱情,但「清潔架構」組織得很差,缺乏範例,並且對使用現有系統保持沉默。作者錯過了一個重要的機會來教導我們何時以及如何將這些經驗教訓應用到我們自己的系統中。讓我解釋。 ### *乾淨的架構*是一本組織得很差的書 這本書需要很長時間才能開始。關於設計範式(結構化、物件導向和函數式)的章節似乎特別不合時宜且不必要。 關於 SOLID 原則的章節很好。我很高興看到這些原則被分解並得到很好的解釋。我發現思考它們對系統架構的適用性很有趣。但鮑伯叔叔提出了諸如硬性規則之類的堅實原則,這讓我感到不舒服。事實上,我非常確定一個***從未***違反 SOLID 原則的系統將是一個巨大的混亂。 然而,第 137 頁的這一段很重要: >架構的主要目的是支援系統的生命週期。良好的架構使得系統易於理解、易於開發、易於維護、易於部署。最終目標是最小化系統的生命週期成本並最大化程式設計師的生產力。 這是一個很好的觀察。叔叔鮑伯為什麼不把它放在第一章呢? ### 例子不夠多 本書中幾乎沒有任何例子。第 33 章包含一個討論影片銷售電子商務應用程式的範例。沒關係。但我並沒有明確地知道如何將這些概念應用到我自己的系統中。 附錄講述了 Bob 叔叔如何提出 SOLID 原則和乾淨架構規則的故事。它載有過去專案的範例。我認為這是本書**最有趣的部分**。 ### 這本書沒有提到改進現有系統的架構 大多數開發人員將大部分時間花在現有系統上。因此,我期望 *[Clean Architecture](https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164/)* 充滿有關改進現有系統的建議。但這本書在這個問題上顯然隻字不提。 ### 如何建立一個乾淨的架構? 在本書的前半部分中,您將了解如何透過遵循 SOLID 原則將系統沿系統邊界分解為元件來建立一個乾淨的架構(我是這樣解釋的)。如果你停止閱讀那裡,你可能會有這樣的印象:叔叔鮑伯不會批准你為建築所做的一切,這是可以理解的。如果你認為他提出的幾個選項是「正確」的做事方式,這也是情有可原的。然而,在本書的最後,您將在第 228 頁讀到: >這個例子旨在顯示架構邊界無所不在。作為建築師,我們必須小心地辨識何時需要它們。我們也必須意識到,如果完全實施這些邊界,其成本是昂貴的。 >同時,我們必須認識到,當這些邊界被忽略時,即使存在全面的測試套件和重構規則,以後加入它們的成本也會非常昂貴。 >那我們建築師該怎麼辦?答案令人不滿意。一方面,多年來,一些非常聰明的人告訴我們,我們不應該預見抽象的需要。這就是 YAGNI 的哲學:“你不會需要它。”這句話充滿智慧,因為過度設計往往比設計不足更糟。另一方面,當您發現確實需要一個不存在的架構邊界時,加入這樣一個邊界的成本和風險可能會非常高。 >所以你已經得到它了。軟體架構師啊,你必須看見未來。你必須聰明地猜測。您必須權衡成本並確定架構邊界在哪裡,哪些應該完全實現,哪些應該部分實現,哪些應該被忽略。 因此,在本書的一半以上,他說應該由我們來決定我們的系統的邊界在哪裡。這種界限可能無所不在。令人困惑,對吧? ### 這本書缺少什麼 因此,如果軟體架構的最終目標是最小化系統的生命週期成本,那麼一本關於架構的書不應該幫助架構師做出這些決策嗎? #### 未解答的問題 這本書給我留下了很多未解答的問題。各種選擇的經濟學討論在哪裡?如何找到最適合我的系統的架構?研究在哪裡? ![未解答的問題](https://thepracticaldev.s3.amazonaws.com/i/fll25irsu1xbd0268kq2.jpg) 我該如何決定水平分層、垂直切片或其他方式是否可以最大限度地降低系統的生命週期成本?或者,如果我沒有明確定義的層,我該如何決定哪種分層策略(如果有)可以最大限度地降低我的生命週期成本? 我還有更多問題。如果您改進現有系統架構的時間有限,您應該把精力放在哪裡?將資料庫與業務邏輯分開總是一個好主意嗎?您應該始終遵循哪些建議?哪些建議取決於系統? #### 讓*乾淨的架構*更有價值的細節 在*[Code Complete](https://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670/)*中,Steve McConnell 討論了不同非功能性需求(例如可靠性、可靠性)之間的權衡、正確性、可維護性、可讀性等。他解釋了一些需求如何協調一致而另一些需求如何衝突。我很樂意看到類似的東西用於本書中討論的架構決策。 在*清潔架構*中,專案規模、團隊規模、專案失敗的後果、預期程式碼生命週期和其他重要因素作為架構的驅動因素沒有得到足夠的重視。例如,Healthcare.gov 需要比您正在開發的個人待辦事項清單更多的架構,即使它們都是由資料庫支援的 Web 應用程式。 ### 這本書的真正內容是什麼 本書的大部分內容我都感到有些困惑。我有點明白叔叔鮑伯想說什麼。但直到讀了附錄才完全明白。那麼,讓我為您節省一些時間。 #### 一個例子 想像一下您正在為消費市場建立一台桌上型電腦(例如辦公室電腦)。您可以選擇硬件,然後為整個系統編寫軟體(韌體、作業系統、驅動程式、應用程式等)。 #### 你會寫一個整體嗎? 你會怎樣做呢?您會編寫一個巨大的程式(一個整體),其中電子表格的程式碼知道您為電腦選擇的磁碟類型嗎?您能否想像更新電子表格程式碼並在各處新增「if」語句,以便在您有 SATA 磁碟機時執行一件事,而在有 SCSI 磁碟機時執行另一件事?然後對 VGA、DVI 和 HDMI 視訊做同樣的事情? PS/2 與 USB 滑鼠輸入的不同路徑怎麼樣?然後對文字處理器和您打算與電腦捆綁的所有其他應用程式重複該過程? 維護這將是一場惡夢,對嗎?那麼解決方法是什麼呢?建築學!您的電子表格不應該知道您使用的是哪種滑鼠。也不知道你有什麼樣的顯示器。它應該完全忘記這些細節。 #### 計算機中的邊界 如果您查看計算機,您會發現這一點。您的滑鼠中有嵌入式軟體,可以與作業系統進行通訊。然而,詳細資訊對您的應用程式是隱藏的。您的電子表格接受標準化輸入,而無需知道或關心您使用的滑鼠類型。然後,當有人發明了新的輸入裝置(例如觸控板)時,它會自動與您的電子表格配合使用。 這只是計算機的邊界之一。您可能會想出數百個。您可以指派不同的團隊來處理系統的不同部分。您可以為不同元件互動的各種方式建立或採用不同的規格。 此時你可能會說「呃」。顯然,您不希望每次硬體發生變化時都更改並重新編譯電子表格。維護將是一場惡夢。並且我同意。 #### 邊界是你的朋友(如果你正確使用它們) 很容易看出硬體邊界。但使硬體邊界變得有價值的相同邏輯也適用於軟體邊界。軟體邊界更難看到。 因此,您可以從在螢幕上顯示電子表格的能力開始。但您可能還想將其儲存到磁碟、儲存為 PDF、儲存為 CSV 或列印。因此,電子表格程式的邊界之一可能是擁有代表每個電子表格的內部資料結構。然後將該結構傳遞給不同的程式碼,以所需的格式顯示、儲存或列印它。 如果您讓資料結構完全不知道它是如何顯示、保存或列印的,那麼您可以在以後加入「儲存到 XML」功能,而無需深入研究與電子表格資料結構相關的所有程式碼。如果電子表格資料結構有幾百萬行程式碼,您可以想像這會容易得多。 這就是鮑伯叔叔在本書中試圖告訴我們的全部。 **您可以使用 SOLID 原則在系統中建立邊界,隱藏實作細節、降低複雜度並幫助您降低系統的總生命週期成本**。 ### 一本更好的軟體架構書 在許多方面,Martin Fowler 的*[企業應用程式架構模式](https://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420/)* 遠遠優於*清潔架構* 。 Fowler 描述了他在企業應用程式中反覆觀察到的模式。他給出了每個模式的簡單範例,描述了它的工作原理以及在何處使用它。我發現“企業應用程式架構模式”非常可讀並且適用於我的系統。 ### 要點 *乾淨的架構*中有有價值的訊息,但你必須努力找到它。 我發現有關嵌入式軟體的章節是最容易理解的章節之一。直觀上,避免在整個程式碼庫中分散低階呼叫是有意義的。在沒有硬體的情況下使邏輯可測試也是有意義的(特別是因為嵌入式軟體通常是在硬體可用之前開發的)。如果您只能閱讀本書的兩章,我建議您閱讀這一章和附錄,無論您以編寫哪種軟體為生。 總的來說,《乾淨的架構》是一本很難讀的書,鮑伯叔叔留給我的問題比答案還多。我絕對不會推薦這本書作為您關於軟體架構的第一本書(請查看*[企業應用程式架構模式](https://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420/ )* 由馬丁·福勒取代)。 *同意還是不同意。我很想聽聽你的想法。* *喜歡這篇文章嗎?請在下面“喜歡”它。* --- 原文出處:https://dev.to/bosepchuk/why-i-cant-recommend-clean-architecture-by-robert-c-martin-ofd

HTML可以做到這一點嗎?第1部分

歡迎來到我的 HTML can do 這個系列,第 1 部分。 為了與我的[GitHub-Can-Do-This](https://dev.to/yuridevat/reference-a-new-issue-3g5h) 系列保持一致,今天我想寫一篇關於HTML 可以做的很酷的事情的文章(很多人顯然不知道)。 我堅信,越多的人理解 HTML 並充分利用它,網路就會自動變得更容易存取。 ## 表中的內容 1. [詳細資訊和摘要 HTML 標籤](#chapter-1) 2. [電子郵件或電話號碼連結](#chapter-2) 3. [自動完成清單](#chapter-3) 4. [文字修改](#chapter-4) --- 由於我不知道接下來還會有多少部分,因此我將透過按字母順序描述出色的標籤及其背後的 HTML 魔力來開始本系列。 ## 1. 詳細資訊和摘要<a name="chapter-1"></a> 開啟/關閉小部件通常使用 JavaScript 建立。很長一段時間以來,有一個預設的 HTML 標籤恰好涵蓋了這種行為。 樣式可以根據需要進行調整。預設情況下,該小部件是關閉的。賦予 `<details>` 屬性 `open`,它會從頭開始顯示其內容。 {% codepen https://codepen.io/YuriDevAT/pen/dyaBQZm %} ## 2. 連結到電子郵件地址或電話號碼<a name="chapter-2"></a> `<a>` 標籤有一個 `href` 屬性,它指示連結的目的地。它可以連結到網頁、文件、電子郵件地址、同一頁面中的位置或 URL 可以尋址的任何其他內容。 `href` 屬性負責使 `<a>` 可聚焦並在按 Enter 鍵時啟動連結。 ### 連結到電子郵件地址 如果您在「href」的 URL 方案中新增「mailto:」和預期收件者的電子郵件地址,則按一下時會開啟新的外寄電子郵件。 ``` <a href="mailto:[email protected]">Send email to nowhere</a> ``` 您還可以在 URL 中加入更多詳細訊息,包括抄送、密件副本、主題和正文 😯。 ``` <a href="mailto:[email protected][email protected]&subject=This%20is%20the%20subject">Send email with subject to nowhere and nobody</a> ``` 詳細了解[連結至電子郵件地址 rfc6068](https://datatracker.ietf.org/doc/html/rfc6068)。 ### 連結到電話號碼 將電話號碼新增至「tel:」之後的「href」時,您也可以連結到電話號碼。 ``` <a href="tel:+18005551239">(800) 555 1239</a> ``` 連結行為可能因設備而異([關於連結到電話號碼的 MDN 官方文件](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#linking_to_telephone_numbers)): - 在行動裝置上,系統會自動撥打號碼。 - 大多數作業系統都有可以撥打電話的程序,例如 Skype 或 FaceTime。 - 網站可以使用registerProtocolHandler撥打電話,例如web.skype.com。 - 其他行為包括將號碼儲存到聯絡人,或將號碼傳送到另一台裝置。 詳細了解[連結到電話號碼 rfc3966](https://datatracker.ietf.org/doc/html/rfc3966)。 {% codepen https://codepen.io/YuriDevAT/pen/OJdZLKK %} ## 3. 自動完成清單<a name="chapter-3"></a> 使用“<datalist>”,當使用者開始在輸入欄位中寫入時會出現一個下拉式選單,並使他們可以從清單中的可用選項中進行選擇。 {% codepen https://codepen.io/YuriDevAT/pen/zYeVMbE %} ## 4. 文字修改<a name="chapter-4"></a> 使用特定標籤,可以直接修改文本,無需額外的 CSS。非常便利。 {% codepen https://codepen.io/YuriDevAT/pen/LYqKXwJ %} --- 謝謝閱讀。對此,我真的非常感激! --- 原文出處:https://dev.to/yuridevat/html-can-do-this-part-1-3ab2

🔥 NextJS 專案的 12 個頂級庫 🏆

![保存](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szi0gw4l049yctxjeu1p.png) 在過去的十年裡,我一直是一名全端開發人員,建置了像[gitup](https://gitup.dev/) 這樣的較小專案和像[crosspublic](https://github.com/github-20k/) 這樣的更大專案跨公共)。 多年來,我測試了不同的工具: 1. 提高工作效率 2. bug 更少 3. 少寫程式碼 我整理了一系列庫來幫助您開發我每天使用的優秀 NextJS 東西,並解釋了您可以用它們做什麼。 **讓我們深入了解一下。** ![變得更好](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ap38q1ej3tqypjuebg3u.gif) --- # 1. [Trigger.dev](https://github.com/triggerdotdev/trigger.dev) 使用 NextJS,我總是需要幫助來處理與後台作業相關的所有事情。 它可以是在背景執行的 cron 作業,用於傳送電子郵件或處理系統中的新使用者管道。 這導致我執行另一台伺服器來處理這些作業,無論是外部 EC2 伺服器還是帶有事件橋的無伺服器功能。 這會導致我支付額外的服務費用(管理更多服務)並自行管理水平擴展(在某些時候)。 [Trigger.dev](http://Trigger.dev) 改變了這一點,在 NextJS(以及許多其他)之上提供後台作業。 他們也知道如何解決 NextJS 無伺服器逾時限制來處理長時間執行的作業。   ![TriggerDev](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/triggertop.gif) --- ## 2. [Prisma](https://www.prisma.io) Prisma 不是 NextJS 特有的。它是一個與資料庫一起使用的 ORM。 ORM 是資料庫查詢的統一包裝器。 它保持良好的結構,並允許您在不同的資料庫提供者之間快速更改。 雖然您可以使用很多 ORM,但 Prisma 的獨特之處在於為您的查詢提供 Typescript 支持,使一切速度提高 100 倍。 NextJS 在預設配置中使用了 typescript,使其成為完美的匹配。   ![prisma.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/prisma.gif) --- ## 3. [NextAuth.js](https://next-auth.js.org) 假設您要實現任何服務提供者身份驗證,例如 Facebook / Google / GitHub (oAuth)。 在這種情況下,您必須為每個提供者建立實作或使用外部服務,例如 [Auth0](https://auth0.com/) 或 [Clerk](https://clerk.com/)。 如果您打算自行執行此操作,NextAuth 提供了豐富的實現,以便您只需提供正確的金鑰即可輕鬆新增它們。 一旦您登錄,他們也會處理授權。 *Next.JS auth 可以與 Prisma 開箱即用。*   ![authjs.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/authjs.gif) --- ## 4. [下一個網站地圖](https://github.com/iamvishnusankar/next-sitemap) 在伺服器上部署 NextJS 後,您需要協助 google 索引所有頁面。 如果您可以告訴 Google 您網站上的所有頁面,那就更好了。 為此,您可以建立一個列出所有頁面的 sitemap.xml 檔案。 您可以輕鬆地使用 Next-Sitemap 來實現這一點。   ![sitemap.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/sitemap.gif) --- ## 5. [下一步 SEO](https://github.com/garmeeh/next-seo) SEO 是透過向您的網站預覽提供關鍵字、描述和圖像,使您的網站出現在 Google Feed 上以進行不同查詢的過程。 如果您使用新的 NextJS 應用程式路由器,則可能不需要使用它。 您可以使用他們的“導出元資料”方法或“生成元資料”, 但如果您使用舊的應用程式路由器,這是為您的網站加入 SEO 的最佳方式。   ![seo.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/seo.gif) --- ## 6. [Zod](https://github.com/colinhacks/zod) Zod 是一個物件驗證器(伺服器和客戶端)。 您可以在物件上放置不同的規則並稍後對其進行驗證,例如使用者名稱和密碼,或更複雜的內容(例如陣列長度或其他鍵上的條件)。 *Zod 不是 NextJS 特定的。* 多年來,我看過很多物件驗證器,例如 [Yup](https://github.com/jquense/yup) 和 [class-validator](https://github.com/typestack/class-validator)。 是的,它看起來不像 Zod 那樣維護,並且在使用 NestJS 之類的東西時,類驗證器非常強大 - 所以你最好使用 Zod。   ![zod.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/zod.gif) --- ## 7. [React-hook-form](https://github.com/react-hook-form/react-hook-form) 雖然 Zod 可以驗證物件,但如果沒有自訂邏輯,它不會影響您的用戶端和後端。 React-hook-form 是優秀的用戶端驗證專案(顯示輸入錯誤、管理輸入狀態和提交)。 當然,您可以使用 Zod 作為 React-hook-form 的驗證器。   ![hookform.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/hookform.gif) --- ## 8. [tRPC](https://github.com/trpc/trpc) 我承認我以前從未使用過 tRPC,但今天它似乎吸引了許多人的目光。 它與 Prisma 有類似的概念;它們為您的請求和回應產生一個接口,因此當您使用前端呼叫時,您會獲得自動完成功能。 這很好,因為它減少了錯誤的機會 - 假設您修改了後端路由,您將無法編譯專案 - 客戶端將返回不存在的參數或回應鍵的錯誤。   ![trpc.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/trpc.gif) --- ## 9. [SWR](https://swr.vercel.app) 和 [React-Query](https://github.com/TanStack/query) 多年來我一直使用 Axios 和 fetch 作為發送請求的基礎庫。 SWR 和 React-Query 增強了這些函式庫並提供鉤子、快取、轉換等。 強烈推薦用於每個專案。請注意,這些庫適用於客戶端元件(“使用客戶端”),而不是伺服器元件。   ![query.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/query.gif) --- ## 10. [lodash](https://lodash.com) 這不是 NextJS 特定的函式庫。 它是一個用於改變資料的函式庫,雖然這些年來 JavaScript 憑藉像 flatMap 這樣優秀的原生函數取得了很大的進步,但仍然缺少一些東西,例如按鍵或分塊和陣列的唯一陣列。 我發現自己幾乎在所有專案中都使用 lodash。   ![lodash.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/lodash.gif) --- ## 11. [dayjs](https://day.js.org/) day.js 是一個包含與日期、格式、時區等相關的所有內容的函式庫。 我可能會因為那件事而被烤。我多年來一直在使用“moment.js”。 現在它不再維護了,dayjs 是一個不錯的選擇。 有些人喜歡新的 JS 函數來處理日期,但我仍然覺得 dayjs 選項和原生 JS 日期函數之間存在很大的差距。   ![scrolldown.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/scrolldown.gif) --- ## 12. [jsdom](https://github.com/jsdom/jsdom) 這不是必須的,但我最近在許多專案中都使用它作為 [cheerio](https://github.com/cheeriojs/cheerio) 的替代品。 您可以取得整個頁面內容(`<html><body>....</html>)` 並將其轉換為稍後可以使用「本機」javascript dom 函數`querySelector`、`innerHTML` 等來操作的物件… 非常適合需要一些刮擦的專案。   ![jsdomer.gif](https://nevdav2.dreamhosters.com/wp-content/uploads/2023/12/jsdomer.gif) --- 我們在 X 上連接嗎? :) [我在這裡](https://twitter.com/nevodavid) 您是否為 NextJS 使用其他一些很酷的程式庫? 請在評論中讓我了解它們:) --- 原文出處:https://dev.to/nevodavid/top-12-libraries-for-your-nextjs-project-1oob

Pipeline 🛠️Repos 對決⚔️:Python 🐍 版

## **簡介** 在不斷發展的資料工程和自動化領域,Python 已經出現了多種工作流程編排器。在本文中,我將介紹 6 個 Python 函式庫及其一些主要功能。 ![介紹 GIF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d09jxm34esmawmnv93lt.gif) --- ## 1. [Taipy](https://github.com/Avaiga/taipy) Taipy 是一個開源 Python 庫,用於建立生產就緒的應用程式前端和後端。 對於 Python 開發人員來說,Taipy 是建立管道最簡單的框架之一,這要歸功於它的管道圖形編輯器 (Taipy Studio)。 然後,您可以透過 Python 腳本輕鬆執行和編排管道。一個非常酷的核心功能是每個管道執行都是註冊的。 這可以輕鬆實現假設分析、KPI 監控、資料沿襲等。 🔑特點: - 圖形管道編輯器 - 與 Taipy 前端功能整合以實現端到端部署 - 日程安排 - 管道的版本控制 - 快取等智慧功能 ![太比圖解](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/28kg5av8pk2sdrvi3bl8.png) --- ![QueenB](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ovcc5kqd8xr9uihrggbb.gif) 您的支持意義重大🌱,並且在許多方面為我們帶來了很大的幫助,例如寫文章! 🙏 --- ## 2. [Kedro](https://github.com/kedro-org/kedro) Kedro 是一個開源 Python 框架。 它為生產就緒的資料科學管道提供了一個工具箱。 事實上,Kedro 可以輕鬆地與成熟的 Python ML 庫集成,並提供統一的方法來實現端到端框架。 🔑特點: - 資料目錄 - 筆記本集成 - 專案模板 - 有自己的觀點,因為它強制執行特定的約定 ![Kedro 圖](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w5zfgz36ogrixa1mfqre.png) --- ## 3. [Airflow](https://github.com/apache/airflow) 十多年來,Airflow 一直是管道領域的知名參與者。 Airbnb 建立了 Airflow 來解決資料處理和工作流程需求的內部挑戰。 眾所周知,這個強大的開源平台具有陡峭的學習曲線,但具有廣泛的功能。 該平台允許您透過建立 DAG(有向無環圖)來建立和管理工作流程。 🔑特點: - 基於DAG的定義 - 豐富的基於 Web 的監控 UI:DAG、故障、重試的可視化… - 各種集成 - 動態任務執行和調度 - 由於其以 Python 為中心的特性而具有靈活性。 - 強大的社區 ![氣流圖](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i7a5qgpcnawim32gudhd.png) --- ## 4. [Prefect](https://github.com/PrefectHQ/prefect) Prefect 是一個資料管道開發框架。 Prefect 在策略上將自己定位在與 Airflow 的直接競爭中,以基於簡單性、用戶友好性和靈活性的獨特身份脫穎而出。 如果您想要一個具有各種功能但比 Airflow 更容易學習的成熟產品,Prefect 是一個很好的中間產品。 🔑特點: - 控制面板 - 快取 - 基於流程的結構 - 動態參數化與依賴管理 - 混合執行(本地/雲端) ![完美插畫](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ejmhma39rkmpjg1lwu6z.png) --- ## 5. [Dagster](https://github.com/dagster-io/dagster) Dagster 是本次編譯中較新的函式庫之一,它是一個雲端原生資料管道編排,旨在統一資料整合、工作流程編排和監控。 與其他工具相比,Dagster 強調工作流程建立和管理的 DataOps 面向。 🔑特點: - 聲明式管道設置 - 固執己見的結構 - 版本控制 - 與 Hadoop 集成 - 全面的元資料跟踪 ![Dagster 圖](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/soebsxs4wfwxayzql8wr.png) --- ## 6. [Luigi](https://github.com/spotify/luigi) Luigi 提供了一個資料處理管道框架。 Spotify 與 Airflow 大約在同一時間開發了這個程式庫,以解決其複雜的資料工作流程和管道。 Luigi 專為管理批次作業的複雜通道而設計。如果您正在尋找簡單的東西並且必須快速上手,Luigi 是一個不錯的選擇。 🔑特點: - 內建 Hadoop 支援 - 基於任務的工作流程定義 - 用於依賴管理的中央調度程序 - 任務依賴關係視覺化 ![Luigi 插圖](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxx0ty0vsldbk8unyhqv.png) --- ##結論 隨著 Python 工作流程編排環境的不斷發展,這些工具展示了主要的共同特徵和特定的差異化因素。 所有這些工具都有不同程度的複雜性,了解您的專案和團隊的需求至關重要。 我建議使用非常簡單的範例來測試一些選項,以獲得對每個框架可用性的第一手了解。 --- 希望您喜歡這篇文章! 我是一名新手作家,歡迎任何改進建議! ![新秀圖片](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/25oebh36ha622u74kpli.gif) 如果您有任何疑問,請隨時與我們聯繫。 --- 原文出處:https://dev.to/taipy/the-pipeline-repos-showdown-python-edition-39i5

程式設計師的 100 個願望清單想法

科技愛好者常有遠大的夢想,渴望實現突破創新和個人發展界限的目標。為了幫助建立這些雄心壯志,我為科技愛好者量身定制了廣泛的願望清單。此清單不僅概述了各種目標,還包括「靈感」、「如何」、「年份」、「狀態」和「紀念品」等附加欄,為您的技術之旅提供全面的路線圖。 ## 清單中有什麼? 該清單涵蓋了廣泛的目標,從培養特定技能(例如達到 150 WPM 的打字速度)到雄心勃勃的專案(例如製作 DIY 電動滑板或自行車)。它包括參加重大活動,例如參加大型技術會議,以及個人發展的里程碑,例如指導初級開發人員。每個目標都經過分類以便於參考,涵蓋 DIY 專案、程式設計、開發、學習等領域。 ## 列表 [我的遺願清單進度](https://syki.dev/bucket-list) |目標|類別|靈感|年份|狀態|紀念品| |---------------------------|----------------|----------------| ----|------|--------| |成為大型專案的貢獻者 |開發 |[如何像專業人士一樣開源](https://www.youtube.com/watch?v=MT6M_sqAuZo) | | | | |建立新聞聚合器 |開發 |[Feedly](https://feedly.com/) | | | | |建立實體引擎 |開發 |[我正在從頭開始編寫整個實體引擎](https://www.youtube.com/watch?v=iSMbRGTBOHU) | | | | |建立推薦系統 |開發 |[推薦系統如何運作 (Netflix/Amazon)](https://www.youtube.com/watch?v=n3RKsY2H-NE) | | | | |建構情緒分析工具 |開發 |【2023 如何掌握人工智慧驅動的情緒分析?】(https://brand24.com/blog/sentiment-analysis/) | | | | |建立擴增實境 (AR) 應用程式 |開發 |[關於如何在 2023 年建立擴增實境應用程式的指南](https://www.tekrevol.com/blogs/how-to-build-an-augmented-reality -應用程式/) | | | | |建置與部署聊天伺服器 |開發 |[IRC](https://en.wikipedia.org/wiki/Internet_Relay_Chat) | | | | |建立網路安全工具|開發|[Kali Tools](https://www.kali.org/tools/) | | | | |建立多人線上遊戲|開發|[Dani](https://www.youtube.com/watch?v=_ze26M_Fm6g) | | | | |建立 PWA(漸進式 Web 應用程式)|開發 |[PWA](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) | | | | |建立 AI 影響者 |開發 |[AI 影響者變得非常富有...讓我們建立一個](https://www.youtube.com/watch?v=ky5ZB-mqZKM&t=11s) | | | | |開發區塊鏈應用程式 |開發 |[使用以太坊智能合約和 Solidity 建置您的第一個區塊鏈應用程式](https://www.youtube.com/watch?v=coQ5dg8wM2o) | | | | |開發一種加密貨幣 |開發 |[您需要立即建立一種加密貨幣!!](https://www.youtube.com/watch?v=befUVytFC80) | | | | |開發VR 應用程式|開發|[教學 - 在Meta Quest 耳機上建立您的第一個VR 應用程式](https://developer.oculus.com/documentation/unity/unity-tutorial-hello-vr/) | | | | |開發一個電子商務網站 |開發 |[Next.js Commerce](https://nextjs.org/commerce) | | | | |開發開源遊戲引擎 |開發 |[C++ 中的 GameDev](https://www.youtube.com/watch?v=LyJkcv_rL9Y&list=PLpM-Dvs8t0Va6RoHkaLuPbRh7Fwpy4nbV) | | | | |開發瀏覽器擴充 |開發 |[Chrome 擴充應該會嚇到你。](https://www.youtube.com/watch?v=xIKwkPWUgOA) | | | | |在 github 上有一個擁有 100 顆星的專案 |開發 |[Linus Torvalds](https://github.com/torvalds?achievement=starstruck&tab=achievements) | | | | |擁有自己的 Tor 頁面 |開發 |[我在 Raspberry Pi 上放置了一個 DARK WEB 網站!!](https://www.youtube.com/watch?v=bllS9tkCkaM) | | | | |發明手勢控制介面 |開發 |[鋼鐵人](https://www.youtube.com/watch?v=P5k-4-OEuTk) | | | | |建立持續整合/持續部署管道|開發|[自動化您的工作流程](https://github.com/features/actions)從想法到生產]| | | | |設定 Kubernetes 叢集 |開發 |[為什麼要建置 Raspberry Pi 叢集?](https://www.youtube.com/watch?v=8zXG4ySy1m8) | | | | |贊助開源專案 |開發 |[投資為您的世界提供動力的軟體](https://github.com/sponsors) | | | | |使用查詢機制編寫自己的資料庫|開發|[製作我們自己的資料庫](https://acmiitr.medium.com/making-our-own-database-part-1-6cd9c49ed924) | | | | |在靜態網站產生器中撰寫頁面 |開發 |[Gatsby](https://www.gatsbyjs.com/) | | | | |編寫單頁應用程式 |開發 |[React](https://react.dev/) | | | | |使用 Raspberry Pi 專案實現家居自動化 |DIY |[我建立了一個更聰明的智慧家庭](https://www.youtube.com/watch?v=0rIvB3LZiKA) | | | | |建立自訂鍵盤 |DIY |[建立自己的機械鍵盤......正確的方式](https://www.youtube.com/watch?v=bBon6WwkdJE) | | | | |DIY電動滑板或自行車 |DIY |[我做了一個電動滑板!](https://www.youtube.com/watch?v=3bcvFzecg2Q) | | | | |以自訂遊戲建立迷你街機 |DIY |[終極 DIY 街機指南](https://www.youtube.com/watch?v=oTydZBIGAuk) | | | | |組裝一台 PC |DIY |[為 Minecraft 組裝一台價值 100,000 美元的 PC](https://www.youtube.com/watch?v=AHR80l7od2Q) | | | | |建立個人雲端儲存系統 |DIY |【這是我的終局之戰 - Mother Vault 伺服器機房更新】(https://www.youtube.com/watch?v=pLC0FUnko-M) | | | | |利用 IoT 建造自動澆水花園系統 |DIY |[Arduino 花園控制器 - 自動澆水和資料記錄](https://www.youtube.com/watch?v=O_Q1WKCtWiA) | | | | |打造小型自動駕駛汽車或機器人 |DIY |[快速巡線機器人](https://www.youtube.com/watch?v=lnP32gzHdvI) | | | | |建造水下ROV |DIY |[建造一艘DIY潛水艇](建造一艘DIY潛水艇) | | | | |搭建並飛行 FPV 無人機 |DIY |[為什麼要以 800mW 功率自由式飛行? | [FPV](https://www.youtube.com/watch?v=bBb_kSO3vTo) | | | | |設計具有互動功能的智慧鏡子|DIY |[DIY智慧鏡子 - 完整教學](https://www.youtube.com/watch?v=OYlloiaBINo) | | | | | 以 3D 方式設計和列印一些東西 |DIY |[我在房間中央製作了一個機器人手臂!](https://www.youtube.com/watch?v=nRsaf16EdNM) | | | | |設計您自己的 PCB |DIY |[PCB 建立初學者 - 10 分鐘內開始完成教程](https://www.youtube.com/watch?v=MsdJgEinb34) | | | | |修復損壞的電子產品 |DIY |[ElectroBOOM](https://www.youtube.com/@ElectroBOOM) | | | | |在本地擁有自己的伺服器 |DIY |[為什麼要建立 Raspberry Pi 叢集?](https://www.youtube.com/watch?v=8zXG4ySy1m8) | | | | |實現智慧家庭|DIY |[我建造了一個更聰明的智慧家庭](https://www.youtube.com/watch?v=0rIvB3LZiKA) | | | | |製作機械手臂 |DIY |[我在房間中央製作了機械手臂!](https://www.youtube.com/watch?v=nRsaf16EdNM) | | | | | 用 Flipper Zero 開啟一些東西 |DIY |[這讓駭客攻擊太容易了 - Flipper Zero](https://www.youtube.com/watch?v=nLIp4wd0oXs) | | | | |成為智慧型手機應用程式的擁有者|創業|[VoidLog](https://www.youtube.com/watch?v=LY4rxYe-jKI&list=PLN3n1USn4xllDDLwgJ4avEqgj4dWynofp) | | | | |開發 SaaS 產品 |創業精神|[我如何在一天內建立一個新的 SaaS 產品](https://www.youtube.com/watch?v=v_3lcqUOaOA) | | | | |在店裡擁有自己的遊戲 |創業|[Dani](https://www.youtube.com/watch?v=_ze26M_Fm6g) | | | | |在新創公司工作 |創業精神|[新創工程師在家工作的一天](https://www.youtube.com/watch?v=TLysAkFM4cA) | | | | |一週多相睡眠|創業|| | | | |參加播客 |創業|[Lex Fridman](https://www.youtube.com/lexfridman) | | | | |完全存取伺服器 - 黑客 |黑客 |[先生。機器人](https://www.youtube.com/watch?v=QqknSms8VVI&t=16s) | | | | |獲得錯誤賞金 |駭客 |[HackerOne](https://www.hackerone.com/) | | | | |在奪旗大賽中進行駭客攻擊 |駭客攻擊 |[Mr.機器人](https://www.youtube.com/watch?v=6MrQ-mN8HM8) | | | | |編寫惡意軟體 |駭客 |[惡意軟體開發:進程、執行緒與句柄](https://www.youtube.com/watch?v=aNEqC-U5tHM) | | | | |150 wpm 速度打字 |程式設計 |[Monkeytype](https://github.com/monkeytypegame/monkeytype) | | | | |使用 Python 腳本自動化您的日常任務 |程式設計 |[開始使用 Python 自動化您的生活! (Python檔案管理教學)](https://www.youtube.com/watch?v=NCvI-K0Gp90) | | | | |建立 Twitter 機器人 |程式設計 |[如何使用人工智慧發布熱門推文 // Twitter 機器人教學](https://www.youtube.com/watch?v=V7LEihbOv3Y) | | | | |編譯您自己的 Linux 核心 |程式設計 |[如何編譯自訂 Linux 核心](https://www.youtube.com/watch?v=APQY0wUbBow) | | | | |建立聊天機器人 |程式設計 |[使用深度學習、Python 和 TensorFlow 建立聊天機器人 p.1](https://www.youtube.com/watch?v=dvOnYLDg8_Y&list=PLQVvvaa0QuDdc2k5dwtDTyT9aCja0on8j) | | | | |使用 D3.js 建立資料視覺化專案 |程式設計 |[使用 D3.js 進行資料視覺化 - 完整教學課程](https://www.youtube.com/watch?v=_8V5o2UHG0E) | | | | |建立照片編輯工具 |程式設計 |[如何製作照片編輯應用程式的完整指南](https://www.cleveroad.com/blog/how-to-build-a-photo-editing-app-like-棱鏡並使其蓬勃發展/) | | | | |開發數位藝術作品產生器 |程式設計 |[如何為初學者產生瘋狂的人工智慧藝術(Midjourney V4)](https://www.youtube.com/watch?v=zf4z8A-OWBY) | | | | |開發檔案加密工具 |程式設計 |[製作您自己的加密程式](https://www.youtube.com/watch?v=TZT7wvTeVyY) | | | | |開發影片編輯軟體|程式設計|[我寫了一個影片編輯器(有點糟糕)](https://www.youtube.com/watch?v=iydG-e1dQGA) | | | | |開發語音助理應用程式 |程式設計 |[建立由 OpenAI 和 Python 驅動的 Jarvis | ChatGPT](https://www.youtube.com/watch?v=BEw5EFqCCEI) | | | | |開發智慧手錶應用程式 |程式設計 |[使用 Android Studio 在 WearOS 上建立並執行穿戴式應用程式](https://www.youtube.com/watch?v=-JO5oHRkybk) | | | | |開發您自己的 Slack/Discord 機器人 |程式設計 |[使用 Python 編寫 Discord 機器人 - 在雲端免費託管](https://www.youtube.com/watch?v=SPTfmiYiuok) | | | | |實現臉部辨識系統 |程式設計 |[從紙張到程式碼建立深度臉部辨識應用程式](https://www.youtube.com/watch?v=bK_k7eebGgc&list=PLgNJO2hghbmhHuhURAGbe6KWpiYZt0AMH) | | | | |學習函數式程式語言 |程式設計 |[函數式程式設計 - 概述](https://www.youtube.com/watch?v=8z_bUIl_uPo) | | | | |學習古老的語言 |程式設計 |[100 秒內的 COBOL](https://www.youtube.com/watch?v=7d7-etf-wNI) | | | | |學習並使用 Docker 進行容器化 |程式設計 |[Docker](https://www.docker.com/) | | | | |學習極快的語言 |程式設計 |[ThePrimeagen](https://www.youtube.com/@ThePrimeagen) | | | | |學習一門不尋常的語言(例如 Brainfuck) |程式設計 |[100 秒內完成 Brainfuck](https://www.youtube.com/watch?v=hdHjjBS4cs8) | | | | |學習量子運算基礎 |程式設計 |[在量子電腦上編碼](https://www.youtube.com/watch?v=q3ecPsMd4tA) | | | | |掌握高階演算法與資料結構(100道LeatCode) |程式設計|【569道 Leetcode 題後的我的大腦】(https://www.youtube.com/watch?v=8wysIxzqgPI) | | | | |對自訂語音控製家庭助理進行程式設計 |程式設計 |[建立由 OpenAI 和 Python 提供支援的 Jarvis | ChatGPT](https://www.youtube.com/watch?v=BEw5EFqCCEI) | | | | |對微控制器進行程式設計 |程式設計 |[微控制器程式設計駭客指南 [教學]](https://www.youtube.com/watch?v=XlFO5Iat178) | | | | |在 Vim 中編程 |程式設計 |[Vim 作為你的編輯器](https://www.youtube.com/watch?v=X6AR2RMB5tE&list=PLm323Lc7iSW_wuxqmKx_xxNtJC_hJbQ7R) | | | | |使用分離式鍵盤|編程|[拆箱新鍵盤!!! (也進行打字測試!)](https://www.youtube.com/watch?v=nh-BAxbithc&t=156s) | | | | |網頁抓取資料 |程式 |[使用 AI 和代理網路進行工業規模的網頁抓取](https://www.youtube.com/watch?v=qo_fUjb02ns) | | | | |用組合語言寫程式 |程式設計 |[Tsoding](https://www.youtube.com/watch?v=WnBXLmKk_qw&t=82s) | | | | |寫一個 NPM 模組 |程式設計 |[NPM](https://www.npmjs.com/) | | | | |編寫伺服器端應用程式 |程式設計 |[Next.js](https://nextjs.org/) | | | | |編寫您自己的人工智慧模型 |程式設計 |[讓我們建立 GPT:從頭開始,用程式碼,拼寫出來。](https://www.youtube.com/watch?v=kCc8FmEb1nY) | | | | |編寫您自己的作業系統 |程式設計 |[Linus Torvalds](https://github.com/torvalds) | | | | |寫你自己的程式語言 |程式設計 |[我製作了自己的程式語言](https://www.youtube.com/watch?v=pgeSGBwtHW8&t=132s) | | | | |擁有私人部落格 |教學 |[Dan Abramov](https://overreacted.io/) | | | | |對學生的講座 |教學 |[馬克·祖克柏的 CS50 講座 - 2005 年 12 月 7 日](https://www.youtube.com/watch?v=xFFs9UgOAlE&t=807s) | | | | |指導初級開發人員 |教學 |[如何正確指導初級開發人員](https://stablekernel.com/article/how-to-properly-mentor-a-junior-developer/) | | | | |寫一篇關於科技主題的論文並發表 |教學 |[兩分鐘論文](https://www.youtube.com/@TwoMinutePapers) | | | | |編寫技術書籍或電子書 |教學 |[編寫技術書籍](https://paulcunningham.me/writing-technical-books/) | | | | |參加大型科技會議 |旅遊 |[CES](https://www.ces.tech/) | | | | |參加黑客松 |旅行 |[我挑戰自己贏得黑客馬拉松](https://www.youtube.com/watch?v=mAJlZUKhOGs) | | | | |參觀電腦歷史博物館 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀 NASA 約翰遜航天中心 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀國家航空暨太空博物館 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀歷史科學儀器收藏 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀大型強子對撞機 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀麻省理工學院博物館 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | |參觀國家核科學與歷史博物館 |旅行 |[工程師的旅行願望清單](https://www.mwrf.com/community/article/21848496/the-engineers-travel-bucket-list) | | | | ### 下載列表 [下載 CSV](https://syki.dev/uploads/bucket-list.csv) [下載 JSON](https://syki.dev/uploads/bucket-list.json) ## 附加列解釋 ### 靈感 本專欄反映了是什麼激發了追求特定目標的想法或願望。它可以是一個人、一個事件、一本書,甚至一部電影,點燃了人們對特定成就的熱情。例如,DIY 電動滑板的靈感可能是對永續交通的熱情或最喜歡的科技影片部落客的專案。 ### 年 本專欄提出了實現該目標的時間表或目標年份。它有助於規劃和設定現實的時間表。例如,您可能計劃在 2025 年之前參加一次大型技術會議。 ### 地位 狀態追蹤您的進度。它可以是“未開始”“進行中”“已完成”或“暫停”這有助於追蹤您的旅程並保持動力。 ### 紀念品 本專欄是一個獨特的補充,旨在紀念這一成就。它可以是實體、數位徽章、部落格文章,甚至是照片。例如,組裝 PC 的紀念品可能是已完成設定的第一張照片。 ## 結論 技術愛好者的願望清單不僅僅是目標的集合;這是技術領域個人和職業成長的路線圖。透過附加專欄提供靈感、方法、計時、追蹤和紀念成就的框架,此列表對於任何熱衷於技術的人來說都是一個動態工具。 快樂的科技冒險! --- 原文出處:https://dev.to/syki/100-bucket-list-ideas-for-programmers-506m

每次開發正確的東西並成為 10 倍工程師🏆:編寫 RFC 的藝術🥋

想像一下,您的任務是在您正在開發的產品中實現一項重要的新功能。這就是您一直在等待的機會 - 每個人都會看到您是多麼出色的 10 倍開發人員!你打開一個你想要嘗試的最酷的新庫和設計模式的列表,然後直接進入它,完整的“地下室”模式。一週後,你勝利地出現並提出了你完美的拉取請求! **但是,團隊中的高級開發人員立即拒絕了** - ***「太複雜了,你應該簡單地使用庫 X 並重用 Y。」***。什麼!?顯然,他們不明白你的解決方案有多天才,很快,你就會看到關於你的 PR 的 100 條評論以及接下來幾天的重構。 如果有一種方法可以在實施一切之前了解 X 和 Y 就好了。是的,它就是 RFC! ![RFC發明漫畫](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z50pl0vodfeisluten8n.png) 我們將透過[關於在 Wasp 中實現身份驗證的 RFC](https://www.notion.so/RFC-Auth-without-user-define-entities-6d2925439627456ab01b74ff4b4cd087?pvs=21) 的範例來了解它。 **[Wasp](https://kdta.io/github-wasp-lang-wasp_4) 是一個建置在 React、Node.js 和 Prisma 之上的全棧框架,提供了大量開箱即用的功能這是建置和部署應用程式的最快方法**。它還附帶一個免費的 GPT 支援的程式碼庫產生器 [MAGE](https://usemage.ai/),已用於建立超過 30,000 個應用程式。 讓我們深入了解一下! ## 支持我們! 🙏⭐️ ![GH 星星點擊](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/id9s6t8rcvfxty40bv2m.gif) 如果您覺得這篇文章有幫助,請[考慮在 Github 上給我們一顆星](https://github.com/wasp-lang/wasp)!我們在 Wasp 所做的一切都是開源的,您的支援幫助我們使 Web 開發變得更容易,並激勵我們撰寫更多這樣的文章。 ![支持我們](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qgbmn45pia04bxt6zf83.gif) ## 那麼,什麼是 RFC? ![RFC 概述](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gno8rt4o3ffxhcj72nmk.png) RFC 代表 *Request For Comments*,簡單地表示 **「**提議更改程式碼庫以解決特定問題的文件。」。 **其主要目的是在實施開始之前找到解決問題的最佳方法。** RFC 最初由開源社群採用,但如今,它們幾乎被用於任何類型的開發者組織。 您在業界可能會遇到此類文件的其他名稱,例如 TDD(*技術設計文件*)或 SDD(*軟體設計文件*)。有些人會爭論它們之間的區別,但我們不會。 **有趣的事實**:RFC 是由 IETF(*網路工程任務組*)發明的,該組織是我們今天使用的一些最重要的網路標準和協議背後的工程組織!不算太寒酸吧? ## 什麼時候該寫 RFC,什麼時候可以跳過? ![RFC meme 只需編碼](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1kvwj97oaduwczudc1b.png) 那麼,為什麼要費勁去寫你最終將要編碼的內容,而不是節省時間並簡單地去做呢? **如果您正在處理錯誤或相對簡單的功能,非常清楚必須做什麼並且不會影響專案結構,那麼就不需要 RFC - 啟動 IDE 並開始破解!** 但是,如果您要引入一個全新的概念(例如,引入基於角色的權限系統)或更改專案的架構(例如,新增對執行後台作業的支援),那麼您可能需要在輸入「git」之前退一步checkout -b my-new-feature` 並深入到那個甜蜜的編碼區域。 綜上所述,有時很難確定是否應該編寫該 RFC。也許這是一個更突出的功能,但你以前做過類似的事情,並且你已經在頭腦中規劃好了一切,而且幾乎沒有任何疑問。為了解決這個問題,我喜歡使用以下一個簡單的啟發式方法:**是否有不只一種明顯的方法來實現此功能?我們是否必須選擇一個新的庫/服務?** 如果這兩個問題的答案都是“否”,那麼您可能不需要 RFC。否則,需要進行討論,而 RFC 是解決問題的方法。做吧。 ![RFC 流程圖 - 何時撰寫](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2a956hqeyai31igbl92q.png) ## 這對我有什麼好處? 我們已經確定瞭如何決定「何時」編寫 RFC,但這也是您應該這樣做的「原因」: - **你將整理你的想法並變得清晰**。如果您決定編寫 RFC,則表示您正在處理一個不平凡的開放式問題。把事情寫下來將有助於提煉你的想法並客觀地看待它們。 - **與剛開始編碼相比,你會學到更多**。你會給自己空間去探索不同的方法,常常會發現一些你原本沒有想到的東西。 - **您將眾包團隊的知識。** 透過向您的團隊尋求回饋(因此請求評論),您將全面了解您正在解決的問題並填補任何剩餘的空白。 - **您將增進團隊對程式碼庫的理解。** 透過在 RFC 上進行協作,團隊中的每個人都會了解您正在做什麼以及最終是如何做到的。這意味著下次有人必須接觸那部分程式碼時,他們將需要問你更少的問題(===更多不間斷的程式碼時間!)。 - **公關審查將會*更*順利**。還記得本文開頭的情況嗎?當你的 PR 因為「太複雜」而被拒絕時?這是因為審閱者忽略了上下文,並且您在沒有獲得團隊其他成員事先支持的情況下進行了相當大的更改。透過先編寫 RFC,您將永遠不會再遇到這種情況。 - **您的文件已經完成了 50%!** 需要明確的是,RFC 不是最終文件,您不能簡單地指出它,但您可以重複使用很多內容 - 圖像、圖表、段落等。 哇,這聽起來太棒了,我現在就想提出一個新功能,這樣我就可以為其編寫 RFC!開個玩笑,首先瀏覽 RFC 會讓編碼部分變得更加有趣 - 你確切地知道你需要做什麼,並且你不需要質疑你的方法以及建立 PR 後將如何接收它。 ## 好吧,好吧,我被賣了!那麼,我該如何寫一篇呢? 很高興你問了!使用了許多不同的格式,或多或少是正式的,但我更喜歡保持簡單。我們在 Wasp 編寫的 RFC 不遵循嚴格的格式,但有一些共同的部分: - **元資料** - 標題、日期、審稿人等… - **問題/目標** - 你要解決什麼 - **建議的解決方案**(或更多) - **實施概述** - **評論/開放式問題** 這幾乎就是它的要點!其中每一個都可以進一步細分和細化,但這是您可以開始的基本輪廓。 ## 元資料 ⌗ ![RFC 元資料範例](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5e894wa2xsw57or0q8oa.png) 這是非常不言自明的 - 您可能想要追蹤有關 RFC 的一些基本資訊 - 狀態、建立日期等。 一些模板還明確列出了審查者以及他們對RFC 的「批准」狀態- 我們沒有它,因為我們是一個溝通速度很快的小團隊,但對於不是每個人都認識每個人的大型團隊來說,它可以很方便,並且您希望有更多的流程(例如,在指導初級開發人員時)。 ![RFC 明確審閱者範例](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0l4elf6a5xtpa567bfg3.png) ## 問題🤔 這就是事情變得有趣的地方。 **您對問題或需要實現的目標/功能以及為什麼需要這樣做的定義越好,以下所有步驟就會越容易**。因此,即使在開始編寫 RFC 之前,這也是值得投資的事情 - 確保與所有相關方(例如產品所有者、其他開發人員,甚至用戶)進行交談,以加深您對要解決的問題的理解。 透過這樣做,您也很可能獲得有關可能解決方案的初步提示和指示,並對您所處的問題空間有一個粗略的認識。 ![RFC 問題定義](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cx3bm2x24hf2z22sl88n.png) 以下是上面範例中的一些提示: - **從高級摘要開始** - 這樣,讀者可以快速決定這是否與他們相關以及是否應該繼續閱讀。 - **提供一些背景** - 解釋一下世界的現狀。這可以是單一句子或整個章節,這取決於目標受眾。 - **清楚地陳述問題/目標** - 解釋為什麼會出現問題並將其與用戶/公司的痛苦聯繫起來,以便動機明確。 - **如果可能的話,提供額外的細節** - 圖表、程式碼範例… → 任何可以幫助讀者更快到達「頓悟」時刻的內容。使用可折疊部分的額外要點是,RFC 的中心部分保持可消化的長度。 如果您完成了所有這些,那麼您已經踏上了通往優秀 RFC 的道路!由於明確定義問題至關重要,所以不要害怕加入更多問題並進一步分解問題。 ### 非目標🛑 這是“問題”的子部分,有時非常有價值。在此程式碼庫變更中編寫我們不想要或不會做的事情可以幫助設定期望並更好地定義其範圍。 例如,如果我們正在努力為我們的應用程式加入基於角色的身份驗證系統,人們可能會認為我們還將為其建立某種管理面板來管理使用者和新增/刪除角色。透過明確聲明不會完成(並簡要解釋原因 - 不需要,這會花費太長時間,...),審查者將更好地理解您的目標是什麼,並且您將跳過不必要的討論。 ## 解決方案與實作🛠️ 一旦我們知道我們想做什麼,我們就必須找出最好的方法!您可能已經在“問題”部分暗示了可能的解決方案,但現在是更深入研究的時候了 - 研究不同的方法,評估它們的優缺點,並概述它們如何適合現有系統。 這一部分可能是最自由的形式 - 因為它很大程度上取決於您正在做的事情的性質,所以在這裡施加許多限制是沒有意義的。您可能希望停留在更高的水平,例如係統架構,或者您可能需要深入研究程式碼並開始編寫您需要的部分程式碼。因此,我沒有一個確切的格式供您遵循,而是一組指南: ### 寫偽程式碼 RFC 的目的是傳達想法和原則,而不是編譯和涵蓋所有邊緣情況的生產級程式碼。隨意發明/想像/草繪任何您需要的東西(例如,想像您已經有一個發送電子郵件的功能並使用它,即使您沒有),並且不要用實現細節來妨礙您自己或讀者(除非這正是RFC 的內容)。 最好從較高的級別開始,然後當您意識到需要它或其中一位審閱者建議時再深入。 ### 了解其他人是如何做到的 ![尋找現有解決方案](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ab8elwlb8o2ap85wi72r.png) 根據您正在開發的產品類型,您發現這一點的方式可能會有所不同,但幾乎總是有一種方法可以做到這一點。如果您正在開發像 [Wasp](https://github.com/wasp-lang/wasp) 這樣的開源工具,您可以簡單地查看其他流行的解決方案(也是開源的)並了解它們是如何做到的它。如果您正在開發 SaaS,並且需要弄清楚是否使用 cookie 還是 JWT 進行身份驗證,您可能有一些朋友以前這樣做過,您可以詢問他們。最後,只需 Google/GPT 即可。 為什麼這麼有幫助? **原因是它讓您(和審閱者)對您的解決方案充滿信心。如果其他人以這種方式成功做到了,這可能是一個有前途的方向。**它還可能幫助您發現以前沒有想到的方法,或者作為您可以建置的基礎。當然,永遠不要認為任何事情都是理所當然的,並考慮到您情況的具體需要,而一定要利用他人的知識和專業知識。 ### 留下未完成的事情並保持骯髒 RFC 的要點是「C」部分,因此協作(是的,我知道它實際上代表「_comments_」)。這不是一個你必須獲得滿分並且不問任何問題的測試 - 如果發生這種情況,你可能一開始就不應該編寫 RFC。 解決問題需要團隊的努力,而你只是第一個嘗試解決問題並推動事情向前發展的人。您的任務是盡可能合理地奠定基礎(完善問題,探索解決問題的多種方法,辨識發現的新子問題),以便審閱者可以快速掌握狀態並提供有效的反饋,指導需要的地方最多。 **RFC 的主要工作是確定最重要的問題並將審閱者的注意力引導到這些問題上,而不是解決它們。** 您正在編寫的 RFC 應該被視為一個討論區和一個正在進行的工作,而不是一件在展示在觀眾面前之前必須完善的藝術品。 ## 評論和開放式問題 🎯 在文件的最後一部分中,您可以總結主要思想並突出顯示最大的未決問題。在瀏覽所有內容之後,提醒讀者他的注意力在哪裡最有價值可能會有所幫助。 ## 現在我知道何時以及如何寫 RFC!您有任何我可以用作起點的模板嗎? 當然!如前所述,我們的格式非常輕量級,但請隨意查看[我們用作示例的 RFC](https://wasp-lang.notion.site/RFC-Auth-without-user-define-entities-6d2925439627456ab01b74ff4b4cd087?pvs=4) 獲得靈感。您的公司也可能已經有他們推薦的現成範本。 以下是您可以使用和/或適應您的需求的一些內容: - [Squarespace RFC 範本](https://engineering.squarespace.com/s/Squarespace-RFC-Template.pdf) - _您有推薦的範本嗎?我很高興將其列在這裡!_ ## 我應該使用什麼工具來寫 RFC?有這麼多選擇! 您使用的確切工具可能是 RFC 中最不重要的部分,但它仍然很重要,因為它圍繞它設置了工作流程。如果您的公司已經選擇了一種工具,那麼當然要堅持使用。如果沒有,以下是我遇到的最常見的選擇,以及快速評論: - **Google 文件** - 經典選擇。超級容易對文件的任何部分進行評論,這是最重要的功能。 - **概念** - 也非常適合協作,此外還提供一些 Markdown 元件,例如可折疊和表格,這可以使您的 RFC 更具可讀性。 - **Github 問題/PR** - 有時會使用它,特別是對於 OSS 專案。缺點是很難對文件的特定部分進行註釋(只能對整行進行註釋),而且插入圖表也相當笨拙。優點是所有內容(程式碼和 RFC)都保留在同一個平台上 我們目前使用 Notion,但以上任何一個都可以是不錯的選擇。 ## 概括 ![這是包裝](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yq0qybvnkxbu9awz35bw.gif) 正如在 RFC 末尾編寫摘要是最佳實踐一樣,我們也會在這裡做同樣的事情!這篇文章比我預期的要長,但是有很多東西要提到 - 我希望你會發現它有用! 最後,**能夠清楚地表達你的想法,提出問題,並根據團隊的反饋客觀地分析可能的解決方案,這將幫助你開發正確的東西,這是最終的生產力黑客**。 這就是您成為真正的 10 倍工程師的方法。感謝您的閱讀,下次再見! --- 原文出處:https://dev.to/wasp/develop-the-right-thing-every-time-and-become-a-10x-engineer-the-art-of-writing-rfcs-2mc6

使用 Jest 和 Supertest 測試 NodeJs/Express API

這是[使用 Express、Sequelize 和 Postgres 建立 API](https://www.oriechinedu.com/posts/performing-crud-with-sequelize/) 的第三部分。在[第二部分](https://www.oriechinedu.com/posts/performing-crud-with-sequelize/)中,我們建立了簡單的API端點來示範Sequelize中的CRUD操作。在本文中,我們將重點放在為第二部分中建立的 API 端點編寫端到端測試。 ### 術語解釋 - **端對端測試** - 一種測試類型,用於測試應用程式從開始到結束的流程是否如預期運作。這也稱為功能測試。此類測試的一個範例是測試端點或路由,其中涉及測試端點工作所需的所有內容,例如資料庫連接、依賴項等。 - **測試執行器** - 在給定目錄或檔案中取得原始程式碼(測試)、執行測試並將結果寫入控制台或任何指定位置的程式庫或工具,例如 Jest、Mocha。 - **Jest** - [Jest](https://jestjs.io/) 是 Facebook 開發的 JavaScript 測試框架。它以最少的配置開箱即用,並具有內建的測試執行器、斷言庫和模擬支援。 - [**Supertest**](https://www.npmjs.com/package/supertest) - 用於測試 Node.js HTTP 伺服器的函式庫。它使我們能夠以程式設計方式向 HTTP 伺服器發送 HTTP 請求(例如 GET、POST、PATCH、PUT、DELETE)並獲取結果。 現在我們已經解釋了基本術語,讓我們深入了解主要業務。 如果您一直按照先前的[文章](https://www.oriechinedu.com/posts/performing-crud-with-sequelize/)進行操作,那麼請在您最喜歡的文字編輯器中開啟它,否則克隆使用的儲存庫[此處](https://github.com/oriechinedu/sequelize-with-postgres-tutorial)。 **第 1 步 - 安裝 Jest 和 supertest** 打開終端機並“cd”到專案根目錄並執行以下命令: ``` npm install --save-dev jest supertest ``` **步驟 2 - 設定 Jest** 打開“package.json”並將以下程式碼新增至其中。 ``` "jest": { "testEnvironment": "node", "coveragePathIgnorePatterns": [ "/node_modules/" ] }, ``` 這是我們測試 API 時需要設定 jest 的基本配置。您希望 `jest` 忽略的任何檔案都放置在 `"coveragePathIgnorePatterns"` 內。 `"coveragePathIgnorePatterns"` 指定一個與要排除的目錄相符的正規表示式,在我們的例子中,我們希望它忽略 `node_modules` 目錄。 接下來我們新增“test”腳本。在 `package.json` 的 `scripts` 部分中,加入以下腳本: ``` "test": "jest" ``` **步驟 3 - 測試配置** 現在,讓我們確認「jest」已準備好執行我們的測試。在終端機中執行“npm test”。您會注意到控制台上列印如下所示的錯誤,這表示「jest」已設定。 ![未指定測試時出現 Jest 錯誤](https://thepracticaldev.s3.amazonaws.com/i/sftb9nkpi1jk076d09xy.png '未指定測試時出現 Jest 錯誤') 讓我們新增一個簡單的測試來驗證配置。建立一個名為「tests」的新目錄並新增一個新檔案「sample.test.js」。在「sample.test.js」中,加入以下程式碼: ``` describe('Sample Test', () => { it('should test that true === true', () => { expect(true).toBe(true) }) }) ``` 現在,執行“npm test”,您將得到如下所示的輸出: ![範例測試輸出](https://thepracticaldev.s3.amazonaws.com/i/4zmad3nkc8stnfyescvi.png) ##### Jest 如何辨識測試檔? Jest 以三種方式辨識測試文件: - 副檔名為「.test.js」的文件 - 副檔名為「.spec.js」的文件 - `__tests__` 資料夾或目錄中的所有檔案。 ## 測試 API 端點 現在我們已經設定了測試環境,是時候開始測試 API 端點了。由於我們的端點需要向資料庫發出請求,因此我們需要設定一個測試資料庫。設定測試資料庫的原因是每次執行測試時我們都會刪除該資料庫。每次執行測試時刪除資料庫可確保測試的完整性。也就是說,如果一個測試是在資料庫中建立一個「post」記錄,我們要確保在測試執行之前資料庫中沒有「post」記錄,這樣,我們就可以確定得到的結果從測試中。 **第 4 步 - 建立測試資料庫** 在本文的[第一部分](https://www.oriechinedu.com/posts/getting-started-with-sequelize-and-postgres/)中,我們建立了兩個資料庫,一個用於開發,另一個用於測試。如果您尚未建立測試資料庫,請依照[連結](https://www.oriechinedu.com/posts/getting-started-with-sequelize-and-postgres/)建立測試資料庫。 **步驟 5 - 設定測試腳本** 我們需要以下腳本: - `pretest` - `pretest` 是一個 npm 腳本,當呼叫 `npm test` 指令時會自動呼叫。我們將掛接命令來更改環境以進行測試,並在每次測試執行之前刷新資料庫。 - `migrate:reset`:此命令將負責在每次測試執行之前刷新資料庫。 現在編輯“package.json”的“scripts”,如下所示: ``` "scripts": { "start-dev": "nodemon index.js", "migrate": "npx sequelize-cli db:migrate", "migrate:reset": "npx sequelize-cli db:migrate:undo:all && npm run migrate", "test": "cross-env NODE_ENV=test jest --testTimeout=10000", "pretest": "cross-env NODE_ENV=test npm run migrate:reset" } ``` 腳本修改需要注意的地方: - [`cross-env`](https://www.npmjs.com/package/cross-env) - 用於設定環境變數的與作業系統無關的套件。我們用它將`NODE_ENV`設為`test`,以便我們的測試可以使用測試資料庫。執行以下命令來安裝跨環境。 ``` npm i -D cross-env ``` - `--testTimeout` 標誌 - 這會增加 Jest 的預設逾時時間,即 5000 毫秒。這很重要,因為測試執行者需要在執行測試之前刷新資料庫。 **第 6 步 - 測試腳本** ``` npm test ``` 如果一切正常,您應該在終端機上看到以下輸出: ![替代文本](https://thepracticaldev.s3.amazonaws.com/i/2panajlcgx7dg0d2oe88.png) 仔細觀察上面的螢幕截圖,您會注意到一行 _`using environment "test"`_ 表示 `cross-env` 已更改了 `NODE_ENV`。 **最後一步 - 測試路由/端點** 現在,讓我們開始為端點編寫測試。在測試目錄中建立一個名為routes.test.js的文件 ``` touch tests/routes.test.js ``` - **測試建立後端點** 將以下程式碼複製到“tests/routes.test.js”中: ``` const request = require('supertest') const app = require('../server') describe('Post Endpoints', () => { it('should create a new post', async () => { const res = await request(app) .post('/api/posts') .send({ userId: 1, title: 'test is cool', }) expect(res.statusCode).toEqual(201) expect(res.body).toHaveProperty('post') }) }) ``` - `describe` 函數用於將相關測試分組在一起 - `it` 是執行實際測試的 `test` 函數的別名。 -“expect”函數使用一組“matcher”函數測試值。 請造訪 [Jest 文件](https://jestjs.io/docs/en/api.html) 以取得 jest 函數的完整清單和詳細資訊。 現在,執行測試 ``` npm test ``` 輸出如下圖所示: ![替代文本](https://thepracticaldev.s3.amazonaws.com/i/yt6o537gzw29030siy6c.png) 有關所有端點測試的完整程式碼,請檢查[儲存庫](https://github.com/oriechinedu/sequelize-with-postgres-tutorial/blob/master/tests/routes.test.js)。 ### 結論 我們已經能夠完成為與資料庫互動的 API 端點編寫測試的過程。在本文的最後部分,我將撰寫有關將 CI/CD 和程式碼覆蓋工具整合到測試環境的文章。在那之前請繼續關注。 如果您對改進文章有任何疑問或建議,請隨時與我聯繫。您也可以透過下面的評論部分分享您的想法。謝謝! _本文最初發表在我的[部落格](https://www.oriechinedu.com/posts/testing-nodejs-express-api-with-jest-and-supertest/)_ --- 原文出處:https://dev.to/nedsoft/testing-nodejs-express-api-with-jest-and-supertest-1km6

🧙‍♂️ 使用 ChatGPT 助理產生部落格 🪄 ✨

# 長話短說;博士 我們都已經看到了 ChatGPT 的功能(這對任何人來說都不陌生)。 很多文章都是使用 ChatGPT 一遍又一遍地寫的。 **實際上**,DEV 上的文章有一半是用 ChatGPT 寫的。 你可以使用一些[AI內容偵測器](https://copyleaks.com/ai-content- detector)來檢視。 問題是,ChatGPT 永遠不會產生一些非凡的內容,除了它內部已經有(經過訓練/微調)的內容。 但有一種方法可以超越目前使用 RAG(OpenAI 助理)訓練的內容。 [上一篇](https://dev.to/triggerdotdev/train-chatgpt-on-your-documentation-1a9g),我們討論了在您的文件上「訓練」ChatGPT;今天,讓我們看看如何從中製作出很多內容。我們將: - 使用 Docusaurus 建立新的部落格系統。 - 詢問 ChatGPT,為我們寫一篇與文件相關的部落格文章。 ![部落格](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ms26qb0uahpi898s0qun.gif) --- ## 你的後台工作平台🔌 [Trigger.dev](https://trigger.dev/) 是一個開源程式庫,可讓您使用 NextJS、Remix、Astro 等為您的應用程式建立和監控長時間執行的作業! &nbsp; [![GiveUsStars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bm9mrmovmn26izyik95z.gif)](https://github.com/triggerdotdev/trigger.dev) 請幫我們一顆星🥹。 這將幫助我們建立更多這樣的文章💖 {% cta https://github.com/triggerdotdev/trigger.dev %} 為 Trigger.dev 儲存庫加註星標 ⭐️ {% endcta %} --- ## 上次回顧 ⏰ - 我們建立了一個作業來取得文件 XML 並提取所有 URL。 - 我們抓取了每個網站的 URL 並提取了標題和內容。 - 我們將所有內容儲存到文件中並將其發送給 ChatGPT 助手。 - 我們建立了一個 ChatBot 畫面來詢問 ChatGPT 有關文件的資訊。 您可以在此處找到上一個[教學]的完整原始程式碼(https://github.com/triggerdotdev/blog/tree/main/openai-assistant)。 --- ![工具](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4adju83b5s1k0qozh3x.png) ## 稍作修改⚙️ 上次,我們建立了一個文件助理。我們寫: ``` You are a documentation assistant, loaded with documentation from ' + payload.url + ', return everything in an MD format. ``` 讓我們將其更改為部落格作者,請轉到“jobs/process.documentation.ts”第 92 行,並將其替換為以下內容: ``` You are a content writer assistant. You have been loaded with documentation from ${payload.url}, you write blog posts based on the documentation and return everything in the following MD format: --- slug: [post-slug] title: [post-title] --- [post-content] ``` 使用“slug”和“title”非常重要,因為這是 Docusaurus 的格式 - 我們的部落格系統可以接受(當然,我們也以 MD 格式發送所有輸出) --- ![Docusaurus](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gu8wlh7qk8e3rh6mz35v.png) ## 多庫龍🦖 您可以使用多種類型的部落格系統! 對於我們的用例,我們將使用 Docusaurus,它可以讀取基於 MD 的格式(我們從 ChatGPT 請求的輸出)。 **我們可以透過執行來安裝 Docusaurus:** ``` npx create-docusaurus@latest blog classic --typescript ``` 接下來,我們可以進入已建立的目錄並執行以下命令: ``` npm run start ``` 這將啟動 Docusaurus。你可以關註一下。還有一個名為“blog”的附加目錄,其中包含所有部落格文章;這是我們保存 ChatGPT 產生的部落格文章的地方。 ![範例](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pgo25rlkw85nfvbh0y4s.png) --- ![部落格](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v3oxjtli1dn9i9klnj5t.png) ## 產生部落格 📨 我們需要創造一個就業機會 - 取得部落格標題 - 使用 ChatGPT 產生完整的部落格文章 - 將其保存到我們部落格上的 MD 文件中 我們可以輕鬆地使用 ChatGPT 來實現這一點! 前往“jobs”資料夾並新增一個名為“process.blog.ts”的新檔案。新增以下程式碼: ``` import { eventTrigger } from "@trigger.dev/sdk"; import { client } from "@openai-assistant/trigger"; import {object, string} from "zod"; import {openai} from "@openai-assistant/helper/open.ai"; import {writeFileSync} from "fs"; import slugify from "slugify"; client.defineJob({ // This is the unique identifier for your Job, it must be unique across all Jobs in your project. id: "process-blog", name: "Process Blog", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.blog.event", schema: object({ title: string(), aId: string(), }) }), integrations: { openai }, run: async (payload, io, ctx) => { const {title, aId} = payload; const thread = await io.openai.beta.threads.create('create-thread'); await io.openai.beta.threads.messages.create('create-message', thread.id, { content: ` title: ${title} `, role: 'user', }); const run = await io.openai.beta.threads.runs.createAndWaitForCompletion('run-thread', thread.id, { model: 'gpt-4-1106-preview', assistant_id: payload.aId, }); if (run.status !== "completed") { console.log('not completed'); throw new Error(`Run finished with status ${run.status}: ${JSON.stringify(run.last_error)}`); } const messages = await io.openai.beta.threads.messages.list("list-messages", run.thread_id, { query: { limit: "1" } }); return io.runTask('save-blog', async () => { const content = messages[0].content[0]; if (content.type === 'text') { const fileName = slugify(title, {lower: true, strict: true, trim: true}); writeFileSync(`./blog/blog/${fileName}.md`, content.text.value) return {fileName}; } }); }, }); ``` - 我們加入了一些必要的變數: - `title` 部落格文章標題 - `aId` 上一篇文章中新增的助手 ID。 - 我們為助手建立了一個新線程(`io.openai.beta.threads.create`) - 我們無法在沒有任何線程的情況下質疑它。與之前的教程不同,在這裡,我們對每個請求建立一個新線程。我們不需要對話中最後一條訊息的上下文。 - 然後,我們使用部落格標題為線程(`io.openai.beta.threads.messages.create`)新增訊息。我們不需要提供額外的說明 - 我們已經在第一部分完成了該部分😀 - 我們執行 `io.openai.beta.threads.runs.createAndWaitForCompletion` 來啟動進程 - 通常,您需要某種每分鐘執行一次的遞歸來檢查作業是否完成,但是 [Trigger.dev]( http://Trigger .dev)已經加入了一種執行進程並同時等待它的方法🥳 - 我們在查詢正文中執行帶有“limit: 1”的“io.openai.beta.threads.messages.list”,以從對話中獲取第一則訊息(在ChatGPT 結果中,第一則訊息是最後一條訊息) 。 - 然後,我們使用「writeFileSync」從 ChatGPT 取得的值來儲存新建立的部落格 - 確保您擁有正確的部落格路徑。 轉到“jobs/index.ts”並加入以下行: ``` export * from "./process.blog"; ``` 現在,讓我們建立一個新的路由來觸發該作業。 前往“app/api”,建立一個名為“blog”的新資料夾,並在一個名為“route.tsx”的新檔案中 新增以下程式碼: ``` import {client} from "@openai-assistant/trigger"; export async function POST(request: Request) { const payload = await request.json(); if (!payload.title || !payload.aId) { return new Response(JSON.stringify({error: 'Missing parameters'}), {status: 400}); } // We send an event to the trigger to process the documentation const {id: eventId} = await client.sendEvent({ name: "process.blog.event", payload }); return new Response(JSON.stringify({eventId}), {status: 200}); } ``` - 我們檢查標題和助理 ID 是否存在。 - 我們在 [Trigger.dev](http://Trigger.dev) 中觸發事件並發送訊息。 - 我們將事件 ID 傳送回客戶端,以便我們可以追蹤作業的進度。 --- ![前端](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kgh52s7mxd20w91kr3c9.png) ## 前端🎩 沒什麼好做的! 在我們的「components」目錄中,建立一個名為「blog.component.tsx」的新檔案和以下程式碼: ``` "use client"; import {FC, useCallback, useEffect, useState} from "react"; import {ExtendedAssistant} from "@openai-assistant/components/main"; import {SubmitHandler, useForm} from "react-hook-form"; import {useEventRunDetails} from "@trigger.dev/react"; interface Blog { title: string, aId: string; } export const BlogComponent: FC<{list: ExtendedAssistant[]}> = (props) => { const {list} = props; const {register, formState, handleSubmit} = useForm<Blog>(); const [event, setEvent] = useState<string | undefined>(undefined); const addBlog: SubmitHandler<Blog> = useCallback(async (param) => { const {eventId} = await (await fetch('/api/blog', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(param) })).json(); setEvent(eventId); }, []); return ( <> <form className="flex flex-col gap-3 mt-5" onSubmit={handleSubmit(addBlog)}> <div className="flex flex-col gap-1"> <div className="font-bold">Assistant</div> <select className="border border-gray-200 rounded-xl py-2 px-3" {...register('aId', {required: true})}> {list.map(val => ( <option key={val.id} value={val.aId}>{val.url}</option> ))} </select> </div> <div className="flex flex-col gap-1"> <div className="font-bold">Title</div> <input className="border border-gray-200 rounded-xl py-2 px-3" placeholder="Blog title" {...register('title', {required: true})} /> </div> <button className="border border-gray-200 rounded-xl py-2 px-3 bg-gray-100 hover:bg-gray-200" disabled={formState.isSubmitting}>Create blog</button> </form> {!!event && ( <Blog eventId={event} /> )} </> ) } export const Blog: FC<{eventId: string}> = (props) => { const {eventId} = props; const { data, error } = useEventRunDetails(eventId); if (data?.status !== 'SUCCESS') { return <div className="pointer bg-yellow-300 border-yellow-500 p-1 px-3 text-yellow-950 border rounded-2xl">Loading</div> } return ( <div> <a href={`http://localhost:3000/blog/${data.output.fileName}`}>Check blog post</a> </div> ) }; ``` - 我們使用「react-hook-form」來輕鬆控制我們的輸入。 - 我們讓使用者選擇他們想要使用的助手。 - 我們建立一個包含文章標題的新輸入。 - 我們將所有內容傳送到先前建立的路由並傳回作業的「eventId」。 - 我們建立一個新的「<Blog />」元件,該元件顯示載入直到事件完成,並使用新建立的教程新增指向我們部落格的連結。 將元件加入我們的“components/main.tsx”檔案中: ``` {assistantState.filter(f => !f.pending).length > 0 && <BlogComponent list={assistantState} />} ``` 我們完成了! ![完成](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fkm37v5idrxexjje2u3o.png) 現在,讓我們新增部落格標題並點擊「生成」。 ![部落格](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gosm1f1ttz3q1m0atu7s.png) --- ![圖片](https://res.cloudinary.com/practicaldev/image/fetch/s--uTFwMeAp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3。 amazonaws.com/uploads/articles/0half2g6r5zfn7asq084.png) ## 讓我們聯絡吧! 🔌 作為開源開發者,您可以加入我們的[社群](https://discord.gg/nkqV9xBYWy) 做出貢獻並與維護者互動。請隨時造訪我們的 [GitHub 儲存庫](https://github.com/triggerdotdev/trigger.dev),貢獻並建立與 Trigger.dev 相關的問題。 本教學的源程式碼可在此處取得: https://github.com/triggerdotdev/blog/tree/main/openai-blog-writer 感謝您的閱讀! --- 原文出處:https://dev.to/triggerdotdev/generate-blogs-with-chatgpt-assistant-1894

✨ 用您的文件訓練 ChatGPT 🪄 ✨

# 簡介 ChatGPT 訓練至 2022 年。 但是,如果您希望它專門為您提供有關您網站的資訊怎麼辦?最有可能的是,這是不可能的,**但不再是了!** OpenAI 推出了他們的新功能 - [助手](https://platform.openai.com/docs/assistants/how-it-works)。 現在您可以輕鬆地為您的網站建立索引,然後向 ChatGPT 詢問有關該網站的問題。在本教程中,我們將建立一個系統來索引您的網站並讓您查詢它。我們將: - 抓取文件網站地圖。 - 從網站上的所有頁面中提取資訊。 - 使用新資訊建立新助理。 - 建立一個簡單的ChatGPT前端介面並查詢助手。 ![助手](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ekre38der95twom33tqb.gif) --- ## 你的後台工作平台🔌 [Trigger.dev](https://trigger.dev/) 是一個開源程式庫,可讓您使用 NextJS、Remix、Astro 等為您的應用程式建立和監控長時間執行的作業!   [![GiveUsStars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bm9mrmovmn26izyik95z.gif)](https://github.com/triggerdotdev/trigger.dev) 請幫我們一顆星🥹。 這將幫助我們建立更多這樣的文章💖 --- ## 讓我們開始吧🔥 讓我們建立一個新的 NextJS 專案。 ``` npx create-next-app@latest ``` >💡 我們使用 NextJS 新的應用程式路由器。安裝專案之前請確保您的節點版本為 18+ 讓我們建立一個新的資料庫來保存助手和抓取的頁面。 對於我們的範例,我們將使用 [Prisma](https://www.prisma.io/) 和 SQLite。 安裝非常簡單,只需執行: ``` npm install prisma @prisma/client --save ``` 然後加入架構和資料庫 ``` npx prisma init --datasource-provider sqlite ``` 轉到“prisma/schema.prisma”並將其替換為以下架構: ``` // This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model Docs { id Int @id @default(autoincrement()) content String url String @unique identifier String @@index([identifier]) } model Assistant { id Int @id @default(autoincrement()) aId String url String @unique } ``` 然後執行 ``` npx prisma db push ``` 這將建立一個新的 SQLite 資料庫(本機檔案),其中包含兩個主表:“Docs”和“Assistant” - 「Docs」包含所有抓取的頁面 - `Assistant` 包含文件的 URL 和內部 ChatGPT 助理 ID。 讓我們新增 Prisma 客戶端。 建立一個名為「helper」的新資料夾,並新增一個名為「prisma.ts」的新文件,並在其中新增以下程式碼: ``` import {PrismaClient} from '@prisma/client'; export const prisma = new PrismaClient(); ``` 我們稍後可以使用“prisma”變數來查詢我們的資料庫。 --- ![ScrapeAndIndex](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fc05wtlc4peosr62ydnx.png) ## 刮擦和索引 ### 建立 Trigger.dev 帳戶 抓取頁面並為其建立索引是一項長期執行的任務。 **我們需要:** - 抓取網站地圖的主網站元 URL。 - 擷取網站地圖內的所有頁面。 - 前往每個頁面並提取內容。 - 將所有內容儲存到 ChatGPT 助手中。 為此,我們使用 Trigger.dev! 註冊 [Trigger.dev 帳號](https://trigger.dev/)。 註冊後,建立一個組織並為您的工作選擇一個專案名稱。 ![pic1](https://res.cloudinary.com/practicaldev/image/fetch/s--B2jtIoA6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bdnxq8o7el7t4utvgf1u.jpeg) 選擇 Next.js 作為您的框架,並按照將 Trigger.dev 新增至現有 Next.js 專案的流程進行操作。 ![pic2](https://res.cloudinary.com/practicaldev/image/fetch/s--K4k6T6mi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4kt7e5r1mwg60atqfka.jpeg) 否則,請點選專案儀表板側邊欄選單上的「環境和 API 金鑰」。 ![pic3](https://res.cloudinary.com/practicaldev/image/fetch/s--Ysm1Dd0r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ser7a2j5qft9vw8rfk0m.png) 複製您的 DEV 伺服器 API 金鑰並執行下面的程式碼片段來安裝 Trigger.dev。 仔細按照說明進行操作。 ``` npx @trigger.dev/cli@latest init ``` 在另一個終端中執行以下程式碼片段,在 Trigger.dev 和您的 Next.js 專案之間建立隧道。 ``` npx @trigger.dev/cli@latest dev ``` ### 安裝 ChatGPT (OpenAI) 我們將使用OpenAI助手,因此我們必須將其安裝到我們的專案中。 [建立新的 OpenAI 帳戶](https://platform.openai.com/) 並產生 API 金鑰。 ![pic4](https://res.cloudinary.com/practicaldev/image/fetch/s--uV1LwOH---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ashau6i2sxcpd0qcxuwq.png) 點擊下拉清單中的「檢視 API 金鑰」以建立 API 金鑰。 ![pic5](https://res.cloudinary.com/practicaldev/image/fetch/s--Tp8aLqSa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4bzc6e7f7avemeuuaygr.png) 接下來,透過執行下面的程式碼片段來安裝 OpenAI 套件。 ``` npm install @trigger.dev/openai ``` 將您的 OpenAI API 金鑰新增至「.env.local」檔案。 ``` OPENAI_API_KEY=<your_api_key> ``` 建立一個新目錄“helper”並新增一個新檔案“open.ai.tsx”,其中包含以下內容: ``` import {OpenAI} from "@trigger.dev/openai"; export const openai = new OpenAI({ id: "openai", apiKey: process.env.OPENAI_API_KEY!, }); ``` 這是我們透過 Trigger.dev 整合封裝的 OpenAI 用戶端。 ### 建立後台作業 讓我們繼續建立一個新的後台作業! 前往“jobs”並建立一個名為“process.documentation.ts”的新檔案。 **新增以下程式碼:** ``` import { eventTrigger } from "@trigger.dev/sdk"; import { client } from "@openai-assistant/trigger"; import {object, string} from "zod"; import {JSDOM} from "jsdom"; import {openai} from "@openai-assistant/helper/open.ai"; client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "process-documentation", name: "Process Documentation", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.documentation.event", schema: object({ url: string(), }) }), integrations: { openai }, run: async (payload, io, ctx) => { } }); ``` 我們定義了一個名為「process.documentation.event」的新作業,並新增了一個名為 URL 的必要參數 - 這是我們稍後要傳送的文件 URL。 正如您所看到的,該作業是空的,所以讓我們向其中加入第一個任務。 我們需要獲取網站網站地圖並將其返回。 抓取網站將返回我們需要解析的 HTML。 為此,我們需要安裝 JSDOM。 ``` npm install jsdom --save ``` 並將其導入到我們文件的頂部: ``` import {JSDOM} from "jsdom"; ``` 現在,我們可以新增第一個任務。 用「runTask」包裝我們的程式碼很重要,這可以讓 Trigger.dev 將其與其他任務分開。觸發特殊架構將任務拆分為不同的進程,因此 Vercel 無伺服器逾時不會影響它們。 **這是第一個任務的程式碼:** ``` const getSiteMap = await io.runTask("grab-sitemap", async () => { const data = await (await fetch(payload.url)).text(); const dom = new JSDOM(data); const sitemap = dom.window.document.querySelector('[rel="sitemap"]')?.getAttribute('href'); return new URL(sitemap!, payload.url).toString(); }); ``` - 我們透過 HTTP 請求從 URL 取得整個 HTML。 - 我們將其轉換為 JS 物件。 - 我們找到網站地圖 URL。 - 我們解析它並返回它。 接下來,我們需要抓取網站地圖,提取所有 URL 並返回它們。 讓我們安裝“Lodash”——陣列結構的特殊函數。 ``` npm install lodash @types/lodash --save ``` 這是任務的程式碼: ``` export const makeId = (length: number) => { let text = ''; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < length; i += 1) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; }; const {identifier, list} = await io.runTask("load-and-parse-sitemap", async () => { const urls = /(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/g; const identifier = makeId(5); const data = await (await fetch(getSiteMap)).text(); // @ts-ignore return {identifier, list: chunk(([...new Set(data.match(urls))] as string[]).filter(f => f.includes(payload.url)).map(p => ({identifier, url: p})), 25)}; }); ``` - 我們建立一個名為 makeId 的新函數來為所有頁面產生隨機辨識碼。 - 我們建立一個新任務並加入正規表示式來提取每個可能的 URL - 我們發送一個 HTTP 請求來載入網站地圖並提取其所有 URL。 - 我們將 URL「分塊」為 25 個元素的陣列(如果有 100 個元素,則會有四個 25 個元素的陣列) 接下來,讓我們建立一個新作業來處理每個 URL。 **這是完整的程式碼:** ``` function getElementsBetween(startElement: Element, endElement: Element) { let currentElement = startElement; const elements = []; // Traverse the DOM until the endElement is reached while (currentElement && currentElement !== endElement) { currentElement = currentElement.nextElementSibling!; // If there's no next sibling, go up a level and continue if (!currentElement) { // @ts-ignore currentElement = startElement.parentNode!; startElement = currentElement; if (currentElement === endElement) break; continue; } // Add the current element to the list if (currentElement && currentElement !== endElement) { elements.push(currentElement); } } return elements; } const processContent = client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "process-content", name: "Process Content", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.content.event", schema: object({ url: string(), identifier: string(), }) }), run: async (payload, io, ctx) => { return io.runTask('grab-content', async () => { // We first grab a raw html of the content from the website const data = await (await fetch(payload.url)).text(); // We load it with JSDOM so we can manipulate it const dom = new JSDOM(data); // We remove all the scripts and styles from the page dom.window.document.querySelectorAll('script, style').forEach((el) => el.remove()); // We grab all the titles from the page const content = Array.from(dom.window.document.querySelectorAll('h1, h2, h3, h4, h5, h6')); // We grab the last element so we can get the content between the last element and the next element const lastElement = content[content.length - 1]?.parentElement?.nextElementSibling!; const elements = []; // We loop through all the elements and grab the content between each title for (let i = 0; i < content.length; i++) { const element = content[i]; const nextElement = content?.[i + 1] || lastElement; const elementsBetween = getElementsBetween(element, nextElement); elements.push({ title: element.textContent, content: elementsBetween.map((el) => el.textContent).join('\n') }); } // We create a raw text format of all the content const page = ` ---------------------------------- url: ${payload.url}\n ${elements.map((el) => `${el.title}\n${el.content}`).join('\n')} ---------------------------------- `; // We save it to our database await prisma.docs.upsert({ where: { url: payload.url }, update: { content: page, identifier: payload.identifier }, create: { url: payload.url, content: page, identifier: payload.identifier } }); }); }, }); ``` - 我們從 URL 中獲取內容(之前從網站地圖中提取) - 我們用`JSDOM`解析它 - 我們刪除頁面上存在的所有可能的“<script>”或“<style>”。 - 我們抓取頁面上的所有標題(`h1`、`h2`、`h3`、`h4`、`h5`、`h6`) - 我們迭代標題並獲取它們之間的內容。我們不想取得整個頁面內容,因為它可能包含不相關的內容。 - 我們建立頁面原始文字的版本並將其保存到我們的資料庫中。 現在,讓我們為每個網站地圖 URL 執行此任務。 觸發器引入了名為“batchInvokeAndWaitForCompletion”的東西。 它允許我們批量發送 25 個專案進行處理,並且它將同時處理所有這些專案。下面是接下來的幾行程式碼: ``` let i = 0; for (const item of list) { await processContent.batchInvokeAndWaitForCompletion( 'process-list-' + i, item.map( payload => ({ payload, }), 86_400), ); i++; } ``` 我們以 25 個為一組[手動觸發](https://trigger.dev/docs/documentation/concepts/triggers/invoke)之前建立的作業。 完成後,讓我們將保存到資料庫的所有內容並連接它: ``` const data = await io.runTask("get-extracted-data", async () => { return (await prisma.docs.findMany({ where: { identifier }, select: { content: true } })).map((d) => d.content).join('\n\n'); }); ``` 我們使用之前指定的標識符。 現在,讓我們在 ChatGPT 中使用新資料建立一個新檔案: ``` const file = await io.openai.files.createAndWaitForProcessing("upload-file", { purpose: "assistants", file: data }); ``` `createAndWaitForProcessing` 是 Trigger.dev 建立的任務,用於將檔案上傳到助手。如果您在沒有整合的情況下手動使用“openai”,則必須串流傳輸檔案。 現在讓我們建立或更新我們的助手: ``` const assistant = await io.openai.runTask("create-or-update-assistant", async (openai) => { const currentAssistant = await prisma.assistant.findFirst({ where: { url: payload.url } }); if (currentAssistant) { return openai.beta.assistants.update(currentAssistant.aId, { file_ids: [file.id] }); } return openai.beta.assistants.create({ name: identifier, description: 'Documentation', instructions: 'You are a documentation assistant, you have been loaded with documentation from ' + payload.url + ', return everything in an MD format.', model: 'gpt-4-1106-preview', tools: [{ type: "code_interpreter" }, {type: 'retrieval'}], file_ids: [file.id], }); }); ``` - 我們首先檢查是否有針對該特定 URL 的助手。 - 如果我們有的話,讓我們用新文件更新助手。 - 如果沒有,讓我們建立一個新的助手。 - 我們傳遞「你是文件助理」的指令,需要注意的是,我們希望最終輸出為「MD」格式,以便稍後更好地顯示。 對於拼圖的最後一塊,讓我們將新助手儲存到我們的資料庫中。 **這是程式碼:** ``` await io.runTask("save-assistant", async () => { await prisma.assistant.upsert({ where: { url: payload.url }, update: { aId: assistant.id, }, create: { aId: assistant.id, url: payload.url, } }); }); ``` 如果該 URL 已經存在,我們可以嘗試使用新的助手 ID 來更新它。 這是該頁面的完整程式碼: ``` import { eventTrigger } from "@trigger.dev/sdk"; import { client } from "@openai-assistant/trigger"; import {object, string} from "zod"; import {JSDOM} from "jsdom"; import {chunk} from "lodash"; import {prisma} from "@openai-assistant/helper/prisma.client"; import {openai} from "@openai-assistant/helper/open.ai"; const makeId = (length: number) => { let text = ''; const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < length; i += 1) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; }; client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "process-documentation", name: "Process Documentation", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.documentation.event", schema: object({ url: string(), }) }), integrations: { openai }, run: async (payload, io, ctx) => { // The first task to get the sitemap URL from the website const getSiteMap = await io.runTask("grab-sitemap", async () => { const data = await (await fetch(payload.url)).text(); const dom = new JSDOM(data); const sitemap = dom.window.document.querySelector('[rel="sitemap"]')?.getAttribute('href'); return new URL(sitemap!, payload.url).toString(); }); // We parse the sitemap; instead of using some XML parser, we just use regex to get the URLs and we return it in chunks of 25 const {identifier, list} = await io.runTask("load-and-parse-sitemap", async () => { const urls = /(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])/g; const identifier = makeId(5); const data = await (await fetch(getSiteMap)).text(); // @ts-ignore return {identifier, list: chunk(([...new Set(data.match(urls))] as string[]).filter(f => f.includes(payload.url)).map(p => ({identifier, url: p})), 25)}; }); // We go into each page and grab the content; we do this in batches of 25 and save it to the DB let i = 0; for (const item of list) { await processContent.batchInvokeAndWaitForCompletion( 'process-list-' + i, item.map( payload => ({ payload, }), 86_400), ); i++; } // We get the data that we saved in batches from the DB const data = await io.runTask("get-extracted-data", async () => { return (await prisma.docs.findMany({ where: { identifier }, select: { content: true } })).map((d) => d.content).join('\n\n'); }); // We upload the data to OpenAI with all the content const file = await io.openai.files.createAndWaitForProcessing("upload-file", { purpose: "assistants", file: data }); // We create a new assistant or update the old one with the new file const assistant = await io.openai.runTask("create-or-update-assistant", async (openai) => { const currentAssistant = await prisma.assistant.findFirst({ where: { url: payload.url } }); if (currentAssistant) { return openai.beta.assistants.update(currentAssistant.aId, { file_ids: [file.id] }); } return openai.beta.assistants.create({ name: identifier, description: 'Documentation', instructions: 'You are a documentation assistant, you have been loaded with documentation from ' + payload.url + ', return everything in an MD format.', model: 'gpt-4-1106-preview', tools: [{ type: "code_interpreter" }, {type: 'retrieval'}], file_ids: [file.id], }); }); // We update our internal database with the assistant await io.runTask("save-assistant", async () => { await prisma.assistant.upsert({ where: { url: payload.url }, update: { aId: assistant.id, }, create: { aId: assistant.id, url: payload.url, } }); }); }, }); export function getElementsBetween(startElement: Element, endElement: Element) { let currentElement = startElement; const elements = []; // Traverse the DOM until the endElement is reached while (currentElement && currentElement !== endElement) { currentElement = currentElement.nextElementSibling!; // If there's no next sibling, go up a level and continue if (!currentElement) { // @ts-ignore currentElement = startElement.parentNode!; startElement = currentElement; if (currentElement === endElement) break; continue; } // Add the current element to the list if (currentElement && currentElement !== endElement) { elements.push(currentElement); } } return elements; } // This job will grab the content from the website const processContent = client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "process-content", name: "Process Content", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "process.content.event", schema: object({ url: string(), identifier: string(), }) }), run: async (payload, io, ctx) => { return io.runTask('grab-content', async () => { try { // We first grab a raw HTML of the content from the website const data = await (await fetch(payload.url)).text(); // We load it with JSDOM so we can manipulate it const dom = new JSDOM(data); // We remove all the scripts and styles from the page dom.window.document.querySelectorAll('script, style').forEach((el) => el.remove()); // We grab all the titles from the page const content = Array.from(dom.window.document.querySelectorAll('h1, h2, h3, h4, h5, h6')); // We grab the last element so we can get the content between the last element and the next element const lastElement = content[content.length - 1]?.parentElement?.nextElementSibling!; const elements = []; // We loop through all the elements and grab the content between each title for (let i = 0; i < content.length; i++) { const element = content[i]; const nextElement = content?.[i + 1] || lastElement; const elementsBetween = getElementsBetween(element, nextElement); elements.push({ title: element.textContent, content: elementsBetween.map((el) => el.textContent).join('\n') }); } // We create a raw text format of all the content const page = ` ---------------------------------- url: ${payload.url}\n ${elements.map((el) => `${el.title}\n${el.content}`).join('\n')} ---------------------------------- `; // We save it to our database await prisma.docs.upsert({ where: { url: payload.url }, update: { content: page, identifier: payload.identifier }, create: { url: payload.url, content: page, identifier: payload.identifier } }); } catch (e) { console.log(e); } }); }, }); ``` 我們已經完成建立後台作業來抓取和索引文件🎉 ### 詢問助理 現在,讓我們建立一個任務來詢問我們的助手。 前往“jobs”並建立一個新檔案“question.assistant.ts”。 **新增以下程式碼:** ``` import {eventTrigger} from "@trigger.dev/sdk"; import {client} from "@openai-assistant/trigger"; import {object, string} from "zod"; import {openai} from "@openai-assistant/helper/open.ai"; client.defineJob({ // This is the unique identifier for your Job; it must be unique across all Jobs in your project. id: "question-assistant", name: "Question Assistant", version: "0.0.1", // This is triggered by an event using eventTrigger. You can also trigger Jobs with webhooks, on schedules, and more: https://trigger.dev/docs/documentation/concepts/triggers/introduction trigger: eventTrigger({ name: "question.assistant.event", schema: object({ content: string(), aId: string(), threadId: string().optional(), }) }), integrations: { openai }, run: async (payload, io, ctx) => { // Create or use an existing thread const thread = payload.threadId ? await io.openai.beta.threads.retrieve('get-thread', payload.threadId) : await io.openai.beta.threads.create('create-thread'); // Create a message in the thread await io.openai.beta.threads.messages.create('create-message', thread.id, { content: payload.content, role: 'user', }); // Run the thread const run = await io.openai.beta.threads.runs.createAndWaitForCompletion('run-thread', thread.id, { model: 'gpt-4-1106-preview', assistant_id: payload.aId, }); // Check the status of the thread if (run.status !== "completed") { console.log('not completed'); throw new Error(`Run finished with status ${run.status}: ${JSON.stringify(run.last_error)}`); } // Get the messages from the thread const messages = await io.openai.beta.threads.messages.list("list-messages", run.thread_id, { query: { limit: "1" } }); const content = messages[0].content[0]; if (content.type === 'text') { return {content: content.text.value, threadId: thread.id}; } } }); ``` - 該事件需要三個參數 - `content` - 我們想要傳送給助理的訊息。 - `aId` - 我們先前建立的助手的內部 ID。 - `threadId` - 對話的執行緒 ID。正如您所看到的,這是一個可選參數,因為在第一個訊息中,我們還沒有線程 ID。 - 然後,我們建立或取得前一個執行緒的執行緒。 - 我們在助理提出的問題的線索中加入一條新訊息。 - 我們執行線程並等待它完成。 - 我們取得訊息清單(並將其限制為 1),因為第一則訊息是對話中的最後一則訊息。 - 我們返回訊息內容和我們剛剛建立的線程ID。 ### 新增路由 我們需要為我們的應用程式建立 3 個 API 路由: 1、派新助理進行處理。 2. 透過URL獲取特定助手。 3. 新增訊息給助手。 在「app/api」中建立一個名為assistant的新資料夾,並在其中建立一個名為「route.ts」的新檔案。裡面加入如下程式碼: ``` import {client} from "@openai-assistant/trigger"; import {prisma} from "@openai-assistant/helper/prisma.client"; export async function POST(request: Request) { const body = await request.json(); if (!body.url) { return new Response(JSON.stringify({error: 'URL is required'}), {status: 400}); } // We send an event to the trigger to process the documentation const {id: eventId} = await client.sendEvent({ name: "process.documentation.event", payload: {url: body.url}, }); return new Response(JSON.stringify({eventId}), {status: 200}); } export async function GET(request: Request) { const url = new URL(request.url).searchParams.get('url'); if (!url) { return new Response(JSON.stringify({error: 'URL is required'}), {status: 400}); } const assistant = await prisma.assistant.findFirst({ where: { url: url } }); return new Response(JSON.stringify(assistant), {status: 200}); } ``` 第一個「POST」方法取得一個 URL,並使用用戶端傳送的 URL 觸發「process.documentation.event」作業。 第二個「GET」方法從我們的資料庫中透過客戶端發送的 URL 取得助手。 現在,讓我們建立向助手新增訊息的路由。 在「app/api」內部建立一個新資料夾「message」並新增一個名為「route.ts」的新文件,然後新增以下程式碼: ``` import {prisma} from "@openai-assistant/helper/prisma.client"; import {client} from "@openai-assistant/trigger"; export async function POST(request: Request) { const body = await request.json(); // Check that we have the assistant id and the message if (!body.id || !body.message) { return new Response(JSON.stringify({error: 'Id and Message are required'}), {status: 400}); } // get the assistant id in OpenAI from the id in the database const assistant = await prisma.assistant.findUnique({ where: { id: +body.id } }); // We send an event to the trigger to process the documentation const {id: eventId} = await client.sendEvent({ name: "question.assistant.event", payload: { content: body.message, aId: assistant?.aId, threadId: body.threadId }, }); return new Response(JSON.stringify({eventId}), {status: 200}); } ``` 這是一個非常基本的程式碼。我們從客戶端獲取訊息、助手 ID 和線程 ID,並將其發送到我們之前建立的「question.assistant.event」。 最後要做的事情是建立一個函數來獲取我們所有的助手。 在「helpers」內部建立一個名為「get.list.ts」的新函數並新增以下程式碼: ``` import {prisma} from "@openai-assistant/helper/prisma.client"; // Get the list of all the available assistants export const getList = () => { return prisma.assistant.findMany({ }); } ``` 非常簡單的程式碼即可獲得所有助手。 我們已經完成了後端🥳 讓我們轉到前面。 --- ![前端](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k3s5gks1j0ojoz11b93i.png) ## 建立前端 我們將建立一個基本介面來新增 URL 並顯示已新增 URL 的清單: ![ss1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihvx4yn6uee6gritr9nh.png) ### 首頁 將 `app/page.tsx` 的內容替換為以下程式碼: ``` import {getList} from "@openai-assistant/helper/get.list"; import Main from "@openai-assistant/components/main"; export default async function Home() { const list = await getList(); return ( <Main list={list} /> ) } ``` 這是一個簡單的程式碼,它從資料庫中取得清單並將其傳遞給我們的 Main 元件。 接下來,讓我們建立“Main”元件。 在「app」內建立一個新資料夾「components」並新增一個名為「main.tsx」的新檔案。 **新增以下程式碼:** ``` "use client"; import {Assistant} from '@prisma/client'; import {useCallback, useState} from "react"; import {FieldValues, SubmitHandler, useForm} from "react-hook-form"; import {ChatgptComponent} from "@openai-assistant/components/chatgpt.component"; import {AssistantList} from "@openai-assistant/components/assistant.list"; import {TriggerProvider} from "@trigger.dev/react"; export interface ExtendedAssistant extends Assistant { pending?: boolean; eventId?: string; } export default function Main({list}: {list: ExtendedAssistant[]}) { const [assistantState, setAssistantState] = useState(list); const {register, handleSubmit} = useForm(); const submit: SubmitHandler<FieldValues> = useCallback(async (data) => { const assistantResponse = await (await fetch('/api/assistant', { body: JSON.stringify({url: data.url}), method: 'POST', headers: { 'Content-Type': 'application/json' } })).json(); setAssistantState([...assistantState, {...assistantResponse, url: data.url, pending: true}]); }, [assistantState]) const changeStatus = useCallback((val: ExtendedAssistant) => async () => { const assistantResponse = await (await fetch(`/api/assistant?url=${val.url}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } })).json(); setAssistantState([...assistantState.filter((v) => v.id), assistantResponse]); }, [assistantState]) return ( <TriggerProvider publicApiKey={process.env.NEXT_PUBLIC_TRIGGER_PUBLIC_API_KEY!}> <div className="w-full max-w-2xl mx-auto p-6 flex flex-col gap-4"> <form className="flex items-center space-x-4" onSubmit={handleSubmit(submit)}> <input className="flex-grow p-3 border border-black/20 rounded-xl" placeholder="Add documentation link" type="text" {...register('url', {required: 'true'})} /> <button className="flex-shrink p-3 border border-black/20 rounded-xl" type="submit"> Add </button> </form> <div className="divide-y-2 divide-gray-300 flex gap-2 flex-wrap"> {assistantState.map(val => ( <AssistantList key={val.url} val={val} onFinish={changeStatus(val)} /> ))} </div> {assistantState.filter(f => !f.pending).length > 0 && <ChatgptComponent list={assistantState} />} </div> </TriggerProvider> ) } ``` 讓我們看看這裡發生了什麼: - 我們建立了一個名為「ExtendedAssistant」的新接口,其中包含兩個參數「pending」和「eventId」。當我們建立一個新的助理時,我們沒有最終的值,我們將只儲存`eventId`並監聽作業處理直到完成。 - 我們從伺服器元件取得清單並將其設定為新狀態(以便我們稍後可以修改它) - 我們新增了「TriggerProvider」來幫助我們監聽事件完成並用資料更新它。 - 我們使用「react-hook-form」建立一個新表單來新增助手。 - 我們新增了一個帶有一個輸入「URL」的表單來提交新的助理進行處理。 - 我們迭代並顯示所有現有的助手。 - 在提交表單時,我們將資訊傳送到先前建立的「路由」以新增助理。 - 事件完成後,我們觸發「changeStatus」以從資料庫載入助手。 - 最後,我們有了 ChatGPT 元件,只有在沒有等待處理的助手時才會顯示(`!f.pending`) 讓我們建立 `AssistantList` 元件。 在「components」內,建立一個新檔案「assistant.list.tsx」並在其中加入以下內容: ``` "use client"; import {FC, useEffect} from "react"; import {ExtendedAssistant} from "@openai-assistant/components/main"; import {useEventRunDetails} from "@trigger.dev/react"; export const Loading: FC<{eventId: string, onFinish: () => void}> = (props) => { const {eventId} = props; const { data, error } = useEventRunDetails(eventId); useEffect(() => { if (!data || error) { return ; } if (data.status === 'SUCCESS') { props.onFinish(); } }, [data]); return <div className="pointer bg-yellow-300 border-yellow-500 p-1 px-3 text-yellow-950 border rounded-2xl">Loading</div> }; export const AssistantList: FC<{val: ExtendedAssistant, onFinish: () => void}> = (props) => { const {val, onFinish} = props; if (val.pending) { return <Loading eventId={val.eventId!} onFinish={onFinish} /> } return ( <div key={val.url} className="pointer relative bg-green-300 border-green-500 p-1 px-3 text-green-950 border rounded-2xl hover:bg-red-300 hover:border-red-500 hover:text-red-950 before:content-[attr(data-content)]" data-content={val.url} /> ) } ``` 我們迭代我們建立的所有助手。如果助手已經建立,我們只顯示名稱。如果沒有,我們渲染`<Loading />`元件。 載入元件在螢幕上顯示“正在載入”,並長時間輪詢伺服器直到事件完成。 我們使用 Trigger.dev 建立的 useEventRunDetails 函數來了解事件何時完成。 事件完成後,它會觸發「onFinish」函數,用新建立的助手更新我們的客戶端。 ### 聊天介面 ![聊天介面](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0u7db3qwz03d6jkk965a.png) 現在,讓我們加入 ChatGPT 元件並向我們的助手提問! - 選擇我們想要使用的助手 - 顯示訊息列表 - 新增我們要傳送的訊息的輸入和提交按鈕。 在「components」內部新增一個名為「chatgpt.component.tsx」的新文件 讓我們繪製 ChatGPT 聊天框: ``` "use client"; import {FC, useCallback, useEffect, useRef, useState} from "react"; import {ExtendedAssistant} from "@openai-assistant/components/main"; import Markdown from 'react-markdown' import {useEventRunDetails} from "@trigger.dev/react"; interface Messages { message?: string eventId?: string } export const ChatgptComponent = ({list}: {list: ExtendedAssistant[]}) => { const url = useRef<HTMLSelectElement>(null); const [message, setMessage] = useState(''); const [messagesList, setMessagesList] = useState([] as Messages[]); const [threadId, setThreadId] = useState<string>('' as string); const submitForm = useCallback(async (e: any) => { e.preventDefault(); setMessagesList((messages) => [...messages, {message: `**[ME]** ${message}`}]); setMessage(''); const messageResponse = await (await fetch('/api/message', { method: 'POST', body: JSON.stringify({message, id: url.current?.value, threadId}), })).json(); if (!threadId) { setThreadId(messageResponse.threadId); } setMessagesList((messages) => [...messages, {eventId: messageResponse.eventId}]); }, [message, messagesList, url, threadId]); return ( <div className="border border-black/50 rounded-2xl flex flex-col"> <div className="border-b border-b-black/50 h-[60px] gap-3 px-3 flex items-center"> <div>Assistant:</div> <div> <select ref={url} className="border border-black/20 rounded-xl p-2"> {list.filter(f => !f.pending).map(val => ( <option key={val.id} value={val.id}>{val.url}</option> ))} </select> </div> </div> <div className="flex-1 flex flex-col gap-3 py-3 w-full min-h-[500px] max-h-[1000px] overflow-y-auto overflow-x-hidden messages-list"> {messagesList.map((val, index) => ( <div key={index} className={`flex border-b border-b-black/20 pb-3 px-3`}> <div className="w-full"> {val.message ? <Markdown>{val.message}</Markdown> : <MessageComponent eventId={val.eventId!} onFinish={setThreadId} />} </div> </div> ))} </div> <form onSubmit={submitForm}> <div className="border-t border-t-black/50 h-[60px] gap-3 px-3 flex items-center"> <div className="flex-1"> <input value={message} onChange={(e) => setMessage(e.target.value)} className="read-only:opacity-20 outline-none border border-black/20 rounded-xl p-2 w-full" placeholder="Type your message here" /> </div> <div> <button className="border border-black/20 rounded-xl p-2 disabled:opacity-20" disabled={message.length < 3}>Send</button> </div> </div> </form> </div> ) } export const MessageComponent: FC<{eventId: string, onFinish: (threadId: string) => void}> = (props) => { const {eventId} = props; const { data, error } = useEventRunDetails(eventId); useEffect(() => { if (!data || error) { return ; } if (data.status === 'SUCCESS') { props.onFinish(data.output.threadId); } }, [data]); if (!data || error || data.status !== 'SUCCESS') { return ( <div className="flex justify-end items-center pb-3 px-3"> <div className="animate-spin rounded-full h-3 w-3 border-t-2 border-b-2 border-blue-500" /> </div> } return <Markdown>{data.output.content}</Markdown>; }; ``` 這裡正在發生一些令人興奮的事情: - 當我們建立新訊息時,我們會自動將其呈現在螢幕上作為「我們的」訊息,但是當我們將其發送到伺服器時,我們需要推送事件 ID,因為我們還沒有訊息。這就是我們使用 `{val.message ? <Markdown>{val.message}</Markdown> : <MessageComponent eventId={val.eventId!} onFinish={setThreadId} />}` - 我們用「Markdown」元件包裝訊息。如果您還記得,我們在前面的步驟中告訴 ChatGPT 以 MD 格式輸出所有內容,以便我們可以正確渲染它。 - 事件處理完成後,我們會更新線程 ID,以便我們從以下訊息中獲得相同對話的上下文。 我們就完成了🎉 --- ![完成](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0half2g6r5zfn7asq084.png) ## 讓我們聯絡吧! 🔌 作為開源開發者,您可以加入我們的[社群](https://discord.gg/nkqV9xBYWy) 做出貢獻並與維護者互動。請隨時造訪我們的 [GitHub 儲存庫](https://github.com/triggerdotdev/trigger.dev),貢獻並建立與 Trigger.dev 相關的問題。 本教學的源程式碼可在此處取得: [https://github.com/triggerdotdev/blog/tree/main/openai-assistant](https://github.com/triggerdotdev/blog/tree/main/openai-assistant) 感謝您的閱讀! --- 原文出處:https://dev.to/triggerdotdev/train-chatgpt-on-your-documentation-1a9g

🚀 Kubernetes 上的 GITLAB:終極部署指南! 🌟

## TL;DR 🔍 探索在 Kubernetes 上部署 GitLab 的逐步指南,並專注於 Omnibus 套件配置。了解如何設定 PostgreSQL、SMTP、Container Registry、Sidekiq、Prometheus 指標和備份。使用 Glasskube GitLab Kubernetes Operator 探索替代方案,簡化流程並將設定時間縮短至僅 5 分鐘。 --- ## 我們需要您的回饋! 🫶 在下面的評論中分享您的想法!讓我們知道您想要更多內容的主題。如果本指南有幫助,請點擊貓並留下一顆星,以支持我們建立更多以開發人員為中心的內容。您的回饋很重要! [![Glasskube Github](https://cms.glasskube.eu/uploads/CTA_51bbe3bb2a.png) ](https://github.com/glasskube/operator) --- ## 讓我們開始吧🏌️ [GitLab](https://glasskube.eu/en/s/kubernetes-operator/gitlab/) 是一個以軟體工程團隊為導向的開源 DevSecOps 平台。 有兩種方式可用於在 Kubernetes 叢集上部署 GitLab: 1.GitLab雲端原生混合 2.GitLab包(綜合) ## GitLab 雲端原生混合 部署 GitLab Cloud 原生混合架構僅在查詢特定用例中才有意義。例如 - 如 [GitLab 決策樹](https://docs.gitlab.com/ee/administration/reference_architectures/#decision-tree) 所示 - 如果 GitLab 實例需要為至少 3,000 個使用者提供服務。對於這種部署,GitLab 元件將單獨安裝在不同的 docker 容器中。最低要求為 10 個節點,總共 19 個 vCPU 和 60 GB 內存,這將導致每月數千美元的雲端成本。 <img width="100%" style="width:100%" src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExbHl6eXltNWw0ZDNjbnNqbDdicXBpbzNpdX6e nlfaWQmY 3Q9Zw/2aIZfQdC2V7bBvU5t2/giphy.gif"> 因此,在大多數情況下,GitLab 雲端原生是不需要的,而且過於複雜。 **那麼,如何在 Kubernetes 上部署 GitLab Omnibus?** ## Kubernetes 上的 GitLab Omnibus 使用「omnibus」這個名稱,GitLab 也發布了一體化軟體包,這些軟體包可以作為docker 映像[`hub.docker.com/r/gitlab/gitlab-ce`](https://hub.docker. com /r/gitlab/gitlab-ce),可以透過環境變數輕鬆配置。正確進行設定可以輕鬆在 Kubernetes 上部署 GitLab。 應正確配置以下重要元件,以便歸檔合理的 Kubernetes 設定: 1.PostgreSQL資料庫 2. SMTP配置 3. Kubernetes 上的 GitLab 容器註冊表 4. Sidekiq、Gitaly 和 Puma 配置 5. 普羅米修斯指標 6. Kubernetes 上的 GitLab 備份 ### Kubernetes 上的 GitLab:設定外部 PostgreSQL 資料庫 建立 PostgreSQL 資料庫可以使用 [CloudNativePG PostgreSQL Operator](https://cloudnative-pg.io/) 來完成。安裝完 Operator 後,可以透過套用 Kubernetes CR 來部署新的 Postgres 叢集: ``` kind: Cluster apiVersion: postgresql.cnpg.io/v1 metadata: name: gitlab spec: enableSuperuserAccess: false instances: 2 bootstrap: initdb: database: gitlabhq_production owner: gitlab storage: size: 20Gi ``` 重要的是,資料庫名稱為“gitlabhq_product”,而配置的“gitlab”資料庫使用者是資料庫的擁有者。在本例中,我們建立了一個由兩個 PostgreSQL 副本組成的集群,以確保資料庫保持可用,即使執行主副本的節點發生故障也是如此。這稱為高可用性 (HA)。 現在,我們建立了自己的 PostgreSQL 集群,必須在「gitlab.rb」檔案中停用綜合映像中包含的資料庫。 ``` postgresql['enable'] = false gitlab_rails['db_adapter'] = 'postgresql' gitlab_rails['db_encoding'] = 'unicode' gitlab_rails['db_host'] = 'gitlab-pg-rw' gitlab_rails['db_password'] = '<your-password>' ``` 很好,我們已經為 GitLab 安裝提供了雲端原生資料庫。 <img width="25%" style="width:25%" src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExc2VydjJoN3BsN2RycDY5N3prZHhoaW8xdTYxenJoN3BsN2RycDY5N3prZHhoaW8xdTYxenhkbHdtBlcMpm30FmDFt0Fyg n;ipPlcmDMlcM40M400005450000200020025400000 mY3 Q9Zw/SVH9y2LQUVVCRcqD7o/giphy.gif"> #### Kubernetes 上的 GitLab:設定 SMTP 憑證 為了確保您的 GitLab 實例能夠傳送電子郵件,您需要設定 SMTP 伺服器。這也可以在 `gitlab.rb` 檔案中完成。 ``` gitlab_rails['smtp_enable'] = ... gitlab_rails['smtp_address'] = ... gitlab_rails['smtp_port'] = ... gitlab_rails['smtp_user_name'] = ... gitlab_rails['smtp_password'] = ... gitlab_rails['smtp_tls'] = ... gitlab_rails['gitlab_email_from'] = ... ``` 例如,可以在 Brevo 平台上建立用於交易電子郵件的免費 smtp 伺服器。 <img width="25%" style="width:25%" src="https://media.giphy.com/media/0IR3vO2bWY1AQPAsAn/giphy.gif"> #### Kubernetes 上的 GitLab:容器註冊表 在眾多功能中,GitLab 支援充當容器註冊表。這是透過捆綁 docker 容器註冊表的參考實作來實現的,但預設情況下它是禁用的,因為正確設定它相當麻煩。 > **重要** > 重要的是要了解 GitLab 綜合容器中的所有請求將首先由內部 nginx 伺服器處理,然後分發到元件。 容器註冊表僅適用於 TLS 加密連接,因此我們需要透過入口負載平衡器停用 TLS 終止,並將加密流量直接傳送到 GitLab 容器。如果使用 NGINX 入口控制器,可以透過在入口中新增以下註解來完成:「nginx.ingress.kubernetes.io/ssl-passthrough: true」。 之後,必須套用以下`gitlab.rb`配置: ``` registry['enable'] = true gitlab_rails['registry_enabled'] = true registry['token_realm'] = "https://" + ENV['GITLAB_HOST'] gitlab_rails['registry_enabled'] = true gitlab_rails['registry_host'] = ENV['GITLAB_REGISTRY_HOST'] gitlab_rails['registry_api_url'] = "http://localhost:5000" registry_external_url 'https://' + ENV['GITLAB_REGISTRY_HOST'] registry_nginx['redirect_http_to_https'] = true registry_nginx['listen_port'] = 5443 registry_nginx['ssl_certificate'] = "/etc/gitlab/ssl/tls.crt" registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/tls.key" registry_nginx['real_ip_trusted_addresses'] = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] registry_nginx['server_names_hash_bucket_size'] = 128 ``` 此外,可以配置 S3 儲存桶(或相容的雲端儲存端點),以便所有影像層不會儲存在磁碟區內,而是儲存在可擴充的 S3 中 ### Kubernetes 上的 GitLab:Sidekiq、Gitaly 和 Puma 配置 Puma(Ruby 的 HTTP 伺服器)、Sidekiq(作業排程器)和 Gitaly(GitLabs Git 橋接器)都可以使用自訂數量的工作執行緒/執行緒來啟動。最佳配置在很大程度上取決於您的用例和需要支援的使用者數量。合理的最小配置是: ``` puma['worker_processes'] = 2 sidekiq['max_concurrency'] = 9 ``` ### Kubernetes 上的 GitLab:Prometheus 指標 GitLab 綜合包在鏡像中附帶了自己的 prometheus 實例。由於大多數 Kubernetes 叢集中已經存在 Prometheus 實例,因此可以安全地停用包含的 Prometheus。 ``` prometheus_monitoring['enable'] = false ``` ServiceMonitor 可以用來告訴正在執行的 Prometheus 實例在下列連接埠上監視 GitLabs 元件所公開的指標端點: - 8082(sidekiq) - 9168(gitlab) - 9236(義大利) ### Kubernetes 上的 GitLab:備份 嗯,在 Kubernetes 上備份 GitLab 本身就是一個技術指南。最簡單的方法是使用 [Velero](https://velero.io/) 等備份解決方案備份完整的命名空間。 ## 玻璃立方體 GitLab Kubernetes Operator 了解Glasskube GitLab [Kubernetes Operator](https://glasskube.eu/en/r/glossary/kubernetes-operator/) - 我們的開發團隊對繁瑣的配置過程、耗時的設定和不斷的故障排除感到沮喪的產物與 GitLab 部署相關。為了應對這些挑戰,我們精心設計了一個簡化整個體驗的解決方案。 Glasskube GitLab Kubernetes Operator 部署一個完全配置的 GitLab 實例,其所有功能均透過自訂資源定義 (CRD) 巧妙抽象化。感謝操作員,花費過多時間進行設定和更新的日子已經結束。現在,您可以在短短 5 分鐘內輕鬆啟動新的 GitLab 實例。嘗試一下並向我們提供反饋! ### 安裝 Glasskube 運算符 第一步是透過 Helm Chart 安裝 Glasskube: ``` helm repo add glasskube https://charts.glasskube.eu/ helm repo update helm install my-glasskube-operator glasskube/glasskube-operator ``` 完整的文件可以在我們的[入門](https://glasskube.eu/docs/getting-started/install/)文件中找到。 ### 部署 GitLab > **重要** > 必須在「LoadBalancer」或「Ingress Host」上設定 DNS 專案。 SSL 憑證是 > 如果配置了“ClusterIssuer”,則由“LoadBalancer”或“cert-manager”自動產生。 **gitlab.yaml** ``` apiVersion: "v1" kind: "Secret" metadata: name: "gitlab-smtp" stringData: username: "..." password: "..." --- apiVersion: v1 kind: Secret metadata: name: gitlab-registry-secret stringData: accessKey: "..." secretKey: "..." --- apiVersion: "glasskube.eu/v1alpha1" kind: "Gitlab" metadata: name: "gitlab" spec: host: "gitlab.mycompany.eu" sshEnabled: true sshHost: "ssh.gitlab.mycompany.eu" smtp: host: "..." port: 465 fromAddress: "[email protected]" authSecret: name: "gitlab-smtp" tlsEnabled: true registry: host: "registry.gitlab.mycompany.eu" storage: s3: bucket: gitlab-registry accessKeySecret: name: gitlab-registry-secret key: accessKey secretKeySecret: name: gitlab-registry-secret key: secretKey region: ... ``` ``` kubectl apply -f gitlab.yaml ``` <img width="25%" style="width:25%" src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExa2RldHpiYnMzOWdlZTgwdWtqOHN3N3eG9I0NjVyd2l4m Y3Q9Zw/YRhUem7n2UaF9EK2PH/giphy.gif"> 就是這樣! 完整的自訂資源文件可以在 Glasskube 文件中找到 關於 [GitLab](https://glasskube.eu/docs/crd-reference/gitlab/) ### 在 Kubernetes 上部署 GitLab Runner <img width="25%" style="width:25%" src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZDJsN2x6NTI0dWdhZW55MjBzMjFobHdtbDRtaHEycXlidWdhZW55MjBzMjFobHdtbDRtaHEycXlidWdhZW55MjBzMjFobHdtbDRtaHEycXlidGt. WQmY 3Q9Zw/945jGDodvZCDe/giphy.gif"> GitLab 上的運作程序是為持續整合和持續交付 (CI/CD) 管道提供支援的執行代理。 他們負責執行作業,即管道中的各個步驟或任務。 Glasskube Gitlab Kubernetes Operator 讓在 Gitlab 中新增執行器變得如此簡單。首先,需要透過「https://{{host}}/admin/runners/new」建立一個新的執行程式。之後,這些執行器令牌只需加入到「gitlab.yaml」規格中,Glasskube Kubernetes Operator 將自動產生並將這些執行器與 Gitlab 實例連接起來。 ``` runners: - token: glrt-xxxxXX-xxxxxXXXXX # can be generated at https://{{host}}/admin/runners/new ``` 完整的自訂資源文件可以在關於 [GitLab runner] 的 Glasskube 文件中找到(https://glasskube.eu/docs/crd-reference/gitlab/runner/) 現在安裝完成了。 Kubernetes Operator 的更新將自動更新 GitLab。 ## 結論 在 Kubernetes 上為少於 1000 個使用者部署 GitLab 實例不應該使用 GitLabs 雲端原生混合 Helm Charts 來完成,而應該使用專門安裝的 GitLab Omnibus 套件與雲端原生基礎架構結合來完成。 Glasskube Kubernetes 操作員負責處理這個問題。 --- [![玻璃立方體Github]( https://cms.glasskube.eu/uploads/CTA_51bbe3bb2a.png) ](https://github.com/glasskube/operator) --- 星星冰塊: # [`glasskube/operator`](https://github.com/glasskube/operator) --- 原文出處:https://dev.to/glasskube/gitlab-on-kubernetes-the-ultimate-deployment-guide-188b