2021-12-29 21:07:17 +01:00
|
|
|
open Db
|
2022-02-02 19:16:53 +01:00
|
|
|
include Bindings
|
2021-12-29 21:07:17 +01:00
|
|
|
|
2022-01-29 09:23:57 +01:00
|
|
|
type thread_data =
|
2022-02-17 03:59:23 +01:00
|
|
|
{ subject : string
|
2022-01-29 09:23:57 +01:00
|
|
|
; lng : float
|
|
|
|
|
; lat : float
|
2022-01-11 15:01:59 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-21 01:59:36 +01:00
|
|
|
type post =
|
2022-01-11 15:01:59 +01:00
|
|
|
{ id : string
|
|
|
|
|
; parent_id : string
|
|
|
|
|
; date : int
|
|
|
|
|
; nick : string
|
|
|
|
|
; comment : string
|
2022-02-18 19:40:19 +01:00
|
|
|
; image_info : (string * string) option
|
2022-01-11 15:01:59 +01:00
|
|
|
; tags : string list
|
|
|
|
|
; replies : string list
|
|
|
|
|
; citations : string list
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-21 01:59:36 +01:00
|
|
|
type t =
|
|
|
|
|
| Op of thread_data * post
|
|
|
|
|
| Post of post
|
2022-01-11 15:01:59 +01:00
|
|
|
|
2022-02-21 07:22:07 +01:00
|
|
|
let unwrap_list f ids =
|
|
|
|
|
let l = List.map f ids in
|
|
|
|
|
let res = List.find_opt Result.is_error l in
|
|
|
|
|
if Option.is_some res then
|
|
|
|
|
Error (Result.fold ~ok:(assert false) ~error:Fun.id (Option.get res))
|
|
|
|
|
else Ok (List.map Result.get_ok l)
|
|
|
|
|
|
2021-12-29 21:07:17 +01:00
|
|
|
module Q = struct
|
|
|
|
|
let create_post_user_table =
|
|
|
|
|
Caqti_request.exec Caqti_type.unit
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS post_user (post_id TEXT, nick TEXT, PRIMARY \
|
|
|
|
|
KEY(post_id), FOREIGN KEY(nick) REFERENCES user(nick));"
|
|
|
|
|
|
2022-02-21 00:19:35 +01:00
|
|
|
(* one row for each thread, with thread's data *)
|
|
|
|
|
let create_thread_info_table =
|
2021-12-29 21:07:17 +01:00
|
|
|
Caqti_request.exec Caqti_type.unit
|
2022-02-21 00:19:35 +01:00
|
|
|
"CREATE TABLE IF NOT EXISTS thread_info (thread_id TEXT, subject TEXT, \
|
|
|
|
|
lat FLOAT, lng FLOAT, FOREIGN KEY(thread_id) REFERENCES \
|
|
|
|
|
post_user(post_id));"
|
|
|
|
|
|
|
|
|
|
(* map thread and reply to the thread *)
|
|
|
|
|
let create_thread_post_table =
|
|
|
|
|
Caqti_request.exec Caqti_type.unit
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS thread_post (thread_id TEXT, post_id TEXT,\n\
|
|
|
|
|
\ FOREIGN KEY(thread_id) REFERENCES post_user(post_id),\n\
|
|
|
|
|
\ FOREIGN KEY(post_id) REFERENCES post_user(post_id));"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
|
|
|
|
let create_post_replies_table =
|
|
|
|
|
Caqti_request.exec Caqti_type.unit
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS post_replies (post_id TEXT, reply_id TEXT, \
|
|
|
|
|
FOREIGN KEY(post_id) REFERENCES post_user(post_id),\n\
|
|
|
|
|
\ FOREIGN KEY(reply_id) REFERENCES post_user(post_id));"
|
|
|
|
|
|
|
|
|
|
let create_post_citations_table =
|
|
|
|
|
Caqti_request.exec Caqti_type.unit
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS post_citations (post_id TEXT, cited_id TEXT, \
|
|
|
|
|
FOREIGN KEY(post_id) REFERENCES post_user(post_id),\n\
|
|
|
|
|
\ FOREIGN KEY(cited_id) REFERENCES post_user(post_id));"
|
|
|
|
|
|
|
|
|
|
let create_post_date_table =
|
|
|
|
|
Caqti_request.exec Caqti_type.unit
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS post_date (post_id TEXT, date INT, FOREIGN \
|
|
|
|
|
KEY(post_id) REFERENCES post_user(post_id));"
|
|
|
|
|
|
|
|
|
|
let create_post_comment_table =
|
|
|
|
|
Caqti_request.exec Caqti_type.unit
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS post_comment (post_id TEXT, comment TEXT, \
|
|
|
|
|
FOREIGN KEY(post_id) REFERENCES post_user(post_id));"
|
|
|
|
|
|
2022-02-18 19:40:19 +01:00
|
|
|
let create_image_info_table =
|
2021-12-29 21:07:17 +01:00
|
|
|
Caqti_request.exec Caqti_type.unit
|
2022-02-18 19:40:19 +01:00
|
|
|
"CREATE TABLE IF NOT EXISTS image_info (post_id TEXT, image_name TEXT, \
|
|
|
|
|
image_alt TEXT, FOREIGN KEY(post_id) REFERENCES post_user(post_id));"
|
|
|
|
|
|
|
|
|
|
let create_image_content_table =
|
|
|
|
|
Caqti_request.exec Caqti_type.unit
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS image_content (post_id TEXT,image_content \
|
|
|
|
|
TEXT, FOREIGN KEY(post_id) REFERENCES post_user(post_id));"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
|
|
|
|
let create_post_tags_table =
|
|
|
|
|
Caqti_request.exec Caqti_type.unit
|
|
|
|
|
"CREATE TABLE IF NOT EXISTS post_tags (post_id TEXT, tag TEXT, FOREIGN \
|
|
|
|
|
KEY(post_id) REFERENCES post_user(post_id));"
|
|
|
|
|
|
|
|
|
|
let upload_post_id =
|
|
|
|
|
Caqti_request.exec
|
|
|
|
|
Caqti_type.(tup2 string string)
|
|
|
|
|
"INSERT INTO post_user VALUES (?,?);"
|
|
|
|
|
|
2022-02-21 00:19:35 +01:00
|
|
|
let upload_thread_info =
|
2021-12-29 21:07:17 +01:00
|
|
|
Caqti_request.exec
|
2022-02-21 00:19:35 +01:00
|
|
|
Caqti_type.(tup4 string string float float)
|
|
|
|
|
"INSERT INTO thread_info VALUES (?,?,?,?);"
|
|
|
|
|
|
|
|
|
|
let upload_thread_post =
|
|
|
|
|
Caqti_request.exec
|
|
|
|
|
Caqti_type.(tup2 string string)
|
|
|
|
|
"INSERT INTO thread_post VALUES (?,?);"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
2022-02-18 19:40:19 +01:00
|
|
|
let upload_image_info =
|
2021-12-29 21:07:17 +01:00
|
|
|
Caqti_request.exec
|
2022-02-18 19:40:19 +01:00
|
|
|
Caqti_type.(tup3 string string string)
|
|
|
|
|
"INSERT INTO image_info VALUES (?,?,?);"
|
|
|
|
|
|
|
|
|
|
let upload_image_content =
|
|
|
|
|
Caqti_request.exec
|
|
|
|
|
Caqti_type.(tup2 string string)
|
|
|
|
|
"INSERT INTO image_content VALUES (?,?);"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
|
|
|
|
let upload_post_reply =
|
|
|
|
|
Caqti_request.exec
|
|
|
|
|
Caqti_type.(tup2 string string)
|
|
|
|
|
"INSERT INTO post_replies VALUES (?,?);"
|
|
|
|
|
|
|
|
|
|
let upload_post_comment =
|
|
|
|
|
Caqti_request.exec
|
|
|
|
|
Caqti_type.(tup2 string string)
|
|
|
|
|
"INSERT INTO post_comment VALUES (?,?);"
|
|
|
|
|
|
|
|
|
|
let upload_post_tag =
|
|
|
|
|
Caqti_request.exec
|
|
|
|
|
Caqti_type.(tup2 string string)
|
|
|
|
|
"INSERT INTO post_tags VALUES (?,?);"
|
|
|
|
|
|
|
|
|
|
let upload_post_date =
|
|
|
|
|
Caqti_request.exec
|
|
|
|
|
Caqti_type.(tup2 string int)
|
|
|
|
|
"INSERT INTO post_date VALUES (?,?);"
|
|
|
|
|
|
|
|
|
|
let get_post_nick =
|
|
|
|
|
Caqti_request.find Caqti_type.string Caqti_type.string
|
|
|
|
|
"SELECT nick FROM post_user WHERE post_id=?;"
|
|
|
|
|
|
|
|
|
|
let get_post_comment =
|
|
|
|
|
Caqti_request.find Caqti_type.string Caqti_type.string
|
|
|
|
|
"SELECT comment FROM post_comment WHERE post_id=?;"
|
|
|
|
|
|
|
|
|
|
let get_post_image_content =
|
|
|
|
|
Caqti_request.find_opt Caqti_type.string Caqti_type.string
|
2022-02-18 19:40:19 +01:00
|
|
|
"SELECT image_content FROM image_content WHERE post_id=?;"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
2022-01-25 14:07:28 +01:00
|
|
|
let get_post_image_info =
|
|
|
|
|
Caqti_request.find_opt Caqti_type.string
|
|
|
|
|
Caqti_type.(tup2 string string)
|
2022-02-18 19:40:19 +01:00
|
|
|
"SELECT image_name,image_alt FROM image_info WHERE post_id=?;"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
|
|
|
|
let get_post_tags =
|
|
|
|
|
Caqti_request.collect Caqti_type.string Caqti_type.string
|
|
|
|
|
"SELECT tag FROM post_tags WHERE post_id=?;"
|
|
|
|
|
|
|
|
|
|
let get_post_date =
|
|
|
|
|
Caqti_request.find Caqti_type.string Caqti_type.int
|
|
|
|
|
"SELECT date FROM post_date WHERE post_id=?;"
|
|
|
|
|
|
|
|
|
|
let get_post_citations =
|
|
|
|
|
Caqti_request.collect Caqti_type.string Caqti_type.string
|
2022-02-18 19:58:30 +01:00
|
|
|
"SELECT post_id FROM post_citations WHERE post_id=?;"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
|
|
|
|
let get_post_replies =
|
|
|
|
|
Caqti_request.collect Caqti_type.string Caqti_type.string
|
|
|
|
|
"SELECT reply_id FROM post_replies WHERE post_id=?;"
|
|
|
|
|
|
|
|
|
|
let get_thread_posts =
|
|
|
|
|
Caqti_request.collect Caqti_type.string Caqti_type.string
|
2022-02-21 00:19:35 +01:00
|
|
|
"SELECT post_id FROM thread_post WHERE thread_id=?;"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
2022-01-14 14:05:09 +01:00
|
|
|
let count_thread_posts =
|
|
|
|
|
Caqti_request.find Caqti_type.string Caqti_type.int
|
2022-02-21 00:19:35 +01:00
|
|
|
"SELECT COUNT(post_id) FROM thread_post WHERE thread_id=?;"
|
2022-01-14 14:05:09 +01:00
|
|
|
|
2022-02-18 19:58:30 +01:00
|
|
|
let get_is_thread =
|
2022-02-18 01:37:25 +01:00
|
|
|
Caqti_request.find Caqti_type.string Caqti_type.string
|
2022-02-21 00:19:35 +01:00
|
|
|
"SELECT thread_id FROM thread_info WHERE thread_id=? LIMIT 1;"
|
2022-01-12 19:34:55 +01:00
|
|
|
|
2022-02-19 18:17:03 +01:00
|
|
|
let get_is_post =
|
|
|
|
|
Caqti_request.find Caqti_type.string Caqti_type.string
|
|
|
|
|
"SELECT post_id FROM post_user WHERE post_id=? LIMIT 1;"
|
|
|
|
|
|
2022-02-18 19:58:30 +01:00
|
|
|
let get_post_thread =
|
|
|
|
|
Caqti_request.find Caqti_type.string Caqti_type.string
|
2022-02-21 00:19:35 +01:00
|
|
|
"SELECT thread_id FROM thread_post WHERE post_id=? LIMIT 1;"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
2022-02-21 00:19:35 +01:00
|
|
|
let get_thread_info =
|
|
|
|
|
Caqti_request.find Caqti_type.string
|
|
|
|
|
Caqti_type.(tup3 string float float)
|
|
|
|
|
"SELECT subject,lat,lng FROM thread_info WHERE thread_id=?;"
|
2021-12-29 21:07:17 +01:00
|
|
|
|
2022-02-17 03:59:23 +01:00
|
|
|
let get_threads =
|
|
|
|
|
Caqti_request.collect Caqti_type.unit Caqti_type.string
|
2022-02-21 00:19:35 +01:00
|
|
|
"SELECT thread_id FROM thread_info;"
|
2022-02-21 09:46:27 +01:00
|
|
|
|
|
|
|
|
let delete_post =
|
|
|
|
|
Caqti_request.exec Caqti_type.string
|
|
|
|
|
"DELETE FROM post_user WHERE post_id=?;"
|
2021-12-29 21:07:17 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let () =
|
|
|
|
|
let tables =
|
|
|
|
|
[ Q.create_post_user_table
|
2022-02-21 00:19:35 +01:00
|
|
|
; Q.create_thread_info_table
|
|
|
|
|
; Q.create_thread_post_table
|
2021-12-29 21:07:17 +01:00
|
|
|
; Q.create_post_replies_table
|
|
|
|
|
; Q.create_post_citations_table
|
|
|
|
|
; Q.create_post_date_table
|
|
|
|
|
; Q.create_post_comment_table
|
2022-02-18 19:40:19 +01:00
|
|
|
; Q.create_image_info_table
|
|
|
|
|
; Q.create_image_content_table
|
2021-12-29 21:07:17 +01:00
|
|
|
; Q.create_post_tags_table
|
|
|
|
|
]
|
|
|
|
|
in
|
|
|
|
|
if
|
|
|
|
|
List.exists Result.is_error
|
|
|
|
|
(List.map (fun query -> Db.exec query ()) tables)
|
2022-02-18 02:40:04 +01:00
|
|
|
then Dream.error (fun log -> log "can't create table")
|
2021-12-29 21:07:17 +01:00
|
|
|
|
2022-01-25 14:07:28 +01:00
|
|
|
let parse_image image =
|
|
|
|
|
match image with
|
|
|
|
|
| None -> Ok None
|
2022-02-18 19:40:19 +01:00
|
|
|
| Some ((name, alt), content) ->
|
2022-02-18 03:22:24 +01:00
|
|
|
let name =
|
|
|
|
|
match name with
|
|
|
|
|
| Some name -> Dream.html_escape name
|
|
|
|
|
| None ->
|
2022-01-25 14:07:28 +01:00
|
|
|
(* make up random name if no name was given *)
|
2022-02-18 03:22:24 +01:00
|
|
|
Uuidm.to_string (Uuidm.v4_gen random_state ())
|
2022-01-25 14:07:28 +01:00
|
|
|
in
|
2022-02-18 18:31:55 +01:00
|
|
|
if not (is_valid_image content) then Error "invalid image"
|
|
|
|
|
else if String.length alt > 1000 then Error "Image description too long"
|
2022-02-18 19:40:19 +01:00
|
|
|
else Ok (Some ((name, alt), content))
|
2022-01-25 14:07:28 +01:00
|
|
|
|
2022-02-17 03:59:23 +01:00
|
|
|
(*TODO switch to markdown !*)
|
|
|
|
|
(* insert html into the comment, and keep tracks of citations :
|
|
|
|
|
-wraps lines starting with ">" with a <span class="quote">
|
|
|
|
|
-make raw posts uuid into links
|
|
|
|
|
(*TODO fix bad link if post is in other thread*)
|
|
|
|
|
-keeps tracks of every post cited in this comment
|
|
|
|
|
- add <br> at each line *)
|
2021-12-29 21:07:17 +01:00
|
|
|
let parse_comment comment =
|
2022-01-12 20:27:10 +01:00
|
|
|
let handle_word w =
|
|
|
|
|
let trim_w = String.trim w in
|
|
|
|
|
(* '>' is '>' after html_escape *)
|
2022-02-17 03:59:23 +01:00
|
|
|
if String.starts_with ~prefix:{|>>|} trim_w then
|
2022-01-12 20:27:10 +01:00
|
|
|
let sub_w = String.sub trim_w 8 (String.length trim_w - 8) in
|
|
|
|
|
match Uuidm.of_string sub_w with
|
|
|
|
|
| None -> (w, None)
|
|
|
|
|
| Some _ ->
|
|
|
|
|
let new_w = Format.sprintf {|<a href="#%s">%s</a>|} sub_w w in
|
2022-02-17 03:59:23 +01:00
|
|
|
(new_w, Some sub_w)
|
2022-02-18 02:40:04 +01:00
|
|
|
else (w, None)
|
2022-01-12 20:27:10 +01:00
|
|
|
in
|
|
|
|
|
let handle_line l =
|
|
|
|
|
let trim_w = String.trim l in
|
|
|
|
|
(*insert quote*)
|
|
|
|
|
let line =
|
|
|
|
|
match
|
|
|
|
|
String.starts_with ~prefix:{|>|} trim_w
|
|
|
|
|
&& not (String.starts_with ~prefix:{|>>|} trim_w)
|
|
|
|
|
with
|
|
|
|
|
| false -> l
|
|
|
|
|
| true -> {|<span class="quote">|} ^ l ^ {|</span>|}
|
|
|
|
|
in
|
|
|
|
|
let words = String.split_on_char ' ' line in
|
|
|
|
|
let words, cited_posts =
|
|
|
|
|
List.fold_left
|
|
|
|
|
(fun (acc_words, acc_cited_posts) w ->
|
|
|
|
|
match handle_word w with
|
2022-01-13 15:07:20 +01:00
|
|
|
| w, Some cited_id -> (w :: acc_words, cited_id :: acc_cited_posts)
|
|
|
|
|
| w, None -> (w :: acc_words, acc_cited_posts) )
|
2022-01-12 20:27:10 +01:00
|
|
|
([], []) words
|
|
|
|
|
in
|
2022-01-13 15:07:20 +01:00
|
|
|
let words = List.rev words in
|
2022-01-14 13:23:45 +01:00
|
|
|
let line =
|
|
|
|
|
Format.asprintf "%a"
|
|
|
|
|
(Format.pp_print_list
|
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt " ")
|
|
|
|
|
Format.pp_print_string )
|
|
|
|
|
words
|
|
|
|
|
in
|
2022-01-12 20:27:10 +01:00
|
|
|
(line, cited_posts)
|
|
|
|
|
in
|
|
|
|
|
|
2022-01-14 21:04:52 +01:00
|
|
|
let comment = String.trim comment in
|
2022-01-12 20:27:10 +01:00
|
|
|
let lines = String.split_on_char '\n' comment in
|
|
|
|
|
let lines, cited_posts =
|
2021-12-29 21:07:17 +01:00
|
|
|
List.fold_left
|
2022-01-12 20:27:10 +01:00
|
|
|
(fun (acc_lines, acc_cited_posts) l ->
|
|
|
|
|
let line, cited_posts = handle_line l in
|
2022-01-13 15:07:20 +01:00
|
|
|
(line :: acc_lines, cited_posts @ acc_cited_posts) )
|
2022-01-12 20:27:10 +01:00
|
|
|
([], []) lines
|
2021-12-29 21:07:17 +01:00
|
|
|
in
|
2022-01-13 15:07:20 +01:00
|
|
|
let lines = List.rev lines in
|
2022-01-12 20:27:10 +01:00
|
|
|
(*insert <br>*)
|
2022-01-14 13:23:45 +01:00
|
|
|
let comment =
|
|
|
|
|
Format.asprintf "%a"
|
|
|
|
|
(Format.pp_print_list
|
2022-01-14 14:05:09 +01:00
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt "@.<br>")
|
2022-01-14 13:23:45 +01:00
|
|
|
Format.pp_print_string )
|
|
|
|
|
lines
|
|
|
|
|
in
|
2022-01-12 20:27:10 +01:00
|
|
|
(* remove duplicate cited_id *)
|
2022-01-27 10:45:34 +01:00
|
|
|
let cited_posts = List.sort_uniq String.compare cited_posts in
|
2021-12-29 21:07:17 +01:00
|
|
|
(comment, cited_posts)
|
|
|
|
|
|
2022-02-18 19:40:19 +01:00
|
|
|
let upload_post ?image_content post =
|
2022-01-29 09:23:57 +01:00
|
|
|
let thread_data, reply =
|
2022-01-11 15:01:59 +01:00
|
|
|
match post with
|
2022-01-29 09:23:57 +01:00
|
|
|
| Op (thread_data, reply) -> (Some thread_data, reply)
|
2022-02-21 01:59:36 +01:00
|
|
|
| Post reply -> (None, reply)
|
2021-12-29 21:07:17 +01:00
|
|
|
in
|
2022-02-18 19:40:19 +01:00
|
|
|
let { id; parent_id; date; nick; comment; image_info; tags; citations; _ } =
|
2022-02-17 03:59:23 +01:00
|
|
|
reply
|
2022-01-29 09:23:57 +01:00
|
|
|
in
|
|
|
|
|
|
2022-02-17 03:59:23 +01:00
|
|
|
let^ () = Db.exec Q.upload_post_id (id, nick) in
|
|
|
|
|
let^ () = Db.exec Q.upload_post_comment (id, comment) in
|
|
|
|
|
let^ () = Db.exec Q.upload_post_date (id, date) in
|
2022-02-21 00:19:35 +01:00
|
|
|
let^ () = Db.exec Q.upload_thread_post (parent_id, id) in
|
2022-02-18 19:40:19 +01:00
|
|
|
let _res_image_info, _res_image_content =
|
|
|
|
|
match image_info with
|
|
|
|
|
| None -> (Ok (), Ok ())
|
|
|
|
|
| Some (name, alt) -> (
|
|
|
|
|
match image_content with
|
|
|
|
|
| None -> failwith "No image_content for a post with image"
|
|
|
|
|
| Some content ->
|
|
|
|
|
( Db.exec Q.upload_image_info (id, name, alt)
|
|
|
|
|
, Db.exec Q.upload_image_content (id, content) ) )
|
2021-12-29 21:07:17 +01:00
|
|
|
in
|
2022-02-21 07:22:07 +01:00
|
|
|
let^ _ = unwrap_list (fun tag -> Db.exec Q.upload_post_tag (id, tag)) tags in
|
|
|
|
|
let^ _ =
|
|
|
|
|
unwrap_list
|
|
|
|
|
(fun cited_id -> Db.exec Q.upload_post_reply (cited_id, id))
|
|
|
|
|
citations
|
2022-01-11 15:01:59 +01:00
|
|
|
in
|
2022-01-29 09:23:57 +01:00
|
|
|
match thread_data with
|
2022-02-17 03:59:23 +01:00
|
|
|
| None -> Ok id
|
|
|
|
|
| Some { subject; lng; lat } ->
|
2022-02-21 00:19:35 +01:00
|
|
|
let^ () = Db.exec Q.upload_thread_info (id, subject, lat, lng) in
|
2022-02-17 03:59:23 +01:00
|
|
|
Ok id
|
2021-12-29 21:07:17 +01:00
|
|
|
|
2022-01-29 09:23:57 +01:00
|
|
|
let build_reply ~comment ?image ~tags ?parent_id nick =
|
2022-01-14 21:04:52 +01:00
|
|
|
let comment = Dream.html_escape comment in
|
|
|
|
|
let tags = Dream.html_escape tags in
|
2022-01-29 09:23:57 +01:00
|
|
|
let id = Uuidm.to_string (Uuidm.v4_gen random_state ()) in
|
|
|
|
|
(* parent_id is None if this reply is supposed to be a new thread *)
|
2022-02-17 03:59:23 +01:00
|
|
|
let parent_id = Option.value parent_id ~default:id in
|
2022-02-18 02:40:04 +01:00
|
|
|
if Option.is_none (Uuidm.of_string parent_id) then Error "invalid thread id"
|
|
|
|
|
else if String.length comment > 10000 then Error "invalid comment"
|
2022-01-11 15:01:59 +01:00
|
|
|
else
|
2022-01-25 14:07:28 +01:00
|
|
|
match parse_image image with
|
|
|
|
|
| Error e -> Error e
|
|
|
|
|
| Ok image ->
|
2022-02-18 02:40:04 +01:00
|
|
|
if String.length tags > 1000 then Error "invalid tags"
|
2022-01-11 15:01:59 +01:00
|
|
|
else
|
2022-02-18 19:40:19 +01:00
|
|
|
let image_info =
|
|
|
|
|
match image with
|
|
|
|
|
| None -> None
|
|
|
|
|
| Some (image_info, _image_content) -> Some image_info
|
|
|
|
|
in
|
2022-01-11 15:01:59 +01:00
|
|
|
let tag_list = Str.split (Str.regexp " +") tags in
|
|
|
|
|
let date = int_of_float (Unix.time ()) in
|
|
|
|
|
let comment, citations = parse_comment comment in
|
2022-01-25 14:07:28 +01:00
|
|
|
let reply =
|
2022-01-29 09:23:57 +01:00
|
|
|
{ id
|
|
|
|
|
; parent_id
|
|
|
|
|
; date
|
|
|
|
|
; nick
|
|
|
|
|
; comment
|
2022-02-18 19:40:19 +01:00
|
|
|
; image_info
|
2022-01-29 09:23:57 +01:00
|
|
|
; tags = tag_list
|
|
|
|
|
; replies = []
|
|
|
|
|
; citations
|
|
|
|
|
}
|
2022-01-11 15:01:59 +01:00
|
|
|
in
|
2022-01-29 09:23:57 +01:00
|
|
|
Ok reply
|
2022-01-25 14:07:28 +01:00
|
|
|
|
2022-02-17 03:59:23 +01:00
|
|
|
let build_op ~comment ?image ~tags ~subject ~lat ~lng nick =
|
2022-01-25 14:07:28 +01:00
|
|
|
let subject = Dream.html_escape subject in
|
2022-01-29 09:23:57 +01:00
|
|
|
(* TODO latlng validation? *)
|
|
|
|
|
let is_valid_latlng = true in
|
2022-02-18 02:40:04 +01:00
|
|
|
if not is_valid_latlng then Error "Invalid coordinate"
|
|
|
|
|
else if String.length subject > 600 then Error "Invalid subject"
|
2022-01-25 14:07:28 +01:00
|
|
|
else
|
2022-02-17 03:59:23 +01:00
|
|
|
let thread_data = { subject; lng; lat } in
|
2022-02-02 19:16:53 +01:00
|
|
|
let* reply =
|
2022-01-29 09:23:57 +01:00
|
|
|
match image with
|
|
|
|
|
| Some image -> build_reply ~comment ~image ~tags nick
|
|
|
|
|
| None -> build_reply ~comment ~tags nick
|
|
|
|
|
in
|
|
|
|
|
let op = Op (thread_data, reply) in
|
|
|
|
|
Ok op
|
|
|
|
|
|
|
|
|
|
let make_reply ~comment ?image ~tags ~parent_id nick =
|
2022-02-02 19:16:53 +01:00
|
|
|
let* reply = build_reply ~comment ?image ~tags ~parent_id nick in
|
2022-02-21 01:59:36 +01:00
|
|
|
let post = Post reply in
|
2022-02-18 19:40:19 +01:00
|
|
|
match image with
|
|
|
|
|
| None -> upload_post post
|
|
|
|
|
| Some (_image_info, image_content) -> upload_post ~image_content post
|
2022-01-29 09:23:57 +01:00
|
|
|
|
2022-02-17 03:59:23 +01:00
|
|
|
let make_op ~comment ?image ~tags ~subject ~lat ~lng nick =
|
|
|
|
|
let* op = build_op ~comment ?image ~tags ~subject ~lat ~lng nick in
|
2022-02-18 19:40:19 +01:00
|
|
|
match image with
|
|
|
|
|
| None -> upload_post op
|
|
|
|
|
| Some (_image_info, image_content) -> upload_post ~image_content op
|
2022-01-11 15:01:59 +01:00
|
|
|
|
2022-02-18 19:58:30 +01:00
|
|
|
let get_post_image_content id =
|
|
|
|
|
let^? content = Db.find_opt Q.get_post_image_content id in
|
2022-01-12 15:38:30 +01:00
|
|
|
Ok content
|
2022-02-18 19:58:30 +01:00
|
|
|
|
2022-02-21 01:59:36 +01:00
|
|
|
let thread_exists id = Result.is_ok (Db.find Q.get_is_thread id)
|
|
|
|
|
|
|
|
|
|
(* true if post is an op too *)
|
|
|
|
|
let post_exists id = Result.is_ok (Db.find Q.get_is_post id)
|
|
|
|
|
|
2022-02-18 19:58:30 +01:00
|
|
|
let get_post id =
|
|
|
|
|
let^ parent_id = Db.find Q.get_post_thread id in
|
|
|
|
|
let^ nick = Db.find Q.get_post_nick id in
|
|
|
|
|
let^ comment = Db.find Q.get_post_comment id in
|
|
|
|
|
let^ date = Db.find Q.get_post_date id in
|
|
|
|
|
let^ image_info = Db.find_opt Q.get_post_image_info id in
|
|
|
|
|
|
|
|
|
|
let^ tags = Db.collect_list Q.get_post_tags id in
|
|
|
|
|
let^ replies = Db.collect_list Q.get_post_replies id in
|
|
|
|
|
let^ citations = Db.collect_list Q.get_post_citations id in
|
|
|
|
|
let reply =
|
|
|
|
|
{ id; parent_id; date; nick; comment; image_info; tags; replies; citations }
|
|
|
|
|
in
|
|
|
|
|
Ok reply
|
2022-02-19 18:17:03 +01:00
|
|
|
|
2022-02-21 01:59:36 +01:00
|
|
|
let get_thread_data id =
|
|
|
|
|
if thread_exists id then
|
|
|
|
|
let^? subject, lat, lng = Db.find_opt Q.get_thread_info id in
|
|
|
|
|
let thread_data = { subject; lat; lng } in
|
|
|
|
|
Ok thread_data
|
|
|
|
|
else Error "not an op"
|
|
|
|
|
|
|
|
|
|
let get_op id =
|
|
|
|
|
let* thread_data = get_thread_data id in
|
2022-02-21 02:24:17 +01:00
|
|
|
let* post = get_post id in
|
|
|
|
|
Ok (thread_data, post)
|
|
|
|
|
|
|
|
|
|
let get_posts ids = unwrap_list get_post ids
|
|
|
|
|
|
|
|
|
|
let get_ops ids = unwrap_list get_op ids
|
2022-02-21 09:46:27 +01:00
|
|
|
|
|
|
|
|
let try_delete_post ~nick id =
|
|
|
|
|
let* post = get_post id in
|
|
|
|
|
if post.nick = nick then
|
|
|
|
|
let^ () = Db.exec Q.delete_post id in
|
|
|
|
|
Ok ()
|
|
|
|
|
else Error "You can only delete your posts"
|