chore(server): Move api-server in to it's own DIR
This commit is contained in:
committed by
mrugesh mohapatra
parent
9fba6bce4c
commit
46a217d0a5
89
api-server/common/models/User-Credential.js
Normal file
89
api-server/common/models/User-Credential.js
Normal file
@ -0,0 +1,89 @@
|
||||
import { Observable } from 'rx';
|
||||
import debug from 'debug';
|
||||
|
||||
import { observeMethod, observeQuery } from '../../server/utils/rx';
|
||||
import {
|
||||
createUserUpdatesFromProfile,
|
||||
getSocialProvider
|
||||
} from '../../server/utils/auth';
|
||||
|
||||
const log = debug('fcc:models:UserCredential');
|
||||
module.exports = function(UserCredential) {
|
||||
UserCredential.link = function(
|
||||
userId,
|
||||
_provider,
|
||||
authScheme,
|
||||
profile,
|
||||
credentials,
|
||||
options = {},
|
||||
cb
|
||||
) {
|
||||
if (typeof options === 'function' && !cb) {
|
||||
cb = options;
|
||||
options = {};
|
||||
}
|
||||
const User = UserCredential.app.models.User;
|
||||
const findCred = observeMethod(UserCredential, 'findOne');
|
||||
const createCred = observeMethod(UserCredential, 'create');
|
||||
|
||||
const provider = getSocialProvider(_provider);
|
||||
const query = {
|
||||
where: {
|
||||
provider: provider,
|
||||
externalId: profile.id
|
||||
}
|
||||
};
|
||||
|
||||
// find createCred if they exist
|
||||
// if not create it
|
||||
// if yes, update credentials
|
||||
// also if github
|
||||
// update profile
|
||||
// update username
|
||||
// update picture
|
||||
log('link query', query);
|
||||
return findCred(query)
|
||||
.flatMap(_credentials => {
|
||||
const modified = new Date();
|
||||
const updateUser = User.update$(
|
||||
{ id: userId },
|
||||
createUserUpdatesFromProfile(provider, profile)
|
||||
);
|
||||
let updateCredentials;
|
||||
if (!_credentials) {
|
||||
updateCredentials = createCred({
|
||||
provider,
|
||||
externalId: profile.id,
|
||||
authScheme,
|
||||
// we no longer want to keep the profile
|
||||
// this is information we do not need or use
|
||||
profile: null,
|
||||
credentials,
|
||||
userId,
|
||||
created: modified,
|
||||
modified
|
||||
});
|
||||
} else {
|
||||
_credentials.credentials = credentials;
|
||||
updateCredentials = observeQuery(
|
||||
_credentials,
|
||||
'updateAttributes',
|
||||
{
|
||||
profile: null,
|
||||
credentials,
|
||||
modified
|
||||
}
|
||||
);
|
||||
}
|
||||
return Observable.combineLatest(
|
||||
updateUser,
|
||||
updateCredentials,
|
||||
(_, credentials) => credentials
|
||||
);
|
||||
})
|
||||
.subscribe(
|
||||
credentials => cb(null, credentials),
|
||||
cb
|
||||
);
|
||||
};
|
||||
};
|
16
api-server/common/models/User-Credential.json
Normal file
16
api-server/common/models/User-Credential.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "userCredential",
|
||||
"plural": "userCredentials",
|
||||
"base": "UserCredential",
|
||||
"properties": {},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "user",
|
||||
"foreignKey": "userId"
|
||||
}
|
||||
},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
154
api-server/common/models/User-Identity.js
Normal file
154
api-server/common/models/User-Identity.js
Normal file
@ -0,0 +1,154 @@
|
||||
import { Observable } from 'rx';
|
||||
// import debug from 'debug';
|
||||
import dedent from 'dedent';
|
||||
import { isEmail } from 'validator';
|
||||
|
||||
import { observeMethod, observeQuery } from '../../server/utils/rx';
|
||||
import { wrapHandledError } from '../../server/utils/create-handled-error.js';
|
||||
|
||||
// const log = debug('fcc:models:userIdent');
|
||||
|
||||
export default function(UserIdent) {
|
||||
|
||||
UserIdent.on('dataSourceAttached', () => {
|
||||
UserIdent.findOne$ = observeMethod(UserIdent, 'findOne');
|
||||
});
|
||||
|
||||
UserIdent.login = function(
|
||||
_provider,
|
||||
authScheme,
|
||||
profile,
|
||||
credentials,
|
||||
options,
|
||||
cb
|
||||
) {
|
||||
const User = UserIdent.app.models.User;
|
||||
const AccessToken = UserIdent.app.models.AccessToken;
|
||||
options = options || {};
|
||||
if (typeof options === 'function' && !cb) {
|
||||
cb = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// get the social provider data and the external id from auth0
|
||||
profile.id = profile.id || profile.openid;
|
||||
const auth0IdString = '' + profile.id;
|
||||
const [ provider, socialExtId ] = auth0IdString.split('|');
|
||||
const query = {
|
||||
where: {
|
||||
provider: provider,
|
||||
externalId: socialExtId
|
||||
},
|
||||
include: 'user'
|
||||
};
|
||||
// get the email from the auth0 (its expected from social providers)
|
||||
const email = (profile && profile.emails && profile.emails[0]) ?
|
||||
profile.emails[0].value : '';
|
||||
if (!isEmail('' + email)) {
|
||||
throw wrapHandledError(
|
||||
new Error('invalid or empty email recieved from auth0'),
|
||||
{
|
||||
message: dedent`
|
||||
Oops... something is not right. We did not find a valid email from your
|
||||
${provider} account. Please try again with a different provider that has an
|
||||
email available with it.
|
||||
`,
|
||||
type: 'info',
|
||||
redirectTo: '/'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (provider === 'email') {
|
||||
|
||||
return User.findOne$({ where: { email } })
|
||||
.flatMap(user => {
|
||||
return user ?
|
||||
Observable.of(user) :
|
||||
User.create$({ email }).toPromise();
|
||||
})
|
||||
.flatMap(user => {
|
||||
if (!user) {
|
||||
throw wrapHandledError(
|
||||
new Error('could not find or create a user'),
|
||||
{
|
||||
message: dedent`
|
||||
Oops... something is not right. We could not find or create a
|
||||
user with that email.
|
||||
`,
|
||||
type: 'info',
|
||||
redirectTo: '/'
|
||||
}
|
||||
);
|
||||
}
|
||||
const createToken = observeQuery(
|
||||
AccessToken,
|
||||
'create',
|
||||
{
|
||||
userId: user.id,
|
||||
created: new Date(),
|
||||
ttl: user.constructor.settings.ttl
|
||||
}
|
||||
);
|
||||
const updateUser = user.update$({
|
||||
emailVerified: true,
|
||||
emailAuthLinkTTL: null,
|
||||
emailVerifyTTL: null
|
||||
});
|
||||
return Observable.combineLatest(
|
||||
Observable.of(user),
|
||||
createToken,
|
||||
updateUser,
|
||||
(user, token) => ({user, token})
|
||||
);
|
||||
})
|
||||
.subscribe(
|
||||
({ user, token }) => cb(null, user, null, token),
|
||||
cb
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
return UserIdent.findOne$(query)
|
||||
.flatMap(identity => {
|
||||
return identity ?
|
||||
Observable.of(identity.user()) :
|
||||
User.findOne$({ where: { email } })
|
||||
.flatMap(user => {
|
||||
return user ?
|
||||
Observable.of(user) :
|
||||
User.create$({ email }).toPromise();
|
||||
});
|
||||
})
|
||||
.flatMap(user => {
|
||||
|
||||
const createToken = observeQuery(
|
||||
AccessToken,
|
||||
'create',
|
||||
{
|
||||
userId: user.id,
|
||||
created: new Date(),
|
||||
ttl: user.constructor.settings.ttl
|
||||
}
|
||||
);
|
||||
const updateUser = user.update$({
|
||||
email: email,
|
||||
emailVerified: true,
|
||||
emailAuthLinkTTL: null,
|
||||
emailVerifyTTL: null
|
||||
});
|
||||
return Observable.combineLatest(
|
||||
Observable.of(user),
|
||||
createToken,
|
||||
updateUser,
|
||||
(user, token) => ({ user, token })
|
||||
);
|
||||
})
|
||||
.subscribe(
|
||||
({ user, token }) => cb(null, user, null, token),
|
||||
cb
|
||||
);
|
||||
|
||||
}
|
||||
};
|
||||
}
|
23
api-server/common/models/User-Identity.json
Normal file
23
api-server/common/models/User-Identity.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "userIdentity",
|
||||
"plural": "userIdentities",
|
||||
"base": "UserIdentity",
|
||||
"properties": {},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "user",
|
||||
"foreignKey": "userId"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
}
|
9
api-server/common/models/article.js
Normal file
9
api-server/common/models/article.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { Observable } from 'rx';
|
||||
|
||||
module.exports = function(Article) {
|
||||
Article.on('dataSourceAttached', () => {
|
||||
Article.findOne$ = Observable.fromNodeCallback(Article.findOne, Article);
|
||||
Article.findById$ = Observable.fromNodeCallback(Article.findById, Article);
|
||||
Article.find$ = Observable.fromNodeCallback(Article.find, Article);
|
||||
});
|
||||
};
|
102
api-server/common/models/article.json
Normal file
102
api-server/common/models/article.json
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"name": "article",
|
||||
"plural": "articles",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"options": {
|
||||
"validateUpsert": true
|
||||
},
|
||||
"properties": {
|
||||
"shortId": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"slugPart": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "A kebab-case-string created from the title, will have the shortId appended to it"
|
||||
},
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"description": "A place to keep the referral link and read time"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"author": {
|
||||
"type": "object",
|
||||
"required": true
|
||||
},
|
||||
"subtitle": {
|
||||
"type": "string"
|
||||
},
|
||||
"featureImage": {
|
||||
"type": "object"
|
||||
},
|
||||
"draft": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"renderableContent": {
|
||||
"type": "string"
|
||||
},
|
||||
"youtubeId": {
|
||||
"type": "string",
|
||||
"description": "A youtube video id eg: 'EErY9zXGLNU'"
|
||||
},
|
||||
"published": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"default": false
|
||||
},
|
||||
"featured": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"default": false
|
||||
},
|
||||
"underReview": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"default": false
|
||||
},
|
||||
"viewCount": {
|
||||
"type": "number",
|
||||
"required": true,
|
||||
"default": 1
|
||||
},
|
||||
"firstPublishedDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createdDate": {
|
||||
"type": "date",
|
||||
"required": true
|
||||
},
|
||||
"lastEditedDate": {
|
||||
"type": "date",
|
||||
"required": true
|
||||
},
|
||||
"history": {
|
||||
"type": [
|
||||
"object"
|
||||
],
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "user",
|
||||
"foreignKey": "externalId"
|
||||
},
|
||||
"popularity": {
|
||||
"type": "hasOne",
|
||||
"model": "popularity",
|
||||
"foreignKey": "popularityId"
|
||||
}
|
||||
},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
12
api-server/common/models/block.js
Normal file
12
api-server/common/models/block.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { Observable } from 'rx';
|
||||
|
||||
export default function(Block) {
|
||||
Block.on('dataSourceAttached', () => {
|
||||
Block.findOne$ =
|
||||
Observable.fromNodeCallback(Block.findOne, Block);
|
||||
Block.findById$ =
|
||||
Observable.fromNodeCallback(Block.findById, Block);
|
||||
Block.find$ =
|
||||
Observable.fromNodeCallback(Block.find, Block);
|
||||
});
|
||||
}
|
53
api-server/common/models/block.json
Normal file
53
api-server/common/models/block.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "block",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"options": {
|
||||
"validateUpsert": true
|
||||
},
|
||||
"properties": {
|
||||
"superBlock": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "The super block that this block belongs to"
|
||||
},
|
||||
"order": {
|
||||
"type": "number",
|
||||
"required": true,
|
||||
"description": "The order in which this block appears"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "The name of this block derived from the title, suitable for regex search"
|
||||
},
|
||||
"superOrder": {
|
||||
"type": "number",
|
||||
"required": true
|
||||
},
|
||||
"dashedName": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "Generated from the title to be URL friendly"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "The title of this block, suitable for display"
|
||||
},
|
||||
"time": {
|
||||
"type": "string",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"challenges": {
|
||||
"type": "hasMany",
|
||||
"model": "challenge",
|
||||
"foreignKey": "blockId"
|
||||
}
|
||||
},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
12
api-server/common/models/challenge.js
Normal file
12
api-server/common/models/challenge.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { Observable } from 'rx';
|
||||
|
||||
export default function(Challenge) {
|
||||
Challenge.on('dataSourceAttached', () => {
|
||||
Challenge.findOne$ =
|
||||
Observable.fromNodeCallback(Challenge.findOne, Challenge);
|
||||
Challenge.findById$ =
|
||||
Observable.fromNodeCallback(Challenge.findById, Challenge);
|
||||
Challenge.find$ =
|
||||
Observable.fromNodeCallback(Challenge.find, Challenge);
|
||||
});
|
||||
}
|
142
api-server/common/models/challenge.json
Normal file
142
api-server/common/models/challenge.json
Normal file
@ -0,0 +1,142 @@
|
||||
{
|
||||
"name": "challenge",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"trackChanges": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"id": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"index": {
|
||||
"mongodb": {
|
||||
"unique": true,
|
||||
"background": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"order": {
|
||||
"type": "number"
|
||||
},
|
||||
"suborder": {
|
||||
"type": "number"
|
||||
},
|
||||
"checksum": {
|
||||
"type": "number"
|
||||
},
|
||||
"isBeta": {
|
||||
"type": "boolean",
|
||||
"description": "Show only during dev or on beta site. Completely omitted otherwise"
|
||||
},
|
||||
"isComingSoon": {
|
||||
"type": "boolean",
|
||||
"description": "Challenge shows in production, but is unreachable and disabled. Is reachable in beta/dev only if isBeta flag is set"
|
||||
},
|
||||
"dashedName": {
|
||||
"type": "string"
|
||||
},
|
||||
"superBlock": {
|
||||
"type": "string",
|
||||
"description": "Used for ordering challenge blocks in map"
|
||||
},
|
||||
"superOrder": {
|
||||
"type": "number",
|
||||
"description": "Used to determine super block order, set by prepending two digit number to super block folder name"
|
||||
},
|
||||
"block": {
|
||||
"type": "string"
|
||||
},
|
||||
"difficulty": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "array"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"tests": {
|
||||
"type": "array"
|
||||
},
|
||||
"head": {
|
||||
"type": "array",
|
||||
"description": "Appended to user code",
|
||||
"default": []
|
||||
},
|
||||
"tail": {
|
||||
"type": "array",
|
||||
"description": "Prepended to user code",
|
||||
"default": []
|
||||
},
|
||||
"helpRoom": {
|
||||
"type": "string",
|
||||
"description": "Gitter help chatroom this challenge belongs too. Must be PascalCase",
|
||||
"default": "Help"
|
||||
},
|
||||
"fileName": {
|
||||
"type": "string",
|
||||
"description": "Filename challenge comes from. Used in dev mode"
|
||||
},
|
||||
"challengeSeed": {
|
||||
"type": "array"
|
||||
},
|
||||
"challengeType": {
|
||||
"type": "number"
|
||||
},
|
||||
"solutions": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"guideUrl": {
|
||||
"type": "string",
|
||||
"description": "Used to link to an article in the FCC guide"
|
||||
},
|
||||
"required": {
|
||||
"type": [
|
||||
{
|
||||
"type": {
|
||||
"link": {
|
||||
"type": "string",
|
||||
"description": "Used for css files"
|
||||
},
|
||||
"src": {
|
||||
"type": "string",
|
||||
"description": "Used for script files"
|
||||
},
|
||||
"crossDomain": {
|
||||
"type": "boolean",
|
||||
"description": "Files coming from FreeCodeCamp must mark this true"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"default": []
|
||||
},
|
||||
"template": {
|
||||
"type": "string",
|
||||
"description": "A template to render the compiled challenge source into. This template uses template literal delimiter, i.e. ${ foo }"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
}
|
32
api-server/common/models/flyer.json
Normal file
32
api-server/common/models/flyer.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "flyer",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"trackChanges": false,
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"isActive": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
}
|
119
api-server/common/models/job.json
Normal file
119
api-server/common/models/job.json
Normal file
@ -0,0 +1,119 @@
|
||||
{
|
||||
"name": "job",
|
||||
"base": "PersistedModel",
|
||||
"strict": true,
|
||||
"idInjection": true,
|
||||
"trackChanges": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"id": true
|
||||
},
|
||||
"position": {
|
||||
"type": "string"
|
||||
},
|
||||
"company": {
|
||||
"type": "string"
|
||||
},
|
||||
"logo": {
|
||||
"type": "string"
|
||||
},
|
||||
"city": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"phone": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"country": {
|
||||
"type": "string"
|
||||
},
|
||||
"locale": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "format: city, state"
|
||||
},
|
||||
"location": {
|
||||
"type": "geopoint",
|
||||
"description": "location in lat, long"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"isApproved": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"isHighlighted": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"isPaid": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"isFilled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"postedOn": {
|
||||
"type": "date",
|
||||
"defaultFn": "now"
|
||||
},
|
||||
"isFrontEndCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper must be front end certified to apply",
|
||||
"defaut": false
|
||||
},
|
||||
"isBackEndCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper must be back end certified to apply",
|
||||
"default": false
|
||||
},
|
||||
"isFullStackCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper must be full stack certified to apply",
|
||||
"default": false
|
||||
},
|
||||
"isRemoteOk": {
|
||||
"type": "boolean",
|
||||
"description": "Camper may work remotely",
|
||||
"default": false
|
||||
},
|
||||
"howToApply": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "How campers apply to a job"
|
||||
},
|
||||
"promoCodeUsed": {
|
||||
"type": "string",
|
||||
"description": "The promocode, if any, that the job uses"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
}
|
68
api-server/common/models/nonprofit.json
Normal file
68
api-server/common/models/nonprofit.json
Normal file
@ -0,0 +1,68 @@
|
||||
{
|
||||
"name": "nonprofit",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"trackChanges": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"id": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"index": {
|
||||
"mongodb": {
|
||||
"unique": true,
|
||||
"background": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"whatDoesNonprofitDo": {
|
||||
"type": "string"
|
||||
},
|
||||
"websiteLink": {
|
||||
"type": "string"
|
||||
},
|
||||
"endUser": {
|
||||
"type": "string"
|
||||
},
|
||||
"approvedDeliverables": {
|
||||
"type": "array"
|
||||
},
|
||||
"projectDescription": {
|
||||
"type": "string"
|
||||
},
|
||||
"logoUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"estimatedHours": {
|
||||
"type": "number"
|
||||
},
|
||||
"moneySaved": {
|
||||
"type": "number"
|
||||
},
|
||||
"currentStatus": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
}
|
55
api-server/common/models/pledge.json
Normal file
55
api-server/common/models/pledge.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "pledge",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"trackChanges": false,
|
||||
"properties": {
|
||||
"nonprofit": {
|
||||
"type": "string",
|
||||
"index": true
|
||||
},
|
||||
"amount": {
|
||||
"type": "number"
|
||||
},
|
||||
"dateStarted": {
|
||||
"type": "date",
|
||||
"defaultFn": "now"
|
||||
},
|
||||
"dateEnded": {
|
||||
"type": "date"
|
||||
},
|
||||
"formerUserId": {
|
||||
"type": "string"
|
||||
},
|
||||
"isOrphaned": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isCompleted": {
|
||||
"type": "boolean",
|
||||
"default": "false"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"user": {
|
||||
"type": "hasMany",
|
||||
"model": "user",
|
||||
"foreignKey": "userId"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
}
|
15
api-server/common/models/popularity.js
Normal file
15
api-server/common/models/popularity.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { Observable } from 'rx';
|
||||
|
||||
module.exports = function(Popularity) {
|
||||
Popularity.on('dataSourceAttached', () => {
|
||||
Popularity.findOne$ = Observable.fromNodeCallback(
|
||||
Popularity.findOne,
|
||||
Popularity
|
||||
);
|
||||
Popularity.findById$ = Observable.fromNodeCallback(
|
||||
Popularity.findById,
|
||||
Popularity
|
||||
);
|
||||
Popularity.find$ = Observable.fromNodeCallback(Popularity.find, Popularity);
|
||||
});
|
||||
};
|
28
api-server/common/models/popularity.json
Normal file
28
api-server/common/models/popularity.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "popularity",
|
||||
"plural": "popularities",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"options": {
|
||||
"validateUpsert": true
|
||||
},
|
||||
"properties": {
|
||||
"events": {
|
||||
"type": [
|
||||
"object"
|
||||
],
|
||||
"required": true,
|
||||
"default": []
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"article": {
|
||||
"type": "belongsTo",
|
||||
"model": "article",
|
||||
"foreignKey": "articleId"
|
||||
}
|
||||
},
|
||||
"acls": [],
|
||||
"methods": {}
|
||||
}
|
84
api-server/common/models/promo.js
Normal file
84
api-server/common/models/promo.js
Normal file
@ -0,0 +1,84 @@
|
||||
import { isAlphanumeric, isMongoId } from 'validator';
|
||||
import debug from 'debug';
|
||||
|
||||
const log = debug('fcc:models:promo');
|
||||
|
||||
export default function promo(Promo) {
|
||||
Promo.getButton = function getButton(id, code, type = 'isNot') {
|
||||
const Job = Promo.app.models.Job;
|
||||
if (!id || !isMongoId('' + id)) {
|
||||
return Promise.reject(new Error(
|
||||
'Must include job id'
|
||||
));
|
||||
}
|
||||
|
||||
if (
|
||||
!isAlphanumeric('' + code) &&
|
||||
type &&
|
||||
!isAlphanumeric('' + type)
|
||||
) {
|
||||
return Promise.reject(new Error(
|
||||
'Code or Type should be an alphanumeric'
|
||||
));
|
||||
}
|
||||
|
||||
const query = {
|
||||
where: {
|
||||
and: [{
|
||||
code: type === 'isNot' ? type : 'isHighlighted'
|
||||
},
|
||||
{
|
||||
type: type.replace(/^\$/g, '')
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
return Promo.findOne(query)
|
||||
.then(function(promo) {
|
||||
// turn promo model to plain js object;
|
||||
promo = promo.toJSON();
|
||||
return Job.updateAll({ id: id }, { promoCodeUsed: code })
|
||||
.then(function({ count = 0 } = {}) {
|
||||
log('job', count);
|
||||
if (count) {
|
||||
return {
|
||||
...promo,
|
||||
name: `${code} Discount`
|
||||
};
|
||||
}
|
||||
return Promise.reject(new Error(
|
||||
`Job ${id} not found`
|
||||
));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Promo.remoteMethod(
|
||||
'getButton',
|
||||
{
|
||||
description: 'Get button id for promocode',
|
||||
accepts: [
|
||||
{
|
||||
arg: 'id',
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'code',
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
arg: 'type',
|
||||
type: 'string'
|
||||
}
|
||||
],
|
||||
returns: [
|
||||
{
|
||||
arg: 'promo',
|
||||
type: 'object'
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
59
api-server/common/models/promo.json
Normal file
59
api-server/common/models/promo.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "promo",
|
||||
"base": "PersistedModel",
|
||||
"strict": true,
|
||||
"idInjection": true,
|
||||
"trackChanges": false,
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "The code to unlock the promotional discount"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "The name of the discount"
|
||||
},
|
||||
"buttonId": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"description": "The ID of the paypal button"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "A selector of different types of buttons for the same discount"
|
||||
},
|
||||
"fullPrice": {
|
||||
"type": "number",
|
||||
"required": true,
|
||||
"description": "The original amount"
|
||||
},
|
||||
"discountAmount": {
|
||||
"type": "number",
|
||||
"description": "The amount of the discount if applicable"
|
||||
},
|
||||
"discountPercent": {
|
||||
"type": "number",
|
||||
"description": "The amount of the discount as a percentage if applicable"
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "getButton"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
}
|
81
api-server/common/models/story.json
Normal file
81
api-server/common/models/story.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
"name": "story",
|
||||
"base": "PersistedModel",
|
||||
"idInjection": true,
|
||||
"trackChanges": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"id": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"index": {
|
||||
"mongodb": {
|
||||
"unique": true,
|
||||
"background": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"headline": {
|
||||
"type": "string"
|
||||
},
|
||||
"timePosted": {
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"link": {
|
||||
"type": "string"
|
||||
},
|
||||
"metaDescription": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"rank": {
|
||||
"type": "number",
|
||||
"default": 0
|
||||
},
|
||||
"upVotes": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"author": {
|
||||
"type": {}
|
||||
},
|
||||
"image": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"storyLink": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"accessType": "READ",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$authenticated",
|
||||
"permission": "ALLOW",
|
||||
"property": "create"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
}
|
1179
api-server/common/models/user.js
Normal file
1179
api-server/common/models/user.js
Normal file
File diff suppressed because it is too large
Load Diff
376
api-server/common/models/user.json
Normal file
376
api-server/common/models/user.json
Normal file
@ -0,0 +1,376 @@
|
||||
{
|
||||
"name": "user",
|
||||
"base": "User",
|
||||
"strict": "filter",
|
||||
"idInjection": true,
|
||||
"emailVerificationRequired": false,
|
||||
"trackChanges": false,
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"index": {
|
||||
"mongodb": {
|
||||
"unique": true,
|
||||
"background": true,
|
||||
"sparse": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"newEmail": {
|
||||
"type": "string"
|
||||
},
|
||||
"emailVerifyTTL": {
|
||||
"type": "date"
|
||||
},
|
||||
"emailVerified": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"emailAuthLinkTTL": {
|
||||
"type": "date"
|
||||
},
|
||||
"externalId": {
|
||||
"type": "string",
|
||||
"description": "A uuid/v4 used to identify user accounts"
|
||||
},
|
||||
"unsubscribeId": {
|
||||
"type": "string",
|
||||
"description": "An ObjectId used to unsubscribe users from the mailing list(s)"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"description": "No longer used for new accounts"
|
||||
},
|
||||
"progressTimestamps": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"isBanned": {
|
||||
"type": "boolean",
|
||||
"description": "User is banned from posting to camper news",
|
||||
"default": false
|
||||
},
|
||||
"isCheater": {
|
||||
"type": "boolean",
|
||||
"description": "Users who are confirmed to have broken academic honesty policy are marked as cheaters",
|
||||
"default": false
|
||||
},
|
||||
"githubProfile": {
|
||||
"type": "string"
|
||||
},
|
||||
"website": {
|
||||
"type": "string"
|
||||
},
|
||||
"_csrf": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"index": {
|
||||
"mongodb": {
|
||||
"unique": true,
|
||||
"background": true
|
||||
}
|
||||
},
|
||||
"require": true
|
||||
},
|
||||
"about": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"picture": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"linkedin": {
|
||||
"type": "string"
|
||||
},
|
||||
"codepen": {
|
||||
"type": "string"
|
||||
},
|
||||
"twitter": {
|
||||
"type": "string"
|
||||
},
|
||||
"acceptedPrivacyTerms": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"sendQuincyEmail": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"currentChallengeId": {
|
||||
"type": "string",
|
||||
"description": "The challenge last visited by the user",
|
||||
"default": ""
|
||||
},
|
||||
"isHonest": {
|
||||
"type": "boolean",
|
||||
"description": "Camper has signed academic honesty policy",
|
||||
"default": false
|
||||
},
|
||||
"isFrontEndCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper is front end certified",
|
||||
"default": false
|
||||
},
|
||||
"isDataVisCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper is data visualization certified",
|
||||
"default": false
|
||||
},
|
||||
"isBackEndCert": {
|
||||
"type": "boolean",
|
||||
"description": "Campers is back end certified",
|
||||
"default": false
|
||||
},
|
||||
"isFullStackCert": {
|
||||
"type": "boolean",
|
||||
"description": "Campers is full stack certified",
|
||||
"default": false
|
||||
},
|
||||
"isRespWebDesignCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper is responsive web design certified",
|
||||
"default": false
|
||||
},
|
||||
"is2018DataVisCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper is data visualization certified (2018)",
|
||||
"default": false
|
||||
},
|
||||
"isFrontEndLibsCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper is front end libraries certified",
|
||||
"default": false
|
||||
},
|
||||
"isJsAlgoDataStructCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper is javascript algorithms and data structures certified",
|
||||
"default": false
|
||||
},
|
||||
"isApisMicroservicesCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper is apis and microservices certified",
|
||||
"default": false
|
||||
},
|
||||
"isInfosecQaCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper is information security and quality assurance certified",
|
||||
"default": false
|
||||
},
|
||||
"is2018FullStackCert": {
|
||||
"type": "boolean",
|
||||
"description": "Camper is full stack certified (2018)",
|
||||
"default": false
|
||||
},
|
||||
"completedChallenges": {
|
||||
"type": [
|
||||
{
|
||||
"completedDate": "number",
|
||||
"id": "string",
|
||||
"solution": "string",
|
||||
"githubLink": "string",
|
||||
"challengeType": "number",
|
||||
"files": {
|
||||
"type": [
|
||||
{
|
||||
"contents": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"ext": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"default": []
|
||||
},
|
||||
"portfolio": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"yearsTopContributor": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
},
|
||||
"rand": {
|
||||
"type": "number",
|
||||
"index": true
|
||||
},
|
||||
"timezone": {
|
||||
"type": "string"
|
||||
},
|
||||
"theme": {
|
||||
"type": "string",
|
||||
"default": "default"
|
||||
},
|
||||
"profileUI": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"isLocked": true,
|
||||
"showAbout": false,
|
||||
"showCerts": false,
|
||||
"showHeatMap": false,
|
||||
"showLocation": false,
|
||||
"showName": false,
|
||||
"showPoints": false,
|
||||
"showPortfolio": false,
|
||||
"showTimeLine": false
|
||||
}
|
||||
},
|
||||
"badges": {
|
||||
"type": {
|
||||
"coreTeam": {
|
||||
"type": "array",
|
||||
"default": []
|
||||
}
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
"donationEmails": {
|
||||
"type": [
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"isDonating": {
|
||||
"type": "boolean",
|
||||
"description": "Does the camper have an active donation",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"validations": [],
|
||||
"relations": {
|
||||
"donations": {
|
||||
"type": "hasMany",
|
||||
"foreignKey": "",
|
||||
"modal": "donation"
|
||||
},
|
||||
"credentials": {
|
||||
"type": "hasMany",
|
||||
"model": "userCredential",
|
||||
"foreignKey": ""
|
||||
},
|
||||
"identities": {
|
||||
"type": "hasMany",
|
||||
"model": "userIdentity",
|
||||
"foreignKey": ""
|
||||
},
|
||||
"pledge": {
|
||||
"type": "hasOne",
|
||||
"model": "pledge",
|
||||
"foreignKey": ""
|
||||
},
|
||||
"authTokens": {
|
||||
"type": "hasMany",
|
||||
"model": "AuthToken",
|
||||
"foreignKey": "userId",
|
||||
"options": {
|
||||
"disableInclude": true
|
||||
}
|
||||
},
|
||||
"articles": {
|
||||
"type": "hasMany",
|
||||
"model": "article",
|
||||
"foreignKey": "externalId"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"accessType": "*",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY",
|
||||
"property": "create"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY",
|
||||
"property": "login"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY",
|
||||
"property": "verify"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY",
|
||||
"property": "resetPassword"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "doesExist"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "about"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "getPublicProfile"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "giveBrowniePoints"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$owner",
|
||||
"permission": "ALLOW",
|
||||
"property": "updateTheme"
|
||||
},
|
||||
{
|
||||
"accessType": "EXECUTE",
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "getMessages"
|
||||
}
|
||||
],
|
||||
"methods": {}
|
||||
}
|
Reference in New Issue
Block a user