From 2dbf994ec655ad65a68ceb3a74144c09e630b5c5 Mon Sep 17 00:00:00 2001 From: sergeyyarkov Date: Mon, 13 Feb 2023 23:00:29 +0300 Subject: [PATCH] cms config --- .prettierrc | 12 +- astro.config.mjs | 153 +++++++++++++++++- package-lock.json | 14 ++ package.json | 2 + src/components/ArticlePost/ArticlePost.astro | 8 +- src/components/BaseHead.astro | 35 ++-- src/components/Header/Header.astro | 6 +- src/components/OpenGraph.astro | 33 ++++ src/components/ProjectItem.astro | 91 +++++++++++ src/components/ProjectsList.astro | 14 ++ src/constants.ts | 3 +- ...pi-dlya-obshcheniya-s-mikrokontrollerom.md | 92 +++++------ src/content/config.ts | 17 +- src/content/projects/en/devthread.md | 11 ++ src/content/projects/en/digital-store.md | 10 ++ src/content/projects/en/educt.md | 10 ++ src/content/projects/en/memory-led-game.md | 10 ++ .../projects/en/node-docker-example.md | 10 ++ src/content/projects/en/react-chat-app.md | 10 ++ src/content/projects/en/web-serial-example.md | 11 ++ src/content/projects/en/yarkov-tech.md | 11 ++ src/content/projects/ru/devthread.md | 10 ++ src/content/projects/ru/digital-store.md | 10 ++ src/content/projects/ru/educt.md | 9 ++ src/content/projects/ru/memory-led-game.md | 8 + .../projects/ru/node-docker-example.md | 9 ++ src/content/projects/ru/react-chat-app.md | 9 ++ src/content/projects/ru/web-serial-example.md | 11 ++ src/content/projects/ru/yarkov-tech.md | 10 ++ src/content/site-settings.yml | 3 + src/i18n/languages.ts | 1 - src/i18n/utils.ts | 9 +- src/layouts/article.astro | 40 ++--- src/layouts/default.astro | 9 +- src/pages/404.astro | 5 + src/pages/[...slug].astro | 22 +++ src/pages/[lang]/about.astro | 13 ++ src/pages/[lang]/articles.astro | 13 ++ src/pages/[lang]/contacts.astro | 13 ++ src/pages/[lang]/index.astro | 13 ++ src/pages/[lang]/projects.astro | 13 ++ src/pages/about.astro | 5 + src/pages/articles.astro | 5 + src/pages/articles/[...slug].astro | 21 --- src/pages/ping.astro | 11 ++ src/pages/projects.astro | 5 + src/templates/About.astro | 26 +++ src/templates/Articles.astro | 39 +++++ src/templates/Home.astro | 8 +- src/templates/NotFound.astro | 13 ++ src/templates/Projects.astro | 31 ++++ 51 files changed, 823 insertions(+), 134 deletions(-) create mode 100644 src/components/OpenGraph.astro create mode 100644 src/components/ProjectItem.astro create mode 100644 src/components/ProjectsList.astro create mode 100644 src/content/projects/en/devthread.md create mode 100644 src/content/projects/en/digital-store.md create mode 100644 src/content/projects/en/educt.md create mode 100644 src/content/projects/en/memory-led-game.md create mode 100644 src/content/projects/en/node-docker-example.md create mode 100644 src/content/projects/en/react-chat-app.md create mode 100644 src/content/projects/en/web-serial-example.md create mode 100644 src/content/projects/en/yarkov-tech.md create mode 100644 src/content/projects/ru/devthread.md create mode 100644 src/content/projects/ru/digital-store.md create mode 100644 src/content/projects/ru/educt.md create mode 100644 src/content/projects/ru/memory-led-game.md create mode 100644 src/content/projects/ru/node-docker-example.md create mode 100644 src/content/projects/ru/react-chat-app.md create mode 100644 src/content/projects/ru/web-serial-example.md create mode 100644 src/content/projects/ru/yarkov-tech.md create mode 100644 src/content/site-settings.yml create mode 100644 src/pages/404.astro create mode 100644 src/pages/[...slug].astro create mode 100644 src/pages/[lang]/about.astro create mode 100644 src/pages/[lang]/articles.astro create mode 100644 src/pages/[lang]/contacts.astro create mode 100644 src/pages/[lang]/index.astro create mode 100644 src/pages/[lang]/projects.astro create mode 100644 src/pages/about.astro create mode 100644 src/pages/articles.astro delete mode 100644 src/pages/articles/[...slug].astro create mode 100644 src/pages/ping.astro create mode 100644 src/pages/projects.astro create mode 100644 src/templates/About.astro create mode 100644 src/templates/Articles.astro create mode 100644 src/templates/NotFound.astro create mode 100644 src/templates/Projects.astro diff --git a/.prettierrc b/.prettierrc index 90e3128..071de61 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,8 +1,8 @@ { - "printWidth": 120, - "semi": true, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "es5", - "useTabs": true + "printWidth": 100, + "semi": true, + "singleQuote": false, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": true } diff --git a/astro.config.mjs b/astro.config.mjs index 80130d5..569f46f 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -22,7 +22,7 @@ import { h } from "hastscript"; import { toString } from "hast-util-to-string"; import { escape } from "html-escaper"; import languages from "./src/i18n/languages"; -import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "./src/constants"; +import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES, SITE_METADATA } from "./src/constants"; const createAnchorLabel = (heading) => { const node = h("span.anchor-label", escape(heading)); @@ -48,11 +48,11 @@ export default defineConfig({ preview: false, }, display_url: "https://yarkov.tech", - // i18n: { - // locales: SUPPORTED_LANGUAGES, - // default_locale: DEFAULT_LANGUAGE, - // structure: "multiple_files", - // }, + i18n: { + locales: SUPPORTED_LANGUAGES, + default_locale: DEFAULT_LANGUAGE, + structure: "multiple_folders", + }, locale: DEFAULT_LANGUAGE, backend: { name: "test-repo", @@ -61,6 +61,9 @@ export default defineConfig({ media_folder: "public/media", public_folder: "/media", collections: [ + /** + * Articles + */ { name: "posts", label: "Публикации", @@ -75,6 +78,14 @@ export default defineConfig({ widget: "image", label: "Постер", }, + { + name: "draft", + label: "В черновике", + widget: "boolean", + required: true, + default: true, + hint: "Опубликована ли статья", + }, { name: "author", widget: "relation", @@ -107,9 +118,17 @@ export default defineConfig({ }, { name: "pubDate", - widget: "date", + widget: "datetime", label: "Дата публикации", required: true, + picker_utc: true, + }, + { + name: "updatedDate", + widget: "datetime", + label: "Дата обновления", + required: true, + picker_utc: true, }, { name: "body", @@ -118,6 +137,9 @@ export default defineConfig({ }, ], }, + /** + * Tags + */ { name: "tags", label: "Тэги", @@ -135,6 +157,9 @@ export default defineConfig({ }, ], }, + /** + * Authors + */ { name: "authors", label: "Авторы", @@ -159,12 +184,88 @@ export default defineConfig({ }, { label: "Об авторе", - name: "about", + name: "body", widget: "markdown", required: false, }, ], }, + /** + * Projects + */ + { + name: "projects", + label: "Проекты", + label_singular: "проект", + folder: "src/content/projects", + create: true, + delete: true, + i18n: true, + fields: [ + { + name: "icon", + label: "Иконка", + widget: "string", + required: true, + i18n: true, + }, + { + name: "draft", + label: "В черновике", + widget: "boolean", + required: true, + i18n: true, + default: true, + }, + { + name: "title", + label: "Название", + default: "my-project", + widget: "string", + required: true, + i18n: true, + }, + { + name: "year", + label: "В каком году разработал", + widget: "number", + default: 2023, + required: true, + i18n: true, + }, + { + name: "description", + label: "Описание", + widget: "string", + required: true, + i18n: true, + }, + { + name: "sourceUrl", + label: "Ссылка на исходник", + widget: "string", + required: true, + i18n: true, + }, + { + name: "demoUrl", + label: "Ссылка на демонстрацию", + widget: "string", + required: false, + i18n: true, + }, + { + name: "articleUrl", + label: "Ссылка на статью", + widget: "string", + required: false, + i18n: true, + }, + ], + }, + /** + * Skills + */ { name: "skills", label: "Навыки", @@ -179,6 +280,42 @@ export default defineConfig({ }, ], }, + /** + * Settings + */ + { + name: "settings", + label: "Настройки", + files: [ + { + name: "site-settings", + label: "Настройки веб-сайта", + file: "src/content/site-settings.yml", + fields: [ + { + label: "Глобальный заголовок", + name: "globalTitle", + widget: "string", + hint: "Отображется во вкладке браузера.", + }, + { + label: "Статьи", + name: "articles", + widget: "object", + fields: [ + { + name: "recentLimit", + label: "Лимит на отображение", + widget: "number", + hint: "Кол-во публикаций на гл.странице", + default: 5, + }, + ], + }, + ], + }, + ], + }, ], }, }), diff --git a/package-lock.json b/package-lock.json index d098528..cfc03a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "hastscript": "^7.2.0", "html-escaper": "^3.0.3", "ioredis": "^5.3.0", + "mime-types": "^2.1.35", "quicklink": "^2.3.0", "rehype-autolink-headings": "^6.1.1", "rehype-slug": "^5.1.0", @@ -29,6 +30,7 @@ "solid-js": "^1.6.10" }, "devDependencies": { + "@types/mime-types": "^2.1.1", "sass": "^1.58.0" } }, @@ -1689,6 +1691,12 @@ "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.3.tgz", "integrity": "sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==" }, + "node_modules/@types/mime-types": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz", + "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==", + "dev": true + }, "node_modules/@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", @@ -14506,6 +14514,12 @@ "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.3.tgz", "integrity": "sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==" }, + "@types/mime-types": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz", + "integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==", + "dev": true + }, "@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", diff --git a/package.json b/package.json index 2051707..edaf5a3 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "hastscript": "^7.2.0", "html-escaper": "^3.0.3", "ioredis": "^5.3.0", + "mime-types": "^2.1.35", "quicklink": "^2.3.0", "rehype-autolink-headings": "^6.1.1", "rehype-slug": "^5.1.0", @@ -38,6 +39,7 @@ "solid-js": "^1.6.10" }, "devDependencies": { + "@types/mime-types": "^2.1.1", "sass": "^1.58.0" } } diff --git a/src/components/ArticlePost/ArticlePost.astro b/src/components/ArticlePost/ArticlePost.astro index 0356c10..3d2f525 100644 --- a/src/components/ArticlePost/ArticlePost.astro +++ b/src/components/ArticlePost/ArticlePost.astro @@ -15,7 +15,7 @@ export type Props = { const { article, lang } = Astro.props as Props; const canonicalUrl = new URL(Astro.url.pathname, Astro.site); const { - data: { coverImage, title, description, tags, pubDate }, + data: { coverImage, title, author, description, tags, pubDate }, slug, } = article; // const t = useTranslation(Astro); @@ -48,8 +48,10 @@ const {

{title}

- - + +
{ coverImage && ( diff --git a/src/components/BaseHead.astro b/src/components/BaseHead.astro index 9726d7c..b1cb283 100644 --- a/src/components/BaseHead.astro +++ b/src/components/BaseHead.astro @@ -4,7 +4,7 @@ import { SITE_METADATA } from "@root/constants"; export interface Props { title?: string; - description: string; + description?: string; image?: string; } @@ -35,7 +35,7 @@ const { title, description } = Astro.props; - + {description && } @@ -55,25 +55,22 @@ const { title, description } = Astro.props; href="/favicon-light-16x16.png" media="(prefers-color-scheme: light)" /> - - + + - - - - - - - diff --git a/src/components/Header/Header.astro b/src/components/Header/Header.astro index 3d77a08..11aea74 100644 --- a/src/components/Header/Header.astro +++ b/src/components/Header/Header.astro @@ -17,17 +17,17 @@ import NavToggle from "@components/NavToggle"; { (
  • - + статьи
  • - + проекты
  • - + обо мне
  • diff --git a/src/components/OpenGraph.astro b/src/components/OpenGraph.astro new file mode 100644 index 0000000..05b8e2e --- /dev/null +++ b/src/components/OpenGraph.astro @@ -0,0 +1,33 @@ +--- +import mime from 'mime-types' +import { extname } from 'node:path' +import { isURL } from "@utils/string"; + +export interface Props { + title: string; + siteName: string; + description: string; + type: string; + url: string; + image?: { + path: string; + width: string; + height: string; + type: string; + } +} + +const {title, siteName, description, url, image} = Astro.props as Props; +--- + + + + + + +{image && ( + + + + +)} \ No newline at end of file diff --git a/src/components/ProjectItem.astro b/src/components/ProjectItem.astro new file mode 100644 index 0000000..ad1a29e --- /dev/null +++ b/src/components/ProjectItem.astro @@ -0,0 +1,91 @@ +--- +import { DEFAULT_LANGUAGE } from "@root/constants"; +import { getLanguageFromURL } from "@i18n/utils"; +import { Icon } from "astro-icon"; +import type { CollectionEntry } from "astro:content"; +// import UIString from "../UIString"; + +export interface Props { + data: CollectionEntry<"projects">["data"]; +} + +const { data } = Astro.props as Props; +// const lang = getLanguageFromURL(Astro.url.pathname); +const { icon, title, year, description, articleUrl, sourceUrl, demoUrl } = data; +--- + +
    +
    +

    + + {title} +

    + {year} +
    +
    +

    {description}

    +
    + +
    + + diff --git a/src/components/ProjectsList.astro b/src/components/ProjectsList.astro new file mode 100644 index 0000000..39e5e13 --- /dev/null +++ b/src/components/ProjectsList.astro @@ -0,0 +1,14 @@ +--- +--- + +
    + +
    + + diff --git a/src/constants.ts b/src/constants.ts index 4e46401..1cf32ee 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,7 +1,8 @@ export const DEFAULT_LANGUAGE: LanguageKeys = "ru"; /* Show banner about website development */ -export const IS_SITE_UNDER_CONSTRUCTION = import.meta.env.IS_SITE_UNDER_CONSTRUCTION === "true" ? true : false; +export const IS_SITE_UNDER_CONSTRUCTION = + import.meta.env.IS_SITE_UNDER_CONSTRUCTION === "true" ? true : false; export const SUPPORTED_LANGUAGES: SupportedLanguages = ["ru", "en"]; diff --git a/src/content/blog/ispolzovanie-webserial-api-dlya-obshcheniya-s-mikrokontrollerom.md b/src/content/blog/ispolzovanie-webserial-api-dlya-obshcheniya-s-mikrokontrollerom.md index 7003038..94ece3e 100644 --- a/src/content/blog/ispolzovanie-webserial-api-dlya-obshcheniya-s-mikrokontrollerom.md +++ b/src/content/blog/ispolzovanie-webserial-api-dlya-obshcheniya-s-mikrokontrollerom.md @@ -1,5 +1,6 @@ --- coverImage: /media/serial-port.jpg +draft: false author: Sergey Yarkov title: Использование WebSerial API для общения с микроконтроллером description: Речь пойдет о том, что такое WebSerial API, как его можно @@ -10,9 +11,10 @@ tags: - AVR - WebSerial API - Tutorial -pubDate: 2022-11-04 -draft: false +pubDate: 2022-11-04T21:00:00.000Z +updatedDate: 2023-02-13T16:34:23.326Z --- + # Что вы узнаете По итогу вы узнаете, как отправлять и принимать данные от устройства с последовательным портом в браузере, разработаете демонстрационное веб-приложение, которое отправляет команды в микроконтроллер с последовательным портом и напишите прошивку для микроконтроллера [AVR128DA48](http://ww1.microchip.com/downloads/en/DeviceDoc/40002183A.pdf) для обработки этих команд. Готовый вариант проекта можно посмотреть [здесь](https://web-serial-example.netlify.app/), исходники загружены в [репозиторий](https://github.com/sergeyyarkov/avr128da48_web-serial-example) на GitHub. @@ -47,17 +49,17 @@ WebSerial API - это браузерный API, который предоста UART передает данные последовательно по одному биту в одном из трех режимов: -* односторонний режим - данные отправляются только в одном направлении, от передатчика к приемнику -* полудуплексный режим - устройства могут передавать и принимать данные по очереди -* полнодуплексный режим - устройства могут передавать и принимать данные одновременно +- односторонний режим - данные отправляются только в одном направлении, от передатчика к приемнику +- полудуплексный режим - устройства могут передавать и принимать данные по очереди +- полнодуплексный режим - устройства могут передавать и принимать данные одновременно Так как в этом проекте предполагается использование асинхронной версии UART, то соответственно необходимо устанавливать **одинаковую** скорость передачи битов в секунду (`baud rate`, `bps`) на двух устройствах. Наиболее распространенные скорости являются: **4800, 9600, 19.2 К, 57.6К и 115.2К.** Данные передаются в виде так называемых `пакетов`, где каждый пакет содержит в себе: -* стартовый и стоповый биты - стартовый бит сигнализирует о поступлении битов данных, стоповый соответственно о конце данных. -* биты данных - пользовательские данные, которые поступают сразу после стартового бита, может содержаться от 5 до 8 битов. -* бит четности - необязательный бит, который идет после битов данных и перед стоповым битом и используется для обнаружения ошибок. +- стартовый и стоповый биты - стартовый бит сигнализирует о поступлении битов данных, стоповый соответственно о конце данных. +- биты данных - пользовательские данные, которые поступают сразу после стартового бита, может содержаться от 5 до 8 битов. +- бит четности - необязательный бит, который идет после битов данных и перед стоповым битом и используется для обнаружения ошибок.
    Структура UART пакета @@ -82,7 +84,7 @@ UART передает данные последовательно по одно Как я упомянул ранее, прошивка будет написана для микроконтроллера AVR128DA48. На самом деле неважно какой микроконтроллер вы используете, вам достаточно нужно уметь отправлять и принимать данные используя UART периферию вашего микроконтроллера. -Суть программы будет очень проста: необходимо написать некий ***обработчик команд*** который будет принимать на вход строку (например `led_toggle`) и вызывать необходимую функцию, которая будет выполнять какое-то действие, в данном случае переключать светодиод. +Суть программы будет очень проста: необходимо написать некий **_обработчик команд_** который будет принимать на вход строку (например `led_toggle`) и вызывать необходимую функцию, которая будет выполнять какое-то действие, в данном случае переключать светодиод. Постановка задачи ясна, теперь рассмотрим реализацию данной программы. Я буду использовать среду разработки MPLAB X IDE и язык C для написания прошивки. @@ -102,10 +104,10 @@ UART передает данные последовательно по одно #endif /* CONSTANTS_H */ ``` -* `F_CPU` - тактовая частота микроконтроллера -* `BAUD_RATE` - скорость передачи данных UART -* `BUFFER_SIZE` - размер буфера, куда будем складывать поступающие данные -* `EOT` - End-of-Transmission, ASCII символ конца передачи данных +- `F_CPU` - тактовая частота микроконтроллера +- `BAUD_RATE` - скорость передачи данных UART +- `BUFFER_SIZE` - размер буфера, куда будем складывать поступающие данные +- `EOT` - End-of-Transmission, ASCII символ конца передачи данных ## Инициализация USART @@ -155,11 +157,11 @@ void USART1_Initialize(void) { Для полнодуплексного режима инициализация асинхронной версии USART происходит следующим образом: -* Конфигурация скорости передачи данных путем записывания значения в регистр `USARTn.BAUD` -* Конфигурация размера `фрейма`, в нашем случае это 8 бит. -* Конфигурация пина `TX` на выход и пина `RX` на вход. -* Включение прерывания на то, когда закончился прием данных (когда пришел один пакет). -* Включение приемника и передатчика. +- Конфигурация скорости передачи данных путем записывания значения в регистр `USARTn.BAUD` +- Конфигурация размера `фрейма`, в нашем случае это 8 бит. +- Конфигурация пина `TX` на выход и пина `RX` на вход. +- Включение прерывания на то, когда закончился прием данных (когда пришел один пакет). +- Включение приемника и передатчика. Теперь, рассмотрим функции отправки данных: @@ -211,9 +213,9 @@ void command_list(void); Для решения этой задачи я предлагаю создавать массив структур, где в каждой структуре будет содержаться указатель на функцию которую нам нужно выполнить, и название самой команды. -* `command_define` будет создавать новую структуру в массиве. -* `command_process` будет вызывать соответствующую функцию по имени команды. -* `command_list` будет отправлять по UART информацию о существующих командах. +- `command_define` будет создавать новую структуру в массиве. +- `command_process` будет вызывать соответствующую функцию по имени команды. +- `command_list` будет отправлять по UART информацию о существующих командах. Реализовать задуманное можно следующим образом создав файл `command.c`: @@ -367,9 +369,9 @@ void hello_world(void) { С помощью функции `command_define` мы можем определить, по какой переданной команде по USART запускать функцию: -* `led_toggle` будет переключать светодиод. -* `hello_world` выводить сообщение “Hello world”. -* `command_list` будет показывать список имеющихся команд. +- `led_toggle` будет переключать светодиод. +- `hello_world` выводить сообщение “Hello world”. +- `command_list` будет показывать список имеющихся команд. ## Демонстрация работы @@ -377,10 +379,10 @@ void hello_world(void) { Выберете ваше устройство из списка (в моем случае это `/dev/ttyACM1`) и установите следующие настройки: -* Скорость: **9600** бод -* Количество битов: **8** -* Четность: **None** -* Стоповые биты: **1** +- Скорость: **9600** бод +- Количество битов: **8** +- Четность: **None** +- Стоповые биты: **1**
    { # Полезные материалы -* [Статус поддержки браузеров Can I use](https://caniuse.com/web-serial) -* [Документация к WebSerial API](https://wicg.github.io/serial/) -* [Подробное руководство по Stream API](https://web.dev/streams/) -* [Список USB идентификаторов](https://devicehunt.com/all-usb-vendors) -* [Доступ к различным устройствам из браузера](https://web.dev/devices-introduction/) \ No newline at end of file +- [Статус поддержки браузеров Can I use](https://caniuse.com/web-serial) +- [Документация к WebSerial API](https://wicg.github.io/serial/) +- [Подробное руководство по Stream API](https://web.dev/streams/) +- [Список USB идентификаторов](https://devicehunt.com/all-usb-vendors) +- [Доступ к различным устройствам из браузера](https://web.dev/devices-introduction/) diff --git a/src/content/config.ts b/src/content/config.ts index f2b0405..22cd6ea 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -13,8 +13,10 @@ const blog = defineCollection({ .transform((val) => new Date(val)), updatedDate: z .string() + .or(z.date()) .optional() .transform((str) => (str ? new Date(str) : undefined)), + draft: z.boolean(), }), }); @@ -31,4 +33,17 @@ const authors = defineCollection({ }), }); -export const collections = { blog, tags, authors }; +const projects = defineCollection({ + schema: z.object({ + icon: z.string(), + title: z.string(), + year: z.number(), + description: z.string(), + sourceUrl: z.string(), + demoUrl: z.string().nullable().optional(), + articleUrl: z.string().nullable().optional(), + draft: z.boolean(), + }), +}); + +export const collections = { blog, tags, authors, projects }; diff --git a/src/content/projects/en/devthread.md b/src/content/projects/en/devthread.md new file mode 100644 index 0000000..cfcc406 --- /dev/null +++ b/src/content/projects/en/devthread.md @@ -0,0 +1,11 @@ +--- +icon: triangle_filled +draft: false +title: devthread +year: 2021 +description: Static website for publishing various articles, developed with + Gatsby.js and NetlifyCMS +sourceUrl: https://github.com/sergeyyarkov/devthread.ru +demoUrl: https://devthread.yarkov.tech +articleUrl: null +--- diff --git a/src/content/projects/en/digital-store.md b/src/content/projects/en/digital-store.md new file mode 100644 index 0000000..5c927d3 --- /dev/null +++ b/src/content/projects/en/digital-store.md @@ -0,0 +1,10 @@ +--- +icon: round-shop-two +draft: false +title: digital-store +year: 2019 +description: Customizable online store with control panel for selling digital goods +sourceUrl: https://github.com/sergeyyarkov/digital-store/tree/digital-store-docker +demoUrl: https://digital-store.herokuapp.com +articleUrl: null +--- diff --git a/src/content/projects/en/educt.md b/src/content/projects/en/educt.md new file mode 100644 index 0000000..5091d5c --- /dev/null +++ b/src/content/projects/en/educt.md @@ -0,0 +1,10 @@ +--- +icon: round-school +draft: false +title: educt +year: 2022 +description: Learning Management System (LMS) to publish your courses, lectures, lessons +sourceUrl: https://github.com/sergeyyarkov/educt +demoUrl: https://educt.yarkov.tech +articleUrl: null +--- diff --git a/src/content/projects/en/memory-led-game.md b/src/content/projects/en/memory-led-game.md new file mode 100644 index 0000000..fe9098c --- /dev/null +++ b/src/content/projects/en/memory-led-game.md @@ -0,0 +1,10 @@ +--- +icon: chip +draft: false +title: memory-led-game +year: 2022 +description: Analogue of the popular game "Simon says" on the AVR microcontroller +sourceUrl: https://github.com/sergeyyarkov/attiny24a_memory-led-game +demoUrl: null +articleUrl: null +--- diff --git a/src/content/projects/en/node-docker-example.md b/src/content/projects/en/node-docker-example.md new file mode 100644 index 0000000..a7e54cf --- /dev/null +++ b/src/content/projects/en/node-docker-example.md @@ -0,0 +1,10 @@ +--- +icon: nodejs +draft: false +title: node-docker-example +year: 2022 +description: An example of using Docker to deploy a Node.js application +sourceUrl: https://github.com/sergeyyarkov/node-docker-example +demoUrl: https://node-docker-example.onrender.com +articleUrl: null +--- diff --git a/src/content/projects/en/react-chat-app.md b/src/content/projects/en/react-chat-app.md new file mode 100644 index 0000000..97527b8 --- /dev/null +++ b/src/content/projects/en/react-chat-app.md @@ -0,0 +1,10 @@ +--- +icon: chat +draft: false +title: react-chat-app +year: 2021 +description: Client-server web chat application developed with Node.js and Socket.io +sourceUrl: https://github.com/sergeyyarkov/react-chat-app +demoUrl: null +articleUrl: null +--- diff --git a/src/content/projects/en/web-serial-example.md b/src/content/projects/en/web-serial-example.md new file mode 100644 index 0000000..1c9a973 --- /dev/null +++ b/src/content/projects/en/web-serial-example.md @@ -0,0 +1,11 @@ +--- +icon: serial +draft: false +title: web-serial-example +year: 2022 +description: An example of using the Web Serial API to interact with the + microcontroller through a browser +sourceUrl: https://github.com/sergeyyarkov/avr128da48_web-serial-example +demoUrl: https://web-serial-example.netlify.app +articleUrl: null +--- diff --git a/src/content/projects/en/yarkov-tech.md b/src/content/projects/en/yarkov-tech.md new file mode 100644 index 0000000..5f9ab7c --- /dev/null +++ b/src/content/projects/en/yarkov-tech.md @@ -0,0 +1,11 @@ +--- +icon: book +draft: false +title: yarkov.tech +year: 2023 +description: My personal website where you can find information about me and a + list of various articles and tutorials... +sourceUrl: https://github.com/sergeyyarkov/yarkov.tech +demoUrl: https://yarkov.tech +articleUrl: null +--- diff --git a/src/content/projects/ru/devthread.md b/src/content/projects/ru/devthread.md new file mode 100644 index 0000000..4874eec --- /dev/null +++ b/src/content/projects/ru/devthread.md @@ -0,0 +1,10 @@ +--- +icon: triangle_filled +draft: false +title: devthread +year: 2021 +description: Статический веб-сайт для публикации различных статей, разработанный + при помощи Gatsby.js и NetlifyCMS +sourceUrl: https://github.com/sergeyyarkov/devthread.ru +demoUrl: https://devthread.yarkov.tech +--- diff --git a/src/content/projects/ru/digital-store.md b/src/content/projects/ru/digital-store.md new file mode 100644 index 0000000..3b9f2a2 --- /dev/null +++ b/src/content/projects/ru/digital-store.md @@ -0,0 +1,10 @@ +--- +icon: round-shop-two +draft: false +title: digital-store +year: 2019 +description: Настраиваемый интернет-магазин с панелью управления для продажи + цифровых товаров +sourceUrl: https://github.com/sergeyyarkov/digital-store/tree/digital-store-docker +demoUrl: https://digital-store.herokuapp.com +--- diff --git a/src/content/projects/ru/educt.md b/src/content/projects/ru/educt.md new file mode 100644 index 0000000..f2e3562 --- /dev/null +++ b/src/content/projects/ru/educt.md @@ -0,0 +1,9 @@ +--- +icon: round-school +draft: false +title: educt +year: 2022 +description: Система управления обучением (LMS) для публикации ваших курсов, лекций, уроков +sourceUrl: https://github.com/sergeyyarkov/educt +demoUrl: https://educt.yarkov.tech +--- diff --git a/src/content/projects/ru/memory-led-game.md b/src/content/projects/ru/memory-led-game.md new file mode 100644 index 0000000..6ef27af --- /dev/null +++ b/src/content/projects/ru/memory-led-game.md @@ -0,0 +1,8 @@ +--- +icon: chip +draft: false +title: memory-led-game +year: 2022 +description: Аналог популярной игры "Саймон говорит" на AVR микроконтроллере +sourceUrl: https://github.com/sergeyyarkov/attiny24a_memory-led-game +--- diff --git a/src/content/projects/ru/node-docker-example.md b/src/content/projects/ru/node-docker-example.md new file mode 100644 index 0000000..1a1e68e --- /dev/null +++ b/src/content/projects/ru/node-docker-example.md @@ -0,0 +1,9 @@ +--- +icon: nodejs +draft: false +title: node-docker-example +year: 2022 +description: Пример использования Docker для развертывания Node.js приложения +sourceUrl: https://github.com/sergeyyarkov/node-docker-example +demoUrl: https://node-docker-example.onrender.com +--- diff --git a/src/content/projects/ru/react-chat-app.md b/src/content/projects/ru/react-chat-app.md new file mode 100644 index 0000000..be4524f --- /dev/null +++ b/src/content/projects/ru/react-chat-app.md @@ -0,0 +1,9 @@ +--- +icon: chat +draft: false +title: react-chat-app +year: 2021 +description: Клиент-серверное веб приложение чата разработанное при помощи + Node.js и Socket.io +sourceUrl: https://github.com/sergeyyarkov/react-chat-app +--- diff --git a/src/content/projects/ru/web-serial-example.md b/src/content/projects/ru/web-serial-example.md new file mode 100644 index 0000000..04e68d3 --- /dev/null +++ b/src/content/projects/ru/web-serial-example.md @@ -0,0 +1,11 @@ +--- +icon: serial +draft: false +title: web-serial-example +year: 2022 +description: Пример использования Web Serial API для взаимодействия с + микроконтроллером через браузер. +sourceUrl: https://github.com/sergeyyarkov/avr128da48_web-serial-example +demoUrl: https://web-serial-example.netlify.app +articleUrl: https://yarkov.tech/blog/2022-11-04/ispolzovanie-webserial-api-dlya-obshcheniya-s-mikrokontrollerom/ +--- diff --git a/src/content/projects/ru/yarkov-tech.md b/src/content/projects/ru/yarkov-tech.md new file mode 100644 index 0000000..6b36f25 --- /dev/null +++ b/src/content/projects/ru/yarkov-tech.md @@ -0,0 +1,10 @@ +--- +icon: book +draft: false +title: yarkov.tech +year: 2023 +description: Мой персональный веб-сайт где находятся информация обо мне и список + различных статей и туториалов... +sourceUrl: https://github.com/sergeyyarkov/yarkov.tech +demoUrl: https://yarkov.tech +--- diff --git a/src/content/site-settings.yml b/src/content/site-settings.yml new file mode 100644 index 0000000..5816334 --- /dev/null +++ b/src/content/site-settings.yml @@ -0,0 +1,3 @@ +globalTitle: yarkov.tech +articles: + recentLimit: 5 diff --git a/src/i18n/languages.ts b/src/i18n/languages.ts index 3068727..ceb7191 100644 --- a/src/i18n/languages.ts +++ b/src/i18n/languages.ts @@ -1,5 +1,4 @@ export default { ru: "Русский", en: "English", - de: "Deutsch", }; diff --git a/src/i18n/utils.ts b/src/i18n/utils.ts index 0f726a9..eeff31f 100644 --- a/src/i18n/utils.ts +++ b/src/i18n/utils.ts @@ -1,6 +1,5 @@ -import type { AstroGlobal, MDXInstance } from "astro"; import languages from "./languages"; -import { DEFAULT_LANGUAGE } from "@root/constants"; +import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "@root/constants"; function transformExports(modules: Record) { const translations: Record = {}; @@ -11,6 +10,12 @@ function transformExports(modules: Record) { return translations; } +export function getLanguageKeys(): Array { + return Object.keys(languages).filter((lang) => + SUPPORTED_LANGUAGES.includes(lang as LanguageKeys) + ); +} + export const getLanguageFromURL = (pathname: string): LanguageKeys => { const lang = pathname.split("/")[1] as LanguageKeys; if (Object.hasOwn(languages, lang)) return lang; diff --git a/src/layouts/article.astro b/src/layouts/article.astro index d5728f7..67d5fd5 100644 --- a/src/layouts/article.astro +++ b/src/layouts/article.astro @@ -1,38 +1,42 @@ --- -import type { Props as BaseHeadProps } from "@root/components/BaseHead.astro"; +import type { CollectionEntry } from "astro:content"; +import { DEFAULT_LANGUAGE, SITE_METADATA } from "@root/constants"; import BaseHead from "@components/BaseHead.astro"; import BaseLayout from "@components/BaseLayout.astro"; import ArticleContent from "@components/ArticleContent/ArticleContent.astro"; import ArticlePost from "@components/ArticlePost/ArticlePost.astro"; -import { SITE_METADATA } from "@root/constants"; -import type { CollectionEntry } from "astro:content"; +import OpenGraph from "@components/OpenGraph.astro"; export interface Props { lang?: string | undefined; - content: CollectionEntry<"blog">; + article: CollectionEntry<"blog">; } -const { lang = "ru", content } = Astro.props as Props; + +const canonicalUrl = new URL(Astro.url.pathname, Astro.site); +const { lang = DEFAULT_LANGUAGE, article } = Astro.props as Props; +const { + data: { title, description, coverImage }, +} = article; --- - - - - - - - + +
    - + diff --git a/src/layouts/default.astro b/src/layouts/default.astro index a976a71..3024641 100644 --- a/src/layouts/default.astro +++ b/src/layouts/default.astro @@ -1,6 +1,6 @@ --- import type { Props as BaseHeadProps } from "@components/BaseHead.astro"; -import { SITE_METADATA } from "@root/constants"; +import { DEFAULT_LANGUAGE, SITE_METADATA } from "@root/constants"; import BaseHead from "@components/BaseHead.astro"; import BaseLayout from "@components/BaseLayout.astro"; @@ -10,11 +10,14 @@ export interface Props extends BaseHeadProps { canonicalUrl?: string | undefined; } -const { title, description, lang = "ru", noIndex = false } = Astro.props as Props; +const { title, description, lang = DEFAULT_LANGUAGE, noIndex = false } = Astro.props as Props; --- - + {noIndex && } diff --git a/src/pages/404.astro b/src/pages/404.astro new file mode 100644 index 0000000..e5393c6 --- /dev/null +++ b/src/pages/404.astro @@ -0,0 +1,5 @@ +--- +import NotFound from "@templates/NotFound.astro"; +--- + + diff --git a/src/pages/[...slug].astro b/src/pages/[...slug].astro new file mode 100644 index 0000000..62dec71 --- /dev/null +++ b/src/pages/[...slug].astro @@ -0,0 +1,22 @@ +--- +import { CollectionEntry, getCollection } from "astro:content"; +import ArticleLayout from "@root/layouts/article.astro"; + +export async function getStaticPaths() { + const articles = await getCollection("blog"); + return articles + .filter((a) => !a.data.draft) + .map((article) => ({ + params: { slug: `articles/${article.slug}` }, + props: { article }, + })); +} + +type Props = { article: CollectionEntry<"blog"> }; + +const { Content } = await Astro.props.article.render(); +--- + + + + diff --git a/src/pages/[lang]/about.astro b/src/pages/[lang]/about.astro new file mode 100644 index 0000000..ea752f0 --- /dev/null +++ b/src/pages/[lang]/about.astro @@ -0,0 +1,13 @@ +--- +import { getLanguageKeys } from "@i18n/utils"; +import { DEFAULT_LANGUAGE } from "@root/constants"; +import About from "@templates/About.astro"; + +export function getStaticPaths() { + return getLanguageKeys() + .filter((lang) => lang !== DEFAULT_LANGUAGE) + .map((lang) => ({ params: { lang } })); +} +--- + + diff --git a/src/pages/[lang]/articles.astro b/src/pages/[lang]/articles.astro new file mode 100644 index 0000000..727d04a --- /dev/null +++ b/src/pages/[lang]/articles.astro @@ -0,0 +1,13 @@ +--- +import { getLanguageKeys } from "@i18n/utils"; +import { DEFAULT_LANGUAGE } from "@root/constants"; +import Articles from "@templates/Articles.astro"; + +export function getStaticPaths() { + return getLanguageKeys() + .filter((lang) => lang !== DEFAULT_LANGUAGE) + .map((lang) => ({ params: { lang } })); +} +--- + + diff --git a/src/pages/[lang]/contacts.astro b/src/pages/[lang]/contacts.astro new file mode 100644 index 0000000..f306c2c --- /dev/null +++ b/src/pages/[lang]/contacts.astro @@ -0,0 +1,13 @@ +--- +import { getLanguageKeys } from "@i18n/utils"; +import { DEFAULT_LANGUAGE } from "@root/constants"; +import Contacts from "@templates/Contacts.astro"; + +export function getStaticPaths() { + return getLanguageKeys() + .filter((lang) => lang !== DEFAULT_LANGUAGE) + .map((lang) => ({ params: { lang } })); +} +--- + + diff --git a/src/pages/[lang]/index.astro b/src/pages/[lang]/index.astro new file mode 100644 index 0000000..ac6b969 --- /dev/null +++ b/src/pages/[lang]/index.astro @@ -0,0 +1,13 @@ +--- +import { getLanguageKeys } from "@i18n/utils"; +import { DEFAULT_LANGUAGE } from "@root/constants"; +import Home from "@templates/Home.astro"; + +export function getStaticPaths() { + return getLanguageKeys() + .filter((lang) => lang !== DEFAULT_LANGUAGE) + .map((lang) => ({ params: { lang } })); +} +--- + + diff --git a/src/pages/[lang]/projects.astro b/src/pages/[lang]/projects.astro new file mode 100644 index 0000000..d80541a --- /dev/null +++ b/src/pages/[lang]/projects.astro @@ -0,0 +1,13 @@ +--- +import { getLanguageKeys } from "@i18n/utils"; +import { DEFAULT_LANGUAGE } from "@root/constants"; +import Projects from "@templates/Projects.astro"; + +export function getStaticPaths() { + return getLanguageKeys() + .filter((lang) => lang !== DEFAULT_LANGUAGE) + .map((lang) => ({ params: { lang } })); +} +--- + + diff --git a/src/pages/about.astro b/src/pages/about.astro new file mode 100644 index 0000000..cc2f54e --- /dev/null +++ b/src/pages/about.astro @@ -0,0 +1,5 @@ +--- +import About from "@templates/About.astro"; +--- + + diff --git a/src/pages/articles.astro b/src/pages/articles.astro new file mode 100644 index 0000000..6c3a258 --- /dev/null +++ b/src/pages/articles.astro @@ -0,0 +1,5 @@ +--- +import Articles from "@templates/Articles.astro"; +--- + + diff --git a/src/pages/articles/[...slug].astro b/src/pages/articles/[...slug].astro deleted file mode 100644 index 77099e2..0000000 --- a/src/pages/articles/[...slug].astro +++ /dev/null @@ -1,21 +0,0 @@ ---- -import { CollectionEntry, getCollection } from "astro:content"; -import ArticleLayout from "@root/layouts/article.astro"; - -export async function getStaticPaths() { - const posts = await getCollection("blog"); - return posts.map((post) => ({ - params: { slug: post.slug }, - props: post, - })); -} - -type Props = CollectionEntry<"blog">; - -const post = Astro.props; -const { Content } = await post.render(); ---- - - - - diff --git a/src/pages/ping.astro b/src/pages/ping.astro new file mode 100644 index 0000000..7aacb1d --- /dev/null +++ b/src/pages/ping.astro @@ -0,0 +1,11 @@ + + + + + + + + pong + + pong + diff --git a/src/pages/projects.astro b/src/pages/projects.astro new file mode 100644 index 0000000..bc45be5 --- /dev/null +++ b/src/pages/projects.astro @@ -0,0 +1,5 @@ +--- +import Projects from "@root/templates/Projects.astro"; +--- + + diff --git a/src/templates/About.astro b/src/templates/About.astro new file mode 100644 index 0000000..0573bc9 --- /dev/null +++ b/src/templates/About.astro @@ -0,0 +1,26 @@ +--- +import PageLayout from "@layouts/default.astro"; +import { getLanguageFromURL } from "@i18n/utils"; +// import { useTranslation } from "@i18n/utils"; +// import * as API from "@root/lib/api"; +import PageHeading from "@components/PageHeading.astro"; +// import SkillsList from "@components/SkillsList"; +// import SkillsBlock from "@components/SkillsBlock"; +// import ArticleContent from "@components/ArticleContent"; +const lang = getLanguageFromURL(Astro.url.pathname); +// const t = useTranslation(Astro); +// const data = await API.fetchAboutMe(Astro); +// const { skills } = SITE_METADATA; +--- + + + +
    + + +
    +
    diff --git a/src/templates/Articles.astro b/src/templates/Articles.astro new file mode 100644 index 0000000..6aa128e --- /dev/null +++ b/src/templates/Articles.astro @@ -0,0 +1,39 @@ +--- +import PageLayout from "@layouts/default.astro"; +import PageHeading from "@components/PageHeading.astro"; +// import ArticlesList from "@components/ArticlesList"; +// import * as utils from "@utils/articles"; +import { getLanguageFromURL } from "@i18n/utils"; +// import { useTranslation } from "@i18n/utils"; +// import * as API from "@root/lib/api"; +// const t = useTranslation(Astro); +// const data = API.fetchTranslatedArticles(Astro); +// const tags = new Set(data.map((article) => article.frontmatter.tags).flat()); +const lang = getLanguageFromURL(Astro.url.pathname); +// const articles = utils.transformArticles(data); +const canonicalUrl = new URL(Astro.url.pathname, Astro.site); +--- + + + +
    + ... + +
    +
    diff --git a/src/templates/Home.astro b/src/templates/Home.astro index dc6909b..053ed26 100644 --- a/src/templates/Home.astro +++ b/src/templates/Home.astro @@ -7,7 +7,9 @@ import SplashLink from "@root/components/Splash/SplashLink.astro"; import { getCollection } from "astro:content"; import RecentArticles from "@root/components/RecentArticles.astro"; -const articles = (await getCollection("blog")).sort((a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf()); +const articles = (await getCollection("blog")) + .filter((p) => !p.data.draft) + .sort((a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf()); --- @@ -18,8 +20,8 @@ const articles = (await getCollection("blog")).sort((a, b) => a.data.pubDate.val

    Fullstack web-developer & freelancer

    - Я занимаюсь разработкой веб-сайтов и веб-приложений разной сложности, а также публикую статьи и туториалы на - разные IT темы. + Я занимаюсь разработкой веб-сайтов и веб-приложений разной сложности, а также публикую + статьи и туториалы на разные IT темы.

    diff --git a/src/templates/NotFound.astro b/src/templates/NotFound.astro new file mode 100644 index 0000000..31b8bd4 --- /dev/null +++ b/src/templates/NotFound.astro @@ -0,0 +1,13 @@ +--- +import PageLayout from "@layouts/default.astro"; +import { getLanguageFromURL } from "@i18n/utils"; +const lang = getLanguageFromURL(Astro.url.pathname); +// const t = useTranslation(Astro); +--- + + +
    +

    404

    +

    {"Страница не найдена"}

    +
    +
    diff --git a/src/templates/Projects.astro b/src/templates/Projects.astro new file mode 100644 index 0000000..8db4af7 --- /dev/null +++ b/src/templates/Projects.astro @@ -0,0 +1,31 @@ +--- +import PageLayout from "@layouts/default.astro"; +import ProjectsList from "@components/ProjectsList.astro"; +import ProjectItem from "@components/ProjectItem.astro"; +import PageHeading from "@components/PageHeading.astro"; +import { getLanguageFromURL } from "@i18n/utils"; +import { getCollection } from "astro:content"; +// import { useTranslation } from "@i18n/utils"; +// const t = useTranslation(Astro); +const lang = getLanguageFromURL(Astro.url.pathname); +const projects = (await getCollection("projects")).filter( + (p) => !p.data.draft && p.id.split(".")[1] === lang +); +--- + + + +
    + + {projects.map((project) => )} + +
    +