ساخت یک آنالایزر رفتار مالی با قدرت AI با NodeJS ، Python ، Sveltekit و Tailwindcs – قسمت 4

مقدمه
قسمت 5 در حال حاضر در حال حاضر است: https://johnowolabiidogun.dev/blog/asynchronous-server-building-and-rigorously-testing-a-websocket-and-http-server-3918df/67b0ab3c7a900ac23e502c51
در این بخش از این سریال ، ما کنترل کننده WebSocket سرویس پس زمینه خود را پیاده سازی خواهیم کرد و ساخت جلوی آن را شروع می کنیم. این امر باعث می شود تا ارتباطات در زمان واقعی بین پس زمینه و جبهه امکان پذیر باشد و به ما امکان می دهد تا در صورت دسترسی ، نتایج تجزیه و تحلیل را نمایش دهیم.
پیش نیاز
پیش نیاز اصلی این است که شما مقالات قبلی را در این سری طی کرده اید. این تضمین می کند که زمینه و محیط لازم را تنظیم کنید.
رمز منبع
آنالایزر و مشاور رفتار مالی با هوش مصنوعی که در پایتون (AIOHTTP) و TypeScript نوشته شده است (Expressjs & Sveltekit با Svelte 5)
اجرای
مرحله: کنترل کننده WebSocket
برای ارائه به روزرسانی های زمان واقعی برای تجزیه و تحلیل داده های مالی و خلاصه ها ، ما به جای درخواست های سنتی HTTP که نیاز به طراوت مداوم دارند ، از WebSockets ، یک پروتکل ارتباطی دو طرفه استفاده می کنیم. در TransactionWebSocketHandler
عملکرد اتصالات WebSocket را مدیریت می کند:
import { WebSocket } from "ws";
import { TransactionService } from "$services/transaction.service.js";
import { baseConfig } from "$config/base.config.js";
import mongoose from "mongoose";
import { sendError } from "$utils/error.utils.js";
export function TransactionWebSocketHandler(ws: WebSocket): void {
ws.on("message", async (message: string) => {
try {
const actions = JSON.parse(message);
if (!Array.isArray(actions)) {
sendError(ws, "Invalid message format. Expected an array.");
return;
}
for (const actionObj of actions) {
if (!actionObj.action || !actionObj.userId) {
sendError(
ws,
"Invalid action format. Each action requires 'action' and 'userId'."
);
return;
}
const { action, userId } = actionObj;
if (!mongoose.Types.ObjectId.isValid(userId)) {
sendError(ws, "Invalid userId format.");
return;
}
switch (action) {
case "analyze":
case "summary":
await handleAction(ws, new mongoose.Types.ObjectId(userId), action);
break;
default:
sendError(ws, `Unknown action: ${action}`);
}
}
} catch (error) {
baseConfig.logger.error(
`Error processing message: ${
error instanceof Error ? error.message : error
}`
);
sendError(
ws,
`Failed to process message: ${
error instanceof Error ? error.message : error
}`
);
}
});
ws.on("close", () => {
baseConfig.logger.info("Frontend WebSocket connection closed");
});
ws.on("error", (error) => {
baseConfig.logger.error(`WebSocket error: ${error.message}`);
});
}
async function handleAction(
frontendWs: WebSocket,
userId: mongoose.Types.ObjectId,
action: string
) {
try {
const transactions = await TransactionService.findTransactionsByUserId(
userId,
-1,
-1
);
if (!transactions) {
sendError(
frontendWs,
`No transactions found for userId: ${userId}`,
action
);
return;
}
await TransactionService.connectToUtilityServer(
action,
transactions.transactions,
frontendWs
);
} catch (error) {
baseConfig.logger.error(
`Error handling action: ${error instanceof Error ? error.message : error}`
);
sendError(
frontendWs,
`Failed to handle action: ${
error instanceof Error ? error.message : error
}`
);
}
}
با استفاده از ws
ماژول (در مقاله قبلی نصب شده است) ، این عملکرد اتصال WebSocket را تنظیم می کند و برای پیام های ورودی ، اتصال نزدیک و خطاها ، دستگیرندگان را تعریف می کند. به محض دریافت پیام ، پاسخ به تماس برای message
شنونده رویداد اخراج می شود و به شرح زیر است:
- پیام های دریافتی به عنوان JSON ، انتظار مجموعه ای از اقدامات را دارند.
- فرمت هر عمل را تأیید می کند ، و از آن اطمینان حاصل می کند
action
وتuserId
بشر - تأیید می کند
userId
برای اطمینان از این که یک ObjectId معتبر Mongoose است. - از a استفاده می کند
switch
جمله ای برای رسیدگی به اقدامات مختلف (analyze
وتsummary
)) ، و - تماس
handleAction
عملکرد برای پردازش هر عمل معتبر.
در handleAction
اقدامات خاص درخواست شده توسط مشتری را با بازیابی معاملات برای مشخص شده انجام می دهد userId
با استفاده از TransactionService.findTransactionsByUserId
روش استاتیک در مقاله قبلی مورد بحث قرار گرفته است. هرگونه معاملات بازیابی شده از طریق ارسال می شود TransactionService.connectToUtilityServer
روش به سرور ابزار برای تجزیه و تحلیل یا خلاصه.
بیایید این کنترل کننده را در برنامه ما ثبت کنیم:
...
import { WebSocketServer } from "ws";
...
import { TransactionWebSocketHandler } from "$websockets/transaction.websocket.js";
...
const startServer = async () => {
try {
const server: HttpServer = createServer(app);
const wss = new WebSocketServer({ server, path: "/ws" });
// 5. Setup WebSocket handlers
wss.on("connection", (ws) => {
TransactionWebSocketHandler(ws);
});
// 6. Connect to MongoDB
baseConfig.logger.info("Connecting to MongoDB cluster...");
const db = await connectToCluster();
...
} catch (error) {
baseConfig.logger.error("Error starting server:", error);
process.exit(1);
}
};
startServer();
با این کار ، ما سرویس باطن را نتیجه می گیریم. اکنون وقت آن رسیده است که Sveltekit را با Tailwindcss تنظیم کنید.
مرحله 2: sveltekit با tailwindcss
برای راه اندازی یک پروژه Sveltekit با Tailwindcss ، ما با برخی از اصلاحات از راهنمای مهاجرت که قبلاً نوشتم ، به راهنمای رسمی Tailwindcss مراجعه خواهیم کرد.
ابتدا یک پروژه جدید Sveltekit را از طریق Svelte 5 ایجاد کنید sv
CLI:
$ npx sv create interface # modify the name as you please
از شما خواسته می شود نصب کنید sv
که باید به آن لهجه کنید. تعامل شما با CLI باید به این شکل باشد:
projects npx sv create interface
Need to install the following packages:
sv@0.6.18
Ok to proceed? (y) y
┌ Welcome to the Svelte CLI! (v0.6.18)
│
◇ Which template would you like?
│ SvelteKit minimal
│
◇ Add type checking with Typescript?
│ Yes, using Typescript syntax
│
◆ Project created
│
◇ What would you like to add to your project? (use arrow keys / space bar)
│ prettier, eslint, vitest, tailwindcss, sveltekit-adapter
│
◇ tailwindcss: Which plugins would you like to add?
│ typography, forms
│
◇ sveltekit-adapter: Which SvelteKit adapter would you like to use?
│ node
│
◇ Which package manager do you want to install dependencies with?
│ npm
│
◆ Successfully setup add-ons
│
◆ Successfully installed dependencies
│
◇ Successfully formatted modified files
│
◇ Project next steps ─────────────────────────────────────────────────────╮
│ │
│ 1: cd interface │
│ 2: git init && git add -A && git commit -m "Initial commit" (optional) │
│ 3: npm run dev -- --open │
│ │
│ To close the dev server, hit Ctrl-C │
│ │
│ Stuck? Visit us at https://svelte.dev/chat │
│ │
├──────────────────────────────────────────────────────────────────────────╯
│
└ You're all set!
در صورت تمایل می توانید هر یک از مراحل را همانطور که دوست دارید اصلاح کنید. می توانید فهرست را به پروژه تازه ایجاد شده تغییر داده و وابستگی ها را نصب کنید.
به دلایلی ، tailwindcss نصب شده توسط sv
نسخه بود 3.4.17
بشر با این حال ، در زمان نوشتن این ، Tailwindcss در حال حاضر در نسخه است 4.0.2
بشر بنابراین ما باید مهاجرت کنیم. برای جلوگیری از مراحل مهاجرت ، این دستور را اجرا کنید:
interface$ npx @tailwindcss/upgrade@next
شما باید چیزی شبیه به این را بدست آورید:
interface$ npx @tailwindcss/upgrade@next
Need to install the following packages:
@tailwindcss/upgrade@4.0.2
Ok to proceed? (y) y
≈ tailwindcss v4.0.2
fatal: not a git repository (or any of the parent directories): .git
│ Searching for CSS files in the current
│ directory and its subdirectories…
│ ↳ Linked `./tailwind.config.ts` to
│ `./src/app.css`
│ Migrating JavaScript configuration files…
│ ↳ Migrated configuration file:
│ `./tailwind.config.ts`
│ Migrating templates…
│ ↳ Migrated templates for configuration file:
│ `./tailwind.config.ts`
│ Migrating stylesheets…
│ ↳ Migrated stylesheet: `./src/app.css`
│ Migrating PostCSS configuration…
│ ↳ Installed package: `@tailwindcss/postcss`
│ ↳ Removed package: `autoprefixer`
│ ↳ Migrated PostCSS configuration:
│ `./postcss.config.js`
│ Updating dependencies…
│ ↳ Updated package:
│ `prettier-plugin-tailwindcss`
│ ↳ Updated package: `tailwindcss`
fatal: not a git repository (or any of the parent directories): .git
│ No changes were made to your repository.
این جادویی شما را اصلاح می کند src/app.css
به نظر می رسد:
@import "tailwindcss";
@plugin '@tailwindcss/typography';
@plugin '@tailwindcss/forms';
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}
tailwind.config.ts
حذف خواهد شد و postcss.config.js
اصلاح خواهد شد ما باید تغییر جزئی ایجاد کنیم src/app.css
وت vite.config.js
:
@import "tailwindcss";
@plugin '@tailwindcss/typography';
@plugin '@tailwindcss/forms';
@custom-variant dark (&:where(.dark, .dark *));
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}
ما به خط 6 احتیاج داریم تا بتوانیم از کلاسها استفاده کنیم (و بعداً ، prefers-color-scheme
) برای تغییر پویا مضامین.
بعدی است vite.config.js
:
import { defineConfig } from "vitest/config";
import { sveltekit } from "@sveltejs/kit/vite";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [tailwindcss(), sveltekit()],
test: {
include: ["src/**/*.{test,spec}.{js,ts}"],
},
});
این نتیجه گیری اولیه را نتیجه می گیرد.
مرحله 3: اهرم prefers-color-scheme
در تغییر تم
بسیاری از سیستم عامل های مدرن (OS) حالت تاریک را به ویژگی درجه یک تبدیل کرده اند و تنظیم برنامه وب خود برای احترام به اولویت موضوع سیستم عامل کاربر ، تجربه کاربری بهتری را فراهم می کند. CSS prefers-color-scheme
ویژگی رسانه ای این کار را آسان می کند. در اینجا نحوه استفاده از آن آورده شده است:
lang="en">
charset="utf-8" />
name="language" content="en" />
name="theme-color"
content="#ffffff"
media="(prefers-color-scheme: light)"
/>
name="theme-color"
content="#111827"
media="(prefers-color-scheme: dark)"
/>
name="viewport" content="width=device-width, initial-scale=1" />
rel="preconnect" href="https://fonts.googleapis.com" />
rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&family=Fira+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet"
/>
... %sveltekit.head%
// Get user's explicit preference
const userTheme = localStorage.getItem("theme");
// Get system preference
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)");
// Use user preference if set, otherwise follow system
const theme = userTheme || (systemTheme.matches ? "dark" : "light");
// Apply theme
document.documentElement.classList.toggle("dark", theme === "dark");
// Listen for system changes
systemTheme.addEventListener("change", (e) => {
// Only follow system if user hasn't set preference
if (!localStorage.getItem("theme")) {
document.documentElement.classList.toggle("dark", e.matches);
}
});
data-sveltekit-preload-data="hover"
class="bg-white text-black dark:bg-gray-900 dark:text-white"
>
style="display: contents">%sveltekit.body%
این کد کوتاه HTML کارهای زیادی را انجام می دهد. اول ، theme-color
برچسب های متا UI مرورگر (به عنوان مثال ، نوار آدرس) را تطبیق می دهد تا با موضوع مطابقت داشته باشد. prefers-color-scheme
برای تنظیم رنگ های مختلف برای حالت های سبک و تاریک استفاده می شود. سپس ، در script
، ما ابتدا بررسی می کنیم که آیا کاربر اولویت موضوع ذخیره شده را دارد localStorage
بشر اگر اینگونه نباشد ، تنظیم حالت تاریک سطح سیستم عامل را با استفاده از آن بررسی می کند window.matchMedia('(prefers-color-scheme: dark)')
بشر سپس با اضافه کردن یا حذف آن ، موضوع مناسب را اعمال می کند dark
کلاس به element. This class is used to toggle CSS styles (e.g., using
dark:bg-gray-900
در class). Finally, it listens for changes in the OS-level dark mode setting and updates the theme accordingly (but only if the user hasn’t explicitly set a preference). We also used the
element to set the background and text colors based on the presence of the dark class using Tailwind CSS classes.
Step 4: Theme switching logic with icons
Before we proceed to creating the authentication/login page, let’s make a simple ThemeSwitcher.svelte
مؤلفه:
"ts">
import Moon from "$lib/components/icons/Moon.svelte";
import Sun from "$lib/components/icons/Sun.svelte";
let { ...props } = $props();
let isDark = $state(false);
$effect(() => {
isDark = document.documentElement.classList.contains("dark");
});
function toggleTheme() {
isDark = !isDark;
localStorage.setItem("theme", isDark ? "dark" : "light");
document.documentElement.classList.toggle("dark", isDark);
}
از آنجا که این برنامه کاملاً از Svelte 5 استفاده می شود ، ما از آن استفاده می کنیم $props()
RUNE برای پذیرش هیچ ویژگی های منتقل شده به مؤلفه به عنوان غرفه ها ، و اپراتور گسترش به گسترش این ویژگی ها کمک می کند. ما همچنین یک متغیر واکنشی را با $state
Rune ، و در آن به روز می شود $effect
رون و در toggleTheme
عملکرد. در $effect
Rune یک بار در ابتدای مؤلفه اجرا می شود و هر زمان که وابستگی های آن تغییر می کند. در این حالت ، بررسی می کند که آیا dark
کلاس در element and updates the
isDark
بر این اساس بیان کنید. این تضمین می کند که حالت اولیه مؤلفه با موضوع فعلی مطابقت دارد. در مورد toggleTheme
عملکرد ، هنگامی که دکمه کلیک می شود و آن را تغییر می دهد isDark
حالت ، موضوع انتخاب شده (“تاریک” یا “نور”) را ذخیره می کند localStorage
، و dark
کلاس در element. The
عنصر تماس می گیرد
toggleTheme
روی کلیک کنید و هرگونه پیشنهاد اضافی را به دکمه منتقل کنید. داخل دکمه ، {#if isDark}...{:else}...{/if}
بلوک به طور مشروط یا Sun
یا Moon
مؤلفه بر اساس isDark
دولت
توجه: رویدادها خواصی در Svelte 5 هستند
در on:
دستورالعمل در SVELTE 4 برای اتصال وقایع به عناصر HTML استفاده شد. با این حال ، Svelte 5 با طبیعی تر بودن با HTML ، آن روایت را تغییر داد ، که وقایع را به سادگی خواص می داند.
نمادهای خورشید و ماه به سادگی SVG هستند که به اجزای svelte تبدیل شده اند:
"ts">
let { ...props } = $props();
"ts">
let { ...props } = $props();
این یک روش عالی برای انعطاف پذیری SVG است زیرا می توانید سبک ها را منتقل کنید ، سایر ویژگی ها هستند و آنها منعکس می شوند.
مرحله 5: صفحه احراز هویت یا ورود به سیستم
حال ، بیایید ببینیم که صفحه ورود به سیستم چگونه خواهد بود:
"ts">
import { page } from '$app/state';
import AiNode from '$lib/components/icons/AINode.svelte';
import Calculator from '$lib/components/icons/Calculator.svelte';
import FinChart from '$lib/components/icons/FinChart.svelte';
import GitHub from '$lib/components/icons/GitHub.svelte';
import Google from '$lib/components/icons/Google.svelte';
import Logo from '$lib/components/logos/Logo.svelte';
import ThemeSwitcher from '$lib/components/reusables/ThemeSwitcher.svelte';
import { BASE_API_URI } from '$lib/utils/contants';
import { fade } from 'svelte/transition';
const next = page.url.searchParams.get('next') || '/';
class="relative min-h-screen bg-linear-to-br from-gray-100 to-gray-200 transition-colors duration-300 dark:from-gray-900 dark:to-gray-800"
>
class="dark:ring-black-500/50 absolute top-4 right-4 z-50 cursor-pointer rounded-full bg-white p-2 shadow-lg transition-all duration-300 hover:shadow-xl dark:bg-gray-700 dark:ring-2"
/>
class="absolute inset-0 z-0 overflow-hidden">
class="floating-icons absolute top-10 left-10 opacity-20 dark:opacity-30">
/>
class="floating-icons absolute right-20 bottom-32 opacity-20 dark:opacity-30">
/>
class="floating-icons absolute top-20 right-10 opacity-20 dark:opacity-30">
/>
class="relative z-10 flex min-h-screen items-center justify-center">
in:fade={{ duration: 300 }}
class="w-full max-w-md space-y-8 rounded-xl bg-white/80 p-8 shadow-lg backdrop-blur-xs transition-all duration-300 dark:bg-gray-800/90 dark:shadow-gray-900/30"
>
class="logo-container flex justify-center">
isSmall={false} class="h-12 w-auto" />
class="text-center">
class="mt-6 text-3xl font-extrabold text-gray-900 dark:text-white">Welcome back
class="mt-2 text-sm text-gray-600 dark:text-gray-400">
Sign in to continue to your account
{#if page.url.searchParams.get('error')}
class="mt-2 text-sm text-red-500 dark:text-red-400">
Log in failed. Please try again.
{/if}
class="mt-6">
class="relative">
class="absolute inset-0 flex items-center">
class="w-full border-t border-gray-300 dark:border-gray-600">
class="relative flex justify-center text-sm">
class="bg-white px-2 text-gray-500 dark:bg-gray-800 dark:text-gray-400">
Policy Agreement
.floating-icons {
animation: float 6s ease-in-out infinite;
}
.floating-icons:nth-child(2) {
animation-delay: 2s;
}
.floating-icons:nth-child(3) {
animation-delay: 4s;
}
@keyframes float {
0% {
transform: translateY(0px) rotate(0deg);
}
50% {
transform: translateY(-20px) rotate(360deg);
}
100% {
transform: translateY(0px) rotate(0deg);
}
}
اگرچه این کد طولانی به نظر می رسد ، تنها بخش بسیار مهم این است:
"ts">
import { page } from '$app/state';
...
import { BASE_API_URI } from '$lib/utils/contants';
...
const next = page.url.searchParams.get('next') || '/';
...
...
اینها فقط هستند عناصری که URL آنها به طور مستقیم به نقاط پایانی API پیوند می یابد (هنوز Google اجرا نشده است). در
BASE_API_URI
صادر شده توسط src/lib/utils/constants.ts
:
export const BASE_API_URI = import.meta.env.DEV
? import.meta.env.VITE_BASE_API_URI_DEV
: import.meta.env.VITE_BASE_API_URI_PROD;
لازم است که شما هم تنظیم کنید VITE_BASE_API_URI_DEV
یا VITE_BASE_API_URI_PROD
به عنوان متغیرهای محیطی که به URL پایه سرور شما اشاره می کنند. در توسعه ، من تنظیم کردم VITE_BASE_API_URI_DEV=http://localhost:3030/api
در الف .env
پرونده در ریشه پروژه Sveltekit من.
نکته: متغیرهای محیطی در تولید
هنگام اعزام به یک سرور تولید ، متغیر محیط را پیکربندی کنید VITE_BASE_API_URI_PROD
برای اشاره به URL تولید پس زمینه خود ، از جمله /api
مسیر (به عنوان مثال ، https://your-production-url.com/api
). بیشتر سیستم عامل های استقرار راهی برای تنظیم این متغیرها ارائه می دهند.
بازگشت در ما +page.svelte
، ما همچنین بازیابی می کنیم next
صفحه ، که مشخص می کند که کاربر پس از احراز هویت موفقیت آمیز باید هدایت شود. این به صورت هر مسیر پیکربندی شده است. علاوه بر این ویژگی های اصلی ، این صفحه شامل عناصر بصری مانند نمادهای شناور بی نهایت انیمیشن است. این نمادها –AiNode
(ادغام AI) ، FinChart
(امور مالی) ، و Calculator
(تجزیه و تحلیل) – مضامین اصلی برنامه را ارائه دهید. سبک های سفارشی برای ایجاد یک ترجمه عمودی مداوم از 0px
به -20px
و به عقب ، همراه با یک چرخش همزمان از 0deg
به 360deg
بشر ما می توانستیم مستقیماً از سبک های Tailwind استفاده کنیم.
این مقاله طولانی می شود ، بنابراین ما داشبورد کاربر را به مقاله بعدی معطل خواهیم کرد. با این حال ، کمی امنیت وجود دارد که باید انجام شود. کاربران معتبر نباید از آنجا که قبلاً تأیید شده اند ، به این صفحه ورود دسترسی پیدا کنند. برای دستیابی به این هدف ، ما +page.server.ts
پرونده ای که هدف آنها تغییر مسیر هر کاربر معتبر از آمدن به اینجا است:
import { redirect } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async ({ locals }) => {
if (locals.user) {
throw redirect(302, "/finanalyzer");
}
return {};
};
در مورد بعدی می بینیم!
دیگر
از این مقاله لذت بردید؟ من یک مهندس نرم افزار ، نویسنده فنی و مهندس پشتیبانی فنی هستم که به طور فعال به دنبال فرصت های جدید ، به ویژه در زمینه های مربوط به امنیت وب ، امور مالی ، مراقبت های بهداشتی و آموزش هستم. اگر فکر می کنید تخصص من با نیازهای تیم شما هماهنگ است ، بیایید گپ بزنیم! شما می توانید من را در LinkedIn و X پیدا کنید. من همچنین یک ایمیل دور هستم.
اگر این مقاله را با ارزش پیدا کردید ، برای کمک به گسترش دانش ، آن را با شبکه خود به اشتراک بگذارید!