17

I have a shiny app deployed on a Linux server. I want the app to timeout if there is no activity for a minute. Based on what I read, I added the line app_idle_timeout to the shiny-server.conf file but I notice that it doesn't work. Can someone please advice how I can ensure that the session times out after a minute? Note: I do NOT have shiny server PRO.

Below is what my shiny-server.conf looks like.

Instruct Shiny Server to run applications as the user "shiny"
run_as shiny;

# Define a server that listens on port 3838
server {
  listen 3838;

  # Define a location at the base URL
  location / {

    # Host the directory of Shiny Apps stored in this directory
    site_dir /srv/shiny-server;

    # Log all Shiny output to files in this directory
    log_dir /var/log/shiny-server;
    app_idle_timeout 60;

    # When a user visits the base URL rather than a particular application,
    # an index of the applications available in this directory will be shown.
    directory_index on;

  }
}
~                 
rookieJoe
  • 509
  • 6
  • 14

3 Answers3

23

You can configure your idle time within the shiny app like so using some JS, here the app will timeout after 5 seconds.

library(shiny)
library(leaflet)

inactivity <- "function idleTimer() {
  var t = setTimeout(logout, 5000);
  window.onmousemove = resetTimer; // catches mouse movements
  window.onmousedown = resetTimer; // catches mouse movements
  window.onclick = resetTimer;     // catches mouse clicks
  window.onscroll = resetTimer;    // catches scrolling
  window.onkeypress = resetTimer;  //catches keyboard actions

  function logout() {
    window.close();  //close the window
  }

  function resetTimer() {
    clearTimeout(t);
    t = setTimeout(logout, 5000);  // time is in milliseconds (1000 is 1 second)
  }
}
idleTimer();"


ui <- fluidPage(
  tags$script(inactivity),    
  leafletOutput("mymap")

)

server <- shinyServer(function(input,output,session){

  points <- eventReactive(input$recalc, {
    cbind(rnorm(40) * 2 + 13, rnorm(40) + 48)
  }, ignoreNULL = FALSE)

  output$mymap <- renderLeaflet({
    leaflet() %>%
      addProviderTiles(providers$Stamen.TonerLite,options = providerTileOptions(noWrap = TRUE)) %>% 
      addMarkers(data = points())
  })

})
runApp(list(ui = ui, server = server))
Pork Chop
  • 28,528
  • 5
  • 63
  • 77
  • 1
    A close solution that works since 09/2018. `library(shiny);ui <- fluidPage( textOutput("keepAlive"), titlePanel("Hello !"), HTML( " " ) )` 1/2 – phili_b Aug 29 '22 at 12:21
  • 1
    `server <- function(input, output) { output$keepAlive <- renderText({duration_bef<- 240; req(input$Occur); if (input$Occur – phili_b Aug 29 '22 at 12:22
21

@PorkChop, thanks for your very useful answer!

Just for the sake of completeness, here is a slightly modified version of @PorkChop's code which doesen't close the browser tab, but instead only closes the session and leaves a message for the user:

library(shiny)
library(leaflet)

timeoutSeconds <- 5

inactivity <- sprintf("function idleTimer() {
var t = setTimeout(logout, %s);
window.onmousemove = resetTimer; // catches mouse movements
window.onmousedown = resetTimer; // catches mouse movements
window.onclick = resetTimer;     // catches mouse clicks
window.onscroll = resetTimer;    // catches scrolling
window.onkeypress = resetTimer;  //catches keyboard actions

function logout() {
Shiny.setInputValue('timeOut', '%ss')
}

function resetTimer() {
clearTimeout(t);
t = setTimeout(logout, %s);  // time is in milliseconds (1000 is 1 second)
}
}
idleTimer();", timeoutSeconds*1000, timeoutSeconds, timeoutSeconds*1000)


ui <- fluidPage(
  tags$script(inactivity),    
  leafletOutput("mymap")
)

server <- shinyServer(function(input,output,session){
  
  observeEvent(input$timeOut, { 
    print(paste0("Session (", session$token, ") timed out at: ", Sys.time()))
    showModal(modalDialog(
      title = "Timeout",
      paste("Session timeout due to", input$timeOut, "inactivity -", Sys.time()),
      footer = NULL
    ))
    session$close()
  })
  
  points <- eventReactive(input$recalc, {
    cbind(rnorm(40) * 2 + 13, rnorm(40) + 48)
  }, ignoreNULL = FALSE)
  
  output$mymap <- renderLeaflet({
    leaflet() %>%
      addProviderTiles(providers$Stamen.TonerLite, options = providerTileOptions(noWrap = TRUE)) %>% 
      addMarkers(data = points())
  })
  
})

runApp(list(ui = ui, server = server))

result

This was very helpful to get here.

ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
  • 1
    If the app is split into ui.R and server.R files, can this code go in the server.R file? – Drwhit Dec 11 '18 at 17:54
  • 1
    If you want to replicate it 1:1 you can put the variables`timeoutSeconds` and `inactivity` in a file called global.R in the same folder as ui.R and server.R. – ismirsehregal Dec 11 '18 at 20:52
  • 1
    @ismirsehregal this is a great answer, and worked for me. I was able to end sessions on the open source server with this. rookieJoe I would consider marking this as your accepted answer. – bluescholar1212 Dec 17 '19 at 19:33
  • This is great! It closes the individual session but leaves the app running. How would you close the app when the last session is closed? – Simon Woodward Apr 29 '20 at 22:18
  • 2
    [Here](https://gist.github.com/trestletech/9926129) you can find an example on how to count active sessions. In the `onSessionEnded` callback you can check if the number of sessions = 0 and run `stopApp()`. – ismirsehregal Apr 30 '20 at 05:01
2

The session timeout feature isn't available on the open source shiny server. It comes only as part of the pro version.

rookieJoe
  • 509
  • 6
  • 14