Compare commits

...

11 Commits

Author SHA1 Message Date
bc8d59f7a9 fix gosum 2025-09-27 16:33:52 +08:00
c8eb519f7e Update Go base image to version 1.25 in Dockerfile 2025-09-27 16:27:55 +08:00
e117a2e708 Add modified_time field to Resource model and update logic for tracking modifications 2025-09-27 16:22:04 +08:00
c131f48448 Update dependencies 2025-09-27 16:12:24 +08:00
45beaced89 Check stopwords 2025-09-10 20:06:46 +08:00
af81f66f25 Improve search 2025-09-10 20:01:32 +08:00
ff1f6e7340 Add unit test for searching 2025-09-10 16:56:50 +08:00
0a88a65846 Improve tag 2025-09-10 16:32:26 +08:00
316929db33 Improve tag 2025-09-10 16:32:11 +08:00
844233e70d Fix RemoveSpaces 2025-09-09 21:26:26 +08:00
f5c39e0315 Improve rendering markdown. 2025-09-09 18:50:53 +08:00
16 changed files with 821 additions and 116 deletions

View File

@@ -12,7 +12,7 @@ COPY frontend/ ./
RUN npm run build
# 第二阶段构建Go应用
FROM golang:1.24-alpine AS backend-builder
FROM golang:1.25-alpine AS backend-builder
WORKDIR /app

View File

@@ -19,6 +19,7 @@
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
"react-router": "^7.5.3",
"remark-gfm": "^4.0.1",
"spark-md5": "^3.0.2",
"tailwindcss": "^4.1.5"
},
@@ -4248,6 +4249,16 @@
"yallist": "^3.0.2"
}
},
"node_modules/markdown-table": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
"integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/masonic": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/masonic/-/masonic-4.1.0.tgz",
@@ -4279,6 +4290,34 @@
"node": ">= 0.4"
}
},
"node_modules/mdast-util-find-and-replace": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
"integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"escape-string-regexp": "^5.0.0",
"unist-util-is": "^6.0.0",
"unist-util-visit-parents": "^6.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mdast-util-from-markdown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
@@ -4303,6 +4342,107 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-gfm": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
"integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
"license": "MIT",
"dependencies": {
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-gfm-autolink-literal": "^2.0.0",
"mdast-util-gfm-footnote": "^2.0.0",
"mdast-util-gfm-strikethrough": "^2.0.0",
"mdast-util-gfm-table": "^2.0.0",
"mdast-util-gfm-task-list-item": "^2.0.0",
"mdast-util-to-markdown": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-gfm-autolink-literal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
"integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"ccount": "^2.0.0",
"devlop": "^1.0.0",
"mdast-util-find-and-replace": "^3.0.0",
"micromark-util-character": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-gfm-footnote": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
"integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"devlop": "^1.1.0",
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-to-markdown": "^2.0.0",
"micromark-util-normalize-identifier": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-gfm-strikethrough": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
"integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-to-markdown": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-gfm-table": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
"integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"devlop": "^1.0.0",
"markdown-table": "^3.0.0",
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-to-markdown": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-gfm-task-list-item": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
"integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"devlop": "^1.0.0",
"mdast-util-from-markdown": "^2.0.0",
"mdast-util-to-markdown": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-mdx-expression": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
@@ -4534,6 +4674,127 @@
"micromark-util-types": "^2.0.0"
}
},
"node_modules/micromark-extension-gfm": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
"integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
"license": "MIT",
"dependencies": {
"micromark-extension-gfm-autolink-literal": "^2.0.0",
"micromark-extension-gfm-footnote": "^2.0.0",
"micromark-extension-gfm-strikethrough": "^2.0.0",
"micromark-extension-gfm-table": "^2.0.0",
"micromark-extension-gfm-tagfilter": "^2.0.0",
"micromark-extension-gfm-task-list-item": "^2.0.0",
"micromark-util-combine-extensions": "^2.0.0",
"micromark-util-types": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-extension-gfm-autolink-literal": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
"integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
"license": "MIT",
"dependencies": {
"micromark-util-character": "^2.0.0",
"micromark-util-sanitize-uri": "^2.0.0",
"micromark-util-symbol": "^2.0.0",
"micromark-util-types": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-extension-gfm-footnote": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
"integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
"license": "MIT",
"dependencies": {
"devlop": "^1.0.0",
"micromark-core-commonmark": "^2.0.0",
"micromark-factory-space": "^2.0.0",
"micromark-util-character": "^2.0.0",
"micromark-util-normalize-identifier": "^2.0.0",
"micromark-util-sanitize-uri": "^2.0.0",
"micromark-util-symbol": "^2.0.0",
"micromark-util-types": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-extension-gfm-strikethrough": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
"integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
"license": "MIT",
"dependencies": {
"devlop": "^1.0.0",
"micromark-util-chunked": "^2.0.0",
"micromark-util-classify-character": "^2.0.0",
"micromark-util-resolve-all": "^2.0.0",
"micromark-util-symbol": "^2.0.0",
"micromark-util-types": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-extension-gfm-table": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
"integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
"license": "MIT",
"dependencies": {
"devlop": "^1.0.0",
"micromark-factory-space": "^2.0.0",
"micromark-util-character": "^2.0.0",
"micromark-util-symbol": "^2.0.0",
"micromark-util-types": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-extension-gfm-tagfilter": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
"integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
"license": "MIT",
"dependencies": {
"micromark-util-types": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-extension-gfm-task-list-item": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
"integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
"license": "MIT",
"dependencies": {
"devlop": "^1.0.0",
"micromark-factory-space": "^2.0.0",
"micromark-util-character": "^2.0.0",
"micromark-util-symbol": "^2.0.0",
"micromark-util-types": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/micromark-factory-destination": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
@@ -5524,6 +5785,24 @@
"node": ">=18"
}
},
"node_modules/remark-gfm": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
"integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"mdast-util-gfm": "^3.0.0",
"micromark-extension-gfm": "^3.0.0",
"remark-parse": "^11.0.0",
"remark-stringify": "^11.0.0",
"unified": "^11.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-parse": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
@@ -5557,6 +5836,21 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-stringify": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
"integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
"license": "MIT",
"dependencies": {
"@types/mdast": "^4.0.0",
"mdast-util-to-markdown": "^2.0.0",
"unified": "^11.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",

View File

@@ -26,6 +26,7 @@
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
"react-router": "^7.5.3",
"remark-gfm": "^4.0.1",
"spark-md5": "^3.0.2",
"tailwindcss": "^4.1.5"
},

View File

@@ -62,6 +62,7 @@ import KunApi, {
kunResourceTypeToString,
} from "../network/kun.ts";
import { Debounce } from "../utils/debounce.ts";
import remarkGfm from "remark-gfm";
export default function ResourcePage() {
const params = useParams();
@@ -448,6 +449,7 @@ function Article({ resource }: { resource: ResourceDetails }) {
return (
<article>
<Markdown
remarkPlugins={[remarkGfm]}
components={{
p: ({ node, ...props }) => {
if (
@@ -727,8 +729,8 @@ function FileTile({ file }: { file: RFile }) {
<MdOutlineArchive size={16} className={"inline-block"} />
{file.is_redirect ? t("Redirect") : fileSizeToString(file.size)}
</Badge>
{
file.hash && <Badge
{file.hash && (
<Badge
className={
"badge-soft badge-accent text-xs mr-2 break-all hidden sm:inline-flex"
}
@@ -737,7 +739,7 @@ function FileTile({ file }: { file: RFile }) {
<MdOutlineVerifiedUser size={16} className={"inline-block"} />
Md5: {file.hash}
</Badge>
}
)}
<DeleteFileDialog fileId={file.id} uploaderId={file.user.id} />
<UpdateFileInfoDialog file={file} />
</p>

49
go.mod
View File

@@ -1,23 +1,26 @@
module nysoure
go 1.24
go 1.25.0
require (
github.com/gofiber/fiber/v3 v3.0.0-beta.4
github.com/golang-jwt/jwt/v5 v5.2.2
golang.org/x/crypto v0.37.0
gorm.io/driver/sqlite v1.5.7
gorm.io/gorm v1.26.1
github.com/gofiber/fiber/v3 v3.0.0-rc.1
github.com/golang-jwt/jwt/v5 v5.3.0
golang.org/x/crypto v0.42.0
gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.31.0
)
require (
github.com/blevesearch/bleve v1.0.14
github.com/chai2010/webp v1.4.0
gorm.io/driver/mysql v1.5.7
github.com/disintegration/imaging v1.6.2
github.com/stretchr/testify v1.11.1
gorm.io/driver/mysql v1.6.0
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/RoaringBitmap/roaring v0.4.23 // indirect
github.com/blevesearch/bleve v1.0.14 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/mmap-go v1.0.2 // indirect
github.com/blevesearch/segment v0.9.0 // indirect
@@ -28,15 +31,17 @@ require (
github.com/blevesearch/zap/v14 v14.0.5 // indirect
github.com/blevesearch/zap/v15 v15.0.3 // indirect
github.com/couchbase/vellum v1.0.2 // indirect
github.com/disintegration/imaging v1.6.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/golang/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/steveyen/gtreap v0.1.0 // indirect
github.com/willf/bitset v1.1.10 // indirect
go.etcd.io/bbolt v1.3.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
@@ -51,11 +56,10 @@ require (
)
require (
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/gofiber/schema v1.3.0 // indirect
github.com/gofiber/utils/v2 v2.0.0-beta.8 // indirect
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/gofiber/schema v1.6.0 // indirect
github.com/gofiber/utils/v2 v2.0.0-rc.1 // indirect
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a
github.com/google/uuid v1.6.0
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
@@ -64,13 +68,12 @@ require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.28 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/tinylib/msgp v1.2.5 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/tinylib/msgp v1.3.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.61.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/image v0.28.0
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.26.0 // indirect
github.com/valyala/fasthttp v1.65.0 // indirect
golang.org/x/image v0.31.0
golang.org/x/net v0.43.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect
)

97
go.sum
View File

@@ -1,11 +1,14 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo=
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/blevesearch/bleve v1.0.14 h1:Q8r+fHTt35jtGXJUM0ULwM3Tzg+MRfyai4ZkWDy2xO4=
github.com/blevesearch/bleve v1.0.14/go.mod h1:e/LJTr+E7EaoVdkQZTfoz7dt4KoDNvDbLb8MSKuNTLQ=
github.com/blevesearch/blevex v1.0.0 h1:pnilj2Qi3YSEGdWgLj1Pn9Io7ukfXPoQcpAI1Bv8n/o=
github.com/blevesearch/blevex v1.0.0/go.mod h1:2rNVqoG2BZI8t1/P1awgTKnGlx5MP9ZbtEciQaNhswc=
github.com/blevesearch/cld2 v0.0.0-20200327141045-8b5f551d37f5/go.mod h1:PN0QNTLs9+j1bKy3d/GB/59wsNBFC4sWLWG3k69lWbc=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
@@ -36,6 +39,7 @@ github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37g
github.com/couchbase/vellum v1.0.2 h1:BrbP0NKiyDdndMPec8Jjhy0U47CZ0Lgx3xUC2r9rZqw=
github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
@@ -50,36 +54,36 @@ github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htX
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gofiber/fiber/v3 v3.0.0-beta.4 h1:KzDSavvhG7m81NIsmnu5l3ZDbVS4feCidl4xlIfu6V0=
github.com/gofiber/fiber/v3 v3.0.0-beta.4/go.mod h1:/WFUoHRkZEsGHyy2+fYcdqi109IVOFbVwxv1n1RU+kk=
github.com/gofiber/schema v1.3.0 h1:K3F3wYzAY+aivfCCEHPufCthu5/13r/lzp1nuk6mr3Q=
github.com/gofiber/schema v1.3.0/go.mod h1:YYwj01w3hVfaNjhtJzaqetymL56VW642YS3qZPhuE6c=
github.com/gofiber/utils/v2 v2.0.0-beta.8 h1:ZifwbHZqZO3YJsx1ZhDsWnPjaQ7C0YD20LHt+DQeXOU=
github.com/gofiber/utils/v2 v2.0.0-beta.8/go.mod h1:1lCBo9vEF4RFEtTgWntipnaScJZQiM8rrsYycLZ4n9c=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/gofiber/fiber/v3 v3.0.0-rc.1 h1:034MxesK6bqGkidP+QR+Ysc1ukOacBWOHCarCKC1xfg=
github.com/gofiber/fiber/v3 v3.0.0-rc.1/go.mod h1:hFdT00oT0XVuQH1/z2i5n1pl/msExHDUie1SsLOkCuM=
github.com/gofiber/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY=
github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s=
github.com/gofiber/utils/v2 v2.0.0-rc.1 h1:b77K5Rk9+Pjdxz4HlwEBnS7u5nikhx7armQB8xPds4s=
github.com/gofiber/utils/v2 v2.0.0-rc.1/go.mod h1:Y1g08g7gvST49bbjHJ1AVqcsmg93912R/tbKWhn6V3E=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b h1:EY/KpStFl60qA17CptGXhwfZ+k1sFNJIUNR8DdbcuUk=
github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -91,6 +95,7 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -125,8 +130,8 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
@@ -134,6 +139,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/shamaton/msgpack/v2 v2.3.0 h1:eawIa7lQmwRv0V6rdmL/5Ev9KdJHk07eQH3ceJi3BUw=
github.com/shamaton/msgpack/v2 v2.3.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
@@ -149,19 +156,21 @@ github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tebeka/snowball v0.4.2/go.mod h1:4IfL14h1lvwZcp1sfXuuc7/7yCsvVffTWxWxCLfFpYg=
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok=
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.61.0 h1:VV08V0AfoRaFurP1EWKvQQdPTZHiUzaVoulX1aBDgzU=
github.com/valyala/fasthttp v1.61.0/go.mod h1:wRIV/4cMwUPWnRcDno9hGnYZGh78QzODFfo1LTUhBog=
github.com/valyala/fasthttp v1.65.0 h1:j/u3uzFEGFfRxw79iYzJN+TteTJwbYkru9uDp3d0Yf8=
github.com/valyala/fasthttp v1.65.0/go.mod h1:P/93/YkKPMsKSnATEeELUCkG8a7Y+k99uxNHVbKINr4=
github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
@@ -173,17 +182,15 @@ go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
golang.org/x/image v0.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA=
golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -192,14 +199,13 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -207,10 +213,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw=
gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=

View File

@@ -70,3 +70,11 @@ func GetDB() *gorm.DB {
func IsReady() bool {
return ready
}
func Close() error {
sqlDB, err := db.DB()
if err != nil {
return err
}
return sqlDB.Close()
}

View File

@@ -100,6 +100,11 @@ func CreateFile(filename string, description string, resourceID uint, storageID
if err != nil {
return err
}
err = tx.Model(&model.Resource{}).Where("id = ?", resourceID).
UpdateColumn("modified_time", time.Now()).Error
if err != nil {
return err
}
return nil
})

View File

@@ -15,6 +15,7 @@ import (
func CreateResource(r model.Resource) (model.Resource, error) {
err := db.Transaction(func(tx *gorm.DB) error {
r.ModifiedTime = time.Now()
err := tx.Create(&r).Error
if err != nil {
return err
@@ -71,9 +72,9 @@ func GetResourceList(page, pageSize int, sort model.RSort) ([]model.Resource, in
order := ""
switch sort {
case model.RSortTimeAsc:
order = "created_at ASC"
order = "modified_time ASC"
case model.RSortTimeDesc:
order = "created_at DESC"
order = "modified_time DESC"
case model.RSortViewsAsc:
order = "views ASC"
case model.RSortViewsDesc:
@@ -83,7 +84,7 @@ func GetResourceList(page, pageSize int, sort model.RSort) ([]model.Resource, in
case model.RSortDownloadsDesc:
order = "downloads DESC"
default:
order = "created_at DESC" // Default sort order
order = "modified_time DESC" // Default sort order
}
if err := db.Offset((page - 1) * pageSize).Limit(pageSize).Preload("User").Preload("Images").Preload("Tags").Order(order).Find(&resources).Error; err != nil {
@@ -102,6 +103,7 @@ func UpdateResource(r model.Resource) error {
r.Images = nil
r.Tags = nil
r.Files = nil
r.ModifiedTime = time.Now()
if err := db.Save(&r).Error; err != nil {
return err
}

View File

@@ -19,7 +19,7 @@ func CreateTag(tag string) (model.Tag, error) {
if err := db.Create(&t).Error; err != nil {
return model.Tag{}, err
}
return t, nil
return GetTagByID(t.ID)
}
func CreateTagWithType(tag string, tagType string) (model.Tag, error) {
@@ -82,13 +82,34 @@ func GetTagByName(name string) (model.Tag, error) {
}
func SetTagInfo(id uint, description string, aliasOf *uint, tagType string) error {
// Get the tag information
old, err := GetTagByID(id)
if err != nil {
return err
}
if aliasOf != nil && len(old.Aliases) > 0 {
return model.NewRequestError("Tag already has aliases, cannot set alias_of")
// If the alias tag is an alias itself, we need to find its root tag
if aliasOf != nil {
tag, err := GetTagByID(*aliasOf)
if err != nil {
return err
}
if tag.AliasOf != nil {
aliasOf = tag.AliasOf
}
}
// If the tag has aliases, we need to update their alias_of field
if aliasOf != nil && len(old.Aliases) > 0 {
for _, alias := range old.Aliases {
err := db.Model(&alias).Update("alias_of", *aliasOf).Error
if err != nil {
return err
}
}
}
// Update the tag information
t := model.Tag{Model: gorm.Model{
ID: id,
}, Description: description, Type: tagType, AliasOf: aliasOf}
@@ -113,24 +134,51 @@ func ListTags() ([]model.Tag, error) {
// SetTagAlias sets a tag with the given ID having the given alias.
func SetTagAlias(tagID uint, alias string) error {
// Set a tag as an alias of another tag
var t model.Tag
if err := db.Where("name = ?", alias).First(&t).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// create
newTag, err := CreateTag(alias)
exists, err := ExistsTagByID(tagID)
if err != nil {
return err
}
t = newTag
} else {
if !exists {
return model.NewNotFoundError("Tag not found")
}
exists, err = ExistsTag(alias)
if err != nil {
return err
}
if !exists {
// Create the alias tag if it does not exist
_, err := CreateTag(alias)
if err != nil {
return err
}
}
if t.ID == tagID {
return model.NewRequestError("Tag cannot be an alias of itself")
// Get the alias tag
tag, err := GetTagByName(alias)
if err != nil {
return err
}
return db.Model(&t).Update("alias_of", tagID).Error
// If the alias tag is an alias itself, we need to find its root tag
if tag.AliasOf != nil {
tag, err = GetTagByID(*tag.AliasOf)
if err != nil {
return err
}
}
// If the tag has aliases, we need to update their alias_of field
for _, alias := range tag.Aliases {
err := db.Model(&alias).Update("alias_of", tagID).Error
if err != nil {
return err
}
}
tag.Aliases = nil
// A tag cannot be an alias of itself
if tag.ID == tagID {
return model.NewRequestError("A tag cannot be an alias of itself")
}
// Set the alias_of field of the tag
return db.Model(&tag).Update("alias_of", tagID).Error
}
// RemoveTagAliasOf sets a tag is an independent tag, removing its alias relationship.
@@ -180,3 +228,11 @@ func ExistsTag(name string) (bool, error) {
}
return count > 0, nil
}
func ExistsTagByID(id uint) (bool, error) {
var count int64
if err := db.Model(&model.Tag{}).Where("id = ?", id).Count(&count).Error; err != nil {
return false, err
}
return count > 0, nil
}

109
server/dao/tag_test.go Normal file
View File

@@ -0,0 +1,109 @@
package dao
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestTag(t *testing.T) {
// Create tags
tag1, err := CreateTag("test1")
assert.Nil(t, err)
tag2, err := CreateTag("test2")
assert.Nil(t, err)
tag3, err := CreateTagWithType("test3", "type1")
assert.Nil(t, err)
// Get tag by ID
fetchedTag, err := GetTagByID(tag1.ID)
assert.Nil(t, err)
assert.Equal(t, tag1.Name, fetchedTag.Name)
// Get tag by Name
fetchedTag, err = GetTagByName(tag2.Name)
assert.Nil(t, err)
assert.Equal(t, tag2.ID, fetchedTag.ID)
// Search tags
tags, err := SearchTag("test", true)
assert.Nil(t, err)
assert.GreaterOrEqual(t, len(tags), 3)
// Update tag
err = SetTagInfo(tag1.ID, "updated description", nil, "updated type")
assert.Nil(t, err)
updatedTag, err := GetTagByID(tag1.ID)
assert.Nil(t, err)
assert.Equal(t, "updated description", updatedTag.Description)
assert.Equal(t, "updated type", updatedTag.Type)
// Set tag alias
err = SetTagAlias(tag1.ID, tag2.Name)
assert.Nil(t, err)
err = SetTagAlias(tag1.ID, tag3.Name)
assert.Nil(t, err)
err = SetTagAlias(tag1.ID, "test4")
assert.Nil(t, err)
tag4, err := GetTagByName("test4")
assert.Nil(t, err)
tag1, err = GetTagByID(tag1.ID)
assert.Nil(t, err)
aliasesIDs := []uint{}
for _, alias := range tag1.Aliases {
aliasesIDs = append(aliasesIDs, alias.ID)
}
assert.Equal(t, []uint{tag2.ID, tag3.ID, tag4.ID}, aliasesIDs)
// let a tag which has alias point to another tag
tag5, err := CreateTag("test5")
assert.Nil(t, err)
err = SetTagAlias(tag5.ID, tag1.Name)
assert.Nil(t, err)
tag1, err = GetTagByID(tag1.ID)
assert.Nil(t, err)
tag2, err = GetTagByID(tag2.ID)
assert.Nil(t, err)
tag3, err = GetTagByID(tag3.ID)
assert.Nil(t, err)
tag4, err = GetTagByID(tag4.ID)
assert.Nil(t, err)
tag5, err = GetTagByID(tag5.ID)
assert.Nil(t, err)
assert.Empty(t, tag1.Aliases)
assert.Equal(t, &tag5.ID, tag1.AliasOf)
assert.Equal(t, &tag5.ID, tag2.AliasOf)
assert.Equal(t, &tag5.ID, tag3.AliasOf)
assert.Equal(t, &tag5.ID, tag4.AliasOf)
assert.Nil(t, tag5.AliasOf)
// Same operation as above, but using `SetTagInfo`
tag6, err := CreateTag("test6")
assert.Nil(t, err)
err = SetTagInfo(tag5.ID, "", &tag6.ID, "")
assert.Nil(t, err)
tag1, err = GetTagByID(tag1.ID)
assert.Nil(t, err)
tag2, err = GetTagByID(tag2.ID)
assert.Nil(t, err)
tag3, err = GetTagByID(tag3.ID)
assert.Nil(t, err)
tag4, err = GetTagByID(tag4.ID)
assert.Nil(t, err)
tag5, err = GetTagByID(tag5.ID)
assert.Nil(t, err)
tag6, err = GetTagByID(tag6.ID)
assert.Nil(t, err)
assert.Equal(t, &tag6.ID, tag1.AliasOf)
assert.Equal(t, &tag6.ID, tag2.AliasOf)
assert.Equal(t, &tag6.ID, tag3.AliasOf)
assert.Equal(t, &tag6.ID, tag4.AliasOf)
assert.Equal(t, &tag6.ID, tag5.AliasOf)
assert.Empty(t, tag5.Aliases)
assert.Nil(t, tag6.AliasOf)
// cleanup
_ = Close()
_ = os.Remove("test.db")
}

View File

@@ -20,6 +20,7 @@ type Resource struct {
Views uint
Downloads uint
Comments uint
ModifiedTime time.Time
}
type Link struct {

View File

@@ -101,3 +101,14 @@ func SearchResource(keyword string) ([]uint, error) {
return results, nil
}
func IsStopWord(word string) bool {
mapping := bleve.NewIndexMapping()
analyzerName := mapping.DefaultAnalyzer
analyzer := mapping.AnalyzerNamed(analyzerName)
if analyzer == nil {
return false
}
tokens := analyzer.Analyze([]byte(word))
return len(tokens) == 0
}

View File

@@ -0,0 +1,185 @@
package search
import (
"nysoure/server/dao"
"nysoure/server/model"
"os"
"testing"
"github.com/blevesearch/bleve"
"gorm.io/gorm"
)
func Init() {
err := index.Close()
if err != nil {
panic(err)
}
_ = os.RemoveAll("search_test.bleve")
mapper := bleve.NewIndexMapping()
index, err = bleve.New("search_test.bleve", mapper)
if err != nil {
panic(err)
}
}
func TearDown() {
err := index.Close()
if err != nil {
panic(err)
}
dao.Close()
os.RemoveAll("search_test.bleve")
os.Remove("test.db")
}
func TestSearchResource(t *testing.T) {
Init()
defer TearDown()
resources := []model.Resource{
// normal cases
{Model: gorm.Model{ID: 1}, Title: "The Great Adventure", AlternativeTitles: []string{"Adventure Time", "The Big Adventure"}},
{Model: gorm.Model{ID: 2}, Title: "Mystery of the Lost City", AlternativeTitles: []string{"Lost City Chronicles"}},
{Model: gorm.Model{ID: 3}, Title: "Romance in Paris", AlternativeTitles: []string{"Love in Paris", "Parisian Romance"}},
{Model: gorm.Model{ID: 4}, Title: "Sci-Fi Extravaganza", AlternativeTitles: []string{"Future World", "Sci-Fi Saga"}},
{Model: gorm.Model{ID: 5}, Title: "Comedy Nights", AlternativeTitles: []string{"Laugh Out Loud", "Comedy Central"}},
// With special characters
{Model: gorm.Model{ID: 6}, Title: "Action & Adventure", AlternativeTitles: []string{"Action-Packed", "Adventure Time!"}},
{Model: gorm.Model{ID: 7}, Title: "Horror: The Awakening", AlternativeTitles: []string{"Scary Movie", "Horror Nights"}},
{Model: gorm.Model{ID: 8}, Title: "Drama @ Home", AlternativeTitles: []string{"Home Stories", "Dramatic Tales"}},
{Model: gorm.Model{ID: 9}, Title: "Fantasy #1", AlternativeTitles: []string{"Fantasy World", "Magical Tales"}},
{Model: gorm.Model{ID: 10}, Title: "Thriller ~Uncut~"},
{Model: gorm.Model{ID: 11}, Title: "Epic ~ Saga ~"},
{Model: gorm.Model{ID: 12}, Title: "Journey - Dawn -", AlternativeTitles: []string{"Dawn Adventures", "Journey Chronicles"}},
{Model: gorm.Model{ID: 13}, Title: "Legends -Rise-", AlternativeTitles: []string{"Rise of Legends", "Legendary Tales"}},
{Model: gorm.Model{ID: 14}, Title: "Chronicles: Time", AlternativeTitles: []string{"Time Chronicles", "Chronicles of Ages"}},
}
// Add resources to index
for _, r := range resources {
err := AddResourceToIndex(r)
if err != nil {
t.Fatalf("Failed to add resource ID %d to index: %v", r.ID, err)
}
}
tests := []struct {
query string
expectedIDs []uint
unexpectedID uint
}{
// Basic searches
{query: "Adventure", expectedIDs: []uint{1, 6}},
{query: "Mystery", expectedIDs: []uint{2}},
{query: "Romance", expectedIDs: []uint{3}},
{query: "Sci-Fi", expectedIDs: []uint{4}},
{query: "Comedy", expectedIDs: []uint{5}},
// Exact matches
{query: "The Great Adventure", expectedIDs: []uint{1}},
{query: "Mystery of the Lost City", expectedIDs: []uint{2}},
{query: "Romance in Paris", expectedIDs: []uint{3}},
{query: "Sci-Fi Extravaganza", expectedIDs: []uint{4}},
{query: "Comedy Nights", expectedIDs: []uint{5}},
{query: "Action & Adventure", expectedIDs: []uint{6}},
{query: "Horror: The Awakening", expectedIDs: []uint{7}},
{query: "Drama @ Home", expectedIDs: []uint{8}},
{query: "Fantasy #1", expectedIDs: []uint{9}},
{query: "Thriller ~Uncut~", expectedIDs: []uint{10}},
{query: "Epic ~ Saga ~", expectedIDs: []uint{11}},
{query: "Journey - Dawn -", expectedIDs: []uint{12}},
{query: "Legends -Rise-", expectedIDs: []uint{13}},
{query: "Chronicles: Time", expectedIDs: []uint{14}},
// Searches with special characters
{query: "Action & Adventure", expectedIDs: []uint{6}},
{query: "Horror: The Awakening", expectedIDs: []uint{7}},
{query: "Drama @ Home", expectedIDs: []uint{8}},
{query: "Fantasy #1", expectedIDs: []uint{9}},
{query: "Thriller ~Uncut~", expectedIDs: []uint{10}},
{query: "Epic ~ Saga ~", expectedIDs: []uint{11}},
{query: "Journey - Dawn -", expectedIDs: []uint{12}},
{query: "Legends -Rise-", expectedIDs: []uint{13}},
{query: "Chronicles: Time", expectedIDs: []uint{14}},
// Case insensitivity
{query: "adventure", expectedIDs: []uint{1, 6}},
{query: "MYSTERY", expectedIDs: []uint{2}},
{query: "rOmAnCe", expectedIDs: []uint{3}},
// Searches using alternative titles
{query: "Adventure Time", expectedIDs: []uint{1, 6}},
{query: "Lost City Chronicles", expectedIDs: []uint{2}},
{query: "Love in Paris", expectedIDs: []uint{3}},
{query: "Future World", expectedIDs: []uint{4}},
{query: "Laugh Out Loud", expectedIDs: []uint{5}},
// Searches with special characters in alternative titles
{query: "Action-Packed", expectedIDs: []uint{6}},
{query: "Scary Movie", expectedIDs: []uint{7}},
{query: "Home Stories", expectedIDs: []uint{8}},
{query: "Fantasy World", expectedIDs: []uint{9}},
{query: "Epic Tales", expectedIDs: []uint{11}},
{query: "Dawn Adventures", expectedIDs: []uint{12}},
{query: "Rise of Legends", expectedIDs: []uint{13}},
{query: "Time Chronicles", expectedIDs: []uint{14}},
// Searches with special characters in queries
{query: "Horror:", expectedIDs: []uint{7}},
{query: "@ Home", expectedIDs: []uint{8}},
{query: "#1", expectedIDs: []uint{9}},
{query: "~Uncut~", expectedIDs: []uint{10}},
{query: "Uncut", expectedIDs: []uint{10}},
{query: "~ Saga ~", expectedIDs: []uint{11}},
{query: "Saga", expectedIDs: []uint{11}},
{query: "- Dawn -", expectedIDs: []uint{12}},
{query: "-Rise-", expectedIDs: []uint{13}},
}
for _, test := range tests {
t.Run(test.query, func(t *testing.T) {
resultIDs, err := SearchResource(test.query)
if err != nil {
t.Fatalf("Search failed for query '%s': %v", test.query, err)
}
// Check for expected IDs
for _, expectedID := range test.expectedIDs {
found := false
for _, resultID := range resultIDs {
if resultID == expectedID {
found = true
break
}
}
if !found {
t.Errorf("Expected ID %d not found in results for query '%s'", expectedID, test.query)
}
}
// Check for unexpected ID
if test.unexpectedID != 0 {
for _, resultID := range resultIDs {
if resultID == test.unexpectedID {
t.Errorf("Unexpected ID %d found in results for query '%s'", test.unexpectedID, test.query)
}
}
}
})
}
}
func TestIsStopWord(t *testing.T) {
Init()
defer TearDown()
stopWords := []string{"the", "is", "at", "which", "on", "and", "a", "an", "in", "to", "of"}
nonStopWords := []string{"adventure", "mystery", "romance", "sci-fi", "comedy", "action", "horror"}
for _, word := range stopWords {
if !IsStopWord(word) {
t.Errorf("Expected '%s' to be identified as a stop word", word)
}
}
for _, word := range nonStopWords {
if IsStopWord(word) {
t.Errorf("Expected '%s' to not be identified as a stop word", word)
}
}
}

View File

@@ -291,6 +291,19 @@ func SearchResource(query string, page int) ([]model.ResourceView, int, error) {
// split query to search
keywords := splitQuery(query)
var temp []uint
haveTag := false
for _, keyword := range keywords {
if len([]rune(keyword)) <= maxTagLength {
exists, err := dao.ExistsTag(keyword)
if err != nil {
return nil, 0, err
}
if exists {
haveTag = true
}
}
}
if haveTag {
first := true
for _, keyword := range keywords {
if keyword == "" {
@@ -304,6 +317,9 @@ func SearchResource(query string, page int) ([]model.ResourceView, int, error) {
if err != nil {
return nil, 0, err
}
if len(res) == 0 && search.IsStopWord(keyword) {
continue
}
if first {
temp = utils.RemoveDuplicate(res)
first = false
@@ -320,6 +336,13 @@ func SearchResource(query string, page int) ([]model.ResourceView, int, error) {
temp = temp1
}
}
} else {
res, err := searchWithKeyword(query)
if err != nil {
return nil, 0, err
}
temp = res
}
resources = append(resources, temp...)
resources = utils.RemoveDuplicate(resources)