+1
-1
src/entity/user.v
+1
-1
src/entity/user.v
···
52
52
id: util.or_throw[string](row.vals[0]).int()
53
53
username: util.or_throw[string](row.vals[1])
54
54
nickname: if row.vals[2] == none { ?string(none) } else {
55
-
util.or_throw[string](row.vals[3])
55
+
util.or_throw[string](row.vals[2])
56
56
}
57
57
password: 'haha lol, nope'
58
58
password_salt: 'haha lol, nope'
+10
-2
src/static/js/search.js
+10
-2
src/static/js/search.js
···
1
-
const search = async (query, limit, offset) => {
2
-
const data = await fetch(`/api/search?query=${query}&limit=${limit}&offset=${offset}`, {
1
+
const search_posts = async (query, limit, offset) => {
2
+
const data = await fetch(`/api/post/search?query=${query}&limit=${limit}&offset=${offset}`, {
3
+
method: 'GET'
4
+
})
5
+
const json = await data.json()
6
+
return json
7
+
}
8
+
9
+
const search_users = async (query, limit, offset) => {
10
+
const data = await fetch(`/api/user/search?query=${query}&limit=${limit}&offset=${offset}`, {
3
11
method: 'GET'
4
12
})
5
13
const json = await data.json()
+5
src/templates/components/user_card_mini.html
+5
src/templates/components/user_card_mini.html
+112
-49
src/templates/search.html
+112
-49
src/templates/search.html
···
7
7
8
8
<div>
9
9
<input type="text" name="query" id="query">
10
+
<div>
11
+
<p>search for:</p>
12
+
<input type="radio" name="search-for" id="search-for-posts" value="posts" checked aria-checked>
13
+
<label for="search-for-posts">posts</label>
14
+
<input type="radio" name="search-for" id="search-for-users" value="users">
15
+
<label for="search-for-users">users</label>
16
+
</div>
17
+
<br>
10
18
<button id="search">search</button>
11
19
</div>
12
20
···
39
47
offset = 0
40
48
}
41
49
42
-
document.getElementById('search').addEventListener('click', async () => {
43
-
results.innerHTML = '' // yeet the children!
44
-
pages.innerHTML = '' // yeet more children!
50
+
const add_post_result = result => {
51
+
// same as components/post_mini.html except js
52
+
const element = document.createElement('div')
53
+
element.classList.add('post', 'post-mini')
54
+
const p = document.createElement('p')
55
+
56
+
const user_link = document.createElement('a')
57
+
user_link.href = '/user/' + result.author.username
58
+
const user_text = document.createElement('strong')
59
+
user_text.innerText = get_display_name(result.author)
60
+
user_link.appendChild(user_text)
61
+
p.appendChild(user_link)
62
+
63
+
p.innerHTML += ': '
64
+
65
+
const post_link = document.createElement('a')
66
+
post_link.href = '/post/' + result.post.id
67
+
post_link.innerText = result.post.title
68
+
p.appendChild(post_link)
69
+
70
+
element.appendChild(p)
71
+
results.appendChild(element)
72
+
}
73
+
74
+
const add_user_result = user => {
75
+
const element = document.createElement('div')
76
+
const p = document.createElement('p')
77
+
const user_link = document.createElement('a')
78
+
user_link.href = '/user/' + user.username
79
+
user_link.innerText = get_display_name(user)
80
+
p.appendChild(user_link)
81
+
element.appendChild(p)
82
+
results.appendChild(element)
83
+
}
45
84
46
-
console.log('search: ', query.value, limit, offset)
85
+
const add_pages = () => {
86
+
// creates a separator
87
+
const sep = () => {
88
+
const span = document.createElement('span')
89
+
span.innerText = ' - '
90
+
pages.appendChild(span)
91
+
}
47
92
48
-
const search_results = await search(query.value, limit, offset)
49
-
if (search_results.length >= 0) {
50
-
for (result of search_results) {
51
-
// same as components/post_mini.html except js
52
-
const element = document.createElement('div')
53
-
element.classList.add('post', 'post-mini')
54
-
const p = document.createElement('p')
93
+
const first_link = document.createElement('a')
94
+
// we escape the $ here because otherwise V will try to perform replacements at compile-time.
95
+
//todo: report this, this behaviour should be changed or at least looked into further.
96
+
first_link.href = '/search?q=' + query.value + '&limit=' + limit + '&offset=0'
97
+
first_link.innerText = '0'
98
+
pages.appendChild(first_link)
55
99
56
-
const user_link = document.createElement('a')
57
-
user_link.href = '/user/' + result.author.username
58
-
const user_text = document.createElement('strong')
59
-
user_text.innerText = get_display_name(result.author)
60
-
user_link.appendChild(user_text)
61
-
p.appendChild(user_link)
100
+
sep()
62
101
63
-
p.innerHTML += ': '
102
+
const back_link = document.createElement('a')
103
+
back_link.href = '/search?q=' + query.value + '&limit=' + limit + '&offset=' + Math.min(0, offset - 10)
104
+
back_link.innerText = '<'
105
+
pages.appendChild(back_link)
106
+
107
+
sep()
108
+
109
+
const next_link = document.createElement('a')
110
+
next_link.href = '/search?q=' + query.value + '&limit=' + limit + '&offset=' + (offset + 10)
111
+
next_link.innerText = '>'
112
+
pages.appendChild(next_link)
113
+
}
64
114
65
-
const post_link = document.createElement('a')
66
-
post_link.href = '/post/' + result.post.id
67
-
post_link.innerText = result.post.title
68
-
p.appendChild(post_link)
115
+
document.getElementById('search').addEventListener('click', async () => {
116
+
results.innerHTML = '' // yeet the children!
117
+
pages.innerHTML = '' // yeet more children!
69
118
70
-
element.appendChild(p)
71
-
results.appendChild(element)
119
+
var search_for
120
+
for (const radio of document.getElementsByName('search-for')) {
121
+
if (radio.checked) {
122
+
search_for = radio.value
123
+
break
72
124
}
73
-
74
-
// set up pagination
75
-
if (offset > 0) {
76
-
// creates a separator
77
-
function sep() {
78
-
const span = document.createElement('span')
79
-
span.innerText = ' - '
80
-
pages.appendChild(span)
81
-
}
125
+
}
126
+
if (search_for == undefined) {
127
+
alert('please select either "users" or "posts" to search for.')
128
+
return
129
+
}
82
130
83
-
const first_link = document.createElement('a')
84
-
// we escape the $ here because otherwise V will try to perform replacements at compile-time.
85
-
//todo: report this, this behaviour should be changed or at least looked into further.
86
-
first_link.href = '/search?q=' + query.value + '&limit=' + limit + '&offset=0'
87
-
first_link.innerText = '0'
88
-
pages.appendChild(first_link)
131
+
console.log('search: ', query.value, limit, offset)
89
132
90
-
sep()
133
+
var search_results
134
+
if (search_for == 'users') {
135
+
search_results = await search_users(query.value, limit, offset)
136
+
} else if (search_for == 'posts') {
137
+
search_results = await search_posts(query.value, limit, offset)
138
+
} else {
139
+
// this should never happen
140
+
alert('something wrong occured while searching, please report this (01)')
141
+
return
142
+
}
91
143
92
-
const back_link = document.createElement('a')
93
-
back_link.href = '/search?q=' + query.value + '&limit=' + limit + '&offset=' + Math.min(0, offset - 10)
94
-
back_link.innerText = '<'
95
-
pages.appendChild(back_link)
144
+
console.log(search_results)
96
145
97
-
sep()
146
+
if (search_results.length >= 0) {
147
+
// i iterate inside the if statements so that i do not have to perform a redundant
148
+
// string comparison for every single result.
149
+
if (search_for == 'users') {
150
+
for (result of search_results) {
151
+
add_user_result(result)
152
+
}
153
+
} else if (search_for == 'posts') {
154
+
for (result of search_results) {
155
+
add_post_result(result)
156
+
}
157
+
} else {
158
+
// this should never happen
159
+
alert('something wrong occured while searching, please report this (02)')
160
+
return
161
+
}
98
162
99
-
const next_link = document.createElement('a')
100
-
next_link.href = '/search?q=' + query.value + '&limit=' + limit + '&offset=' + (offset + 10)
101
-
next_link.innerText = '>'
102
-
pages.appendChild(next_link)
163
+
// set up pagination, but only if we actually have pages to display
164
+
if (offset > 0) {
165
+
add_pages()
103
166
}
104
167
} else {
105
168
results.innerText = 'no results!'
+22
-13
src/webapp/api.v
+22
-13
src/webapp/api.v
···
5
5
import entity { Like, LikeCache, Post, Site, User, Notification }
6
6
import database { PostSearchResult }
7
7
8
+
// search_hard_limit is the maximum limit for a search query, used to prevent
9
+
// people from requesting searches with huge limits and straining the SQL server
10
+
pub const search_hard_limit := 50
11
+
8
12
////// user //////
9
13
10
14
@['/api/user/register'; post]
···
402
406
return ctx.redirect('/')
403
407
}
404
408
409
+
@['/api/user/search'; get]
410
+
fn (mut app App) api_user_search(mut ctx Context, query string, limit int, offset int) veb.Result {
411
+
if limit >= search_hard_limit {
412
+
return ctx.text('limit exceeds hard limit (${search_hard_limit})')
413
+
}
414
+
users := app.search_for_users(query, limit, offset)
415
+
return ctx.json[[]User](users)
416
+
}
417
+
405
418
////// post //////
406
419
407
420
@['/api/post/new_post'; post]
···
611
624
return ctx.json[Post](post)
612
625
}
613
626
627
+
@['/api/post/search'; get]
628
+
fn (mut app App) api_post_search(mut ctx Context, query string, limit int, offset int) veb.Result {
629
+
if limit >= search_hard_limit {
630
+
return ctx.text('limit exceeds hard limit (${search_hard_limit})')
631
+
}
632
+
posts := app.search_for_posts(query, limit, offset)
633
+
return ctx.json[[]PostSearchResult](posts)
634
+
}
635
+
614
636
////// site //////
615
637
616
638
@['/api/site/set_motd'; post]
···
634
656
return ctx.redirect('/')
635
657
}
636
658
}
637
-
638
-
////// Misc //////
639
-
640
-
pub const search_hard_limit := 50
641
-
642
-
@['/api/search'; get]
643
-
fn (mut app App) api_search(mut ctx Context, query string, limit int, offset int) veb.Result {
644
-
if limit >= search_hard_limit {
645
-
return ctx.text('limit exceeds hard limit (${search_hard_limit})')
646
-
}
647
-
posts := app.search_for_posts(query, limit, offset)
648
-
return ctx.json[[]PostSearchResult](posts)
649
-
}