Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#13 Setup the auth command #14

Merged
merged 7 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ glint = "~> 0.18"
argv = "~> 1.0"
simplifile = ">= 1.7.0 and < 2.0.0"
dot_env = ">= 0.5.1 and < 1.0.0"
plex_pin_auth = ">= 1.0.1 and < 2.0.0"
repeatedly = ">= 2.1.1 and < 3.0.0"
gleam_erlang = ">= 0.25.0 and < 1.0.0"

[dev-dependencies]
gleeunit = "~> 1.0"
15 changes: 15 additions & 0 deletions manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,43 @@

packages = [
{ name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" },
{ name = "certifi", version = "2.12.0", build_tools = ["rebar3"], requirements = [], otp_app = "certifi", source = "hex", outer_checksum = "EE68D85DF22E554040CDB4BE100F33873AC6051387BAF6A8F6CE82272340FF1C" },
{ name = "dot_env", version = "0.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "simplifile"], otp_app = "dot_env", source = "hex", outer_checksum = "AF5C972D6129F67AF3BB00134AB2808D37111A8D61686CFA86F3ADF652548982" },
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
{ name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" },
{ name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" },
{ name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" },
{ name = "gleam_hackney", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_stdlib", "hackney"], otp_app = "gleam_hackney", source = "hex", outer_checksum = "066B1A55D37DBD61CC72A1C4EDE43C6015B1797FAF3818C16FE476534C7B6505" },
{ name = "gleam_http", version = "3.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "8C07DF9DF8CC7F054C650839A51C30A7D3C26482AC241C899C1CEA86B22DBE51" },
{ name = "gleam_json", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "8B197DD5D578EA6AC2C0D4BDC634C71A5BCA8E7DB5F47091C263ECB411A60DF3" },
{ name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" },
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" },
{ name = "glint", version = "0.18.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "BB0F14643CC51C069A5DC6E9082EAFCD9967AFD1C9CC408803D1A40A3FD43B54" },
{ name = "hackney", version = "1.20.1", build_tools = ["rebar3"], requirements = ["certifi", "idna", "metrics", "mimerl", "parse_trans", "ssl_verify_fun", "unicode_util_compat"], otp_app = "hackney", source = "hex", outer_checksum = "FE9094E5F1A2A2C0A7D10918FEE36BFEC0EC2A979994CFF8CFE8058CD9AF38E3" },
{ name = "idna", version = "6.1.1", build_tools = ["rebar3"], requirements = ["unicode_util_compat"], otp_app = "idna", source = "hex", outer_checksum = "92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA" },
{ name = "metrics", version = "1.0.1", build_tools = ["rebar3"], requirements = [], otp_app = "metrics", source = "hex", outer_checksum = "69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16" },
{ name = "mimerl", version = "1.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "mimerl", source = "hex", outer_checksum = "A1E15A50D1887217DE95F0B9B0793E32853F7C258A5CD227650889B38839FE9D" },
{ name = "parse_trans", version = "3.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "parse_trans", source = "hex", outer_checksum = "620A406CE75DADA827B82E453C19CF06776BE266F5A67CFF34E1EF2CBB60E49A" },
{ name = "plex_pin_auth", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_hackney", "gleam_http", "gleam_json", "gleam_stdlib"], otp_app = "plex_pin_auth", source = "hex", outer_checksum = "06ADEBD5AB03A8EAA994F5E962E34A2E4CB5688BA0252C5BB14C274125BAB4B3" },
{ name = "repeatedly", version = "2.1.1", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "38808C3EC382B0CD981336D5879C24ECB37FCB9C1D1BD128F7A80B0F74404D79" },
{ name = "shellout", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "shellout", source = "hex", outer_checksum = "E2FCD18957F0E9F67E1F497FC9FF57393392F8A9BAEAEA4779541DE7A68DD7E0" },
{ name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" },
{ name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" },
{ name = "ssl_verify_fun", version = "1.1.7", build_tools = ["mix", "rebar3", "make"], requirements = [], otp_app = "ssl_verify_fun", source = "hex", outer_checksum = "FE4C190E8F37401D30167C8C405EDA19469F34577987C76DDE613E838BBC67F8" },
{ name = "survey", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "survey", source = "hex", outer_checksum = "97FE1264757BB787B579095E180F8BC22CBDD16AD8D769C6597AD6591CA3E7F7" },
{ name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" },
{ name = "unicode_util_compat", version = "0.7.0", build_tools = ["rebar3"], requirements = [], otp_app = "unicode_util_compat", source = "hex", outer_checksum = "25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521" },
]

[requirements]
argv = { version = "~> 1.0" }
dot_env = { version = ">= 0.5.1 and < 1.0.0" }
gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" }
gleam_stdlib = { version = "~> 0.36" }
gleeunit = { version = "~> 1.0" }
glint = { version = "~> 0.18" }
plex_pin_auth = { version = ">= 1.0.1 and < 2.0.0" }
repeatedly = { version = ">= 2.1.1 and < 3.0.0" }
shellout = { version = "~> 1.6" }
simplifile = { version = ">= 1.7.0 and < 2.0.0" }
survey = { version = "~> 0.3" }
117 changes: 116 additions & 1 deletion src/commands/auth.gleam
Original file line number Diff line number Diff line change
@@ -1,12 +1,107 @@
//// Command logic for the authentication tool.

import gleam/int
import gleam/io
import gleam/option.{None, Some}
import glint.{type CommandInput, type Glint}
import plex_pin_auth
import plex_pin_auth/util/parser.{type PlexError, type PlexPin}
import repeatedly
import util/client_identifier
import util/commands/auth_tool
import util/env.{ConfigOption}
import util/terminal

/// The interval in which we poll the server for the token
const interval_ms = 2000

/// The 'auth' command logic
fn do(_input: CommandInput) {
io.println("todo")
// First validate the config. If it's not valid then this command can not be executed
// This command has a reliance on hostname and username
case env.is_base_config_valid() {
False -> {
io.println(
"You have not setup the config yet! Please run `plexrpc config` first.",
)
terminal.exit()
}
_ -> Nil
}

// Second validate the auth config.
// If there is already a token we need to confirm with the user that they want to replace the current token
case env.is_auth_config_valid() {
True ->
case
terminal.confirm(
"You already have an auth token. Do you want to replace it?",
default: Some(False),
)
{
// Spacing out the terminal if the prompt was sent
True -> io.println("")
False -> terminal.exit()
}
_ -> Nil
}

// Generate the client identifier
let assert Ok(client_id) = client_identifier.generate()

// We need to create the Plex pin and inform the user of the next steps
let assert Ok(created_pin) =
plex_pin_auth.create_pin(client_id)
|> handle_plex_error

// Inform the user what the code is and what to do with it
io.println(
"Successfully created your Plex pin as "
<> client_id
<> "\nYour Plex code is: "
<> created_pin.code
<> "\nYou can open the QR code in the browser user this link: "
<> created_pin.qr
<> "\nOtherwise go to https://plex.tv/link and enter the code above."
<> "\n\nWaiting for you to link your account...",
)

// Setup repeatedly so we can poll the server for the token
repeatedly.call(interval_ms, Nil, fn(_, _) {
// Poll the server for the token
let assert Ok(token) =
plex_pin_auth.get_token(client_id, created_pin.id)
|> handle_plex_error

case token.auth_token {
// If the token is returned then we can save it to the config
Some(auth_token) -> {
case
env.set_config(ConfigOption(
hostname: None,
port: None,
username: None,
https: None,
check_users: None,
token: Some(auth_token),
))
{
Ok(_) -> io.println("Successfully authenticated with Plex!")
Error(err) -> {
io.println("Failed to save the token to the config")
io.debug(err)
Nil
}
}
terminal.exit()
}
None -> Nil
}
})

// Sleep for the time it takes for the pin to expire
terminal.sleep(created_pin.expires_in * 1000)
Nil
}

/// Add the 'auth' command to the glint instance
Expand All @@ -18,3 +113,23 @@ pub fn command(to glint: Glint(Nil)) {
|> glint.description(auth_tool.description),
)
}

/// Handles the usual Plex error and throws it into the terminal
harrisonhoward marked this conversation as resolved.
Show resolved Hide resolved
fn handle_plex_error(response: Result(PlexPin, PlexError)) {
case response {
Ok(_) -> Nil
Error(err) -> {
io.println(
"Oh no! Something went wrong creating your pin..."
<> "\nStatus: "
<> int.to_string(err.status)
<> "\nCode: "
<> int.to_string(err.code)
<> "\nMessage: "
<> err.message,
)
terminal.exit()
}
}
response
}
17 changes: 17 additions & 0 deletions src/util/client_identifier.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//// This module is to handle client_identifier utilities

import util/env

/// Generates a new client identifier based on the current hostname and username
/// if the config isn't valid it throws an error
pub fn generate() -> Result(String, Nil) {
case env.is_base_config_valid() {
True -> {
let base_config = env.get_base_config(True)
let hostname = base_config.hostname
let username = base_config.username
Ok(hostname <> "-" <> username)
}
False -> Error(Nil)
}
}
20 changes: 14 additions & 6 deletions src/util/env.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,13 @@ pub fn init_config_file() -> Result(Nil, FileError) {
/// Essentially just checks if hostname is present and returns true or false
pub fn is_base_config_valid() -> Bool {
// Check all required keys and return true or false
case env.get("hostname") {
Ok(_) -> True
Error(_) -> False
let hostname = env.get("hostname")
let username = env.get("username")
case hostname, username {
// Don't allow empty strings
Ok(""), Ok("") -> False
Ok(_), Ok(_) -> True
_, _ -> False
}
}

Expand All @@ -84,12 +88,14 @@ pub fn get_base_config(should_panic should_panic: Bool) -> BaseConfig {
Error(_) if should_panic -> panic as "Hostname not found in config file"
Error(_) -> ""
}
let username = case env.get("username") {
Ok(value) -> value
Error(_) if should_panic -> panic as "Username not found in config file"
Error(_) -> ""
}
let port =
env.get_int("port")
|> result.unwrap(32_400)
let username =
env.get("username")
|> result.unwrap("me")
let https =
env.get_bool("https")
|> result.unwrap(False)
Expand All @@ -111,6 +117,8 @@ pub fn get_base_config(should_panic should_panic: Bool) -> BaseConfig {
pub fn is_auth_config_valid() -> Bool {
// Check all required keys and return true or false
case env.get("token") {
// Don't allow empty strings
Ok("") -> False
Ok(_) -> True
Error(_) -> False
}
Expand Down
6 changes: 6 additions & 0 deletions src/util/terminal.gleam
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//// This module is a wrapper for the survey module to handle input and output within the terminal.

import gleam/erlang/process
import gleam/int
import gleam/option.{type Option, None, Some}
import gleam/string
Expand Down Expand Up @@ -155,3 +156,8 @@ pub fn confirm(text display: String, default default: Option(Bool)) -> Bool {
pub fn exit() {
shellout.exit(0)
}

/// Sleeps for a given amount of milliseconds
pub fn sleep(ms: Int) -> Nil {
process.sleep(ms)
}
Loading