15 Commits

Author SHA1 Message Date
a577f94acd BLOG-142 Move label to a new feature (#143)
All checks were successful
Frontend CI / build (push) Successful in 1m35s
### Description

- As the title

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #142.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #143
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-15 06:23:58 +08:00
24a98f8f70 BLOG-126 Post management (list and create) (#139)
All checks were successful
Frontend CI / build (push) Successful in 6m14s
### Description

- As the title

### Package Changes

_No response_

### Screenshots

|Scenario|Screenshot|
|-|-|
|Posts list|![截圖 2025-10-15 凌晨4.09.28.png](/attachments/c8ad41e8-1065-4249-90e3-977bd36031e8)|
|Empty input|![截圖 2025-10-15 凌晨4.10.12.png](/attachments/c902fdc0-4287-4b5d-9b9e-63dd6a5604a6)|
|Pattern not matched|![截圖 2025-10-15 凌晨4.11.05.png](/attachments/88e4fb1d-6a69-4305-a94c-bd48982bb8a5)|

### Reference

Resolve #126.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #139
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-15 04:21:13 +08:00
de2099011b BLOG-127 Image management (upload) (#138)
All checks were successful
Frontend CI / build (push) Successful in 1m37s
### Description

- Implement the frontend to upload image.
- It will copy the image URL when uploaded.

### Package Changes

```json
{
		"@internationalized/date": "^3.10.0",
		"@lucide/svelte": "^0.544.0",
		"bits-ui": "^2.11.5",
		"mode-watcher": "^1.1.0",
		"svelte-sonner": "^1.0.5"
}
```

### Screenshots

|Scenario|Screenshot|
|-|-|
|Dialog|![截圖 2025-10-14 凌晨2.16.13.png](/attachments/8b38b87f-7f17-4e31-adc1-cb9660501e87)|
|Empty file or invalid format|![截圖 2025-10-14 凌晨2.17.12.png](/attachments/950cebd0-ae6b-4f2b-a821-6d7366e8ae57)|
|Success|![截圖 2025-10-14 凌晨2.17.32.png](/attachments/938d8669-598f-4dba-b1ce-24d5f9ae390a)|
|Error|![截圖 2025-10-14 凌晨2.18.52.png](/attachments/b35048e6-e012-498b-be77-1f1793b87038)|

### Reference

Resolves #127.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #138
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-14 02:23:06 +08:00
e8c5e678d5 BLOG-136 Dashboard route and frontend authentication (#137)
All checks were successful
Frontend CI / build (push) Successful in 1m23s
### Description

- Create a dashboard layout (with a navigation sidebar)
- Only logged in users are able to access (others 404)

### Package Changes

> shadcn-svelte

```json
{
  "@lucide/svelte": "^0.545.0",
  "clsx": "^2.1.1",
  "tailwind-merge": "^3.3.1",
  "tailwind-variants": "^3.1.1",
  "tw-animate-css": "^1.4.0"
}
```

### Screenshots

|Status|Screenshot|
|-|-|
|Logged in|![image.png](/attachments/5d5fc3b9-c15f-43b8-8201-dedeef3c3fb9)|
|Logged out|![image.png](/attachments/29683c9f-395c-4dd8-892f-dc21df2dd0cc)|

### Reference

Resolves #136.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #137
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-13 20:33:36 +08:00
565df7aace BLOG-125 Get post by sementic ID (#134)
All checks were successful
Frontend CI / build (push) Successful in 1m26s
### Description

#### Backend

- String and interger can be pass as `id` to `GET` `/post/{id}`

- For the posts existed, the default `semantic_id` for them will be `_id`. (e.g. `_1`, `_2`)

- Semantic ID should follow the rules:

  1. It shouldn't be an integer

  1. It should match the pattern: `^[0-9a-zA-Z_\-]+$`

  <br>

  |Semantic ID|Result|Note|
  |-|-|-|
  |12|X|against with `i`|
  |-3|X|against with `i`|
  |3.14|X|against with `ii`|
  |hello world|X|against with `ii`|
  |*EMPTY*|X|against with `ii`|
  |12_34-56|O||

#### Frontend

- The href of post preview card becomes the semantic ID.

### Package Changes

```toml
regex = "1.12.1"
```

### Screenshots

![截圖 2025-10-12 下午6.23.12.png](/attachments/67de1cd7-f584-40ad-9bbd-27f8bf6f1894)

### Reference

Resolves #125.

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #134
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-10-12 18:28:58 +08:00
08c5262df6 BLOG-118 Fix to allow nullable published_time to support unpublished posts (#121)
All checks were successful
Frontend CI / build (push) Successful in 1m19s
### Description

This PR updates the application to handle posts that may not have a publication date (e.g., drafts) by making the `published_time` field optional across the entire post feature stack.

This ensures that draft posts can be processed and rendered without causing errors, and prevents search engine metadata from being generated for content that is not yet published.

#### Key Changes:

* **DTO & Schema (`postInfoResponseDto.ts`):**
    * The Zod schema for `PostInfoResponseSchema` has been updated to mark `published_time` as `.nullable()`.
    * The `PostInfoResponseDto` class now correctly handles a `null` value from the API, mapping it to `Date | null`.

* **Domain Entity (`postInfo.ts`):**
    * The core `PostInfo` entity's `publishedTime` property is now typed as `Date | null` to reflect the business logic that a post may be unpublished.

* **View Model (`postInfoViewModel.ts`):**
    * Updated `publishedTime` to be `Date | null`.
    * Added a new `isPublished` boolean getter for convenient conditional logic in the UI.
    * The `formattedPublishedTime` getter now returns `string | null`.
    * Dehydration and rehydration logic (`dehydrate`/`rehydrate`) has been updated to correctly handle the nullable `publishedTime`.

* **UI Component (`PostContentPage.svelte`):**
    * The component now uses the new `isPublished` flag to conditionally render the `<StructuredData>` component for SEO. This ensures that structured data is only included for posts that have been officially published.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #118

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #121
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-08-06 21:36:54 +08:00
171410e115 BLOG-48 SEO Improvement (#116)
All checks were successful
Frontend CI / build (push) Successful in 1m14s
### Description

#### Overview
This PR improves the website's SEO by:
1. Moving title and meta description tags from app.html to individual page components
2. Adding dynamic meta descriptions based on page content
3. Implementing structured data for blog posts using JSON-LD
4. Optimizing meta descriptions for better search engine visibility

#### Changes
- **app.html**: Removed static title and meta description tags
- **HomePage.svelte**: Added descriptive title parameter to generateTitle function
- **Terminal.svelte**: Dynamically generates meta description from terminal lines
- **PostContentPage.svelte**: Added meta description and structured data for blog posts
- **PostOverallPage.svelte**: Added descriptive meta description for blog listing page
- **StructuredData.svelte**: Created new component to generate JSON-LD structured data for blog posts

#### Benefits
- Improved SEO through better metadata management
- Enhanced search engine visibility with structured data
- More accurate and dynamic meta descriptions
- Better control over page-specific metadata

> [!NOTE]
> Since sitemap auto generating is a little more complex, it will be solved in #117 in the future.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #48

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #116
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-08-05 11:25:39 +08:00
bc20385ff2 Merge branch 'release/0.2' into NO-ISSUE_merge_v0.2.2
All checks were successful
Frontend CI / build (push) Successful in 1m8s
Auto Comment On PR / add_improve_comment (pull_request) Successful in 17s
PR Title Check / pr-title-check (pull_request) Successful in 16s
2025-08-04 07:32:40 +08:00
71528294ae BLOG-105 Implement CRUD functionality for Labels (#107)
All checks were successful
Frontend CI / build (push) Successful in 1m8s
### Description

This PR introduces full CRUD (Create, Read, Update) functionality for post labels, implemented by following the existing Clean Architecture.

#### Backend

* **New API Endpoints for Label Management:**
    * `POST /label`: Create a new label (**authentication required**).
    * `PUT /label/{id}`: Update a label by its ID (**authentication required**).
    * `GET /label`: Get all labels.

* **Architectural Implementation:**
    * **Delivery Layer**: Added `CreateLabelRequestDto`, `UpdateLabelRequestDto`, and updated `PostController` with methods to handle label-related operations.
    * **Application Layer**: Created corresponding use cases (`CreateLabelUseCase`, `UpdateLabelUseCase`, `GetAllLabelsUseCase`) to handle business logic.
    * **Gateway/Framework Layer**: Implemented `LabelRepository` and `LabelDbService` to manage database interactions, including creating, updating, and querying labels.

* **Route Adjustment:**
    * The route for fetching all post info has been changed from `GET /post/all` to `GET /post` to be more RESTful.

#### Frontend

* **API Call Update:**
    * To match the backend route change, the API path for fetching all posts is updated from `/post/all` to `/post`.

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #105

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #107
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-08-02 10:46:00 +08:00
64ebfaa3e9 BLOG-87 Fix to unify post content title color (#88)
All checks were successful
Frontend CI / build (push) Successful in 1m5s
### Description

- Enhance typography and color consistency in PostPreview and PostContentPage
- Add tailwind configuration for custom typography styles

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #87

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #88
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-07-28 00:21:09 +08:00
95fabee99d BLOG-73 Change website title dynamically (#77)
All checks were successful
Frontend CI / build (push) Successful in 1m7s
### Description

- Use `generateTitle` to combine app name and page title.

### Package Changes

_No response_

### Screenshots

|Home|Post Overall|Post Content|
|-|-|-|
|![Screenshot From 2025-07-26 01-58-04.png](/attachments/70ed7440-bd5a-4e9d-a747-bb785c9f7e16)|![Screenshot From 2025-07-26 01-58-27.png](/attachments/58386f0d-e476-4795-8ac4-f0abc6586721)|![Screenshot From 2025-07-26 01-58-31.png](/attachments/cf7cb5a6-af5e-4dc6-907f-8cb8253f1b13)|

### Reference

Resolves #73

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #77
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-07-26 02:01:24 +08:00
9ad3809f47 BLOG-70 Add tooltip for post preview (#76)
All checks were successful
Frontend CI / build (push) Successful in 1m7s
### Description

- A a `title` attribute on the `<a>` element.
- Make the title as `<h2>` and the description as `<p>`.

### Package Changes

_No response_

### Screenshots

![image.png](/attachments/0d4b815e-d1c5-432c-8174-53273189a9c3)

### Reference

Resolves #

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #76
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-07-26 01:29:18 +08:00
3cb69f6e7c BLOG-45 Post content page (#67)
All checks were successful
Frontend CI / build (push) Successful in 1m8s
### Description

- Implement the content page
  - Parse markdown formant content to html by `markdown-it`
  - Use `sanitize-html` to prevent from XSS attack
  - Style the html with `tailwindcss-typography`
- Fix the issue when backend parse the password to url
- Fix and make the post info list from backend always sorted by id

### Package Changes

### Rust

```toml
percent-encoding = "2.3.1"
```

### Node

```json
{
  "@types/markdown-it": "^14.1.2",
  "@types/sanitize-html": "^2.16.0",
  "markdown-it": "^14.1.0",
  "sanitize-html": "^2.17.0"
}
```

### Screenshots

|Desktop|Mobile|
|-|-|
|![image.png](/attachments/0ec5718a-f804-432f-8e4b-e9dc22c080d2)|![beta.squidspirit.com_post(iPhone 12 Pro) (1).png](/attachments/b30d1b96-d4a4-4b2b-b9bd-90fd2592ab52)|

### Reference

Resolves #45

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #67
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-07-24 22:20:58 +08:00
4a924c1b92 BLOG-65 Establish beta environment (#66)
All checks were successful
Frontend CI / build (push) Successful in 1m4s
### Description

- Change some environment variables implementation
- Nginx configuration:

  ```nginx
  server {
      server_name beta.squidspirit.com;

      proxy_pass_request_headers on;
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;

      location / {
          proxy_pass http://127.0.0.1:10013/;
      }

      location /api/ {
          proxy_pass http://127.0.0.1:10014/;
      }

      listen 443 ssl; # managed by Certbot
      ssl_certificate /etc/letsencrypt/live/beta.squidspirit.com/fullchain.pem; # managed by Certbot
      ssl_certificate_key /etc/letsencrypt/live/beta.squidspirit.com/privkey.pem; # managed by Certbot
      include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
      ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
  }

  server {
      if ($host = beta.squidspirit.com) {
          return 301 https://$host$request_uri;
      } # managed by Certbot

      server_name beta.squidspirit.com;
      listen 80;
      return 404; # managed by Certbot
  }
  ```

- Podman kube configuration:

  ```yaml
    apiVersion: v1
  kind: Secret
  metadata:
    name: beta-blog-secret
  data:
    DATABASE_PASSWORD: {{BASE64_PASSWORD}}

  ---

  apiVersion: v1
  kind: Pod
  metadata:
    name: beta-blog
  spec:
    containers:
      - name: postgres
        image: docker.io/library/postgres:17-alpine
        imagePullPolicy: always
        env:
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                name: beta-blog-secret
                key: DATABASE_PASSWORD
        volumeMounts:
          - name: beta-blog-postgres
            mountPath: /var/lib/postgresql/data
      - name: backend
        image: registry.squidspirit.com/squid/beta-blog-backend:latest
        imagePullPolicy: always
        env:
          - name: DATABASE_PASSWORD
            valueFrom:
              secretKeyRef:
                name: beta-blog-secret
                key: DATABASE_PASSWORD
        volumeMounts:
          - name: beta-blog-localtime
            mountPath: /etc/localtime
            readonly: true
        ports:
          - hostPort: 10014
            hostIP: 127.0.0.1
            containerPort: 8080
      - name: frontend
        image: registry.squidspirit.com/squid/beta-blog-frontend:latest
        imagePullPolicy: always
        env:
          - name: PUBLIC_API_BASE_URL
            value: https://beta.squidspirit.com/api/
        volumeMounts:
          - name: beta-blog-localtime
            mountPath: /etc/localtime
            readonly: true
        ports:
          - hostPort: 10013
            hostIP: 127.0.0.1
            containerPort: 3000
    volumes:
      - name: beta-blog-localtime
        hostPath:
          path: /etc/localtime
      - name: beta-blog-postgres
        persistentVolumeClaim:
          claimName: beta-blog-postgres
  ```

### Package Changes

_No response_

### Screenshots

_No response_

### Reference

Resolves #65

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #66
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-07-24 02:23:44 +08:00
c2462fe537 BLOG-44 Post overall page (#64)
All checks were successful
Frontend CI / build (push) Successful in 1m6s
### Description

- Change the format of color response

  ```json
  {
    "red": 0,
    "green": 255,
    "blue": 128,
    "alpha": 255
  }
  ```

- The relationship between the label's background color and its highlight color is calculated. The method involves first converting the RGB color value to HSL, then decreasing the L (lightness) component, and finally converting it back to RGB.

### Package Changes

```json
{
  "zod": "^4.0.5"
}
```

### Screenshots

|Desktop|Mobile|
|-|-|
|![image.png](/attachments/851450c4-0975-4b66-9bf7-606d0114c03e)|![image.png](/attachments/f263d65c-89e9-4439-8d50-ae92ae9482ac)|

### Reference

Resolves #44

### Checklist

- [x] A milestone is set
- [x] The related issuse has been linked to this branch

Reviewed-on: #64
Co-authored-by: SquidSpirit <squid@squidspirit.com>
Co-committed-by: SquidSpirit <squid@squidspirit.com>
2025-07-24 00:32:20 +08:00