diff --git a/Project.toml b/Project.toml index a450612..cca3946 100755 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "GenieBuilder" uuid = "c9453c14-af8a-11ec-351d-c7c9a2035d70" authors = ["Adrian Salceanu"] -version = "0.17.3" +version = "0.17.4" [deps] Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" diff --git a/src/Actions.jl b/src/Actions.jl index 9aad69d..dae500f 100644 --- a/src/Actions.jl +++ b/src/Actions.jl @@ -11,5 +11,6 @@ ACTION_START_REPL = "start_repl" # start REPL ACTION_START_PKG_MNG = "start_pkg_mng" # start package manager ACTION_STOP_PKG_MNG = "stop_pkg_mng" # stop package manager ACTION_DOWNLOAD_APP = "download_app" # download app +ACTION_TELEMETRY_DISABLED = "telemetry_disabled" # telemetry disabled end \ No newline at end of file diff --git a/src/GenieBuilder.jl b/src/GenieBuilder.jl index eea7b01..2387c2f 100755 --- a/src/GenieBuilder.jl +++ b/src/GenieBuilder.jl @@ -5,13 +5,6 @@ using GenieCache, GenieCacheFileCache import Pkg -include("Generators.jl") -include("Licensing.jl") -include("Actions.jl") -using .Generators -using .Licensing -using .Actions - const GBDIR = Ref{String}("") const DB_FOLDER = Ref{String}("") const DB_CONFIG_FILE = Ref{String}("") @@ -19,6 +12,19 @@ const LOG_FOLDER = Ref{String}("") const RUN_STATUS = Ref{Symbol}() # :install or :update const WS_PORT = 10102 const VERSION = "0.16.x" +const GBFOLDER = joinpath(homedir(), ".geniebuilder") + +include("Generators.jl") +using .Generators + +include("Settings.jl") +using .Settings + +include("Licensing.jl") +using .Licensing + +include("Actions.jl") +using .Actions function __init__() GBDIR[] = joinpath(Base.DEPOT_PATH[1], "geniebuilder") @@ -53,6 +59,14 @@ function go(; port = get!(ENV, "GB_PORT", -1)) @info "Starting GenieBuilder v$(get_version())" @async set_starting_flag() |> errormonitor + try + if ! GenieBuilder.Settings.data("telemetry") + GenieBuilder.Licensing.log(type = GenieBuilder.Actions.ACTION_TELEMETRY_DISABLED, force = true) + end + catch ex + @error ex + end + try if Genie.Configuration.isprod() @async withcache(; key="system_update_GB", expiration=60*60*24) do # 24 hours cache @@ -155,7 +169,12 @@ function get_version() end function exit() - GenieBuilder.Licensing.log(type = Actions.ACTION_END_SESSION) + try + GenieBuilder.Licensing.log(type = Actions.ACTION_END_SESSION) + catch ex + @error ex + end + Genie.Server.down!() Base.exit() end diff --git a/src/Licensing.jl b/src/Licensing.jl index 9763213..09386cb 100644 --- a/src/Licensing.jl +++ b/src/Licensing.jl @@ -4,14 +4,23 @@ Licensing integration for JuliaHub module Licensing using Base64, HTTP, JSON, Logging, Random +import GenieBuilder, GenieBuilder.Settings +import Base: log const LICENSE_API = get!(ENV, "GENIE_LICENSE_API", "https://genielicensing.hosting.genieframework.com/api/v1") # no trailing slash # const LICENSE_API = get!(ENV, "GENIE_LICENSE_API", "http://127.0.0.1:3333/api/v1") # no trailing slash #TODO: change this to the production URL -const GBFOLDER = joinpath(homedir(), ".geniebuilder") +const GBFOLDER = GenieBuilder.GBFOLDER const GBPASSFILE = joinpath(GBFOLDER, "pass") const GBSESSIONFILE = joinpath(GBFOLDER, "sessionid") +const COMMERCIAL_LICENSE = Ref{Union{Bool,Nothing}}(nothing) +function commercial_license() + isnothing(COMMERCIAL_LICENSE[]) && status() + # @show COMMERCIAL_LICENSE[] + COMMERCIAL_LICENSE[] == true +end + function isregistered() :: Bool isfile(GBPASSFILE) end @@ -227,17 +236,17 @@ end function log(; type::Union{AbstractString,Symbol}, metadata::AbstractDict = Dict(), - origin::AbstractString = ORIGIN) + origin::AbstractString = ORIGIN, + force::Bool = false) + # we check the telemetry settings only if the force flag is not set and the user has a commercial license + # otherwise we log the action regardless of the telemetry settings + ! force && commercial_license() && GenieBuilder.Settings.@check_telemetry + if ! isloggedin() @warn("User not logged in, skipping logging") return end - if ! TELEMETRY - @warn("Telemetry is disabled, skipping logging") - return - end - payload = Dict( "origin" => origin, "type" => type, @@ -262,16 +271,15 @@ function log(; end function quotas() + # TODO: review this, quotas should not be affected by telemetry settings + # but let's understand how much traffic this generates first + GenieBuilder.Settings.@check_telemetry + if ! isloggedin() @warn("User not logged in, skipping quotas") return Dict() end - if ! TELEMETRY - @warn("Telemetry is disabled, skipping quotas") - return Dict() - end - quotas_data = try HTTP.get(LICENSE_API * "/quotas"; status_exception = true, @@ -297,7 +305,7 @@ function quotas() end function status() - quotas_data = try + status_data = try HTTP.get(LICENSE_API * "/status"; status_exception = true, headers = headers() @@ -308,14 +316,17 @@ function status() return Dict() end - if quotas_data.status != 200 - @warn("Failed to get status: $(quotas_data.status)") + if status_data.status != 200 + @warn("Failed to get status: $(status_data.status)") return Dict() end try - (quotas_data.body |> String |> JSON.parse) + user_status = (status_data.body |> String |> JSON.parse) + COMMERCIAL_LICENSE[] = user_status["license"] != "trial" && user_status["license"] != "edu" && user_status["license_status"] != "expired" + return user_status catch ex + @error ex @warn("Failed to parse status data: $ex") return Dict() end @@ -326,13 +337,11 @@ function __init__() #TODO: uncouple this ENV["GENIE_USER_FULL_NAME"] = get(ENV, "JULIAHUB_USER_FULL_NAME", "") ENV["GENIE_ORIGIN"] = get(ENV, "JULIA_PKG_SERVER", "local") ENV["GENIE_METADATA"] = get(ENV, "JULIAHUB_APP_URL", "") - ENV["GENIE_TELEMETRY"] = get(ENV, "GENIE_TELEMETRY", "true") @eval const USER_EMAIL = ENV["GENIE_USER_EMAIL"] @eval const USER_FULL_NAME = ENV["GENIE_USER_FULL_NAME"] @eval const ORIGIN = ENV["GENIE_ORIGIN"] @eval const METADATA = ENV["GENIE_METADATA"] - @eval const TELEMETRY = lowercase(ENV["GENIE_TELEMETRY"]) == "true" end diff --git a/src/Settings.jl b/src/Settings.jl new file mode 100644 index 0000000..f4075f8 --- /dev/null +++ b/src/Settings.jl @@ -0,0 +1,65 @@ +module Settings + +using JSON +import GenieBuilder +import Logging + +export @check_telemetry + +const SETTINGS = Ref{Dict{String, Any}}(Dict{String, Any}()) +const ENV_KEYS = Dict( + "telemetry" => "GENIE_TELEMETRY" +) + +macro check_telemetry() + return quote + if ! data("telemetry") + # @show("Telemetry is disabled, skipping logging") + return + else + # @show("Telemetry is enabled") + end + end +end + +function default_from_env(key::String, default) :: Bool + haskey(ENV, key) || (ENV[key] = "") # default to empty string + (isempty(ENV[key]) || ENV[key] == string(default)) && return default # ENV vars are strings +end + +function load_settings() + if isfile(joinpath(GenieBuilder.GBFOLDER, "settings.json")) + SETTINGS[] = try + JSON.parsefile(joinpath(GenieBuilder.GBFOLDER, "settings.json")) + catch ex + @error ex + Dict{String, Any}() + end + end + + # @show SETTINGS[] + + haskey(SETTINGS[], "settings") || (SETTINGS[] = Dict{String, Any}("settings" => Dict{String, Any}())) + haskey(SETTINGS[]["settings"], "telemetry") || (SETTINGS[]["settings"]["telemetry"] = default_from_env(ENV_KEYS["telemetry"], true)) + + # env overrides settings + haskey(ENV, ENV_KEYS["telemetry"]) && ! isempty(ENV[ENV_KEYS["telemetry"]]) && (SETTINGS[]["settings"]["telemetry"] = ENV[ENV_KEYS["telemetry"]]) + + nothing +end + +function data() + haskey(SETTINGS[], "settings") || load_settings() + @show SETTINGS[] + return SETTINGS[]["settings"] +end + +function data(key::String) + return data()[key] +end + +function __init__() + load_settings() +end + +end \ No newline at end of file