关于应用程序的简介:我目前正在从事一个项目,我在R Shiny上创建了一个应用程序,该应用程序捕获了不同候选人的所有详细信息,可以在任何人保存数据的那一刻使用邮件功能更新详细信息并发送状态。应用程序将弹出以使用邮件向候选人发送更新的状态。此时,如果他们打开自己的 Shiny 应用程序实例,则功能运行良好。
问题:由于所有用户都在同一网络上,我们已从PC托管此应用程序作为服务器。目前,个人用户正在使用主机的 IP 地址访问应用程序。当有人每次更新状态并保存到数据库中时,数据库中最后更新的用户条目将被注册为主机的详细信息,而不是进行更新的实际用户。此外,邮件是从主机用户而不是实际用户 ID 触发的。
是否有任何选项可以记录使用我的 IP 地址在我的组织内运行我的应用程序的单个用户详细信息?
如前所述,您可以设置 cookie。您可以设置一个用户 IDuid
,该 ID 在每个用户的会话中保持不变。
每次应用程序使用开始时,应用程序将检查浏览器 Cookie 中的uid
。如果找到一个,它将帮助您跨会话识别用户。如果没有找到它(可能(一个新用户,并且将分配一个uid
。
library(shiny)
library(shinyjs)
library(magrittr)
if (!dir.exists('www/')) {
dir.create('www')
}
download.file(
url = 'https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js',
destfile = 'www/js.cookie.js'
)
addResourcePath("js", "www")
jsCode <- '
shinyjs.getcookie = function(params) {
var cookie = Cookies.get(params[0]);
Shiny.onInputChange("jscookie", [params[0], cookie]);
}
shinyjs.setcookie = function(params) {
Cookies.set(params[1], escape(params[0]), { expires: 0.5 });
}
'
server <- function(input, output) {
observe({
input$set
js$getcookie("uid")
})
checkCookie <- eventReactive(input$jscookie, {
uid <- input$jscookie[2]
if(is.null(uid) | is.na(uid)){
uid <- sample(c(0:9, letters), 30, replace = TRUE) %>% paste(collapse = "")
js$setcookie(uid, "uid")
}
return(uid)
})
output$output <- renderText({
checkCookie()
})
}
ui <- fluidPage(
shiny::tags$head(
# you must copy:
# https://raw.githubusercontent.com/js-cookie/js-cookie/master/src/js.cookie.js
# to www/
shiny::tags$script(src = "js/js.cookie.js")
),
useShinyjs(),
extendShinyjs(text = jsCode),
sidebarLayout(
sidebarPanel(
actionButton('set', 'set')
),
mainPanel(
verbatimTextOutput('output')
)
)
)
runApp(
appDir = shinyApp(ui = ui, server = server),
port = 9898
)
它假定用户不删除 Cookie。而且非常准确地说,它们在每个用户的浏览器中都是唯一的。
代码基于 https://gist.github.com/calligross/e779281b500eb93ee9e42e4d72448189。
js.cookie.min.js:(如评论中要求的那样(。
/**
* Minified by jsDelivr using Terser v3.14.1.
* Original file: /npm/js-cookie@2.2.1/src/js.cookie.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
!function(e){var n;if("function"==typeof define&&define.amd&&(define(e),n=!0),"object"==typeof exports&&(module.exports=e(),n=!0),!n){var t=window.Cookies,o=window.Cookies=e();o.noConflict=function(){return window.Cookies=t,o}}}(function(){function e(){for(var e=0,n={};e<arguments.length;e++){var t=arguments[e];for(var o in t)n[o]=t[o]}return n}function n(e){return e.replace(/(%[0-9A-Z]{2})+/g,decodeURIComponent)}return function t(o){function r(){}function i(n,t,i){if("undefined"!=typeof document){"number"==typeof(i=e({path:"/"},r.defaults,i)).expires&&(i.expires=new Date(1*new Date+864e5*i.expires)),i.expires=i.expires?i.expires.toUTCString():"";try{var c=JSON.stringify(t);/^[{[]/.test(c)&&(t=c)}catch(e){}t=o.write?o.write(t,n):encodeURIComponent(String(t)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=encodeURIComponent(String(n)).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var f="";for(var u in i)i[u]&&(f+="; "+u,!0!==i[u]&&(f+="="+i[u].split(";")[0]));return document.cookie=n+"="+t+f}}function c(e,t){if("undefined"!=typeof document){for(var r={},i=document.cookie?document.cookie.split("; "):[],c=0;c<i.length;c++){var f=i[c].split("="),u=f.slice(1).join("=");t||'"'!==u.charAt(0)||(u=u.slice(1,-1));try{var a=n(f[0]);if(u=(o.read||o)(u,a)||n(u),t)try{u=JSON.parse(u)}catch(e){}if(r[a]=u,e===a)break}catch(e){}}return e?r[e]:r}}return r.set=i,r.get=function(e){return c(e,!1)},r.getJSON=function(e){return c(e,!0)},r.remove=function(n,t){i(n,"",e(t,{expires:-1}))},r.defaults={},r.withConverter=t,r}(function(){})});
//# sourceMappingURL=/sm/b0ce608ffc029736e9ac80a8dd6a7db2da8e1d45d2dcfc92043deb2214aa30d8.map