@@ -20,13 +20,7 @@ import (
2020type results struct {
2121 Entries []data.Entry `json:"entries"`
2222
23- Query struct {
24- Query string `json:"query"`
25- FromLang string `json:"from_lang"`
26- ToLang string `json:"to_lang"`
27- Types []string `json:"types"`
28- Tags []string `json:"tags"`
29- } `json:"query"`
23+ Query data.Query `json:"query"`
3024
3125 // Pagination fields.
3226 paginator.Set
@@ -44,31 +38,25 @@ type glossary struct {
4438
4539// okResp represents the HTTP response wrapper.
4640type okResp struct {
47- Data interface {} `json:"data"`
48- }
49-
50- type httpResp struct {
51- Status string `json:"status"`
52- Message string `json:"message,omitempty"`
53- Data interface {} `json:"data,omitempty"`
41+ Data any `json:"data"`
5442}
5543
5644// handleSearch performs a search and responds with JSON results.
5745func handleSearch (c echo.Context ) error {
58- isAuthed := c .Get (isAuthed ) != nil
46+ var (
47+ app = c .Get ("app" ).(* App )
48+ isAuthed = c .Get (isAuthed ) != nil
49+ )
5950
60- _ , out , err := doSearch (c , isAuthed )
51+ // Prepare the query.
52+ query , err := prepareQuery (c )
6153 if err != nil {
62- var s int
63-
64- // If out is nil, it's a non 500 "soft" error.
65- if out != nil {
66- s = http .StatusBadRequest
67- } else {
68- s = http .StatusInternalServerError
69- }
54+ return echo .NewHTTPError (http .StatusBadRequest , err .Error ())
55+ }
7056
71- return echo .NewHTTPError (s , err .Error ())
57+ out , err := doSearch (query , isAuthed , app .pgAPI , app )
58+ if err != nil {
59+ return echo .NewHTTPError (http .StatusInternalServerError , err .Error ())
7260 }
7361
7462 return c .JSON (http .StatusOK , okResp {out })
@@ -158,90 +146,107 @@ func handleServeBundle(c echo.Context, bundleType string, staticDir string) erro
158146 return c .Blob (http .StatusOK , contentType , buf .Bytes ())
159147}
160148
161- // doSearch is a helper function that takes an HTTP query context,
162- // gets search params from it, performs a search and returns results .
163- func doSearch (c echo.Context , isAuthed bool ) (data.Query , * results , error ) {
149+ // prepareQuery extracts and validates search parameters from the HTTP context.
150+ // Returns a filled data.Query struct ready for searching .
151+ func prepareQuery (c echo.Context ) (data.Query , error ) {
164152 var (
165153 app = c .Get ("app" ).(* App )
166154
167155 fromLang = c .Param ("fromLang" )
168156 toLang = c .Param ("toLang" )
169- q = strings .TrimSpace (c .Param ("q" ))
170-
171- qp = c .Request ().URL .Query ()
172- pg = app .resultsPg .NewFromURL (qp )
173- out = & results {}
157+ qStr = strings .TrimSpace (c .Param ("q" ))
174158 )
175159
176- // Query from /path/:query
177- q , err := url .QueryUnescape (q )
160+ // Scan query params.
161+ var q data.Query
162+ if err := c .Bind (& q ); err != nil {
163+ return data.Query {}, fmt .Errorf ("error parsing query: %v" , err )
164+ }
165+
166+ // Query string from /path/:query
167+ qStr , err := url .QueryUnescape (qStr )
178168 if err != nil {
179- return data.Query {}, nil , fmt .Errorf ("error parsing query: %v" , err )
169+ return data.Query {}, fmt .Errorf ("error parsing query: %v" , err )
180170 }
181- q = strings .TrimSpace (q )
182- if q == "" {
183- v , err := url .QueryUnescape (qp . Get ( "q" ) )
171+ qStr = strings .TrimSpace (qStr )
172+ if qStr == "" {
173+ v , err := url .QueryUnescape (q . Query )
184174 if err != nil {
185- return data.Query {}, nil , fmt .Errorf ("error parsing query: %v" , err )
175+ return data.Query {}, fmt .Errorf ("error parsing query: %v" , err )
186176 }
187- q = strings .TrimSpace (v )
177+ qStr = strings .TrimSpace (v )
178+ }
179+ if qStr == "" {
180+ return data.Query {}, errors .New ("no query given" )
188181 }
189182
190- if q == "" {
191- return data.Query {}, nil , errors .New ("no query given" )
183+ // Languages not in path?
184+ if fromLang == "" {
185+ fromLang = q .FromLang
186+ }
187+ if toLang == "" {
188+ toLang = q .ToLang
192189 }
193190
191+ // Check languages.
194192 if _ , ok := app .data .Langs [fromLang ]; ! ok {
195- return data.Query {}, nil , errors .New ("unknown `from` language" )
193+ return data.Query {}, errors .New ("unknown `from` language" )
196194 }
197-
198195 if toLang == "*" {
199196 toLang = ""
200197 } else {
201198 if _ , ok := app .data .Langs [toLang ]; ! ok {
202- return data.Query {}, nil , errors .New ("unknown `to` language" )
199+ return data.Query {}, errors .New ("unknown `to` language" )
203200 }
204201 }
205202
206- // Search query.
207- query := data.Query {
208- FromLang : fromLang ,
209- ToLang : toLang ,
210- Types : qp ["type" ],
211- Tags : qp ["tag" ],
212- Query : q ,
213- Status : data .StatusEnabled ,
214- Offset : pg .Offset ,
215- Limit : pg .Limit ,
203+ // Check types.
204+ for _ , t := range q .Types {
205+ if _ , ok := app .data .Langs [fromLang ].Types [t ]; ! ok {
206+ return data.Query {}, fmt .Errorf ("unknown type %s" , t )
207+ }
216208 }
217209
218- if err = validateSearchQuery (query , app .data .Langs ); err != nil {
219- return query , out , err
210+ // Final query.
211+ q .Query = qStr
212+ q .FromLang = fromLang
213+ q .ToLang = toLang
214+ q .Status = data .StatusEnabled
215+
216+ if q .Types == nil {
217+ q .Types = []string {}
220218 }
219+ if q .Tags == nil {
220+ q .Tags = []string {}
221+ }
222+
223+ return q , nil
224+ }
225+
226+ // doSearch takes a prepared query and performs the search, returning results.
227+ func doSearch (q data.Query , isAuthed bool , pgn * paginator.Paginator , app * App ) (* results , error ) {
228+ // Pagination.
229+ pg := pgn .New (q .Page , q .PerPage )
230+ q .Offset = pg .Offset
231+ q .Limit = pg .Limit
221232
222233 // Search and compose results.
223- out = & results {
224- Entries : []data.Entry {},
225- }
226- res , total , err := app .data .Search (query )
234+ out := & results {Entries : []data.Entry {}}
235+ res , total , err := app .data .Search (q )
227236 if err != nil {
228237 app .lo .Printf ("error querying db: %v" , err )
229- return query , nil , errors .New ("error querying db" )
238+ return nil , errors .New ("error querying db" )
230239 }
231240
232241 if len (res ) == 0 {
233- return query , out , nil
242+ out .Query = q
243+
244+ return out , nil
234245 }
235246
236247 // Load relations into the matches.
237- if err := app .data .SearchAndLoadRelations (res , data.Query {
238- ToLang : toLang ,
239- Offset : pg .Offset ,
240- Limit : pg .Limit ,
241- Status : data .StatusEnabled ,
242- }); err != nil {
243- app .lo .Printf ("error querying db for defs: %v" , err )
244- return query , nil , errors .New ("error querying db for definitions" )
248+ if err := app .data .SearchAndLoadRelations (res , q ); err != nil {
249+ return nil , errors .New ("error querying db for definitions" )
245250 }
246251
247252 // If this is an un-authenticated query, hide the numerical IDs.
@@ -256,28 +261,17 @@ func doSearch(c echo.Context, isAuthed bool) (data.Query, *results, error) {
256261 }
257262 }
258263
264+ // Calculate pagination.
259265 pg .SetTotal (total )
260266
261- out .Query .FromLang = fromLang
262- out .Query .ToLang = toLang
263- out .Query .Types = qp ["type" ]
264- out .Query .Tags = qp ["tag" ]
265- out .Query .Query = q
266-
267- if out .Query .Types == nil {
268- out .Query .Types = []string {}
269- }
270- if out .Query .Tags == nil {
271- out .Query .Tags = []string {}
272- }
273-
267+ out .Query = q
274268 out .Entries = res
275269 out .Page = pg .Page
276270 out .PerPage = pg .PerPage
277271 out .TotalPages = pg .TotalPages
278272 out .Total = total
279273
280- return query , out , nil
274+ return out , nil
281275}
282276
283277// getGlossaryWords is a helper function that takes an HTTP query context,
0 commit comments