├── hosting └── src │ ├── static │ ├── .gitkeep │ ├── favicon.ico │ ├── apple-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── ms-icon-70x70.png │ ├── apple-icon-57x57.png │ ├── apple-icon-60x60.png │ ├── apple-icon-72x72.png │ ├── apple-icon-76x76.png │ ├── favicon-opengraph.png │ ├── ms-icon-144x144.png │ ├── ms-icon-150x150.png │ ├── ms-icon-310x310.png │ ├── android-icon-36x36.png │ ├── android-icon-48x48.png │ ├── android-icon-72x72.png │ ├── android-icon-96x96.png │ ├── apple-icon-114x114.png │ ├── apple-icon-120x120.png │ ├── apple-icon-144x144.png │ ├── apple-icon-152x152.png │ ├── apple-icon-180x180.png │ ├── android-icon-144x144.png │ ├── android-icon-192x192.png │ ├── apple-icon-precomposed.png │ ├── politica-de-privacidade-v1.pdf │ ├── browserconfig.xml │ └── manifest.json │ ├── ads.txt │ ├── robots.txt │ ├── config │ ├── prod.env.js │ ├── dev.env.js │ ├── app.sample.js │ └── index.js │ ├── build │ ├── logo.png │ ├── vue-loader.conf.js │ ├── build.js │ ├── check-versions.js │ ├── webpack.base.conf.js │ ├── utils.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js │ ├── src │ ├── assets │ │ ├── logo.png │ │ ├── tmau1.png │ │ ├── tmau2.png │ │ ├── tmau3.png │ │ ├── chevron-left.svg │ │ ├── bear.svg │ │ └── bear2.svg │ ├── store │ │ ├── getters.js │ │ ├── actions.js │ │ ├── index.js │ │ └── mutations.js │ ├── main.js │ ├── utils.js │ ├── router │ │ └── index.js │ ├── scss │ │ └── _color.scss │ ├── App.vue │ └── components │ │ ├── messageDetail.vue │ │ ├── global │ │ ├── GlobalHeader.vue │ │ ├── bear.vue │ │ ├── GlobalBear.vue │ │ ├── GlobalFooter.vue │ │ └── GlobalEmailForm.vue │ │ ├── messageList.vue │ │ └── createEmail.vue │ ├── .editorconfig │ ├── .gitignore │ ├── .postcssrc.js │ ├── .babelrc │ ├── README.md │ ├── package.json │ └── index.html ├── functions ├── .gitignore ├── config │ └── app.sample.js ├── index.js ├── package.json ├── src │ ├── mailgun.js │ └── sendgrid.js └── utils.js ├── database.rules.json ├── README.md ├── firebase.json ├── LICENSE └── .gitignore /hosting/src/static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /hosting/src/ads.txt: -------------------------------------------------------------------------------- 1 | google.com, pub-3468723296547492, DIRECT, f08c47fec0942fa0 2 | -------------------------------------------------------------------------------- /hosting/src/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: /static/ 3 | Allow: / 4 | Disallow: /* 5 | -------------------------------------------------------------------------------- /hosting/src/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /hosting/src/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/build/logo.png -------------------------------------------------------------------------------- /hosting/src/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/favicon.ico -------------------------------------------------------------------------------- /hosting/src/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/src/assets/logo.png -------------------------------------------------------------------------------- /hosting/src/src/assets/tmau1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/src/assets/tmau1.png -------------------------------------------------------------------------------- /hosting/src/src/assets/tmau2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/src/assets/tmau2.png -------------------------------------------------------------------------------- /hosting/src/src/assets/tmau3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/src/assets/tmau3.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon.png -------------------------------------------------------------------------------- /hosting/src/static/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/favicon-16x16.png -------------------------------------------------------------------------------- /hosting/src/static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/favicon-32x32.png -------------------------------------------------------------------------------- /hosting/src/static/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/favicon-96x96.png -------------------------------------------------------------------------------- /hosting/src/static/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/ms-icon-70x70.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-57x57.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-60x60.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-72x72.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-76x76.png -------------------------------------------------------------------------------- /hosting/src/static/favicon-opengraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/favicon-opengraph.png -------------------------------------------------------------------------------- /hosting/src/static/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/ms-icon-144x144.png -------------------------------------------------------------------------------- /hosting/src/static/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/ms-icon-150x150.png -------------------------------------------------------------------------------- /hosting/src/static/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/ms-icon-310x310.png -------------------------------------------------------------------------------- /hosting/src/static/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/android-icon-36x36.png -------------------------------------------------------------------------------- /hosting/src/static/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/android-icon-48x48.png -------------------------------------------------------------------------------- /hosting/src/static/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/android-icon-72x72.png -------------------------------------------------------------------------------- /hosting/src/static/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/android-icon-96x96.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-114x114.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-120x120.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-144x144.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-152x152.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-180x180.png -------------------------------------------------------------------------------- /hosting/src/static/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/android-icon-144x144.png -------------------------------------------------------------------------------- /hosting/src/static/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/android-icon-192x192.png -------------------------------------------------------------------------------- /hosting/src/static/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/apple-icon-precomposed.png -------------------------------------------------------------------------------- /hosting/src/static/politica-de-privacidade-v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edtsz/tuamaeaquelaursa/HEAD/hosting/src/static/politica-de-privacidade-v1.pdf -------------------------------------------------------------------------------- /hosting/src/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /hosting/src/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /functions/config/app.sample.js: -------------------------------------------------------------------------------- 1 | // use https://firebase.google.com/docs/functions/config-env instead 2 | 3 | exports.token = 'your_custom_token'; 4 | exports.remove_older_than = 86400000; // milliseconds 5 | 6 | -------------------------------------------------------------------------------- /hosting/src/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | .tool-versions 16 | -------------------------------------------------------------------------------- /hosting/src/config/app.sample.js: -------------------------------------------------------------------------------- 1 | export const hostingConfig = { 2 | suffix: '@${your_domain}' 3 | }; 4 | 5 | export const firebaseConfig = { 6 | apiKey: "{apiKey}", 7 | authDomain: "{authDomain}", 8 | databaseURL: "{databaseURL}", 9 | }; 10 | -------------------------------------------------------------------------------- /functions/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const utils = require('./utils.js'); 4 | const glob = require('glob'); 5 | 6 | const files = glob.sync('./src/*.js', { cwd: __dirname }); 7 | 8 | files.forEach(file => { 9 | require(file)(exports, utils); 10 | }) 11 | -------------------------------------------------------------------------------- /database.rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "INBOX": { 4 | "$user_hash": { 5 | ".read": true, 6 | ".write": false 7 | } 8 | }, 9 | "INDEX": { 10 | "$uid" : { 11 | ".read": true 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![tuamaeaquelaursa header](./hosting/src/static/apple-icon.png)](https://tuamaeaquelaursa.com) 2 | 3 | # Emails descartáveis @tuamaeaquelaursa.com 4 | 5 | [Tua mãe, aquela ursa](https://tuamaeaquelaursa.com) é o email descartável que você precisa pra zoar a equipe de marketing que fica enviando SPAM 6 | -------------------------------------------------------------------------------- /hosting/src/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /hosting/src/static/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /hosting/src/src/store/getters.js: -------------------------------------------------------------------------------- 1 | 2 | export const messages = state => { 3 | return state.messages; 4 | } 5 | 6 | export const message = state => { 7 | return state.messages.find((currentValue, index, arr) => { 8 | return currentValue.key == state.current_params.message; 9 | }) || state.notFound; 10 | } 11 | -------------------------------------------------------------------------------- /hosting/src/src/assets/chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /hosting/src/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "modules": false, 7 | "targets": { 8 | "browsers": [ 9 | "> 1%", 10 | "last 2 versions", 11 | "not ie <= 8" 12 | ] 13 | } 14 | } 15 | ], 16 | "stage-2" 17 | ], 18 | "plugins": [ 19 | "transform-vue-jsx", 20 | "transform-runtime" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /hosting/src/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue'; 4 | import App from '@/App'; 5 | import store from '@/store'; 6 | import router from '@/router'; 7 | 8 | Vue.config.productionTip = false; 9 | 10 | new Vue({ 11 | el: '#app', 12 | store, 13 | router, 14 | render: h => h(App) 15 | }) 16 | -------------------------------------------------------------------------------- /hosting/src/README.md: -------------------------------------------------------------------------------- 1 | # tuamaeaquelaursa.com 2 | 3 | > Crie um email descartável @tuamaeaquelaursa.com 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 22 | -------------------------------------------------------------------------------- /hosting/src/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Tua mãe, aquela ursa | API", 4 | "scripts": { 5 | "serve": "firebase serve --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "12" 13 | }, 14 | "dependencies": { 15 | "busboy": "^0.3.1", 16 | "firebase-admin": "^8.13.0", 17 | "firebase-functions": "^3.22.0", 18 | "glob": "^7.2.3" 19 | }, 20 | "devDependencies": { 21 | "firebase-functions-test": "^0.1.6" 22 | }, 23 | "private": true 24 | } 25 | -------------------------------------------------------------------------------- /hosting/src/src/utils.js: -------------------------------------------------------------------------------- 1 | export const fancy_date = ( date ) => { 2 | let cur_date = new Date(); 3 | let msg_date = new Date( date * 1000 ); 4 | let options = { 5 | hour: 'numeric', 6 | minute: 'numeric' 7 | }; 8 | 9 | if ( msg_date.getMonth() < cur_date.getMonth() ) { 10 | options.weekday = 'short'; 11 | options.month = 'short'; 12 | options.day = '2-digit'; 13 | } else { 14 | if ( msg_date.getDate() < cur_date.getDate() ) { 15 | options.weekday = 'short'; 16 | options.day = '2-digit'; 17 | } 18 | 19 | if ( msg_date.getDate() == cur_date.getDate() ) 20 | options.second = 'numeric'; 21 | } 22 | 23 | return msg_date.toLocaleString(navigator.language, options); 24 | } 25 | -------------------------------------------------------------------------------- /functions/src/mailgun.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const functions = require('firebase-functions'); 4 | 5 | 6 | module.exports = function (e, utils) { 7 | e.mailgun = functions.https.onRequest((request, response, next) => { 8 | if ( !utils.isValidRequest(request) ) 9 | return response.status(401).send(); 10 | 11 | const fields = { 12 | from : request.body['from'] || null, 13 | recipient : request.body['recipient'] || null, 14 | subject : request.body['subject'] || null, 15 | bodyHtml : request.body['body-html'] || null, 16 | bodyPlain : request.body['body-plain'] || null, 17 | headers : request.body['headers'] || null, 18 | }; 19 | 20 | response.send(utils.addNewMessage(fields)); 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "rules": "database.rules.json" 4 | }, 5 | "hosting": { 6 | "public": "hosting/public", 7 | "ignore": [ 8 | "firebase.json", 9 | "**/.*", 10 | "**/node_modules/**" 11 | ], 12 | "rewrites": [ 13 | { 14 | "source": "**", 15 | "destination": "/index.html" 16 | } 17 | ], 18 | "headers": [{ 19 | "source": "**/*", 20 | "headers": [ 21 | {"key": "X-Frame-Options", "value": "DENY"}, 22 | {"key": "X-Content-Type-Options", "value": "nosniff"}, 23 | {"key": "X-UA-Compatible", "value": "ie=edge"}, 24 | {"key": "X-XSS-Protection", "value": "1; mode=block"} 25 | ] 26 | }] 27 | }, 28 | "emulators": { 29 | "database": { 30 | "host": "0.0.0.0", 31 | "port": "9001" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /hosting/src/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Router from 'vue-router'; 3 | import ga from 'vue-ga'; 4 | import CreateEmail from '@/components/createEmail.vue'; 5 | import MessageList from '@/components/messageList.vue'; 6 | import MessageDetail from '@/components/messageDetail.vue'; 7 | 8 | Vue.use(Router); 9 | 10 | const router = new Router({ 11 | mode: 'history', 12 | routes: [ 13 | { 14 | path: '/', 15 | name: 'Home', 16 | component: CreateEmail, 17 | }, 18 | { 19 | path: '/:email', 20 | name: 'MessageList', 21 | component: MessageList, 22 | }, 23 | { 24 | path: '/:email/:message', 25 | name: 'MessageDetail', 26 | component: MessageDetail, 27 | } 28 | ], 29 | scrollBehavior (to, from, savedPosition) { 30 | return { x: 0, y: 0 }; 31 | } 32 | }); 33 | 34 | ga(router, 'UA-126612006-1'); 35 | 36 | export default router 37 | -------------------------------------------------------------------------------- /hosting/src/src/scss/_color.scss: -------------------------------------------------------------------------------- 1 | //----------------------------------------------- 2 | // Base Color 3 | //----------------------------------------------- 4 | $app-background: #859CC2; 5 | $app-foreground: #FFFFFF; 6 | 7 | //----------------------------------------------- 8 | // Text colors 9 | //----------------------------------------------- 10 | $bright-text: #FFFFFF; 11 | $dark-text: #040404; 12 | 13 | //----------------------------------------------- 14 | // CALL TO ACTION - colors 15 | //----------------------------------------------- 16 | $cta-base: #FF1B5F; 17 | $cta-base-text: #FFFFFF; 18 | $cta-hover: darken($cta-base, 10%); 19 | $cta-hover-text: #FFFFFF; 20 | 21 | // #DDE8F9 22 | // #C5B697 23 | // #383838 24 | // #775D46 25 | 26 | $background: #DDE8F9; 27 | $lighter-brown: #DAC4A5; 28 | $light-brown: #C5B697; 29 | $dark-brown: #765D46; 30 | $light-black: #464646; 31 | $black: #383838; 32 | $gray: #A2A2A2; 33 | $light-gray: #F2F2F2; 34 | -------------------------------------------------------------------------------- /hosting/src/static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tua mãe, aquela ursa", 3 | "icons": [ 4 | { 5 | "src": "\/static\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/static\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/static\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/static\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/static\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/static\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /hosting/src/src/store/actions.js: -------------------------------------------------------------------------------- 1 | 2 | export const clear = ({ commit, state }) => { 3 | console.info( 'action: clear' ); 4 | 5 | commit('clear'); 6 | } 7 | 8 | 9 | 10 | export const connect_to_box = ({ commit, state }) => { 11 | console.info( 'action: connect_to_box' ); 12 | 13 | if ( state._user_box === null ) 14 | commit('connect_to_box'); 15 | } 16 | 17 | 18 | 19 | export const hydrate_messages = ({ commit, state }, params) => { 20 | console.info( 'action: hydrate_messages' ); 21 | 22 | if ( state._user_box === null ) 23 | throw 'user_box undefined.'; 24 | 25 | if ( state.messages.length !== 0 ) { 26 | console.error('hydrate_messages: messages.length !== 0'); 27 | return; 28 | } 29 | 30 | state._unsubscribe = state._user_box 31 | .onSnapshot((querySnapshot) => { 32 | 33 | querySnapshot.docChanges().forEach((change) => { 34 | var res = change.doc.data(); 35 | res.key = change.doc.id; 36 | 37 | commit(`${change.type}_messages`, res); 38 | }); 39 | 40 | commit('sort_messages'); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Uilicious Private Limited. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /functions/src/sendgrid.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const functions = require('firebase-functions'); 4 | const Busboy = require('busboy'); 5 | 6 | module.exports = function (e, utils) { 7 | e.sendgrid = functions.https.onRequest((request, response, next) => { 8 | if ( !utils.isValidRequest(request) ) 9 | return response.status(401).send(); 10 | 11 | const busboy = new Busboy({headers: request.headers}); 12 | const input = {}; 13 | 14 | busboy.on('field', (fieldname, val) => { 15 | input[fieldname] = val; 16 | }); 17 | 18 | busboy.on('finish', () => { 19 | 20 | const fields = { 21 | from : input['from'] || null, 22 | recipient : input['to'] || null, 23 | subject : input['subject'] || null, 24 | bodyHtml : input['html'] || null, 25 | bodyPlain : input['text'] || null, 26 | headers : input['headers'] || null, 27 | }; 28 | 29 | response.send(utils.addNewMessage(fields)); 30 | }); 31 | 32 | busboy.end(request.rawBody); 33 | }); 34 | }; 35 | -------------------------------------------------------------------------------- /hosting/src/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 23 | 24 | 50 | -------------------------------------------------------------------------------- /hosting/src/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /functions/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const functions = require('firebase-functions'); 4 | const admin = require('firebase-admin'); 5 | 6 | admin.initializeApp(); 7 | const database = admin.firestore(); 8 | const mailboxes = database.collection('MAILBOXES'); 9 | const CONFIG = functions.config().app; 10 | 11 | 12 | 13 | const isValidRequest = (request) => CONFIG.token == request.query.token; 14 | 15 | 16 | 17 | const addNewMessage = (fields) => { 18 | if (! fields.recipient) 19 | return; 20 | 21 | const recipient = fields.recipient.match(/([\w-\.]+)@((?:[\w]+\.)+)([a-z]{2,4})/gi).shift().toLowerCase(); 22 | 23 | const domain = recipient.split('@').pop(); 24 | if (domain != 'tuamaeaquelaursa.com') { 25 | return {"message": "mailbox not found"}; 26 | } 27 | 28 | var expires_at = new Date(); 29 | 30 | if (recipient == 'falecom@tuamaeaquelaursa.com') { 31 | expires_at.setMonth(expires_at.getMonth() + 2); 32 | } 33 | 34 | const message = mailboxes.doc(recipient).collection('INBOX').add({ 35 | recipient : recipient == 'falecom@tuamaeaquelaursa.com' ? recipient : 'any', 36 | from : fields.from, 37 | subject : fields.subject, 38 | bodyHtml : fields.bodyHtml, 39 | created_at : Date.now(), 40 | expires_at : admin.firestore.Timestamp.fromDate(expires_at), 41 | }); 42 | 43 | return message; 44 | } 45 | 46 | 47 | module.exports = { 48 | isValidRequest, 49 | addNewMessage, 50 | } 51 | -------------------------------------------------------------------------------- /hosting/src/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | 9 | # Firebase cache 10 | .firebase/ 11 | 12 | # Firebase config 13 | 14 | # Uncomment this if you'd like others to create their own Firebase project. 15 | # For a team working on the same Firebase project(s), it is recommended to leave 16 | # it commented so all members can deploy to the same project(s) in .firebaserc. 17 | .firebaserc 18 | 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | *.pid.lock 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (http://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | package-lock.json 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | 68 | # dist folder 69 | hosting/public/ 70 | 71 | hosting/src/config/app.js 72 | functions/config/app.js 73 | .idea/ 74 | -------------------------------------------------------------------------------- /hosting/src/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | import * as getters from './getters' 5 | import * as actions from './actions' 6 | import * as mutations from './mutations' 7 | 8 | import firebase from 'firebase/compat/app'; 9 | import 'firebase/compat/firestore'; 10 | 11 | 12 | import { hostingConfig, firebaseConfig } from '@/../config/app.js'; 13 | import router from '@/router'; 14 | 15 | Vue.use(Vuex); 16 | 17 | const state = { 18 | notFound: { 19 | from: 'Tua mãe, aquela ursa', 20 | subject: 'Correspondências não encontrada', 21 | bodyHtml: '

nada para exibir
¯\\_(ツ)_/¯

', 22 | created_at: Date.now(), 23 | }, 24 | loading: { 25 | from: 'Tua mãe, aquela ursa', 26 | subject: 'Buscando correspondências', 27 | bodyHtml: '
...
', 28 | created_at: Date.now(), 29 | }, 30 | messages: [], 31 | message: {}, 32 | current_page: null, 33 | current_params: null, 34 | loader : false, 35 | _hosting : hostingConfig, 36 | _user_box: null, 37 | _unsubscribe: null, 38 | _db : firebase.initializeApp(firebaseConfig).firestore(), 39 | } 40 | 41 | state.message = state.loading; 42 | 43 | const store = new Vuex.Store({ 44 | state, 45 | getters, 46 | actions, 47 | mutations 48 | }); 49 | 50 | 51 | router.beforeEach((to, from, next) => { 52 | store.commit('current_page', to); 53 | 54 | if ( to.path === '/' ) 55 | store.dispatch('clear'); 56 | else 57 | store.dispatch('connect_to_box'); 58 | 59 | next(); 60 | }); 61 | 62 | export default store 63 | -------------------------------------------------------------------------------- /hosting/src/src/store/mutations.js: -------------------------------------------------------------------------------- 1 | 2 | export const current_page = (state, to) => { 3 | state.current_page = to.name; 4 | state.current_params = to.params; 5 | 6 | console.info('mutation: current_page = ' + to.name); 7 | } 8 | 9 | 10 | export const toggle_loader = (state) => { 11 | state.loader = !state.loader; 12 | 13 | console.info('mutation: toggle_loader = ' + state.loader); 14 | } 15 | 16 | 17 | export const clear = (state) => { 18 | state._unsubscribe && state._unsubscribe(); 19 | state.messages = []; 20 | state.message = state.loading; 21 | state._user_box = null; 22 | 23 | console.info('mutation: cleared'); 24 | } 25 | 26 | 27 | export const connect_to_box = (state) => { 28 | var recipient = (state.current_params.email + state._hosting.suffix).toLowerCase(); 29 | 30 | state._user_box = state._db.collection('MAILBOXES').doc(recipient).collection('INBOX'); 31 | 32 | console.info('mutation: connect_to_box'); 33 | } 34 | 35 | 36 | export const added_messages = (state, msg) => { 37 | state.messages.unshift(msg); 38 | 39 | console.info('mutation: added_messages'); 40 | } 41 | 42 | 43 | export const modified_messages = (state, msg) => { 44 | state.messages.forEach((currentValue, index, arr) => { 45 | if (currentValue.key === msg.key) { 46 | Object.keys(currentValue).forEach(prop => { 47 | if (msg[prop]) { 48 | currentValue[prop] = msg[prop]; 49 | } 50 | }) 51 | } 52 | }); 53 | 54 | console.info('mutation: modified_messages'); 55 | } 56 | 57 | 58 | export const removed_messages = (state, msg) => { 59 | state.messages.forEach((currentValue, index, arr) => { currentValue.key === msg.key && arr.splice(index, 1) }); 60 | 61 | console.info('mutation: removed_messages ' + msg.key); 62 | } 63 | 64 | 65 | export const sort_messages = (state) => { 66 | console.log('sort'); 67 | 68 | state.messages.sort((a, b) => { 69 | console.log('sorting'); 70 | return a.created_at < b.created_at; 71 | }) 72 | 73 | console.info('mutation: sort_messages '); 74 | } 75 | -------------------------------------------------------------------------------- /hosting/src/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: '0.0.0.0', // can be overwritten by process.env.HOST 17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: false, 19 | errorOverlay: true, 20 | notifyOnErrors: true, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | 24 | /** 25 | * Source Maps 26 | */ 27 | 28 | // https://webpack.js.org/configuration/devtool/#development 29 | devtool: 'cheap-module-eval-source-map', 30 | 31 | // If you have problems debugging vue-files in devtools, 32 | // set this to false - it *may* help 33 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 34 | cacheBusting: true, 35 | 36 | cssSourceMap: true 37 | }, 38 | 39 | build: { 40 | // Template for index.html 41 | index: path.resolve(__dirname, '../../public/index.html'), 42 | 43 | // Paths 44 | assetsRoot: path.resolve(__dirname, '../../public'), 45 | assetsSubDirectory: 'static', 46 | assetsPublicPath: '/', 47 | 48 | /** 49 | * Source Maps 50 | */ 51 | 52 | productionSourceMap: false, 53 | // https://webpack.js.org/configuration/devtool/#production 54 | devtool: '#source-map', 55 | 56 | // Gzip off by default as many popular static hosts such as 57 | // Surge or Netlify already gzip all static assets for you. 58 | // Before setting to `true`, make sure to: 59 | // npm install --save-dev compression-webpack-plugin 60 | productionGzip: false, 61 | productionGzipExtensions: ['js', 'css'], 62 | 63 | // Run the build command with an extra argument to 64 | // View the bundle analyzer report after build finishes: 65 | // `npm run build --report` 66 | // Set to `true` or `false` to always turn it on or off 67 | bundleAnalyzerReport: process.env.npm_config_report 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /hosting/src/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | 12 | 13 | module.exports = { 14 | context: path.resolve(__dirname, '../'), 15 | entry: { 16 | app: './src/main.js' 17 | }, 18 | output: { 19 | path: config.build.assetsRoot, 20 | filename: '[name].js', 21 | publicPath: process.env.NODE_ENV === 'production' 22 | ? config.build.assetsPublicPath 23 | : config.dev.assetsPublicPath 24 | }, 25 | resolve: { 26 | extensions: ['.js', '.vue', '.json'], 27 | alias: { 28 | 'vue$': 'vue/dist/vue.esm.js', 29 | '@': resolve('src'), 30 | } 31 | }, 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.vue$/, 36 | loader: 'vue-loader', 37 | options: vueLoaderConfig 38 | }, 39 | { 40 | test: /\.js$/, 41 | loader: 'babel-loader', 42 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 43 | }, 44 | { 45 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 46 | loader: 'url-loader', 47 | options: { 48 | limit: 10000, 49 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 50 | } 51 | }, 52 | { 53 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 54 | loader: 'url-loader', 55 | options: { 56 | limit: 10000, 57 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 58 | } 59 | }, 60 | { 61 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 62 | loader: 'url-loader', 63 | options: { 64 | limit: 10000, 65 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 66 | } 67 | } 68 | ] 69 | }, 70 | node: { 71 | // prevent webpack from injecting useless setImmediate polyfill because Vue 72 | // source contains it (although only uses it if it's native). 73 | setImmediate: false, 74 | // prevent webpack from injecting mocks to Node native modules 75 | // that does not make sense for the client 76 | dgram: 'empty', 77 | fs: 'empty', 78 | net: 'empty', 79 | tls: 'empty', 80 | child_process: 'empty' 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /hosting/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tuamaeaquelaursa.com", 3 | "version": "1.0.0", 4 | "description": "Crie um email descartável @tuamaeaquelaursa.com", 5 | "author": "Éderson Tiago Szlachta ", 6 | "private": true, 7 | "scripts": { 8 | "firebase": "firebase", 9 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 10 | "start": "npm run dev", 11 | "build": "node build/build.js", 12 | "deploy": "npm run build && npm run deploy-only", 13 | "deploy-only": "firebase deploy --only hosting" 14 | }, 15 | "dependencies": { 16 | "firebase": "9.6.1", 17 | "normalize.css": "^8.0.1", 18 | "vue": "^2.6.10", 19 | "vue-ga": "^1.1.0", 20 | "vue-router": "^3.1.2", 21 | "vuex": "^3.1.1" 22 | }, 23 | "devDependencies": { 24 | "autoprefixer": "^7.1.2", 25 | "babel-core": "^6.26.3", 26 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 27 | "babel-loader": "^7.1.1", 28 | "babel-plugin-syntax-jsx": "^6.18.0", 29 | "babel-plugin-transform-runtime": "^6.23.0", 30 | "babel-plugin-transform-vue-jsx": "^3.7.0", 31 | "babel-preset-env": "^1.7.0", 32 | "babel-preset-stage-2": "^6.24.1", 33 | "chalk": "^2.4.2", 34 | "copy-webpack-plugin": "^4.0.1", 35 | "css-loader": "^3.2.0", 36 | "extract-text-webpack-plugin": "^3.0.2", 37 | "file-loader": "^1.1.4", 38 | "firebase-tools": "^8.20.0", 39 | "friendly-errors-webpack-plugin": "^1.7.0", 40 | "html-webpack-externals-plugin": "^3.8.0", 41 | "html-webpack-plugin": "^2.30.1", 42 | "node-notifier": "^5.4.1", 43 | "node-sass": "^4.12.0", 44 | "optimize-css-assets-webpack-plugin": "^3.2.0", 45 | "ora": "^1.2.0", 46 | "portfinder": "^1.0.21", 47 | "postcss-import": "^11.0.0", 48 | "postcss-loader": "^2.0.8", 49 | "postcss-url": "^7.2.1", 50 | "rimraf": "^2.6.3", 51 | "sass-loader": "^7.2.0", 52 | "semver": "^5.3.0", 53 | "shelljs": "^0.7.6", 54 | "uglifyjs-webpack-plugin": "^1.1.1", 55 | "url-loader": "^2.1.0", 56 | "vue-loader": "^13.3.0", 57 | "vue-style-loader": "^3.0.1", 58 | "vue-template-compiler": "^2.6.10", 59 | "webpack": "^3.6.0", 60 | "webpack-bundle-analyzer": "^3.4.1", 61 | "webpack-cdn-plugin": "^3.1.4", 62 | "webpack-dev-server": "^2.9.1", 63 | "webpack-merge": "^4.2.1" 64 | }, 65 | "engines": { 66 | "node": ">= 6.0.0", 67 | "npm": ">= 3.0.0" 68 | }, 69 | "browserslist": [ 70 | "> 1%", 71 | "last 2 versions", 72 | "not ie <= 8" 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /hosting/src/src/components/messageDetail.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 53 | 54 | 97 | -------------------------------------------------------------------------------- /hosting/src/src/components/global/GlobalHeader.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 25 | 26 | 111 | -------------------------------------------------------------------------------- /hosting/src/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /hosting/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Tua mãe, aquela ursa 40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /hosting/src/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 11 | const portfinder = require('portfinder') 12 | 13 | const HOST = process.env.HOST 14 | const PORT = process.env.PORT && Number(process.env.PORT) 15 | 16 | const devWebpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 19 | }, 20 | // cheap-module-eval-source-map is faster for development 21 | devtool: config.dev.devtool, 22 | 23 | // these devServer options should be customized in /config/index.js 24 | devServer: { 25 | clientLogLevel: 'warning', 26 | historyApiFallback: { 27 | rewrites: [ 28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, 29 | ], 30 | }, 31 | hot: true, 32 | contentBase: false, // since we use CopyWebpackPlugin. 33 | compress: true, 34 | host: HOST || config.dev.host, 35 | port: PORT || config.dev.port, 36 | open: config.dev.autoOpenBrowser, 37 | overlay: config.dev.errorOverlay 38 | ? { warnings: false, errors: true } 39 | : false, 40 | publicPath: config.dev.assetsPublicPath, 41 | proxy: config.dev.proxyTable, 42 | quiet: true, // necessary for FriendlyErrorsPlugin 43 | watchOptions: { 44 | poll: config.dev.poll, 45 | } 46 | }, 47 | plugins: [ 48 | new webpack.DefinePlugin({ 49 | 'process.env': require('../config/dev.env') 50 | }), 51 | new webpack.HotModuleReplacementPlugin(), 52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 53 | new webpack.NoEmitOnErrorsPlugin(), 54 | // https://github.com/ampedandwired/html-webpack-plugin 55 | new HtmlWebpackPlugin({ 56 | filename: 'index.html', 57 | template: 'index.html', 58 | inject: true 59 | }), 60 | // copy custom static assets 61 | new CopyWebpackPlugin([ 62 | { 63 | from: path.resolve(__dirname, '../ads.txt'), 64 | to: '.', 65 | ignore: ['.*'] 66 | }, 67 | { 68 | from: path.resolve(__dirname, '../static'), 69 | to: config.dev.assetsSubDirectory, 70 | ignore: ['.*'] 71 | } 72 | ]) 73 | ] 74 | }) 75 | 76 | module.exports = new Promise((resolve, reject) => { 77 | portfinder.basePort = process.env.PORT || config.dev.port 78 | portfinder.getPort((err, port) => { 79 | if (err) { 80 | reject(err) 81 | } else { 82 | // publish the new Port, necessary for e2e tests 83 | process.env.PORT = port 84 | // add port to devServer config 85 | devWebpackConfig.devServer.port = port 86 | 87 | // Add FriendlyErrorsPlugin 88 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 89 | compilationSuccessInfo: { 90 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 91 | }, 92 | onErrors: config.dev.notifyOnErrors 93 | ? utils.createNotifierCallback() 94 | : undefined 95 | })) 96 | 97 | resolve(devWebpackConfig) 98 | } 99 | }) 100 | }) 101 | -------------------------------------------------------------------------------- /hosting/src/src/assets/bear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /hosting/src/src/components/messageList.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 65 | 66 | 140 | -------------------------------------------------------------------------------- /hosting/src/src/components/global/bear.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | 20 | 182 | -------------------------------------------------------------------------------- /hosting/src/src/components/global/GlobalBear.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 | 21 | 191 | -------------------------------------------------------------------------------- /hosting/src/src/components/global/GlobalFooter.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 55 | 56 | 134 | -------------------------------------------------------------------------------- /hosting/src/src/assets/bear2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Em mãenutenção! 46 | Alguma coisa de errada não está certa 47 | 48 | -------------------------------------------------------------------------------- /hosting/src/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin') 11 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 12 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 13 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 14 | 15 | const env = require('../config/prod.env') 16 | 17 | const webpackConfig = merge(baseWebpackConfig, { 18 | module: { 19 | rules: utils.styleLoaders({ 20 | sourceMap: config.build.productionSourceMap, 21 | extract: true, 22 | usePostCSS: true 23 | }) 24 | }, 25 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 26 | output: { 27 | path: config.build.assetsRoot, 28 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 29 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 30 | }, 31 | plugins: [ 32 | new HtmlWebpackExternalsPlugin({ 33 | externals: [ 34 | { 35 | module: 'normalize.css', 36 | entry: 'https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css', 37 | global: 'normalize.css', 38 | }, 39 | { 40 | module: 'core-js', 41 | entry: 'https://cdnjs.cloudflare.com/ajax/libs/core-js/2.6.9/core.min.js', 42 | global: 'core-js', 43 | }, 44 | { 45 | module: 'underscore', 46 | entry: 'https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js', 47 | global: 'underscore', 48 | }, 49 | { 50 | module: 'lodash', 51 | entry: 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js', 52 | global: 'lodash', 53 | }, 54 | { 55 | module: 'vue', 56 | entry: 'https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js', 57 | global: 'Vue', 58 | }, 59 | { 60 | module: 'vue-router', 61 | entry: 'https://unpkg.com/vue-router@3.1.2/dist/vue-router.min.js', 62 | global: 'VueRouter', 63 | }, 64 | { 65 | module: 'vuex', 66 | entry: 'https://unpkg.com/vuex@3.1.1/dist/vuex.min.js', 67 | global: 'Vuex', 68 | }, 69 | ], 70 | }), 71 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 72 | new webpack.DefinePlugin({ 73 | 'process.env': env 74 | }), 75 | new UglifyJsPlugin({ 76 | uglifyOptions: { 77 | compress: { 78 | warnings: false 79 | } 80 | }, 81 | sourceMap: config.build.productionSourceMap, 82 | parallel: true 83 | }), 84 | // extract css into its own file 85 | new ExtractTextPlugin({ 86 | filename: utils.assetsPath('css/[name].[contenthash].css'), 87 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 88 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 89 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 90 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 91 | allChunks: true, 92 | }), 93 | // Compress extracted CSS. We are using this plugin so that possible 94 | // duplicated CSS from different components can be deduped. 95 | new OptimizeCSSPlugin({ 96 | cssProcessorOptions: config.build.productionSourceMap 97 | ? { safe: true, map: { inline: false } } 98 | : { safe: true } 99 | }), 100 | // generate dist index.html with correct asset hash for caching. 101 | // you can customize output by editing /index.html 102 | // see https://github.com/ampedandwired/html-webpack-plugin 103 | new HtmlWebpackPlugin({ 104 | filename: config.build.index, 105 | template: 'index.html', 106 | inject: true, 107 | minify: { 108 | removeComments: true, 109 | collapseWhitespace: true, 110 | removeAttributeQuotes: true 111 | // more options: 112 | // https://github.com/kangax/html-minifier#options-quick-reference 113 | }, 114 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 115 | chunksSortMode: 'dependency' 116 | }), 117 | // keep module.id stable when vendor modules does not change 118 | new webpack.HashedModuleIdsPlugin(), 119 | // enable scope hoisting 120 | new webpack.optimize.ModuleConcatenationPlugin(), 121 | // split vendor js into its own file 122 | new webpack.optimize.CommonsChunkPlugin({ 123 | name: 'vendor', 124 | minChunks (module) { 125 | 126 | if ( module.resource && 127 | /\.js$/.test(module.resource) && 128 | module.resource.indexOf( 129 | path.join(__dirname, '../node_modules') 130 | ) === 0 ) 131 | console.log( module.resource ) 132 | 133 | // any required modules inside node_modules are extracted to vendor 134 | return ( 135 | module.resource && 136 | /\.js$/.test(module.resource) && 137 | module.resource.indexOf( 138 | path.join(__dirname, '../node_modules') 139 | ) === 0 140 | ) 141 | } 142 | }), 143 | // extract webpack runtime and module manifest to its own file in order to 144 | // prevent vendor hash from being updated whenever app bundle is updated 145 | new webpack.optimize.CommonsChunkPlugin({ 146 | name: 'manifest', 147 | minChunks: Infinity 148 | }), 149 | // This instance extracts shared chunks from code splitted chunks and bundles them 150 | // in a separate chunk, similar to the vendor chunk 151 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 152 | new webpack.optimize.CommonsChunkPlugin({ 153 | name: 'app', 154 | async: 'vendor-async', 155 | children: true, 156 | minChunks: 3 157 | }), 158 | 159 | // copy custom static assets 160 | new CopyWebpackPlugin([ 161 | { 162 | from: path.resolve(__dirname, '../ads.txt'), 163 | to: '.', 164 | ignore: ['.*'] 165 | }, 166 | { 167 | from: path.resolve(__dirname, '../static'), 168 | to: config.build.assetsSubDirectory, 169 | ignore: ['.*'] 170 | } 171 | ]) 172 | ] 173 | }) 174 | 175 | if (config.build.productionGzip) { 176 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 177 | 178 | webpackConfig.plugins.push( 179 | new CompressionWebpackPlugin({ 180 | asset: '[path].gz[query]', 181 | algorithm: 'gzip', 182 | test: new RegExp( 183 | '\\.(' + 184 | config.build.productionGzipExtensions.join('|') + 185 | ')$' 186 | ), 187 | threshold: 10240, 188 | minRatio: 0.8 189 | }) 190 | ) 191 | } 192 | 193 | if (config.build.bundleAnalyzerReport) { 194 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 195 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 196 | } 197 | 198 | module.exports = webpackConfig 199 | -------------------------------------------------------------------------------- /hosting/src/src/components/createEmail.vue: -------------------------------------------------------------------------------- 1 | 121 | 122 | 138 | 139 | 348 | 349 | -------------------------------------------------------------------------------- /hosting/src/src/components/global/GlobalEmailForm.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 75 | 76 | 190 | --------------------------------------------------------------------------------