Builds and deploys
This commit is contained in:
parent
d7feb26208
commit
07f2537382
|
@ -0,0 +1,17 @@
|
||||||
|
# This file specifies files that are *not* uploaded to Google Cloud Platform
|
||||||
|
# using gcloud. It follows the same syntax as .gitignore, with the addition of
|
||||||
|
# "#!include" directives (which insert the entries of the given .gitignore-style
|
||||||
|
# file at that point).
|
||||||
|
#
|
||||||
|
# For more information, run:
|
||||||
|
# $ gcloud topic gcloudignore
|
||||||
|
#
|
||||||
|
.gcloudignore
|
||||||
|
# If you would like to upload your .git directory, .gitignore file or files
|
||||||
|
# from your .gitignore file, remove the corresponding line
|
||||||
|
# below:
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
#!include:.gitignore
|
|
@ -0,0 +1,16 @@
|
||||||
|
# This file specifies files that are *not* uploaded to Google Cloud Platform
|
||||||
|
# using gcloud. It follows the same syntax as .gitignore, with the addition of
|
||||||
|
# "#!include" directives (which insert the entries of the given .gitignore-style
|
||||||
|
# file at that point).
|
||||||
|
#
|
||||||
|
# For more information, run:
|
||||||
|
# $ gcloud topic gcloudignore
|
||||||
|
#
|
||||||
|
.gcloudignore
|
||||||
|
# If you would like to upload your .git directory, .gitignore file or files
|
||||||
|
# from your .gitignore file, remove the corresponding line
|
||||||
|
# below:
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
node_modules
|
|
@ -0,0 +1,8 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.domain = 'quepasaalpujarra.com';
|
||||||
|
exports.mailgun = {
|
||||||
|
apiKey: 'key-64fc7260cecfd4a8d5fb51e97791b330',
|
||||||
|
domain: exports.domain
|
||||||
|
};
|
||||||
|
exports.projectId = 'calendar-189316';
|
|
@ -0,0 +1,23 @@
|
||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const url_1 = require("url");
|
||||||
|
const user_1 = require("./user");
|
||||||
|
exports.isUserAvailable = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const params = url_1.parse(req.url, true).query;
|
||||||
|
const user = yield user_1.getUser({
|
||||||
|
email: params.email,
|
||||||
|
username: params.username
|
||||||
|
});
|
||||||
|
res.send({
|
||||||
|
exists: !!user
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
});
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "ts-gcf-functions",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@google-cloud/datastore": "^1.4.0",
|
||||||
|
"mailgun-js": "^0.16.0",
|
||||||
|
"node-pre-gyp": "^0.9.0",
|
||||||
|
"randomstring": "^1.1.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/es6-promise": "^3.3.0",
|
||||||
|
"@types/node": "^9.6.1",
|
||||||
|
"tslint": "^5.9.1",
|
||||||
|
"typescript": "^2.8.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "./node_modules/.bin/tsc"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const mailgun_js_1 = require("mailgun-js");
|
||||||
|
const config_1 = require("./config");
|
||||||
|
const client = mailgun_js_1.default(config_1.mailgun);
|
||||||
|
exports.sendEmail = (email) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
mailgun_js_1.default.messages().send(email, function (error, body) {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resolve(body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,51 @@
|
||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const Datastore = require('@google-cloud/datastore');
|
||||||
|
const config_1 = require("./config");
|
||||||
|
const datastore = Datastore({
|
||||||
|
projectId: config_1.projectId,
|
||||||
|
});
|
||||||
|
class Repository {
|
||||||
|
static createUser(user) {
|
||||||
|
const entity = {
|
||||||
|
key: datastore.key('User'),
|
||||||
|
data: user
|
||||||
|
};
|
||||||
|
return datastore.save(entity);
|
||||||
|
}
|
||||||
|
static saveSessionInvite(invite) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const entity = {
|
||||||
|
key: datastore.key('SessionInvite'),
|
||||||
|
data: invite
|
||||||
|
};
|
||||||
|
return yield datastore.save(entity);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static getUser(user) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let query = datastore
|
||||||
|
.createQuery('user');
|
||||||
|
if (user.email) {
|
||||||
|
query = query.filter('email', '=', user.email);
|
||||||
|
}
|
||||||
|
if (user.username) {
|
||||||
|
query = query.filter('username', '=', user.username);
|
||||||
|
}
|
||||||
|
return yield datastore.runQuery(query)
|
||||||
|
.then(results => {
|
||||||
|
return results[0];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
;
|
||||||
|
}
|
||||||
|
exports.default = Repository;
|
|
@ -0,0 +1,46 @@
|
||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
// Free API to get location from IP: http://freegeoip.net/json/149.11.144.50
|
||||||
|
const randomstring_1 = require("randomstring");
|
||||||
|
const post_office_1 = require("./post_office");
|
||||||
|
const config_1 = require("./config");
|
||||||
|
const repository_1 = require("./repository");
|
||||||
|
const newInvite = (user) => {
|
||||||
|
return {
|
||||||
|
oneTimeKey: randomstring_1.default.generate({
|
||||||
|
length: 24,
|
||||||
|
charset: 'alphabetic'
|
||||||
|
}),
|
||||||
|
userId: user.id
|
||||||
|
};
|
||||||
|
};
|
||||||
|
exports.inviteUser = (user) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const invite = newInvite(user);
|
||||||
|
try {
|
||||||
|
yield repository_1.default.saveSessionInvite(invite);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error('Failed to save invite', invite);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
yield post_office_1.sendEmail({
|
||||||
|
to: user.email,
|
||||||
|
from: `signin@${config_1.domain}`,
|
||||||
|
text: `invitation for session key: ${invite.oneTimeKey}`,
|
||||||
|
subject: 'Invitation for session'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error('Failed to send invitation email', invite);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,14 @@
|
||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const repository_1 = require("./repository");
|
||||||
|
exports.getUser = (keys) => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
return yield repository_1.default.getUser(keys);
|
||||||
|
});
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const domain = 'quepasaalpujarra.com'
|
||||||
|
export const mailgun = {
|
||||||
|
apiKey: 'key-64fc7260cecfd4a8d5fb51e97791b330',
|
||||||
|
domain
|
||||||
|
}
|
||||||
|
export const projectId = 'calendar-189316'
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import {parse} from 'url'
|
||||||
|
import { getUser } from './user'
|
||||||
|
|
||||||
|
export const isUserAvailable = async (req, res) => {
|
||||||
|
const params = parse(req.url, true).query
|
||||||
|
|
||||||
|
const user = await getUser({
|
||||||
|
email: params.email as string,
|
||||||
|
username: params.username as string
|
||||||
|
})
|
||||||
|
|
||||||
|
res.send({
|
||||||
|
exists: !!user
|
||||||
|
});
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import mailgun from 'mailgun-js';
|
||||||
|
import {mailgun as mailgunConfig} from './config';
|
||||||
|
|
||||||
|
const client = mailgun(mailgunConfig);
|
||||||
|
|
||||||
|
interface Email {
|
||||||
|
from: string
|
||||||
|
to: string
|
||||||
|
subject: string
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendEmail = async (email: Email) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
mailgun.messages().send(email, function (error, body) {
|
||||||
|
if (error) {
|
||||||
|
reject(error)
|
||||||
|
} else {
|
||||||
|
resolve(body)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
const Datastore = require('@google-cloud/datastore')
|
||||||
|
import {SessionInvite, User, UserKeys} from './types'
|
||||||
|
import { projectId } from './config'
|
||||||
|
|
||||||
|
const datastore = Datastore({
|
||||||
|
projectId: projectId,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default class Repository {
|
||||||
|
|
||||||
|
static createUser(user: User) {
|
||||||
|
const entity = {
|
||||||
|
key: datastore.key('User'),
|
||||||
|
data: user
|
||||||
|
}
|
||||||
|
return datastore.save(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
static async saveSessionInvite(invite: SessionInvite) {
|
||||||
|
const entity = {
|
||||||
|
key: datastore.key('SessionInvite'),
|
||||||
|
data: invite
|
||||||
|
}
|
||||||
|
return await datastore.save(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getUser (user: UserKeys) {
|
||||||
|
let query = datastore
|
||||||
|
.createQuery('user')
|
||||||
|
|
||||||
|
if (user.email) {
|
||||||
|
query = query.filter('email', '=', user.email)
|
||||||
|
}
|
||||||
|
if (user.username) {
|
||||||
|
query = query.filter('username', '=', user.username)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await datastore.runQuery(query)
|
||||||
|
.then(results => {
|
||||||
|
return results[0]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Free API to get location from IP: http://freegeoip.net/json/149.11.144.50
|
||||||
|
import randomstring from 'randomstring'
|
||||||
|
|
||||||
|
import { sendEmail } from './post_office'
|
||||||
|
import { domain } from './config'
|
||||||
|
import repository from './repository'
|
||||||
|
import {SessionInvite, User} from "./types";
|
||||||
|
|
||||||
|
const newInvite = (user: User): SessionInvite => {
|
||||||
|
return {
|
||||||
|
oneTimeKey: randomstring.generate({
|
||||||
|
length: 24,
|
||||||
|
charset: 'alphabetic'
|
||||||
|
}),
|
||||||
|
userId: user.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const inviteUser = async (user: User) => {
|
||||||
|
const invite = newInvite(user)
|
||||||
|
try {
|
||||||
|
await repository.saveSessionInvite(invite)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to save invite', invite)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendEmail({
|
||||||
|
to: user.email,
|
||||||
|
from: `signin@${domain}`,
|
||||||
|
text: `invitation for session key: ${invite.oneTimeKey}`,
|
||||||
|
subject: 'Invitation for session'
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to send invitation email', invite)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
export interface Session {
|
||||||
|
createdAt: Date
|
||||||
|
ttlMs: number
|
||||||
|
expired: boolean
|
||||||
|
authMethod: string
|
||||||
|
hash: string
|
||||||
|
userAgent: string
|
||||||
|
location: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SessionInvite {
|
||||||
|
oneTimeKey: string
|
||||||
|
userId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DBEntity {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserKeys {
|
||||||
|
username: string,
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User extends UserKeys, DBEntity {
|
||||||
|
firstName: string
|
||||||
|
lastName?: string
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import {UserKeys} from './types'
|
||||||
|
import repository from './repository'
|
||||||
|
|
||||||
|
export const getUser = async (keys: UserKeys) => {
|
||||||
|
return await repository.getUser(keys)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
const path = require('path')
|
||||||
|
const nodeExternals = require('webpack-node-externals');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './src/index.ts',
|
||||||
|
target: 'node',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.ts?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts']
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: 'index.js', // <-- Important
|
||||||
|
libraryTarget: 'this' // <-- Important
|
||||||
|
},
|
||||||
|
externals: [nodeExternals()]
|
||||||
|
};
|
Loading…
Reference in New Issue