Provides default generic behaviours, including authentication and session handling. Unlike other applications, it is always loaded for all sites.
page.error=false
to prevent fallback to the error view.node.provider.name
and
node.provider.domain
if defined.
action.Signin ()
generic.Password ("password")
user.session
user.session.visits=count
client.keychain.User
generic.Session (token)
generic.Signout ()
.
Also provided at the /Signout address.generic.Email { to="recipient@example.com", cc="recipient@example.com", bcc="recipient@example.com", subject="Test", body="content", server="smtp.example.com", username="login", password="secret", from="sender@example.com", headers={} }
. Recipient addresses may be specified as arrays of addresses, and each address may be specified using the complete [["Name" <address>]]
format. Note that this function blocks the scribe from processing other requests whilst communicating with the SMTP server, and should therefore ideally be run as a task, else response to the user will be delayed.NewUserSessionDelegate (newtoken)
DeleteUserSessionDelegate (existingtoken)
node.localhost.applications = { "application", }
smtp = { server="smtp.example.com", username="login", password="secret", from="sender@example.com", headers={} }
Loads bundles in the sites folder, as sites.
site.domain
(the primary domain).
Any attributes of a site bundle may be specified in the site's settings.lua file, however the following are specific to the settings file, providing simplified syntax. All attributes and their values defined in the site settings file (including your own) are accessible in the site
table with each request.
domains = { "example.net", "wibble.example.com", "example.org" }
applications = { "manager", }
autoaddresses = true
false
.
redirect = true
domains
, or for the 'www' variant of your primary domain if none) — to your primary domain. I.e. if your primary domain is example.com then www.example.com will be redirected to that, or vice-versa. If you specify false
, domain redirection is disabled and all domains function as aliases for the same site content. A canonical link is also added to pages on any non-primary domain, referencing your primary domain.
redirect = false
, references to assets must currently use absolute URLs to a site's primary domain only. (Use absolute references, or enable the built-in CDN remapping.)Provides helper functionality for working with HTML pages, loading and running JavaScript, and facilitating the use of third-party integrations such as Google Analytics.
Kit utilises an editor() and thus its page modifcation functions are retroactive, and may be called at any time (e.g. before any view has even provided page content to modify).
page.language
(if any).
src
attributes are made absolute. The root may be specified (such as to use a CDN) with node.base
, or this may be set to false to disable this behaviour entirely. This feature enables you to develop views locally with relative asset references, and they will be changed automatically upon deployment.
page.javascript.varName = value
kit.Script ( script )
script.Load "/assets/script.js"
, an assignment e.g. script.Load "myvar = 'foo'"
, or a function to be called after loading e.g. script.Load "myfunc()"
. You may delcare dependencies to be loaded simultaneously as supplementary parameters, e.g. script.Load ("/moonstalk.kit/jquery.js", "/assets/script.js", "myfunc()")
or sequential dependencies as a single string using greater than as a seperator, e.g. script.Load ("/moonstalk.kit/jquery.js > /assets/script.js", "myfunc()")
. Expressions should not be terminated with semi-colon.
site.services.analytics = "id"
site.services.reinvigorate = "id"
kit.Head ( tag )
nocache = true
robots = "none"
Kit includes a number of useful JavaScript libraries.
For any key in the page.flags
table corresponding to a form input name created using a tag
function, it's class is set to error, and if the value of the key is a string an error message is displayed in a span with the id error. The tag functions should be used in server tags not expression tags.
tag.select ( "name", list, selected, localised )
form
select
input with the specified name
, and an option
corresponding to each item in the list
—having integer values and display names from the list item value, either as a localised string name, or (if localised
is false
) the value itself. Specify selected
using or
syntax to provide a default e.g. form.name or 2
.
tag.checkbox ( "name",selected )
form
checkbox
input with the specified name
, and checked as per selected
, which may be specified using or
syntax to provide a default e.g. form.name or true
.
tag.input ( "name", value )
or tag.text { name="name", value=value, attribute="value", ... }
form
text
input with the specified name
and value
, or the attributes and their values specified in a table parameter. Specify value
using or
syntax to provide a default e.g. form.name or "value"
.
If no value parameter is specified it will default to form[name]
page.focusfield = "name"
tag.Error ("name"
, "message")tag
functions.
format.Date ( datetime, format, toggle )
format.ReferenceDate
may be used to output a reftime without conversion to site localtime.
format.Time ( datetime, format )
format.ReferenceTime
may be used to output a reftime without conversion.
tag.time ( datetime )
format.Number ( number, decimals, locale )
user.locale
.
format.Money ( number, decimals, locale )
user.locale
. If no number is provided, displays n/a.
format.TelPrefix ( number )
user.locale
.
captcha.Generate ()
captcha.Validate ()
The geo application provides geographic reference and manipulation functionality, including RAM cached (<1ms) GeoIP user location lookups, using free country and city level databases. In future this application will also provide GeoNames data.
Providing the data files are available, the application is enabled by default to provide a country for the user locale in cases where a request language is not country-specific. To disable specify geo=false
in the Node settings, to force lookups for all requests, specify geo=true
or geo={lookup=true}
.
You may enable country-level resolution for all requests by specifying geo={countries={'cc'}}
, and/or city-level resolution by specifying geo={cities={'cc'}}
in the same manner, an empty table will load all available data. At the time of writing per-scribe RAM use is 10MB for country level data for ambigious languages, 40MB for UK city-level data, and 250MB RAM for the US.
geo = nil
geo = false
geo = true
The following unpacked third-party files must be placed in the data/library/geoip folder.
client.place
table is defined.
geo.LocateIp ('ip.ad.dr.es')
Perform a lookup for the specified IP address string. Also defines the user.place table.
geo.Hash (latitude,longitude)
Create a geohash from the defined coordinate numbers or strings.
geo.Coordinates ('geohash')
Returns latitude, longitude
numbers for the specified geohash string.
The manager application provides an adminstration interface for the Moonstalk servers, extensible by other applications. Requires the User application.
localhost
site.node.secret
operator
operator
key.
Enables the retrieval of localisable pages (content objects) from a database (the Teller), and extends the Manager application with functionality to manage these pages. Requires the Tenants application.
Page tables are composed of keys referred to as pieces, each having any valid value(s), including localised tables. A localised table is a table containing a key for each language identifier and a table or string value, but should also contain _localised=true
. When a piece is requested corresponding to a localised table, only the value matching the user's preferred language or site language, is returned. If no language matched we also set _localised=false
in the response. If the localised table does not contain a _localised
flag the corresponding piece name should be specified as "name[localised]"
.
For site folders only, to avoid disassociation of data in the case of a folder name change, it is desirable to provide an ID that is unique and permanent. Define one to the site settings.
id = guuid
page.pieces = {"piece"}
collators={content.PageCollator}
to be specified.page.content_urn = "pattern"
Pieces ("urn", {"piece"}
){"meta", "vocabulary", "title", "body"}
, however you may specify true
to retrieve all (i.e. the same as the page table itself, but without any redundant languages). The following attributes are always returned: created, modified, controller, view, template, css.NewPageDelegate (page, page_id, site, site_id)
This application virtualises sites, storing their settings and content in the Teller database and extends the Manager application, where settings may be managed.
Primarily intended to facilitate developing SaaS applications where users have individual domains, it also enables support for the Content application (or any other per-site/per-user content in a database) with disk-based sites.
Tenant (virtual) sites have similar functionality as disk-based site folders. They can have per-site application settings and content (using the Content application) but they cannot define addresses (beyond those supported by the Content application), controllers, views, or databases. Nor may they use variables in content, or disable/enable applications.
All virtual sites use a foundation site (node.sites.tenants
) with which a virtual site is merged, enabling a configured set of applications for every tenant site. All CoreAPI site attributes are valid within a virtual site and will replace those defined in the foundation site, and it is therefore necessary to ensure arbitrary attributes cannot be specified for virtual sites from any management interface exposed to a tenant.
Specify the following in Node.lua to customise the generic/unknown view.
provider = { domain="example.com", name="Example SaaS", }
Specify a site (loaded elsewhere, e.g. from disk using Sites Folder) to be used instead using tenant in Node.lua.
tenant = { applications = {}, template = "name", subdomain = "", }
node.curators={"sitesfolder","matchdomains","tenant"}
.event.sites (site, site_id)
event.domains (domain, site, site_id)
client.keychain
.
people.DisplayName (user)
Concatenates the first and lastname for output, either for the specified user, or the current user if none.people.SplitName ("name")
Returns firstname, lastname for a given user-input name string where the latter may be nil.people.New {name=name, email="address", telephone="number", }
Returns a new user object with ID. Accepts any valid keys from the user table, in addition to the above convience keys.event.user (user, user_id)
event.email (email, user, user_id)
Adds support to sites for wildcard sub-domains (i.e. DNS CNAME) and pattern-matched domains. To enable add the following to settings.lua.
applications = {"matchdomains",}
To enable matches for a domain in the domains
list of settings.lua, specify their names in one of the following manners. Matching is not supported on primary domains (site folder name).
Simply specify a period as the first character of the name.
domains = {".example.com",}
This example would catch all subdomains of example.com, but not example.com itself unless specified as the primary domain / site folder name.
Specify the name using Lua pattern matching syntax. Be sure to escape the periods in the name itself.
domains = {".-%.example%.com$",}
This example is the equivalent pattern syntax of the prior.
This application functions simply by parsing all site domains upon startup for a valid pattern, bulding an array of these domains, and providing a curator function which is utilised to return a corresponding site (if any) for each request.
The Tenants application already supports multi-tenant sub-domains for users via its database, however both applications may still be used together but node.curators should be specified such that matchdomains appears before tenant e.g. node.curators = {"matchdomains", "tenant"}
.
Manages the NGINX webserver and OpenResty framework.
cd /usr/local/moonstalk; certbot certonly --webroot -w sites/example.com/ -d example.com
(for as many sites as needed) and make sure to add a cron job for renewals certbot renew --deploy-hook "/usr/local/runner restart web"
so when the certificates renew there will be no downtime; note that the primary site.domain must match the first domain for the certificate.
To define a new database table (as opposed extend another application's tables, such as users
or tenants
), declare it in an application's schema file.
table_name = { "field_name", …}
The standard schema lets you declare field names as a list, in Tarantool this order cannot subsequently be changed nor fields removed, however new fields may be added anytime, and field names may also be changed.
The following keys in each database table declaration are also supported for each table.
system = "tarantool"
"teller"
.
record = { "field_name", …}
The recommended naming is a plural for a table e.g. users = {…}
with the singular form for indivudal record models e.g. users = { record="user", …}
.
Manages database processes for the Tarantool system, through the standard moonstalk schema.lua configuration file, with automatic support for roles and table replication across hosts having the same roles enabled.
.
Always shutdown the Teller process after the Scribe backends and only using signal 15 (use ./runner stop teller) otherwise current queries may not be able to complete and data could be lost before final persistence and replication is carried out.
To define a new database table (as opposed extend another application's tables, such as users
or tenants
), declare it in an application's schema file.
table_name = { "field_name", …}
The standard schema lets you declare field names as a list, in Tarantool this order cannot subsequently be changed nor fields removed, however new fields may be added anytime, and field names may also be changed.
The following keys in each database table declaration are also supported for each table.
system = "tarantool"
"teller"
.
record = { "field_name", …}
The recommended naming is a plural for a table e.g. users = {…}
with the singular form for indivudal record models e.g. users = { record="user", …}
.
These are called in your pages (views, controllers) to access the database from Scribe backends.
Applications may define database functions that run in the Teller (procedures), and thus have unrestricted access to the database. This provides the ability to work with records, such as to iterate over all records (e.g. for search) without the overhead of fetching [all] values into a page first. You call and define these functions as you would a standard function, but you create them in the include/tarantool.lua file.
Database functions are not suitable for long-running complex queries as they block other queries unless you use the Taranool yield behaviour.
Working with hierarchical data tables (nested hashmaps and arrays) contrasts with column and row-based data tables. Consider this when designing your data structures, and optimise for retrieving tables and subtables, instead of individual values.
Runs a new server process providing data persistence (storage) and queries (retrieval). For general usage see the tutorial.
This application supports and is required by many of the bundled Moonstalk applications. Use of an alternative database system with Teller-compatible applications would require an application implementing the Teller database interface functions.
Always shutdown the Teller process after the Scribe backends and only using signal 15 (use ./runner stop teller) otherwise current queries may not be able to complete and data could be lost before final persistence and replication is carried out.
To define a new database table (as opposed extend another application's tables, such as users
or tenants
), declare it in an application's settings file.
database = { "name", }
Teller database tables are global keys (names) and therefore a key for any database value will always begin with a database table name, and which cannot conflict with any other global name (i.e. application names, and Moonstalk tables or functions).
These are called in your pages (views, controllers) to access the database from Scribe backends.
save (database.key, new_value, original_value)
lookup (database.key)
true
or nil
.
fetch (database.key)
nil
if it or any of its parents do not exist.
filter {table=database.key, key=name, value=value, max=200 }
teller.Pieces (database.key, keys, localise)
teller.FetchRecord (database.key)
nil
if the key does not exist. Records facilitate working with tables in the Teller, in particular for CRUD operations, by maintaining references to their key (record._key
) and original value (record._original
). Record objects behave the same as oridinary tables but are enhanced with the following methods for handling the record.
teller.MakeRecord (table)
record:Save ([database.key,] change)
save(database.key or record._key, record, record._original)
.record:Delete ()
save(record._key, nil, {})
.record:Fetch (database.key)
newvalues=fetch(key or record._key); copy(newvalues, record)
.record:Key (database.key, create)
teller.Sum (database.key, value, return)
teller.Tally (database.key, value)
teller.Run (app.function, ...)
app.function(...)
.
teller.Delegate ("function", ...)
Applications may define database functions that run in the Teller (procedures), and thus have unrestricted access to the database. This provides the ability to work with records, such as to iterate over all records (e.g. for search) without the overhead of fetching [all] values into a page first. You call and define these functions as you would a standard function, but you create them in the database.lua file.
Database functions accept a maximum of 3 parameters, and may return a single value only (use of a single table is recommended in both cases). Returning nil,"message" invokes the error view with the provided message.
Database functions are not suitable for long-running complex queries as they block queries from other backends. You may however run procedures in separate threads (see Tasks application), or you may periodically release the CPU (such as with every x iterations) using coroutine.yield()
to allow the database to interleave the servicing of other requests with your procedure.
These are used with your database functions to work directly in the database. Keys must be specified as serialised Lua strings (using the varkey
function as necessary).
Loader ()
Starter ()
delegate ("name", ...)
true
or nil, errmsg
. Typically called only be a high-level function to notify other applications of an operation that is to be carried out so that any dependencies may be resolved by them.
save ("key", new_value, original_value)
{}
.
Failure to do this will result in removed values being restored the next time the Teller is started. To change a table value to some other type, you must first delete it
and only then assign the new value, failure to do this will corrupt the database.
These behaviours are handled automatically when you specify both new and old values.
Provides event-type behaviour such that when operations take place that other applications may be dependant upon, any application may catch the event notification by simply defining a function with the corresponding name. The parameters are dictated by the application that instigates the delegation and carrying out the operation. The name of the delegate functions may be the same as the instigating function (e.g. DeleteUser), or different to better distinguish the purpose (e.g. DeleteUserDelegate).
The primary use of delegation is to maintain indexes.
It is essential to note that any indexed table (i.e. a 'record' table that is pointed to from other 'index' tables), must not have any new new table assignment made to it, e.g. don't use index[1] = users[1] and then save("users[1]",{…}) as the index will still point to the old table unless the delegate mechanisms update it accordingly (thus in this example one must also call teller.Delegate("IndexUser",id) to create pointers to this new table that replaced the old. This also applies to any subtable. To update values within such a table without replacing, you must use the teller.Update function.
Working with hierarchical data tables (nested hashmaps and arrays) contrasts with column and row-based data tables. Consider this when designing your data structures, and optimise for retrieving tables and subtables, instead of individual values.
varkey
as necessary) instead of using the (proxy) Lua table syntax.nil
) original_value may be specified as {}
(e.g. an empty table).false
.Database table keys are specified using native Lua table syntax, however this cannot be used to retrieve the entire database table or a root key (e.g. fetch(database)
). However the key may be specified instead as a string, e.g. fetch "database"
, and for which hierarchical keys incorporating variables may use the following function.