extract secrets to env, use docker compose
This commit is contained in:
parent
14ba148284
commit
c8300f5978
16 changed files with 186 additions and 133 deletions
|
|
@ -12,4 +12,5 @@ lustre = ">= 5.3.5 and < 6.0.0"
|
|||
gleam_crypto = ">= 1.5.1 and < 2.0.0"
|
||||
group_registry = ">= 1.0.0 and < 2.0.0"
|
||||
wisp = ">= 2.2.1 and < 3.0.0"
|
||||
envoy = ">= 1.1.0 and < 2.0.0"
|
||||
shared = { path = "../shared" }
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ packages = [
|
|||
]
|
||||
|
||||
[requirements]
|
||||
envoy = { version = ">= 1.1.0 and < 2.0.0" }
|
||||
gleam_crypto = { version = ">= 1.5.1 and < 2.0.0" }
|
||||
gleam_erlang = { version = ">= 1.0.0 and < 2.0.0" }
|
||||
gleam_http = { version = ">= 3.7.2 and < 5.0.0" }
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type State {
|
|||
State(
|
||||
question_number: Int,
|
||||
// id, (name (question#, answer_attempt)
|
||||
slow_answers: List(#(String, #(String, List(#(String, String))))),
|
||||
single_answers: List(#(String, #(String, List(#(String, String))))),
|
||||
// int in #pair: ping counted since response back.
|
||||
name_answers: List(#(String, #(Int, AnswerStatus))),
|
||||
hide_answers: Bool,
|
||||
|
|
@ -71,16 +71,16 @@ pub fn initialize(
|
|||
GiveSingleAnswer(id, question, answer) -> {
|
||||
State(
|
||||
..state,
|
||||
slow_answers: case list.key_find(state.slow_answers, id) {
|
||||
single_answers: case list.key_find(state.single_answers, id) {
|
||||
Ok(value) -> {
|
||||
let #(name, list) = value
|
||||
list.key_set(state.slow_answers, id, #(
|
||||
list.key_set(state.single_answers, id, #(
|
||||
name,
|
||||
list.key_set(list, question, answer),
|
||||
))
|
||||
}
|
||||
Error(_) -> {
|
||||
state.slow_answers
|
||||
state.single_answers
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -91,11 +91,11 @@ pub fn initialize(
|
|||
// Switch from "Wait for next question" to "Answer next question" mode
|
||||
AnswerQuiz -> answer_quiz(state, registry)
|
||||
message.FetchPlayers(subject:) -> {
|
||||
fetch_players(state.slow_answers, subject)
|
||||
fetch_players(state.single_answers, subject)
|
||||
state
|
||||
}
|
||||
message.AddPlayer(name) ->
|
||||
State(..state, slow_answers: add_player(name, state.slow_answers))
|
||||
State(..state, single_answers: add_player(name, state.single_answers))
|
||||
}
|
||||
|> actor.continue()
|
||||
})
|
||||
|
|
@ -241,7 +241,7 @@ fn combine_lists(state: State) {
|
|||
}),
|
||||
// Second list require a bit more work Iterate over each payers answers,
|
||||
// creating user objects where question number match current question number.
|
||||
list.flat_map(state.slow_answers, fn(name_answers) {
|
||||
list.flat_map(state.single_answers, fn(name_answers) {
|
||||
let #(_, #(name, answers)) = name_answers
|
||||
list.filter_map(answers, fn(number_answer) {
|
||||
let #(answer_number, answer) = number_answer
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ pub fn serve(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn serve_slow(
|
||||
pub fn serve_single(
|
||||
request: Request(Connection),
|
||||
component: lustre.App(
|
||||
#(List(#(String, String)), message.ClientsServer),
|
||||
|
|
|
|||
|
|
@ -18,10 +18,14 @@ import web/components/control
|
|||
import web/router
|
||||
import wisp
|
||||
import wisp/wisp_mist
|
||||
import envoy
|
||||
|
||||
pub fn main() {
|
||||
wisp.configure_logger()
|
||||
|
||||
let assert Ok(sha_api_key) = envoy.get("SHAED_API_KEY")
|
||||
let assert Ok(secret) = envoy.get("QTERM_SECRET")
|
||||
|
||||
let assert Ok(state_handler) = statehandler.initialize()
|
||||
let assert Ok(room_handler) = roomhandler.initialize(state_handler)
|
||||
|
||||
|
|
@ -48,7 +52,7 @@ pub fn main() {
|
|||
room_handler,
|
||||
)
|
||||
["socket", "single", id, pin] ->
|
||||
sockethandler.serve_slow(
|
||||
sockethandler.serve_single(
|
||||
req,
|
||||
answerlist.component(),
|
||||
id,
|
||||
|
|
@ -58,8 +62,8 @@ pub fn main() {
|
|||
)
|
||||
_ ->
|
||||
wisp_mist.handler(
|
||||
router.handle_request(room_handler, state_handler, _),
|
||||
"very_secret",
|
||||
router.handle_request(sha_api_key, room_handler, state_handler, _),
|
||||
secret,
|
||||
)(req)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,83 +190,94 @@ fn view(model: Model) -> Element(Msg) {
|
|||
])
|
||||
}
|
||||
},
|
||||
html.div([class("terminal-section")], case lobby {
|
||||
[] -> []
|
||||
lobby -> {
|
||||
let answered =
|
||||
list.filter(lobby, fn(x) {
|
||||
case x.answer {
|
||||
message.IDontKnow | message.HasAnswered | message.GivenAnswer(_) ->
|
||||
True
|
||||
_ -> False
|
||||
case model.state {
|
||||
Answer(_) | WaitForQuiz(_) ->
|
||||
element.fragment([
|
||||
html.div([class("terminal-section")], case lobby {
|
||||
[] -> []
|
||||
lobby -> {
|
||||
let answered =
|
||||
list.filter(lobby, fn(x) {
|
||||
case x.answer {
|
||||
message.IDontKnow
|
||||
| message.HasAnswered
|
||||
| message.GivenAnswer(_) -> True
|
||||
_ -> False
|
||||
}
|
||||
})
|
||||
|> list.length
|
||||
|> int.to_string
|
||||
let size = lobby |> list.length |> int.to_string
|
||||
[
|
||||
html.div([attribute.class("terminal-box")], [
|
||||
html.span([attribute.class("terminal-label")], [
|
||||
html.text("[PROGRESS] "),
|
||||
]),
|
||||
html.text("Answered: "),
|
||||
case answered == size {
|
||||
True -> html.text("Everyone!")
|
||||
False -> html.text(answered <> "/" <> size)
|
||||
},
|
||||
]),
|
||||
]
|
||||
}
|
||||
})
|
||||
|> list.length
|
||||
|> int.to_string
|
||||
let size = lobby |> list.length |> int.to_string
|
||||
[
|
||||
html.div([attribute.class("terminal-box")], [
|
||||
html.span([attribute.class("terminal-label")], [
|
||||
html.text("[PROGRESS] "),
|
||||
]),
|
||||
html.text("Answered: "),
|
||||
case answered == size {
|
||||
True -> html.text("Everyone!")
|
||||
False -> html.text(answered <> "/" <> size)
|
||||
}),
|
||||
terminal_section(
|
||||
lobby,
|
||||
"[ACTIVE TRANSMISSIONS]",
|
||||
fn(x) {
|
||||
case x.answer {
|
||||
message.GivenAnswer(_) | message.HasAnswered -> True
|
||||
_ -> False
|
||||
}
|
||||
},
|
||||
]),
|
||||
]
|
||||
}
|
||||
}),
|
||||
terminal_section(
|
||||
lobby,
|
||||
"[ACTIVE TRANSMISSIONS]",
|
||||
fn(x) {
|
||||
case x.answer {
|
||||
message.GivenAnswer(_) | message.HasAnswered -> True
|
||||
_ -> False
|
||||
}
|
||||
},
|
||||
fn(user) {
|
||||
let User(name, ping_time, answer) = user
|
||||
case answer {
|
||||
message.GivenAnswer(answer) -> answer
|
||||
message.HasAnswered -> "Answer Given"
|
||||
_ -> "Odd State..."
|
||||
}
|
||||
|> content_cell(name, ping_time, _)
|
||||
},
|
||||
),
|
||||
terminal_section(
|
||||
lobby,
|
||||
"[P A S S]",
|
||||
fn(x) {
|
||||
case x.answer {
|
||||
message.IDontKnow -> True
|
||||
_ -> False
|
||||
}
|
||||
},
|
||||
fn(user) {
|
||||
let User(name, ping_time, _) = user
|
||||
content_cell(name, ping_time, "P.A.S.S :(")
|
||||
},
|
||||
),
|
||||
terminal_section(
|
||||
lobby,
|
||||
"[AWAITING RESPONSE]",
|
||||
fn(x) {
|
||||
case x.answer {
|
||||
message.NotAnswered -> True
|
||||
_ -> False
|
||||
}
|
||||
},
|
||||
fn(user) {
|
||||
case user {
|
||||
User(name, ping_time, _) ->
|
||||
content_cell(name, ping_time, "Not Answered")
|
||||
}
|
||||
},
|
||||
),
|
||||
fn(user) {
|
||||
let User(name, ping_time, answer) = user
|
||||
case answer {
|
||||
message.GivenAnswer(answer) -> answer
|
||||
message.HasAnswered -> "Answer Given"
|
||||
_ -> "Odd State..."
|
||||
}
|
||||
|> content_cell(name, ping_time, _)
|
||||
},
|
||||
),
|
||||
terminal_section(
|
||||
lobby,
|
||||
"[P A S S]",
|
||||
fn(x) {
|
||||
case x.answer {
|
||||
message.IDontKnow -> True
|
||||
_ -> False
|
||||
}
|
||||
},
|
||||
fn(user) {
|
||||
let User(name, ping_time, _) = user
|
||||
content_cell(name, ping_time, "P.A.S.S :(")
|
||||
},
|
||||
),
|
||||
terminal_section(
|
||||
lobby,
|
||||
"[AWAITING RESPONSE]",
|
||||
fn(x) {
|
||||
case x.answer {
|
||||
message.NotAnswered -> True
|
||||
_ -> False
|
||||
}
|
||||
},
|
||||
fn(user) {
|
||||
case user {
|
||||
User(name, ping_time, _) ->
|
||||
content_cell(name, ping_time, "Not Answered")
|
||||
}
|
||||
},
|
||||
),
|
||||
server_component.element(
|
||||
[server_component.route("/socket/control/TMA/PINA")],
|
||||
[],
|
||||
),
|
||||
])
|
||||
_ -> element.none()
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ pub fn view_players(
|
|||
[click_cell_pair(Some("ENTER NEW PLAYER"), None, True, handler)],
|
||||
),
|
||||
),
|
||||
|
||||
])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import gleam/bit_array
|
||||
import gleam/crypto
|
||||
import gleam/dynamic
|
||||
import gleam/dynamic/decode
|
||||
import gleam/erlang/process.{type Subject}
|
||||
import gleam/http
|
||||
|
|
@ -11,6 +12,7 @@ import web/handlers/serve.{html_404}
|
|||
import wisp.{type Request, type Response}
|
||||
|
||||
pub fn handle_request(
|
||||
sha_api_key: String,
|
||||
room_handler: Started(Subject(RoomControl)),
|
||||
state_handler: Started(Subject(StateControl)),
|
||||
req: Request,
|
||||
|
|
@ -18,19 +20,54 @@ pub fn handle_request(
|
|||
use req <- middleware(req)
|
||||
case wisp.path_segments(req) {
|
||||
[] | ["index.html"] -> serve.main_html(fetch_rooms(room_handler))
|
||||
["api", "room"] -> handle_room(room_handler, req)
|
||||
["api", ..path] -> handle_admin_api(state_handler, req, path)
|
||||
["api", ..path] ->
|
||||
handle_api(sha_api_key, room_handler, state_handler, req, path)
|
||||
|
||||
_ -> html_404()
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_room(room_handler: Started(Subject(RoomControl)), req: Request) {
|
||||
use json <- wisp.require_json(req)
|
||||
|
||||
fn handle_room(
|
||||
room_handler: Started(Subject(RoomControl)),
|
||||
req: Request,
|
||||
json: dynamic.Dynamic,
|
||||
) {
|
||||
case req.method {
|
||||
http.Post -> add_room(room_handler, json)
|
||||
_ -> #(404, "bad api path", "Resource not found")
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_api(
|
||||
sha_api_key: String,
|
||||
room_handler: Started(Subject(RoomControl)),
|
||||
state_handler: Started(Subject(StateControl)),
|
||||
req: Request,
|
||||
path: List(String),
|
||||
) {
|
||||
use json <- wisp.require_json(req)
|
||||
|
||||
case list.key_find(req.headers, "x-api-key") {
|
||||
Ok(key) -> {
|
||||
case
|
||||
bit_array.base16_encode(crypto.hash(crypto.Sha256, <<key:utf8>>))
|
||||
== sha_api_key
|
||||
{
|
||||
True ->
|
||||
case path {
|
||||
["api", "room"] -> handle_room(room_handler, req, json)
|
||||
["api", ..path] -> handle_admin_api(state_handler, req, path, json)
|
||||
_ -> #(404, "bad api path", "Resource not found")
|
||||
}
|
||||
False -> {
|
||||
#(401, "invalid api key", "unauthorized")
|
||||
}
|
||||
}
|
||||
}
|
||||
Error(_) -> {
|
||||
#(401, "missing api key", "unauthorized")
|
||||
}
|
||||
}
|
||||
|> serve.create_json_response
|
||||
}
|
||||
|
||||
|
|
@ -38,9 +75,8 @@ fn handle_admin_api(
|
|||
actor: Started(Subject(StateControl)),
|
||||
req: Request,
|
||||
path: List(String),
|
||||
json: dynamic.Dynamic,
|
||||
) {
|
||||
use json <- wisp.require_json(req)
|
||||
|
||||
case list.key_find(req.headers, "x-api-key") {
|
||||
Ok(key) -> {
|
||||
case
|
||||
|
|
@ -65,7 +101,6 @@ fn handle_admin_api(
|
|||
#(401, "missing api key", "unauthorized")
|
||||
}
|
||||
}
|
||||
|> serve.create_json_response
|
||||
}
|
||||
|
||||
fn fetch_rooms(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue