Compare commits

..

No commits in common. "dev" and "dev-upgrade-v6.5.5" have entirely different histories.

48 changed files with 316 additions and 1166 deletions

View File

@ -1 +0,0 @@
{}

View File

@ -1 +0,0 @@
{}

View File

@ -1 +0,0 @@
{"taxonomy":{"tags":["comment-system","hello","principles","privacy","remark42","self-hosted","test","world"],"categories":[]}}

View File

@ -42,7 +42,7 @@ jobs:
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.3
ruby-version: 3.2
bundler-cache: true
- name: Build site
@ -53,7 +53,7 @@ jobs:
- name: Test site
run: |
bundle exec htmlproofer _site \
\-\-disable-external \
\-\-disable-external=true \
\-\-ignore-urls "/^http:\/\/127.0.0.1/,/^http:\/\/0.0.0.0/,/^http:\/\/localhost/"
- name: Upload site artifact

6
.gitignore vendored
View File

@ -5,7 +5,6 @@ Gemfile.lock
# Jekyll cache
.jekyll-cache
.jekyll-metadata
_site
# RubyGems
@ -17,11 +16,6 @@ package-lock.json
# IDE configurations
.idea
.vscode/*
!.vscode/settings.json
!.vscode/extensions.json
!.vscode/tasks.json
# Misc
_sass/vendors
assets/js/dist

15
.vscode/settings.json vendored
View File

@ -1,15 +0,0 @@
{
"[markdown]": {
"editor.fontSize": 14,
"editor.lineHeight": 26,
"editor.wordWrap": "wordWrapColumn",
"editor.wordWrapColumn": 64,
"editor.lineNumbers": "off",
"editor.quickSuggestions": {
"comments": "off",
"strings": "off",
"other": "off"
},
"editor.minimap.enabled": false
}
}

26
.vscode/tasks.json vendored
View File

@ -1,26 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Run Jekyll Server",
"type": "shell",
"command": "./tools/run.sh",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [],
"detail": "Runs the Jekyll server with live reload."
},
{
"label": "Build Jekyll Site",
"type": "shell",
"command": "./tools/test.sh",
"group": {
"kind": "build"
},
"problemMatcher": [],
"detail": "Build the Jekyll site for production."
}
]
}

View File

@ -2,10 +2,10 @@
source "https://rubygems.org"
gem "jekyll-theme-chirpy", "~> 7.2", ">= 7.2.2"
gem "jekyll-theme-chirpy", "~> 6.2", ">= 6.2.2"
group :test do
gem "html-proofer", "~> 5.0", group: :test
gem "html-proofer", "~> 4.4"
end
# Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem
@ -16,7 +16,7 @@ platforms :mingw, :x64_mingw, :mswin, :jruby do
end
# Performance-booster for watching directories on Windows
gem "wdm", "~> 0.2.0", :platforms => [:mingw, :x64_mingw, :mswin]
gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
# do not have a Java counterpart.

View File

@ -1,43 +1,81 @@
# Chirpy Starter
<div align="center">
[![Gem Version](https://img.shields.io/gem/v/jekyll-theme-chirpy)][gem]&nbsp;
[![GitHub license](https://img.shields.io/github/license/cotes2020/chirpy-starter.svg?color=blue)][mit]
# Chirpy Jekyll Theme
When installing the [**Chirpy**][chirpy] theme through [RubyGems.org][gem], Jekyll can only read files in the folders
`_data`, `_layouts`, `_includes`, `_sass` and `assets`, as well as a small part of options of the `_config.yml` file
from the theme's gem. If you have ever installed this theme gem, you can use the command
`bundle info --path jekyll-theme-chirpy` to locate these files.
A minimal, responsive, and feature-rich Jekyll theme for technical writing.
The Jekyll team claims that this is to leave the ball in the users court, but this also results in users not being
able to enjoy the out-of-the-box experience when using feature-rich themes.
[![Gem Version](https://img.shields.io/gem/v/jekyll-theme-chirpy?color=brightgreen)][gem]&nbsp;
[![CI](https://github.com/cotes2020/jekyll-theme-chirpy/actions/workflows/ci.yml/badge.svg?branch=master&event=push)][ci]&nbsp;
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/4e556876a3c54d5e8f2d2857c4f43894)][codacy]&nbsp;
[![GitHub license](https://img.shields.io/github/license/cotes2020/jekyll-theme-chirpy.svg)][license]&nbsp;
[![996.icu](https://img.shields.io/badge/link-996.icu-%23FF4D5B.svg)](https://996.icu)
To fully use all the features of **Chirpy**, you need to copy the other critical files from the theme's gem to your
Jekyll site. The following is a list of targets:
[**Live Demo** →][demo]
```shell
.
├── _config.yml
├── _plugins
├── _tabs
└── index.html
```
[![Devices Mockup](https://chirpy-img.netlify.app/commons/devices-mockup.png)][demo]
To save you time, and also in case you lose some files while copying, we extract those files/configurations of the
latest version of the **Chirpy** theme and the [CD][CD] workflow to here, so that you can start writing in minutes.
</div>
## Usage
## Features
Check out the [theme's docs](https://github.com/cotes2020/jekyll-theme-chirpy/wiki).
- Dark / Light Theme Mode
- Localized UI language
- Pinned Posts on Home Page
- Hierarchical Categories
- Trending Tags
- Table of Contents
- Last Modified Date
- Syntax Highlighting
- Mathematical Expressions
- Mermaid Diagrams & Flowcharts
- Dark / Light Mode Images
- Embed Videos
- Disqus / Giscus / Utterances Comments
- Built-in Search
- Atom Feeds
- PWA
- Google Analytics / GoatCounter
- SEO & Performance Optimization
## Documentation
To learn how to use, develop, and upgrade the project, please refer to the [Wiki][wiki].
## Contributing
This repository is automatically updated with new releases from the theme repository. If you encounter any issues or want to contribute to its improvement, please visit the [theme repository][chirpy] to provide feedback.
Contributions (_pull requests_, _issues_, and _discussions_) are what make the open-source community such an amazing place
to learn, inspire, and create. Any contributions you make are greatly appreciated.
For details, see the "[Contributing Guidelines][contribute-guide]".
## Credits
### Contributors
Thanks to [all the contributors][contributors] involved in the development of the project!
[![all-contributors](https://contrib.rocks/image?repo=cotes2020/jekyll-theme-chirpy&columns=16)][contributors]
<sub> —— Made with [contrib.rocks](https://contrib.rocks)</sub>
### Third-Party Assets
This project is built on the [Jekyll][jekyllrb] ecosystem and some [great libraries][lib], and is developed using [VS Code][vscode] as well as tools provided by [JetBrains][jetbrains] under a non-commercial open-source software license.
The avatar and favicon for the project's website are from [ClipartMAX][clipartmax].
## License
This work is published under [MIT][mit] License.
This project is published under [MIT License][license].
[gem]: https://rubygems.org/gems/jekyll-theme-chirpy
[chirpy]: https://github.com/cotes2020/jekyll-theme-chirpy/
[CD]: https://en.wikipedia.org/wiki/Continuous_deployment
[mit]: https://github.com/cotes2020/chirpy-starter/blob/master/LICENSE
[ci]: https://github.com/cotes2020/jekyll-theme-chirpy/actions/workflows/ci.yml?query=event%3Apush+branch%3Amaster
[codacy]: https://app.codacy.com/gh/cotes2020/jekyll-theme-chirpy/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade
[license]: https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/LICENSE
[jekyllrb]: https://jekyllrb.com/
[clipartmax]: https://www.clipartmax.com/middle/m2i8b1m2K9Z5m2K9_ant-clipart-childrens-ant-cute/
[demo]: https://cotes2020.github.io/chirpy-demo/
[wiki]: https://github.com/cotes2020/jekyll-theme-chirpy/wiki
[contribute-guide]: https://github.com/cotes2020/jekyll-theme-chirpy/blob/master/docs/CONTRIBUTING.md
[contributors]: https://github.com/cotes2020/jekyll-theme-chirpy/graphs/contributors
[lib]: https://github.com/cotes2020/chirpy-static-assets
[vscode]: https://code.visualstudio.com/
[jetbrains]: https://www.jetbrains.com/?from=jekyll-theme-chirpy

19
_config-prod.yml Normal file
View File

@ -0,0 +1,19 @@
# fill in the protocol & hostname for your site, e.g., 'https://username.github.io'
id: 'blog'
url: "https://bangararaju.kottedi.in"
show_drafts: false
future: false
comments:
active: 'remark42' # The global switch for posts comments, e.g., 'disqus'. Keep it empty means disable
# The active options are as follows:
# remark42 settings > https://remark42.com/
remark42:
host: 'https://remark42.kottedi.in'
# Custom configuration
custom-config:
blog-services:
service_url: 'https://blog-svc.kottedi.in'

12
_config-uat.yml Normal file
View File

@ -0,0 +1,12 @@
# fill in the protocol & hostname for your site, e.g., 'https://username.github.io'
id: 'blog-uat'
url: "https://bangararaju-uat.kottedi.in"
show_drafts: true
future: true
# Custom configuration
custom-config:
blog-services:
service_url: 'https://blog-svc-uat.kottedi.in'

View File

@ -15,22 +15,22 @@ timezone: Asia/Kolkata
# jekyll-seo-tag settings https://github.com/jekyll/jekyll-seo-tag/blob/master/docs/usage.md
# ↓ --------------------------
title: Engineer's Odyssey # the main title
title: Bangara Raju # the main title
tagline: Your Hub for Tech, DIY, and Innovation # it will display as the subtitle
tagline: Only Blogging Nothing More or Less # it will display as the sub-title
description: >- # used by seo meta and the atom feed
Through my articles and stories, I hope to guide you through the intricate world of technology, offer insights into automation's transformative power, unveil the secrets behind successful DIY projects, and encourage you to fearlessly pursue new skills.
# Fill in the protocol & hostname for your site.
# E.g. 'https://username.github.io', note that it does not end with a '/'.
# e.g. 'https://username.github.io', note that it does not end with a '/'.
url: "http://localhost:4000"
github:
username: rajukottedi # change to your GitHub username
username: rajukottedi # change to your github username
twitter:
username: RajuKottedi # change to your Twitter username
username: RajuKottedi # change to your twitter username
social:
# Change to your full name.
@ -39,44 +39,22 @@ social:
email: bangararaju.kottedi@gmail.com # change to your email address
links:
# The first element serves as the copyright owner's link
- https://github.com/rajukottedi # change to your GitHub homepage
- https://twitter.com/rajukottedi # change to your Twitter homepage
- https://github.com/rajukottedi # change to your github homepage
- https://twitter.com/rajukottedi # change to your twitter homepage
# Uncomment below to add more social links
- https://www.facebook.com/RajuKottedi435
- https://www.linkedin.com/in/bangararajuk/
- https://www.linkedin.com/in/RajuKottedi
# Site Verification Settings
webmaster_verifications:
google: # fill in your Google verification code
bing: # fill in your Bing verification code
alexa: # fill in your Alexa verification code
yandex: # fill in your Yandex verification code
baidu: # fill in your Baidu verification code
facebook: # fill in your Facebook verification code
google_site_verification: # fill in to your verification string
# ↑ --------------------------
# The end of `jekyll-seo-tag` settings
# Web Analytics Settings
analytics:
google:
id: # fill in your Google Analytics ID
goatcounter:
id: # fill in your GoatCounter ID
umami:
id: # fill in your Umami ID
domain: # fill in your Umami domain
matomo:
id: # fill in your Matomo ID
domain: # fill in your Matomo domain
cloudflare:
id: # fill in your Cloudflare Web Analytics token
fathom:
id: # fill in your Fathom Site ID
google_analytics:
id: G-W2VG43SN3N # fill in your Google Analytics ID
# Page Views settings
pageviews:
provider: # now only supports 'goatcounter'
goatcounter:
id: # fill in your Goatcounter ID
# Prefer color scheme setting.
#
@ -91,12 +69,12 @@ pageviews:
#
theme_mode: # [light | dark]
# The CDN endpoint for media resources.
# The CDN endpoint for images.
# Notice that once it is assigned, the CDN url
# will be added to all media resources (site avatar, posts' images, audio and video files) paths starting with '/'
# will be added to all image (site avatar & posts' images) paths starting with '/'
#
# e.g. 'https://cdn.com'
cdn:
img_cdn:
# the avatar on sidebar, support local or CORS resources
avatar: /assets/img/avatar.jpg
@ -109,8 +87,7 @@ social_preview_image: # string, local or CORS resources
toc: true
comments:
# Global switch for the post comment system. Keeping it empty means disabled.
provider: 'remark42' # [disqus | utterances | giscus]
active: 'remark42' # The global switch for posts comments, e.g., 'disqus'. Keep it empty means disable
# The active options are as follows:
# remark42 settings > https://remark42.com/
remark42:
@ -128,7 +105,6 @@ comments:
category:
category_id:
mapping: # optional, default to 'pathname'
strict: # optional, default to '0'
input_position: # optional, default to 'bottom'
lang: # optional, default to the value of `site.lang`
reactions_enabled: # optional, default to the value of `1`
@ -142,14 +118,13 @@ assets:
env: # [development | production]
pwa:
enabled: true # The option for PWA feature (installable)
enabled: true # the option for PWA feature (installable)
cache:
enabled: true # the option for PWA offline cache
# Paths defined here will be excluded from the PWA cache.
# Usually its value is the `baseurl` of another website that
# shares the same domain name as the current website.
deny_paths:
- "/api"
# - "/example" # URLs match `<SITE_URL>/example/*` will not be cached by the PWA
paginate: 10
@ -160,7 +135,6 @@ baseurl: "/blog"
# ------------ The following options are not recommended to be modified ------------------
kramdown:
footnote_backlink: "&#8617;&#xfe0e;"
syntax_highlighter: rouge
syntax_highlighter_opts: # Rouge Options https://github.com/jneen/rouge#full-options
css_class: highlight
@ -221,11 +195,8 @@ exclude:
- tools
- README.md
- LICENSE
- purgecss.js
- rollup.config.js
- "package*.json"
- db_scripts
- frontmatter.json
- package*.json
jekyll-archives:
enabled: [categories, tags]
@ -243,5 +214,4 @@ future: true
# Custom configuration
custom-config:
blog-services:
service_url: 'https://localhost:7013/blog/api/v1/posts'
service_key: 'c6eAXYcNT873TT7BfMgQyS4ii7hxa53TLEUN7pAGaaU='
service_url: 'http://localhost:5000'

View File

@ -5,7 +5,7 @@
- type: linkedin
icon: 'fab fa-linkedin' # icons powered by <https://fontawesome.com/>
url: 'https://www.linkedin.com/in/bangararajuk/' # Fill with your Linkedin homepage
url: 'https://in.linkedin.com/in/bangara-raju-kottedi-299072109' # Fill with your Linkedin homepage
# - type: twitter
# icon: "fa-brands fa-x-twitter"
@ -26,15 +26,3 @@
# - type: stack-overflow
# icon: 'fab fa-stack-overflow'
# url: '' # Fill with your stackoverflow homepage
#
# - type: bluesky
# icon: 'fa-brands fa-bluesky'
# url: '' # Fill with your Bluesky profile link
#
# - type: reddit
# icon: 'fa-brands fa-reddit'
# url: '' # Fill with your Reddit profile link
#
# - type: threads
# icon: 'fa-brands fa-threads'
# url: '' # Fill with your Threads profile link

View File

@ -22,7 +22,7 @@ platforms:
#
# - type: Weibo
# icon: "fab fa-weibo"
# link: "https://service.weibo.com/share/share.php?title=TITLE&url=URL"
# link: "http://service.weibo.com/share/share.php?title=TITLE&url=URL"
#
# - type: Mastodon
# icon: "fa-brands fa-mastodon"
@ -36,15 +36,3 @@ platforms:
# link: "https://fosstodon.org/"
# - label: photog.social
# link: "https://photog.social/"
#
# - type: Bluesky
# icon: "fa-brands fa-bluesky"
# link: "https://bsky.app/intent/compose?text=TITLE%20URL"
#
# - type: Reddit
# icon: "fa-brands fa-square-reddit"
# link: "https://www.reddit.com/submit?url=URL&title=TITLE"
#
# - type: Threads
# icon: "fa-brands fa-square-threads"
# link: "https://www.threads.net/intent/post?text=TITLE%20URL"

View File

@ -0,0 +1,8 @@
---
title: Future Post
date: 2023-07-23 0:00:00 +0530
tags: [test, self-hosted]
---
# Future Post Modified

View File

@ -0,0 +1,25 @@
title: This is the Quiz Title
randomized: false
questions:
- type: "multiple-choice"
question: "What is your favorite color?"
items:
- choice: Red
correct: null
- choice: Blue
correct: null
- choice: Green
correct: null
followup: There is no correct answer to asking your favorite color! All choices would be good.
- type: "multiple-choice"
question: "True or False, Pittsburgh is West of Philadelphia"
items:
- choice: True
correct: true
- choice: False
correct: false
followup: |
The answer is True! Pittsburgh is 304.9 miles West of Philadelphia, or approximately
a car ride of 4 hours and 52 minutes. Buckle up!

View File

@ -1,8 +0,0 @@
---
title: Anatomy of a CSRF Attack
date: 2024-07-08 0:00:00 +5:30
tags:
- csrf
- security
slug: anatomy-csrf-attack
---

View File

@ -1,28 +0,0 @@
---
title: "Part 1: Getting Started with Python"
description: Kick off your Python journey! Learn what Python is, set up your environment, and write your first program. Perfect for beginners—lets start coding!
date: 2025-02-02T16:59:46.763Z
tags: []
categories: []
image:
path: /assets/img/posts/python-getting-started.png
disable-ty: false
series: "Python Practical Guide: A Beginner's Journey"
slug: part-1-started-python
---
## What is Python?
Python is a versatile, high-level programming language known for its simplicity and readability. Its used for:
- Web development
- Data analysis and visualization
- Artificial intelligence and machine learning
- Automation and scripting
- Game development
- And much more!
## Why Learn Python?
1. **Beginner-Friendly**: Easy-to-read syntax makes it perfect for first-time coders.
2. **Versatile**: Use it for almost any type of project or industry.
3. **In-Demand Skill**: Python developers are highly sought after in the job market.
4. **Huge Community**: Tons of resources, libraries, and frameworks to help you learn and grow.
5. **Fun and Powerful**: Automate tasks, analyze data, or build apps—Python makes it easy!

View File

@ -1,12 +0,0 @@
---
title: "Part 2: Python Basics - Syntax and Fundamentals"
description: "Learn Pythons building blocks: variables, data types, input/output, and basic operations. Start writing clean, readable code today!"
date: 2025-02-02T18:04:14.853Z
tags: []
categories: []
image:
path: /assets/img/posts/python-logo.jpg
disable-ty: true
series: "Python Practical Guide: A Beginner's Journey"
slug: part-2-python-basics-syntax-fundamentals
---

View File

@ -1,5 +0,0 @@
<!-- The comments switcher -->
{% if page.comments and site.comments.provider %}
{% capture path %}comments/{{ site.comments.provider }}.html{% endcapture %}
{% include {{ path }} %}
{% endif %}

View File

@ -4,30 +4,6 @@
}
</style>
<i class="fa fa-comment" aria-hidden="true" {% if include.tooltip %}
title="{{site.data.locales[include.lang].post.comments}}" data-bs-toggle="tooltip" data-bs-placement="bottom" {%
endif %}></i>
<i class="fa fa-comment" aria-hidden="true" {% if include.tooltip %} title="{{site.data.locales[include.lang].post.comments}}"
data-bs-toggle="tooltip" data-bs-placement="bottom" {% endif %}></i>
<span class="remark42__counter"></span>
<script>
function commentTextChange() {
var target = document.querySelector('.remark42__counter');
if(!target){
window.setTimeout(commentTextChange, 500);
return;
}
var observer = new MutationObserver(function (mutations) {
const comments = document.querySelector(".remark42__counter");
var commentsCount = 0;
if(Number(comments.innerText) !== NaN){
commentsCount = Number(comments.innerText);
}
if(commentsCount !== commentsCountFromResponse){
ajax(serviceUrl + "/UpdatePostCommentsCount?blogUrl=" + siteId + "&postSlug=" + postId, "POST", commentsCount);
}
});
var config = { childList: true };
observer.observe(target, config);
}
commentTextChange();
</script>

5
_includes/comments.html Normal file
View File

@ -0,0 +1,5 @@
<!-- The comments switcher -->
{% if page.comments and site.comments.active %}
{% capture path %}comments/{{ site.comments.active }}.html{% endcapture %}
{% include {{ path }} %}
{% endif %}

View File

@ -4,11 +4,11 @@
<script>
var remark_config = {
host: remark42Host,
site_id: "{{site.id}}",
site_id: siteId,
components: ['embed', 'counter'],
max_shown_comments: 10,
theme: Theme.visualState,
theme: modeToggle.modeStatus,
locale: 'en',
show_email_subscription: false
@ -23,11 +23,11 @@
}
})(remark_config.components || ['embed']);
var targetDiv = document.getElementById("mode-toggle");
var targetDiv = document.getElementById("sidebar").getElementsByClassName("mode-toggle")[0];
if (targetDiv != null) {
targetDiv.addEventListener("click", () => {
window.REMARK42.changeTheme(Theme.visualState === 'light' ? 'dark' : 'light');
window.REMARK42.changeTheme(modeToggle.modeStatus === 'light' ? 'dark' : 'light');
})
}
</script>

View File

@ -4,7 +4,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#f7f7f7">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#1b1b1e">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport"
content="width=device-width, user-scalable=no initial-scale=1, shrink-to-fit=no, viewport-fit=cover">
@ -20,7 +20,7 @@
{% unless src contains '://' %}
{%- capture img_url -%}
{% include media-url.html src=src subpath=page.media_subpath absolute=true %}
{% include img-url.html src=src img_path=page.img_path absolute=true %}
{%- endcapture -%}
{%- capture old_url -%}{{ src | absolute_url }}{%- endcapture -%}
@ -31,7 +31,7 @@
{% elsif site.social_preview_image %}
{%- capture img_url -%}
{% include media-url.html src=site.social_preview_image absolute=true %}
{% include img-url.html src=site.social_preview_image absolute=true %}
{%- endcapture -%}
{%- capture og_image -%}
@ -59,30 +59,34 @@
{% include_cached favicons.html %}
<!-- Resource Hints -->
{% unless site.assets.self_host.enabled %}
{% for hint in site.data.origin.cors.resource_hints %}
{% for link in hint.links %}
<link rel="{{ link.rel }}" href="{{ hint.url }}" {{ link.opts | join: ' ' }}>
{% endfor %}
{% endfor %}
{% endunless %}
{% if site.resources.ignore_env != jekyll.environment and site.resources.self_hosted %}
<link href="{{ site.data.origin[type].webfonts | relative_url }}" rel="stylesheet">
{% else %}
{% for cdn in site.data.origin[type].cdns %}
<link rel="preconnect" href="{{ cdn.url }}" {{ cdn.args }}>
<link rel="dns-prefetch" href="{{ cdn.url }}" {{ cdn.args }}>
{% endfor %}
<link rel="stylesheet" href="{{ site.data.origin[type].webfonts | relative_url }}">
{% endif %}
<!-- GA -->
{% if jekyll.environment == 'production' and site.google_analytics.id != empty and site.google_analytics.id %}
<link rel="preconnect" href="https://www.google-analytics.com" crossorigin="use-credentials">
<link rel="dns-prefetch" href="https://www.google-analytics.com">
<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin="anonymous">
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
{% endif %}
<!-- Bootstrap -->
{% unless jekyll.environment == 'production' %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
{% endunless %}
<link rel="stylesheet" href="{{ site.data.origin[type].bootstrap.css | relative_url }}">
<!-- Theme style -->
<link rel="stylesheet" href="{{ '/assets/css/:THEME.css' | replace: ':THEME', site.theme | relative_url }}">
<!-- Web Font -->
<link rel="stylesheet" href="{{ site.data.origin[type].webfonts | relative_url }}">
<!-- Font Awesome Icons -->
<!-- Font Awesome -->
<link rel="stylesheet" href="{{ site.data.origin[type].fontawesome.css | relative_url }}">
<!-- 3rd-party Dependencies -->
<link rel="stylesheet" href="{{ '/assets/css/:THEME.css' | replace: ':THEME', site.theme | relative_url }}">
{% if site.toc and page.toc %}
<link rel="stylesheet" href="{{ site.data.origin[type].toc.css | relative_url }}">
@ -93,61 +97,28 @@
{% endif %}
{% if page.layout == 'page' or page.layout == 'post' %}
<!-- Image Popup -->
<link rel="stylesheet" href="{{ site.data.origin[type].glightbox.css | relative_url }}">
<!-- Manific Popup -->
<link rel="stylesheet" href="{{ site.data.origin[type].magnific-popup.css | relative_url }}">
{% endif %}
<!-- Scripts -->
<!-- JavaScript -->
<script>
var serviceUrl = "{{site.custom-config.blog-services.service_url}}";
var serviceApiKey = "{{site.custom-config.blog-services.service_key}}";
var siteId = "{{site.url}}{{site.baseurl}}";
var postId = "{{page.slug}}";
var siteId = "{{site.id}}";
var postId = "{{page.title}}";
var remark42Host = "{{ site.comments.remark42.host }}";
var postDetails = {
postUrl: "{{site.url}}{{site.baseurl}}{{page.url}}",
slug: "{{page.slug}}",
title: "{{page.title}}",
description: "{{page.description}}",
categories: [],
image: "{{page.image.path}}",
createdDate: "{{page.date}}",
modifiedDate: "{{page.last_modified_at | strip}}",
blogUrl: "{{site.url}}{{site.baseurl}}",
views: 0,
likes: 0,
comments: 0
};
function liquidArrayToJsonArray(){
var items = [];
{% for category in page.categories %}
{% assign cat = category %}
items.push("{{cat}}");
{% endfor %}
return items;
}
postDetails.categories = liquidArrayToJsonArray();
function ajax(url, type, payload) {
if (payload === undefined) {
payload = null;
function ajax(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open('GET', url);
xhr.send();
});
}
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open(type, url);
xhr.setRequestHeader('XApiKey', serviceApiKey);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('ngsw-bypass', '');
xhr.send(JSON.stringify(payload));
});
}
</script>
{% if jekyll.environment == 'production' and site.google_analytics.id != empty and site.google_analytics.id %}
<!-- Google Tag Manager -->
@ -169,28 +140,9 @@
<!-- End Microsoft Clarity -->
{% endif %}
<script src="{{ '/assets/js/dist/theme.min.js' | relative_url }}"></script>
{% include js-selector.html lang=lang %}
{% if jekyll.environment == 'production' %}
<!-- PWA -->
{% if site.pwa.enabled %}
<script
defer
src="{{ '/app.min.js' | relative_url }}?baseurl={{ site.baseurl | default: '' }}&register={{ site.pwa.cache.enabled }}"
></script>
{% endif %}
<!-- Web Analytics -->
{% for analytics in site.analytics %}
{% capture str %}{{ analytics }}{% endcapture %}
{% assign platform = str | split: '{' | first %}
{% if site.analytics[platform].id and site.analytics[platform].id != empty %}
{% include analytics/{{ platform }}.html %}
{% endif %}
{% endfor %}
{% endif %}
{% unless site.theme_mode %}
{% include mode-toggle.html %}
{% endunless %}
{% include metadata-hook.html %}
</head>

View File

@ -29,43 +29,22 @@
const views = document.getElementById("views-count");
const likes = document.getElementById("likes-count");
const postLikesAndViews = document.getElementById("likes-views");
var commentsCountFromResponse = 0;
postLikesAndViews.style.display = "none";
ajax(serviceUrl + "/GetPostLikesAndViews?blogUrl=" + siteId + "&postSlug=" + postId, "GET")
ajax(serviceUrl + "/GetPostLikesAndViews?siteId=" + siteId + "&postId=" + postId)
.then(function (result) {
if (result != "N/A") {
var response = JSON.parse(result);
if(!response.postExists){
postDetails.likes = postDetails.views = postDetails.comments = 0;
if(Number(likes.innerText) !== NaN){
postDetails.likes = Number(likes.innerText);
}
if(Number(views.innerText) !== NaN){
postDetails.views = Number(views.innerText);
}
if(postDetails.modifiedDate === ""){
postDetails.modifiedDate = postDetails.createdDate;
}
}
ajax(serviceUrl + "/CreatePost", "POST", postDetails)
.then(function (result) {
if(result != "N/A"){
var response = JSON.parse(result);
setPostMetrics(response);
}
});
views.innerText = response.postViews;
likes.innerText = response.postLikes;
postLikesAndViews.style.display = "inline";
}
});
function setPostMetrics(response){
if(response.postExists){
views.innerText = response.views;
likes.innerText = response.likes;
commentsCountFromResponse = response.comments;
postLikesAndViews.style.display = "inline";
}
}
var apiUrl = serviceUrl + "/GetMyLocation";
ajax(apiUrl)
.then(function (result) {
console.log(result);
})
</script>

View File

@ -37,17 +37,18 @@
function apiCall(url, event) {
onLoadAndClick();
ajax(url, "POST")
ajax(url)
.then(function (result) {
console.log(result);
if (event === "click") {
heartIcon.classList.toggle("liked");
}
likesCount.innerHTML = result;
heartIcon.style.pointerEvents = 'auto';
});
})
}
var apiUrl = serviceUrl + "/GetPostLikes?blogUrl=" + siteId + "&postSlug=" + postId;
var apiUrl = serviceUrl + "/GetPostLikes?siteId=" + siteId + "&postId=" + postId;
if(likesOnLoad == "{{include.onload}}"){
apiCall(apiUrl);
@ -55,11 +56,11 @@
heartIcon.addEventListener("click", () => {
if (!heartIcon.classList.contains("liked")) {
apiUrl = serviceUrl + "/LikePost?blogUrl=" + siteId + "&postSlug=" + postId;
apiUrl = serviceUrl + "/LikePost?siteId=" + siteId + "&postId=" + postId;
} else {
apiUrl = serviceUrl + "/DislikePost?blogUrl=" + siteId + "&postSlug=" + postId;
apiUrl = serviceUrl + "/DislikePost?siteId=" + siteId + "&postId=" + postId;
}
apiCall(apiUrl, "click");
})
});
</script>

View File

@ -1,28 +0,0 @@
<style>
#post-series {
margin-top: 25px;
}
.card {
padding: 10px;
}
</style>
{% if page.series %}
{% assign posts = site.posts | where: "series", page.series | sort: 'date' %}
<div id="post-series">
<div class="card">
<div>
<h3>{{ page.series }} ({{ posts | size }} - Part Series)</h3>
<ol>
{% assign posts = site.posts | where: "series", page.series | sort: "date" %}
{% for post in posts %}
<li>{% if post.url == page.url %}{{ post.title }} {% else %}
<a href="{{ site.baseurl }}{{ post.url }}">{{ post.title }}</a>{% endif %}
</li>
{% endfor %}
</ol>
</div>
</div>
</div>
{% endif %}

View File

@ -1,47 +0,0 @@
{% capture title %}{{ page.title }} - {{ site.title }}{% endcapture %}
{% assign title = title | uri_escape %}
{% assign url = page.url | absolute_url | url_encode %}
{% assign link = site.data.share.platforms[2].link | replace: 'TITLE', title | replace: 'URL', url %}
<style>
.thank-you {
border-left: 5px solid #959595;
margin: 1.5em 0;
padding: 0.5em 1em;
color: inherit;
font-style: italic;
margin-left: -20px;
font-style: italic;
}
</style>
<!-- Thank you message -->
<blockquote class="thank-you">
<p>Thank you for reading. I trust it provided value to you. Your feedback
is important, so please share your thoughts in the <a href="#remark42"><strong>comments</strong></a> below. If
you found it helpful, consider giving it a <a href="#topbar-wrapper" id="ty-like"><strong>like</strong></a> and
<a href="{{ link }}" target="_blank" rel="noopener" data-bs-toggle="tooltip" data-bs-placement="top"
aria-label="Linkedin" data-bs-original-title="Linkedin" aria-describedby="tooltip190113">
<strong>sharing</strong></a> it with others, also feel free to check out my [LinkedIn profile] - <a
href="{{ site.data.contact[1].url }}" target="_blank"><strong>Bangara Raju Kottedi</strong></a>.
</p>
</blockquote>
<script>
function handleDelayedClick() {
setTimeout(function() {
heartIcon.click();
}, 1000);
}
const tyLike = document.getElementById("ty-like");
tyLike.addEventListener("click", (event) => {
if (!heartIcon.classList.contains("liked")) {
handleDelayedClick();
}
else{
event.preventDefault();
}
});
</script>

View File

@ -1,6 +1,7 @@
<i class="fa fa-eye fa-fw" aria-hidden="true" {% if include.tooltip %} title="{{site.data.locales[include.lang].post.views}}"
data-bs-toggle="tooltip" data-bs-placement="bottom" {% endif %}></i>
<em id="views-count" style="margin-right: 12px"></em>
<em id="views-count" style="margin-right: 12px">
</em>
<script>
const viewsOnLoad = "true";

View File

@ -12,7 +12,7 @@ layout: compress
{% endif %}
<!-- `site.alt_lang` can specify a language different from the UI -->
<html lang="{{ page.lang | default: site.lang }}" {{ prefer_mode }}>
<html lang="{{ site.alt_lang | default: site.lang }}" {{ prefer_mode }}>
{% include head.html %}
<body>
@ -40,7 +40,7 @@ layout: compress
</main>
<!-- panel -->
<aside aria-label="Panel" id="panel-wrapper" class="col-xl-3 ps-2 text-muted">
<aside aria-label="Panel" id="panel-wrapper" class="col-xl-3 ps-2 mb-5 text-muted">
<div class="access">
{% include_cached update-list.html lang=lang %}
{% include_cached trending-tags.html lang=lang %}
@ -75,20 +75,21 @@ layout: compress
</aside>
</div>
<div id="mask" class="d-none position-fixed w-100 h-100 z-1"></div>
<div id="mask"></div>
{% if site.pwa.enabled %}
{% include_cached notification.html lang=lang %}
{% endif %}
<!-- Embedded scripts -->
<!-- JavaScripts -->
{% for _include in layout.script_includes %}
{% assign _include_path = _include | append: '.html' %}
{% include {{ _include_path }} %}
{% endfor %}
{% include js-selector.html %}
{% include_cached search-loader.html lang=lang %}
{% if page.mermaid %}
{% include mermaid.html %}
{% endif %}
{% include_cached search-loader.html %}
</body>
</html>

View File

@ -2,25 +2,18 @@
layout: default
refactor: true
panel_includes:
- toc
- toc
tail_includes:
- related-posts
- post-nav
- comment
script_includes:
- comment
- related-posts
- post-nav
- comments
---
{% include lang.html %}
{% include toc-status.html %}
<article class="px-1" data-toc="{{ enable_toc }}">
<article class="px-1">
<header>
<h1 data-toc-skip>{{ page.title }}</h1>
{% if page.description %}
<p class="post-desc fw-light mb-4">{{ page.description }}</p>
{% endif %}
<div class="post-meta text-muted">
<div class="d-flex justify-content-between">
@ -49,136 +42,103 @@ script_includes:
</div>
{% if page.image %}
{% capture src %}src="{{ page.image.path | default: page.image }}"{% endcapture %}
{% capture class %}class="preview-img{% if page.image.no_bg %}{{ ' no-bg' }}{% endif %}"{% endcapture %}
{% capture alt %}alt="{{ page.image.alt | xml_escape | default: "Preview Image" }}"{% endcapture %}
{% capture src %}src="{{ page.image.path | default: page.image }}"{% endcapture %}
{% capture class %}class="preview-img{% if page.image.no_bg %}{{ ' no-bg' }}{% endif %}"{% endcapture %}
{% capture alt %}alt="{{ page.image.alt | xml_escape | default: "Preview Image" }}"{% endcapture %}
{% if page.image.lqip %}
{%- capture lqip -%}lqip="{{ page.image.lqip }}"{%- endcapture -%}
{% endif %}
{% if page.image.lqip %}
{%- capture lqip -%}lqip="{{ page.image.lqip }}"{%- endcapture -%}
{% endif %}
<div class="mt-3 mb-3">
<img {{ src }} {{ class }} {{ alt }} w="1200" h="630" {{ lqip }}>
{%- if page.image.alt -%}
<figcaption class="text-center pt-2 pb-2">{{ page.image.alt }}</figcaption>
{%- endif -%}
</div>
<div class="mt-3 mb-3">
<img {{ src }} {{ class }} {{ alt }} w="1200" h="630" {{ lqip }}>
{%- if page.image.alt -%}
<figcaption class="text-center pt-2 pb-2">{{ page.image.alt }}</figcaption>
{%- endif -%}
</div>
{% endif %}
<div class="d-flex justify-content-between">
<!-- author(s) -->
<span>
{% if page.author %}
{% assign authors = page.author %}
{% assign authors = page.author %}
{% elsif page.authors %}
{% assign authors = page.authors %}
{% assign authors = page.authors %}
{% endif %}
{{ site.data.locales[lang].post.written_by }}
<em>
{% if authors %}
{% for author in authors %}
{% if site.data.authors[author].url -%}
<a href="{{ site.data.authors[author].url }}">{{ site.data.authors[author].name }}</a>
{%- else -%}
{{ site.data.authors[author].name }}
{%- endif %}
{% unless forloop.last %}{{ '</em>, <em>' }}{% endunless %}
{% endfor %}
{% for author in authors %}
{% if site.data.authors[author].url -%}
<a href="{{ site.data.authors[author].url }}">{{ site.data.authors[author].name }}</a>
{%- else -%}
{{ site.data.authors[author].name }}
{%- endif %}
{% unless forloop.last %}{{ '</em>, <em>' }}{% endunless %}
{% endfor %}
{% else %}
<a href="{{ site.social.links[0] }}">{{ site.social.name }}</a>
<a href="{{ site.social.links[0] }}">{{ site.social.name }}</a>
{% endif %}
</em>
</span>
<div>
<!-- pageviews -->
{% if site.pageviews.provider and site.analytics[site.pageviews.provider].id %}
<span>
<em id="pageviews">
<i class="fas fa-spinner fa-spin small"></i>
</em>
{{ site.data.locales[lang].post.pageview_measure }}
</span>
{% endif %}
<!-- read time -->
{% include read-time.html content=content prompt=true lang=lang %}
</div>
<!-- read time -->
{% include read-time.html content=content prompt=true lang=lang %}
</div>
<!-- .d-flex -->
</div>
<!-- .post-meta -->
{% include post-series.html %}
</header>
{% if enable_toc %}
<div id="toc-bar" class="d-flex align-items-center justify-content-between invisible">
<span class="label text-truncate">{{ page.title }}</span>
<button type="button" class="toc-trigger btn me-1">
<i class="fa-solid fa-list-ul fa-fw"></i>
</button>
</div>
<button id="toc-solo-trigger" type="button" class="toc-trigger btn btn-outline-secondary btn-sm">
<span class="label ps-2 pe-1">{{- site.data.locales[lang].panel.toc -}}</span>
<i class="fa-solid fa-angle-right fa-fw"></i>
</button>
<dialog id="toc-popup" class="p-0">
<div class="header d-flex flex-row align-items-center justify-content-between">
<div class="label text-truncate py-2 ms-4">{{- page.title -}}</div>
<button id="toc-popup-close" type="button" class="btn mx-1 my-1 opacity-75">
<i class="fas fa-close"></i>
</button>
</div>
<div id="toc-popup-content" class="px-4 py-3 pb-4"></div>
</dialog>
{% endif %}
<div class="content">
{{ content }}
{% if page.disable-ty != true %}
{% include thank-you.html %}
{% endif %}
</div>
<div class="post-tail-wrapper text-muted">
<!-- categories -->
{% if page.categories.size > 0 %}
<div class="post-meta mb-3">
<i class="far fa-folder-open fa-fw me-1"></i>
{% for category in page.categories %}
<a href="{{ site.baseurl }}/categories/{{ category | slugify | url_encode }}/">{{ category }}</a>
{%- unless forloop.last -%},{%- endunless -%}
{% endfor %}
</div>
<div class="post-meta mb-3">
<i class="far fa-folder-open fa-fw me-1"></i>
{% for category in page.categories %}
<a href="{{ site.baseurl }}/categories/{{ category | slugify | url_encode }}/">{{ category }}</a>
{%- unless forloop.last -%},{%- endunless -%}
{% endfor %}
</div>
{% endif %}
<!-- tags -->
{% if page.tags.size > 0 %}
<div class="post-tags">
<i class="fa fa-tags fa-fw me-1"></i>
{% for tag in page.tags %}
<a href="{{ site.baseurl }}/tags/{{ tag | slugify | url_encode }}/" class="post-tag no-text-decoration">
{{- tag -}}
</a>
{% endfor %}
</div>
<div class="post-tags">
<i class="fa fa-tags fa-fw me-1"></i>
{% for tag in page.tags %}
<a
href="{{ site.baseurl }}/tags/{{ tag | slugify | url_encode }}/"
class="post-tag no-text-decoration"
>
{{- tag -}}
</a>
{% endfor %}
</div>
{% endif %}
<div class="
<div
class="
post-tail-bottom
d-flex justify-content-between align-items-center mt-5 pb-2
">
"
>
<div class="license-wrapper">
{% if site.data.locales[lang].copyright.license.template %}
{% capture _replacement %}
{% capture _replacement %}
<a href="{{ site.data.locales[lang].copyright.license.link }}">
{{ site.data.locales[lang].copyright.license.name }}
</a>
{% endcapture %}
{{ site.data.locales[lang].copyright.license.template | replace: ':LICENSE_NAME', _replacement }}
{{ site.data.locales[lang].copyright.license.template | replace: ':LICENSE_NAME', _replacement }}
{% endif %}
</div>

View File

@ -1,14 +1,9 @@
---
title: Hello World
date: 2023-07-22 0:00:00 +0530
tags:
- hello world
slug: hello-world
description: Hello World
categories: []
disable-ty: true
image:
path: /assets/img/posts/hello-world.jpg
---
## Hello World
title: Hello World
date: 2023-07-22 0:00:00 +0530
tags: [hello, world]
---
# Hello World

View File

@ -1,24 +1,10 @@
---
title: Self-Hosted Commenting System
date: 2023-07-23 0:00:00 +0530
tags:
- privacy
- self-hosted
- comment-system
- remark42
categories:
- Open Source
- Services
slug: hosted-commenting-system
image:
path: /assets/img/posts/remark42.png
keywords:
- remark42
- self-hosted
- privacy
tags: [privacy, self-hosted, comment-system, remark42]
---
## Privacy-focused lightweight commenting engine
# Privacy-focused lightweight commenting engine
## [Remark 42](https://remark42.com/)

View File

@ -1,213 +0,0 @@
---
title: SOLID Principles
description: The SOLID principles are a set of design principles aimed at improving software quality, making it easier to understand, extend, and maintain.
date: 2024-04-03T16:10:57.476Z
tags:
- code quality
- oops
- principles
categories:
- Programming
- Principles
image:
path: /assets/img/posts/solid-principles.jpg
alt: Understanding SOLID Principles
slug: solid-principles
---
The SOLID principles are a set of **five design principles** that are intended to guide software development to create more **understandable**, **maintainable**, **extendable** and **scalable** code. These principles were introduced by **Robert C. Martin** (also known as Uncle Bob) in the early 2000s and have since become fundamental concepts in object-oriented design and programming. Here's a brief overview of each principle:
## 1. Single Responsibility Principle (SRP):
This principle states that a class should have **only one reason to change**. In other words, a class should have **only one responsibility or job**. By adhering to SRP, you ensure that classes are focused and have clear, understandable purposes, which makes them **easier to maintain and test**.
**Example**: *Think of a chef in a restaurant. Instead of having a chef who both cooks meals and serves customers, you'd want separate roles. The chef should focus on cooking delicious dishes, while a waiter takes care of serving customers.*
```c#
// Before
public class Chef
{
public void CookMeals() { /*...*/ }
public void ServeMeals() { /*...*/ }
}
// After
public class Chef
{
public void CookMeals() { /*...*/ }
}
public class Waiter
{
public void ServeMeals() { /*...*/ }
}
```
## 2. Open/Closed Principle (OCP):
This Principle suggests that software entities `(classes, modules, functions, etc.)` should be **open for extension** but **closed for modification**. This means that you should be able to **extend the behavior of a module without modifying its source code**. This is typically achieved through the use of **inheritance**, **polymorphism** and **parameters**.
**Example**: *Consider a shape drawing application. Instead of modifying the existing shape classes every time you need to add a new shape, you'd create a abstact class called **Shape** and implement it in different shape classes like **Circle**, **Square**, etc. Then, when you want to add a new shape, you create a new class that implements the Shape without modifying the existing code.*
```c#
// Before
public class Shape
{
public double CircleArea(double radius) { /*...*/ }
public double SquareArea(double sideLength) { /*...*/ }
}
// After
public abstract class Shape
{
public abstract double Area();
}
public class Circle : Shape
{
public override double Area() { /*...*/ }
}
public class Square : Shape
{
public override double Area() { /*...*/ }
}
```
## 3. Liskov Substitution Principle (LSP):
This Principle states that objects of a superclass should be substitutable with objects of its subclasses without affecting the correctness of the program. In simpler terms, **a subclass should behave in such a way that it does not break the functionality that the superclass expects**.
**Example**: *Consider a program that expects objects of type Bird. According to LSP, if you have a class Swan that inherits from Bird, you should be able to substitute an instance of Swan wherever you expect an Bird without breaking the program's functionality.*
```c#
// Before
public abstract class Bird
{
public abstract void Fly() { /* I can fly */}
}
public abstract class Penguin : Bird
{
// Violating LSP principle (Penguin class breaks Fly functionality)
public override void Fly()
{
throw new NotImplementedException("Penguins can't fly!");
}
}
// After
public abstract class Bird
{
public abstract void Fly() { /* I can fly */}
}
public abstract class Swan : Bird
{
public override void Fly() { /* I can fly */ }
}
```
## 4. Interface Segregation Principle (ISP)
This Principle suggests that clients should not be forced to depend on interfaces they do not use. In other words, **interfaces should be fine-grained and specific to the client's needs**. This involves breaking large interfaces into smaller, more focused interfaces.
**Example**: *Lets consider **IPerson** interface which has methods to work and eat. **Robot** class cannot implement IPerson interface as it cannot eat. IPerson should be splitted in to smaller interfaces like **IEater** and **IWorker** so Robot can implement IWorker.*
```c#
// Before
public interface IPerson
{
void Work();
void Eat();
}
public class Robot : IPerson
{
public void Work() { /*...*/ }
public void Eat() { /*...*/ } // Doesn't make sense for a robot
}
// After
public interface IWorker
{
void Work();
}
public interface IEater
{
void Eat();
}
public class Robot : IWorker
{
public void Work() { /*...*/ }
}
public class Human : IEater, IWorker
{
public void Work() { /*...*/ }
public void Eat() { /*...*/ }
}
```
## 5. Dependency Inversion Principle (DIP)
This Principle states that high-level modules should not depend on low-level modules. Instead, **both should depend on abstractions**. This principle encourages the use of interfaces or abstract classes to decouple classes from their concrete implementations. Abstractions should not depend on details. Details should depend on abstractions.
**Example**: *If UserService directly depends on the concrete implementation of MySQLDatabase. This violates DIP since the high-level class UserService is directly dependent on a low-level class.
**If we want to switch to a different database system (e.g., PostgreSQL), we need to modify the UserService class**.
Instead of depending on concrete implementations, the high-level class UserService should depend on abstractions. Let's create a Database interface as an abstraction:*
```c#
// Before
/* Low-level module */
class MySQLDatabase {
getUserData(id: number): string {
// Logic to fetch user data from MySQL database
}
}
/* High-level module */
class UserService {
private database: MySQLDatabase;
constructor() {
this.database = new MySQLDatabase();
}
getUser(id: number): string {
return this.database.getUserData(id);
}
}
// After
/* Abstract interface (abstraction) for the low-level module */
interface Database {
getUserData(id: number): string;
}
/* low-level module implementing the Database interface */
class MySQLDatabase implements Database {
getUserData(id: number): string {}
}
/* low-level module implementing the Database interface */
class PostgreSQLDatabase implements Database {
getUserData(id: number): string {}
}
/* High-level module */
class UserService {
private database: Database;
constructor(database: Database) {
this.database = database;
}
getUser(id: number): string {
return this.database.getUserData(id);
}
}
```
This way, the UserService class depends on the Database abstraction, not on concrete implementations, fulfilling the Dependency Inversion Principle.
**Note:** I'm excited to share this post that's like a treasure chest filled with nuggets of wisdom from different articles I've come across. Some of the examples taken from these articles [1](https://dev.to/galwaycoder/the-solid-principles-in-software-design-explained-53n) [2](https://dev.to/lukeskw/solid-principles-theyre-rock-solid-for-good-reason-31hn).

View File

@ -1,143 +0,0 @@
---
title: "Keep Your Linux System Secure: A Beginners Guide to UFW"
description: UFW is a tool that helps you decide which connections can or can't access your Linux system. It's simple enough for beginners but also has features for experts.
date: 2025-01-01T17:48:18.319Z
tags:
- firewall
- linux
- security
- ufw
categories:
- Linux
- Firewall
image:
path: /assets/img/posts/ufw-logo.jpg
alt: A Beginner's Guide to UFW (Uncomplicated Firewall)
slug: ufw-guide
---
UFW (Uncomplicated Firewall) is a powerful yet user-friendly tool that allows you to control which connections can access your Linux system. Designed with simplicity in mind, its perfect for beginners while still offering advanced features for experienced users. In this post, well explore how UFW works, how to use it effectively, and why its an essential tool for securing your system.
When it comes to securing your Linux server or desktop, managing network access is one of the most critical tasks. Firewalls serve as the first line of defense, and while tools like iptables offer granular control, they can be intimidating for beginners. Thats where UFW (Uncomplicated Firewall) steps in.
UFW is a user-friendly interface for managing iptables, designed to simplify the process of configuring a firewall. Whether youre a seasoned Linux administrator or a newcomer, UFW makes securing your system straightforward and efficient.
## Why Use UFW?
1. **Ease of Use:** UFW abstracts the complexities of iptables, offering simple commands to configure firewall rules.
2. **Default Settings:** UFW ships with sensible defaults, such as denying all incoming connections while allowing outgoing ones.
3. **Integration:** It integrates well with many linux distributions and is often installed by default.
4. **Script-Friendly:** UFW is ideal for automation and scripting, making it perfect for managing servers at scale.
## Installing UFW
Most modern Linux distributions come with UFW pre-installed. If its not already on your system, you can install it with the following commands:
For Ubuntu/Debian:
```terminal
sudo apt update
sudo apt install ufw
```
For CentOS/RHEL:
```terminal
sudo yum install epel-release
sudo yum install ufw
```
For Arch Linux:
```terminal
sudo pacman -S ufw
```
## Basic UFW Commands
### Enable UFW
Before configuring UFW, you need to enable it:
```terminal
sudo ufw enable
```
### Check UFW Status
To see whether UFW is running and view current rules:
```terminal
sudo ufw status
```
### Allowing connections
To allow traffic on a specific port, use the `allow` command. For example, to allow SSH connections:
```terminal
sudo ufw allow ssh
```
Or, specify the port number:
```terminal
sudo ufw allow 22
```
### Denying Connections
To block traffic on a specific port:
```terminal
sudo ufw deny 80
```
### Removing Rules
To delete a rule, prepend the rule with `delete`. For example:
```terminal
sudo ufw delete allow 22
```
Or Remove a rule by its number
List UFW Rules with Numbers
```terminal
sudo ufw status numbered
```
Example output
```terminal
Status: active
To Action From
[ 1] 22/tcp ALLOW Anywhere
[ 2] 80/tcp ALLOW Anywhere
[ 3] 22/tcp (v6) ALLOW Anywhere (v6)
[ 4] 80/tcp (v6) ALLOW Anywhere (v6)
```
Delete the Rule by Number
```terminal
sudo ufw delete 2
```
### Resetting UFW
To reset UFW to its default state, removing all rules:
```terminal
sudo ufw reset
```
## Advanced Usage
### Limiting Connections
To protect against brute-force attacks, you can limit connections by using `limit` rule in UFW. This rule restricts the rate of new connections from the same IP address, allowing only a limited number of connections per minute (default: 6 attempts within 30 seconds). You can adjust these values by modifying the UFW configuration files, typically found in `/etc/ufw/` or `/etc/ufw/ufw.conf`, or by customizing rate limits using iptables rules directly. within a specified time frame. For instance, to limit SSH attempts, you can execute:
```terminal
sudo ufw limit ssh
```
This helps to deter malicious actors trying to gain unauthorized access to your system by repeatedly guessing passwords or exploiting vulnerabilities.
### Allowing Specific IP Addresses
To allow traffic from a specific IP address:
```terminal
sudo ufw allow from 192.168.0.100
```
Allowing Traffic to a Specific Port and IP
For more granular control, you can specify both source IP and destination port:
```terminal
sudo ufw allow from 192.168.0.100 to any port 22
sudo ufw allow from 192.168.0.0/24 to any port 22 proto tcp
```
### Using Application Profiles
UFW supports application profiles to simplify rule management for common services. List available profiles with:
```terminal
sudo ufw app list
```
To allow a specific application, UFW provides predefined profiles for commonly used software and services. These profiles encapsulate the necessary port and protocol details, simplifying firewall configuration. For instance, to permit traffic for an application like Apache, you can execute:
```terminal
sudo ufw allow 'Apache Full'
```
This command enables both HTTP (port 80) and HTTPS (port 443) traffic, as defined in the application profile.
## Best Practices
1. **Start with Defaults:** UFW's default policy denies incoming traffic and allows outgoing traffic, a good starting point for most setups.
2. **Enable Logging:** Turn on logging to monitor blocked traffic:
```terminal
sudo ufw logging on
```
3. **Test Rules:** Before applying complex rules on a production system, test them in a safe environment.
4. **Document Changes:** Keep a record of the rules you add or remove to make troubleshooting easier.
## Conclusion
UFW makes it easy to manage your firewall, even if youre new to Linux. By learning its simple and advanced features, you can protect your system without the hassle of complicated tools like iptables. Whether youre using one computer or many servers, UFW is a great tool to keep things secure.

View File

@ -5,7 +5,7 @@ order: 4
---
## Meet Bangara Raju: Your Guide to Tech, DIY, and Beyond
Hello there, curious minds and fellow explorers! I'm [**Bangara Raju**](https://bangararaju.kottedi.in/){:target="_blank"}, and I'm absolutely thrilled to welcome you to my corner of the internet.
Hello there, curious minds and fellow explorers! I'm **Bangara Raju**, and I'm absolutely thrilled to welcome you to my corner of the internet.
### A Byte-Sized Introduction:
By day, I'm immersed in the world of code and algorithms, working as a **Software Engineer** to shape the digital landscapes we navigate.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@ -4,7 +4,7 @@ var remark_config = {
components: ['embed'],
max_shown_comments: 10,
theme: Theme.visualState,
theme: modeToggle.modeStatus,
locale: 'en',
show_email_subscription: false
@ -23,6 +23,6 @@ var targetDiv = document.getElementById("sidebar").getElementsByClassName("mode-
if (targetDiv != null) {
targetDiv.addEventListener("click", () => {
window.REMARK42.changeTheme(Theme.visualState === 'light' ? 'dark' : 'light');
window.REMARK42.changeTheme(modeToggle.modeStatus === 'light' ? 'dark' : 'light');
})
}

View File

@ -3,17 +3,14 @@ var siteId = document.currentScript.getAttribute('siteid');
var postId = document.currentScript.getAttribute('postid');
var remark42Host = document.currentScript.getAttribute('remark42host');
function ajax(url, type, payload) {
if (payload === undefined) {
payload = null;
}
function ajax(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open(type, url);
xhr.send(payload);
xhr.open('GET', url);
xhr.send();
});
}

View File

@ -16,6 +16,7 @@ function apiCall(url, event) {
onLoadAndClick();
ajax(url)
.then(function (result) {
console.log(result);
if(event === "click"){
heartIcon.classList.toggle("liked");
}

View File

@ -1,75 +0,0 @@
{
"$schema": "https://frontmatter.codes/frontmatter.schema.json",
"frontMatter.taxonomy.contentTypes": [
{
"name": "default",
"pageBundle": false,
"previewPath": null,
"fields": [
{
"title": "Title",
"name": "title",
"type": "string"
},
{
"title": "Description",
"name": "description",
"type": "string"
},
{
"title": "Publishing date",
"name": "date",
"type": "datetime",
"default": "{{now}}",
"isPublishDate": true
},
{
"title": "Tags",
"name": "tags",
"type": "tags"
},
{
"title": "Categories",
"name": "categories",
"type": "categories"
},
{
"title": "image",
"name": "image",
"type": "fields",
"fields": [
{
"title": "path",
"name": "path",
"type": "image"
}
]
},
{
"title": "disable-ty",
"name": "disable-ty",
"type": "boolean"
},
{
"title": "series",
"name": "series",
"type": "string"
}
]
}
],
"frontMatter.framework.id": "jekyll",
"frontMatter.content.publicFolder": "",
"frontMatter.preview.host": "http://localhost:4000",
"frontMatter.content.pageFolders": [
{
"title": "drafts",
"path": "[[workspace]]/_drafts"
},
{
"title": "posts",
"path": "[[workspace]]/_posts"
}
],
"frontMatter.git.enabled": true
}

View File

@ -1,54 +0,0 @@
#!/usr/bin/env bash
#
# Run jekyll serve and then launch the site
prod=false
command="bundle exec jekyll s -l"
host="127.0.0.1"
help() {
echo "Usage:"
echo
echo " bash /path/to/run [options]"
echo
echo "Options:"
echo " -H, --host [HOST] Host to bind to."
echo " -p, --production Run Jekyll in 'production' mode."
echo " -h, --help Print this help information."
}
while (($#)); do
opt="$1"
case $opt in
-H | --host)
host="$2"
shift 2
;;
-p | --production)
prod=true
shift
;;
-h | --help)
help
exit 0
;;
*)
echo -e "> Unknown option: '$opt'\n"
help
exit 1
;;
esac
done
command="$command -H $host"
if $prod; then
command="JEKYLL_ENV=production $command"
fi
if [ -e /proc/1/cgroup ] && grep -q docker /proc/1/cgroup; then
command="$command --force_polling"
fi
echo -e "\n> $command\n"
eval "$command"

View File

@ -1,89 +0,0 @@
#!/usr/bin/env bash
#
# Build and test the site content
#
# Requirement: html-proofer, jekyll
#
# Usage: See help information
set -eu
SITE_DIR="_site"
_config="_config.yml"
_baseurl=""
help() {
echo "Build and test the site content"
echo
echo "Usage:"
echo
echo " bash $0 [options]"
echo
echo "Options:"
echo ' -c, --config "<config_a[,config_b[...]]>" Specify config file(s)'
echo " -h, --help Print this information."
}
read_baseurl() {
if [[ $_config == *","* ]]; then
# multiple config
IFS=","
read -ra config_array <<<"$_config"
# reverse loop the config files
for ((i = ${#config_array[@]} - 1; i >= 0; i--)); do
_tmp_baseurl="$(grep '^baseurl:' "${config_array[i]}" | sed "s/.*: *//;s/['\"]//g;s/#.*//")"
if [[ -n $_tmp_baseurl ]]; then
_baseurl="$_tmp_baseurl"
break
fi
done
else
# single config
_baseurl="$(grep '^baseurl:' "$_config" | sed "s/.*: *//;s/['\"]//g;s/#.*//")"
fi
}
main() {
# clean up
if [[ -d $SITE_DIR ]]; then
rm -rf "$SITE_DIR"
fi
read_baseurl
# build
JEKYLL_ENV=production bundle exec jekyll b \
-d "$SITE_DIR$_baseurl" -c "$_config"
# test
bundle exec htmlproofer "$SITE_DIR" \
--disable-external \
--ignore-urls "/^http:\/\/127.0.0.1/,/^http:\/\/0.0.0.0/,/^http:\/\/localhost/"
}
while (($#)); do
opt="$1"
case $opt in
-c | --config)
_config="$2"
shift
shift
;;
-h | --help)
help
exit 0
;;
*)
# unknown option
help
exit 1
;;
esac
done
main