{"id":18338778,"url":"https://github.com/tableflip/post","last_synced_at":"2025-04-09T20:35:51.575Z","repository":{"id":72910885,"uuid":"54400499","full_name":"tableflip/post","owner":"tableflip","description":":love_letter: The TABLEFLIP contact form handler","archived":false,"fork":false,"pushed_at":"2018-02-14T15:53:22.000Z","size":131,"stargazers_count":2,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-15T12:46:45.998Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tableflip.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-03-21T15:28:19.000Z","updated_at":"2017-10-13T00:50:35.000Z","dependencies_parsed_at":"2023-05-26T01:15:15.229Z","dependency_job_id":null,"html_url":"https://github.com/tableflip/post","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tableflip%2Fpost","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tableflip%2Fpost/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tableflip%2Fpost/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tableflip%2Fpost/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tableflip","download_url":"https://codeload.github.com/tableflip/post/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248108372,"owners_count":21049124,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-05T20:15:21.341Z","updated_at":"2025-04-09T20:35:51.557Z","avatar_url":"https://github.com/tableflip.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# POST - The TABLEFLIP contact form handler\n\nStore and forward data posted to it from forms on static sites.\nData is stored in leveldb.\nPeriodically sent on to recipients.\n\n## Getting started\n\nCopy `config/defaults.json` to `config/local.json` and fill out your mailgun credentials.\nCreate a reCAPTCHA key pair here [https://www.google.com/recaptcha/](https://www.google.com/recaptcha/admin#list) for the domain from which you are posting.\n\nIn the `post-infrastructure` project add the following two pieces of config\n\n* Place the secret key in config as follows creating a 'lookup table' with your domain as *key* and secret as *value*:\n\n```\n{\n  google: {\n    captcha: {\n      \"new-domain.name\": \"_SECRET_KEY_FROM_GOOGLE_\"\n    }\n  }\n}\n```\n\n* Add a reference to this in `/roles/post/templates/local.json`\n\n```\n{\n  google: {\n    captcha: {\n      \"new-domain.name\": \"{{secret.google.capcha['new-domain.name']}}\",\n      \"another-domain\": \"{{secret.google.capcha['another-domain']}}\"\n    }\n  }\n}\n```\n\n**From the command line**\n\n- `npm watch` =\u003e to run the site in dev mode, with automagic restarting on change.\n- `npm start` =\u003e to run it for real.\n\n**Files of note**\n\n- `server.js` is an express app.\n- `db.js` initialises a leveldb instance.\n- `pages` dir holds our route. `/pages/home` is mounted under `/`\n\n## P O S T usage\n\nLoad the google recaptcha api in the `\u003chead\u003e` of the page\n\n```html\n\u003cscript src=\"https://www.google.com/recaptcha/api.js\" async defer\u003e\u003c/script\u003e\n```\n\nCreate an html form. Set `method=\"post\"` the `action` to point to  \n\n`https://post.tableflip.io/butterfield-diet.com`\n\nReplace `butterfield-diet.com` with your domain that will be sending the posts.\n\n```html\n\u003cform id=\"your-form-id\" action=\"https://post.tableflip.io/butterfield-diet.com\" method=\"post\"\u003e\n  \u003clabel for=\"email\" class=\"label\"\u003eYour email address\u003c/label\u003e\n  \u003cinput id=\"email\" name=\"email\" type=\"email\" required=\"\" class=\"input border-box\"\u003e\n  \u003clabel for=\"info\" class=\"label\"\u003eAny info\u003c/label\u003e\n  \u003ctextarea id=\"info\" name=\"info\" rows=\"3\" class=\"textarea border-box\"\u003e\u003c/textarea\u003e\n  \u003cscript\u003efunction onSubmit (token) {document.getElementById('your-form-id').submit()}\u003c/script\u003e\n  \u003cbutton class=\"btn btn-primary g-recaptcha\" data-sitekey=\"_PUBLIC_KEY_FROM_GOOGLE_CAPTCHA_\" data-callback='onSubmit'\u003eSend\u003c/button\u003e\n\u003c/form\u003e\n```\n\nThe `name` values on your inputs will show up as labels in your notification, to give the values some context.\n\nUse the admin screen at `https://post.tableflip.io/routes` to configure a route for `https://butterfield-diet.com`, to tell it where to send the message, and where to redirect the user afterwards.\n\n## Skip the spam filter\n\nYou can be in a situation where you just want to post to post and skip the Google's recaptcha. You can do this by adding a **\"skip\"** flag on your post's payload:\n\n```json\n  {\n    \"name\": \"Bernard\",\n    \"email\": \"bernard@tableflip.io\",\n    \"open-source\": \"true\",\n    \"g-recaptcha-response\": \"skip\"\n  }\n```\n\n## Level DB\n\nSee: http://dailyjs.com/2013/04/18/leveldb-and-node-1/\n\nFor looking up where to send a message to, we have general purpose **domain** routes, that'll be used when a more specific **domain + path** route can't be found.\n\nThis let's you configure a default handler for all forms on a site, and occasionally override it for a specific form if you need to. The path can be as deeply nested as you need, it just needs to correspond to the path that you append to https://post.tableflip.io/yoursite.com/path/to/your/special/form/here\n\n```\nroute!tableflip.io =\u003e { email: 'hello@tableflip.io', frequency: 'daily' }\nroute!tableflip.io/startup =\u003e { email: 'hello@tableflip.io', frequency: 'all' }\nroute!marmalade-productions.com =\u003e { email: 'hello@tableflip.io', frequency: 'daily' }\n```\n\nWe look up for a specific match on domain + path, then a domain match if there isn't a specific match.\n\n1. lookup `tableflip.io/foo/bar`, not found.\n2. lookup `tableflip.io`. found; make it so.\n\n---\n\nA [(╯°□°）╯︵TABLEFLIP](https://tableflip.io) side project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftableflip%2Fpost","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftableflip%2Fpost","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftableflip%2Fpost/lists"}