chore: remove blog, open-source and news sections

- Delete the blogs, opensource-projects and notifications trees
- Drop their nav entries and i18n keys
- Point the nav at the new open-source site instead
- Drop the opensource-projects sitemap priority
This commit is contained in:
zihluwang committed 2026-09-29 12:21:53 +08:00
1 parent e149401346
commit 7a63074a65
92 files changed
+20 -8112

No files matched your search

+1 -12
View File
@@ -14,19 +14,8 @@
"link": "/services/",
"activeMatch": "/services/"
},
{
"text": "nav.insights",
"link": "/blogs/",
"activeMatch": "/blogs/"
},
{
"text": "nav.news",
"link": "/notifications/",
"activeMatch": "/notifications/"
},
{
"text": "nav.opensource",
"link": "/opensource-projects/",
"activeMatch": "/opensource-projects/"
"link": "https://opensource.onixbyte.com"
}
]
-33
View File
@@ -1,33 +0,0 @@
[
"index",
{ "type": "section-header", "label": "insights.group.frontend" },
{ "type": "file", "name": "ecmascript-2025-syntax-sugar" },
{ "type": "file", "name": "frontend-development-standards" },
{ "type": "file", "name": "frontend-tips-and-solutions" },
{ "type": "file", "name": "font-size-conversion-table" },
{ "type": "section-header", "label": "insights.group.backend" },
{ "type": "file", "name": "java-development-standards" },
{ "type": "file", "name": "java-development-cheatsheet" },
{ "type": "file", "name": "mybatis-flex" },
{ "type": "file", "name": "postgresql-zhparser-fuzzy-search" },
{ "type": "file", "name": "use-json-in-mysql" },
{ "type": "section-header", "label": "insights.group.devops" },
{ "type": "file", "name": "docker-deployment-standards" },
{ "type": "file", "name": "gitlab-ops" },
{ "type": "file", "name": "setup-ldap-service" },
{ "type": "file", "name": "minio-admin-guide" },
{ "type": "section-header", "label": "insights.group.practice" },
{ "type": "file", "name": "general-development-standards" },
{ "type": "file", "name": "google-code-review-standards" },
{ "type": "file", "name": "version-control-and-code-review" },
{ "type": "file", "name": "git-cheatsheet" },
{ "type": "section-header", "label": "insights.group.tooling" },
{ "type": "file", "name": "fix-idea-application-yaml-schema-validation" },
{ "type": "file", "name": "windows-driver-backup-restore" },
{ "type": "file", "name": "fix-macos-monterey-sleep-wake" },
{ "type": "file", "name": "macos-private-dns-hostname" },
{ "type": "file", "name": "quartile-method" },
{ "type": "file", "name": "use-of-gravatar" },
{ "type": "file", "name": "email-like-a-boss" },
{ "type": "file", "name": "cooking-seasoning-guide" }
]
@@ -1,31 +0,0 @@
---
title: Cooking Seasoning Guide
tags:
- cooking
- cheatsheet
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## When to Add Seasonings
| Seasoning Type | Timing | Suitable Dishes | Effect |
| -------------------------------------- | -------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------- |
| **Salt** | Last | Leafy greens | Prevents leaves from wilting and releasing water, keeps them crisp and tender |
| | Mid-way | Shredded potatoes, green beans, garlic shoots | Better flavour absorption |
| **MSG, Light Soy Sauce, Oyster Sauce** | Towards the end | Dishes needing umami and aroma enhancement | Adds umami and aroma; high heat destroys freshness and fragrance |
| **Cooking Wine** | Together with ingredients | Ingredients needing deodorisation | Better deodorisation when blanching |
| | At highest wok heat | Stir-fried ingredients needing deodorisation | High heat accelerates alcohol evaporation, effective aroma searing |
| **Vinegar** | Early | Hot and sour shredded potatoes, hot and sour cabbage | Makes shredded potatoes crisper and more flavourful |
| | Drizzle around wok edge before serving | Stir-fried chilli pork and similar high-heat dishes | High-heat searing enhances aroma |
| **Dark Soy Sauce** | After half-cooked | Dishes needing colour | Achieves optimal colouring effect |
## Quick Reference
| Seasoning | Timing | Effect | Example |
| --------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| **Salt** | 10–15 seconds before serving | Prevents premature water loss and wilting of leafy greens; keeps them crisp | Sprinkle salt and toss before turning off the heat for lettuce or spinach |
| **Minced Garlic / Ginger** | First, after oil is hot | Releases fragrance and enhances flavour | Sauté minced garlic in hot oil, then add greens |
| **Light Soy Sauce / Oyster Sauce** (optional) | After salt | Adds umami; use sparingly to avoid overpowering the vegetables | Add 1–2 drops of light soy sauce after salting Shanghai greens |
| **Cooking Oil** | Hot wok, hot oil | Quick stir-fry at high heat locks in moisture | Use slightly more oil and high heat for fast stir-frying |
@@ -1,18 +0,0 @@
---
title: Docker Deployment Standards
tags:
- docker
- deployment
- standards
- best-practice
author:
name: Zihlu Wang
email: real@zihluwang.me
---
- **Dockerfiles**: Provide a `Dockerfile` for the application to enable containerised deployment.
- **Lightweight Images**: Strive for lightweight Docker images by using appropriate base images and multi-stage builds.
- **Configuration**: Ensure environment-specific configuration (e.g., database connection strings, external service
URLs) is managed through environment variables injected into Docker containers.
- **Logging**: Configure containerised logging to output to `stdout` and `stderr` so that log aggregation systems can
collect logs easily.
@@ -1,55 +0,0 @@
---
title: ECMAScript 2025 Syntax Sugar Guide
tags:
- javascript
- ecmascript
- pattern-matching
- frontend
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Pattern Matching
### Traditional Approach
```javascript
function processResponse(response) {
if (response.status === 200 && response.data) {
return { success: true, data: response.data }
} else if (response.status === 404) {
return { success: false, error: "Not found" }
} else if (response.status >= 500) {
return { success: false, error: "Server error" }
} else {
return { success: false, error: "Unknown error" }
}
}
```
### Pattern Matching Approach
```javascript
function processResponse(response) {
return match (response) {
when ({ status: 200, data }) -> ({ success: true, data })
when ({ status: 404 }) -> ({ success: false, error: 'Not found' })
when ({ status: status if status >= 500 }) -> ({ success: false, error: 'Server error' })
default -> ({ success: false, error: 'Unknown error' })
};
}
```
### Handling Array Length Branches Gracefully
```javascript
function handleArray(arr) {
return match (arr) {
when ([]) -> "Empty array"
when ([first]) -> `Only one element: ${first}`
when ([first, second]) -> `Two elements: ${first}, ${second}`
when ([first, ...rest]) -> `First element: ${first}, others: ${rest.length} items`
};
}
```
-43
View File
@@ -1,43 +0,0 @@
---
title: Email Like a Boss
tags:
- communication
- email
- soft-skill
- career
- productivity
author:
name: Zihlu Wang
email: real@zihluwang.me
---
Email at work is about more than sharing information — it's about building trust and shaping your professional image.
The same message, phrased differently, can leave an entirely different impression.
Below are 9 common email scenarios, contrasting "low-power" expressions with "high-power" alternatives that make you
sound more confident and professional.
## Scenario Cheatsheet
| Scenario | ❌ Don't Use (sounds…) | ✅ Use Instead (sounds…) | Why It Works |
| -------------------------- | -------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Late reply | **Sorry for the delay** | **Thanks for your patience** | Swaps an apology for gratitude — the focus shifts from "I was wrong" to "you were generous," acknowledging the delay while making the recipient feel respected |
| Scheduling | **What works best for you?** | **Could you do …?** | The former throws the decision entirely back to the other person; the latter offers a concrete option, cutting down on back-and-forth |
| After helping someone | **No problem / No worries** | **Always happy to help** | The former implies the task _could_ have been a problem; the latter signals you enjoyed it and would happily do it again |
| Making a suggestion | **I think maybe we should …** | **It'd be best if we …** | The former oozes hesitation and self-doubt; the latter delivers a clear judgment — like someone with experience making a decision |
| Text isn't working | **_Spending 30 minutes rewriting an email_** | **It'd be easier to discuss in person** | Recognising the medium itself is the bottleneck and switching channels can be the most efficient move |
| Checking for understanding | **Hopefully that makes sense?** | **Let me know if you have questions** | The former betrays doubt about your own clarity; the latter calmly shares responsibility — the reader now has an action item too |
| Following up on progress | **Just wanted to check in** | **When can I expect an update?** | The former tiptoes around the ask; the latter names the time frame directly — clear, polite, and professional |
| Owned a small mistake | **Ahh sorry my bad totally missed that** | **Thanks for letting me know** | Over-apologising makes things awkward; this acknowledges the catch while keeping the focus on moving forward |
| Need to leave early | **Could I possibly leave early?** | **I will need to leave at …** | The former asks for permission; the latter states a plan — you're a professional and don't need to apologise for reasonable needs |
## Core Principles
Writing great emails is less about vocabulary and more about **stance**. Keep three rules in mind:
1. **State instead of ask** — "I need…" carries more weight than "Could I possibly…"
2. **Thank instead of apologise** — Shift the focus from "my shortcoming" to "their support"
3. **Be specific instead of vague** — Offer exact times, options, and action items rather than lobbing the ball back
into their court
Next time you open your inbox, take five seconds to ask: can I phrase this more like someone who makes decisions?
@@ -1,58 +0,0 @@
---
title: Fix IntelliJ IDEA Missing Spring Boot application.yaml Code Completion
tags:
- intellij-idea
- spring-boot
- yaml
- schema-validation
- troubleshooting
author:
name: Zihlu Wang
email: real@zihluwang.me
---
import { Kbd } from "@rspress/core/theme"
Recently, while developing a Spring Boot project in IntelliJ IDEA, I noticed that the smart code completion for `application.yaml` and `application.yml` had suddenly vanished. Worse, the editor was flooded with warnings reading:
> Schema validation: Missing required property 'kind' = 'Application'
## The Symptom
When opening any `application.yaml` or `application.yml` under `src/main/resources/`, IDEA would treat it as an Enonic XP application descriptor rather than a Spring Boot configuration file. All Spring-specific property hints were gone, replaced by schema validation errors complaining about a missing `kind` property — a field that has nothing to do with Spring Boot.
Restarting IDEA, invalidating caches, and re-importing the project made no difference. The problem persisted across restarts.
## The Quick Fix
After some digging, I found the culprit. IDEA downloads JSON Schemas from remote sources (specifically, the [SchemaStore](https://www.schemastore.org/) catalogue) and uses them to validate configuration files. A recent schema update had introduced a `fileMatch` pattern that was too aggressive.
To disable this behaviour immediately:
1. Open **Settings** (<Kbd>Ctrl</Kbd>+<Kbd>Alt</Kbd>+<Kbd>S</Kbd> for Windows / <Kbd mac="cmd" /> + <Kbd>,</Kbd> for macOS)
2. Navigate to **Languages & Frameworks → Schemas and DTDs → Remote JSON Schemas**
3. Uncheck **Allow downloading JSON Schemas from remote sources**
4. Click **OK**
Your Spring Boot configuration hints will return instantly.
## Root Cause
The real culprit traces back to **19 May 2026**, when a developer from [Enonic](https://www.enonic.com/) submitted a pull request to the SchemaStore repository ([PR #5704](https://github.com/SchemaStore/schemastore/pull/5704)) that added the Enonic XP application descriptor schema. The schema's `fileMatch` property was set to:
```json
"fileMatch": [
"**/src/main/resources/application.yaml",
"**/src/main/resources/application.yml"
]
```
This pattern is far too broad — it matches **every** `application.yaml` and `application.yml` placed under `src/main/resources/`, which happens to be exactly where Spring Boot projects store their configuration files. Once this schema was merged into SchemaStore, any IntelliJ IDEA instance with remote JSON schema downloading enabled would fetch it and begin validating Spring Boot configs against the Enonic XP schema.
The developer has since acknowledged the issue and submitted a follow-up fix ([PR #5711](https://github.com/SchemaStore/schemastore/pull/5711)) to tighten the `fileMatch` patterns. As of writing, the fix has not yet been merged into SchemaStore.
## Why This Matters
SchemaStore is a community-maintained catalogue of JSON Schemas used by many editors (VS Code, IntelliJ IDEA, Neovim, etc.) to provide validation and autocompletion for common configuration files. When a schema in this catalogue declares overly broad matching rules, it affects users across all platforms and editors — not just IDEA.
For Spring Boot developers, the takeaway is simple: if your `application.yaml` hints disappear overnight, check what schema your IDE thinks the file belongs to. It might not be Spring Boot at all.
@@ -1,40 +0,0 @@
---
title: Fix macOS Monterey+ Devices Waking Frequently from Sleep
tags:
- macos
- sleep
- power-management
- bug
---
> This article was originally written by **落格博客
> **: [落格博客](https://www.logcg.com/) » [Frequent Wake-from-Sleep Issues After Upgrading to macOS Monterey](https://www.logcg.com/archives/3528.html)
After upgrading to macOS Monterey, my screen kept lighting up in the middle of the night for no apparent reason. It had
happened before, but only when notifications came in. Now the screen lights up on its own with no trigger — same
hardware, so it must be a software issue.
After searching online, I first
found [Apple's official guide](https://support.apple.com/en-gb/guide/mac-help/mchlp2995/mac). It's very detailed, but
clearly of no help whatsoever.
Digging deeper, I found the root cause. Run **`pmset -g log | grep DarkWake`** and you'll see your Mac hasn't been
resting while you slept...
Several typical patterns appear, most with a DarkWake immediately followed by a Wake. The issue: DarkWake is meant to
wake the computer in the background to update data, but somehow a peripheral gets triggered, causing a full system wake.
In any case, I don't want this feature — Power Nap. For me, I'd rather it save as much power as possible. The fix path:
disable network access wake, disable Power Nap... But here's the catch — on M1 devices, there is no Power Nap option in
settings. (Clearly, Apple is confident in their battery life but overlooked the power of bugs.)
So for Power Nap, we have to go through the command line. First, check the current status with **`pmset -g`**. Find the
**`powernap`** value — if it isn't 0, it's enabled. Disable it with **`sudo pmset -a powernap 0`**.
Also check **`tcpkeepalive`** — it likely isn't 0 either and should also be turned off. It controls whether your Mac
maintains TCP connections while sleeping. Run **`sudo pmset -a tcpkeepalive 0`** — you'll see a terminal warning:
**_Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical
features like 'Find My Mac' not to function properly._**
Essentially, turning it off limits some features — the system simply won't connect to the network while asleep. I'm
fairly confident that if someone actually steals your Mac, they won't be getting it online anyway.
@@ -1,30 +0,0 @@
---
title: Font Size Conversion Table
tags:
- typography
- font
- cheatsheet
- design
author:
name: Zihlu Wang
email: real@zihluwang.me
---
| Chinese Size Name | English Point Size (pt) | mm | px |
| -------------------- | ----------------------- | ----- | ---- |
| 初号 (Primary) | 42 | 14.82 | 56 |
| 小初 (Small Primary) | 36 | 12.7 | 48 |
| 一号 (No. 1) | 26 | 9.17 | 34.7 |
| 小一 (Small No. 1) | 24 | 8.47 | 32 |
| 二号 (No. 2) | 22 | 7.76 | 29.3 |
| 小二 (Small No. 2) | 18 | 6.35 | 24 |
| 三号 (No. 3) | 16 | 5.64 | 21.3 |
| 小三 (Small No. 3) | 15 | 5.29 | 20 |
| 四号 (No. 4) | 14 | 4.94 | 18.7 |
| 小四 (Small No. 4) | 12 | 4.23 | 16 |
| 五号 (No. 5) | 10.5 | 3.7 | 14 |
| 小五 (Small No. 5) | 9 | 3.18 | 12 |
| 六号 (No. 6) | 7.5 | 2.56 | 10 |
| 小六 (Small No. 6) | 6.5 | 2.29 | 8.7 |
| 七号 (No. 7) | 5.5 | 1.94 | 7.3 |
| 八号 (No. 8) | 5 | 1.76 | 6.7 |
@@ -1,47 +0,0 @@
---
title: Frontend Development Standards
tags:
- frontend
- react
- standards
- best-practice
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Dependency Management (pnpm)
- **Strictness**: Leverage pnpm's strict dependency management to ensure a more deterministic `node_modules` structure
and efficient disk space usage.
- **Workspaces**: When using a monorepo approach, configure pnpm workspaces to streamline dependency management across
multiple frontend packages.
- **Auditing**: Regularly audit frontend dependencies for known vulnerabilities using `pnpm audit`.
## API Communication (Axios)
- **Axios Instance**: Create a centralised Axios instance for API calls to apply common configuration (base URL,
headers, interceptors).
- **Interceptors**: Use Axios interceptors to:
- Add authentication tokens to outgoing requests.
- Handle global error responses (e.g., display a notification for `401 Unauthorized`).
- Log requests/responses in development environments.
- **Error Handling**: Centralise API error handling in Axios interceptors or custom utility functions to provide
consistent user feedback.
## React and Component Standards
- **Function Components & Hooks**: Prefer function components with React Hooks over class components for new
development.
- **Props**:
- Define `interface` or `type` for component props to ensure type safety.
- Destructure props at the component entry point for clarity.
- **State Management (Redux)**:
- Use Redux Toolkit for efficient, boilerplate-reduced Redux development.
- Use `createSlice` to organise Redux logic into "slices" (feature-specific reducers, actions, and selectors).
- Follow the "ducks" pattern or "slices" approach to co-locate Redux logic.
- **Component Composition**: Break down complex UIs into smaller, reusable, single-responsibility components.
- **Ant Design**:
- Leverage Ant Design components for consistent UI/UX.
- Use CSS-in-JS solutions to consistently customise Ant Design themes and styles across the application if needed.
- **Accessibility**: Design and implement components with web accessibility (a11y) in mind from the start.
@@ -1,75 +0,0 @@
---
title: Frontend Tips and Solutions Cheatsheet
tags:
- frontend
- react
- ant-design
- tailwind
- best-practice
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Decoupling Form Components
Split form components into UI components and logic components. Use the UI components inside the logic components for rendering styles.
## React Entry Component Order
```typescript
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import { Provider as ReduxProvider } from "react-redux"
import { PersistGate } from "redux-persist/integration/react"
import { RouterProvider } from "react-router"
import { App as AntApp, ConfigProvider as AntConfigProvider } from "antd"
import { StyleProvider } from "@ant-design/cssinjs"
import AntSimplifiedChinese from "antd/locale/zh_CN"
import "./index.css"
import "@/config/dayjs-config"
import store, { persistor } from "@/store"
import router from "@/router"
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ReduxProvider store={store}>
<PersistGate loading={null} persistor={persistor}>
{/* Note: StyleProvider must be the parent of ConfigProvider!!! */}
<StyleProvider layer>
<AntConfigProvider
locale={AntSimplifiedChinese}
button={{
autoInsertSpace: false,
}}>
<AntApp className="h-full w-full">
<RouterProvider router={router} />
</AntApp>
</AntConfigProvider>
</StyleProvider>
</PersistGate>
</ReduxProvider>
</StrictMode>
)
```
## Integrating Ant Design with Tailwind CSS in React
> Reference: [https://github.com/ant-design/ant-design/discussions/56152](https://github.com/ant-design/ant-design/discussions/56152)
```css
/* index.css */
@layer theme, base, antd, components, utilities;
@import "../../../node_modules/.pnpm/tailwindcss@4.3.0/node_modules/tailwindcss/dist/lib.d.mts";
```
```typescript
<StyleProvider>
<ConfigProvider>
<App>
<RouterProvider/>
</App>
</ConfigProvider>
</StyleProvider>
```
@@ -1,26 +0,0 @@
---
title: General Application Development Standards
tags:
- standards
- best-practice
- engineering
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Introduction
This document outlines coding standards and best practices for application development. Adhering to these guidelines
ensures code quality, maintainability, and consistency across all projects.
## General Principles
- **Clarity & Readability**: Code must be easy to read and understand. Prefer clear, self-documenting code over clever,
terse, but obscure solutions.
- **Consistency**: Maintain a consistent coding style, naming conventions, and architectural patterns across the
project.
- **Modularity**: Design components with loose coupling and high cohesion to promote reusability and simpler testing.
- **Testability**: Write code that is inherently testable.
- **Security-First Design**: Incorporate security considerations at every stage of development.
- **Performance Awareness**: Be mindful of performance implications for critical code paths and API endpoints.
-161
View File
@@ -1,161 +0,0 @@
---
title: Git Cheatsheet
tags:
- git
- tips
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Ignoring Local Modifications to Tracked Files: `git update-index --skip-worktree`
When you need to modify a Git-tracked file locally but don't want those changes to show up in `git status` or in commits (e.g. a local `application-local.yml`, `.env`, or IDE configuration), you can use `--skip-worktree` to make Git ignore local modifications to that file.
### Marking and Unmarking
```bash
# Make Git ignore local modifications to the file
git update-index --skip-worktree <file>
# Unmark the file and track it again
git update-index --no-skip-worktree <file>
```
### Listing Ignored Files
Marked files are shown with an `S` flag in `git ls-files -v`:
```bash
git ls-files -v | grep '^S'
```
### Committing Changes to a Marked File
To commit new changes to the file, unmark it first, commit, then mark it again:
```bash
git update-index --no-skip-worktree <file>
git add <file>
git commit -m "chore: update config"
git update-index --skip-worktree <file>
```
### `--skip-worktree` vs `--assume-unchanged`
Both options stop Git from reporting modifications to tracked files, but they serve different purposes:
| Option | Purpose |
| -------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `--assume-unchanged` | A performance optimisation for large files that rarely change; Git may re-track the file once it notices a change |
| `--skip-worktree` | Deliberately ignore local changes for "keep local modifications, don't commit" scenarios; more persistent |
So for "ignoring local changes", use `--skip-worktree`.
### Caveats
- `--skip-worktree` only affects the working tree — it is not a permanent ignore. Operations that rewrite the file, such as `git pull`, `git checkout`, or `git stash`, may overwrite your local changes or cause a conflict. If the file is also likely to change upstream, unmark it before pulling.
- It only applies to **tracked** files; for untracked files, use `.gitignore`.
## Stashing Unfinished Work: `git stash`
When you need to switch branches mid-task, use `git stash` to set your working-tree changes aside:
```bash
git stash push -m "work in progress" # save with a message
git stash -u # include untracked files
git stash list # list saved stashes
git stash pop # restore and drop the stash
git stash apply # restore but keep the stash
git stash drop # delete a stash
```
## Amending the Latest Commit: `git commit --amend`
When you commit and then realise you forgot a file or want to change the message:
```bash
git add <forgotten-file>
git commit --amend # keep the same message, fold in new changes
git commit --amend -m "new message" # also change the message
```
Note: if the commit has already been pushed, prefer adding a new commit instead of rewriting shared history.
## Undoing Changes: `git restore` and `git reset`
```bash
git restore <file> # discard unstaged working-tree changes
git restore --staged <file> # unstage but keep the changes (like git reset HEAD <file>)
git restore --source=HEAD <file> # restore the file to its HEAD version
```
To discard all uncommitted changes (including the staging area):
```bash
git reset --hard HEAD
```
`--hard` also resets the working tree, so double-check that nothing you want to keep would be lost.
## Recovering Lost Commits: `git reflog`
When a commit seems to have vanished after a bad `git reset --hard` or `git rebase`, `reflog` records every place HEAD has been:
```bash
git reflog
git reset --hard HEAD@{2} # go back to an earlier recorded position
```
As long as the commit still exists in the object database, reflog can get you back to it.
## Bisecting a Regression: `git bisect`
When you don't know which commit introduced a bug, `git bisect` uses binary search to locate it:
```bash
git bisect start
git bisect bad # the current commit is bad
git bisect good <sha> # mark a known-good commit
```
Git checks out a midpoint commit; you just mark each as `good` or `bad` until it narrows down the offending commit:
```bash
git bisect good # or git bisect bad
git bisect reset # finish and return to your branch
```
## Friendlier History: `git log`
```bash
git log --oneline --graph --decorate --all # graph view across all branches
git log -S "some-string" --all # find commits that added/removed a string
git blame <file> # show the last modifier of each line
git blame -L 10,20 <file> # restrict to a line range
```
## Cleaning Up Untracked Files: `git clean`
```bash
git clean -n -fd # preview which files would be removed (always run this first)
git clean -fd # delete untracked files and directories
```
`-n` only shows what would be deleted without deleting it — preview before you execute.
## Picking Commits: `git cherry-pick`
Apply a specific commit from another branch onto your current branch:
```bash
git cherry-pick <sha>
```
## Deleting Merged Local Branches
```bash
git branch --merged | grep -v '\*' | xargs -n 1 git branch -d
```
This deletes all local branches merged into the current branch (except the current one). Preview with `git branch --merged` first.
-60
View File
@@ -1,60 +0,0 @@
---
title: GitLab Operations
tags:
- gitlab
- devops
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Setting the Default Language to Chinese in GitLab
GitLab filters out languages with less than 90% translation coverage via the `SWITCHER_MINIMUM_TRANSLATION_LEVEL`
variable in the code. Simplified Chinese sits at 84% and Traditional Chinese at 83%.
Navigate to `/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb` and modify
the code as follows:
```diff title="/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb"
- SWITCHER_MINIMUM_TRANSLATION_LEVEL = 90
+ SWITCHER_MINIMUM_TRANSLATION_LEVEL = 80
```
Then restart GitLab with `gitlab-ctl restart`.
## Adding ICP Filing Information to the Homepage
Navigate to `/path/to/gitlab/embedded/service/gitlab-rails/app/views/devise/shared/_footer.html.haml` and add the
following:
```diff
+ = link_to _("Your ICP Filing Number"), "https://beian.miit.gov.cn/", target: '_blank', class: 'text-nowrap', rel: 'noopener noreferrer'
```
## Upgrading GitLab
### Downloading the GitLab CE Package
Download the deb package from the [GitLab Package page](https://packages.gitlab.com/gitlab/gitlab-ce) and transfer it to
the server using `scp` or a similar tool.
### Stopping Memory-Intensive Services
```shell
gitlab-ctl stop puma
gitlab-ctl stop sidekiq
```
### Running a Backup
```shell
# Example: skip build artefacts and container registry, backing up only the database and repositories
sudo gitlab-backup create SKIP=artifacts,registry
```
### Installing
```shell
sudo dpkg -i gitlab-package.deb
```
@@ -1,131 +0,0 @@
---
title: Google Code Review Standards
tags:
- code-review
- best-practice
- engineering
- google
author:
name: Zihlu Wang
email: real@zihluwang.me
---
Code review is a step in the development process where one or more developers review code written by another developer (
the author) to ensure that:
- The code is free of errors, bugs, and issues;
- The code conforms to quality and style guide requirements and standards;
- The code fulfils all intended functionality;
- Once merged, the codebase continues to function properly and is left in a better state.
This is why code review is a vital part of software development. Code reviewers act as gatekeepers, responsible for
deciding whether the code is ready to become part of the codebase and enter production.
## The Code Should Improve the Overall Health of the System
Every code change (pull request) should improve the overall health of the system. The key point is that even small
improvements, once merged, enhance the health of the software or codebase.
## Review Code Quickly and Provide Active Responses and Feedback
First and foremost, do not delay the merging of code. There is no such thing as perfect code. If the code improves the
overall health of the system, it should be delivered promptly.
> The key insight is that there is no perfect code — only better code. — Google Engineering Practices Documentation
If there are no urgent tasks at hand, review code as soon as it is submitted. The maximum response time for a pull
request should not exceed one working day. Multiple rounds of partial or complete code review should be completed for a
single pull request within a day.
## Educate and Inspire During Code Review
During code review, provide guidance by sharing knowledge and experience wherever possible.
## Review Code Against Standards
Always remember that style guides, coding standards, and relevant documentation should serve as the absolute authority
in code review. For example, when consistency of tabs versus spaces is in question, cite the coding conventions.
> If you use Java, the following article may be helpful — it summarises Java coding best practices from major tech
> companies: [A Short Summary of Java Coding Best Practices](https://rhamedy.medium.com/a-short-summary-of-java-coding-best-practices-31283d0167d3)
## Resolving Code Review Conflicts
When resolving conflicts during code review, follow the agreed best practices in the style guide and coding standards,
and seek advice from others with more domain knowledge and experience.
If your comment is optional or relatively unimportant, indicate so in the comment and let the author decide whether to
address or skip it.
As a reviewer, when there is no style guide or coding standard to reference, you can at minimum suggest that the code
change remain consistent with the rest of the codebase.
## UI Change Demonstrations Are Part of Code Review
If a code change involves user interface modifications, a demonstration is required in addition to the code review to
ensure the UI meets expectations and aligns with the interface design.
For frontend code changes, you must provide a demonstration or ensure that the code change includes the necessary UI
automation tests to verify the added or updated functionality.
## Ensure All Tests Are Included in the Code Review
Unless in an emergency, pull requests should include all necessary tests, such as unit tests, integration tests, and
end-to-end tests.
An emergency here means a bug or security vulnerability that needs fixing as soon as possible, and tests can be added
later. In such cases, ensure that appropriate tickets/issues are created and someone is responsible for completing the
tests immediately after the hotfix or deployment.
Skipping tests must never be accepted. If time is short and certain goals risk not being met, the solution is not to
skip tests but to scope down the deliverables.
## Don't Interrupt Your Own Work for Code Review
If you are deeply focused on your work, don't interrupt yourself — it takes a long time to get back into the flow. In
other words, the cost of interrupting a developer in flow far exceeds the cost of making them wait for a code review. Do
your code reviews after a break (lunch, coffee, etc.).
Most of the time, an entire code review and merge cannot be completed in a single day. What matters is giving the author
prompt feedback. For example, even if you cannot complete a full review, you can quickly point out a few areas worth
discussing. This significantly reduces frustration during the review process.
## Review All Code — Make No Assumptions
Review every line of code that is submitted. Do not make assumptions about manually written classes and methods, and
make sure you understand what the code is doing.
Make sure you understand the code you are reviewing. If you don't, ask the author for clarification or a code
walkthrough and explanation. If you are not qualified to review part of the code, ask another qualified developer to
review it in your place.
## Keep the Big Picture in Mind During Code Review
It helps to look at code changes from a broader perspective. For example, a file is modified and four new lines of code
are added. Don't just look at those four lines — consider reviewing the entire file and examining what was added. Do the
additions degrade the quality of existing code? Do they make existing functionality a candidate for refactoring?
Reviewing added code outside the context of the function/method or class will, over time, lead to classes that are
unmaintainable, tangled, difficult to test, and hard to extend or refactor.
Remember that just as trivial improvements can compound into a better product over time, even minor code degradation or
technical debt can accumulate to the point where the product becomes difficult to maintain and extend.
## Recognise and Encourage Great Work During Code Review
If you see an excellent code change, don't forget to acknowledge and encourage the author generously. The purpose of
code review is not only to find errors but also to encourage and guide developers towards great work.
## Be Considerate, Respectful, Kind, and Clear During Code Review
It is critical to remain kind, clear, polite, and respectful during code review, while also providing the author with
clear feedback and positive assistance. When reviewing code, comment on the code — not the developer.
## Explain Code Review Comments Thoroughly, with a Sense of Proportion
Whenever a code review comment proposes an alternative or points out a problem, it's important to explain the reasoning
and provide examples based on your knowledge and experience to help the developer understand why your suggestion
improves the code quality.
When suggesting modifications or changes, find the right balance in how you guide the author. For example, I prefer
guidance, explanation, hints, or suggestions over providing the entire solution.
-18
View File
@@ -1,18 +0,0 @@
---
title: Insights
---
# Insights
Practical guides, cheatsheets, and standards distilled from daily engineering
work.
## Topics
- **Frontend** — React patterns, TailwindCSS tips, ECMAScript syntax guides, browser quirks
- **Backend & Database** — Java development, MyBatis, PostgreSQL, MySQL JSON
- **DevOps & Infrastructure** — Docker, GitLab CI/CD, LDAP, MinIO, macOS troubleshooting
- **Engineering Standards** — code review, version control, general and language-specific conventions
- **Tools & Productivity** — email etiquette, font sizing, data analysis methods
Browse the sidebar or scroll through the posts below — each article is self-contained and written to be immediately useful.
@@ -1,282 +0,0 @@
---
title: Java Development Cheatsheet
tags:
- java
- tips
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Comparison for `BigDecimal`
In Java, comparing `BigDecimal` values requires care because `equals` and `compareTo` behave differently.
### `equals` vs `compareTo`
`BigDecimal#equals` checks both **value** and **scale**, while `BigDecimal#compareTo` only checks **value** (ignoring scale). This means:
```java
var a = new BigDecimal("100"); // scale = 0
var b = new BigDecimal("100.00"); // scale = 2
a.equals(b); // false — different scale
a.compareTo(b); // 0 — same mathematical value
var c = new BigDecimal("200");
a.compareTo(c); // -1 (negative) — a is less than c
c.compareTo(a); // 1 (positive) — c is greater than a
```
### Why this matters
The scale mismatch often appears when values come from different sources — e.g., parsing user input, reading from a database (`DECIMAL(10,2)` columns), or receiving JSON payloads. You might think two values are equal when `equals` says they aren't.
### Rule of thumb
- Use **`compareTo`** for numeric equality checks: `a.compareTo(b) == 0`
- Use **`equals`** only when you mean "identical representation" (same value and same scale)
- Use **`stripTrailingZeros()`** if you need to normalise scale before `equals`:
```java
a.stripTrailingZeros().equals(b.stripTrailingZeros()); // true
```
### Comparing with zero
Avoid `==` or `.equals(BigDecimal.ZERO)` to check for zero — prefer `compareTo`:
```java
if (value.compareTo(BigDecimal.ZERO) == 0) { ... }
```
## How to Retrieve Data from a BlockingQueue
- `take()` — retrieves and removes the head of the queue, waiting if necessary until an element becomes available.
- `poll()` — retrieves and removes the head of the queue, or returns `null` if the queue is empty.
- `poll(long timeout, TimeUnit unit)` — retrieves and removes the head of the queue, waiting up to the specified wait time if necessary for an element to become available. Returns `null` if the timeout expires.
- `peek()` — retrieves but does not remove the head of the queue. Returns `null` if the queue is empty.
## Spring Cloud Alibaba FAQs
### How to prevent Nacos from creating a `nacos` folder in the user's home directory?
Add the following two configuration properties to specify the Nacos storage path:
- `JM.LOG.PATH`
- `JM.SNAPSHOT.PATH`
### How to deal with Sentinel's scattered log files?
Add the configuration property `csp.sentinel.log.dir` to change Sentinel's log directory.
### How to add Configuration Properties in JetBrains IntelliJ IDEA?
In JetBrains IntelliJ IDEA, click **`Edit Configurations…`** in the run configuration dropdown at the top right.
Click the **`Modify options`** button on the page, then add the properties you need to reset in the **`Override configuration properties`** table that appears below.
## Spring Data JPA FAQs
### How to fix the "Serializing `PageImpl` instances as-is not supported" warning?
Spring Data JPA warns about unstable JSON serialization of `PageImpl`. To resolve this, enable VIA_DTO serialization mode on your application's main class:
```java
@EnableSpringDataWebSupport(pageSerializationMode =
EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
```
### Why does the first page return no results?
JPA pagination is **zero-indexed**. Page `0` is the first page. If your frontend sends `page=1`, you need to pass `page - 1` to Spring Data:
```java
Pageable pageable = PageRequest.of(requestPage - 1, pageSize);
```
### How to avoid the N+1 query problem?
The N+1 problem occurs when JPA executes one query for the parent entity, then N additional queries for each child association.
**Detection** — look for repetitive SQL queries in the logs, or configure `spring.jpa.properties.hibernate.generate_statistics=true` to spot it.
**Fixes:**
| Approach | When to use |
| ------------------------ | ------------------------------------------------- |
| `@EntityGraph` | Declarative, good for entity-specific fetch plans |
| `JOIN FETCH` in `@Query` | Fine-grained control per query |
| `@BatchSize` | Reduces N+1 to N/k+1 by batching |
```java
// Option 1: EntityGraph
@EntityGraph(attributePaths = {"roles", "permissions"})
Optional<User> findById(long id);
// Option 2: JOIN FETCH
@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
Optional<User> findByIdWithRoles(@Param("id") long id);
```
### `findById` vs `getReferenceById` — which one to use?
- **`findById`** — hits the database immediately, returns the entity or `Optional.empty()`. Use this when you need the data.
- **`getReferenceById`** — returns a lazy proxy **without** hitting the database. Throws `EntityNotFoundException` only when you access a non-existent proxy's properties. Use this when you only need the ID to set a foreign key relationship.
```java
// Good: only need the user reference to set a FK
Post post = new Post();
post.setAuthor(userRepository.getReferenceById(userId));
```
### How to fix `LazyInitializationException`?
This happens when you access a lazily-loaded association outside the persistence context (e.g., in a controller or serializer after the transaction has closed).
**Solutions:**
1. **Use `JOIN FETCH` or `@EntityGraph`** to eagerly load needed associations.
2. **Use DTO projections** — return only the fields you need instead of whole entities:
```java
@Query("SELECT new com.example.UserDto(u.id, u.name) FROM User u WHERE u.id = :id")
UserDto findUserDtoById(@Param("id") long id);
```
3. **`@Transactional(readOnly = true)`** on the service method — keep the session open for the entire method scope.
### When should I use `@Transactional(readOnly = true)`?
Use `@Transactional(readOnly = true)` on **read-only** service methods for three benefits:
- Hibernate skips dirty checking (no snapshots, less memory).
- The JDBC driver may route to read replicas.
- It documents the intent clearly.
```java
@Service
public class UserService {
@Transactional(readOnly = true)
public UserDto getUser(long id) { ... }
@Transactional
public UserDto createUser(CreateUserRequest request) { ... }
}
```
### `save()` vs `saveAll()` — which is faster for batch inserts?
`saveAll()` uses a single transaction and can benefit from JDBC batching. Configure the batch size:
```yaml
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 20
order_inserts: true
order_updates: true
```
For large bulk inserts (thousands of rows), consider `JdbcTemplate` batch operations instead — Hibernate's entity management overhead is significant at that scale.
### How to use dynamic queries with `Specification`?
For complex search forms with optional filters, use `JpaSpecificationExecutor`:
```java
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}
// Usage
Specification<User> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (name != null) {
predicates.add(cb.like(root.get("name"), "%" + name + "%"));
}
if (status != null) {
predicates.add(cb.equal(root.get("status"), status));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
Page<User> page = userRepository.findAll(spec, pageable);
```
## Flattening Objects or Maps into JSON with Jackson
When serialising to JSON, you sometimes need to "flatten" a nested object's properties into the parent JSON rather than having them appear as a nested field. Jackson provides two annotations for handling objects and `Map` flattening separately.
### `@JsonUnwrapped` — flattening objects with a known structure
`@JsonUnwrapped` expands the properties of a nested object directly into the parent JSON:
```java
public class Address {
private String street;
private String city;
// getters and setters
}
public class User {
private String name;
@JsonUnwrapped
private Address address;
// getters and setters
}
```
Serialised output:
```json
{
"name": "John Doe",
"street": "Oxford Street",
"city": "London"
}
```
Without `@JsonUnwrapped`, `address` would appear as a nested JSON object: `"address": { "street": "...", "city": "..." }`.
### `@JsonAnyGetter` — flattening a Map with dynamic keys
`@JsonAnyGetter` flattens a `Map`'s key-value pairs directly into the parent JSON, ideal for dynamic or open-ended fields:
```java
public class Product {
private String name;
private Map<String, Object> attributes = new HashMap<>();
@JsonAnyGetter
public Map<String, Object> getAttributes() {
return attributes;
}
// other getters and setters
}
```
With `attributes` containing `{"colour": "red", "size": "XL"}`, the serialised output is:
```json
{
"name": "Example Product",
"colour": "red",
"size": "XL"
}
```
Note that `@JsonAnyGetter` must be placed on the **getter method**, not the field itself. You will also need `@JsonAnySetter` to support deserialisation.
### How to choose
| Scenario | Annotation |
| --------------------------------------------------------------- | ---------------- |
| Flatten nested objects with fixed fields (e.g. address, config) | `@JsonUnwrapped` |
| Flatten dynamic key-value pairs with unknown keys | `@JsonAnyGetter` |
@@ -1,238 +0,0 @@
---
title: Java Development Standards
tags:
- java
- spring-boot
- standards
- best-practice
- backend
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## Java Language and Coding Style
- **Java Version**: Projects should use the latest LTS version of the JDK whenever possible.
- **Naming Conventions**:
- Classes: `PascalCase` (e.g., `UserService`, `OrderController`).
- Methods: `camelCase` (e.g., `getUserById`, `saveOrder`).
- Variables: `camelCase` (e.g., `username`, `statusCode`).
- Constants: `SCREAMING_SNAKE_CASE` (e.g., `DEFAULT_PAGE_SIZE`).
- **Immutability**: Prefer immutability for domain objects and DTOs where possible, using `records` or immutable classes to reduce side effects and improve thread safety.
- **Optional**: Use `Optional<T>` to explicitly handle potentially absent values and avoid `NullPointerException`.
- **Streams API**: Prefer the Java Streams API for collection processing, promoting functional and declarative programming.
- **Exception Handling**:
- Use specific exceptions. Avoid catching generic `Exception`.
- Throw runtime exceptions for unrecoverable errors.
- Define custom checked exceptions for recoverable errors if required by business logic.
- Leverage Spring's `@ControllerAdvice` and `@ExceptionHandler` for centralised global exception handling and consistent API error responses.
- **Code Review**: All backend code must undergo thorough manual code review before merging, particularly following GitFlow principles. IntelliJ IDEA's integrated code analysis tools should be used as a first-pass review.
## Documentation and Comments
- All **public** classes, methods, and significant fields in backend Java code must include comprehensive Javadoc comments.
- Javadoc should explain the purpose, parameters (`@param`), return values (`@return`), and thrown exceptions (`@throws`).
- Javadoc formatting:
- Javadoc must follow a maximum of 100 characters per line (including whitespace for formatting). If the content exceeds 100 characters, break at the last word that ends within the 100-character limit. However, if after the line break only a single word remains at the start of the next line, break one word earlier.
```java
/**
* Enables configuration properties for S3 file storage services. Individual service beans are
* created by their respective service classes to better support conditional configuration.
*/
```
- Use `<p>` to separate paragraphs.
```java
/**
* This is the first paragraph of the Javadoc.
* <p>
* This is the second paragraph of the Javadoc.
*/
```
- Each paragraph must be a grammatically correct and semantically complete paragraph following English sentence conventions.
- All `@param`, `@return`, `@throws`, and `@see` explanations must follow these rules:
- Do not start with a capital letter.
- If the description ends with a declarative sentence, do not use punctuation at the end.
```java
/**
* Returns the greater of two {@code int} values. That is, the
* result is the argument closer to the value of
* {@link Integer#MAX_VALUE}. If the arguments have the same value,
* the result is that same value.
*
* @param a an argument
* @param b another argument
* @return the larger of {@code a} and {@code b}
*/
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
```
## Dependency Management (Gradle)
- **Build File**: `build.gradle.kts` must be well-organised with clear separation of plugins, dependencies, and tasks.
- **Dependency Versions**: Dependency versions must be centrally managed in the `gradle/libs.versions.toml` file to ensure consistency.
- **Plugin Management**: Explicitly declare Gradle plugins and their versions.
- **Avoid Unnecessary Dependencies**: Only include dependencies actually used by the project. Regularly review and clean up unused dependencies.
## API Design (RESTful)
- **RESTful Principles**: Follow RESTful principles:
- **Resources**: Model data as resources identifiable by URI.
- **HTTP Methods**: Use standard HTTP methods appropriately (GET for retrieval, POST for creation, PUT for full update, PATCH for partial update, DELETE for removal).
- **Statelessness**: APIs should be stateless; each request from client to server must contain all the information needed to understand the request.
- **URIs**:
- Use plural nouns for collection resources (e.g., `/users`, `/products`).
- Use hyphens in URIs for readability (e.g., `/user-accounts`).
- Avoid verbs in URIs (e.g., use `/users` instead of `/getAllUsers`).
- **Status Codes**: Use appropriate HTTP status codes to indicate the result of API requests (e.g., `200 OK`, `201 Created`, `204 No Content`, `400 Bad Request`, `401 Unauthorized`, `403 Forbidden`, `404 Not Found`, `500 Internal Server Error`).
- **Response Format**: JSON is the preferred response format.
- **Versioning**: Where version control is required, use the `X-Endpoint-Version` header parameter to control API versions.
## Spring Boot Best Practices & Layered Architecture
- **Layered Architecture (MVC with Manager Layer)**: Our backend applications follow a strict multi-layered architecture, ensuring clear separation of responsibilities and improving maintainability and testability. The layers and their responsibilities are:
- **Controller Layer**: Located in the `controller` package. Responsible for exposing RESTful APIs, handling HTTP requests, and mapping request parameters/bodies to service layer calls. Controllers should remain lightweight, focusing primarily on input validation (using DTOs) and coordinating calls to the `Service` layer.
- **Service Layer**: Located in the `service` package. This layer encapsulates core business logic. Services expose a directly consumable API to the `Controller` layer, abstracting business processes and transaction management. Services coordinate calls to the `Manager` layer to execute business operations.
- **Manager Layer**: Located in the `manager` package. This layer provides atomic business operations that can be composed by the `Service` layer. Managers typically handle more complex business logic and may involve interacting with multiple repositories or other external systems at a finer granularity.
- **Repository Layer (MyBatis)**: Located in the `repository` package and `src/main/resources/repository` (for XML mapping files). This layer is responsible for providing atomic database interaction operations.
**Inter-Layer Communication Strategy**:
- **Components within a layer may only call components in the layer directly below it.**
- **Cross-layer calls are strictly prohibited** (e.g., Controller directly calling Manager, Service directly calling Repository).
- **Upward calls are strictly prohibited** (e.g., Service calling Controller).
- **Lateral calls** (e.g., Service A calling Service B for a different domain) should be carefully considered and typically indicate a need to refactor shared logic into a `Manager` or a dedicated `Service` for that shared concern.
- **Configuration**: Prefer `application.yml` over `application.properties` for configuration properties, offering better readability and hierarchical structure. Use `@ConfigurationProperties` for type-safe configuration.
- **Dependency Injection**: All dependencies should use constructor injection (mandatory dependencies) or setter injection (optional dependencies). Avoid `@Autowired` on fields, as it makes testing more difficult and hides dependencies.
- **Services**: Business logic classes are annotated with `@Service`. Services should be kept lean and focused on orchestrating domain operations, typically involving business logic processing through `Manager` layer components and interaction through `Manager` or directly with MyBatis repositories (if the particular operation does not require intermediate Manager logic, though the Manager layer is preferred for all repository interactions consistent with the layered architecture definition).
- **Repositories (MyBatis & JPA)**:
- **The project uses both MyBatis and JPA for database operations**. Repository interfaces in the `repository` and `mapper` packages define the data access contract.
- Corresponding SQL definitions are managed in XML mapping files located in `src/main/resources/mapper`.
- Data operation conventions:
- Use JPA for simple database operations.
- Use MyBatis for complex database operations.
- When performing paginated queries, page numbers should start from 0 (_compatible with Spring Data JPA_).
- **Data Access Method Naming Conventions**:
- For **querying data lists**: methods **must** start with `selectListBy`, followed by the filter criteria (e.g., `selectListByUserId`, `selectListByDepartmentIdAndStatus`). These methods must also include a `PageRequest` parameter for pagination.
- For querying **single data records**: methods **must** start with `selectOne` (e.g., `selectOneById`, `selectOneByUsername`).
- For **saving new data**: methods **must** be named `save`, and Mapper method return type must be `int` (affected row count).
- For **updating existing data**: methods **must** be named `update`, and Mapper method return type must be `int` (affected row count).
- For **deleting data**: methods **must** start with `deleteBy`, clearly indicating the deletion criteria (e.g., `deleteById`). Mapper method return type must be `int` (affected row count).
- **Controllers**:
- Annotate REST controllers with `@RestController`.
- Use `@GetMapping`, `@PostMapping`, etc. to map HTTP methods (GET, POST, PUT, DELETE) to appropriate controller methods.
- Ensure request and response payloads are clearly defined (DTOs) and documented.
- Controllers should primarily handle HTTP request/response mapping and delegate actual business logic to the service layer.
- **DTOs (Data Transfer Objects)**: Define separate DTOs for request and response bodies to decouple the internal domain model from the API contract. Use validation annotations on DTOs (e.g., `@Valid`, `@NotNull`, `@Size`).
- **Logging (SLF4J & Logback)**:
- All logging in the application uses `org.slf4j.Logger`.
- Configure `application-dev.yml` to set logging levels, appenders, and output formats.
- Log messages should be descriptive and provide sufficient context. Avoid logging sensitive information.
- Use parameterised logging for performance and to prevent string concatenation overhead (e.g., `log.debug("Processing user: {}", userId);`).
- Standard log levels: `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`.
## Project Structure
Backend applications follow a structured Gradle project layout. The core application should be clearly divided into various sub-packages.
```text
backend-application
├── build.gradle.kts // Project's main Gradle build script
├── config // External configuration directory
│ ├── application-dev.yml // Application properties for the development environment
│ └── application-prod.yml.example // Example production properties (to be copied and configured)
├── database // Database-related files
│ └── init.d // Database initialisation scripts
│ └── init-en_GB.sql // SQL script for database schema and initial data using the specified locale (British English)
├── gradle // Gradle Wrapper and configuration files
│ ├── libs.versions.toml // Centralised dependency version management (Gradle Version Catalog)
│ └── wrapper // Gradle Wrapper files
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties // Project-specific Gradle properties
├── gradlew // Gradle Wrapper executable (Linux/macOS)
├── gradlew.bat // Gradle Wrapper executable (Windows)
├── settings.gradle.kts // Gradle settings for multi-project builds (if applicable)
└── src
├── main
│ ├── java
│ │ └── com/onixbyte/application // Application root package
│ │ ├── Application.java // Spring Boot application main entry point
│ │ ├── config // Spring configuration classes
│ │ ├── constant // Classes defining application-wide constants
│ │ ├── controller // REST API endpoints
│ │ ├── domain // Core domain models and related types
│ │ │ ├── common // Common domain objects/utilities
│ │ │ ├── entity // JPA/MyBatis entities representing database tables
│ │ │ ├── model // General-purpose entity class models
│ │ │ ├── view // Data transfer objects specifically for read-only operations (e.g., query results, report structures)
│ │ │ └── web // Data transfer objects specifically for web request/response bodies
│ │ │ ├── request // Request DTOs
│ │ │ └── response // Response DTOs
│ │ ├── exception // Custom application-specific exceptions
│ │ ├── extension // Extension points or custom functionality
│ │ │ ├── jackson // Jackson serialisation/deserialisation extensions
│ │ │ └── redis // Redis-related extensions
│ │ │ └── serializer
│ │ ├── filter // Servlet filters or Spring Security filters
│ │ ├── manager // Business logic coordinators, typically orchestrating multiple services or repositories
│ │ ├── mapper // Data access layer (MyBatis interfaces)
│ │ ├── processor // General-purpose processing components or business workflows
│ │ ├── properties // Classes for type-safe configuration properties (`@ConfigurationProperties`)
│ │ ├── repository // Data access layer (Spring Data JPA interfaces)
│ │ ├── security // Spring Security-specific components
│ │ │ ├── authentication // Custom authentication mechanisms
│ │ │ └── provider // Custom authentication providers
│ │ ├── service // Core business logic (transactional layer)
│ │ ├── utils // General-purpose utility classes
│ │ └── validation // Custom validation logic
│ │ └── group // Validation groups for different contexts (e.g., create, update)
│ └── resources
│ ├── application.yml // Default application properties
│ └── mapper // MyBatis XML mapping files
└── test
└── java
└── com/onixbyte/helix
└── HelixApplicationTests.java // Spring Boot integration tests
```
**Key Observations and Specific Instructions:**
- **External `config` Directory**
- Environment configurations (`application-dev.yml`, `application-prod.yml.example`) are managed in the top-level `config` directory, separate from `src/main/resources`. This facilitates environment-specific property management, allowing different configurations to be mounted or linked at deployment time.
- **Do not upload any configuration files other than `src/main/resources/application.yml` to the Git repository.**
- **Database Initialisation**: The `database/init.d` directory is reserved for SQL scripts, specifically database schema initialisation (`init-en_GB.sql`), which is critical for environment setup and CI/CD pipelines. This structure suggests a "schema-first" or "code-driven schema evolution" approach.
- **`client` Package**: This package is used to provide services for all middleware to the application, such as HTTP, S3 storage, Redis calls, etc. For self-coded functional implementations that need to be added to the Spring context (such as JSON Web Token generation and parsing), placing them in this package is also recommended.
- **MyBatis & Spring Data JPA Integration**
- The `src/main/java/.../mapper` package contains MyBatis mapper interfaces, while the actual SQL definitions reside in `src/main/resources/mapper/*.xml` files. This separation is key to keeping code clean while leveraging MyBatis's powerful XML mapping capabilities.
- The `src/main/java/.../repository` package contains Spring Data JPA interfaces.
- **`domain` Package Granularity**:
- `domain.entity`: Reserved for classes directly mapped to database tables (POJOs for MyBatis).
- `domain.model`: For more general-purpose domain objects or aggregate roots that do not map one-to-one with individual tables.
- `domain.view`: Specifically for Data Transfer Objects (DTOs) used in read-only scenarios (e.g., query results, report structures).
- `domain.web.request` / `domain.web.response`: Clearly separated DTOs for incoming API requests and outgoing API responses, strictly adhering to the API contract and decoupled from internal domain entities.
- **`manager` and `processor` Packages**: These packages imply a layered architecture where "managers" coordinate operations involving multiple services or repositories, while "processors" may handle specific aspects of business processes. It is mandatory to clearly define the responsibilities of classes in these packages to prevent anti-patterns such as "anaemic domain model" or "god objects".
- **`security` Package**: This sub-package contains custom Spring Security components beyond the initial configuration, such as custom authentication types and providers, indicating a tailored security implementation.
- **`properties` Package**: This package is for custom `@ConfigurationProperties` classes, facilitating type-safe access to application settings defined in YML files — well-positioned.
- **`extension` Package**: This is a flexible area for application-specific extensions, such as custom Jackson serialisers or Redis customisations. It should be used sparingly to avoid becoming a "miscellaneous" dumping ground.
## Security (Spring Security)
- **Mandatory Use**: Spring Security is mandatory in all Spring Boot web applications.
- **Authentication & Authorisation**: Configure authentication mechanisms (e.g., OAuth 2.0, JWT, session-based) and authorisation rules in `SecurityConfig.java`.
- **CSRF Protection**: Ensure CSRF protection is enabled for state-modifying operations, unless there is a strong reason to disable it (e.g., stateless APIs with other mechanisms already in place).
- **CORS**: Correctly configure Cross-Origin Resource Sharing (CORS) according to frontend deployment requirements.
- **Third-Party Identity Providers**: When integrating with third-party identity providers (e.g., Microsoft Entra ID), follow best practices for secure token handling and user provisioning. Sensitive credentials must be securely managed (e.g., environment variables, Vault).
- **Input Validation**: Always validate all user input on the server side to prevent common vulnerabilities such as SQL injection, XSS, etc.
- **Content Security Policy (CSP)**: Consider implementing a robust CSP for the frontend to mitigate XSS attacks.
@@ -1,55 +0,0 @@
---
title: Fix macOS Terminal Host Name Showing IP Segments Under Private DNS
tags:
- macos
- dns
- terminal
author:
name: Zihlu Wang
email: real@zihluwang.me
---
In some enterprise or home private network environments, reverse DNS lookups may resolve a device's private IP to a hostname starting with `192`, `172`, or `10`. When this happens, the macOS terminal prompt changes from the normal `user@MacBook-Pro` to something like `user@192-168-1-100`, which can be distracting.
## Cause
When starting a terminal session, macOS performs a reverse DNS lookup to determine the hostname for the current IP address. If the private DNS server returns a hostname derived from IP octets (e.g. `192-168-1-100.example.com`), the system adopts it as the Host Name and the terminal prompt reflects it.
## Fix
Use the built-in `scutil` (System Configuration Utility) to pin the Host Name to your preferred value.
### Check Current State
```shell
# View the current Host Name (may be empty or overridden by DNS)
scutil --get HostName
# View the local Bonjour name
scutil --get LocalHostName
# View the computer name shown in Finder
scutil --get ComputerName
```
### Set the Host Name
```shell
sudo scutil --set HostName "MacBook-Pro"
```
Prefer a name without spaces or special characters, such as `MacBook-Pro`, `My-Mac`, or your device serial number.
### Verify
Open a new terminal window — the value after `@` in the prompt should now show your chosen hostname.
```shell
scutil --get HostName
# Output: MacBook-Pro
```
## Notes
- `HostName` only affects the network-level hostname. `LocalHostName` (Bonjour) and `ComputerName` (Finder display) are managed independently.
- If the issue returns after a reboot, check `/etc/hosts` for conflicting entries or verify whether the DHCP/DNS server continues to push an undesired hostname.
-200
View File
@@ -1,200 +0,0 @@
---
title: MinIO Administration Guide for New Versions
tags:
- minio
- storage
- s3
- devops
- cheatsheet
author:
name: Zihlu Wang
email: real@zihluwang.me
---
In newer versions, MinIO has removed administrative functionality from the Web UI. You now need to use the **MinIO Client (mc)** command-line tool for all management operations.
## Installing MinIO Client (mc)
### Windows:
```powershell
# Download mc.exe
Invoke-WebRequest -Uri "https://dl.min.io/client/mc/release/windows-amd64/mc.exe" -OutFile "mc.exe"
# Or using Chocolatey
choco install minio-client
```
### Linux/macOS:
```bash
# Linux
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/
# macOS
brew install minio/stable/mc
```
## Configuring the MinIO Client
```bash
# Add a MinIO server alias
mc alias set myminio http://localhost:9000 minioadmin minioadmin
# Verify the connection
mc admin info myminio
```
## User Management
### Creating Users
```bash
# Create a new user
mc admin user add myminio newuser newpassword
# List all users
mc admin user list myminio
```
### Creating Access Keys and Secret Keys
```bash
# Create a service account for a user (generates AccessKey/SecretKey)
mc admin user svcacct add myminio newuser
# Or specify custom AccessKey and SecretKey
mc admin user svcacct add myminio newuser --access-key "MYACCESSKEY123" --secret-key "MYSECRETKEY456"
# View a user's service accounts
mc admin user svcacct list myminio newuser
```
## Permission Management
### Creating Policies
```bash
# Create a policy file policy.json
cat > policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::mybucket/*"
]
}
]
}
EOF
# Add the policy
mc admin policy add myminio mypolicy policy.json
# Assign the policy to a user
mc admin policy set myminio mypolicy user=newuser
```
## Bucket Management
```bash
# Create a bucket
mc mb myminio/mybucket
# List buckets
mc ls myminio
# Set bucket policy
mc policy set public myminio/mybucket
```
## Common Administration Commands
```bash
# View server information
mc admin info myminio
# View server configuration
mc admin config get myminio
# Restart the server
mc admin service restart myminio
# View logs
mc admin logs myminio
# View statistics
mc admin prometheus metrics myminio
```
## Practical Script Example
Create an administration script `setup-minio.sh`:
```bash
#!/bin/bash
MINIO_ALIAS="myminio"
MINIO_URL="http://localhost:9000"
ADMIN_USER="minioadmin"
ADMIN_PASS="minioadmin"
# Configure the MinIO client
mc alias set $MINIO_ALIAS $MINIO_URL $ADMIN_USER $ADMIN_PASS
# Create an application user
APP_USER="appuser"
APP_PASS="apppassword"
mc admin user add $MINIO_ALIAS $APP_USER $APP_PASS
# Create a service account and retrieve AccessKey/SecretKey
echo "Creating service account for $APP_USER..."
CREDENTIALS=$(mc admin user svcacct add $MINIO_ALIAS $APP_USER --json)
ACCESS_KEY=$(echo $CREDENTIALS | jq -r '.accessKey')
SECRET_KEY=$(echo $CREDENTIALS | jq -r '.secretKey')
echo "Generated credentials:"
echo "Access Key: $ACCESS_KEY"
echo "Secret Key: $SECRET_KEY"
# Create a bucket
mc mb $MINIO_ALIAS/app-bucket
# Set a read-only policy
mc policy set download $MINIO_ALIAS/app-bucket
```
## Web Console Access
Although administrative functionality has been removed, you can still access the MinIO Console via:
```bash
# Launch the MinIO Console (if installed separately)
mc admin console myminio
```
Alternatively, specify the console address when starting the MinIO server:
```bash
minio server /data --console-address ":9001"
```
## Summary
Managing the new MinIO relies entirely on the `mc` command-line tool:
1. **Install the mc client**
2. **Configure the server alias**
3. **Use `mc admin` commands for user, permission, and bucket management**
4. **Generate AccessKeys/SecretKeys via `mc admin user svcacct`**
While this approach requires command-line operations, it provides more powerful and flexible management capabilities, particularly suited for automated deployment and script-based management.
-31
View File
@@ -1,31 +0,0 @@
---
title: MyBatis Flex
tags:
- java
- mybatis
- spring-boot
- orm
- framework
author:
name: Zihlu Wang
email: real@zihluwang.me
---
> This article only aims at using `MyBatis Flex` in Spring Boot 3
## Installation and Configuration
### Installation
Add the following codes to `libs.versions.toml` in `gradle` :
```toml
[versions]
mybatisFlexVersion = "X.Y.Z"
hikariVersion = "X.Y.Z"
[libraries]
hikari = { group = "com.zaxxer", name = "HikariCP", version.ref = "hikariVersion" }
mybatisFlex-processor = { group = "com.mybatis-flex", name = "mybatis-flex-processor", version.ref = "mybatisFlexVersion" }
mybatisFlex-starter = { group = "com.mybatis-flex", name = "mybatis-flex-spring-boot3-starter", version.ref = "mybatisFlexVersion" }
```
@@ -1,220 +0,0 @@
---
title: Accelerating Fuzzy Search in PostgreSQL with Tokenisation
tags:
- postgresql
- zhparser
- full-text-search
- performance
author:
name: Siu Jam Oh
email: jamo.siu@gmail.com
---
## Background and Challenges
As our business data surpassed **2 million rows**, traditional `LIKE '%keyword%'` fuzzy queries triggered frequent database I/O alerts, with query response times degrading from milliseconds to seconds. To improve search efficiency and support Chinese semantics, we decided to introduce the `zhparser` extension for full-text search.
## Evolution Path and Environment Adaptation
This implementation went through four key phases, each addressing distinct technical challenges:
### CentOS 7.9 VM (Feasibility Validation)
- **Goal**: Validate the compatibility of `SCWS` + `zhparser` on older systems.
- **Core Action**: Manually compiled `postgresql-16.2` from source in a CentOS 7.9 environment, got the extension working end-to-end.
**Conclusion**: Confirmed significant performance improvements from the tokenisation approach for Chinese search.
### Local Docker Container (Containerisation Exploration)
- **Goal**: Initial testing in a complete local system.
- **Core Action**: Injected binary `.so` files via `docker cp`, resolved `ldconfig` dynamic library path visibility issues.
- **Discovery**: Identified that **missing dictionary files** cause tokenisation to degrade into single-character (particle-level) tokenisation — a critical failure point.
### EulerOS 2.0 Test Server (Self-Compiled Environment Adaptation)
- **Goal**: Adapt to the production OS architecture (x86_64) and self-compiled PostgreSQL installation.
- **Core Issue**: Resolved `libscws.so.1` loading errors.
- **Key Solutions**:
- Ensured the `postgres` runtime user has access permissions to `/usr/local/scws/lib`.
- Modified `systemd` service environment variables or created `/usr/lib64` symlinks to force refresh library search paths.
### Production Deployment Preparation (Final Tuning)
- **Goal**: Ensure query stability at 2M+ data volume.
- **Optimisation**: Addressed cases where non-semantic fragments (e.g., "古唐合") returned no results by establishing a "full-text search first + `pg_trgm` index assist" degraded query strategy.
## Core Installation and Configuration Steps (Self-Compiled Environments)
### Installing the `SCWS` Tokenisation Engine
SCWS is the underlying core dependency of `zhparser` and must be installed first.
1. **Download and Extract**: Download the source package (e.g., `scws-1.2.3`).
2. **Compile and Install**:
```bash
./configure --prefix=/usr/local/scws
make && make install
```
3. **Verify the Library**: Ensure `/usr/local/scws/lib/libscws.so.1` exists.
### Compiling and Installing `zhparser`
This step requires `pg_config` from the self-compiled PostgreSQL installation.
1. **Get the Source**: Clone the `zhparser` project from GitHub.
2. **Compile with Specified Path**:
```bash
# Ensure pg_config is in PATH, or specify manually
make USE_PGXS=1 PG_CONFIG=/usr/local/pgsql/bin/pg_config
make USE_PGXS=1 PG_CONFIG=/usr/local/pgsql/bin/pg_config install
```
_Note: The `install` step automatically places `zhparser.so` into PG's `pkglibdir` and extension scripts into the `extension` directory._
### Resolving Dynamic Library Dependencies
1. **Refresh System Cache**:
```bash
echo "/usr/local/scws/lib" > /etc/ld.so.conf.d/scws.conf
ldconfig
```
2. **Permission Check**: Ensure the OS user running `postgres` has `rx` permission on `/usr/local/scws/lib`.
3. **Force Symlink (Alternative)**: If `ldconfig` fails, symlink the library file to `/usr/lib64`.
### Restarting the Database
After modifying system shared library configuration, the PostgreSQL process must be restarted to reload environment variables and linked libraries.
```bash
## Restart using pg_ctl (paths may vary for self-compiled installations)
/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data restart
## Or restart via systemd (if registered as a service)
systemctl restart postgresql
```
### Database-Level Initialisation
Connect to `psql` and run the logical configuration:
```sql
-- Create the extension
CREATE EXTENSION zhparser;
-- Create a full-text search configuration and bind the tokeniser
CREATE TEXT SEARCH CONFIGURATION chinese (PARSER = zhparser);
-- Add token mappings
ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n,v,a,i,e,l,t,b WITH simple;
-- [Optional] Specify the dictionary path for self-compiled installations
-- ALTER DATABASE postgres SET zhparser.dict_path = '/usr/local/scws/etc/dict.utf8.xdb';
```
## Performance Analysis and Pitfalls
### Index Performance Bottleneck Analysis
During testing, it was found that even exact queries suffered from `Bitmap Index Scan` due to an improperly designed composite index (with `create_time` as the leading column), resulting in query times as high as **482 ms**.
- **Improvement**: Created single-column **B-tree** indexes on frequently searched columns, reducing response time to under **10 ms** with `Index Scan`.
### GIN Index and Non-Semantic Matching
- **Cross-Word Truncation**: The tokenisation engine is semantic-based, so truncated strings like "古唐合" may fail to match with `@@` due to tokenisation boundaries.
- **Mitigation Strategy**: Adopt a "waterfall search" approach. Full-text search (FTS) first; if the result set is empty, automatically degrade to `LIKE` fuzzy queries, assisted by `pg_trgm` indexing.
## Final Deployment Strategy: Dual-Track Parallel Retrieval
After analysing the data, we found that approximately 1.24% of account names contain non-standard Simplified Chinese characters, and some company names in the database are unusual enough to cause search failures. We adopted a "stepwise degradation" strategy:
- **Step 1: Full-Text Search (Fast Track)**: Use GIN index for `@@` matching.
- **Step 2: Result Evaluation**: If the result set is empty, check whether the search term contains letters or suspected Traditional Chinese characters.
- **Step 3: Fuzzy Fallback (Safe Fallback)**: Execute `LIKE '%keyword%'`. Although slower, since this serves as a "gap-fill" logic triggered only ~1% of the time, it won't impose overall system pressure.
## Search Optimisation: Integrating a Custom Business Lexicon
To address issues like company brand names being incorrectly segmented by full-text search (e.g., "元一" being split into a numeral and a quantifier), we built an automated maintenance pipeline from data extraction to index rebuild.
### Lexicon Extraction and Preprocessing
Leverage the structural parsing capabilities of **`companynameparser`** to strip region names and industry suffixes, and use **`jieba`** for semantic validation to ensure core brand name integrity:
- **Extraction Logic**: Traverse all `buyer_unique_name` values via a Python script, extracting the core `brand` field.
- **Weight Compensation**: For words prone to fragmentation (e.g., those containing "元", "一", "三"), manually boost TF (term frequency weight) to **50.0–60.0** to ensure their priority overrides built-in quantifier rules.
- **Output Specification**: Produce SCWS-compliant 4-field `UTF-8` text (WORD, TF, IDF, ATTR). Use tab `\t` separators to avoid parsing anomalies.
### Lexicon Compilation and Deployment
<!-- TODO(link): this tip used to offer OnixByte's pre-compiled Windows build of
scws. Both URLs were dead — the GitHub one referenced an account that does not
exist, and the Gitea one is being retired. Pointing at upstream in the meantime;
restore a download link here if one is published again. -->
:::tip
The native scws command-line tool for Windows is compiled with MingW. Source and
build instructions are available from the upstream
[SCWS project](https://github.com/hightman/scws).
:::
Convert the text dictionary to SCWS's efficient binary format (XDB):
1. **Compile the Binary Dictionary**:
```bash
# Use scws-gen-dict to generate an encrypted binary lexicon
/usr/local/scws/bin/scws-gen-dict -i custom_company.txt -o /usr/local/scws/etc/custom_company.xdb -c utf8
```
2. **File Distribution and Permissions**: Move the generated `.xdb` file to the tokenisation data directory and ensure the `postgres` user has read permission:
```bash
cp custom_company.xdb /usr/local/pgsql/share/tsearch_data/
chown postgres:postgres /usr/local/pgsql/share/tsearch_data/custom_company.xdb
```
### Database Parameter Configuration
Modify `postgresql.conf` to force-load `zhparser` and its custom extension lexicon:
```plain text
## Preload the extension library (requires restart to take effect)
shared_preload_libraries = 'zhparser'
## Load custom external dictionaries (use paths relative to tsearch_data)
zhparser.extra_dicts = 'custom_company.xdb'
```
### Hot Index Rebuild and Verification
Since tokenisation rules have changed, existing data must be semantically synchronised via index rebuild:
**Physically Restart the Service**:
```bash
su - postgres -c "/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data restart"
```
**Online Index Rebuild**: Use the `CONCURRENTLY` keyword to refresh the GIN index without blocking DML operations on 400K rows:
```bash
REINDEX INDEX CONCURRENTLY index_name;
```
**Tokenisation Effectiveness Verification**:
```sql
-- Expected part-of-speech should show as n (noun), not x (unknown)
SELECT * FROM ts_debug('chinese', '元一能源');
```
**Optimisation Notes:**
- **Explicit Weight Compensation**: This is the key technique that resolved the "元一" tokenisation failure (shown as `x`).
- **Distinguish Restart from Reload**: `shared_preload_libraries` must be activated via `restart`, not a simple reload.
-22
View File
@@ -1,22 +0,0 @@
---
title: The Quartile Method
tags:
- statistics
- algorithm
- data-analysis
- math
author:
name: Zihlu Wang
email: real@zihluwang.me
---
The quartile method is a commonly used statistical technique primarily employed for data analysis and presentation. The method divides a data set into four equal parts, each containing one quarter of the data. The key statistics include the first quartile (Q1), second quartile (Q2, the median), and third quartile (Q3).
- The first quartile (`Q1`), also known as the lower quartile, is the value at the 25th percentile of a data set sorted in ascending order.
- The second quartile (`Q2`), also known as the median, is the value at the 50th percentile of a data set sorted in ascending order.
- The third quartile (`Q3`), also known as the upper quartile, is the value at the 75th percentile of a data set sorted in ascending order.
- The interquartile range (`IQR`) is the difference between the third quartile and the first quartile, used to measure the dispersion of the middle 50% of data. The formula is IQR = Q3 - Q1. The IQR is commonly used in constructing box plots, an effective way to describe data distribution, particularly useful for identifying outliers.
Upper bound = Q3 + 1.5 × IQR.
Lower bound = Q1 - 1.5 × IQR.
-140
View File
@@ -1,140 +0,0 @@
---
title: Setting Up an LDAP Service
tags:
- ldap
- openldap
- authentication
- linux
- devops
author:
name: Zihlu Wang
email: real@zihluwang.me
---
Setting up an LDAP (Lightweight Directory Access Protocol) server is a core step in implementing enterprise-level **centralised identity authentication** and **permission management**. The most commonly used open-source implementation is **OpenLDAP**.
Below are detailed steps for setting up an OpenLDAP server on Debian/Ubuntu-based Linux systems.
## Environment Preparation and Installation
Before starting, ensure your system packages are up to date and the hostname is properly configured.
```bash
## Update the system
sudo apt update && sudo apt upgrade -y
## Install OpenLDAP and management tools
## slapd is the daemon, ldap-utils is the command-line client tool
sudo apt install slapd ldap-utils -y
```
> During installation, you will be prompted to set the **LDAP administrator password**. Be sure to remember this password — it will be used frequently during configuration.
## Configuring the OpenLDAP Server
Although the installation performs some initial setup, we typically need to customise the configuration for a specific domain (e.g., `example.com`).
### Reconfigure slapd
Run the following command to enter the interactive configuration interface:
```bash
sudo dpkg-reconfigure slapd
```
**Configuration recommendations**:
1. **Omit OpenLDAP server configuration?** Choose **No**.
2. **DNS domain name:** Enter your domain (e.g., `centre.example.com`). This determines your Base DN, such as `dc=centre,dc=example,dc=com`.
3. **Organization name:** Enter your organisation name.
4. **Administrator password:** Enter the administrator password you set earlier.
5. **Database backend:** **MDB** is recommended.
6. **Remove database when slapd is purged?** Choose **No**.
7. **Move old database?** Choose **Yes**.
## Understanding the LDAP Hierarchy
LDAP data is stored in a **tree structure**. To facilitate management, we typically create two "Organisational Units" (OUs): one for users (People) and one for groups (Groups).
## Creating Organisational Units (OUs)
In LDAP, we use **LDIF** (LDAP Data Interchange Format) files to add or modify directory entries.
Create a file named `base.ldif`:
```text
## Create the users organisational unit
dn: ou=people,dc=centre,dc=example,dc=com
objectClass: organizationalUnit
ou: people
## Create the groups organisational unit
dn: ou=groups,dc=centre,dc=example,dc=com
objectClass: organizationalUnit
ou: groups
```
**Import the data:**
```bash
## Import the LDIF file into the database as the administrator
ldapadd -x -D "cn=admin,dc=centre,dc=example,dc=com" -W -f base.ldif
```
## Adding Users and Groups
Create a file named `groups.ldif` to define a new group:
```text
## Define a new group
dn: cn=developers,ou=groups,dc=centre,dc=example,dc=com
objectClass: inetOrgGroup
cn: developers
```
Create a file named `users.ldif` to define a new user:
```text
## Define a new user
dn: uid=jbloggs,ou=people,dc=centre,dc=example,dc=com # The Distinguished Name (DN) of this entry.
objectClass: inetOrgPerson # Object class — inetOrgPerson represents an internet organisation person. It enables common contact attributes such as email, displayName, telephoneNumber, etc.
objectClass: posixAccount # Makes this entry compatible with Unix/Linux system accounts. With it, the user can log into Linux servers and has a UID, GID, and home directory.
objectClass: shadowAccount # Used to manage password ageing (expiry, change warnings, etc.), corresponding to /etc/shadow functionality in Linux.
uid: jbloggs # The user's login name (User ID). This is typically what you enter on the Linux login screen.
sn: Bloggs # Surname. Required by the inetOrgPerson class.
givenName: Joe # First name.
cn: Joe Bloggs # Common Name. The standard display name for an LDAP entry.
displayName: Joe Bloggs # The friendly name displayed in graphical interfaces or email clients.
uidNumber: 10000 # The user's numeric ID in the Linux system.
gidNumber: 5000 # The numeric ID of the user's primary group.
userPassword: {SSHA}password-hash-or-plaintext # The user's encrypted password.
homeDirectory: /home/jbloggs # The path to the user's home directory after logging into Linux.
loginShell: /bin/bash # The shell environment the user gets after logging in.
```
> If you plan to integrate **GitLab**, **Jenkins**, or a **VPN** later, they typically search the `uid` attribute to verify login names.
### FAQ
#### If the user doesn't need server login permissions, can the `objectClass: posixAccount` be removed?
**Yes, it can be completely removed**, but you need to be aware of the "binding relationships" between attributes.
If you only need this user for web application logins (e.g., GitLab, Jenkins, Wiki) or as an email contact, and they don't need to log into a Linux server via SSH or console, removing `posixAccount` is the more standard approach.
When you remove `objectClass: posixAccount`, the following attributes **must also be deleted**, as they belong to that class's mandatory or optional attributes:
- `uidNumber`
- `gidNumber`
- `homeDirectory`
- `loginShell`
Additionally, `shadowAccount` is also typically associated with system logins — if you don't need to manage Linux password expiry policies, it can also be removed.
## Follow-up Suggestions and Management Tools
Command-line LDAP management can be cumbersome. The following tools are recommended for visual administration:
- **phpLDAPAdmin**: A web-based management interface, ideal for quick onboarding.
- **Apache Directory Studio**: A powerful cross-platform desktop client, suitable for complex architecture design.
- **Security Hardening**: By default, LDAP transmits data in plain text. It is recommended to configure **LDAPS (LDAP over SSL/TLS)** to encrypt communication on port 636.
-89
View File
@@ -1,89 +0,0 @@
---
title: Using JSON in MySQL
tags:
- mysql
- json
- database
author:
name: Zihlu Wang
email: real@zihluwang.me
---
MySQL (since version 5.7) **does not directly support a data type called `jsonb`**. `jsonb` is a data type specific to PostgreSQL, which stores JSON data in a binary format with pre-parsing for faster access and manipulation during queries.
However, MySQL's `JSON` data type is functionally and internally similar to PostgreSQL's `jsonb` in many respects, particularly when it comes to querying data.
The `JSON` data type in MySQL was introduced in MySQL 5.7 and has the following characteristics:
1. **Binary Storage**: Like PostgreSQL's `jsonb`, MySQL's `JSON` type data is stored in an **internal binary format** rather than as a plain text string. This makes reading and manipulating JSON data more efficient, as the database does not need to parse text-format JSON strings on every query.
2. **Automatic Validation**: When you insert or update a `JSON` column, MySQL automatically validates that its content is a valid JSON document. If not, it throws an error.
3. **Optimised Storage**: The binary format is also space-optimised, typically more compact than storing JSON in raw text format.
MySQL provides a powerful set of functions and operators for querying and manipulating `JSON` data, very similar to what you'd expect from `jsonb`:
1. **`->` (JSON Extract Operator)**: Extracts a value from a JSON document. It returns a JSON value.
```sql
SELECT my_json_column->'$.key' FROM my_table;
-- Example: extract the name property of a user object
-- Assuming my_json_column stores {'user': {'name': 'Alice'}}
SELECT json_data->'$.user.name' FROM my_table;
```
2. **`->>` (JSON Unquote Operator)**: Extracts a value from a JSON document and **automatically unquotes it**, typically returning a scalar value (e.g., string, number). This is equivalent to `JSON_UNQUOTE(JSON_EXTRACT(...))`.
```sql
SELECT my_json_column->>'$.key' FROM my_table;
-- Example: extract the name property of a user object (returns the string 'Alice' directly)
SELECT json_data->>'$.user.name' FROM my_table;
```
3. **`JSON_EXTRACT(json_doc, path, ...)`**: Explicitly extracts data from a JSON document.
```sql
SELECT JSON_EXTRACT(my_json_column, '$.key') FROM my_table;
```
4. **`JSON_CONTAINS(json_doc, candidate, path)`**: Checks whether a JSON document contains a specified value.
```sql
-- Check whether the tags array contains 'backend'
-- Assuming my_json_column stores {'tags': ['frontend', 'backend']}
SELECT * FROM my_table WHERE JSON_CONTAINS(json_data->'$.tags', '"backend"');
```
5. **`JSON_SEARCH(json_doc, one_or_all, search_str, escape_char, path, ...)`**: Returns the path to a specified string within a JSON document.
```sql
-- Find the path to a value of 'test'
SELECT JSON_SEARCH(my_json_column, 'one', 'test') FROM my_table;
```
6. **`JSON_TABLE(json_doc, path COLUMNS ... )` (MySQL 8.0 and later)**: A very powerful function that "expands" JSON data into relational rows and columns, ideal for complex queries and reporting.
```sql
-- Assuming json_data stores {'items': [{'id': 1, 'name': 'A'}, {'id': 2, 'name': 'B'}]}
SELECT *
FROM my_table,
JSON_TABLE(json_data, '$.items[*]' COLUMNS(
itemId INT PATH '$.id',
itemName VARCHAR(50) PATH '$.name'
)) AS jt;
```
Like PostgreSQL's `jsonb`, efficient querying on JSON fields typically requires indexing. Since the content of JSON fields is dynamic, MySQL does not directly support creating traditional B-tree indexes on a specific internal path of a JSON field. However, you can achieve this through **Virtual Generated Columns**:
1. **Create a Virtual Column**: Define a virtual column whose value is extracted from a specific path in the JSON field.
```sql
ALTER TABLE my_table
ADD COLUMN user_name VARCHAR(255) AS (json_data->>'$.user.name') VIRTUAL;
```
2. **Create an Index on the Virtual Column**: This way, when you query `WHERE json_data->>'$.user.name' = 'Alice'`, the MySQL optimiser can use the `idx_user_name` index, significantly improving query performance.
```sql
CREATE INDEX idx_user_name ON my_table (user_name);
```
Although MySQL does not have the exact name `jsonb`, its `JSON` data type provides highly similar functionality: binary storage optimisation, automatic validation, and rich query operators and functions. By combining virtual columns with indexes, MySQL can deliver query performance and flexibility comparable to PostgreSQL's `jsonb` when working with JSON data.
-84
View File
@@ -1,84 +0,0 @@
---
title: Use Gravatar in First-party Systems
tags:
- gravatar
- avatar
author:
name: Zihlu Wang
email: real@zihluwang.me
---
[Gravatar](https://gravatar.com) (Globally Recognised Avatar) is a service that associates avatar images with email
addresses. When a user registers on your platform with their email, you can display their Gravatar as a default avatar
without building your own image upload and storage pipeline.
## How It Works
Gravatar exposes a simple HTTP endpoint. You compute the SHA256 hash of the user's **lowercased and trimmed** email
address, then embed it in an image URL:
```
https://gravatar.com/avatar/<hash>
```
## Generating the Hash
### Node.js
```js
import { createHash } from "node:crypto"
const email = "user@example.com".trim().toLowerCase()
const hash = createHash("sha256").update(email).digest("hex")
const url = `https://gravatar.com/avatar/${hash}`
```
### Python
```python
import hashlib
email = "user@example.com".strip().lower()
hash = hashlib.sha256(email.encode()).hexdigest()
url = f"https://gravatar.com/avatar/{hash}"
```
### Shell
```shell
echo -n "user@example.com" | tr '[:upper:]' '[:lower:]' | sha256sum | cut -d ' ' -f1
```
## URL Parameters
The `/avatar/` endpoint accepts several query parameters to customise the result:
| Parameter | Description | Example |
| --------- | --------------------------------------- | -------------- |
| `s` | Size in pixels (default: 80) | `?s=200` |
| `d` | Default image when no Gravatar is found | `?d=identicon` |
| `r` | Content rating (`g`, `pg`, `r`, `x`) | `?r=g` |
### Default Image Options (`d`)
- `identicon` — a geometric pattern based on the hash
- `robohash` — a generated robot image
- `retro` — an 8-bit style pixelated face
- `monsterid` — a generated monster cartoon
- `wavatar` — a generated face
- `mp` — a generic silhouette (Mystery Person)
- `blank` — a transparent PNG
- A custom URL (must be URL-encoded)
### Putting It Together
```html
<img
src="https://gravatar.com/avatar/a3b4c5d6e7f8?s=160&d=robohash&r=g"
alt="User avatar"
width="160"
height="160" />
```
Always pass the `d` parameter to avoid broken images for users who have not set up a Gravatar. `identicon` and
`robohash` are popular choices because they generate a unique, recognisable image for every hash.
@@ -1,38 +0,0 @@
---
title: Version Control and Code Review
tags:
- git
- code-review
- best-practice
- workflow
author:
name: Zihlu Wang
email: real@zihluwang.me
---
## GitFlow Workflow
Version control will use the GitFlow branching model, consisting of `main`, `develop`, `feature`, `release`, and
`hotfix` branches.
- `main`: Production-ready code. Only `release` and `hotfix` branches are merged into `main`.
- `develop`: Integration branch for upcoming features.
- `feature/*`: Branches for new features, branched off `develop`.
- `release/*`: Branches for preparing new production releases, branched off `develop`.
- `hotfix/*`: Branches for urgent production bug fixes, branched off `main`.
## Pull Requests/Merge Requests
All code changes (except direct pushes to feature branches) must be submitted via pull requests.
## Code Review
- Each pull request must be reviewed by at least one other developer.
- Reviewers are responsible for checking compliance with these coding standards, code quality, logical correctness, and
test coverage.
- IntelliJ IDEA's integrated code analysis tools should be run locally before creating a PR.
## Commit Messages
Write clear, concise, and descriptive commit messages that explain what was changed and why. If possible, follow the
Conventional Commits format (e.g., `feat: add user registration endpoint`).
@@ -1,85 +0,0 @@
---
title: Backup and Restore Windows Drivers When Reinstalling the System
tags:
- windows
- driver
- backup
- system-reinstall
- dism
author:
name: Zihlu Wang
email: real@zihluwang.me
---
import { Kbd } from "@rspress/core/theme"
One of the most tedious parts of reinstalling Windows is getting all the drivers back — graphics, network, audio, Bluetooth, touchpad, chipset — missing any one of them disrupts your workflow. Windows Update can fetch some automatically, but the versions are often outdated, and niche hardware drivers may not be found at all.
Fortunately, Windows' built-in DISM tool provides driver export and restore functionality. Spend one minute backing up before the reinstall, and you can restore every driver in a single command afterwards.
## Before Reinstalling: Back Up All Drivers
Open a terminal as **Administrator** (PowerShell or Command Prompt) and run:
```powershell
dism /online /export-driver /destination:D:\DriverBackup
```
Replace `D:\DriverBackup` with your desired backup path. It is best to save the backup to an external drive, USB stick, or a non-system partition so it survives the reformat.
The command enumerates every third-party driver installed on the system and exports them to the destination folder. The whole process typically completes within a minute. You should see output similar to:
```
The operation completed successfully.
```
Keep the backup directory safe, and you are ready to reinstall.
### What gets backed up?
The command exports **third-party drivers** — i.e. drivers that are not built into Windows. This covers graphics cards, audio, network adapters, chipsets, Bluetooth, card readers, and any other drivers you have installed over the lifetime of the system. Generic inbox drivers that ship with Windows are skipped, as they will be restored automatically after the reinstall.
## After Reinstalling: Restore All Drivers
### Method 1: One-command restore (recommended)
This is the most convenient approach. Open a terminal as **Administrator** and run:
```powershell
dism /online /add-driver /driver:D:\DriverBackup /recurse
```
The `/recurse` flag tells DISM to search all subfolders within the backup directory and install every driver it finds. **You must restart the computer** afterwards for the changes to take effect.
### Method 2: Via Device Manager (GUI)
If you prefer a graphical interface or only need to restore specific drivers:
1. Press <Kbd>Win</Kbd> + <Kbd>X</Kbd> and select **Device Manager**
2. Locate devices marked with a yellow exclamation mark (missing drivers)
3. Right-click the device and choose **Update driver**
4. Select **Browse my computer for drivers**
5. Point the path to your driver backup directory and tick **Include subfolders**
6. Click Next — Windows will search and install the matching driver
Repeat for each device until all are restored.
## Common Issues
### "The driver package could not be installed"
In rare cases, certain drivers may fail to install via DISM due to signature issues or version incompatibility. As a workaround, locate the corresponding `.inf` file in the backup directory, right-click it, and select **Install**.
### Some devices still lack drivers after restore
DISM only backs up drivers that were currently installed. If a device was already undriven before the backup, its driver will obviously not be included. It is worth checking Device Manager beforehand to ensure all hardware is properly functional.
### Can I restore across Windows versions?
Restoring drivers from Windows 10 to Windows 11 on the same machine is generally viable, but migrating driver backups across different hardware is not recommended. Each machine should have its own independent backup.
## Summary
The key advantage of this workflow is **zero third-party dependencies** — DISM is a built-in Windows component, and no extra software is required. One minute to export before reinstalling, one command to restore afterwards. Far more efficient than manually downloading and installing each driver one by one. Make driver backup a habit before every system reinstall, and each migration will be that much smoother.
> This guide is based on a video tutorial by Douyin user [@科技碎碎念](https://v.douyin.com/Cbf0ruVdbUM/).
-18
View File
@@ -1,18 +0,0 @@
---
title: News
---
## Microsoft Email Delivery Issues (@outlook, @hotmail, @live, @msn and custom domain emails hosted on Microsoft)
> Updated at _19 May 2026_.
**Urgent Action Required for Users with Microsoft-Hosted Emails.**
Please be advised that our automated email service is currently experiencing severe delivery issues to all email addresses hosted by Microsoft. Due to recent aggressive filtering policies by their Exchange Online Protection (EOP), our mail delivery server has been blocked by Microsoft. As a consequence, our system is **unable to send any notifications, password resets, or activation emails** to accounts ending in **@outlook.com**, **@hotmail.com**, **@live.com**, **@msn.com**, or **indeed any custom domains hosted by Microsoft**.
To ensure uninterrupted service, please follow the guidance below based on your account status:
- For Prospective Users (Before Registration): Please **do not** use a Microsoft-hosted email address to register your account. You will not receive the essential activation link. Kindly use an alternative email service provider, such as your corporate email, Gmail, etc. to complete your registration.
- For Existing Registered Users: If your profile is currently linked to an Outlook or Hotmail address, please log in immediately and update your primary email address. You can change this by navigating to your **[User Settings -> Preferences -> Emails](https://github.com/settings/emails)** page. Failing to do so will result in missing critical repository updates, CI/CD pipeline failure logs, and Merge Request notifications.
We are actively working to appeal this block with the Microsoft Postmaster Centre. We sincerely apologise for any inconvenience caused and appreciate your prompt cooperation in updating your contact details.
-14
View File
@@ -1,14 +0,0 @@
[
"index",
{ "type": "section-header", "label": "opensource.group.libraries" },
{ "type": "dir", "name": "onixbyte-toolbox" },
{ "type": "dir", "name": "regions4j" },
{ "type": "dir", "name": "captcha" },
{ "type": "dir", "name": "calendar-toolbox" },
{ "type": "section-header", "label": "opensource.group.applications" },
{ "type": "dir", "name": "helix" },
{ "type": "dir", "name": "delta-force-guide" },
{ "type": "section-header", "label": "opensource.group.tools" },
{ "type": "dir", "name": "claude-code-skill-manager" },
{ "type": "dir", "name": "vite-plugins" }
]
@@ -1,390 +0,0 @@
---
title: Calendar Toolbox API
---
## Package Overview
The library is organised under the base package `com.onixbyte.calendar` with the following structure:
| Package | Description |
| ------------------------------------------ | ------------------------------------------ |
| `com.onixbyte.calendar` | Root calendar object |
| `com.onixbyte.calendar.component` | iCalendar components (VEVENT, VTODO, etc.) |
| `com.onixbyte.calendar.component.property` | Component-level properties |
| `com.onixbyte.calendar.property` | Calendar-level properties |
| `com.onixbyte.calendar.parameter` | iCalendar property parameters |
| `com.onixbyte.calendar.recurrence` | Recurrence rule types |
| `com.onixbyte.calendar.value` | Value types |
| `com.onixbyte.calendar.util` | Formatting utilities |
---
## Calendar
The top-level object representing an iCalendar (RFC 5545) VCALENDAR container.
```java
Calendar calendar = Calendar.builder()
.withCalendarScale(...)
.withMethod(...)
.withProductIdentifier(...)
.withVersion(...)
.withOwner(...)
.withPrimaryCalendar(...)
.withPublishedTTL(...)
.withCalendarDescription(...)
.withCalendarName(...)
.withCalendarId(...)
.withCustomProperties(...)
.withComponents(event, todo, ...)
.build();
String icsContent = calendar.formatted();
```
### Calendar Properties
All calendar-level properties live in `com.onixbyte.calendar.property` and implement `CalendarProperty`.
| Class | iCalendar Property | Description |
| ------------------------ | -------------------- | ----------------------------------------- |
| `CalendarScale` | `CALSCALE` | Calendar system (e.g., GREGORIAN) |
| `Method` | `METHOD` | iCalendar method (e.g., PUBLISH, REQUEST) |
| `ProductIdentifier` | `PRODID` | Product identifier of the calendar |
| `Version` | `VERSION` | iCalendar version (e.g., 2.0) |
| `Owner` | `X-OWNER` | Owner of the calendar |
| `PrimaryCalendar` | `X-PRIMARY-CALENDAR` | Whether this is a primary calendar |
| `PublishedTTL` | `X-PUBLISHED-TTL` | Time-to-live for published calendar |
| `CalendarDescription` | `X-CALENDAR-DESC` | Calendar description |
| `CalendarName` | `X-CALENDAR-NAME` | Calendar name |
| `CalendarId` | `X-CALENDAR-ID` | Calendar identifier |
| `CustomCalendarProperty` | `X-*` | Custom calendar properties |
Custom properties are created using a builder:
```java
var customProp = CustomCalendarProperty.builder()
.withParameters(...)
.build("X-MY-PROPERTY", "my-value");
```
> Note: Custom property names **must** start with `X-`.
---
## Components
Components represent the core entities in an iCalendar object. Each component implements `CalendarComponent` and is rendered between `BEGIN:TYPE` and `END:TYPE` delimiters.
### Event (VEVENT)
Represents a scheduled event.
```java
Event event = Event.builder()
.withDateTimeStamp(dtstamp) // required
.withUniqueIdentifier(uid) // required
.withDateTimeStart(dtstart)
.withDateTimeEnd(dtend) // mutually exclusive with duration
.withDuration(duration) // mutually exclusive with dtend
.withSummary(summary)
.withDescription(description)
.withClassification(classification)
.withDateTimeCreated(dateTimeCreated)
.withGeographicPosition(geoPos)
.withLastModified(lastModified)
.withLocation(location)
.withOrganiser(organiser)
.withPriority(priority)
.withSequenceNumber(seqNumber)
.withStatus(status)
.withTimeTransparency(transparency)
.withUniformResourceLocator(url)
.withRecurrenceId(recurrenceId)
.withRecurrenceRule(rrule)
.withAttachments(attachments)
.withAttendees(attendees)
.withCategories(categories)
.withComments(comments)
.withContacts(contacts)
.withExceptionDateTimes(exDates)
.withRequestStatuses(statuses)
.withRelated(related)
.withResources(resources)
.withRecurrenceDateTimes(rDates)
.build();
```
### Todo (VTODO)
Represents a to-do item or task.
```java
Todo todo = Todo.builder()
.withDateTimeStamp(dtstamp) // required
.withUniqueIdentifier(uid) // required
.withSummary(summary)
.withDateTimeStart(dtstart)
.withDateTimeDue(due) // mutually exclusive with duration
.withDuration(duration) // mutually exclusive with due
.withDateTimeCompleted(completed)
.withPercentComplete(percent)
.withClassification(classification)
.withDateTimeCreated(created)
.withDescription(description)
.withGeographicPosition(geoPos)
.withLastModified(lastModified)
.withLocation(location)
.withOrganiser(organiser)
.withPriority(priority)
.withSequenceNumber(seq)
.withStatus(status)
.withRecurrenceId(recurId)
.withRecurrenceRule(rrule)
.withUniformResourceLocator(url)
.withAttachments(attachments)
.withAttendees(attendees)
.withCategories(categories)
.withComments(comments)
.withContacts(contacts)
.withExceptionDateTimes(exDates)
.withRequestStatuses(statuses)
.withRelatedToList(related)
.withResources(resources)
.withRecurrenceDateTimes(rDates)
.build();
```
### Journal (VJOURNAL)
Represents a journal entry or diary note.
```java
Journal journal = Journal.builder()
.withDateTimeStamp(dtstamp) // required
.withUniqueIdentifier(uid) // required
.withSummary(summary)
.withClassification(classification)
.withDateTimeCreated(created)
.withDateTimeStart(dtstart)
.withLastModified(lastModified)
.withOrganiser(organiser)
.withRecurrenceId(recurId)
.withSequenceNumber(seq)
.withStatus(status)
.withUniformResourceLocator(url)
.withRecurrenceRule(rrule)
.withAttachments(attachments)
.withAttendees(attendees)
.withCategories(categories)
.withComments(comments)
.withContacts(contacts)
.withDescriptions(descriptions)
.withExceptionDateTimes(exDates)
.withRelatedToList(related)
.withRecurrenceDate(rDates)
.withRequestStatuses(statuses)
.build();
```
### FreeBusy (VFREEBUSY)
Represents free/busy time information.
```java
FreeBusy freeBusy = FreeBusy.builder()
.withDateTimeStamp(dtstamp) // required
.withUniqueIdentifier(uid) // required
.withContact(contact)
.withDateTimeStart(dtstart)
.withDateTimeEnd(dtend)
.withOrganiser(organiser)
.withUniformResourceLocator(url)
.withAttendees(attendees)
.withComments(comments)
.withFreeBusyTimes(freeBusyTimes)
.withRequestStatuses(statuses)
.build();
```
### TimeZone (VTIMEZONE)
Defines time zone rules including standard and daylight saving time transitions.
```java
TimeZone timezone = TimeZone.builder()
.withTimeZoneIdentifier(tzId)
.withLastModified(lastModified)
.withTimeZoneUrl(tzUrl)
.withTimeZoneProperties(standardProp, daylightProp)
.build();
```
Time zone properties define the actual transition rules:
```java
TimeZoneProperty standard = TimeZoneProperty.builder()
.withDateTimeStart(dtstart)
.withTimeZoneOffsetTo(offsetTo)
.withTimeZoneOffsetFrom(offsetFrom)
.withRecurrenceRule(rrule)
.withTimeZoneNames(tzName)
.buildStandard(); // or buildDaylight()
TimeZoneProperty daylight = TimeZoneProperty.builder()
.withDateTimeStart(dtstart)
.withTimeZoneOffsetTo(offsetTo)
.withTimeZoneOffsetFrom(offsetFrom)
.buildDaylight();
```
### Alarm (VALARM)
Defines alarm/reminder notifications. Three alarm types are supported:
```java
// Audio alarm
Alarm audioAlarm = Alarm.builder()
.withTrigger(trigger)
.withDuration(duration)
.withRepeatCount(repeatCount)
.withAttachments(audioAttachment)
.buildAudio();
// Display alarm
Alarm displayAlarm = Alarm.builder()
.withTrigger(trigger)
.withDescription(description) // required
.withDuration(duration)
.withRepeatCount(repeatCount)
.buildDisplay();
// Email alarm
Alarm emailAlarm = Alarm.builder()
.withTrigger(trigger)
.withDescription(description) // required
.withSummary(summary) // required
.withAttendees(attendees) // required
.withDuration(duration)
.withRepeatCount(repeatCount)
.buildEmail();
```
---
## Component Properties
Component properties live in `com.onixbyte.calendar.component.property` and implement `ComponentProperty`.
| Class | iCalendar Property | Description |
| ------------------------- | ------------------ | -------------------------------------------------------- |
| `Action` | `ACTION` | Alarm action type (AUDIO, DISPLAY, EMAIL) |
| `Attachment` | `ATTACH` | Document attachment |
| `Attendee` | `ATTENDEE` | Event/task attendee |
| `Categories` | `CATEGORIES` | Categories or tags |
| `Classification` | `CLASS` | Access classification (PUBLIC, PRIVATE, CONFIDENTIAL) |
| `Comment` | `COMMENT` | Comment |
| `Contact` | `CONTACT` | Contact information |
| `DateTimeCompleted` | `COMPLETED` | Completion date-time |
| `DateTimeCreated` | `CREATED` | Creation date-time |
| `DateTimeDue` | `DUE` | Due date-time |
| `DateTimeEnd` | `DTEND` | End date-time |
| `DateTimeStamp` | `DTSTAMP` | Date-time stamp |
| `DateTimeStart` | `DTSTART` | Start date-time |
| `Description` | `DESCRIPTION` | Description |
| `ExceptionDateTimes` | `EXDATE` | Exception date-times |
| `FreeBusyTime` | `FREEBUSY` | Free/busy time periods |
| `GeographicPosition` | `GEO` | Geographic position (lat/long) |
| `LastModified` | `LAST-MODIFIED` | Last modified date-time |
| `Location` | `LOCATION` | Location |
| `Organiser` | `ORGANIZER` | Event organiser |
| `PercentComplete` | `PERCENT-COMPLETE` | Completion percentage |
| `Priority` | `PRIORITY` | Priority level |
| `RecurrenceDateTimes` | `RDATE` | Recurrence date-times |
| `RecurrenceId` | `RECURRENCE-ID` | Recurrence instance identifier |
| `RecurrenceRule` | `RRULE` | Recurrence rule |
| `RelatedTo` | `RELATED-TO` | Related component reference |
| `RepeatCount` | `REPEAT` | Alarm repeat count |
| `RequestStatus` | `REQUEST-STATUS` | Request status |
| `Resources` | `RESOURCES` | Resources |
| `SequenceNumber` | `SEQUENCE` | Sequence number (revision) |
| `Status` | `STATUS` | Component status (TENTATIVE, CONFIRMED, CANCELLED, etc.) |
| `Summary` | `SUMMARY` | Title or summary |
| `TimeTransparency` | `TRANSP` | Time transparency (OPAQUE, TRANSPARENT) |
| `TimeZoneIdentifier` | `TZID` | Time zone identifier |
| `TimeZoneName` | `TZNAME` | Time zone name |
| `TimeZoneOffsetFrom` | `TZOFFSETFROM` | Time zone offset from UTC |
| `TimeZoneOffsetTo` | `TZOFFSETTO` | Time zone offset to UTC |
| `TimeZoneUrl` | `TZURL` | Time zone URL |
| `Trigger` | `TRIGGER` | Alarm trigger |
| `UniformResourceLocator` | `URL` | Associated URL |
| `UniqueIdentifier` | `UID` | Unique identifier |
| `CustomComponentProperty` | `X-*` | Custom component properties |
---
## Parameters
Parameters in `com.onixbyte.calendar.parameter` provide additional qualifiers on component properties.
| Class | iCalendar Parameter | Description |
| ----------------------------- | ------------------- | ------------------------------------------------------ |
| `AlarmTriggerRelationship` | `RELATED` | Trigger relationship (START, END) |
| `AlternateTextRepresentation` | `ALTREP` | Alternate text URI |
| `CalendarUserType` | `CUTYPE` | Calendar user type (INDIVIDUAL, GROUP, RESOURCE, etc.) |
| `CommonName` | `CN` | Common name |
| `Delegatees` | `DELEGATED-TO` | Delegatees |
| `Delegators` | `DELEGATED-FROM` | Delegators |
| `DirectoryEntryReference` | `DIR` | Directory entry URI |
| `FormatType` | `FMTTYPE` | Format type (MIME type) |
| `FreeBusyTimeType` | `FBTYPE` | Free/busy time type |
| `InlineEncoding` | `ENCODING` | Inline encoding (BASE64) |
| `Language` | `LANGUAGE` | Language |
| `Membership` | `MEMBER` | Group membership |
| `ParticipationRole` | `ROLE` | Participation role (CHAIR, REQ-PARTICIPANT, etc.) |
| `ParticipationStatus` | `PARTSTAT` | Participation status (ACCEPTED, DECLINED, etc.) |
| `RecurrenceIdentifierRange` | `RANGE` | Recurrence range (THISANDPRIOR, THISANDFUTURE) |
| `RelationshipType` | `RELTYPE` | Relationship type (PARENT, CHILD, SIBLING) |
| `RsvpExpectation` | `RSVP` | RSVP expectation (TRUE, FALSE) |
| `SentBy` | `SENT-BY` | Sent by |
| `TimeZoneIdentifier` | `TZID` | Time zone identifier |
| `ValueDataType` | `VALUE` | Value data type (DATE, DATE-TIME, etc.) |
---
## Recurrence Types
Located in `com.onixbyte.calendar.recurrence`.
| Class | Description |
| ------------ | ------------------------------------------------------------------------- |
| `Frequency` | Frequency constants (DAILY, WEEKLY, MONTHLY, YEARLY) for recurrence rules |
| `WeekdayNum` | Weekday number for recurrence rules (e.g., 2nd Monday) |
---
## Value Types
Located in `com.onixbyte.calendar.value`.
| Class | Description |
| ------------------- | ---------------------------------------------- |
| `FreeBusyTimeValue` | Represents free/busy time value |
| `PeriodOfTime` | Represents a period of time with start and end |
| `PropertyValue` | Base value type for properties |
| `UtcOffset` | UTC offset value (e.g., `-05:00`, `+01:00`) |
---
## Formatted Output
All components and properties provide a `formatted()` method that returns an iCalendar-compliant string representation. The `Calendar.formatted()` method produces a complete `.ics` output wrapped in `BEGIN:VCALENDAR`/`END:VCALENDAR`.
```java
String ics = calendar.formatted();
// BEGIN:VCALENDAR
// CALSCALE:GREGORIAN
// METHOD:PUBLISH
// ...
// END:VCALENDAR
```
@@ -1,100 +0,0 @@
---
title: Calendar Toolbox
---
import { Tabs, Tab } from "@rspress/core/theme"
## Introduction
Calendar Toolbox is a Java library designed to simplify the creation of `.ics` files, adhering to the iCalendar specification (RFC 5545). This library provides developers with a straightforward API to generate calendar events that can be imported into popular calendar applications such as Microsoft Outlook, Google Calendar, and Apple Calendar.
With Calendar Toolbox, you can effortlessly create, customise, and export calendar event files programmatically, enabling integration of calendar functionalities into your Java applications with minimal effort.
## Features
- **Full RFC 5545 Compliance** — Supports standard iCalendar components including VEVENT, VTODO, VJOURNAL, VFREEBUSY, VTIMEZONE, and VALARM.
- **Fluent Builder Pattern** — All components and properties use builder pattern for readable and intuitive construction.
- **Type-Safe Properties** — Strongly typed properties and parameters ensure correctness at compile time.
- **Custom Extensions** — Support for custom `X-` properties at both calendar and component level.
- **Lightweight** — Zero external dependencies for core functionality.
## Installation
<Tabs>
<Tab label="Gradle with Kotlin DSL">
```kotlin title="build.gradle.kts" dependencies{" "}
{implementation("com.onixbyte:calendar-toolbox:1.1.0")}
```
</Tab>
<Tab label="Maven">
```xml title="pom.xml"
<dependencies>
<dependency>
<groupId>com.onixbyte</groupId>
<artifactId>calendar-toolbox</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
```
</Tab>
</Tabs>
## Quick Start
```java
import com.onixbyte.calendar.Calendar;
import com.onixbyte.calendar.component.Event;
import com.onixbyte.calendar.component.property.*;
import com.onixbyte.calendar.property.*;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
public class CalendarExample {
public static void main(String[] args) {
// Create required date-time properties
var now = ZonedDateTime.now(ZoneOffset.UTC);
var dtstamp = DateTimeStamp.of(now);
var uid = UniqueIdentifier.of("meeting-001@example.com");
var dtstart = DateTimeStart.of(
LocalDateTime.of(2025, 8, 15, 9, 0),
"America/New_York"
);
var dtend = DateTimeEnd.of(
LocalDateTime.of(2025, 8, 15, 10, 0),
"America/New_York"
);
var summary = Summary.of("Team Standup");
var description = Description.of("Daily team standup meeting.");
// Build the event
var event = Event.builder()
.withDateTimeStamp(dtstamp)
.withUniqueIdentifier(uid)
.withDateTimeStart(dtstart)
.withDateTimeEnd(dtend)
.withSummary(summary)
.withDescription(description)
.build();
// Build the calendar and output .ics content
var calendar = Calendar.builder()
.withCalendarScale(CalendarScale.of("GREGORIAN"))
.withMethod(Method.of("PUBLISH"))
.withProductIdentifier(ProductIdentifier.of("-//OnixByte//Calendar Toolbox//EN"))
.withVersion(Version.of("2.0"))
.withComponents(event)
.build();
System.out.println(calendar.formatted());
}
}
```
This produces an `.ics` string that can be saved to a file or sent via email.
## License
Calendar Toolbox is open-source software released under the MIT License.
@@ -1,248 +0,0 @@
---
title: Captcha API
---
## Package Overview
The library is organised under the base package `com.onixbyte.captcha` with the following structure:
| Package | Description |
| -------------------------------------- | ------------------------------------- |
| `com.onixbyte.captcha` | Core interfaces |
| `com.onixbyte.captcha.impl` | Default implementations |
| `com.onixbyte.captcha.text` | Text producer and renderer interfaces |
| `com.onixbyte.captcha.text.impl` | Text producer and renderer impls |
| `com.onixbyte.captcha.text.enums` | Text-related enums |
| `com.onixbyte.captcha.background` | Background producer interface |
| `com.onixbyte.captcha.background.impl` | Background producer implementations |
| `com.onixbyte.captcha.noise` | Noise producer interface |
| `com.onixbyte.captcha.noise.impl` | Noise producer implementations |
| `com.onixbyte.captcha.gimpy` | Distortion engine interface |
| `com.onixbyte.captcha.gimpy.impl` | Distortion engine implementations |
---
## Producer
The top-level interface responsible for creating CAPTCHA images with text drawn on them.
```java
Producer captcha = DefaultCaptchaProducer.builder()
.textProducer(textProducer)
.wordRenderer(wordRenderer)
.gimpyEngine(gimpyEngine)
.backgroundProducer(backgroundProducer)
.width(200)
.height(50)
.borderDrawn(true)
.borderColour(Color.BLACK)
.borderThickness(1)
.build();
String text = captcha.createText();
BufferedImage image = captcha.createImage(text);
```
### DefaultCaptchaProducer Builder
| Method | Default Value | Description |
| -------------------- | --------------------------------------------- | ------------------------------ |
| `textProducer` | `DefaultTextProducer.builder().build()` | The text producer to use |
| `wordRenderer` | `DefaultWordRenderer.builder().build()` | The word renderer to use |
| `gimpyEngine` | `WaterRipple.builder().build()` | The distortion engine to use |
| `backgroundProducer` | `DefaultBackgroundProducer.builder().build()` | The background producer to use |
| `width` | `200` | Width of the CAPTCHA image |
| `height` | `50` | Height of the CAPTCHA image |
| `borderDrawn` | `true` | Whether a border is drawn |
| `borderColour` | `Color.BLACK` | The colour of the border |
| `borderThickness` | `1` | The thickness of the border |
---
## Text
### TextProducer
Interface for creating CAPTCHA text strings.
```java
public interface TextProducer {
String getText();
}
```
#### DefaultTextProducer
Generates random text with configurable length and character set.
```java
DefaultTextProducer textProducer = DefaultTextProducer.builder()
.length(6) // default: 6
.chars("ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray()) // default: a-z, A-Z, 0-9
.build();
```
| Method | Default Value | Description |
| -------- | ----------------------------------- | ------------------------------ |
| `length` | `6` | Length of generated text |
| `chars` | `a-z`, `A-Z`, `0-9` (62 characters) | Characters used for generation |
### WordRenderer
Interface for rendering text onto an image.
```java
public interface WordRenderer {
BufferedImage renderWord(String word, int width, int height);
}
```
#### DefaultWordRenderer
Renders text with configurable font, colour, and spacing.
```java
DefaultWordRenderer wordRenderer = DefaultWordRenderer.builder()
.fontSize(40) // default: 40
.fonts("Arial", "Courier") // default: Arial, Courier
.fontColour(Color.BLACK) // default: Color.BLACK
.charSpace(2) // default: 2
.fontStyle(FontStyle.BOLD) // default: FontStyle.BOLD
.build();
```
| Method | Default Value | Description |
| ------------ | ---------------------- | -------------------------------------- |
| `fontSize` | `40` | Font size in pixels |
| `fonts` | `["Arial", "Courier"]` | Font families to use (randomly chosen) |
| `fontColour` | `Color.BLACK` | Font colour |
| `charSpace` | `2` | Space between characters in pixels |
| `fontStyle` | `FontStyle.BOLD` | Font style to apply |
### FontStyle
Defines the supported font styles for rendering CAPTCHA text.
| Value | Corresponding AWT Constant | Description |
| ------------- | -------------------------- | ------------ | ------------------------ |
| `PLAIN` | `java.awt.Font.PLAIN` | Plain style |
| `BOLD` | `java.awt.Font.BOLD` | Bold style |
| `ITALIC` | `java.awt.Font.ITALIC` | Italic style |
| `BOLD_ITALIC` | `java.awt.Font.BOLD | Font.ITALIC` | Bold and italic combined |
---
## GimpyEngine
Interface for applying image distortion effects.
```java
public interface GimpyEngine {
BufferedImage getDistortedImage(BufferedImage baseImage);
}
```
### WaterRipple
Applies a water ripple distortion effect. Extends `AbstractGimpyEngine` and adds noise automatically.
```java
WaterRipple gimpy = WaterRipple.builder()
.noiseProducer(DefaultNoiseProducer.builder().build()) // default: DefaultNoiseProducer
.build();
```
| Method | Default Value | Description |
| --------------- | ---------------------------------------- | ------------------------- |
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use |
### FishEyeGimpy
Applies a fish-eye distortion effect with horizontal and vertical lines over the image.
```java
FishEyeGimpy gimpy = FishEyeGimpy.builder()
.build();
```
### ShadowGimpy
Applies a shadow and ripple effect. Extends `AbstractGimpyEngine` and adds noise automatically.
```java
ShadowGimpy gimpy = ShadowGimpy.builder()
.noiseProducer(DefaultNoiseProducer.builder().build()) // default: DefaultNoiseProducer
.build();
```
| Method | Default Value | Description |
| --------------- | ---------------------------------------- | ------------------------- |
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use |
---
## Noise
### NoiseProducer
Interface for adding noise to an image.
```java
public interface NoiseProducer {
void makeNoise(BufferedImage image, float factorOne, float factorTwo,
float factorThree, float factorFour);
}
```
#### DefaultNoiseProducer
Adds noise curves with configurable colour.
```java
DefaultNoiseProducer noise = DefaultNoiseProducer.builder()
.noiseColour(Color.BLACK) // default: Color.BLACK
.build();
```
| Method | Default Value | Description |
| ------------- | ------------- | ---------------- |
| `noiseColour` | `Color.BLACK` | The noise colour |
#### NoNoiseProducer
A no-op implementation that does not add any noise to the image.
```java
NoiseProducer noise = NoNoiseProducer.builder()
.build();
```
---
## Background
### BackgroundProducer
Interface for adding background to an image.
```java
public interface BackgroundProducer {
BufferedImage addBackground(BufferedImage image);
}
```
#### DefaultBackgroundProducer
Creates a gradient background with configurable start and end colours.
```java
DefaultBackgroundProducer background = DefaultBackgroundProducer.builder()
.colourFrom(Color.WHITE) // default: Color.LIGHT_GRAY
.colourTo(Color.LIGHT_GRAY) // default: Color.WHITE
.build();
```
| Method | Default Value | Description |
| ------------ | ------------------ | ------------------------------- |
| `colourFrom` | `Color.LIGHT_GRAY` | The starting colour of gradient |
| `colourTo` | `Color.WHITE` | The ending colour of gradient |
@@ -1,86 +0,0 @@
---
title: Captcha
---
import { Tabs, Tab } from "@rspress/core/theme"
## Introduction
Captcha is a Java library for generating CAPTCHA images. This project is a spiritual successor to Google's Kaptcha, modernised for contemporary development practices.
With Captcha, you can easily generate customisable CAPTCHA images with various distortion effects, noise, and background options to integrate into your Java applications.
## Features
- **Easy Integration** — Simple builder-based API for quick setup and configuration.
- **Customisable CAPTCHA Generation** — Configure text length, character sets, font styles, and colours.
- **Multiple Distortion Effects** — Choose from Water Ripple, Fish Eye, and Shadow effects.
- **Configurable Noise** — Add noise lines with custom colours, or disable noise entirely.
- **Gradient Backgrounds** — Customisable gradient background colours.
- **Clean, Modern Codebase** — Well-documented, type-safe, and easy to extend.
## Installation
<Tabs>
<Tab label="Gradle with Kotlin DSL">
```kotlin title="build.gradle.kts" dependencies {implementation("com.onixbyte:captcha:1.0.0")}
```
</Tab>
<Tab label="Maven">
```xml title="pom.xml"
<dependencies>
<dependency>
<groupId>com.onixbyte</groupId>
<artifactId>captcha</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
```
</Tab>
</Tabs>
## Quick Start
```java
import com.onixbyte.captcha.Producer;
import com.onixbyte.captcha.background.BackgroundProducer;
import com.onixbyte.captcha.background.impl.DefaultBackgroundProducer;
import com.onixbyte.captcha.gimpy.GimpyEngine;
import com.onixbyte.captcha.gimpy.impl.WaterRipple;
import com.onixbyte.captcha.impl.DefaultCaptchaProducer;
import com.onixbyte.captcha.noise.NoiseProducer;
import com.onixbyte.captcha.noise.impl.DefaultNoiseProducer;
import com.onixbyte.captcha.text.TextProducer;
import com.onixbyte.captcha.text.WordRenderer;
import com.onixbyte.captcha.text.impl.DefaultTextProducer;
import com.onixbyte.captcha.text.impl.DefaultWordRenderer;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class CaptchaExample {
public static void main(String[] args) throws IOException {
// Create a CAPTCHA producer with default configurations
Producer captcha = DefaultCaptchaProducer.builder().build();
// Generate text and create the image
String captchaText = captcha.createText();
BufferedImage captchaImage = captcha.createImage(captchaText);
// Save the image to a file
ImageIO.write(captchaImage, "png", new File("captcha.png"));
System.out.println("CAPTCHA text: " + captchaText);
}
}
```
This generates a CAPTCHA image with a 6-character random text and saves it as a PNG file.
## License
Captcha is open-source software released under the MIT License.
@@ -1,217 +0,0 @@
---
title: Claude Code Skills Manager
---
import { Tabs, Tab } from "@rspress/core/theme"
## Introduction
Claude Code Skills Manager (`ccsm`) is a CLI tool for discovering, installing, and managing Claude Code skills from Git-based marketplace repositories. It lets you register any public Git repository that exposes a `skills.json` manifest as a skill source, then search, install, update, and remove skills through a single command-line interface.
Skills are deployed directly into Claude Code's own skills directory (`~/.claude/skills/`), where Claude Code picks them up automatically at startup.
## Installation
<Tabs>
<Tab label="Pre-built binaries">
Download the latest binary for your platform from the [Releases page](https://onixbyte.dev/onixbyte/claude-code-skill-manager/releases).
| Platform | Binary |
|----------|--------|
| Linux (amd64) | `ccsm-linux-amd64` |
| macOS (amd64) | `ccsm-darwin-amd64` |
| Windows (amd64) | `ccsm-windows-amd64.exe` |
Place the binary somewhere on your `PATH` and rename it to `ccsm` (or `ccsm.exe` on Windows).
</Tab>
<Tab label="Cargo install">
```bash
git clone https://onixbyte.dev/onixbyte/claude-code-skill-manager.git
cd claude-code-skill-manager
cargo install --path .
```
Requires a Rust toolchain (install via [rustup.rs](https://rustup.rs)).
</Tab>
<Tab label="Build from source">
```bash
git clone https://onixbyte.dev/onixbyte/claude-code-skill-manager.git
cd claude-code-skill-manager
cargo build --release
```
The binary will be at `target/release/ccsm` (or `ccsm.exe` on Windows).
</Tab>
</Tabs>
## Quick Start
### Registering a marketplace
A marketplace is any public Git repository with a `skills.json` manifest at its root. Register one with:
```bash
ccsm marketplace add https://github.com/example/skills
```
CCSM fetches the manifest and caches it locally. You can register as many marketplaces as you need.
### Browsing and installing skills
```bash
# Search across all registered marketplaces
ccsm search
# Search with a keyword filter
ccsm search git
# Install a skill by name
ccsm install my-awesome-skill
# If the same name exists in multiple marketplaces, disambiguate
ccsm install my-skill --marketplace my-marketplace
```
### Managing installed skills
```bash
# List installed skills
ccsm list
# Show details about an installed skill
ccsm info my-awesome-skill
# Update all installed skills to their latest versions
ccsm update
# Update a specific skill
ccsm update my-awesome-skill
# Remove a skill
ccsm remove my-awesome-skill
```
### Marketplace management
```bash
# List all registered marketplaces
ccsm marketplace list
# Refresh cached manifests (all marketplaces)
ccsm marketplace update
# Refresh a specific marketplace
ccsm marketplace update my-marketplace
# Remove a marketplace
ccsm marketplace remove my-marketplace
```
### Install modes
CCSM supports two deploy modes:
| Mode | Behaviour | Use case |
| ---------------- | --------------------------------------------------------- | --------------------------------------------------------------------- |
| `copy` (default) | Files are physically copied to `~/.claude/skills/<name>/` | Normal use — safe and self-contained |
| `link` | A symlink is created pointing to the local store | Skill development — changes to source files are picked up immediately |
Set the default mode in your config, or override per install:
```bash
ccsm install my-skill --link
```
## Creating Your Own Skill Marketplace
A marketplace is simply a public Git repository containing a `skills.json` manifest. There is no server component, no authentication, and no API key — CCSM reads the manifest directly from the Git forge's raw file endpoint.
### Marketplace repository structure
```
your-marketplace-repo/
skills.json # Required: the manifest
README.md # Optional
```
### The `skills.json` manifest
```json
{
"name": "My Marketplace",
"version": "1.0.0",
"skills": [
{
"name": "my-skill",
"description": "A short description shown in search results",
"repository": "https://github.com/user/skill-repo",
"branch": "main"
},
{
"name": "another-skill",
"description": "A skill that lives in a monorepo subdirectory",
"repository": "https://github.com/user/monorepo",
"branch": "main",
"path": "skills/another-skill"
}
]
}
```
**Top-level fields:**
| Field | Required | Description |
| --------- | -------- | ---------------------------------------------- |
| `name` | Yes | Marketplace display name |
| `version` | No | Arbitrary version string for your own tracking |
| `skills` | No | Array of skill entries (defaults to `[]`) |
**Skill entry fields:**
| Field | Required | Description |
| ------------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `name` | Yes | Unique skill identifier (used with `ccsm install <name>`) |
| `description` | Yes | One-line summary shown in `ccsm search` output |
| `repository` | Yes | Public Git repository URL for the skill |
| `branch` | No | Git branch to install from (tries `main` first, falls back to `master`) |
| `path` | No | Subdirectory within the repository where the skill files live — enables monorepos hosting multiple skills |
### How it works
1. **Manifest fetching** — CCSM constructs a raw-file URL from the marketplace repository URL and fetches `skills.json` from either the `main` or `master` branch. This works with GitHub, Gitea, GitLab, and any Git forge that exposes raw file endpoints.
2. **Skill downloading** — When a user installs a skill, CCSM downloads the skill repository archive (ZIP or tar.gz) from the Git forge, extracts it, and deploys the files to `~/.claude/skills/<name>/`.
3. **Monorepo support** — If a skill entry specifies a `path`, CCSM navigates into that subdirectory after extraction before deploying, so a single repository can host multiple skills.
### What goes in a skill repository
Each skill repository is a standard Claude Code skill directory. At minimum it should contain a `SKILL.md` manifest file:
```
my-skill-repo/
SKILL.md # Claude Code skill manifest
script.py # Supporting files
templates/
template.txt
```
### Publishing steps
1. Create a **public** Git repository with a `skills.json` at the root, following the format above.
2. Push it.
3. Anyone can register it with `ccsm marketplace add <url>`.
## How CCSM Stores Data
| Path | Purpose |
| ------------------------------ | ---------------------------------------------------------- |
| `~/.claude/skills/` | Claude Code's skills directory — where skills are deployed |
| `~/.config/ccsm/config.json` | CCSM configuration (registered marketplaces, default mode) |
| `~/.config/ccsm/marketplaces/` | Cached marketplace manifests |
| `~/.config/ccsm/skills/` | Downloaded skill files (local store) |
On Windows, `~/.config/` resolves to `C:\Users\<user>\AppData\Roaming\`.
## Licence
Claude Code Skills Manager is open-source software released under the MIT Licence.
@@ -1,91 +0,0 @@
---
title: Delta Force Guide API
---
## Backend API Overview
The backend is a Spring Boot 3 application organised under the base package `com.onixbyte.deltaforceguide`:
| Package | Description |
| ------------- | -------------------------------------------- |
| `controller` | REST API endpoint controllers |
| `service` | Business logic layer |
| `repository` | MyBatis data access layer |
| `domain` | JPA entity and domain objects |
| `config` | Spring configuration (security, cache, etc.) |
| `properties` | Configuration property classes |
| `enumeration` | Enumerated types |
| `shared` | Shared utilities |
## API Endpoints
### Authentication
| Method | Path | Description | Auth |
| ------ | ------------------- | ------------------------ | ---- |
| `POST` | `/api/auth/login` | Log in with credentials | No |
| `POST` | `/api/auth/refresh` | Refresh access token | No |
| `GET` | `/api/auth/profile` | Get current user profile | Yes |
### Firearms
| Method | Path | Description |
| -------- | ------------------- | --------------------------------------------- |
| `GET` | `/api/firearms` | List firearms (paginated, filterable by type) |
| `GET` | `/api/firearms/:id` | Get firearm by ID |
| `POST` | `/api/firearms` | Create a new firearm |
| `PUT` | `/api/firearms/:id` | Update an existing firearm |
| `DELETE` | `/api/firearms/:id` | Remove a firearm |
### Modifications
| Method | Path | Description |
| -------- | --------------------------------- | -------------------------------------------------------------- |
| `GET` | `/api/modifications` | List modifications (paginated, filterable by firearm and tags) |
| `GET` | `/api/modifications/:id` | Get modification by ID |
| `POST` | `/api/modifications` | Create a modification |
| `POST` | `/api/modifications/batch` | Batch create modifications |
| `PUT` | `/api/modifications/:id` | Update a modification |
| `DELETE` | `/api/modifications/:id` | Remove a modification |
| `DELETE` | `/api/modifications/batch-delete` | Batch remove modifications |
### Tags
| Method | Path | Description |
| ------ | ----------- | ------------- |
| `GET` | `/api/tags` | List all tags |
## Frontend Routes
| Path | Layout | Page | Description |
| ------------ | ------------ | ------------------ | ----------------------------- |
| `/` | Hero Layout | Firearms Browser | Home page — firearm list |
| `/firearms` | Hero Layout | Firearms Browser | Firearm detail and search |
| `/mod-codes` | Hero Layout | Modification Codes | Modification code library |
| `/legal` | Hero Layout | Legal | Legal and privacy information |
| `/login` | Empty Layout | Login | User authentication page |
## Frontend State Management
Redux Toolkit with Redux Persist manages:
- **Auth state** — Access tokens, refresh tokens, user profile
- **Settings state** — User preferences persisted to local storage
## API Client
The frontend uses a shared `WebClient` wrapper around Axios that handles:
- Base URL configuration (pointing to the backend API)
- JWT access token injection via request interceptors
- Automatic token refresh on 401 responses
## Infrastructure
| Component | Technology |
| ---------------- | ----------------------------------- |
| Database | PostgreSQL (with Flyway migrations) |
| Cache | Redis (session/token storage) |
| File Storage | AWS S3 (firearm images) |
| Containerisation | Docker |
| Deployment | Docker Compose behind Nginx |
@@ -1,46 +0,0 @@
---
title: Delta Force Guide
---
## Introduction
Delta Force Guide is a full-stack web application providing a searchable library of firearm modification codes for the game Delta Force. It consists of two components:
- **Web Frontend** — A React-based SPA for browsing, filtering, and copying modification codes.
- **Backend Server** — A Spring Boot REST API handling data persistence, authentication, and content management.
The live site is available at **[dfguide.onixbyte.cn](https://dfguide.onixbyte.cn)**.
## Features
- **Firearm Browser** — Browse a curated list of in-game firearms with detailed stats.
- **Modification Code Library** — Search and filter modification codes by weapon, mode, and tags.
- **One-Click Copy** — Copy modification codes directly from the interface.
- **Efficient Rendering** — Large lists rendered smoothly with window virtualisation.
- **Authentication** — User login with JWT-based auth and CAPTCHA verification.
- **Admin Panel** — Full CRUD operations for firearms and modification data.
- **Static-Friendly** — Frontend deployed as a static site with API proxy.
## Architecture
| Component | Tech Stack |
| ------------ | --------------------------------------------------------------------------------------------------- |
| **Frontend** | React 19, TypeScript, Tailwind CSS 4, Ant Design 6, React Router 7, Redux Toolkit, TanStack Virtual |
| **Backend** | Spring Boot 3, Java 21, PostgreSQL, MyBatis, Flyway, Redis |
| **Auth** | JWT (access + refresh tokens), CAPTCHA integration |
| **Storage** | AWS S3 for image uploads |
The frontend is a static site served by Vite, communicating with the Spring Boot REST API backend. Authentication uses JWT access and refresh tokens with Redis-backed refresh token management.
## Repositories
- [delta-force-guide-server](https://github.com/onixbyte/delta-force-guide-server) — Spring Boot backend
- [delta-force-guide-web](https://github.com/onixbyte/delta-force-guide-web) — React frontend
## Quick Links
- **Live Site:** [dfguide.onixbyte.cn](https://dfguide.onixbyte.cn)
## License
Delta Force Guide is open-source software released under the MIT Licence.
@@ -1,73 +0,0 @@
---
title: Helix
---
## Introduction
Helix is a full-stack enterprise application template designed to accelerate the development of production-grade business applications. It provides a pre-built foundation with all the essential features an enterprise application needs, allowing teams to focus on business logic rather than infrastructure.
The project consists of two components:
- **Helix Server** — A Spring Boot backend providing REST APIs, authentication, authorisation, and data management.
- **Helix Web** — A React-based SPA frontend with a modern UI, role-based routing, and enterprise identity integration.
Helix is intended to be used as a starting point. Fork or clone it and begin building your own domain-specific features on top of the ready-made foundation.
## Features
### Backend (Helix Server)
- **Authentication & Authorisation** — JWT-based access and refresh token flow with Spring Security. Supports Microsoft Entra ID (Azure AD) integration.
- **Role-Based Access Control** — Fine-grained permissions via roles, authorities, and menu-level access control.
- **User & Organisation Management** — Full CRUD for users, departments, positions, and roles.
- **Menu Management** — Hierarchical menu tree with configurable visibility per role.
- **Asset Management** — File uploads to AWS S3 with CDN-ready prefix handling.
- **Settings Management** — Typed application settings persisted to database.
- **Captcha Integration** — Image CAPTCHA verification for login and registration flows.
- **Redis Caching** — High-performance caching with configurable TTL and custom serialisers.
- **Data Access** — Hybrid persistence with Spring Data JPA and MyBatis.
- **Validation** — Bean Validation groups with custom validation constraints.
### Frontend (Helix Web)
- **Enterprise Identity** — Microsoft Entra ID login via MSAL. Extensible social login provider architecture (Microsoft, Google, GitLab, Slack, Discord, DingTalk, Lark, WeCom, email/password).
- **Dashboard Layout** — Responsive admin dashboard with collapsible sidebar and role-based menu rendering.
- **User Management** — Data table with search, pagination, add/edit dialogues, role assignment, and phone number validation.
- **Role & Menu Management** — Role CRUD with authority assignment. Tree-based menu configuration.
- **Department & Position** — Organisation hierarchy management interfaces.
- **Protected Routing** — Route guards checking authentication and authorisation before page load.
- **State Management** — Redux Toolkit with Redux Persist for auth token persistence.
- **HTTP Client** — Axios wrapper with automatic JWT token injection and 401 refresh handling.
## Architecture
| Layer | Technology |
| ------------ | ---------------------------------------------------------------------------------------------- |
| **Frontend** | React 19, TypeScript, Tailwind CSS 4, Ant Design 6, React Router 7, Redux Toolkit, Axios, MSAL |
| **Backend** | Spring Boot 3.5, Java 21, Spring Security, MyBatis, Spring Data JPA |
| **Database** | PostgreSQL (with Flyway migrations) |
| **Cache** | Redis (session/token storage) |
| **Storage** | AWS S3 (asset uploads) |
The frontend communicates with the backend via a RESTful JSON API. Authentication flow uses JWT access tokens (short-lived) with refresh tokens stored in Redis. Microsoft Entra ID integration is handled client-side via MSAL and validated server-side.
## Repositories
Helix is a monorepo — both halves live in the same repository.
- [server/](https://github.com/onixbyte-opensource/helix/tree/main/server) — Spring Boot backend
- [web/](https://github.com/onixbyte-opensource/helix/tree/main/web) — React frontend
## Getting Started
Helix is designed as a template project. To start a new project:
1. Fork or clone the [Helix repository](https://github.com/onixbyte-opensource/helix).
2. Configure your database, Redis, and S3 settings in the backend.
3. Configure your authentication providers and API base URL in the frontend.
4. Run the backend: `./gradlew bootRun`
5. Run the frontend: `pnpm install && pnpm dev`
## License
Helix is open-source software released under the MIT License.
-25
View File
@@ -1,25 +0,0 @@
---
title: Open Source
---
Open-source projects developed by OnixByte.
## Libraries & Toolkits
- **[OnixByte Toolbox](/opensource-projects/onixbyte-toolbox)** — A development kit for Java applications: AES, hashing, Base64, collection utilities, crypto key loaders, statistical calculators, identity generators, and generic tuples.
- **[regions4j](/opensource-projects/regions4j)** — Type-safe ISO 3166-1 regional metadata as a Java enum with O(1) lookups, E.164 dialling codes, and locale tags.
- **[Captcha](/opensource-projects/captcha)** — A modern Java CAPTCHA generation library with water ripple, fish-eye, and shadow distortion effects.
- **[Calendar Toolbox](/opensource-projects/calendar-toolbox)** — A Java library for creating `.ics` files conforming to the iCalendar specification (RFC 5545).
## Full-Stack Applications
- **[Helix](/opensource-projects/helix)** — An enterprise application template with Spring Boot backend and React frontend, featuring JWT auth, RBAC, user and organisation management, and Microsoft Entra ID integration.
- **[Delta Force Guide](/opensource-projects/delta-force-guide)** — A web app providing a searchable library of firearm modification codes for the game _Delta Force: Havvk Ops_, with a React SPA and Spring Boot REST API.
## Developer Tools
- **[Claude Code Skills Manager](/opensource-projects/claude-code-skill-manager)** — A CLI tool for discovering, installing, and managing Claude Code skills from Git-based marketplace repositories.
## Vite Plugins
- **[Port Checker](/opensource-projects/vite-plugins/port-checker)** — Warns when the Vite dev server listens on a browser-restricted port, preventing silent connection failures.
@@ -1,209 +0,0 @@
---
title: OnixByte Toolbox API
---
## Package Overview
All modules are published under the `com.onixbyte` group ID. Each module has its own base package:
| Module | Maven Artifact | Base Package |
| ------------------ | -------------------- | -------------------------------- |
| Common Toolbox | `common-toolbox` | `com.onixbyte.common` |
| Crypto Toolbox | `crypto-toolbox` | `com.onixbyte.crypto` |
| Math Toolbox | `math-toolbox` | `com.onixbyte.math` |
| Tuple | `tuple` | `com.onixbyte.tuple` |
| Identity Generator | `identity-generator` | `com.onixbyte.identitygenerator` |
---
## Common Toolbox
### Adapter
`ObjectMapAdapter` — Converts between objects and maps. Useful for serialisation or data transformation scenarios where key-value representations are preferred.
```java
import com.onixbyte.common.adapter.ObjectMapAdapter;
```
### Utility Classes
| Class | Description |
| ---------------- | ---------------------------------------------------------- |
| `AesUtil` | AES symmetric encryption/decryption with key generation |
| `Base64Util` | Base64 encoding and decoding with multiple charset support |
| `BoolUtil` | Boolean parsing and conversion utilities |
| `BranchUtil` | Conditional branching helpers with functional-style chains |
| `CollectionUtil` | Collection manipulation — grouping, partitioning, merging |
| `HashUtil` | Hashing utilities supporting MD5, SHA-1, SHA-256, SHA-512 |
| `MapUtil` | Map builder, merge, and transformation helpers |
| `RangeUtil` | Numeric range operations with boundary handling |
**AesUtil Example:**
```java
var key = AesUtil.generateKey();
var encrypted = AesUtil.encrypt("hello world", key);
var decrypted = AesUtil.decrypt(encrypted, key);
```
**HashUtil Example:**
```java
var md5 = HashUtil.md5("hello world");
var sha256 = HashUtil.sha256("hello world");
var fileSha = HashUtil.sha256(new File("data.bin"));
```
**CollectionUtil Example:**
```java
var partitioned = CollectionUtil.partition(list, 100);
var grouped = CollectionUtil.groupBy(users, User::getDepartment);
```
---
## Crypto Toolbox
Provides a clean abstraction for loading cryptographic keys in PEM format. Supports both RSA and ECDSA algorithms.
### Key Loaders
| Class | Description |
| --------------------- | ---------------------------------------- |
| `RSAPrivateKeyLoader` | Load RSA private keys from PEM strings |
| `RSAPublicKeyLoader` | Load RSA public keys from PEM strings |
| `ECPrivateKeyLoader` | Load ECDSA private keys from PEM strings |
| `ECPublicKeyLoader` | Load ECDSA public keys from PEM strings |
All key loaders implement either `PrivateKeyLoader` or `PublicKeyLoader` interfaces.
**RSA Example:**
```java
var privateKey = new RSAPrivateKeyLoader().load(pemString);
var publicKey = new RSAPublicKeyLoader().load(pemString);
```
**ECDSA Example:**
```java
var ecPrivateKey = new ECPrivateKeyLoader().load(pemString);
var ecPublicKey = new ECPublicKeyLoader().load(pemString);
```
### Utility
`CryptoUtil` — Provides convenient methods for digital signature creation and verification, as well as general-purpose cryptographic helpers.
### Exceptions
| Class | Description |
| --------------------- | ---------------------------------------------- |
| `KeyLoadingException` | Thrown when a PEM-formatted key fails to parse |
---
## Math Toolbox
Statistical computation utilities for working with numeric datasets.
### Core Classes
| Class | Description |
| ---------------------- | ------------------------------------------------------------------------------------ |
| `Calculator` | Statistical calculator for mean, median, variance, standard deviation, sum, min, max |
| `PercentileCalculator` | Percentile computation with configurable interpolation strategies |
**Calculator Example:**
```java
var stats = new Calculator(Arrays.asList(1.0, 2.0, 3.0, 4.0, 5.0));
double mean = stats.getMean();
double median = stats.getMedian();
double stdDev = stats.getStandardDeviation();
```
**PercentileCalculator Example:**
```java
var calculator = new PercentileCalculator(data);
double p95 = calculator.calculate(95);
double p99 = calculator.calculate(99);
```
### Model
| Class | Description |
| ---------------- | ------------------------------------------------------------------ |
| `QuartileBounds` | Holds Q1, median (Q2), Q3 values along with IQR and whisker bounds |
---
## Tuple
Lightweight generic tuple types for when you need to return multiple values without defining a dedicated class.
### Classes
| Class | Type Parameters | Mutability |
| -------------------------- | --------------- | ---------- |
| `Tuple<A, B>` | Two elements | Mutable |
| `ImmutableTuple<A, B>` | Two elements | Immutable |
| `Triple<A, B, C>` | Three elements | Mutable |
| `ImmutableTriple<A, B, C>` | Three elements | Immutable |
**Example:**
```java
var pair = Tuple.of("key", 42);
var triple = Triple.of("id", "name", 100);
var immutable = ImmutableTuple.of("constant", true);
// Accessors
String first = pair.getFirst();
Integer second = pair.getSecond();
// Triple accessors
String a = triple.getFirst();
String b = triple.getSecond();
Integer c = triple.getThird();
```
---
## Identity Generator
Generates unique identifiers suitable for use as database primary keys or distributed IDs.
### Interface
`IdentityGenerator<T>` — Generates identities of type `T`.
### Implementations
| Class | Description |
| ---------------------------- | ------------------------------------------------------------------- |
| `SequentialUuidGenerator` | Generates time-ordered, sequential UUIDs (optimised for DB indexes) |
| `SnowflakeIdentityGenerator` | Snowflake-style distributed unique ID generation |
**Sequential UUID:**
```java
var generator = new SequentialUuidGenerator();
UUID id = generator.nextId(); // time-ordered UUID, B-tree friendly
```
**Snowflake:**
```java
var generator = new SnowflakeIdentityGenerator(workerId, datacenterId);
long id = generator.nextId(); // 64-bit snowflake ID
```
### Exceptions
| Class | Description |
| ----------------- | ------------------------------------------------------------------------ |
| `TimingException` | Thrown when the system clock moves backwards, compromising ID uniqueness |
@@ -1,54 +0,0 @@
---
title: OnixByte Toolbox
---
import { Tabs, Tab } from "@rspress/core/theme"
## Introduction
OnixByte Toolbox is a Development Kit for Java applications that provides a set of convenient, reusable tools for writing code efficiently. It is designed and maintained by OnixByte as a monorepo containing multiple purpose-built libraries, each addressing a specific development need — from common utilities and cryptography to mathematical computation and identity generation.
The toolkit is available on Maven Central under the `com.onixbyte` group ID. Each module is independently versioned and can be included in any Java 17+ project.
## Modules
| Module | Artifact ID | Description |
| ------------------ | -------------------- | ------------------------------------------------------------------------- |
| Common Toolbox | `common-toolbox` | Everyday utilities — AES, hashing, Base64, collections, branching, ranges |
| Crypto Toolbox | `crypto-toolbox` | Public/private key loading for RSA and ECDSA algorithms |
| Math Toolbox | `math-toolbox` | Statistical calculation, percentile analysis, quartile computation |
| Tuple | `tuple` | Generic 2-tuple and 3-tuple with immutable variants |
| Identity Generator | `identity-generator` | Snowflake-style and sequential UUID identity generation |
| Version Catalogue | `version-catalogue` | Centralised Gradle version catalogue for OnixByte projects |
## Installation
Add the desired module as a dependency in your build config:
<Tabs>
<Tab label="Gradle with Kotlin DSL">
```kotlin title="build.gradle.kts"
dependencies {
implementation("com.onixbyte:$artefactId:$version")
}
```
</Tab>
<Tab label="Maven">
```xml title="pom.xml"
<dependency>
<groupId>com.onixbyte</groupId>
<artifactId>${artefactId}</artifactId>
<version>${version}</version>
</dependency>
```
</Tab>
</Tabs>
## Requirements
- Java 17 or later
## License
OnixByte Toolbox is open-source software released under the MIT Licence.
@@ -1,98 +0,0 @@
---
title: Regions for Java
---
## Introduction
**regions4j** is a lightweight, type-safe Java library that provides comprehensive metadata for world regions based on the ISO 3166-1 standard. It is designed to simplify internationalisation (i18n), telecommunication routing, and regional data management in Java-based enterprise applications.
The library models every ISO 3166-1 country and territory as an enum constant, giving you compile-time safety and IDE autocompletion when referencing regions. With built-in O(1) lookups, zero external dependencies, and Java 8 compatibility, it is suitable for projects of any size — from microservices to monolithic enterprise systems.
## Features
- **ISO 3166-1 Compliant** — Full support for Alpha-2 (`abbreviation`) and Alpha-3 (`code`) formats across all assigned countries and territories.
- **Telecommunication Metadata** — Includes E.164 international dialling prefixes for every region.
- **Locality Support** — Default locale tags (`zh_HK`, `en_US`, `en_GB`, etc.) for each region to assist with i18n workflows.
- **High Performance** — Thread-safe, immutable internal hash maps provide constant-time lookups by abbreviation or code.
- **Zero Dependencies** — A pure Java library with no external overhead.
- **Calendar Versioning** — Versions follow a `YYYY.MM` scheme (e.g. `2025.12`) to transparently indicate the freshness of the underlying ISO data, rather than semantic versioning.
## Core Schema
Each region exposes four attributes:
| Attribute | Type | Description | Example |
| -------------- | ------ | ----------------------------------- | ---------------------------------- |
| `abbreviation` | String | ISO 3166-1 alpha-2 abbreviation | `HK`, `US`, `GB`, `RU` |
| `code` | String | ISO 3166-1 alpha-3 code | `HKG`, `USA`, `GBR`, `RUS` |
| `callingCode` | String | E.164 international dialling prefix | `852`, `1`, `44`, `7` |
| `locale` | String | Default language and region tag | `zh_HK`, `en_US`, `en_GB`, `ru_RU` |
## Installation
**Maven:**
```xml
<dependency>
<groupId>com.onixbyte</groupId>
<artifactId>regions4j</artifactId>
<version>2025.12</version>
</dependency>
```
**Gradle (Kotlin DSL):**
```kotlin
implementation("com.onixbyte:regions4j:2025.12")
```
> **Versioning note:** This library uses Calendar Versioning (`YYYY.MM`) rather than Semantic Versioning. A version string like `2025.12` tells you immediately that the regional data reflects the ISO 3166-1 state as of December 2025.
## Usage
### Direct Access
Reference any region directly via its enum constant:
```java
Region uk = Region.UNITED_KINGDOM;
uk.getAbbreviation(); // "GB"
uk.getCode(); // "GBR"
uk.getDialCode(); // "44"
uk.getLocale(); // "en_GB"
```
### Efficient Lookups
Look up regions from string inputs — ideal for processing web requests, API payloads, or database records:
```java
// By Alpha-2 abbreviation (case-sensitive)
Region region = Region.fromAbbreviation("HK");
// By Alpha-3 code (case-insensitive)
Region region = Region.fromCode("HKG");
```
### Java Locale Integration
Convert a region's locale string to a `java.util.Locale` for use with standard Java i18n APIs:
```java
Region region = Region.CHINA;
Locale javaLocale = Locale.forLanguageTag(region.getLocale().replace("_", "-"));
javaLocale.getDisplayCountry(Locale.UK); // "China"
```
## Performance
On first access, the library initialises all regions into immutable `HashMap` structures. Subsequent lookups via `fromAbbreviation()` and `fromCode()` are thread-safe and run in constant time — suitable for high-throughput request paths.
## Requirements
- Java 8 or later
## License
regions4j is open-source software released under the MIT License.
@@ -1,11 +0,0 @@
---
title: Vite Plugins
---
## Overview
A collection of Vite plugins developed and maintained by OnixByte. Each plugin is designed to solve a specific problem in the Vite development workflow — lightweight, zero-dependency, and plug-and-play.
## Available Plugins
- **[Port Checker](/opensource-projects/vite-plugins/port-checker)** — Warns when the dev server listens on a browser-restricted port (per the Fetch specification), helping you avoid silent connection failures.
@@ -1,60 +0,0 @@
---
title: Port Checker
---
import { Tabs, Tab } from "@rspress/core/theme"
## Introduction
**vite-plugin-port-checker** is a lightweight Vite plugin that warns you when the dev server is listening on a port blocked by common browsers. Browsers such as Chrome and Firefox maintain a [list of restricted ports](https://fetch.spec.whatwg.org/#port-blocking) — typically those associated with well-known protocols like SMTP, SSH, and DNS — and refuse to connect to them. Accidentally running your dev server on one of these ports leads to a confusing "connection refused" or "blocked" error with no clear explanation.
This plugin detects the situation at startup and prints a clear, colour-coded warning to the console, advising you to change the `server.port` setting in your Vite configuration.
## Features
- **Automatic Detection** — Checks the dev server port against the complete Fetch spec port-blocking list (68 restricted ports).
- **Clear Warnings** — Yellow-highlighted console output with actionable guidance.
- **Zero Configuration** — Works out of the box with no options to set.
- **Lightweight** — Single-file plugin with no dependencies beyond Vite itself.
## Installation
<Tabs>
<Tab label="npm">```bash npm install vite-plugin-port-checker -D ```</Tab>
<Tab label="pnpm">```bash pnpm add vite-plugin-port-checker -D ```</Tab>
</Tabs>
## Usage
Add the plugin to your `vite.config.ts`:
```ts
// vite.config.ts
import { defineConfig } from "vite"
import checkRestrictedPort from "vite-plugin-port-checker"
export default defineConfig({
plugins: [checkRestrictedPort()],
})
```
When the dev server starts on a restricted port (e.g. port 25, 22, or 53), you will see:
```
[Warning] The current listening port 25 is categorised as a restricted port
by most browsers. This may prevent you from accessing the application.
Please consider changing the port in your 'vite.config.ts' or 'vite.config.js'
via 'server.port'.
```
## How It Works
The plugin hooks into Vite's `configureServer` lifecycle. Once the HTTP server emits the `listening` event, the plugin inspects the bound port and checks it against the hardcoded set of 68 restricted ports defined in the [Fetch specification](https://fetch.spec.whatwg.org/#port-blocking). If there is a match, a yellow `console.warn` message alerts the developer.
## Requirements
- Vite 3.0.0 or later
## License
vite-plugin-port-checker is open-source software released under the MIT License.