0
mirror of https://github.com/valentineus/popov.link.git synced 2025-07-02 23:50:27 +03:00

Compare commits

..

162 Commits

Author SHA1 Message Date
29385b1edf fix(deps): update dependency astro to v5.10.2 (#60)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-01 16:30:49 +00:00
ae7dac3099 chore(deps): update dependency prettier to v3.6.2 (#59)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-27 05:23:58 +00:00
d8a59ef4c4 chore(deps): update dependency prettier to v3.6.1 (#58)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-25 19:13:58 +00:00
4412049beb fix(deps): update dependency astro to v5.10.1 (#57)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 19:11:55 +00:00
c32aa4e773 chore(deps): update dependency prettier to v3.6.0 (#56)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 07:43:05 +00:00
c9d67d9210 fix(deps): update dependency astro to v5.10.0 (#55)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-19 19:52:42 +00:00
d99c901b42 fix(deps): update dependency astro to v5.9.4 (#54)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-17 17:06:39 +00:00
15fdeb46f1 chore: update README for consistency
- Removed duplicate line regarding project maintenance to streamline the README content.
2025-06-15 12:22:29 +00:00
15795a5337 chore: update GitHub Actions workflow for repository mirroring
- Modified the workflow trigger to include both push and pull request events on the master branch.
- Set permissions for the workflow to allow read access to contents.
2025-06-15 12:02:26 +00:00
bc4f65c1f6 chore: update README and add GitHub Actions workflow for repository mirroring
- Updated the README to change the read-only mirror link to the new URL.
- Added a GitHub Actions workflow for mirroring the repository to a remote Git repository.
2025-06-15 11:51:56 +00:00
a81117972d feat: implement Open Graph image generation and enhance configuration
- Added ogImages integration to generate Open Graph images for blog posts.
- Updated configuration to include Open Graph settings and default preview image.
- Refactored Head component to utilize new preview property for Open Graph meta tags.
- Enhanced blog post schema to include preview image for structured data representation.
- Introduced utility functions for creating Open Graph images with dynamic content.
2025-06-14 19:25:16 +00:00
3d0f485746 feat: add Open Graph and JSON-LD support to Head component
- Introduced OpenGraph component for enhanced social media sharing with Open Graph meta tags.
- Updated Head component to include OpenGraph and JSON-LD for improved SEO and structured data representation.
- Added comments for better clarity on meta tags and JSON-LD integration.
2025-06-14 12:19:01 +00:00
25ebd94466 refactor: remove preview property from blog post schema and configuration
- Eliminated the optional `preview` field from the blog post schema and configuration to streamline data handling.
- Updated the blog post schema to utilize the default image directly from configuration, enhancing consistency in image representation.
2025-06-14 12:12:16 +00:00
0473060773 feat: add configuration and default image for blog posts
- Introduced a new configuration file to centralize author information and default image settings for blog posts.
- Added a default image path in the blog post configuration for improved content presentation.
- Updated blog post schema to utilize the new configuration for author details, enhancing structured data representation.
2025-06-14 12:08:48 +00:00
a65e9c8455 feat: enhance blog post and page schema with new properties
- Added optional `basedOn` field to blog post schema for better content attribution.
- Updated blog post markdown to include `basedOn` reference for improved context.
- Refactored page schema to replace deprecated website schema, enhancing structured data representation.
- Adjusted 404 and index pages to utilize the new page schema for consistency and SEO improvements.
2025-06-14 11:47:17 +00:00
17f9a467d7 refactor: update blog post date handling and schema
- Replaced `pubDate` with `datePublished` in blog post components for consistency.
- Updated sorting logic in blog sections to use `datePublished`.
- Enhanced blog post schema to include `dateModified` for better structured data representation.
- Adjusted various blog markdown files to reflect the new date fields.
2025-06-14 11:25:17 +00:00
3df02c5304 feat: enhance blog post schema and structure
- Added description and lang parameters to the blogPostSchema for improved structured data.
- Updated the blog post layout to include a header section for better semantic structure and accessibility.
2025-06-14 11:09:34 +00:00
9777d996d1 refactor: enhance PostElement structure and update blog schema
- Wrapped the post link in an <article> tag for improved semantic structure.
- Updated blogSchema to include posts for better structured data representation.
- Adjusted the blog index to utilize the new posts parameter for enhanced SEO.
2025-06-14 11:01:42 +00:00
1c15151ef5 fix: enhance accessibility by adding role attribute to SVG icons 2025-06-14 10:53:20 +00:00
968b379ff0 fix(deps): update dependency astro to v5.9.3 (#53)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-13 17:56:38 +00:00
f408fd6327 chore(deps): update npm to v11.4.2 (#52)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-12 23:26:10 +00:00
67f245a48f feat: integrate schema.org support and enhance component structure
- Added schema.org support by introducing `schema-dts` for structured data in components.
- Updated `Head`, `BaseLayout`, and various page components to accept and utilize schema props for better SEO.
- Created new utility schemas for blog posts and website to standardize structured data implementation.
- Enhanced the `SocialLinks` section by utilizing dedicated icon components for improved maintainability.
- Refactored the `Header` component to improve accessibility and structure.
- Introduced new icons for social links and integrated them into the layout.
2025-06-11 23:20:36 +00:00
16fa8a3b5d feat: enhance accessibility and language support across components
- Updated various components to include `lang` attributes for improved accessibility and SEO.
- Introduced a new `env.d.ts` file to define environment variables for better type safety.
- Adjusted the print width in `.prettierrc.mjs` for improved code formatting.
- Streamlined the `Header`, `PostElement`, and `SocialLinks` components for better structure and clarity.
- Added language support to blog posts and updated the layout to reflect these changes.
2025-06-11 17:49:14 +00:00
423344fca5 chore: remove unused environment variables and update layout components
- Deleted the .env and src/env.d.ts files as they are no longer needed.
- Updated BaseLayout to require title and description props directly, ensuring better clarity in component usage.
- Adjusted various pages to pass explicit title and description values, enhancing SEO and user experience.
- Increased print width in .prettierrc.mjs for improved code formatting.
2025-06-11 17:20:43 +00:00
78a9c2abc5 feat: add LatestPosts section to homepage
- Introduced a new LatestPosts component to display the five most recent blog posts.
- Updated the index page to include the LatestPosts section, enhancing content visibility.
- Made minor text adjustments in the Welcome section for clarity.
2025-06-11 16:47:48 +00:00
604e507b31 refactor: update blog layout and components
- Removed the PostSummary component and replaced it with a new PostElement component for better post display.
- Introduced SocialLinks and Welcome sections to enhance the homepage layout.
- Updated the index page to utilize the new sections, improving overall structure and user experience.
2025-06-11 16:34:34 +00:00
3d6aedd272 feat: group blog posts by year in index page
- Implemented functionality to categorize blog posts by publication year.
- Updated the blog index page to display posts organized under their respective years for improved navigation.
2025-06-11 16:05:50 +00:00
6fe5df4e32 refactor: simplify Header component by removing site title and navigation wrapper
- Removed the site title and navigation links wrapper from the Header component for a cleaner structure.
- Updated styles to reflect these changes, streamlining the component's layout.
2025-06-11 15:56:06 +00:00
26de615385 chore: update README to reflect new badge and remove CI link
- Replaced the CI badge with a 512KB club badge for better representation of the project.
2025-06-11 00:11:24 +00:00
77e65cb92c refactor: simplify Analytics component and update BaseLayout
* style: enhance Header component and update SCSS imports

- Improved the Header component by adding a site title with styling.
- Wrapped navigation links in a div for better structure.
- Updated SCSS imports across multiple components for consistency.

* refactor: simplify Analytics component and update BaseLayout

- Removed props from the Analytics component to streamline its usage.
- Updated BaseLayout to call Analytics without passing the title prop, ensuring default values are used.
2025-06-11 00:12:57 +04:00
34ce9f6162 style: enhance Header component and update SCSS imports (#50)
- Improved the Header component by adding a site title with styling.
- Wrapped navigation links in a div for better structure.
- Updated SCSS imports across multiple components for consistency.
2025-06-10 22:38:58 +04:00
f3cc07e92c chore: migrate sass imports to use (#49) 2025-06-10 18:27:34 +04:00
d74eec1c47 Merge pull request #48 from valentineus/header
New Header
2025-06-10 18:20:48 +04:00
9ebcd40f60 feat: create PostElement component for blog post display
- Added a new PostElement component to render individual blog posts with title, publication date, and reading time.
- Updated the blog index page to utilize PostElement instead of PostSummary for improved post presentation.
2025-06-10 14:17:48 +00:00
4e8c17a6ea chore: update blog routing and header link
- Removed the redirects for the blog route in the configuration.
- Updated the blog link in the Header component to include a trailing slash.
- Added a new index page for the blog to display all posts.
2025-06-10 14:05:48 +00:00
6a47cb4165 refactor: remove Pagination component and restructure blog page
- Deleted the Pagination component as it is no longer needed.
- Refactored the blog page to directly display posts without pagination.
- Introduced a new index page to list all blog posts in a single view.
2025-06-10 14:01:27 +00:00
bb7481670e feat: add header component and update blog layout
- Introduced a new Header component for site navigation.
- Integrated Header into BaseLayout for consistent site structure.
- Updated blog post layout to include the post title in a dedicated section.
- Minor update to README for license clarity.
2025-06-10 13:44:56 +00:00
d322487420 fix(deps): update all digest updates (#47)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-10 02:59:47 +00:00
dfe9115ac9 fix(deps): update dependency astro to v5.9.1 (#46)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-07 14:31:57 +00:00
2f535ac598 docs: update README and license
* docs: update README and add licenses

* chore: remove unused materials license and update README for clarity
2025-06-06 04:03:32 +04:00
b1c85503d6 revert: update comment section theme 2025-06-05 23:52:13 +00:00
009dca3402 style: update comment section theme 2025-06-05 23:50:10 +00:00
e56c4f3edf perf: improve site icon loading 2025-06-05 23:33:09 +00:00
e9612c756b feat: add IE11 compatibility 2025-06-05 23:22:39 +00:00
eb36082087 chore(deps): update dependencies 2025-06-05 23:16:27 +00:00
81bcfbdc65 Migrate from Gitea Actions to GitHub Actions 2025-06-05 23:12:22 +00:00
4266100d7f Update all digest updates 2025-06-05 00:00:57 +00:00
2dcf837bee Update all digest updates 2025-05-31 00:00:59 +00:00
9b937e2736 Refactor CI workflow step names for clarity by adding descriptive titles for dependency installation and checks. 2025-05-23 12:02:49 +00:00
6d0f766671 Remove push trigger from Renovate workflow configuration to streamline scheduling. 2025-05-23 11:59:13 +00:00
aaeefd48ee Update all digest updates 2025-05-23 11:58:00 +00:00
27caca159e Update Renovate configuration to enable 'ignoreScripts' for npm and improve workflow step naming for clarity during dependency updates. 2025-05-23 11:57:15 +00:00
cf5901f8c1 Update Renovate configuration to use the full container image for enhanced functionality during dependency updates. 2025-05-23 11:52:02 +00:00
547b008398 Update Renovate configuration by removing 'githubTokenWarn' and adding 'RENOVATE_GITHUB_COM_TOKEN' for improved token management in Gitea workflows. 2025-05-23 11:45:24 +00:00
1d79bd154b Update Renovate configuration to enable 'githubTokenWarn' and set 'ignoreScripts' to false, enhancing security and improving script execution during dependency updates. 2025-05-23 11:43:17 +00:00
24c710bd41 Add GitHub token to Renovate configuration for enhanced repository access during updates. 2025-05-23 11:39:15 +00:00
3660982271 Update Renovate configuration to enable 'ignoreScripts' for npm, enhancing security during dependency updates. 2025-05-23 11:29:43 +00:00
ad6903f7ee Remove 'binarySource' setting from Renovate configuration to streamline package source management. 2025-05-23 11:23:19 +00:00
5c89158bdb Update Renovate configuration by removing 'binarySource' from JSON file and adding it to CJS file for consistent package source management. 2025-05-23 11:20:03 +00:00
bff792581a Add 'binarySource' setting to Renovate configuration for improved package source management. 2025-05-23 11:15:35 +00:00
a170c87cdb Refactor Renovate configuration by removing 'containerbase' and 'lockFileMaintenance' settings, streamlining dependency management and focusing on package update rules. 2025-05-23 11:12:37 +00:00
dca4c61251 Add 'containerbase' setting to Renovate configuration, disabling container base updates for improved dependency management control. 2025-05-23 11:01:36 +00:00
d0b675b944 Update Renovate configuration to include 'postUpdateOptions' for npm lockfile updates, improving dependency management automation. 2025-05-23 10:59:26 +00:00
cb4820f8f1 Update Renovate configuration to include 'skipInstalls' setting, enhancing npm management during dependency updates. 2025-05-23 10:54:37 +00:00
39e28d06ea Remove 'labels' from Renovate configuration and retain 'automerge' setting for improved dependency update control. 2025-05-23 10:50:09 +00:00
6f0260f7bc Update Renovate configuration to include a schedule for lock file maintenance, allowing updates at any time for enhanced flexibility in dependency management. 2025-05-23 10:48:49 +00:00
d74d8acdc2 Enhance Renovate configuration by adding range strategy and enabling lock file maintenance, while consolidating npm settings for improved dependency management. 2025-05-23 10:47:27 +00:00
c47b9f9037 Refactor Renovate configuration by removing automerge setting to enhance control over dependency updates. 2025-05-23 10:42:23 +00:00
1ed689ed71 Add new Renovate configuration file to automate dependency management 2025-05-23 10:39:24 +00:00
1d7fbe8fbb Updated Renovate configuration to set npm requireConfig to false, removing the allowed post-upgrade commands for improved dependency management clarity. 2025-05-23 08:03:09 +00:00
2bb3139579 Updated Renovate configuration to include allowed post-upgrade commands and specify file filters, enhancing the automation of dependency management tasks. 2025-05-23 07:58:56 +00:00
f88a1eaa69 Updated Renovate configuration to set npm requireConfig to "optional" and removed post-upgrade tasks for streamlined dependency management. 2025-05-23 07:56:42 +00:00
1f18536f7a Update npm to v11 2025-05-23 07:52:03 +00:00
a560f6d2e0 Updated package.json to specify npm version 10.9.0 as the package manager, enhancing project dependency management. 2025-05-23 07:51:17 +00:00
6adb475581 Updated Renovate configuration to enable npm settings, added post-upgrade tasks for smoother dependency management, and refined command execution for package updates. 2025-05-23 07:42:39 +00:00
725ec5df89 Refined Renovate configuration by removing unnecessary parameters and simplifying package rules for improved clarity and efficiency. 2025-05-23 07:34:44 +00:00
a6078cdf0b Updated Renovate configuration to include binary source setting, streamlining dependency management and enhancing package update processes. 2025-05-23 07:29:14 +00:00
b7005555c7 Added push trigger for master branch in Renovate workflow to enhance automation on code updates. 2025-05-23 07:24:54 +00:00
5364a666fd Enhanced Renovate configuration by enabling lock file updates and adding post-upgrade tasks for npm, ensuring smoother dependency management. 2025-05-23 07:24:15 +00:00
b5bc0bc56a Updated Renovate configuration to include range strategy and enabled npm settings with stability days and post-update options. 2025-05-22 23:33:01 +00:00
da49ba84ba Added debug log level to Renovate workflow for improved troubleshooting. 2025-05-22 23:28:25 +00:00
a1895c8bc8 The Renovate configuration file has been added and the setting has been updated to optimize for disabled actions. 2025-05-21 17:35:20 +04:00
cfa3cc7be8 Unnecessary parameters have been removed from the Renovate configuration 2025-05-21 17:30:43 +04:00
0db17945d4 Moved renovate.json 2025-05-10 14:53:13 +04:00
2b4908aa92 Added self-hosted Renovate Bot 2025-05-08 13:12:45 +00:00
001585b8a6 Update dependency 2025-05-08 12:54:35 +00:00
8bce6939d1 Disabled GitHub CI; Enabled Gitea Actions 2025-05-08 12:34:43 +00:00
e296d4a801 Update dependency astro to v5.4.3 (#30)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-11 17:36:53 +00:00
f99f41dfc7 Update dependency autoprefixer to v10.4.21 (#28)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-09 17:14:53 +00:00
827ee4c225 Update dependency astro to v5.4.2 (#27)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-04 18:12:44 +00:00
badac0763d Update dependency prettier to v3.5.3 (#26)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 07:20:57 +00:00
dc18c50c4e Update all digest updates (#25)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-28 23:02:12 +00:00
7e12cdc213 Update dependency astro to v5.4.0 (#24)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-27 23:29:47 +00:00
0460ff06d9 Update dependency sass to v1.85.1 (#23)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 06:46:35 +00:00
549e1ac8b8 Update dependency astro to v5.3.1 (#22)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 23:36:55 +00:00
5cb421cfec Update dependency prettier to v3.5.2 (#21)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-22 09:26:32 +00:00
86efbe695c Update dependency sass to v1.85.0 (#20)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-14 06:00:30 +00:00
71a7c3cb77 Update all digest updates (#19)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-13 23:12:52 +00:00
97a4c5b168 Update dependency astro to v5.2.6 (#18)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-12 22:45:25 +00:00
7fe8a10f02 Update dependency prettier to v3.5.0 (#17)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-09 17:09:46 +00:00
6986070ec8 Update dependency sass to v1.84.0 (#16)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-06 04:56:58 +00:00
0deb65fde2 Added an icon for the website 2025-02-05 00:20:40 +00:00
88464d0240 Updated Renovate config 2025-02-05 03:43:12 +04:00
fd32a6b8f7 Update dependency astro to v5.2.5 (#15)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-04 22:12:40 +00:00
2202c45aba Merge pull request #14 from valentineus/renovate/all-digest
Update dependency astro to v5.2.4
2025-02-04 22:53:53 +04:00
29e3b8e224 Update dependency astro to v5.2.4 2025-02-04 14:01:24 +00:00
0bba3fe744 Updated CI parameters & Added mirror 2025-02-01 18:12:29 +04:00
f826ef31c7 Update dependency astro to v5.2.3 (#13)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-01 01:35:10 +00:00
2cc827ef55 Update dependency astro to v5.2.2 (#12)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-31 17:18:14 +00:00
6c083b7b0e Merge pull request #11 from valentineus/renovate/all-digest
Update dependency astro to v5.2.1
2025-01-31 00:16:48 +04:00
7a3c63e85d Update dependency astro to v5.2.1 2025-01-30 19:25:48 +00:00
d2ac0ca590 Merge pull request #10 from valentineus/renovate/all-digest
Update dependency astro to v5.2.0
2025-01-30 18:57:49 +04:00
a468e4e7a4 Update dependency astro to v5.2.0 2025-01-30 13:09:05 +00:00
e48033a97d Merge pull request #9 from valentineus/renovate/all-digest
Update dependency astro to v5.1.10
2025-01-27 18:36:29 +04:00
1d139b0872 Update dependency astro to v5.1.10 2025-01-27 14:03:58 +00:00
9b62213886 Updating the project version to 2025.01.24 in the package.json and package-lock.json 2025-01-23 23:14:20 +00:00
c694648c2c Adding a link to the main blog page 2025-01-23 23:10:09 +00:00
7724e9617d Adding the "vitesse-dark" theme configuration for Shiki in the Markdown settings 2025-01-23 23:03:20 +00:00
0cbdbbad7f Improved printing styles: adding backgrounds and text colors, customizing tables and links 2025-01-23 22:47:57 +00:00
849d637880 Changing the background color to a darker shade 2025-01-23 22:42:11 +00:00
6a81db4cfc Adding disabling the Dependency panel to the Renovate configuration 2025-01-23 22:36:12 +00:00
1b6b61ba31 Merge pull request #7 from valentineus/renovate/migrate-config
Migrate renovate config
2025-01-24 02:33:44 +04:00
5c0ed9cf56 Migrate config .renovaterc 2025-01-23 22:32:09 +00:00
f2bd75ac67 Adding the Dependabot configuration for devcontainers updates at a weekly interval 2025-01-23 22:31:04 +00:00
90a0ba8b97 Changing the Dependabot configuration to update dependencies once a week and adding support for GitHub Actions updates; removing the Renovate configuration. 2025-01-23 22:29:33 +00:00
aa94393f4d Updating the Dependabot configuration for daily dependency updates with permission for direct dependencies and activation of security updates; disabling security updates in Renovate. 2025-01-23 22:18:54 +00:00
f2025da4fd Adding Renovate configuration and updating dependencies 2025-01-23 22:15:33 +00:00
5e4542edcb Merge pull request #5 from valentineus/dependabot/npm_and_yarn/astro-5.1.9
Bump astro from 5.1.8 to 5.1.9
2025-01-24 02:00:37 +04:00
8a203a3502 Bump astro from 5.1.8 to 5.1.9
Bumps [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro) from 5.1.8 to 5.1.9.
- [Release notes](https://github.com/withastro/astro/releases)
- [Changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/astro@5.1.9/packages/astro)

---
updated-dependencies:
- dependency-name: astro
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-23 21:59:37 +00:00
f1416bf181 Configuring Dependabot for daily dependency updates and deleting Renovate configurations 2025-01-23 21:58:33 +00:00
0d0f2923ac Update dependency astro to v5.1.8 2025-01-21 00:03:04 +00:00
231375202d Update dependency astro to v5.1.7 2025-01-16 00:02:07 +00:00
347636d1bb Update all digest updates 2025-01-14 07:41:51 +00:00
2f5d40dadd Update all digest updates 2025-01-10 21:02:03 +00:00
b841e04f60 Update dependency astro to v5.1.3 2025-01-06 21:01:28 +00:00
a01ded06c1 Update dependency sass to v1.83.1 2025-01-04 21:02:07 +00:00
f84ecc39b0 Update all digest updates 2025-01-02 21:03:56 +00:00
077c2320d4 Update dependency astro to v5.1.1 2024-12-20 21:03:53 +00:00
e619a27d51 Update dependency astro to v5.0.9 2024-12-17 21:02:17 +00:00
cfb4026385 Update dependency astro to v5.0.8 2024-12-16 21:02:21 +00:00
8597054d13 Update dependency sass to v1.83.0 2024-12-13 21:03:18 +00:00
cab6ad95f8 Update dependency astro to v5.0.5 2024-12-11 21:01:14 +00:00
cf75ecfe6b Update all digest updates 2024-12-05 21:01:11 +00:00
4d4eb86bfa Merge pull request 'Update dependency astro to v5' (!30) from renovate/major-astro-monorepo into master
Reviewed-on: #30
2024-12-04 10:11:37 +03:00
566685adc9 Update dependency astro to v5 2024-12-03 21:03:00 +00:00
01b82f0524 Update dependency sass to v1.81.1 2024-12-03 21:02:46 +00:00
b886cd95c5 Update dependency astro to v4.16.16 2024-11-27 21:04:09 +00:00
d466742e89 Update all digest updates 2024-11-26 21:03:33 +00:00
563e197897 Update all digest updates 2024-11-22 21:04:17 +00:00
18fb312ca4 Update all digest updates 2024-11-15 21:01:22 +00:00
493042f450 Update all digest updates 2024-11-13 21:04:59 +00:00
874d7fa715 Update dependency astro to v4.16.10 2024-11-10 21:07:31 +00:00
471c98d958 Merge pull request 'Update ghcr.io/renovatebot/renovate Docker tag to v39' (!22) from renovate/ghcr.io-renovatebot-renovate-39.x into master
Reviewed-on: #22
2024-11-06 09:43:04 +03:00
6a05b1f22f Update ghcr.io/renovatebot/renovate Docker tag to v39 2024-11-04 21:07:08 +00:00
058299a1fa Update all digest updates 2024-11-04 21:07:05 +00:00
f4ba238874 Update dependency astro to v4.16.8 2024-10-31 21:01:45 +00:00
f7f601e619 Update dependency sass to v1.80.5 2024-10-30 21:02:11 +00:00
b6a06ed787 Deleted the unnecessary CF adapter 2024-10-24 19:54:32 +00:00
bc13a45d3d Updated the structure of the post's page 2024-10-24 19:50:34 +00:00
ef6185eb00 Deleted the footer of the pages 2024-10-24 19:45:26 +00:00
2aa8042e3d Deleted the page headers 2024-10-24 19:16:56 +00:00
f8d98d2933 Update package dependencies 2024-10-24 19:11:06 +00:00
97 changed files with 3964 additions and 4325 deletions

2
.env
View File

@ -1,2 +0,0 @@
DEFAULT_TITLE=Valentin Popovs Blog
DEFAULT_DESCRIPTION=Tech insights and coding best practices from an OpenSource enthusiast and ethical hacker.

View File

@ -1,14 +0,0 @@
name: Test
on: [push, pull_request]
jobs:
test:
container: gitea/runner-images:ubuntu-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run check

View File

@ -1,16 +0,0 @@
name: RenovateBot
on:
schedule:
- cron: "@daily"
jobs:
renovate:
container: ghcr.io/renovatebot/renovate:38
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: renovate
env:
RENOVATE_CONFIG_FILE: renovate.config.cjs
RENOVATE_REPOSITORIES: ${{ gitea.repository }}
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}

14
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,14 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

16
.github/renovate.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended", ":disableDependencyDashboard"],
"assignees": ["valentineus"],
"labels": ["dependencies", "automated"],
"packageRules": [
{
"description": "Group patch & minor updates together",
"groupName": "all digest updates",
"groupSlug": "all-digest",
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"matchPackageNames": ["*"],
"automerge": true
}
]
}

View File

28
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
permissions:
contents: read
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: npm ci
- name: Run checks
run: npm run check
- name: Run type checks
run: npm run typecheck

25
.github/workflows/mirror.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Mirror
on:
push:
branches: [master]
pull_request:
branches: [master]
permissions:
contents: read
jobs:
mirror:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Mirror to remote repository
uses: yesolutions/mirror-action@master
with:
REMOTE: "https://git.popov.link/popov.link.git"
GIT_USERNAME: ${{ secrets.GIT_USERNAME }}
GIT_PASSWORD: ${{ secrets.GIT_PASSWORD }}

View File

@ -30,7 +30,7 @@ export default {
},
],
plugins: ["prettier-plugin-astro"],
printWidth: 120,
printWidth: 256,
proseWrap: "never",
quoteProps: "consistent",
requirePragma: false,

View File

@ -1,24 +0,0 @@
{
"assignees": [
"valentineus"
],
"extends": [
"config:recommended",
":disableDependencyDashboard"
],
"packageRules": [
{
"groupName": "all digest updates",
"groupSlug": "all-digest",
"matchPackagePatterns": [
"*"
],
"matchUpdateTypes": [
"minor",
"patch",
"pin",
"digest"
]
}
]
}

7
LICENSE.txt Normal file
View File

@ -0,0 +1,7 @@
Copyright (c) 2025 Valentin Popov <valentin@popov.link>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,49 +1,54 @@
# Personal site
# popov.link
This is my main site. The site publishes articles, useful information and notes. Also, the site serves as a hosting of free and personal images.
[![512KB club](https://512kb.club/assets/images/green-team.svg)](https://512kb.club)
Principles of site development:
Personal website source code built with [Astro](https://astro.build/).
- Lightness and minimalism. The site is designed to store information, it's not a heavy application;
## Requirements
- Maximum cross-platform. Information should be read from any device and software;
- Focusing on content perception. Only useful information, nothing superfluous;
- No JS on the site. The site should be completely safe for the user;
- Node.js v22 or later
- npm v11 or later
## Development
To start a local server for development, you need:
1. Clone the source repository:
1. Install dependencies:
```bash
git clone "https://github.com/valentineus/valentineus.github.io.git"
npm ci
```
2. Install packages:
2. Start the development server:
```bash
cd valentineus.github.io && bundle update
npm run dev
```
3. Start the server:
3. Open your browser and go to http://localhost:3000 to view changes live.
## Build & Preview
- To build the project for production:
```bash
bundle exec jekyll serve --host "${IP}" --port "${PORT}" --trace
npm run build
```
Please note in the executable command uses environment variables `IP` and `PORT`.
- To preview the production build locally:
## License hosted material
```bash
npm run preview
```
<img width="256px" alt="CC BY-NC 3.0 License" src="https://raw.githubusercontent.com/valentineus/valentineus.github.io/master/assets/images/87624cb5-4a8f-4be4-90b6-0ec5b9a90333.png" />
## Project Info
Material on the site is published on the CC BY-NC 3.0 license.
- Issues: [GitHub](https://github.com/valentineus/popov.link/issues)
- Read-only mirror: [git.popov.link](https://git.popov.link/popov.link/)
- Maintained by [Valentin Popov](mailto:valentin@popov.link)
## Source Code License
## Comments
<img width="256px" alt="MIT License" src="https://raw.githubusercontent.com/valentineus/valentineus.github.io/master/assets/images/7d05cad0-d553-42c7-be1f-7007926ba720.png" />
Comments on the site are powered by [giscus.app](https://giscus.app) and stored in [GitHub Discussions](https://github.com/valentineus/popov.link/discussions).
[MIT](LICENSE.txt). Copyright (c) [Valentin Popov](https://valentineus.link/).
## License
This project is licensed under the [MIT License](LICENSE.txt).

View File

@ -1,26 +1,19 @@
import { defineConfig } from "astro/config";
import { remarkReadingTime } from "./src/plugins/remarkReadingTime";
import cloudflare from "@astrojs/cloudflare";
import icon from "astro-icon";
import ogImages from "./src/integrations/ogImages";
import sitemap from "@astrojs/sitemap";
export default defineConfig({
site: "https://popov.link",
output: "hybrid",
adapter: cloudflare({
imageService: "compile",
}),
integrations: [icon(), sitemap()],
output: "static",
integrations: [sitemap(), ogImages()],
build: {
inlineStylesheets: "always",
},
markdown: {
remarkPlugins: [remarkReadingTime],
},
redirects: {
"/blog": {
destination: "/",
status: 301,
shikiConfig: {
theme: "vitesse-dark",
},
},
});

6857
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,17 @@
{
"name": "website",
"type": "module",
"version": "0.0.1",
"version": "2025.01.24",
"private": true,
"packageManager": "npm@11.4.2",
"browserslist": [
">0.2%",
"not dead",
"IE 11"
],
"scripts": {
"format": "prettier --write .",
"typecheck": "tsc --noEmit",
"dev": "astro dev",
"start": "astro dev",
"check": "astro check",
@ -12,24 +20,29 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.9.3",
"@astrojs/cloudflare": "^11.1.0",
"@astrojs/rss": "^4.0.7",
"@astrojs/sitemap": "^3.1.6",
"@iconify-json/hugeicons": "^1.2.0",
"astro": "^4.15.10",
"astro-icon": "^1.1.1",
"autoprefixer": "^10.4.20",
"cssnano": "^7.0.6",
"cssnano-preset-advanced": "^7.0.6",
"@astrojs/check": "^0.9.4",
"@astrojs/rss": "^4.0.12",
"@astrojs/sitemap": "^3.4.1",
"@resvg/resvg-js": "^2.6.2",
"astro": "^5.9.0",
"autoprefixer": "^10.4.21",
"cssnano": "^7.0.7",
"cssnano-preset-advanced": "^7.0.7",
"dayjs": "^1.11.13",
"geist": "^1.4.2",
"globby": "^14.1.0",
"gray-matter": "^4.0.3",
"mdast-util-to-string": "^4.0.0",
"reading-time": "^1.5.0",
"sass": "^1.79.4",
"typescript": "^5.6.2"
"sass": "^1.89.1",
"satori": "^0.15.2",
"satori-html": "^0.3.2",
"schema-dts": "^1.1.5",
"sharp": "^0.34.2",
"typescript": "^5"
},
"devDependencies": {
"prettier": "^3.3.3",
"prettier": "^3.5.3",
"prettier-plugin-astro": "^0.14.1"
}
}

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

BIN
public/images/photo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

2
public/images/preview/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

24
public/manifest.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "Valentin Popov's Blog",
"short_name": "popov.link",
"icons": [
{
"src": "favicon.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "apple-touch-icon.png",
"sizes": "180x180",
"type": "image/png"
},
{
"src": "google-touch-icon.png",
"sizes": "512x512",
"type": "image/png"
}
],
"background_color": "#ffffff",
"theme_color": "#ffffff",
"display": "fullscreen"
}

View File

@ -1,6 +0,0 @@
module.exports = {
endpoint: "https://code.popov.link",
gitAuthor: "RenovateBot <renovatebot@noreply.localhost>",
optimizeForDisabled: true,
platform: "gitea",
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,17 +1 @@
---
type Props = {
readonly title: string;
};
const path = Astro.url.pathname;
const { title } = Astro.props;
---
<!-- AppMetrix -->
<script is:inline src="https://appmetrix.com/pixel/T5X0z12SoASBV8Dv"></script>
<!-- GoatCounter -->
<script is:inline data-goatcounter="https://analytics.popov.link/count" src="//gc.zgo.at/count.js"></script>
<noscript>
<img alt="pixel" src={`https://analytics.popov.link/count?p=${encodeURI(path)}&t=${encodeURI(title)}`} />
</noscript>
<script is:inline defer src="https://appmetrix.com/pixel/T5X0z12SoASBV8Dv"></script>

View File

@ -15,6 +15,7 @@ const theme = "transparent_dark";
<script
is:inline
defer
src="https://giscus.app/client.js"
data-category-id={categoryId}
data-category={category}

View File

@ -1,60 +0,0 @@
---
import { Icon } from "astro-icon/components";
import dayjs from "dayjs";
---
<style lang="scss">
@import "../scss/_variables.scss";
footer {
bottom: 0;
height: 4em;
left: 0;
padding: 1em 4em;
position: absolute;
right: 0;
}
a {
color: $colorText;
&:visited {
color: $colorText;
}
}
.left-nav,
.right-nav {
display: inline-block;
vertical-align: top;
width: 49%;
}
.left-nav {
float: left;
font-size: $fontSizeBase * 0.75;
text-align: left;
}
.right-nav {
float: right;
text-align: right;
}
@media (width <=684px) {
footer {
padding: 4em 1em 2em;
}
}
</style>
<footer>
<div class="left-nav">
<span>&copy; {dayjs().year()} <a href="mailto:valentin@popov.link">Valentin Popov</a></span>
</div>
<div class="right-nav">
<a href="https://github.com/valentineus" target="_blank"><Icon name="hugeicons:github-01" /></a>
<a href="https://analytics.popov.link" target="_blank"><Icon name="hugeicons:analytics-up" /></a>
<a href="/feed.xml" target="_blank"><Icon name="hugeicons:rss" /></a>
</div>
</footer>

View File

@ -1,14 +1,22 @@
---
import type { WithContext, Thing } from "schema-dts";
import JsonLd from "./JsonLd.astro";
type Props = {
readonly description: string;
readonly preview: string;
readonly schema: WithContext<Thing>;
readonly title: string;
};
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
const { description, title } = Astro.props;
const { description, preview, schema, title } = Astro.props;
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
const previewUrl = new URL(preview, Astro.site);
---
<head>
<!-- Meta Tags -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
@ -18,7 +26,29 @@ const { description, title } = Astro.props;
<link href="/feed.xml" rel="alternate" title="RSS" type="application/atom+xml" />
<link href="/sitemap-index.xml" rel="sitemap" />
<link href={canonicalURL} rel="canonical" />
<link href={canonicalUrl} rel="canonical" />
<title>{title}</title>
<!-- Icons -->
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#ffffff" />
<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={previewUrl} />
<meta property="og:url" content={canonicalUrl} />
<!-- Twitter Cards -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={previewUrl} />
<JsonLd schema={schema} />
</head>

View File

@ -1,64 +1,16 @@
---
import { Icon } from "astro-icon/components";
const { pathname } = Astro.url;
---
<style lang="scss">
@import "../scss/_variables.scss";
[data-icon] {
vertical-align: middle;
}
header {
background-color: $colorBg;
left: 0;
line-height: 3.5em;
opacity: 0.975;
position: absolute;
right: 0;
top: 0;
}
nav {
margin: auto;
max-width: 52em;
padding: 0 4em;
}
a {
color: $colorText;
margin-right: 1.5rem;
&:visited {
color: $colorText;
}
}
@media (width <=684px) {
header {
position: fixed;
text-align: center;
&:last-child {
margin-right: 0;
}
}
</style>
<header>
<nav>
<a href="/">
{
pathname !== "/" ? (
<>
<Icon name="hugeicons:arrow-left-01" />
<span>Home</span>
</>
) : (
<>
<Icon name="hugeicons:source-code" />
<span>{import.meta.env.DEFAULT_TITLE}</span>
</>
)
}
</a>
<nav aria-label="Navigation">
<a href="/" lang="en" aria-label="Home">Home</a>
<a href="/blog/" lang="en" aria-label="Blog">Blog</a>
</nav>
</header>

View File

@ -0,0 +1,20 @@
<style lang="scss">
@use "../../scss/variables" as *;
a {
color: $colorText;
display: inline-block;
margin: 0 0.5rem;
}
svg {
vertical-align: middle;
}
</style>
<a href="mailto:valentin@popov.link" title="E-Mail" target="_blank">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-label="E-Mail" aria-hidden="true">
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
<polyline points="22,6 12,13 2,6"></polyline>
</svg>
</a>

View File

@ -0,0 +1,22 @@
<style lang="scss">
@use "../../scss/variables" as *;
a {
color: $colorText;
display: inline-block;
margin: 0 0.5rem;
}
svg {
vertical-align: middle;
}
</style>
<a href="https://github.com/valentineus" title="GitHub" target="_blank">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-label="GitHub" aria-hidden="true">
<path
d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"
>
</path>
</svg>
</a>

View File

@ -0,0 +1,21 @@
<style lang="scss">
@use "../../scss/variables" as *;
a {
color: $colorText;
display: inline-block;
margin: 0 0.5rem;
}
svg {
vertical-align: middle;
}
</style>
<a href="https://www.linkedin.com/in/valentineus/" title="LinkedIn" target="_blank">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-label="LinkedIn" aria-hidden="true">
<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"></path>
<rect x="2" y="9" width="4" height="12"></rect>
<circle cx="4" cy="4" r="2"></circle>
</svg>
</a>

View File

@ -0,0 +1,17 @@
<style lang="scss">
a {
display: inline-block;
}
svg {
vertical-align: middle;
}
</style>
<a href="/feed.xml" title="RSS Feed" target="_blank">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-label="RSS Feed" aria-hidden="true">
<path d="M4 11a9 9 0 0 1 9 9"></path>
<path d="M4 4a16 16 0 0 1 16 16"></path>
<circle cx="5" cy="19" r="1"></circle>
</svg>
</a>

View File

@ -0,0 +1,13 @@
---
import type { WithContext, Thing } from "schema-dts";
type Props = {
readonly schema: WithContext<Thing>;
};
const { schema } = Astro.props;
const json = JSON.stringify(schema);
---
<!-- JSON-LD -->
<script is:inline type="application/ld+json" set:html={json} />

View File

@ -1,35 +0,0 @@
---
type Props = {
readonly nextUrl?: string;
readonly prevUrl?: string;
};
const { nextUrl, prevUrl } = Astro.props;
---
<style lang="scss">
div {
text-align: center;
}
span {
margin: 0 2em;
}
</style>
<div>
{
prevUrl && (
<span>
<a href={prevUrl}>&lt; Prev</a>
</span>
)
}
{
nextUrl && (
<span>
<a href={nextUrl}>Next &gt;</a>
</span>
)
}
</div>

View File

@ -0,0 +1,40 @@
---
import { type CollectionEntry } from "astro:content";
import dayjs from "dayjs";
type Props = {
readonly post: CollectionEntry<"blog">;
};
const { post } = Astro.props;
const { remarkPluginFrontmatter } = await post.render();
const formattedDate = dayjs(post.data.datePublished.toString()).format("MMMM DD, YYYY");
const datePublished = post.data.datePublished.toISOString();
---
<style lang="scss">
@use "../scss/variables" as *;
a {
color: $colorText;
}
small {
font-size: $fontSizeBase * 0.75;
opacity: 0.5;
}
</style>
<li>
<article>
<a href={`/blog/${post.slug}`} lang={post.data.lang}>{post.data.title}</a>
<div>
<small>
<time datetime={datePublished} lang="en">{formattedDate}</time>
<span>•</span>
<span>{remarkPluginFrontmatter.minutesRead}</span>
</small>
</div>
</article>
</li>

View File

@ -1,49 +0,0 @@
---
import { type CollectionEntry } from "astro:content";
import dayjs from "dayjs";
type Props = {
readonly post: CollectionEntry<"blog">;
};
const { post } = Astro.props;
const { remarkPluginFrontmatter } = await post.render();
const formattedDate = dayjs(post.data.pubDate.toString()).format("MMMM DD, YYYY");
---
<style lang="scss">
@import "../scss/_variables.scss";
a {
color: $colorText;
display: block;
padding-bottom: 3rem;
&:visited {
color: $colorText;
}
}
h2 {
color: $colorBlossom;
font-size: 1.25em;
margin: 0.5em 0;
}
div {
font-size: $fontSizeBase * 0.75;
opacity: 0.5;
}
</style>
<a href={`/blog/${post.slug}`}>
<article>
<div>
<time datetime={post.data.pubDate.toISOString()}>{formattedDate}</time>
<span>•</span>
<span>{remarkPluginFrontmatter.minutesRead}</span>
</div>
<h2>{post.data.title}</h2>
<p>{post.data.description}</p>
</article>
</a>

View File

@ -0,0 +1,43 @@
---
import { getCollection } from "astro:content";
import dayjs from "dayjs";
import RSSIcon from "../Icons/RSS.astro";
const posts = await getCollection("blog", ({ data }) => {
return data.draft !== true;
});
posts.sort((a, b) => b.data.datePublished.getTime() - a.data.datePublished.getTime());
const latestPosts = posts.slice(0, 5);
---
<style lang="scss">
@use "../../scss/variables" as *;
small {
font-size: $fontSizeBase * 0.75;
opacity: 0.5;
}
</style>
<section>
<h2>Latest posts <RSSIcon /></h2>
<ul>
{
latestPosts.map((post) => (
<li>
<a href={`/blog/${post.slug}`} lang={post.data.lang}>
{post.data.title}
</a>
<small>
<time datetime={post.data.datePublished.toISOString()} lang="en">
{dayjs(post.data.datePublished.toString()).format("MMMM DD, YYYY")}
</time>
</small>
</li>
))
}
</ul>
</section>

View File

@ -0,0 +1,19 @@
---
import GitHubIcon from "../Icons/GitHub.astro";
import LinkedInIcon from "../Icons/LinkedIn.astro";
import EmailIcon from "../Icons/Email.astro";
---
<style lang="scss">
div {
margin-bottom: 2rem;
}
</style>
<section>
<div>
<GitHubIcon />
<LinkedInIcon />
<EmailIcon />
</div>
</section>

View File

@ -0,0 +1,7 @@
<section>
<div>
<h1>Hi, I'm Valentin 👋</h1>
<p>I'm a professional software developer currently working as a project manager and team lead. On my personal website, I share thoughts on tech, leadership, and digital life.</p>
<p>Welcome, and feel free to explore!</p>
</div>
</section>

29
src/config.ts Normal file
View File

@ -0,0 +1,29 @@
export const config = {
author: {
name: "Valentin Popov",
email: "valentin@popov.link",
url: "https://popov.link/",
sameAs: ["https://www.linkedin.com/in/valentineus/", "https://github.com/valentineus"],
},
// Open Graph
og: {
color: {
bg: "#181818",
bgCode: "#3b3d42",
blossom: "#6da13f",
text: "#dee2e6",
},
defaultPreview: "/images/photo.png",
dimensions: {
height: 630,
width: 1200,
},
fonts: {
bold: "./src/assets/JetBrainsMono/JetBrainsMono-Bold.ttf",
regular: "./src/assets/JetBrainsMono/JetBrainsMono-Regular.ttf",
},
photo: "./public/images/photo.png",
website: "popov.link",
},
};

View File

@ -1,8 +1,10 @@
---
basedOn: "https://adrianhenke.wordpress.com/2008/12/05/create-lib-file-from-dll/"
title: 'Create ".lib" file from ".dll" (archive)'
author: "Adrian Henke"
pubDate: "2023-05-04"
description: "Learn how to generate a *.lib file from a *.dll with this comprehensive guide. Using the Visual Studio Command Prompt and Microsoft's recommended tools, this article walks you through the steps for a seamless process. Perfect for developers working with 3rd party win dll's."
datePublished: "2023-05-04"
dateModified: "2023-05-04"
lang: "en"
---
> This's a copy of a non-my post. The original article [is here](https://adrianhenke.wordpress.com/2008/12/05/create-lib-file-from-dll/) ([archive](https://web.archive.org/web/20161118122539/https://adrianhenke.wordpress.com/2008/12/05/create-lib-file-from-dll/)).

View File

@ -1,8 +1,9 @@
---
title: "Горячая перезагрузка ElectronJS приложения"
author: "Valentin Popov"
pubDate: "2019-08-15"
description: "Руководство по автоматической перезагрузке приложений на Electron с помощью пакетов electron-reload и electron-webpack. Обход проблем с совместимостью и использование HMR для renderer процесса."
datePublished: "2019-08-15"
dateModified: "2019-08-15"
lang: "ru"
---
## Main процесс

View File

@ -1,8 +1,9 @@
---
title: "Example Content"
author: "Example User"
pubDate: "2018-01-01"
description: "Howdy! This is an example blog post that shows several types of HTML content supported in this theme."
datePublished: "2018-01-01"
dateModified: "2018-01-01"
lang: "en"
draft: true
---
@ -16,12 +17,12 @@ Etiam porta **sem malesuada magna** mollis euismod. Cras mattis consectetur puru
HTML defines a long list of available inline tags, a complete list of which can be found on the [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/HTML/Element).
- **To bold text**, use `<strong>`.
- _To italicize text_, use `<em>`.
- Abbreviations, like <abbr title="HyperText Markup Langage">HTML</abbr> should use `<abbr>`, with an optional `title` attribute for the full phrase.
- Citations, like <cite>&mdash; Mark otto</cite>, should use `<cite>`.
- <del>Deleted</del> text should use `<del>` and <ins>inserted</ins> text should use `<ins>`.
- Superscript <sup>text</sup> uses `<sup>` and subscript <sub>text</sub> uses `<sub>`.
- **To bold text**, use `<strong>`.
- _To italicize text_, use `<em>`.
- Abbreviations, like <abbr title="HyperText Markup Langage">HTML</abbr> should use `<abbr>`, with an optional `title` attribute for the full phrase.
- Citations, like <cite>&mdash; Mark otto</cite>, should use `<cite>`.
- <del>Deleted</del> text should use `<del>` and <ins>inserted</ins> text should use `<ins>`.
- Superscript <sup>text</sup> uses `<sup>` and subscript <sub>text</sub> uses `<sub>`.
Most of these elements are styled by browsers with few modifications on our part.
@ -58,9 +59,9 @@ Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. N
Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.
- Praesent commodo cursus magna, vel scelerisque nisl consectetur et.
- Donec id elit non mi porta gravida at eget metus.
- Nulla vitae elit libero, a pharetra augue.
- Praesent commodo cursus magna, vel scelerisque nisl consectetur et.
- Donec id elit non mi porta gravida at eget metus.
- Nulla vitae elit libero, a pharetra augue.
Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.

View File

@ -1,8 +1,9 @@
---
title: 'Получение исходного кода "Chromium Projects"'
author: "Valentin Popov"
pubDate: "2012-01-30"
description: "Изучение исходных кодов Chromium: подготовка системы и установка необходимых программных компонентов. Руководство для начинающих разработчиков. Получите инструкции по установке Microsoft Visual Studio, Cygwin, Python и других инструментов. Действительно на январь-февраль 2012 года."
datePublished: "2012-01-30"
dateModified: "2012-01-30"
lang: "ru"
---
> Перенос [оригинальной статьи](https://adeptus-mechanicus.blogspot.com/2012/01/chromium-projects.html) 2012 года из моего [старого блога](https://adeptus-mechanicus.blogspot.com/) ([зеркало](https://web.archive.org/web/20160217052148/http://adeptus-mechanicus.blogspot.com/)).
@ -15,26 +16,26 @@ description: "Изучение исходных кодов Chromium: подго
Для начала немного введения. Весь процесс от подготовки системы, до работы с исходными кодами я условно разделю на четыре пункта, это:
- Подготовка операционной системы, установка стандартных программных комплектов разработчика;
- Установка и настройка программы "Cygwin";
- Установка и настройка пакета "depot_tools";
- Получение и подготовка для работы исходных кодов "Chromium";
- Подготовка операционной системы, установка стандартных программных комплектов разработчика;
- Установка и настройка программы "Cygwin";
- Установка и настройка пакета "depot_tools";
- Получение и подготовка для работы исходных кодов "Chromium";
## Подготовка начального набора программ
Ниже приведу список и краткое описание программного обеспечения, которое требуется установить перед работой с исходными кодами:
- "**Microsoft Visual Studio 2010**" — Среда разработки, требуется для работы с исходным кодом. Имеется возможность использовать Microsoft Visual Studio 2008 и Microsoft Visual C++ Express 2010'го и 2008'го годов соответственно. Бесплатные Express версии продуктов можно взять на [официальном сайте](http://www.microsoft.com/express) Microsoft.
- "**Microsoft Visual Studio 2010**" — Среда разработки, требуется для работы с исходным кодом. Имеется возможность использовать Microsoft Visual Studio 2008 и Microsoft Visual C++ Express 2010'го и 2008'го годов соответственно. Бесплатные Express версии продуктов можно взять на [официальном сайте](http://www.microsoft.com/express) Microsoft.
- "**Microsoft Windows SDK**" — Пакет для предоставления заголовочных файлов, библиотек, компиляторов и пр. для разработчиков программного обеспечения под операционную систему Windows. Требуется для успешной сборки и компиляции проекта под Windows систему. Бесплатно можно скачать с [официальной страницы](https://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b).
- "**Microsoft Windows SDK**" — Пакет для предоставления заголовочных файлов, библиотек, компиляторов и пр. для разработчиков программного обеспечения под операционную систему Windows. Требуется для успешной сборки и компиляции проекта под Windows систему. Бесплатно можно скачать с [официальной страницы](https://www.microsoft.com/downloads/en/details.aspx?FamilyID=6b6c21d2-2006-4afa-9702-529fa782d63b).
- "**Microsoft DirectX SDK**" — Пакет с библиотеками мультимедийной подсистемы DirectX. Требуется для успешной сборки и компиляции проекта. Бесплатно доступен на [официальном сайте](http://msdn.microsoft.com/en-us/directx/default.aspx).
- "**Microsoft DirectX SDK**" — Пакет с библиотеками мультимедийной подсистемы DirectX. Требуется для успешной сборки и компиляции проекта. Бесплатно доступен на [официальном сайте](http://msdn.microsoft.com/en-us/directx/default.aspx).
- "**Python 2.x**" — Высокоуровневый язык программирования. Требуется для начальной подготовки исходных кодов. Установка не обязательная, но желательная. Про третью версию Python официальной информации нет, у меня установлены обе версии для опытов и "Path" системы направлен на вторую версию. Установку Python коротко разберу ниже. Python бесплатно можно взять на [официальном сайте](http://python.org/).
- "**Python 2.x**" — Высокоуровневый язык программирования. Требуется для начальной подготовки исходных кодов. Установка не обязательная, но желательная. Про третью версию Python официальной информации нет, у меня установлены обе версии для опытов и "Path" системы направлен на вторую версию. Установку Python коротко разберу ниже. Python бесплатно можно взять на [официальном сайте](http://python.org/).
- "**Cygwin**" — Unix-подобная среда и интерфейс командной строки для систем Microsoft Windows. Требуется для работы с исходными кодами, их подготовки, обновлением и проверки на ошибки. Сам инструмент бесплатно доступен на [официальной странице](http://www.cygwin.com/). Его установку я распишу в следующих пунктах.
- "**Cygwin**" — Unix-подобная среда и интерфейс командной строки для систем Microsoft Windows. Требуется для работы с исходными кодами, их подготовки, обновлением и проверки на ошибки. Сам инструмент бесплатно доступен на [официальной странице](http://www.cygwin.com/). Его установку я распишу в следующих пунктах.
- "**TortoiseSVN**" — Клиент Subversion под систему Windows. Установка не обязательная, но желательная. Требуется для более простого обновления пакета "depot_tools" из официального репозитория SVN от корпорации Google. Бесплатно доступен на [официальной странице](http://tortoisesvn.net/).
- "**TortoiseSVN**" — Клиент Subversion под систему Windows. Установка не обязательная, но желательная. Требуется для более простого обновления пакета "depot_tools" из официального репозитория SVN от корпорации Google. Бесплатно доступен на [официальной странице](http://tortoisesvn.net/).
Над установкой Microsoft Visual Studio, Microsoft Windows SDK и Microsoft DirectX SDK я подробно останавливаться не буду. Установка данных программных комплектов проста, и особой сложности вызывать не должна. В случае возникновения каких-либо проблем, имеются огромные сообщества разработчиков, которые помогут с установкой.
@ -64,8 +65,8 @@ python --version
На диске `C:\` я создал каталог `OpenSource`, в нём я создал каталог `ChromiumProjects`, в котором появилось два подкаталога `depot_tools` и `trunk`. Т.е. система каталогов выглядит так:
- `C:\OpenSource\ChromiumProjects\depot_tools` — Пакет для работы с исходным кодом;
- `C:\OpenSource\ChromiumProjects\trunk` — Место хранения исходных кодов;
- `C:\OpenSource\ChromiumProjects\depot_tools` — Пакет для работы с исходным кодом;
- `C:\OpenSource\ChromiumProjects\trunk` — Место хранения исходных кодов;
В случае использование других каталогов в вашей системе, используйте собственные пути, подставляя их в мои примеры.
@ -77,31 +78,31 @@ python --version
Первым делом получите установочный файл последней версии по этой [ссылке](http://cygwin.com/setup.exe). Установочный файл во время установки скачивает необходимые пакеты из интернета. Когда запустите файл, следуйте инструкциям. После выбор зеркала пакетов, Вам покажут список доступных пакетов с зеркала. Требуется найти и отметить для установки следующие пакеты:
- `apache`;
- `bc`;
- `bison`;
- `curl`;
- `diffutils`;
- `e2fsprogs`;
- `emacs`;
- `flex`;
- `gcc`;
- `gperf`;
- `keychain`;
- `make`;
- `nano`;
- `openssh`;
- `patch`;
- `perl`;
- `perl-libwin32`;
- `python`;
- `rebase`;
- `rsync`;
- `ruby`;
- `subversion`;
- `unzip`;
- `vim`;
- `zip`;
- `apache`;
- `bc`;
- `bison`;
- `curl`;
- `diffutils`;
- `e2fsprogs`;
- `emacs`;
- `flex`;
- `gcc`;
- `gperf`;
- `keychain`;
- `make`;
- `nano`;
- `openssh`;
- `patch`;
- `perl`;
- `perl-libwin32`;
- `python`;
- `rebase`;
- `rsync`;
- `ruby`;
- `subversion`;
- `unzip`;
- `vim`;
- `zip`;
Имена пакетов должны полностью совпадать. Воспользуйтесь поиском по пакетам, включённым в саму программу установки. Это должно сильно облегчить задачу.
@ -177,9 +178,9 @@ gclient sync
## Полезные ссылки
- [http://dev.chromium.org/Home](http://dev.chromium.org/Home) — Официальная страница проекта "The Chromium Projects";
- [http://dev.chromium.org/developers/how-tos/build-instructions-windows](http://dev.chromium.org/developers/how-tos/build-instructions-windows) — Официальная страница по подготовке операционной системы Windows, перед работой с исходными кодами;
- [http://dev.chromium.org/developers/how-tos/get-the-code](http://dev.chromium.org/developers/how-tos/get-the-code) — Официальная инструкция по получению, настройке и подготовке исходных кодов проекта "The Chromium Projects";
- [http://dev.chromium.org/developers/how-tos/install-depot-tools](http://dev.chromium.org/developers/how-tos/install-depot-tools) — Официальная инструкция по установке и настройке пакета "depot_tools";
- [http://dev.chromium.org/developers/how-tos/cygwin](http://dev.chromium.org/developers/how-tos/cygwin) — Страница по установки и настройке терминала "Cygwin";
- [http://groups.google.com/a/chromium.org/group/chromium-discuss/topics](http://groups.google.com/a/chromium.org/group/chromium-discuss/topics) — Официальная дискуссия разработчиков браузера "Chromium";
- [http://dev.chromium.org/Home](http://dev.chromium.org/Home) — Официальная страница проекта "The Chromium Projects";
- [http://dev.chromium.org/developers/how-tos/build-instructions-windows](http://dev.chromium.org/developers/how-tos/build-instructions-windows) — Официальная страница по подготовке операционной системы Windows, перед работой с исходными кодами;
- [http://dev.chromium.org/developers/how-tos/get-the-code](http://dev.chromium.org/developers/how-tos/get-the-code) — Официальная инструкция по получению, настройке и подготовке исходных кодов проекта "The Chromium Projects";
- [http://dev.chromium.org/developers/how-tos/install-depot-tools](http://dev.chromium.org/developers/how-tos/install-depot-tools) — Официальная инструкция по установке и настройке пакета "depot_tools";
- [http://dev.chromium.org/developers/how-tos/cygwin](http://dev.chromium.org/developers/how-tos/cygwin) — Страница по установки и настройке терминала "Cygwin";
- [http://groups.google.com/a/chromium.org/group/chromium-discuss/topics](http://groups.google.com/a/chromium.org/group/chromium-discuss/topics) — Официальная дискуссия разработчиков браузера "Chromium";

View File

@ -1,15 +1,16 @@
---
title: "Установка Moodle в Fedora"
author: "Valentin Popov"
pubDate: "2018-07-23"
description: "Решение проблем установки Moodle из-за SELinux: как настроить правила доступа для устранения ошибок в веб-интерфейсе и при работе с cURL. Практические советы и команды."
datePublished: "2018-07-23"
dateModified: "2018-07-23"
lang: "ru"
---
Во время установки Moodle, сталкиваешься со следующими проблемами:
- Веб-интерфейс не продолжает установку после настройки базы данных;
- Если установить через консольный интерфейс, проявляются артефакты;
- Нет доступа к сети, появляется ошибка `unexpected cURL error`.
- Веб-интерфейс не продолжает установку после настройки базы данных;
- Если установить через консольный интерфейс, проявляются артефакты;
- Нет доступа к сети, появляется ошибка `unexpected cURL error`.
Главная причина, это [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux). Решение, это настроить правила доступа:

View File

@ -1,20 +1,21 @@
---
title: "Компиляция Rust на TL-MR3020"
author: "Valentin Popov"
pubDate: "2023-05-01"
description: 'Как настроить и оптимизировать проект Rust для кросс-компиляции на TP-Link TL-MR3020 с использованием Fedora Linux 38 и OpenWrt 22.03.4. Шаг за шагом от базового "Hello, World!" до асинхронного TCP сервера.'
datePublished: "2023-05-01"
dateModified: "2023-05-01"
lang: "ru"
---
Информация в статье актуальна для дистрибутива [Fedora Linux 38](https://docs.fedoraproject.org/en-US/releases/f38/), прошивки [OpenWrt 22.03.4](https://openwrt.org/releases/22.03/notes-22.03.4) и устройства [TP-Link TL-MR3020](https://www.tp-link.com/en/home-networking/3g-4g-router/tl-mr3020/) ревизии v3.20.
Потребуется:
- Установленный [rustup](https://rustup.rs/) инструментарий.
- Установленный пакет [cross-rs](https://github.com/cross-rs/cross) для кросс-компиляции.
- Упаковщик исполняемых файлов [upx](https://github.com/upx/upx).
- Контейнеризатор [Docker](https://docs.docker.com/engine/install/) (рекомендуется) или [Podman](https://podman.io/getting-started/installation).
- SSH подключение к маршрутизатору.
- Установленный [SFTP сервер](https://openwrt.org/docs/guide-user/services/nas/sftp.server) на TL-MR3020.
- Установленный [rustup](https://rustup.rs/) инструментарий.
- Установленный пакет [cross-rs](https://github.com/cross-rs/cross) для кросс-компиляции.
- Упаковщик исполняемых файлов [upx](https://github.com/upx/upx).
- Контейнеризатор [Docker](https://docs.docker.com/engine/install/) (рекомендуется) или [Podman](https://podman.io/getting-started/installation).
- SSH подключение к маршрутизатору.
- Установленный [SFTP сервер](https://openwrt.org/docs/guide-user/services/nas/sftp.server) на TL-MR3020.
> Требуется rustup инструментарий с официального сайта. Rust и Cargo из репозитория дистрибутива не подойдут. Пакет кросс-компиляции требует rustup, который в репозиториях дистрибутива отсутствует.
@ -166,7 +167,7 @@ curl -L "http://10.0.0.2:3000"
## Полезные ссылки и источники
- [Building Rust code for my OpenWrt Wi-Fi router](https://blog.dend.ro/building-rust-for-routers/)
- [Cross Compile Rust For OpenWRT](https://www.kiloleaf.com/posts/cross-compile-rust-for-openwrt/)
- [Minimizing Rust Binary Size](https://github.com/johnthagen/min-sized-rust)
- [Кросс-компиляция программ Rust для запуска на маршрутизаторе](https://dzen.ru/media/nuancesprog.ru/krosskompiliaciia-programm-rust-dlia-zapuska-na-marshrutizatore-5f6457b8bdfa745d402cd1ec)
- [Building Rust code for my OpenWrt Wi-Fi router](https://blog.dend.ro/building-rust-for-routers/)
- [Cross Compile Rust For OpenWRT](https://www.kiloleaf.com/posts/cross-compile-rust-for-openwrt/)
- [Minimizing Rust Binary Size](https://github.com/johnthagen/min-sized-rust)
- [Кросс-компиляция программ Rust для запуска на маршрутизаторе](https://dzen.ru/media/nuancesprog.ru/krosskompiliaciia-programm-rust-dlia-zapuska-na-marshrutizatore-5f6457b8bdfa745d402cd1ec)

View File

@ -3,10 +3,12 @@ import { defineCollection, z } from "astro:content";
const blog = defineCollection({
type: "content",
schema: z.object({
author: z.string(),
basedOn: z.optional(z.string()),
dateModified: z.coerce.date(),
datePublished: z.coerce.date(),
description: z.string(),
draft: z.optional(z.boolean()),
pubDate: z.coerce.date(),
lang: z.string(),
title: z.string(),
}),
});

View File

@ -0,0 +1,47 @@
import type { AstroIntegration } from "astro";
import { createOgImage } from "../utils/createOgImage";
import { globby } from "globby";
import fs from "fs/promises";
import matter from "gray-matter";
import path from "path";
const postsDir = path.resolve("./src/content/blog");
const outDir = path.resolve("./public/images/preview");
export default function ogImageGenerator(): AstroIntegration {
return {
name: "og-images",
hooks: {
"astro:build:setup": async ({ logger }) => {
await fs.mkdir(outDir, { recursive: true });
const mdFiles = await globby("*.md", { cwd: postsDir });
logger.info(`${mdFiles.length} posts found`);
const results = await Promise.allSettled(
mdFiles.map(async (file) => {
const slug = file.replace(/\.md$/, "");
const content = await fs.readFile(path.join(postsDir, file), "utf-8");
const { data } = matter(content);
const png = await createOgImage(data.title, data.datePublished);
const outPath = path.join(outDir, `${slug}.png`);
await fs.writeFile(outPath, png);
logger.info(`OG image created: ${slug}`);
})
);
results.forEach((r) => {
if (r.status === "rejected") {
logger.error(`Error for ${r.reason.slug}: ${r.reason.message}`);
}
});
const failures = results.filter((r) => r.status === "rejected");
if (failures.length) {
throw new Error(`Failed to generate OG images for ${failures.length} posts`);
}
},
},
};
}

View File

@ -1,30 +1,32 @@
---
import type { WithContext, Thing } from "schema-dts";
import Analytics from "../components/Analytics.astro";
import Footer from "../components/Footer.astro";
import Head from "../components/Head.astro";
import Header from "../components/Header.astro";
import "../scss/global.scss";
type Props = {
readonly description?: string;
readonly title?: string;
readonly description: string;
readonly lang: string;
readonly preview: string;
readonly schema: WithContext<Thing>;
readonly title: string;
};
const { description, title } = Astro.props;
const { description, lang, preview, schema, title } = Astro.props;
---
<html lang="ru">
<Head
description={description ?? import.meta.env.DEFAULT_DESCRIPTION}
title={title ?? import.meta.env.DEFAULT_TITLE}
/>
<html lang={lang}>
<Head title={title} description={description} preview={preview} schema={schema} />
<body>
<Header />
<main>
<section>
<Header />
</section>
<slot />
</main>
<Footer />
<Analytics title={title ?? import.meta.env.DEFAULT_TITLE} />
<Analytics />
</body>
</html>

View File

@ -1,17 +1,30 @@
---
import { config } from "../config";
import Layout from "../layouts/BaseLayout.astro";
import pageSchema from "../utils/schemas/pageSchema";
const title = "404 — Page Not Found | Valentin Popov";
const description = "The page you're looking for doesn't exist!";
const preview = config.og.defaultPreview;
const lang = "en";
const schema = pageSchema({
siteUrl: new URL("/", Astro.site).toString(),
page: "/404",
title,
description,
lang,
});
---
<Layout>
<div style="text-align:center;">
<Layout title={title} description={description} preview={preview} lang={lang} schema={schema}>
<div style={{ "text-align": "center" }}>
<h1>404</h1>
<p><strong>Page not found</strong></p>
<p>
<small>
If you see this message, please
<a href=`mailto:valentin@popov.link?subject=${encodeURIComponent('I found a broken page')}`>
let me know
</a>
<a href=`mailto:valentin@popov.link?subject=${encodeURIComponent('I found a broken page')}`>let me know</a>
</small>
</p>
</div>

View File

@ -1,33 +0,0 @@
---
import type { GetStaticPaths, InferGetStaticPropsType } from "astro";
import { getCollection } from "astro:content";
import Layout from "../layouts/BaseLayout.astro";
import Pagination from "../components/Pagination.astro";
import PostSummary from "../components/PostSummary.astro";
type Props = InferGetStaticPropsType<typeof getStaticPaths>;
export const getStaticPaths = (async ({ paginate }) => {
const posts = await getCollection("blog", ({ data }) => {
return data.draft !== true;
});
posts.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
return paginate(posts, {
pageSize: 10,
});
}) satisfies GetStaticPaths;
const { page } = Astro.props;
---
<Layout>
<section style={{ "margin-top": "3rem" }}>
{page.data.map((post) => <PostSummary post={post} />)}
</section>
<section>
<Pagination nextUrl={page.url.next} prevUrl={page.url.prev} />
</section>
</Layout>

View File

@ -2,6 +2,7 @@
import { type CollectionEntry, getCollection } from "astro:content";
import Comments from "../../components/Comments.astro";
import Layout from "../../layouts/BaseLayout.astro";
import blogPostSchema from "../../utils/schemas/blogPostSchema";
import dayjs from "dayjs";
type Props = CollectionEntry<"blog">;
@ -18,28 +19,58 @@ export async function getStaticPaths() {
}
const post = Astro.props;
const { Content } = await post.render();
const formattedDate = dayjs(post.data.pubDate.toString()).format("MMMM DD, YYYY");
const { Content, remarkPluginFrontmatter } = await post.render();
const description = post.data.description;
const isBasedOn = post.data.basedOn;
const lang = post.data.lang;
const preview = `/images/preview/${post.slug}.png`;
const slug = post.slug;
const title = post.data.title;
const dateModified = post.data.dateModified?.toISOString();
const datePublished = post.data.datePublished.toISOString();
const formattedDate = dayjs(post.data.datePublished.toString()).format("MMMM DD, YYYY");
const schema = blogPostSchema({
siteUrl: new URL("/", Astro.site).toString(),
dateModified,
datePublished,
description,
isBasedOn,
lang,
preview,
slug,
title,
});
---
<Layout description={post.data.description} title={post.data.title}>
<style lang="scss">
@use "../../scss/variables" as *;
p {
opacity: 0.5;
}
</style>
<Layout title={title} description={description} preview={preview} lang={lang} schema={schema}>
<article>
<section>
<h1>{post.data.title}</h1>
</section>
<header>
<h1>{title}</h1>
<section>
<Content />
</section>
<section>
<p>
<small>
Posted
<time datetime={post.data.pubDate.toISOString()}>{formattedDate}</time>
by&nbsp;{post.data.author}
<time datetime={datePublished} lang="en">{formattedDate}</time>
<span>&nbsp;•&nbsp;</span>
<span>{remarkPluginFrontmatter.minutesRead}</span>
</small>
</p>
</header>
<section>
<Content />
</section>
<section>

View File

@ -0,0 +1,61 @@
---
import type { CollectionEntry } from "astro:content";
import { config } from "../../config";
import { getCollection } from "astro:content";
import blogSchema from "../../utils/schemas/blogSchema";
import Layout from "../../layouts/BaseLayout.astro";
import PostElement from "../../components/PostElement.astro";
import RSSIcon from "../../components/Icons/RSS.astro";
const posts = await getCollection("blog", ({ data }) => {
return data.draft !== true;
});
posts.sort((a, b) => b.data.datePublished.getTime() - a.data.datePublished.getTime());
const postsByYear = posts.reduce<Record<string, CollectionEntry<"blog">[]>>((acc, post) => {
const year = post.data.datePublished.getFullYear().toString();
if (!acc[year]) {
acc[year] = [];
}
acc[year].push(post);
return acc;
}, {});
const years = Object.keys(postsByYear).sort((a, b) => Number(b) - Number(a));
const title = "Valentin Popov's Blog | Software Development, Leadership & Open-Source";
const description = "Explore Valentin Popov's blog on software development, tech leadership, and open-source experiments. Stay updated with in-depth tutorials and expert insights.";
const preview = config.og.defaultPreview;
const lang = "en";
const schema = blogSchema({
siteUrl: new URL("/", Astro.site).toString(),
title,
posts,
});
---
<Layout title={title} description={description} preview={preview} lang={lang} schema={schema}>
<section>
<h1>
Blog posts
<RSSIcon />
</h1>
</section>
<section>
{
years.map((year) => (
<div>
<h2>{year}</h2>
<ul>
{postsByYear[year].map((post) => (
<PostElement post={post} />
))}
</ul>
</div>
))
}
</section>
</Layout>

View File

@ -2,13 +2,16 @@ import { getCollection } from "astro:content";
import rss from "@astrojs/rss";
export async function GET(context) {
const title = "RSS Feed | Valentin Popov Blog";
const description = "Follow the latest posts from Valentin Popov via RSS.";
const posts = await getCollection("blog", ({ data }) => {
return data.draft !== true;
});
return rss({
customData: `<language>ru-ru</language>`,
description: import.meta.env.DEFAULT_DESCRIPTION,
customData: `<language>en</language>`,
description: description,
items: posts.map((post) => ({
customData: post.data.customData,
description: post.data.description,
@ -17,6 +20,6 @@ export async function GET(context) {
title: post.data.title,
})),
site: context.site,
title: import.meta.env.DEFAULT_TITLE,
title: title,
});
}

27
src/pages/index.astro Normal file
View File

@ -0,0 +1,27 @@
---
import { config } from "../config";
import LatestPostsSection from "../components/Sections/LatestPosts.astro";
import Layout from "../layouts/BaseLayout.astro";
import pageSchema from "../utils/schemas/pageSchema";
import SocialLinksSection from "../components/Sections/SocialLinks.astro";
import WelcomeSection from "../components/Sections/Welcome.astro";
const title = "Valentin Popov Software Developer & Team Lead | Tech Insights";
const description = "Blog by Valentin Popov — software developer and team lead writing about code, side projects, digital tools, and fun experiments.";
const preview = config.og.defaultPreview;
const lang = "en";
const schema = pageSchema({
siteUrl: new URL("/", Astro.site).toString(),
page: "/",
title,
description,
lang,
});
---
<Layout title={title} description={description} preview={preview} lang={lang} schema={schema}>
<WelcomeSection />
<SocialLinksSection />
<LatestPostsSection />
</Layout>

View File

@ -1,3 +1,5 @@
@use "variables" as *;
*,
*::after,
*::before {
@ -35,7 +37,7 @@ body {
margin: auto;
max-width: 52em;
min-height: 100vh;
padding: 4em;
padding: 2em 4em;
position: relative;
text-rendering: optimizelegibility;
}
@ -43,7 +45,7 @@ body {
@media (width <=684px) {
body {
font-size: $fontSizeBase * 0.85;
padding: 4em 1em 2em;
padding: 2em 1em;
}
}

View File

@ -1,5 +1,7 @@
@media print {
body {
background: #fff;
color: #000;
padding: 0;
}
@ -18,4 +20,25 @@
img {
max-width: 500px;
}
* {
background: none;
border-color: #000;
color: #000;
}
a {
color: #000;
text-decoration: underline;
}
table {
border: 1px solid #000;
}
th,
td {
background: none;
border: 1px solid #000;
}
}

View File

@ -1,5 +1,5 @@
$colorBg: #212529;
$colorBgAlt: hwb(0deg 0% 100% / 20%);
$colorBg: #181818;
$colorBgAlt: rgba(0, 0, 0, 0.2);
$colorBgCode: #3b3d42;
$colorBlossom: #6da13f;
$colorFade: #598332;

View File

@ -1,3 +1,3 @@
@import "variables";
@import "framework";
@import "print";
@use "variables";
@use "framework";
@use "print";

View File

@ -0,0 +1,52 @@
import { config } from "../config";
import { html } from "satori-html";
import { resources } from "./ogResources";
import { Resvg } from "@resvg/resvg-js";
import dayjs from "dayjs";
import satori from "satori";
export async function createOgImage(title: string, datePublished: Date): Promise<Buffer> {
const formattedDate = dayjs(datePublished).format("MMMM DD, YYYY");
const markup = await satori(
html(`
<div tw="flex flex-col w-full h-full" style="background-color: ${config.og.color.bg}">
<div tw="flex flex-col w-full h-4/5 p-10 justify-center">
<div tw="text-2xl mb-6" style="color: ${config.og.color.text}">${formattedDate}</div>
<div tw="flex text-6xl w-full font-bold" style="color: ${config.og.color.text}">${title}</div>
</div>
<div tw="w-full h-1/5 flex p-10 items-center justify-between text-2xl" style="border-top: 1px solid ${config.og.color.bgCode}">
<div tw="flex items-center">
<span tw="ml-3" style="color: ${config.og.color.text}">${config.og.website.toLocaleUpperCase()}</span>
</div>
<div tw="flex items-center">
<img src="${resources.photoBase64}" tw="w-15 h-15 rounded-full" />
<div tw="flex flex-col ml-4">
<span style="color: ${config.og.color.text}">${config.author.name}</span>
<span style="color: ${config.og.color.blossom}">${config.author.email}</span>
</div>
</div>
</div>
</div>
`),
{
width: config.og.dimensions.width,
height: config.og.dimensions.height,
fonts: [
{
name: "Inter",
data: resources.fonts.regular,
weight: 400,
},
{
name: "Inter",
data: resources.fonts.bold,
weight: 700,
},
],
}
);
const image = new Resvg(markup, { fitTo: { mode: "width", value: config.og.dimensions.width } });
return image.render().asPng();
}

15
src/utils/ogResources.ts Normal file
View File

@ -0,0 +1,15 @@
import { config } from "../config";
import fs from "fs/promises";
import path from "path";
import sharp from "sharp";
export const resources = {
fonts: {
regular: await fs.readFile(path.resolve(config.og.fonts.regular)),
bold: await fs.readFile(path.resolve(config.og.fonts.bold)),
},
photoBase64: await (async () => {
const buf = await fs.readFile(path.resolve(config.og.photo));
return "data:image/png;base64," + (await sharp(buf).resize(120, 120).png({ quality: 95 }).toBuffer()).toString("base64");
})(),
};

View File

@ -0,0 +1,37 @@
import type { WithContext, BlogPosting } from "schema-dts";
import { config } from "../../config";
export type BlogPostSchemaParams = {
readonly dateModified: string;
readonly datePublished: string;
readonly description: string;
readonly isBasedOn?: string;
readonly lang: string;
readonly preview: string;
readonly siteUrl: string;
readonly slug: string;
readonly title: string;
};
export default ({ siteUrl, slug, title, description, preview, datePublished, dateModified, lang, isBasedOn }: BlogPostSchemaParams): WithContext<BlogPosting> => ({
"@context": "https://schema.org",
"@type": "BlogPosting",
"url": new URL(`/blog/${slug}`, siteUrl).toString(),
"headline": title,
"description": description,
"image": new URL(preview, siteUrl).toString(),
"datePublished": datePublished,
"dateModified": dateModified,
"inLanguage": lang,
"author": {
"@type": "Person",
"name": config.author.name,
"url": config.author.url,
"sameAs": config.author.sameAs,
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": new URL(`/blog/${slug}`, siteUrl).toString(),
},
...(isBasedOn && { isBasedOn: isBasedOn }),
});

View File

@ -0,0 +1,26 @@
import type { WithContext, CollectionPage } from "schema-dts";
import type { CollectionEntry } from "astro:content";
export type BlogSchemaParams = {
readonly posts: CollectionEntry<"blog">[];
readonly siteUrl: string;
readonly title: string;
};
export default ({ siteUrl, title, posts }: BlogSchemaParams): WithContext<CollectionPage> => ({
"@context": "https://schema.org",
"@type": "CollectionPage",
"url": new URL("/blog/", siteUrl).toString(),
"name": title,
"mainEntity": {
"@type": "ItemList",
"itemListOrder": "https://schema.org/ItemListOrderDescending",
"numberOfItems": posts.length,
"itemListElement": posts.map((post, index) => ({
"@type": "ListItem",
"position": index + 1,
"url": new URL(`/blog/${post.slug}`, siteUrl).toString(),
"name": post.data.title,
})),
},
});

View File

@ -0,0 +1,23 @@
import type { WithContext, WebPage } from "schema-dts";
export type WebsiteSchemaParams = {
readonly description: string;
readonly page: string;
readonly siteUrl: string;
readonly title: string;
readonly lang: string;
};
export default ({ siteUrl, page, title, description, lang }: WebsiteSchemaParams): WithContext<WebPage> => ({
"@context": "https://schema.org",
"@type": "WebPage",
"@id": new URL(page, siteUrl).toString(),
"url": new URL(page, siteUrl).toString(),
"name": title,
"description": description,
"inLanguage": lang,
"mainEntity": {
"@type": "WebSite",
"@id": new URL("/", siteUrl).toString(),
},
});