···11+# web_server
22+33+[](https://hex.pm/packages/web_server)
44+[](https://hexdocs.pm/web_server/)
55+66+```sh
77+gleam add web_server
88+```
99+```gleam
1010+import web_server
1111+1212+pub fn main() {
1313+ // TODO: An example of the project in use
1414+}
1515+```
1616+1717+Further documentation can be found at <https://hexdocs.pm/web_server>.
1818+1919+## Development
2020+2121+```sh
2222+gleam run # Run the project
2323+gleam test # Run the tests
2424+gleam shell # Run an Erlang shell
2525+```
+25
Gleam/web_server/gleam.toml
···11+name = "web_server"
22+version = "1.0.0"
33+44+# Fill out these fields if you intend to generate HTML documentation or publish
55+# your project to the Hex package manager.
66+#
77+# description = ""
88+# licences = ["Apache-2.0"]
99+# repository = { type = "github", user = "username", repo = "project" }
1010+# links = [{ title = "Website", href = "https://gleam.run" }]
1111+#
1212+# For a full reference of all the available options, you can have a look at
1313+# https://gleam.run/writing-gleam/gleam-toml/.
1414+1515+[dependencies]
1616+gleam_stdlib = ">= 0.34.0 and < 2.0.0"
1717+mist = ">= 1.2.0 and < 2.0.0"
1818+gleam_http = ">= 3.6.0 and < 4.0.0"
1919+gleam_erlang = ">= 0.25.0 and < 1.0.0"
2020+wisp = ">= 1.5.3 and < 2.0.0"
2121+gleam_json = ">= 2.3.0 and < 3.0.0"
2222+sqlight = ">= 1.0.1 and < 2.0.0"
2323+2424+[dev-dependencies]
2525+gleeunit = ">= 1.0.0 and < 2.0.0"
+41
Gleam/web_server/manifest.toml
···11+# This file was generated by Gleam
22+# You typically do not need to edit this file
33+44+packages = [
55+ { name = "birl", version = "1.8.0", build_tools = ["gleam"], requirements = ["gleam_regexp", "gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "2AC7BA26F998E3DFADDB657148BD5DDFE966958AD4D6D6957DD0D22E5B56C400" },
66+ { name = "directories", version = "1.1.0", build_tools = ["gleam"], requirements = ["envoy", "gleam_stdlib", "platform", "simplifile"], otp_app = "directories", source = "hex", outer_checksum = "BDA521A4EB9EE3A7894F0DC863797878E91FF5C7826F7084B2E731E208BDB076" },
77+ { name = "envoy", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "envoy", source = "hex", outer_checksum = "95FD059345AA982E89A0B6E2A3BF1CF43E17A7048DCD85B5B65D3B9E4E39D359" },
88+ { name = "esqlite", version = "0.8.9", build_tools = ["rebar3"], requirements = [], otp_app = "esqlite", source = "hex", outer_checksum = "465AE9AE28AE4192EA54C829FDC90C320447D439A9B2E10946621672FC6A6F8C" },
99+ { name = "exception", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "F5580D584F16A20B7FCDCABF9E9BE9A2C1F6AC4F9176FA6DD0B63E3B20D450AA" },
1010+ { name = "filepath", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "65F51013BCF78A603AFFD7992EF1CC6ECA96C74038EB48887F656DE44DBC1902" },
1111+ { name = "gleam_crypto", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "8AE56026B3E05EBB1F076778478A762E9EB62B31AEEB4285755452F397029D22" },
1212+ { name = "gleam_erlang", version = "0.34.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "0C38F2A128BAA0CEF17C3000BD2097EB80634E239CE31A86400C4416A5D0FDCC" },
1313+ { name = "gleam_http", version = "3.7.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8A70D2F70BB7CFEB5DF048A2183FFBA91AF6D4CF5798504841744A16999E33D2" },
1414+ { name = "gleam_json", version = "2.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "C55C5C2B318533A8072D221C5E06E5A75711C129E420DD1CE463342106012E5D" },
1515+ { name = "gleam_otp", version = "0.16.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "50DA1539FC8E8FA09924EB36A67A2BBB0AD6B27BCDED5A7EF627057CF69D035E" },
1616+ { name = "gleam_regexp", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "7F5E0C0BBEB3C58E57C9CB05FA9002F970C85AD4A63BA1E55CBCB35C15809179" },
1717+ { name = "gleam_stdlib", version = "0.57.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86EFACDF6460B8681E82752C5490F9630EC0F138F88A037DDCB241799AA8811F" },
1818+ { name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" },
1919+ { name = "gleeunit", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "0E6C83834BA65EDCAAF4FE4FB94AC697D9262D83E6F58A750D63C9F6C8A9D9FF" },
2020+ { name = "glisten", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "glisten", source = "hex", outer_checksum = "CF3A9383E9BA4A8CBAF2F7B799716290D02F2AC34E7A77556B49376B662B9314" },
2121+ { name = "gramps", version = "2.0.3", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "3CCAA6E081225180D95C79679D383BBF51C8D1FDC1B84DA1DA444F628C373793" },
2222+ { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" },
2323+ { name = "logging", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "1098FBF10B54B44C2C7FDF0B01C1253CAFACDACABEFB4B0D027803246753E06D" },
2424+ { name = "marceau", version = "1.3.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "2D1C27504BEF45005F5DFB18591F8610FB4BFA91744878210BDC464412EC44E9" },
2525+ { name = "mist", version = "1.2.0", build_tools = ["gleam"], requirements = ["birl", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "109B4D64E68C104CC23BB3CC5441ECD479DD7444889DA01113B75C6AF0F0E17B" },
2626+ { name = "platform", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "platform", source = "hex", outer_checksum = "8339420A95AD89AAC0F82F4C3DB8DD401041742D6C3F46132A8739F6AEB75391" },
2727+ { name = "ranger", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_yielder"], otp_app = "ranger", source = "hex", outer_checksum = "C8988E8F8CDBD3E7F4D8F2E663EF76490390899C2B2885A6432E942495B3E854" },
2828+ { name = "simplifile", version = "2.2.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0DFABEF7DC7A9E2FF4BB27B108034E60C81BEBFCB7AB816B9E7E18ED4503ACD8" },
2929+ { name = "sqlight", version = "1.0.1", build_tools = ["gleam"], requirements = ["esqlite", "gleam_stdlib"], otp_app = "sqlight", source = "hex", outer_checksum = "5435662352757841F9395C933404A223F39F73C73C4EC5E4418A7CB8AE85CDE6" },
3030+ { name = "wisp", version = "1.5.3", build_tools = ["gleam"], requirements = ["directories", "exception", "gleam_crypto", "gleam_erlang", "gleam_http", "gleam_json", "gleam_stdlib", "logging", "marceau", "mist", "simplifile"], otp_app = "wisp", source = "hex", outer_checksum = "DCD083E0ED3D30EA8CF2EECCA01A8108F7987D7BD908F527862E3D2725436444" },
3131+]
3232+3333+[requirements]
3434+gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" }
3535+gleam_http = { version = ">= 3.6.0 and < 4.0.0" }
3636+gleam_json = { version = ">= 2.3.0 and < 3.0.0" }
3737+gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
3838+gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
3939+mist = { version = ">= 1.2.0 and < 2.0.0" }
4040+sqlight = { version = ">= 1.0.1 and < 2.0.0" }
4141+wisp = { version = ">= 1.5.3 and < 2.0.0" }
+138
Gleam/web_server/src/app/database.gleam
···11+import app/error.{type AppError}
22+import app/transaction
33+import gleam/dynamic/decode
44+import gleam/result
55+import sqlight
66+77+pub type Connection =
88+ sqlight.Connection
99+1010+pub fn with_connection(name: String, f: fn(sqlight.Connection) -> a) -> a {
1111+ use db <- sqlight.with_connection(name)
1212+ let assert Ok(_) = sqlight.exec("pragma foreign_keys = on;", db)
1313+ f(db)
1414+}
1515+1616+pub fn migrate_schema(db: Connection) -> Result(Nil, AppError) {
1717+ sqlight.exec(
1818+ "
1919+ CREATE TABLE IF NOT EXISTS users (
2020+ id INTEGER PRIMARY KEY,
2121+ limit_in_cents INTEGER NOT NULL,
2222+ initial_balance INTEGER NOT NULL DEFAULT 0
2323+ );
2424+2525+ INSERT INTO users (limit_in_cents, initial_balance)
2626+ VALUES (1000 * 100, 0),
2727+ (800 * 100, 0),
2828+ (10000 * 100, 0),
2929+ (100000 * 100, 0),
3030+ (5000 * 100, 0);
3131+3232+ CREATE TABLE IF NOT EXISTS history (
3333+ id INTEGER PRIMARY KEY,
3434+ user_id INTEGER NOT NULL,
3535+ value INTEGER NOT NULL,
3636+ type CHAR(1) NOT NULL,
3737+ description VARCHAR(10) NOT NULL,
3838+ do_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
3939+ );
4040+4141+ CREATE INDEX IF NOT EXISTS idx_history ON history (user_id);
4242+ ",
4343+ db,
4444+ )
4545+ |> result.map_error(error.SqlightError)
4646+}
4747+4848+pub fn get_user(
4949+ db: Connection,
5050+ user_id: String,
5151+) -> Result(List(transaction.UserBalance), sqlight.Error) {
5252+ let decoder = {
5353+ use limit <- decode.field(0, decode.string)
5454+ use balance <- decode.field(1, decode.int)
5555+5656+ transaction.UserBalance(limite: limit, saldo: balance)
5757+ |> decode.success
5858+ }
5959+6060+ sqlight.query(
6161+ "SELECT limit_in_cents, initial_balance FROM users WHERE id = ?",
6262+ on: db,
6363+ with: [sqlight.text(user_id)],
6464+ expecting: decoder,
6565+ )
6666+}
6767+6868+pub fn save_in_history(
6969+ db: Connection,
7070+ user_id: String,
7171+ history: transaction.Transaction,
7272+) -> Result(List(Int), sqlight.Error) {
7373+ let decoder = {
7474+ use id <- dynamic.element(0)
7575+ dynamic.int(id)
7676+ }
7777+7878+ sqlight.query(
7979+ "INSERT INTO history (user_id, value, type, description)
8080+ VALUES (?, ?, ?, ?)",
8181+ on: db,
8282+ with: [
8383+ sqlight.text(user_id),
8484+ sqlight.int(history.valor),
8585+ sqlight.text(history.tipo),
8686+ sqlight.text(history.descricao),
8787+ ],
8888+ expecting: decoder,
8989+ )
9090+}
9191+9292+pub fn add_credit(
9393+ db: Connection,
9494+ user_id: String,
9595+ history: transaction.Transaction,
9696+) -> Result(List(Int), sqlight.Error) {
9797+ let decoder = {
9898+ use id <- dynamic.element(0)
9999+ dynamic.int(id)
100100+ }
101101+102102+ sqlight.query(
103103+ "INSERT INTO history (user_id, value, type, description)
104104+ VALUES (?, ?, ?, ?)",
105105+ on: db,
106106+ with: [
107107+ sqlight.text(user_id),
108108+ sqlight.int(history.valor),
109109+ sqlight.text(history.tipo),
110110+ sqlight.text(history.descricao),
111111+ ],
112112+ expecting: decoder,
113113+ )
114114+}
115115+116116+pub fn add_debit(
117117+ db: Connection,
118118+ user_id: String,
119119+ history: transaction.Transaction,
120120+) -> Result(List(Int), sqlight.Error) {
121121+ let decoder = {
122122+ use id <- dynamic.element(0)
123123+ dynamic.int(id)
124124+ }
125125+126126+ sqlight.query(
127127+ "INSERT INTO history (user_id, value, type, description)
128128+ VALUES (?, ?, ?, ?)",
129129+ on: db,
130130+ with: [
131131+ sqlight.text(user_id),
132132+ sqlight.int(history.valor),
133133+ sqlight.text(history.tipo),
134134+ sqlight.text(history.descricao),
135135+ ],
136136+ expecting: decoder,
137137+ )
138138+}
+5
Gleam/web_server/src/app/error.gleam
···11+import sqlight
22+33+pub type AppError {
44+ SqlightError(sqlight.Error)
55+}