256 Commits

Author SHA1 Message Date
Luffy 897fc8c4d4 fix: add missing license entry for mozillazg-go-unidecode (#1552)
Lint / Lint (ubuntu-latest) (push) Has been cancelled
Build Binary For Release / build-goreleaser (push) Has been cancelled
Build Docker Image For Release / build (push) Has been cancelled
2026-07-14 16:03:24 +08:00
Artur Iusupov 3a5a15fe77 fix(site): require explicit email verification setting
Replace OptionalBool with an explicit require_email_verification value for the login settings save request while keeping legacy read defaults intact.

Use positive RequireEmailVerification naming through the registration flow and inline the site setting mapping.

Add validation, save-path, and registration coverage for the explicit email verification setting.
2026-07-10 10:19:39 +08:00
Artur Iusupov 7123326515 feat(site): allow disabling email verification 2026-07-10 10:19:39 +08:00
Ahmed Qasid a6d867e4cc fix: avoid topic fallback for non-Latin titles via pragmatic ASCII transliteration (#1526)
# fix: avoid `topic` fallback for non-Latin titles via pragmatic ASCII
transliteration

> **Scope update (in response to review):** this PR is intentionally
broader than its original "Arabic-only" framing. The implementation
changes URL slug generation for **every non-Latin, non-CJK script** that
`slugify` previously stripped — see *Scope* below for the explicit list.
The goal is *not* linguistically correct romanization; it is "avoid
collapsing to `/topic` by producing a usable ASCII slug."

## What this PR is (and isn't)

**Goal:** when a question title contains characters outside Basic Latin
/ Latin Extended / CJK Han, generate a URL slug that is a deterministic
ASCII approximation instead of letting `slugify` strip everything and
falling back to the literal `"topic"`.

**Non-goal:** this is *not* a linguistically correct multi-language
romanizer. The output is a machine-acceptable ASCII slug, not what a
native speaker would choose. For example, `こんにちは` → `konnichiha` (not
the more natural `kon'nichiwa`), `ไทย` → `aithy` (not `thai`). Treat the
slug as an opaque, stable, indexable identifier — the
path-after-`/questions/<id>/` is for SEO and shareability, the canonical
reference is always the ID.

## The bug

Pure non-Latin titles previously got stripped by `slugify.Slugify`, hit
the empty-result fallback in `htmltext.UrlTitle`, and collapsed to the
literal slug `"topic"`. On a live multilingual site, every Arabic / Thai
/ Japanese-hiragana / Korean / Hebrew / Cyrillic question ended up at
`/questions/<id>/topic`.

## The fix

`UrlTitle()` gets a `convertNonLatin` pre-step that mirrors the existing
`convertChinese` pre-step pattern, using
`github.com/mozillazg/go-unidecode` (same author as `go-pinyin` already
in the repo, to minimise new-dep friction).

```
UrlTitle(title)
  → convertChinese(title)        // pre-existing: Han-block → pinyin
  → convertNonLatin(title)       // NEW: detect non-Latin letters → unidecode to ASCII
  → clearEmoji / slugify / url.QueryEscape / cutLongTitle (unchanged)
```

The non-Latin detector skips ASCII, Latin-1 Supplement, Latin
Extended-A/B, and CJK Han. Inputs that hit none of those non-Latin
letter categories short-circuit and return unchanged, so Latin-only and
Chinese-only inputs remain byte-identical (pinned by tests).

## Scope — what scripts are affected

This PR changes behavior for **any** title containing letters in scripts
that `slugify` doesn't handle. Confirmed by tests in
`pkg/htmltext/htmltext_test.go`:

| Script | Example title | Before | After |
| --- | --- | --- | --- |
| Arabic | `كيف حالك` | `topic` | `kyf-hlk` |
| Mixed Latin + Arabic | `مرحبا hello` | `hello` | `mrhb-hello` |
| Thai | `ไทย ไทย` | `topic` | `aithy-aithy` |
| Japanese hiragana | `こんにちは` | `topic` | `konnichiha` |
| Korean | `안녕하세요` | `topic` | `annyeonghaseyo` |
| Hebrew | `שלום עולם` | `topic` | `shlvm-vlm` |
| Cyrillic | `Привет мир` | `topic` | `privet-mir` |

**Unchanged:**

| Case | Behavior |
| --- | --- |
| Pure Latin (`hello world`) | unchanged → `hello-world` |
| Pure Chinese (`这是一个,标题,title`) | unchanged → `zhe-shi-yi-ge-biao-ti`
(pinyin path) |
| Japanese with Han-block kanji (`日本`) | unchanged → `ri-ben` (caught by
pre-existing pinyin path; treated as Chinese reading, not Japanese — a
pre-existing limitation, **not** introduced by this PR) |
| Emoji only (`😂😂😂`) | unchanged → `topic` |
| Empty / whitespace | unchanged → `topic` |

## Transliteration quality — explicit acknowledgement

`go-unidecode` is a generic Unicode → ASCII approximation. It is **not**
a per-language romanization library. Specifically:

- It will pick *one* approximation per codepoint regardless of language
context. `ใ` → `ai` (Thai romanization is `i` or `ai` depending on
standard), `한` → `han`, `語` → `Yu` (Chinese pinyin reading even when
used in Japanese), etc.
- The result is *good enough* to be a stable, URL-safe,
human-recognizable handle, but speakers of the source language will not
consider it "correct."
- It is deterministic, so the same title always produces the same slug —
important since `url_title` is recomputed on every request.

If maintainers prefer to scope this PR more narrowly (e.g. Arabic only,
and reject Thai/Hebrew/Cyrillic/etc.), the detector in
`containsNonLatin` can be tightened to specific Unicode blocks — but
that means the other scripts continue to collapse to `topic`, which is
the bug we're trying to fix. I'd argue the broader fix is preferable to
a piecemeal one, but happy to narrow if you want.

## Live deployment / real-world verification

This patch has been running in production on
**[ask.namasoft.com](https://ask.namasoft.com)** (an Apache Answer
instance we operate) since deployment, built directly from this branch
via `docker compose build`. The site hosts Arabic-language questions, so
the fix exercises the affected code path on every page load.

Sample question URL on the deployed instance:

> `https://ask.namasoft.com/questions/10010000000000115`

The slug in the URL is the transliterated Arabic title rather than
`topic`. No data migration was needed since `url_title` is computed on
every request from `Title` and never persisted (see *Why this is safe to
ship* below).

## Admin-configurable

The transliteration is gated by a package-level `atomic.Bool` (default
**on**, since the current behavior is objectively broken for affected
users):

- `htmltext.SetTransliterateNonLatin(enabled bool)`
- `htmltext.IsTransliterateNonLatinEnabled() bool`

This is deliberately the minimum surface needed to satisfy "the setting
must be readable from `UrlTitle()`". A follow-up PR can add an admin UI
section that calls `SetTransliterateNonLatin` on save and on startup,
without having to re-plumb every `htmltext.UrlTitle` call site through
`context.Context`.

**Default choice — please confirm:** I picked **default-on** because the
existing `topic` behavior is a bug for affected users. If you'd prefer
default-off for strict backward compat on existing installs, flip the
`init()` in `pkg/htmltext/htmltext.go` to `Store(false)` and surface the
toggle as opt-in.

## Why this is safe to ship

- `url_title` is **not** a persisted column. It's not on the `Question`
entity in `internal/entity/question_entity.go`, no migration has ever
added/dropped it, and every call site (`question_service.go`,
`revision_service.go`, `vote_service.go`,
search/report/review/rank/comment services, controllers, repos)
recomputes it from `Title` at response-build time via
`htmltext.UrlTitle(...)`.
- That means the fix is read-only: existing rows light up with correct
slugs on the next request, with no migration and no data rewrite.
- Rollback is just redeploying the prior image; nothing on disk changes.

## Test coverage

`pkg/htmltext/htmltext_test.go`:

- **`TestUrlTitleTable`** — table-driven, one case per affected script
(the full matrix above), plus:
  - `empty` → `topic`
  - `pure latin unchanged` → byte-identical to pre-fix
- `pure chinese unchanged` → byte-identical to pre-fix (pins existing
pinyin behavior)
- `japanese kanji goes through pinyin path unchanged` → documents the
pre-existing Han-block limitation
  - `emoji only falls back to topic` → unchanged
- `long arabic truncates at cutLongTitle boundary` → exercises the
150-byte cap and UTF-8 boundary safety
- **`TestUrlTitleTransliterationToggle`** — with the toggle off,
non-Latin titles collapse to `topic` (pre-fix behavior); with it on,
they transliterate.
- Existing `TestUrlTitle` left untouched.

Test plan for reviewers:

- [ ] `go test ./pkg/htmltext/...` — all pass
- [ ] Visit the live sample URL above and confirm slug is
transliterated, not `topic`
- [ ] Verify Chinese / Latin / emoji-only / empty behavior is
byte-identical to `main` (covered by table tests)

## Out of scope (intentionally)

- No admin UI / site setting plumbing in this PR — see
*Admin-configurable* above. Happy to do the React `Non-Latin Languages
Handling` admin page + `SiteType` + service / controller / migration in
a follow-up if maintainers want it.
- No change to the `"topic"` empty-result fallback.
- No plugin interface for slug generation — mirrored the existing
`convertChinese` pre-step pattern instead.
- No per-language romanization library — this is an explicit non-goal;
see *Transliteration quality* above.

## Issues / discussion

I didn't find an existing upstream issue covering this — happy to be
pointed at one if there is.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: LinkinStars <linkinstar@foxmail.com>
2026-07-10 10:19:39 +08:00
Luffy 4b8de1897b fix: update license entries 2026-07-10 10:19:39 +08:00
Luke Gao 09eaa5b7e3 feat: add reasoning content to AI conversation records and update related components (#1530)
Fix #1524 

Root cause

DeepSeek's reasoning models stream reasoning_content alongside content.
Answer ignored it, so follow-up requests failed with 400: The
reasoning_content in the thinking mode must be passed back to the API,
and the thinking text was never shown or saved.

Fix

- Capture reasoning_content from the stream and pass it back to theAPI
on subsequent rounds.
 - Persist it with the conversation (new DB column via migrationv2.0.2).
- Render it in the chat UI as a collapsible "Thinking…/Thoughts"panel
above the answer.

Compatibility

Nullable column, omitempty field, UI hides the panel when empty — old
conversations and non-reasoning models behave exactly as before.

Demo



https://github.com/user-attachments/assets/49b1a2a1-9133-4ac2-bbeb-860215a50285
2026-07-10 10:19:39 +08:00
robin 2746bf5b45 fix: update copyright year to 2026
Build Latest Docker Image For Release / build (push) Has been cancelled
Lint / Lint (ubuntu-latest) (push) Has been cancelled
Build Binary For Release / build-goreleaser (push) Has been cancelled
Build Docker Image For Release / build (push) Has been cancelled
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 10:46:54 +08:00
Luffy f9941827b1 fix: missing uuid license 2026-05-25 10:38:59 +08:00
LinkinStars e5cb38bd74 chore: add license files and update Go version to 1.24.0 2026-01-28 17:30:31 +08:00
LinkinStars a5edc4fc01 feat: add advanced site settings and related API endpoints 2026-01-28 11:15:40 +08:00
LinkinStars c1549d2909 feat: add AI configuration support with related controllers and services 2026-01-23 17:09:05 +08:00
LinkinStars 6b834c745a Merge remote-tracking branch 'origin/dev' into test 2026-01-14 11:31:17 +08:00
LinkinStars 5be6ec9e71 docs(lic): add MIT license file and clean up init function in install_main.go 2026-01-14 11:30:50 +08:00
shuai b5ae2d0351 fix: admin/themes add layout config 2025-12-19 14:09:38 +08:00
ferhat elmas f723d120d9 feat: add golangci-lint into lint target
* replace empty interface with any
* run fmt via golangci-lint

related to #1432

Signed-off-by: ferhat elmas <elmas.ferhat@gmail.com>
2025-12-01 11:14:38 +08:00
ferhat elmas bc629db132 chore(deps): bump mockgen to 0.6.0 for go1.25 support
Signed-off-by: ferhat elmas <elmas.ferhat@gmail.com>
2025-11-28 17:00:25 +08:00
LinkinStars eb1b20ab96 chore(ci): Add ASF header 2025-10-28 15:27:11 +08:00
shuai 78df776449 fix: Details page editing time value adjustment 2025-09-30 15:34:37 +08:00
LinkinStars fda869f1dc docs: remove description for suspended_until field 2025-07-17 15:52:25 +08:00
LinkinStars 6d02706d4d feat(user): add user suspension functionality 2025-06-26 14:42:08 +08:00
fen 157291c2d5 fix: screenshot 2025-06-24 14:19:20 +08:00
LinkinStars 67b9c0b462 refactor(api): update API documentation for Accept Answer endpoint 2025-06-24 11:19:53 +08:00
LinkinStars d02d09bb9a refactor(api): improve API documentation for Answer endpoints 2025-06-24 11:06:14 +08:00
Luffy afdf6ce778 fix: Unified display name and username length checks 2025-05-23 15:09:34 +08:00
LinkinStars 02c1304b99 feat(docs): Add Apache License header 2025-04-29 15:00:40 +08:00
shuai 9f420e67d2 Merge branch 'dev' into test 2025-04-27 11:59:34 +08:00
sy-records 59f0c3fb63 fix: Username minimum length still set to 4 2025-04-18 11:52:55 +08:00
LinkinStars 5b9c00c4a7 Merge remote-tracking branch 'origin/feat/1.5.0/user' into test 2025-04-17 11:37:23 +08:00
shuai bebf5931a1 style: admin ui style changed 2025-04-07 15:31:31 +08:00
shuai 105cd37dfe fix: update seo template for footer template 2025-04-07 11:44:34 +08:00
Seiya f92860e8ec chore: remove duplicated security requirements (#1302)
close #1300
2025-04-02 09:12:39 +08:00
Luffy 1db30228d5 chore: sync swagger 2025-03-18 14:03:17 +08:00
Luffy 0285a47ae3 fix: missing ApiKeyAuth configuration in Swagger 2025-01-09 17:00:28 +08:00
LinkinStars 47c666200e Merge remote-tracking branch 'origin/main' into dev
# Conflicts:
#	internal/base/middleware/accept_language.go
#	internal/schema/answer_schema.go
#	internal/schema/comment_schema.go
#	internal/service/user_admin/user_backyard.go
2025-01-07 12:48:13 +08:00
LinkinStars 0618947350 refactor(name): rename incubator-answer 2025-01-07 10:39:23 +08:00
Luffy 4a74eed4a7 feat: Add permanently delete 2024-12-25 15:53:04 +08:00
LinkinStars 9c266c5f33 feat(upload): add support for attachment uploads and improve image handling 2024-12-09 11:52:18 +08:00
Luffy e41a8522fc fix: administrator add user failure 2024-12-09 11:52:18 +08:00
LinkinStars 1fe3b7d8fc refactor(gomock): update gomock dependence and docs 2024-12-09 11:52:18 +08:00
shuai 8e3ce7d32e docs: rename codemirror license file name
Build Binary For Release / build-goreleaser (push) Has been cancelled
Build Docker Image For Release / build (push) Has been cancelled
2024-10-24 10:15:41 +08:00
shuai 6d933c2dbe docs: add front-matter license files 2024-10-24 10:13:09 +08:00
shuai 8cff85b7ad docs: update license files 2024-10-24 10:01:37 +08:00
sy-records eeb79b3ab5 fix: missing js-sha256 license 2024-10-24 09:59:18 +08:00
Sonui 3eb9822c09 feat(question): support linking question 2024-10-12 10:17:18 +08:00
sy-records bd0d9fda22 docs: regenerate docs 2024-10-12 09:59:17 +08:00
LinkinStars e062cbcb0e refactor(gomod): replace pinyin dependency 2024-09-26 16:25:00 +08:00
sy-records 48c2387853 chore: sync swagger 2024-09-19 14:37:04 +08:00
sy-records 1d33c8827c fix: change fail 2024-09-05 11:07:15 +08:00
sy-records c751364974 fix: add getInstallLanguageConfig 2024-09-05 11:07:15 +08:00
LinkinStars 2ca5e8bb4a Merge branch 'dev' into test
# Conflicts:
#	cmd/wire_gen.go
#	internal/service/content/question_service.go
2024-09-04 10:29:05 +08:00