ddd
This commit is contained in:
1 parent
d72ef520cd
commit
b96e1e19df
3 files changed
+90
-1
No files matched your search
@@ -131,6 +131,24 @@ func main() {
|
|||||||
}
|
}
|
||||||
writeJSON(w, http.StatusOK, map[string][]flashcardSummary{"flashcards": summaries})
|
writeJSON(w, http.StatusOK, map[string][]flashcardSummary{"flashcards": summaries})
|
||||||
})))
|
})))
|
||||||
|
mux.Handle("/api/v1/flashcards/search", withAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodGet {
|
||||||
|
writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
query := r.URL.Query().Get("q")
|
||||||
|
if query == "" {
|
||||||
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing query parameter 'q'"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
results, err := searchEngine.Search(r.Context(), query, 50)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("search flashcards", zap.Error(err))
|
||||||
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "internal error"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeJSON(w, http.StatusOK, map[string]any{"results": results, "query": query})
|
||||||
|
})))
|
||||||
mux.Handle("/api/v1/flashcards/", withAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
mux.Handle("/api/v1/flashcards/", withAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodGet {
|
if r.Method != http.MethodGet {
|
||||||
writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
|
writeJSON(w, http.StatusMethodNotAllowed, map[string]string{"error": "method not allowed"})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import m from "https://esm.sh/mithril@2.3.8";
|
import m from "https://esm.sh/mithril@2.3.8";
|
||||||
|
|
||||||
const API = "/api/v1/flashcards";
|
const API = "/api/v1/flashcards";
|
||||||
|
const SEARCH_API = "/api/v1/flashcards/search";
|
||||||
const AUTH = "/api/v1/auth";
|
const AUTH = "/api/v1/auth";
|
||||||
const USERS = "/api/v1/users";
|
const USERS = "/api/v1/users";
|
||||||
const SESSIONS = "/api/v1/sessions";
|
const SESSIONS = "/api/v1/sessions";
|
||||||
@@ -221,6 +222,8 @@ const List = {
|
|||||||
oninit: async function (v) {
|
oninit: async function (v) {
|
||||||
v.state.cards = [];
|
v.state.cards = [];
|
||||||
v.state.loading = true;
|
v.state.loading = true;
|
||||||
|
v.state.query = "";
|
||||||
|
v.state.searching = false;
|
||||||
const res = await fetch(API);
|
const res = await fetch(API);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
v.state.cards = data.flashcards || [];
|
v.state.cards = data.flashcards || [];
|
||||||
@@ -235,10 +238,78 @@ const List = {
|
|||||||
m("p.cyber-text-muted.cyber-mt-md", "loading\u2026"),
|
m("p.cyber-text-muted.cyber-mt-md", "loading\u2026"),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const doSearch = async () => {
|
||||||
|
const q = v.state.query.trim();
|
||||||
|
if (!q) {
|
||||||
|
v.state.searching = true;
|
||||||
|
const res = await fetch(API);
|
||||||
|
const data = await res.json();
|
||||||
|
v.state.cards = data.flashcards || [];
|
||||||
|
v.state.searching = false;
|
||||||
|
v.state.page = 1;
|
||||||
|
m.redraw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
v.state.searching = true;
|
||||||
|
m.redraw();
|
||||||
|
const res = await fetch(SEARCH_API + "?q=" + encodeURIComponent(q));
|
||||||
|
const data = await res.json();
|
||||||
|
v.state.cards = (data.results || []).map((r) => ({
|
||||||
|
id: r.id,
|
||||||
|
version: r.version,
|
||||||
|
note: r.note,
|
||||||
|
tags: r.tags,
|
||||||
|
host: r.host,
|
||||||
|
user: r.user,
|
||||||
|
score: r.score,
|
||||||
|
}));
|
||||||
|
v.state.searching = false;
|
||||||
|
v.state.page = 1;
|
||||||
|
m.redraw();
|
||||||
|
};
|
||||||
|
|
||||||
const totalPages = Math.max(1, Math.ceil(v.state.cards.length / PAGE_SIZE));
|
const totalPages = Math.max(1, Math.ceil(v.state.cards.length / PAGE_SIZE));
|
||||||
const page = Math.min(v.state.page, totalPages);
|
const page = Math.min(v.state.page, totalPages);
|
||||||
const slice = v.state.cards.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
|
const slice = v.state.cards.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
|
||||||
return m(".cyber-p-lg", { style: "max-width:720px;margin:0 auto" }, [
|
return m(".cyber-p-lg", { style: "max-width:720px;margin:0 auto" }, [
|
||||||
|
m(
|
||||||
|
"form.cyber-flex.cyber-gap-sm.cyber-mb-md",
|
||||||
|
{
|
||||||
|
onsubmit: (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
doSearch();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
m("input.cyber-input", {
|
||||||
|
style: "flex:1",
|
||||||
|
placeholder: "Search flashcards\u2026",
|
||||||
|
value: v.state.query,
|
||||||
|
oninput: (e) => {
|
||||||
|
v.state.query = e.target.value;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
m(
|
||||||
|
"button.cyber-btn.cyber-btn--primary",
|
||||||
|
{ type: "submit", disabled: v.state.searching },
|
||||||
|
v.state.searching ? "..." : "Search",
|
||||||
|
),
|
||||||
|
v.state.query
|
||||||
|
? m(
|
||||||
|
"button.cyber-btn.cyber-btn--ghost",
|
||||||
|
{
|
||||||
|
type: "button",
|
||||||
|
onclick: () => {
|
||||||
|
v.state.query = "";
|
||||||
|
doSearch();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Clear",
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
],
|
||||||
|
),
|
||||||
m(Pager, {
|
m(Pager, {
|
||||||
page,
|
page,
|
||||||
total: totalPages,
|
total: totalPages,
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ func (s *sqliteStore) listAll(ctx context.Context) ([]flashcardSummary, error) {
|
|||||||
WHERE f.version = (SELECT MAX(f2.version) FROM flashcard f2 WHERE f2.public_id = f.public_id)
|
WHERE f.version = (SELECT MAX(f2.version) FROM flashcard f2 WHERE f2.public_id = f.public_id)
|
||||||
AND f.delete_timestamp IS NULL
|
AND f.delete_timestamp IS NULL
|
||||||
AND f.public_id != 0
|
AND f.public_id != 0
|
||||||
ORDER BY f.public_id`,
|
ORDER BY f.created DESC`,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("list: %w", err)
|
return nil, fmt.Errorf("list: %w", err)
|
||||||
|
|||||||
Reference in new issue
Block a user