Repository: uilicious/inboxkitten Branch: master Commit: fa1270fea685 Files: 71 Total size: 141.8 KB Directory structure: gitextract_chfcm49f/ ├── .dockerignore ├── .gitignore ├── .travis.yml ├── CODE-GUIDE.md ├── DEPLOY-GUIDE-LOCALHOST.md ├── DEPLOY-GUIDE-SERVERLESS.md ├── Dockerfile ├── LICENSE ├── README.md ├── api/ │ ├── app.js │ ├── cloudflare.js │ ├── config/ │ │ ├── cacheControl.js │ │ ├── kittenRouterConfig.sample.js │ │ └── mailgunConfig.sample.js │ ├── firebase.js │ ├── package.json │ ├── public/ │ │ ├── .gitignore │ │ └── _anchor.txt │ ├── src/ │ │ ├── api/ │ │ │ ├── mailGetHtml.js │ │ │ ├── mailGetInfo.js │ │ │ ├── mailGetUrl.js │ │ │ └── mailList.js │ │ ├── app-setup.js │ │ ├── cloudflare-api/ │ │ │ ├── KittenRouter.js │ │ │ ├── mailGetHtml.js │ │ │ ├── mailGetKey.js │ │ │ ├── mailList.js │ │ │ └── optionsHandler.js │ │ └── mailgunReader.js │ ├── test/ │ │ └── mailgunReader.test.js │ └── webpack.config.js ├── build.sh ├── cli/ │ ├── Makefile │ ├── README.md │ ├── go.sh │ └── src/ │ └── inboxkitten.go ├── config.sh ├── deploy/ │ ├── cloudflare/ │ │ └── deploy.sh │ └── firebase/ │ ├── deploy.sh │ └── firebase.json ├── docker-dev-build.sh ├── docker-entrypoint.sh ├── docker-notes.md └── ui/ ├── .gitignore ├── README.md ├── commonshost.js ├── config/ │ ├── apiconfig.sample.js │ ├── carbonads.js │ ├── dev.env.js │ ├── index.js │ ├── prod.env.js │ └── shareConfig.js ├── index.html ├── package.json ├── src/ │ ├── App.vue │ ├── components/ │ │ ├── CarbonAds.vue │ │ ├── NavBar.vue │ │ └── mail/ │ │ ├── inbox.vue │ │ ├── message_detail.vue │ │ └── message_list.vue │ ├── kittenrouter.vue │ ├── landingpage.vue │ ├── main.js │ ├── router/ │ │ └── index.js │ ├── scss/ │ │ ├── _color.scss │ │ ├── _common.scss │ │ ├── _producthunt.scss │ │ └── landingpage.scss │ └── store/ │ └── emailStore.js ├── uilicious-test/ │ └── inboxkitten.test.js └── vite.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ # # Hidden files ignore # .* # # Remove uneeded files from docker context # /api/node_modules /ui/node_modules /ui/dist /cli/** /deploy/** # # Config files to ignore # /ui/config/apiconfig.js /api/config/mailgunConfig.js ================================================ FILE: .gitignore ================================================ # Hidden files **/.* # lib / build folders folder **/node_modules **/bin **/dist **/gopath # UI build folder ignore ui/build/* # deploy build files deploy/firebase/functions deploy/firebase/public deploy/firebase/*.log # Remove out config files api/config/*.js ui/config/apiconfig.js # Include sample config files !api/config/*.sample.js !api/config/cacheControl.js # Whitelist anchor files & .gitignore !.gitignore !.dockerignore !.travis.yml !**/.anchor ================================================ FILE: .travis.yml ================================================ sudo: false language: go # Our CLI toolchain is based on node_js node_js: "8.10" # Installation of subdependencies before_install: # Install firebase tools - npm install -g firebase-tools # # Installing of uilicious toolchain # - npm install -g uilicious-cli # Addons (for build debugging) addons: apt: packages: - tree # Building and testing on travis script: - ./config.sh - ./build.sh - echo "Displaying project file tree (for easy debugging)" && tree -L 2 # The actual deploy scripts deploy: - provider: script skip_cleanup: true script: cd ./deploy/firebase && ./deploy.sh --token "$FIREBASE_TOKEN" --project "$FIREBASE_PROJECT" on: branch: master # Dependencies caching (for NPM, and GO) cache: directories: - api/node_modules - ui/node_modules - cli/gopath ================================================ FILE: CODE-GUIDE.md ================================================ # Code Guide If you are interested to developing Inboxkitten, this is a brief guide of how Inboxkitten has been structured. ## Main Components - API - that serves as the backbone to connect to MailGun - UI - the user interface that you see on Inboxkitten that interacts with the API - CLI - the command line tool that you can use to interact with the API ### API Under the `api` folder is where all the code resides for the API component. The API component utilizes `axios` to perform its requests to Mailgun. - The configuration settings of the API are all located under `config/` - `mailgunConfig.js` is the configuration file that contains the keys and domains - `mailgunConfig.sample.js` is the template that `./config.sh` used to write into `mailgunConfig.js` - The rest of the code resides in `src/`. - The main point of entry in an ExpressJS setup is at `app-setup.js`. In this file, - `mailgunReader.js` contains the underlying code that connects to Mailgun - the `api` folder will contain the code that performs the validation of the params in the endpoint that the user called before sending over to `mailgunReader.js`. - The `test` folder contains the mocha test cases to check the `mailgunReader.js`. To add any endpoints, it is recommended to create a prototype function in `mailgunReader.js` that performs the execution that connects to Mailgun. Following which, you should create the endpoint that user will be using as a new file under `src/api/` folder for easy maintenance. ### UI The UI component code is under `ui` folder. It is constructed using Vue.js for its frontend development and `axios` to perform to requests to API component. - The configuration settings of the UI are all located under `config/` - `apiconfig.js` contains the configuration for connecting to API component as well as the domain to display on the UI. - `apiconfig.sample.js` is the template used in `./config.sh` for writing into `apiconfig.js` - The other configuration to be concerned would be the `shareConfig.js` where it is the settings for shareable features such as Twitter's tweeting and GitHub's fork. - The other files are auto generated files by vue-cli. - The `src` folder contains the body for UI. It is separated into 3 folders. - The `assets` will contain the images. - The `components` will contain the components used in the UI. - The `router` is an auto generated file but it is used to add subpaths to link to the components. - The `scss` contains the styling used for Inboxkitten's UI. - The `dist` folder contains the files built using the `npm run build` command. - The `uilicious-test` is an uilicious test script that can be ran on [test.uilicious.com](https://test.uilicious.com) to check if your email has been received properly. The main entrypoint will be the `App.vue` and by default the Vue router will direct to `landingpage.vue`. ### CLI The CLI is under the `cli` folder. There are only one file that performs the tasks to connect to the API component. It is `inboxkitten.go` under the `src` folder. The `go.sh` script is a custom go script that ensures the environment is within the `cli` folder. ================================================ FILE: DEPLOY-GUIDE-LOCALHOST.md ================================================ # Developing on localhost / Custom deployment Note: You will still need to do the mail gun setup in the firebase guide. Instead of running `./config.sh`, you should setup the config files respectively for the deployment. ## Running the api server **Configuring : api/config/mailgunConfig.js** ``` module.exports = { "apiKey" : "", "emailDomain" : "", "corsOrigin" : "http://localhost:8000" } ``` + MAILGUN_API_KEY : Mailgun private api key + MAILGUN_EMAIL_DOMAIN : domain for mailgun + UI_HOST : Url to the UI domain. `http://localhost:8000` is the default for the UI `npm run dev`, **Running the express.js API server** ``` # Assuming that you are on the root directory of Inboxkitten $ cd api # Start the server $ npm start ``` Validate your API server is online at `http://localhost:8000/api/v1/mail/list?recipient=hello-world` You should see an empty array representing an empty inbox. ## Running the ui server - in development mode **Configuring ui/config/apiconfig.js** ``` export default { apiUrl: 'http://localhost:8000/api/v1/mail', domain: '' } ``` + apiUrl : Api server to point to, `localhost:8000` is the default for the api server `npm start` + MAILGUN_EMAIL_DOMAIN : domain for mailgun **Running the nodejs+webpack UI server** ``` # Assuming that you are on the root directory of Inboxkitten $ cd ui # If you do not have a http server, you can install one $ npm run dev ``` You can now access it on `http://localhost:8000` and enjoy your kitten-ventures. ## Running cli This is built using the `./build.sh` script ``` # Assuming that you are on the root directory of Inboxkitten $ cd cli # You can immediately execute the executable $ ./bin/inboxkitten # Retrieving list of email $ ./bin/inboxkitten list exampleEmailName # Retrieving individual email $ ./bin/inboxkitten get sw eyJwIjpmYWxzZSwiayI6IjI3YzVkNmZkLTk5ZjQtNGY5MC1iYTM4LThiNDBhNTJmNzA1OCIsInMiOiI0NzFhZjYxYjA4IiwiYyI6InRhbmtiIn0= # Target different api endpoint $ ./bin/inboxkitten -api http://localhost:8000/api/v1 (list|get) [params] ``` To run without compilation of `inboxkitten.go` in the src/ folder ``` $ ./go.sh run src/inboxkitten.go ``` ## Calling the API using curl If you have your API running on port `8000`, ``` # Get list of email $ curl localhost:8000/api/v1/mail/list\?recipient=hard-dust-64 # Get individual email $ curl localhost:8000/api/v1/mail/list\?mailKey=se-eyJwIjpmYWxzZSwiayI6ImVlMWNiMTAzLWZhZjMtNDg3Ni04MjI2LWE1YmE1ZTU3YzMxMiIsInMiOiI3NTdhNTY5ZGFkIiwiYyI6InRhbmtiIn0= ``` If you have it hosted on the net, change the endpoint to where you have hosted it on :) ================================================ FILE: DEPLOY-GUIDE-SERVERLESS.md ================================================ # Serverless Deployment Guide Follow the 5 steps guide below to get started on Firebase! - [Step 0 - Clone Me](#step-0---clone-me) - [Step 1 - Setup Serverless provider](#step-1---mailgun--firebase-signup) - [Step 2 - Configuration](#step-2---configuration) - [Step 3 - Build the package](#step-3---build-the-package) - [Step 4 - Deployment](#step-4---deployment) > Also do let us know how we can help make this better 😺 ## Step 0 - Clone Me ``` $ git clone https://github.com/uilicious/inboxkitten.git ``` ### Step 1a - Setup Firebase 1. Go to Firebase and click on `Get Started`. 2. Sign in with your favorite Google account. 3. Click on `Add Project` and create your own firebase inboxkitten project. 4. Remember the project ID On your local machine where your InboxKitten is located at, ``` # Go to the root folder of InboxKitten $ cd # Ensure that firebase CLI tool is installed $ npm install -g firebase-tools # Login to your firebase account $ firebase login # Set your firebase project $ firebase use --add ``` OR ### Step 1b - Setup Cloudflare Workers 1. Go to Cloudflare and signup with a domain. 2. Setup cloudflare worker and get an API key ___ ## Step 2 - Configuration In the root directory of Inboxkitten, run the following command ``` $ ./config.sh ``` During the run time of `./config.sh`, there are three environment variables that is being used to set the configuration for your configuration files. 1. `MAILGUN_EMAIL_DOMAIN` - any custom domain that you owned or the default domain in Mailgun 2. `WEBSITE_DOMAIN` - any custom domain that you owned. If you use your default firebase url, it will be `.firebaseapp.com` 3. `MAILGUN_API_KEY` - retrieve the api key from your Mailgun account configuration ___ ## Step 3 - Build the package ``` $ ./build.sh ``` `./build.sh` will package the three components to be ready for deployment. ___ ## Step 4 - Deployment For API deployment on Firebase: ``` # Run the deployment script $ ./deploy/firebase/deploy.sh ``` For API deployment on Cloudflare: ``` # Run the deployment script $ ./deploy/cloudflare/deploy.sh ``` ================================================ FILE: Dockerfile ================================================ #------------------------------------------------------- # # Base alpine images with all the runtime os dependencies # # Note that the node-sass is broken with node 12 # https://github.com/nodejs/docker-node/issues/1028 # #------------------------------------------------------- # Does basic node, and runtime dependencies FROM node:14-alpine AS baseimage RUN apk add --no-cache gettext RUN mkdir -p /application/ # WORKDIR /application/ #------------------------------------------------------- # # code builders (used by dockerbuilders) # #------------------------------------------------------- # Install dependencies for some NPM modules FROM baseimage AS codebuilder # RUN apk add --no-cache make gcc g++ python #------------------------------------------------------- # # Docker builders (also resets node_modules) # # Note each major dependency is compiled seperately # so as to isolate the impact of each code change # #------------------------------------------------------- # Build the API # with reseted node_modules FROM codebuilder AS apibuilder # copy and download dependencies COPY api/package.json /application/api-mods/package.json RUN cd /application/api-mods/ && npm install # copy source code COPY api /application/api/ RUN rm -rf /application/api/node_modules # merge in dependnecies RUN cp -r /application/api-mods/node_modules /application/api/node_modules RUN ls /application/api/ # Build the UI # with reseted node_modules FROM codebuilder AS uibuilder # copy and reset the code COPY ui /application/ui/ RUN rm -rf /application/ui/node_modules RUN rm -rf /application/ui/dist RUN cd /application/ui && ls && npm install # Lets do the UI build RUN cp /application/ui/config/apiconfig.sample.js /application/ui/config/apiconfig.js RUN cd /application/ui && npm run build # Entry script # & Permission reset FROM codebuilder AS entrypointbuilder COPY docker-entrypoint.sh /application/docker-entrypoint.sh RUN chmod +x /application/docker-entrypoint.sh #------------------------------------------------------- # # Full Docker application # #------------------------------------------------------- FROM baseimage as inboxkitten # Copy over the built files COPY --from=apibuilder /application/api /application/api COPY --from=uibuilder /application/ui/dist /application/ui-dist COPY --from=entrypointbuilder /application/docker-entrypoint.sh /application/docker-entrypoint.sh # Debugging logging # RUN ls /application/./ # RUN ls /application/ui-dist # RUN ls /application/api # Expose the server port EXPOSE 8000 # # Configurable environment variable # ENV MAILGUN_EMAIL_DOMAIN="" ENV MAILGUN_API_KEY="" ENV WEBSITE_DOMAIN="" # Setup the workdir WORKDIR "/application/" # Setup the entrypoint ENTRYPOINT [ "/application/docker-entrypoint.sh" ] CMD [] ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2018 Uilicious Private Limited. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![inboxkitten header](./ui/static/inbox-kitten-opengraph.jpg)](https://inboxkitten.com) # Open-Source Disposable Email - Served by Serverless Kittens [![Build Status](https://travis-ci.org/uilicious/inboxkitten.svg?branch=master)](https://travis-ci.org/uilicious/inboxkitten) [Inboxkitten](https://inboxkitten.com) is an open-source disposable email service that you can freely deploy adopt on your own! Visit [our site](https://inboxkitten.com) to give a spin, or ... # Docker Deployment Guide Its one simple line - to use our prebuilt docker container. Note you will need to [setup your mailgun account first](#setup-mailgun) ``` # PS: you should modify this for your use case docker run \ -e MAILGUN_EMAIL_DOMAIN="" \ -e MAILGUN_API_KEY="" \ -e WEBSITE_DOMAIN="localhost:8000" \ -p 8000:8000 \ uilicious/inboxkitten ``` And head over to port 8000 - for your inboxkitten # Other Deployment Options - [Serverless deployment guide (for cloudflare/firebase)](./DEPLOY-GUIDE-SERVERLESS.md) - [localhost/custom deployment/configuration guide](./DEPLOY-GUIDE-LOCALHOST) # Support us on product hunt 🚀 + https://www.producthunt.com/posts/inboxkitten # Somewhat related blog / articles + [The Stack : Making a free open-source disposable email service prototype (inboxkitten.com) in 14 hours](https://dev.to/picocreator/the-stack-making-a-free-open-source-disposable-email-service-prototype-inboxkittencom-in-14-hours-206g) + [What I have learnt from a 14 hours project](https://dev.to/jmtiong/what-i-have-learnt-from-a-14-hours-project-2joo) + [Development timeline](https://blog.uilicious.com/development-timeline-for-inboxkitten-com-lessons-learnt-e802a2f0a47c) # Other References - [Coding Guide](./CODE-GUIDE.md) # Looking for sponsor Note: Due to this project rather heavy traffic usage, a good half sadly spam/bot related, we are looking for a hosting sponsor / sponsor to subsidise running cost ___ ## How to Setup Mailgun - and get your free API key ### Mailgun To sign up for a Mailgun account, go to the signup page. > 2021 Udpate: Inbound routing for mailgun, now requires any paid account (starting at $35/month) see : https://www.mailgun.com/pricing/ #### Custom Domain ``` 1. Click on `Add New Domain` button under your Domains panel. 2. Follow the steps accordingly ``` > You can use the default domain that was provided by Mailgun if you do not have your own domain. #### Routes Configuration After setting up your domain, in order for you to receive email, you have to configure the routes. Routes act as rules that will filter through all the incoming mails and execute actions on matched conditions. In your Routes panel, simply click on `Create Route` button and follow the steps accordingly. Mailgun Route > The above route will match all names ending with `@inboxkitten.com`, store them in the storage that mailgun provides (only for 3 days) and stop processing any other rules once this route is matched. #### Mailgun API Key You can locate your Mailgun API key by clicking on the domain that you are managing. In it you can see your API key. Mailgun API key Or you can go to the security settings and locate the API key there. Mailgun API key ___ ================================================ FILE: api/app.js ================================================ // Cache control settings const cacheControl = require("./config/cacheControl"); // app package loading let app = require("./src/app-setup"); // Setup the routes app.get("/api/v1/mail/list", require("./src/api/mailList")); app.get("/api/v1/mail/getInfo", require("./src/api/mailGetInfo")); app.get("/api/v1/mail/getHtml", require("./src/api/mailGetHtml")); // Legacy fallback behaviour - // Note this is to be deprecated (after updating UI) app.get("/api/v1/mail/getKey", require("./src/api/mailGetInfo")); // Static regex const staticRegex = /static\/(js|css|img)\/(.+)\.([a-zA-Z0-9]+)\.(css|js|png|gif)/g; // Static folder hosting with cache control // See express static options: https://expressjs.com/en/4x/api.html#express.static app.use( app.express.static("public", { etag: true, setHeaders: function (res, path, stat) { if( staticRegex.test(path) ) { res.set('cache-control', cacheControl.immutable); } else { res.set('cache-control', cacheControl.static ); } } }) ) // Custom 404 handling - use index.html app.use(function(req, res) { res.set('cache-control', cacheControl.static) res.sendFile(__dirname + '/public/index.html'); }); // Setup the server var server = app.listen(8000, function () { console.log("app running on port.", server.address().port); }); ================================================ FILE: api/cloudflare.js ================================================ /** * Cloudflare fetch result handling */ addEventListener('fetch', event => { event.respondWith(handleFetchEvent(event)) }) const KittenRouter = require("kittenrouter"); const kittenRouterConfig = require("./config/kittenRouterConfig") || {}; const router = new KittenRouter(kittenRouterConfig); async function handleFetchEvent(event) { // Get the request object let req = event.request; // Get the request URL let url = new URL(req.url) // Does the CORS options hanlding if (req.method === "OPTIONS") { return require("./src/cloudflare-api/optionsHandler")(req) } // Get the pathname let pathname = url.pathname; // Does API processing if(pathname === "/api/v1/mail/list"){ return require("./src/cloudflare-api/mailList")(url) } else if(pathname === "/api/v1/mail/getKey") { return require("./src/cloudflare-api/mailGetKey")(url) } else if (pathname === "/api/v1/mail/getHtml") { return require("./src/cloudflare-api/mailGetHtml")(url) } else if (pathname.startsWith("/api/")) { // Throw an exception for invalid API endpoints return new Response('Invalid endpoint - ' + pathname, { status: 400, statusText: 'INVALID_ENDPOINT' }); } // KittenRouter handling if( pathname === "" || pathname === "/" || pathname.startsWith("/inbox/") || pathname.startsWith("/static/css") || pathname.startsWith("/static/img") || pathname.startsWith("/static/js") ) { return router.handleFetchEvent(event); } // Throw an exception for invalid file request return new Response('Invalid filepath - ' + url.pathname, { status: 404, statusText: 'UNKNOWN_FILEPATH' }); } ================================================ FILE: api/config/cacheControl.js ================================================ /** * Configure the various level of cache controls */ module.exports = { // Frequent changing dynamic content "dynamic" : "public, max-age=1, max-stale=5, stale-while-revalidate=10, stale-if-error=86400", // Rarely changing static content // with very aggressive caching "static" : "public, max-age=60, max-stale=120, stale-while-revalidate=3600, stale-if-error=86400", // Immutable content "immutable": "public, max-age=36000, max-stale=72000, stale-while-revalidate=360000, stale-if-error=864000" } ================================================ FILE: api/config/kittenRouterConfig.sample.js ================================================ // // Routing and logging options // module.exports = { // logging endpoint to use log : [ { // Currently only elasticsearch is supported, scoped here for future alternatives // One possible option is google analytics endpoint type : "elasticsearch", // // Elasticsearch index endpoint // url : "https://elasticsearch-server.secret-domain.com/", // // Authorization header (if needed) // basicAuthToken : "user:pass", // // Index prefix for storing data, this is before the "YYYY.MM" is attached // indexPrefix : "test-data-", // Enable logging of the full ipv4/6 // // Else it mask (by default) the last digit of IPv4 address // or the "network" routing for IPv6 // see : https://www.haproxy.com/blog/ip-masking-in-haproxy/ logTrueIP : false, // @TODO support // Additional cookies to log // // Be careful not to log "sensitive" cookies, that can compromise security // typically this would be seesion keys. // cookies : ["__cfduid", "_ga", "_gid", "account_id"] } ], // Routing rules to evaluate, starting from 0 index // these routes will always be processed in sequence route : [ // Lets load all requests to commonshost first "commonshost.inboxkitten.com", // If it fails, we fallback to firebase "firebase.inboxkitten.com" ], // Set to true to disable fallback to origin host // when all routes fails disableOriginFallback : false, } ================================================ FILE: api/config/mailgunConfig.sample.js ================================================ // // API key and valid mailgun domain supported (using sandbox) // module.exports = { "apiKey" : "${MAILGUN_API_KEY}", "emailDomain" : "${MAILGUN_EMAIL_DOMAIN}", //"corsOrigin" : "*" } ================================================ FILE: api/firebase.js ================================================ /** * This is configured for use within firebase cloud function (or GCP cloud functions) * And will be automatically renamed into index.js by the build script * * See : https://firebase.google.com/docs/functions/http-events */ // Dependencies loading const functions = require('firebase-functions'); let app = require("./src/app-setup"); // Setup the routes - for mail list / get app.get("/api/v1/mail/list", require("./src/api/mailList")); app.get("/api/v1/mail/getKey", require("./src/api/mailGetKey")); app.get("/api/v1/mail/getHtml", require("./src/api/mailGetHtml")); // Expose the HTTP on request exports.firebase_api_v1 = functions.https.onRequest(app); ================================================ FILE: api/package.json ================================================ { "name": "inboxkitten-api", "version": "1.0.0", "description": "", "main": "index.js", "dependencies": { "axios": "^0.18.0", "body-parser": "^1.18.3", "cors": "^2.8.5", "express": "^4.16.3", "firebase-admin": "^6.0.0", "firebase-functions": "^2.0.5", "kittenrouter": "^1.0.3", "validator": "^10.7.0" }, "devDependencies": { "delay": "^4.0.0", "mailgun-js": "^0.20.0", "md5": "^2.2.1", "mocha": "^5.2.0", "shortid": "^2.2.13", "uuid": "^3.3.2", "webpack": "^4.30.0", "webpack-cli": "^3.3.0" }, "scripts": { "test": "mocha", "start": "node app.js", "build-cloudflare": "webpack --mode production cloudflare.js" }, "author": "", "license": "MIT" } ================================================ FILE: api/public/.gitignore ================================================ * !_anchor.txt !.gitignore ================================================ FILE: api/public/_anchor.txt ================================================ This directory is used for static file serving via the API ================================================ FILE: api/src/api/mailGetHtml.js ================================================ // Loading mailgun reader and config const mailgunReader = require("../mailgunReader"); const mailgunConfig = require("../../config/mailgunConfig"); const cacheControl = require("../../config/cacheControl"); const reader = new mailgunReader(mailgunConfig); /** * Get and return the etatic email HTML content from the mailgun API, given the mailKey * * @param {*} req * @param {*} res */ module.exports = function(req, res){ let region = req.query.region let key = req.query.key if (region == null || region === ""){ return res.status(400).send('{ "error" : "No `region` param found" }'); } if (key == null || key === ""){ return res.status(400).send('{ "error" : "No `key` param found" }'); } reader.getKey({region, key}).then(response => { let body = response["body-html"] || response["body-plain"] if( body === undefined || body == null) { body = 'The kittens found no messages :(' } // Add JS injection to force all links to open as a new tab // instead of opening inside the iframe body += ' ================================================ FILE: ui/package.json ================================================ { "name": "inboxkitten-ui", "version": "1.1.0", "type": "module", "description": "UI for Inboxkitten - A personal disposable email", "keywords": [ "inboxkitten" ], "author": "uilicious", "license": "MIT", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "dependencies": { "axios": "^1.5.0", "clipboard": "^2.0.11", "jquery": "^3.7.1", "moment": "^2.29.4", "normalize.css": "^8.0.1", "primer-tooltips": "^2.0.0", "random-words": "^2.0.0", "sass": "^1.68.0", "vue": "^2.7.14", "vue-router": "^3.6.5", "vue-spinner": "^1.0.4", "vuescroll": "^4.18.0", "vuex": "^3.6.2" }, "devDependencies": { "@vitejs/plugin-vue2": "^2.2.0", "vite": "^4.4.5" } } ================================================ FILE: ui/src/App.vue ================================================ ================================================ FILE: ui/src/components/CarbonAds.vue ================================================ ================================================ FILE: ui/src/components/NavBar.vue ================================================ ================================================ FILE: ui/src/components/mail/inbox.vue ================================================ ================================================ FILE: ui/src/components/mail/message_detail.vue ================================================ ================================================ FILE: ui/src/components/mail/message_list.vue ================================================ ================================================ FILE: ui/src/kittenrouter.vue ================================================ ================================================ FILE: ui/src/landingpage.vue ================================================ ================================================ FILE: ui/src/main.js ================================================ // The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from "./App.vue" import router from './router' Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: '#app', router, components: {App}, template: '', created () { this.$eventHub = new Vue({ name: 'EventHub', parent: this, functional: true }) } }) ================================================ FILE: ui/src/router/index.js ================================================ import Vue from 'vue' import Vuex from 'vuex' import Router from 'vue-router' import LandingPage from '@/landingpage.vue' import KittenRouter from '@/kittenrouter.vue' import Inbox from '@/components/mail/inbox.vue' import MessageDetail from '@/components/mail/message_detail.vue' import MessageList from '@/components/mail/message_list.vue' import vuescroll from 'vuescroll' import 'vuescroll/dist/vuescroll.css' Vue.mixin({ created () { // pass the event hub down to descendents if (!this.$eventHub && this.$root.$eventHub) { this.$eventHub = this.$root.$eventHub } } }) Vue.use(Vuex) Vue.use(Router) Vue.use(vuescroll) export default new Router({ mode: 'history', routes: [ { path: '/', name: 'Kitten Land', component: LandingPage }, { path: '/inbox/:email', name: 'Inbox', redirect: {name: 'List'}, component: Inbox, children: [ { path: '', redirect: {name: 'List'} }, { path: 'list', name: 'List', component: MessageList }, { path: 'message/:region/:key', name: 'Message', component: MessageDetail }, { path: '*', redirect: {name: 'List'} } ] }, { path: '/kittenrouter', name: 'KittenRouter', component: KittenRouter }, { path: '*', redirect: {name: 'Kitten Land'} } ] }) ================================================ FILE: ui/src/scss/_color.scss ================================================ //----------------------------------------------- // Base Color //----------------------------------------------- $color1-base: #119DA4; $color2-base: #0C7489; $color3-base: #13505B; $color4-base: #040404; $color5-base: #D7D9CE; $domain-base: #d7d9ce; //#06FFAB; //----------------------------------------------- // Dark Color varient //----------------------------------------------- $color1-dark: darken($color1-base, 10%); $color2-dark: darken($color2-base, 10%); $color3-dark: darken($color3-base, 10%); $color4-dark: darken($color4-base, 10%); $color5-dark: darken($color5-base, 10%); //----------------------------------------------- // Light Color varient //----------------------------------------------- $color1-light: lighten($color1-base, 10%); $color2-light: lighten($color2-base, 10%); $color3-light: lighten($color3-base, 10%); $color4-light: lighten($color4-base, 10%); $color5-light: lighten($color5-base, 10%); //----------------------------------------------- // Text colors //----------------------------------------------- $bright-text: #FFFFFF; $dark-text: #040404; //----------------------------------------------- // CALL TO ACTION - colors //----------------------------------------------- $cta-base: #FF4377; $cta-base-text: #FFFFFF; $cta-hover: #ff0; $cta-hover-text: #040404; //----------------------------------------------- // Header gradient background //----------------------------------------------- .header-gradient-background { background: #36D1DC; /* fallback for old browsers */ background: -webkit-linear-gradient(to right, #5B86E5, #36D1DC); /* Chrome 10-25, Safari 5.1-6 */ background: linear-gradient(to right, #5B86E5, #36D1DC); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ } ================================================ FILE: ui/src/scss/_common.scss ================================================ // Fix scroll alignment issues html, body, #app { min-height: 100%; height: 100%; position: relative; } html { min-height: 600px; margin: 0; padding: 0; overflow-x: hidden; } // Flex .flex-row { display: flex; flex-direction: row; } // Text alignment .text-left { text-align: left; } .text-center { text-align: center; } .text-right { text-align: right; } // Code .code, .code pre { font-family: monospace; text-align: left; background: #272822; color: white; overflow: auto; width: auto; border-radius: 5px; padding: 1em; margin-bottom: 1em; } // Margins .m-0 { margin: 0; } .m-1 { margin: 1rem; } .ml-1 { margin-left: 1rem; } .mr-1 { margin-right: 1rem; } .mb-0 { margin-bottom: 0; } .mb-1 { margin-bottom: 1rem; } .mb-2 { margin-bottom: 1rem; } .mx-a { margin-left: auto; margin-right: auto; } .mx-1 { margin-left: 1rem; margin-right: 1rem; } .my-0 { margin-top: 0; margin-bottom: 0; } .my-1 { margin-top: 1rem; margin-bottom: 1rem; } .my-2 { margin-top: 2rem; margin-bottom: 2rem; } // Padding .p-0 { padding: 0; } .p-1 { padding: 1rem; } .p-2 { padding: 2rem; } .pl-1 { padding-left: 1rem; } .pr-1 { padding-right: 1rem; } .pb-1 { padding-bottom: 1rem; } .px-a { padding-left: auto; padding-right: auto; } .px-1 { padding-left: 1rem; padding-right: 1rem; } .py-0 { padding-top: 0; padding-bottom: 0; } .py-1 { padding-top: 1rem; padding-bottom: 1rem; } .py-2 { padding-top: 2rem; padding-bottom: 2rem; } ================================================ FILE: ui/src/scss/_producthunt.scss ================================================ // // Product Hunt banner // .product-hunt { background-color: white; padding: 1rem; vertical-align: middle; color: black; width:100%; margin:auto; display:flex; flex-direction:row; justify-content:center; background: #fafad2; border-top: 1px solid black; border-bottom: 1px solid black; .ph-icon { height: 2rem; display:inline-block; } p { display:inline-block; padding:0; margin:0; margin-left:1rem; margin-top:0.5rem; } cursor: pointer; &:hover { background: darken(#fafad2, 25%); } @media (max-width:400px){ font-size:14px; padding-left:0; } } ================================================ FILE: ui/src/scss/landingpage.scss ================================================ // Loading of font awesome // @import url("https://use.fontawesome.com/releases/v5.3.1/css/all.css"); // Loading of color scheme @import "_color.scss"; // Min-width for body (for some sanity) body { min-width: 280px; background: white; } // // Landing page header and logo // .header { padding-top: 3rem; padding-bottom: 2rem; width: 100vw; .logo { width:40vw; max-width: 800px; margin-bottom:1rem; } h1 { color:$bright-text; padding-left:2rem; padding-right:2rem; } h2 { color:$dark-text; padding-left:2rem; padding-right:2rem; } h1,h2 { margin:0; } @media only screen and (max-width:760px) { .logo { width: 80vw; } // Line display hack to force a new block h1 a { display:block; } } } // // Email selection box // .email-selection { width: 100vw; padding-top: 1rem; padding-bottom: 3rem; // Adust the input box elements borders // $input-box-el-border-radius: 0.4rem; $input-box-el-border-radius: 0rem; // The main input box .input-box { // Remove space between inline-blocks font-size:0; // Input-box radius : note you will need // to update input left top/bottom radius as well background-color:$color5-base; display: inline-block; border-radius: $input-box-el-border-radius; border: 3px solid black; // Common settings for input, and suffix label .input-email, .input-suffix { padding:0; margin:0; font-size:1rem; padding-top:0.25rem; padding-bottom:0.25rem; } // Input email styling .input-email { border:0px; width: 12rem; display: inline-block; text-align:center; // Border radius overwrite border-top-left-radius: $input-box-el-border-radius; border-bottom-left-radius: $input-box-el-border-radius; border-top-right-radius: 0rem; border-bottom-right-radius: 0rem; // input text color color: $dark-text; // Right border border-right: 3px solid black; } // Input email suffix .input-suffix { width: 10rem; display: inline-block; padding-left:1rem; padding-right:1rem; // Text with subtle fade out color: fade-out($dark-text, 0.3); } } // Submit box .submit-box { display:inline-block; margin-left:1rem; } // Submission button styling .submit { padding: 0.25rem 1rem 0.25rem 1rem; margin:0; border: 0; font-size:1rem; font-weight: bold; background: $cta-base; color: $cta-base-text; border-radius: $input-box-el-border-radius; font-size: 100%; font-family: inherit; text-transform: none; -webkit-appearance: button; border: 3px solid black; } // Hover submit button styling .submit:hover { background: $cta-hover; color: $cta-hover-text; } /* @media only screen and (max-width:760px) { // Collapes the inbox input into 2 lines .input-box { display: inline-block; vertical-align: middle; width: 12rem; .input-email { border-top-right-radius: $input-box-el-border-radius; border-bottom-left-radius: 0; } } // Increase submit button height .submit-box { display: inline-block; vertical-align: middle; .submit { display: inline; height: 3.5rem; } } } */ @media only screen and (max-width:760px) { // Collapes the inbox input into 2 lines .input-box { display: inline-block; vertical-align: middle; width: 75%; .input-email { border-top-right-radius: $input-box-el-border-radius; border-bottom-left-radius: 0; width: 100%; height:2rem; } } // Increase submit button height .submit-box { margin-top:1.25rem; margin-left:0; display: block; vertical-align: middle; .submit { display: inline; width: 75%; height:2.5rem; } } } } // // Love notes // .love-notes { padding: 1rem; background: white; p { font-size:1.2rem; } } // // Line break // .line-break { width: 80vw; background: black; margin: auto; height: 1px; } // // Deployment guide content // .info-guide { padding: 1rem; .features { padding-top:2rem; margin:auto; display:flex; flex-direction:row; justify-content:center; } .feature-card { flex-grow: 1; flex-shrink: 1; flex-basis: 0; padding-left:1rem; padding-right:1rem; } .feature-card-hover:hover { background-color:rgba(30,30,30,0.2); border-radius: 8px; } .fas { margin-right:0.5rem; } @media only screen and (max-width:800px) { .features { flex-direction:column; } } @media only screen and (max-width:1080px) { .steps { flex-direction: column; } } } .snippet { padding: 1rem; display: flex; flex-direction: row; margin:auto; justify-content: center; align-items: center; .features { padding: 0; background-color:rgba(30,30,30,0.1); border-radius:8px; } .features:hover { background-color:rgba(30,30,30,0.2); } .feature-card { flex-grow: 1; flex-shrink: 1; flex-basis: 0; padding-left:1rem; padding-right:0.5rem; padding-top: 1rem; align-self: center; } .feature-gif { flex-grow: 1; flex-shrink: 1; flex-basis: 0; align-self: flex-end; padding-left:0.5rem; padding-right:1rem; padding-bottom: 1rem; padding-top: 1rem; img { width: 100%; height: 100%; } } .hljs { text-align: left; border-radius: 8px; display: block; overflow-x: auto; margin-top: 2rem; padding: 0.5em; background: rgb(35, 36, 31); color: rgb(248, 248, 242); } @media only screen and (max-width:1170px) { .features { flex-direction:column; pre { font-size:0.75rem; } } .feature-gif { padding-top: 0; padding-left: 1rem; } } @media only screen and (max-width:630px){ .feature-card { padding-right:1rem; img { width:100%; height: auto; } pre { font-size:0.5rem; } } .hljs { margin-top: 1rem; } } } .line-break { height:2px; // Fallback background:black; // Gradient background:linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.2), rgba(0,0,0,0)); width:50%; margin:auto; margin-top:2rem; // margin-bottom:4rem; } .deploy-code { width: 30rem; max-width:90%; margin:auto; border-radius: 8px; scroll-behavior: auto; } .config-code { font-size:0.8rem; width:50rem; max-width: 90%; background: #F6F8FF; margin:auto; border-radius: 8px; scroll-behavior: auto; text-align:justify; } @media only screen and (max-width:800px) { .deploy-code { font-size:0.75rem; } .config-code { font-size: 0.75rem; } } .deploy-segmet { p { padding:1rem; } h3 { padding-left:1rem; padding-right:1rem; } .self-host-tier-link { text-decoration: none; cursor: pointer; display:inline-block; width:12rem; } .self-host-tier { border:3px solid black; border-radius:5px; display:flex; flex-direction: column; padding:0; max-width:12rem; margin:auto; background:white; text-decoration: none; .tier-title { margin:0; background: #5a86e4; padding:1rem; border-bottom:2px solid black; h3 { display: inline-block; color:white; margin:0; padding:0; } } .price { margin:0; padding:1rem; color:black; h3 { color:black; font-size: 4rem; margin:0; padding:0; } } } .disclaimer { font-size:0.75rem; } } ================================================ FILE: ui/src/store/emailStore.js ================================================ import config from '@/../config/apiconfig.js' // State const state = { domain: config.domain, apiUrl: config.apiUrl } const mutations = { } const getters = {} const actions = {} export default { mutations, state, getters, actions } ================================================ FILE: ui/uilicious-test/inboxkitten.test.js ================================================ // // This is a https://uilicious.com/ test case // // See : https://test.uilicious.com/test/public/DRupiHvXGspN9wVUYNv3Re // for an example of using this test script // // Alternatively signup to schedule a background task, or use their CLI =) // #selfpromotion // // // Testing for empty inbox // // Lets goto inbox kitten I.goTo("https://inboxkitten.com"); I.see("Open-Source Disposable Email"); // Go to a random inbox inbox I.fill("email", SAMPLE.id(22)); I.click("Get Mail Nyow!"); // Check that its empty I.see("There for no messages for this kitten :("); // // Testing for regular email // (sent using a jenkins perodic build) // // Lets go back inbox kitten mailbox I.goTo("https://inboxkitten.com"); I.see("Open-Source Disposable Email"); I.fill("email", "ik-reciever-f7s1g28"); I.click("Get Mail Nyow!"); // See an email we expect, and click it I.click("Testing inboxkitten subject"); // And validate the content I.see("Testing inboxkitten text content"); ================================================ FILE: ui/vite.config.js ================================================ import path from "path" import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue2' // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: { // resolve "@" - required for css imports "@": path.resolve(__dirname, "src"), // we need to use the vue build with the runtime template compiler included, otherwise vue will complain about the needing the runtime template compiler or to pre-compile the templates vue: 'vue/dist/vue.esm.js', } }, })