⭐ Moe-Counter Compatible Website Hit Counter Written in Gleam
mayu.due.moe
hit-counter
svg
moe
1import gleam/dynamic
2import gleam/option
3import sqlight
4import wisp
5
6pub type Counter {
7 Counter(name: String, num: Int, created_at: String, updated_at: String)
8}
9
10pub fn setup(connection) {
11 let _ =
12 sqlight.exec(
13 "pragma foreign_keys = off;
14
15 create table if not exists tb_count (
16 id integer primary key autoincrement not null unique,
17 name text not null unique,
18 num int not null default (0)
19 ) strict;",
20 connection,
21 )
22 let add_column = fn(name) {
23 let _ =
24 sqlight.exec(
25 "alter table tb_count add column " <> name <> " text;",
26 connection,
27 )
28
29 Nil
30 }
31
32 add_column("created_at")
33 add_column("updated_at")
34
35 Nil
36}
37
38pub fn get_counter(connection, name) {
39 case name {
40 "demo" -> Ok(Counter("demo", 1_234_567_890, "", ""))
41 _ -> {
42 case
43 sqlight.query(
44 "INSERT INTO tb_count (name, created_at, updated_at, num)
45 VALUES (?1, datetime('now'), datetime('now'), 1)
46 ON CONFLICT(name) DO UPDATE SET
47 num = tb_count.num + 1,
48 updated_at = datetime('now')
49 RETURNING name, num, created_at, updated_at;",
50 with: [sqlight.text(name)],
51 on: connection,
52 expecting: dynamic.tuple4(
53 dynamic.string,
54 dynamic.int,
55 dynamic.optional(dynamic.string),
56 dynamic.optional(dynamic.string),
57 ),
58 )
59 {
60 Ok([row]) ->
61 Ok(Counter(
62 row.0,
63 row.1,
64 option.unwrap(row.2, ""),
65 option.unwrap(row.3, ""),
66 ))
67 Ok([]) -> {
68 wisp.log_error("Database query returned no rows unexpectedly.")
69
70 Error("Unreachable entity")
71 }
72 Ok([_, _, ..]) -> {
73 wisp.log_error("Database query returned multiple rows unexpectedly.")
74
75 Error("Unreachable entity")
76 }
77 Error(_) -> {
78 wisp.log_error("Database query failed.")
79
80 Error("Database operation failed")
81 }
82 }
83 }
84 }
85}