let express = jsModule("express")
let app = express()

// handle static resources
let process = jsGlobal.process
let assets = express.static(process.cwd() + "/assets")
app.use("/assets", assets)

// setup view engine
let handlebars = jsModule("express-handlebars")
let hbs = handlebars.create({
  "extname": ".hbs",
  "defaultLayout": "main",
  "partialsDir": [
    "views/partials/"
  ]
})

app.engine(".hbs", hbs.engine)
app.set("view engine", ".hbs")

// setup github api
let fs = jsModule("fs")
let ini = jsModule("ini")

// call the async method via a sync style
let fileContent = callJsAsync(fs, fs.readFile, "./config.ini", "utf8")
let config = ini.parse(fileContent)

let GitHubApi = jsModule("github")
let github = newJsObj(GitHubApi, {
  "protocol": "https",
  "host": "api.github.com",
})

github.authenticate({
  "type": "basic",
  "username": config.github.username,
  "password": config.github.password
})

// setup request handlers
let selfCode = null
app.get("/", (req, res) -> {
  timeBegin()
  // call the async method via a sync style
  selfCode = selfCode || callJsAsync(fs, fs.readFile, "./index.circ", "utf8")
  let list = getPostDirTree()
  let elapsed = timeEnd()
  let time = ""
  if elapsed[0] != 0 then time = elapsed[0] + "s "
  time = time + elapsed[1] / 1000000 + "ms"
  res.render("home", {
    "title": "home",
    "list": list,
    "time": time,
    "code": selfCode
  })
})

app.get(regexp("/post/.*"), (req, res) -> {
  res.render("post", {
    "content": getPostContent(req.originalUrl, "html")
  })
})

let port = 3000
app.listen(port, () -> {
  println("app is running at: " + port)
})

fun getPostDirSha() {
  // call the async method via a sync style
  let resp = callJsAsync(github.repos, github.repos.getContent, {
    "owner": config.github.username,
    "repo": config.github.repo,
    "path": ""
  })
  let postDir = null
  for item in resp.data {
    if item.name == "post" then {
      postDir = item
      break
    }
  }
  postDir.sha
}

let postsCache = null
fun getPostDirTree() {
  if postsCache != null
  then postsCache
  else {
    let sha = getPostDirSha()
    // call the async method via a sync style
    let resp = callJsAsync(github.gitdata, github.gitdata.getTree, {
      "owner": config.github.username,
      "repo": config.github.repo,
      "sha": sha,
      "recursive": true
    })
    let expect = 0
    let path = ""
    let posts = for item in resp.data.tree {
      if item.type != "blob"
      then discard
      else {
        expect = item.path.length - 3
        if item.path.indexOf(".md") != expect
        then discard
        else {
         path = "post/" + item.path
         {
           "path": path,
           "title": getPostTitle(path)
         }
        }
      }
    }
    postsCache = posts
    posts
  }
}

fun getPostTitle(path) {
  let text = getPostContent(path, null)
  let reg = regexp("#\\s*(.*)\n*")
  let matches = text.match(reg)
  if matches != null then matches[1] else ""
}

let postContentCache = newJsObj(jsGlobal.Object)
fun getPostContent(path, type) {
  type = type || "raw"
  let key = path + type
  let cache = postContentCache[key]
  if cache != undefined then cache else {
    let text = null
    try(
      () -> {
        // call the async method via a sync style
        text = callJsAsync(github.repos, github.repos.getContent, {
          "owner": config.github.username,
          "repo": config.github.repo,
          "path": path,
          "headers": {
            "Accept": "application/vnd.github.v3." + type + "+json"
          }
        }).data
        postContentCache[key] = text
        text
      },
      true, () -> { text }
    )
  }
}

let startTime = null
fun timeBegin() {
  startTime = process.hrtime()
}

fun timeEnd() {
  process.hrtime(startTime)
}
view all source files
driven by circ: 0.068116ms