<commit_before># Middleware: HTTP API
# --------------------
# Automatically makes all public methods within /app/server accessible over a HTTP request-based API
# This module will only be loaded if SS.config.api.enabled == true

# EXAMPLES:

# /api/app/square.json?5 is the same as calling SS.server.app.square(5) from the browser
# To see the output on screen type type .html instead of .json
# Pass objects in the query string: E.g. /api/user/add.json?name=Tom&age=21 is the same as SS.server.user.add({name: 'Tom', age: 21})
# Note: Make sure your application code casts strings into the type of value you're expecting when using the HTTP API

url_lib = require('url')

base64 = require('../../utils/base64.js')
server = require('../utils.coffee')

rpc = new (require('../../rpc/connection.coffee')).Client('api')


# Connect middleware handler
module.exports = ->

  (request, response, next) ->

    if request.ss.parsedURL.initialDir == SS.config.api.prefix
      url = url_lib.parse(request.url, true)
      path = url.pathname.split('.')
      action = path[0]
      actions = request.ss.parsedURL.actions

      # Show error if no method
      if actions.length <= 1
        server.showError(response, 'Please specify an API Method to call')
      # Or attempt to process request
      else
        process(request, response, url, actions)
    else
      next()


# PRIVATE

# Process an API Request
process = (request, response, url, actions) ->

  try
    params = parseParams(url)
    format = request.ss.parsedURL.extension || 'html'
      
    # Check format is supported
    throw 'Invalid output format. Supported formats: ' + formatters.keys().join(', ') unless formatters.keys().include(format)
    
    post_data = ''
    request.on 'data', (chunk) -> post_data += chunk.toString()
    request.on 'end', ->

      # Generate request for back end and send
      obj = {responder: 'server', method: actions.join('.'), params: params}
      obj.post = post_data if post_data.length > 0

      # Execute the request and deliver the response once it returns
      rpc.send obj, (result) ->
        reply(result, response, format)
    
  catch e
    server.showError(response, e)
    SS.log.error.exception(e)
        
# Formats and deliver the object
reply = (data, response, format) ->
  formatters[format](data, response)
  
# Attempts to make sense of the params passed in the query string
parseParams = (url) ->
  try
    if url.search
      if url.search.match('=')        # Test to see if we're trying to pass an object
        [url.query]
      else
        [url.search.split('?')[1]]      # Or just a string/number
    else
      null
  catch e
    throw new Error('Unable to parse params. Check syntax.')


### TODO: Fix this or replace it ###

# Authenticate. Only Basic Auth is supported at the moment, but this can and should run over HTTPs<commit_msg>Add optional node-formidable dependency to parse post requests<commit_after># Middleware: HTTP API
# --------------------
# Automatically makes all public methods within /app/server accessible over a HTTP request-based API
# This module will only be loaded if SS.config.api.enabled == true

# EXAMPLES:

# /api/app/square.json?5 is the same as calling SS.server.app.square(5) from the browser
# To see the output on screen type type .html instead of .json
# Pass objects in the query string: E.g. /api/user/add.json?name=Tom&age=21 is the same as SS.server.user.add({name: 'Tom', age: 21})
# Note: Make sure your application code casts strings into the type of value you're expecting when using the HTTP API

url_lib = require('url')

base64 = require('../../utils/base64.js')
server = require('../utils.coffee')

rpc = new (require('../../rpc/connection.coffee')).Client('api')


# Connect middleware handler
module.exports = ->

  (request, response, next) ->

    if request.ss.parsedURL.initialDir == SS.config.api.prefix
      url = url_lib.parse(request.url, true)
      path = url.pathname.split('.')
      action = path[0]
      actions = request.ss.parsedURL.actions

      # Show error if no method
      if actions.length <= 1
        server.showError(response, 'Please specify an API Method to call')
      # Or attempt to process request
      else
        process(request, response, url, actions)
    else
      next()


# PRIVATE

# Alternative form parser
if SS.config.api.form_parser.enabled

  form_parser = require('formidable')
  
  # Creating form process func
  process_form = (request, cb)->
    
    data = {fields:[], files:[], errors:[]}
    form = new form_parser.IncomingForm()
    
    #Formiblade config
    form.maxFieldsSize = SS.config.api.form_parser.maxFieldsSize
    form.keepExtensions = SS.config.api.form_parser.keepExtensions
    form.encoding = SS.config.api.form_parser.encoding
    form.uploadDir = SS.config.api.form_parser.uploadDir

    form.on 'field', (field, value)->
      data.fields.push [field, value]

    form.on 'file', (file, value)->
      data.files.push [file, value]

    form.on 'error', (error)->
      data.errors.push [error]

    form.on 'end', ->
      cb(data)

    form.parse request

# Process an API Request
process = (request, response, url, actions) ->

  try
    params = parseParams(url)
    format = request.ss.parsedURL.extension || 'html'
      
    # Check format is supported
    throw 'Invalid output format. Supported formats: ' + formatters.keys().join(', ') unless formatters.keys().include(format)

    post_data = ''

    generate_reply = ->
      # Generate request for back end and send
      obj = {responder: 'server', method: actions.join('.'), params: params}
      obj.post = post_data if post_data.length > 0

      # Execute the request and deliver the response once it returns
      rpc.send obj, (result) ->
        reply(result, response, format)

    if !SS.config.api.form_parser.enabled

      request.on 'data', (chunk) -> post_data += chunk.toString()
      request.on 'end', ->
        generate_reply()

    else
      process_form request, (data)->
        post_data = JSON.stringify(data)
        generate_reply()

    
  catch e
    server.showError(response, e)
    SS.log.error.exception(e)
        
# Formats and deliver the object
reply = (data, response, format) ->
  formatters[format](data, response)
  
# Attempts to make sense of the params passed in the query string
parseParams = (url) ->
  try
    if url.search
      if url.search.match('=')        # Test to see if we're trying to pass an object
        [url.query]
      else
        [url.search.split('?')[1]]      # Or just a string/number
    else
      null
  catch e
    throw new Error('Unable to parse params. Check syntax.')


### TODO: Fix this or replace it ###

# Authenticate. Only Basic Auth is supported at the moment, but this can and should run over HTTPs
{'fraction_non_alphanumeric': 0.1218117854001759, 'fraction_numerical': 0.004104368220463207, 'mean_word_length': 2.839617332583005, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 28, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3091664', 'n_tokens_mistral': 2043, 'n_tokens_neox': 1995, 'n_words': 672}