mirror of https://github.com/xfarrow/blink
person ok
This commit is contained in:
parent
9fe5879554
commit
5835bfbc16
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
This code is part of Blink
|
||||
licensed under GPLv3
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
* ⚠ WARNING ⚠
|
||||
*
|
||||
*
|
||||
* Posts are now scheduled to be developed at a later stage in the development
|
||||
* process, with the possibility that it may not be developed at all.
|
||||
* I am unsure whether it is a good thing or it'll only be used to
|
||||
* flood timelines with low-effort content, like other competing platforms.
|
||||
*
|
||||
*
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
const knex = require('../utils/knex_config');
|
||||
|
||||
/**
|
||||
* Create OrganizationPost object
|
||||
* @param {*} organizationId
|
||||
* @param {*} content
|
||||
* @param {*} originalAuthor
|
||||
*/
|
||||
function createOrganizationPost(organizationId, content, originalAuthor) {
|
||||
const organizationPost = {
|
||||
organization_id: organizationId,
|
||||
content,
|
||||
original_author: originalAuthor
|
||||
};
|
||||
return organizationPost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an OrganizationPost if and only if the author is
|
||||
* one of the Organization's administrators.
|
||||
* @param {*} organization
|
||||
* @returns the inserted OrganizationPost
|
||||
*/
|
||||
async function insertOrganizationPost(organization) {
|
||||
const isOrganizationAdmin = await knex('OrganizationAdministrator')
|
||||
.where('id_person', organization.original_author)
|
||||
.where('id_organization', organization.organization_id)
|
||||
.select('*')
|
||||
.first();
|
||||
|
||||
// Non-exploitable TOC/TOU weakness
|
||||
// For more information https://softwareengineering.stackexchange.com/questions/451038/when-should-i-be-worried-of-time-of-check-time-of-use-vulnerabilities-during-dat
|
||||
if (!isOrganizationAdmin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const organizationPost = await knex('OrganizationPost')
|
||||
.insert({
|
||||
organization_id: organization.organization_id,
|
||||
content: organization.content,
|
||||
original_author: organization.original_author
|
||||
})
|
||||
.returning('*');
|
||||
|
||||
return organizationPost[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the specified Person is an Organization Administrator
|
||||
* of the Organization the Post belongs to.
|
||||
* @param {*} postId
|
||||
* @param {*} personId
|
||||
* @returns true or false
|
||||
*/
|
||||
async function isPersonPostAdministrator(postId, personId) {
|
||||
return await knex('OrganizationPost')
|
||||
.join('OrganizationAdministrator', 'OrganizationPost.organization_id', 'OrganizationAdministrator.id_organization')
|
||||
.where('OrganizationPost.id', postId)
|
||||
.where('OrganizationAdministrator.id_person', personId)
|
||||
.select('*')
|
||||
.first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified OrganizationPost if the requester is one
|
||||
* of the Administrators of the Organization the Post belongs to
|
||||
* @param {*} postId Id of the Post to delete
|
||||
* @param {*} requester Id of the Person requesting the deletion
|
||||
*/
|
||||
async function deleteOrganizationPost(postId, requester) {
|
||||
if (await isPersonPostAdministrator(postId, requester)) {
|
||||
return await knex('OrganizationPost')
|
||||
.where('id', postId)
|
||||
.del() == 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createOrganizationPost,
|
||||
insertOrganizationPost,
|
||||
isPersonPostAdministrator,
|
||||
deleteOrganizationPost
|
||||
};
|
|
@ -25,17 +25,18 @@ const bcrypt = require('bcrypt');
|
|||
* @param {*} placeOfLiving
|
||||
* @returns
|
||||
*/
|
||||
function createPerson(email, password, displayName, dateOfBirth, available, enabled, placeOfLiving, aboutMe, qualification) {
|
||||
function createPerson(email, password, displayName, dateOfBirth, placeOfLiving, aboutMe, qualification, openToWork, enabled) {
|
||||
const person = {
|
||||
email: email.toLowerCase(),
|
||||
password,
|
||||
display_name: displayName,
|
||||
date_of_birth: dateOfBirth,
|
||||
available,
|
||||
enabled,
|
||||
open_to_work: openToWork,
|
||||
place_of_living: placeOfLiving,
|
||||
about_me: aboutMe,
|
||||
qualification
|
||||
qualification,
|
||||
enabled,
|
||||
visibility: "EVERYONE"
|
||||
};
|
||||
return person;
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ async function insert(person, activationLink) {
|
|||
const insertedPerson = await tr('Person')
|
||||
.insert(person)
|
||||
.returning('*');
|
||||
if(activationLink != null){
|
||||
if (activationLink != null) {
|
||||
await tr('ActivationLink')
|
||||
.insert({
|
||||
person_id: insertedPerson[0].id,
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
This code is part of Blink
|
||||
licensed under GPLv3
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
* ⚠ WARNING ⚠
|
||||
*
|
||||
*
|
||||
* Posts are now scheduled to be developed at a later stage in the development
|
||||
* process, with the possibility that it may not be developed at all.
|
||||
* I am unsure whether it is a good thing or it'll only be used to
|
||||
* flood timelines with low-effort content, like other competing platforms.
|
||||
*
|
||||
*
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
const organizationPostModel = require('../models/organization_post_model');
|
||||
const express = require('express');
|
||||
const jwtUtils = require('../utils/jwt_utils');
|
||||
|
||||
/**
|
||||
* POST Request
|
||||
*
|
||||
* Creates a Post belonging to an organization
|
||||
*
|
||||
* Required field(s): organization_id, content
|
||||
* @returns the inserted Post
|
||||
*/
|
||||
async function createOrganizationPost(req, res) {
|
||||
// Ensure that the required fields are present before proceeding
|
||||
if (!req.params.idOrganization || !req.body.content) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid request'
|
||||
});
|
||||
}
|
||||
|
||||
const organizationPost = organizationPostModel.createOrganizationPost(
|
||||
req.params.idOrganization,
|
||||
req.body.content,
|
||||
req.jwt.person_id);
|
||||
|
||||
try {
|
||||
const insertedOrganization = await organizationPostModel.insertOrganizationPost(organizationPost);
|
||||
if(!!insertedOrganization){
|
||||
return res.status(200).json(insertedOrganization);
|
||||
}
|
||||
return res.status(401).json({
|
||||
error: 'Forbidden'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error in function ${createOrganizationPost.name}: ${error}`);
|
||||
return res.status(500).json({
|
||||
error: 'Internal server error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE Request
|
||||
*
|
||||
* Deletes a Post belonging to an Organization, only if
|
||||
* the logged user is an administrator of that Organization.
|
||||
*
|
||||
* Required field(s): none.
|
||||
*/
|
||||
async function deleteOrganizationPost(req, res) {
|
||||
try {
|
||||
const success = await organizationPostModel.deleteOrganizationPost(req.params.id, req.jwt.person_id);
|
||||
|
||||
if (success) {
|
||||
return res.status(200).json({
|
||||
success: true
|
||||
});
|
||||
}
|
||||
return res.status(401).json({
|
||||
error: 'Forbidden'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error in function ${deleteOrganizationPost.name}: ${error}`);
|
||||
res.status(500).json({
|
||||
error: 'Internal server error'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const routes = express.Router();
|
||||
routes.post('/:idOrganization/posts', jwtUtils.extractToken, createOrganizationPost);
|
||||
routes.delete('/posts/:id', jwtUtils.extractToken, deleteOrganizationPost);
|
||||
|
||||
// Exporting a function
|
||||
// means making a JavaScript function defined in one
|
||||
// module available for use in another module.
|
||||
module.exports = {
|
||||
routes
|
||||
};
|
|
@ -16,7 +16,7 @@ const jwtUtils = require('../utils/jwt_utils');
|
|||
const bcrypt = require('bcrypt');
|
||||
const crypto = require('crypto');
|
||||
const Person = require('../models/person_model');
|
||||
const activationModel = require('../models/activation_model');
|
||||
const Activation = require('../models/activation_model');
|
||||
const express = require('express');
|
||||
const mailUtils = require('../utils/mail_utils');
|
||||
|
||||
|
@ -66,13 +66,14 @@ async function registerPerson(req, res) {
|
|||
const personToInsert = Person.createPerson(
|
||||
req.body.email,
|
||||
await hashPasswordPromise,
|
||||
req.body.display_name,
|
||||
req.body.date_of_birth,
|
||||
req.body.available,
|
||||
req.body.displayName,
|
||||
req.body.dateOfBirth,
|
||||
req.body.placeOfLiving,
|
||||
req.body.aboutMe,
|
||||
req.body.qualification,
|
||||
req.body.openToWork,
|
||||
isEnabled,
|
||||
req.body.place_of_living,
|
||||
req.body.about_me,
|
||||
req.body.qualification);
|
||||
);
|
||||
const insertedPerson = await Person.insert(personToInsert, activationCode);
|
||||
delete insertedPerson.password;
|
||||
|
||||
|
@ -190,8 +191,8 @@ async function getMyself(req, res) {
|
|||
* not present, they shall be ignored. An user can
|
||||
* only update themselves
|
||||
*
|
||||
* Required field(s): At least one. Both old_password and
|
||||
* new_password if updating the password.
|
||||
* Required field(s): At least one. Both oldPassword and
|
||||
* newPassword if updating the password.
|
||||
*
|
||||
*/
|
||||
async function updatePerson(req, res) {
|
||||
|
@ -205,46 +206,50 @@ async function updatePerson(req, res) {
|
|||
|
||||
const updatePerson = {};
|
||||
|
||||
if (req.body.display_name !== undefined) {
|
||||
updatePerson.display_name = req.body.display_name;
|
||||
if (req.body.displayName !== undefined) {
|
||||
updatePerson.display_name = req.body.displayName;
|
||||
}
|
||||
|
||||
if (req.body.date_of_birth !== undefined) {
|
||||
updatePerson.date_of_birth = req.body.date_of_birth;
|
||||
if (req.body.dateOfBirth !== undefined) {
|
||||
updatePerson.date_of_birth = req.body.dateOfBirth;
|
||||
}
|
||||
|
||||
if (req.body.available !== undefined) {
|
||||
updatePerson.available = req.body.available;
|
||||
if (req.body.openToWork !== undefined) {
|
||||
updatePerson.open_to_work = req.body.openToWork;
|
||||
}
|
||||
|
||||
if (req.body.place_of_living !== undefined) {
|
||||
updatePerson.place_of_living = req.body.place_of_living;
|
||||
if (req.body.placeOfLiving !== undefined) {
|
||||
updatePerson.place_of_living = req.body.placeOfLiving;
|
||||
}
|
||||
|
||||
if (req.body.about_me !== undefined) {
|
||||
updatePerson.about_me = req.body.about_me;
|
||||
if (req.body.aboutMe !== undefined) {
|
||||
updatePerson.about_me = req.body.aboutMe;
|
||||
}
|
||||
|
||||
if (req.body.qualification !== undefined) {
|
||||
updatePerson.qualification = req.body.qualification;
|
||||
}
|
||||
|
||||
if (req.body.visibility !== undefined) {
|
||||
updatePerson.visibility = req.body.visibility;
|
||||
}
|
||||
|
||||
// If we are tying to change password, the old password must be provided
|
||||
if (req.body.old_password !== undefined || req.body.new_password !== undefined) {
|
||||
if (req.body.old_password === undefined) {
|
||||
if (req.body.oldPassword !== undefined || req.body.newPassword !== undefined) {
|
||||
if (req.body.oldPassword === undefined) {
|
||||
return res.status(401).json({
|
||||
error: 'The old password must be specified'
|
||||
});
|
||||
}
|
||||
if (req.body.new_password === undefined) {
|
||||
if (req.body.newPassword === undefined) {
|
||||
return res.status(401).json({
|
||||
error: 'The new password must be specified'
|
||||
});
|
||||
}
|
||||
const user = await Person.findById(req.jwt.person_id);
|
||||
const passwordMatches = await bcrypt.compare(req.body.old_password, user.password);
|
||||
const passwordMatches = await bcrypt.compare(req.body.oldPassword, user.password);
|
||||
if (passwordMatches) {
|
||||
updatePerson.password = await bcrypt.hash(req.body.new_password, 10);
|
||||
updatePerson.password = await bcrypt.hash(req.body.newPassword, 10);
|
||||
} else {
|
||||
return res.status(401).json({
|
||||
error: 'Password verification failed'
|
||||
|
@ -306,7 +311,7 @@ async function confirmActivation(req, res) {
|
|||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
const personId = await activationModel.getPersonIdByIdentifier(req.body.code);
|
||||
const personId = await Activation.getPersonIdByIdentifier(req.body.code);
|
||||
if (!personId) {
|
||||
return res.status(401).json({
|
||||
error: 'Activation Link either not valid or expired'
|
||||
|
|
|
@ -17,7 +17,7 @@ const {
|
|||
} = require("express-validator"); // This is the destructuring part. It specifies which properties of the imported object (express-validator) you want to extract.
|
||||
|
||||
const registerValidator = [
|
||||
check('display_name').trim().notEmpty().escape().isLength({
|
||||
check('displayName').trim().notEmpty().escape().isLength({
|
||||
max: 128
|
||||
}),
|
||||
check('email').isEmail().normalizeEmail().escape().isLength({
|
||||
|
@ -26,12 +26,12 @@ const registerValidator = [
|
|||
check('password').isLength({
|
||||
min: 5
|
||||
}).trim().escape().withMessage('Password must be at least 5 characters'),
|
||||
check('date_of_birth').optional().isDate().withMessage('Invalid date format. Date must be YYYY-MM-DD'),
|
||||
check('available').optional().isBoolean(),
|
||||
check('place_of_living').isLength({
|
||||
check('dateOfBirth').optional().isDate().withMessage('Invalid date format. Date must be YYYY-MM-DD'),
|
||||
check('openToWork').optional().isBoolean(),
|
||||
check('placeOfLiving').isLength({
|
||||
max: 128
|
||||
}).escape(),
|
||||
check('about_me').isLength({
|
||||
check('aboutMe').isLength({
|
||||
max: 4096
|
||||
}).escape(),
|
||||
check('qualification').isLength({
|
||||
|
@ -45,15 +45,15 @@ const getTokenValidator = [
|
|||
];
|
||||
|
||||
const updatePersonValidator = [
|
||||
check('display_name').trim().escape().isLength({
|
||||
check('displayName').trim().escape().isLength({
|
||||
max: 128
|
||||
}),
|
||||
check('date_of_birth').optional().isDate().withMessage('Invalid date format. Date must be YYYY-MM-DD'),
|
||||
check('available').optional().isBoolean(),
|
||||
check('place_of_living').isLength({
|
||||
check('dateOfBirth').optional().isDate().withMessage('Invalid date format. Date must be YYYY-MM-DD'),
|
||||
check('openToWork').optional().isBoolean(),
|
||||
check('placeOfLiving').isLength({
|
||||
max: 128
|
||||
}).escape(),
|
||||
check('about_me').isLength({
|
||||
check('aboutMe').isLength({
|
||||
max: 4096
|
||||
}).escape(),
|
||||
check('qualification').isLength({
|
||||
|
|
|
@ -2,12 +2,4 @@
|
|||
|
||||
-- DROP DATABASE IF EXISTS "Blink";
|
||||
|
||||
CREATE DATABASE "Blink"
|
||||
WITH
|
||||
OWNER = pg_database_owner
|
||||
ENCODING = 'UTF8'
|
||||
LC_COLLATE = 'Italian_Italy.1252'
|
||||
LC_CTYPE = 'Italian_Italy.1252'
|
||||
TABLESPACE = pg_default
|
||||
CONNECTION LIMIT = -1
|
||||
IS_TEMPLATE = False;
|
||||
CREATE DATABASE "Blink";
|
|
@ -1,140 +1,115 @@
|
|||
CREATE TYPE Visibility AS ENUM ('NOBODY', 'THIS_INSTANCE', 'EVERYONE');
|
||||
CREATE TYPE SalaryCurrency as ENUM ('EUR', 'USD', 'GBP');
|
||||
CREATE TYPE RemotePosition as ENUM ('YES', 'NO', 'NOT_SPECIFIED', 'PARTIALLY');
|
||||
CREATE TYPE ContractType as ENUM ('FULL-TIME','PART-TIME','INTERNSHIP','CONTRACT','FREELANCE','TEMPORARY','SEASONAL','APPRENTICESHIP','VOLUNTEER','ZERO-HOURS','FIXED-TERM','CASUAL','PROBATIONARY','SECONDMENT','JOB-SHARING');
|
||||
CREATE TYPE ExperienceType as ENUM ('EDUCATION', 'WORK', 'VOLOUNTEER');
|
||||
|
||||
-- Table: Person
|
||||
CREATE TABLE IF NOT EXISTS "Person" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
email character varying(128) NOT NULL UNIQUE,
|
||||
-- Primary e-mail
|
||||
password character varying(128) NOT NULL,
|
||||
display_name character varying(128) NOT NULL,
|
||||
email CHARACTER VARYING(128) NOT NULL UNIQUE,
|
||||
password CHARACTER VARYING(128) NOT NULL,
|
||||
display_name CHARACTER VARYING(128) NOT NULL,
|
||||
date_of_birth date,
|
||||
available boolean,
|
||||
-- Whether this person is available to be hired
|
||||
enabled boolean NOT NULL DEFAULT false,
|
||||
-- Whether this profile is active
|
||||
place_of_living character varying(128),
|
||||
about_me character varying(4096),
|
||||
qualification character varying(64)
|
||||
place_of_living CHARACTER VARYING(128),
|
||||
about_me CHARACTER VARYING(4096),
|
||||
qualification CHARACTER VARYING(64),
|
||||
open_to_work BOOLEAN,
|
||||
enabled BOOLEAN NOT NULL DEFAULT false,
|
||||
visibility Visibility NOT NULL
|
||||
);
|
||||
|
||||
-- Table: ActivationLink
|
||||
CREATE TABLE IF NOT EXISTS "ActivationLink" (
|
||||
identifier character varying PRIMARY KEY,
|
||||
person_id integer NOT NULL,
|
||||
identifier CHARACTER VARYING PRIMARY KEY,
|
||||
person_id INTEGER NOT NULL,
|
||||
CONSTRAINT "PersonActivationLinkFK" FOREIGN KEY (person_id) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Table: Organization
|
||||
CREATE TABLE IF NOT EXISTS "Organization" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name character varying(128) NOT NULL,
|
||||
location character varying,
|
||||
description text
|
||||
);
|
||||
|
||||
-- Table: OrganizationPost
|
||||
CREATE TABLE IF NOT EXISTS "OrganizationPost" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
organization_id integer NOT NULL,
|
||||
content text NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now(),
|
||||
original_author integer NOT NULL,
|
||||
CONSTRAINT "AuthorIdFK" FOREIGN KEY (original_author) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE NOT VALID,
|
||||
CONSTRAINT "OrganizationIdFk" FOREIGN KEY (organization_id) REFERENCES "Organization" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE NOT VALID
|
||||
name CHARACTER VARYING(128) NOT NULL,
|
||||
location CHARACTER VARYING,
|
||||
description TEXT
|
||||
);
|
||||
|
||||
-- Table: OrganizationAdministrator
|
||||
CREATE TABLE IF NOT EXISTS "OrganizationAdministrator" (
|
||||
id_person integer NOT NULL,
|
||||
id_organization integer NOT NULL,
|
||||
id_person INTEGER NOT NULL,
|
||||
id_organization INTEGER NOT NULL,
|
||||
CONSTRAINT "OrganizationAdministrator_pkey" PRIMARY KEY (id_organization, id_person),
|
||||
CONSTRAINT "OrganizationAdministratorOrganizationId" FOREIGN KEY (id_organization) REFERENCES "Organization" (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE NOT VALID,
|
||||
CONSTRAINT "OrganizationAdministratorUserId" FOREIGN KEY (id_person) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Table: Message
|
||||
CREATE TABLE IF NOT EXISTS "Message" (
|
||||
id serial NOT NULL,
|
||||
person_id integer NOT NULL,
|
||||
organization_id integer NOT NULL,
|
||||
author_on_behalf_of_organization integer,
|
||||
"timestamp" timestamp without time zone NOT NULL,
|
||||
content character varying(4096) NOT NULL,
|
||||
sender_type character varying(12) NOT NULL,
|
||||
CONSTRAINT "Message_pkey" PRIMARY KEY (id),
|
||||
CONSTRAINT "Message_author_on_behalf_of_company_fkey" FOREIGN KEY (
|
||||
author_on_behalf_of_organization
|
||||
) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE,
|
||||
CONSTRAINT "Message_organization_id_fkey" FOREIGN KEY (organization_id) REFERENCES "Organization" (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE,
|
||||
CONSTRAINT "Message_person_id_fkey" FOREIGN KEY (person_id) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE,
|
||||
CONSTRAINT "Message_sender_type_check" CHECK (
|
||||
sender_type :: text = 'ORGANIZATION' :: text
|
||||
OR sender_type :: text = 'PERSON' :: text
|
||||
),
|
||||
CONSTRAINT "Message_sender_constraint" CHECK (
|
||||
author_on_behalf_of_organization IS NULL
|
||||
AND sender_type :: text = 'PERSON' :: text
|
||||
OR author_on_behalf_of_organization IS NOT NULL
|
||||
AND sender_type :: text = 'ORGANIZATION' :: text
|
||||
)
|
||||
);
|
||||
COMMENT ON COLUMN "Message".sender_type IS 'sender_type can be either be PERSON or ORGANIZATION, depending who''s sending the message. It is PERSON if and only if author_on_behalf_of_organization is NULL. It is ORGANIZATION if and only if author_on_behalf_of_organization is NOT NULL. This column may seem redundant if we already know how to identify a sender, but it does give more clarity to the API implementers';
|
||||
COMMENT ON CONSTRAINT "Message_sender_type_check" ON "Message" IS 'We want the sender to be either PERSON or ORGANIZATION';
|
||||
COMMENT ON CONSTRAINT "Message_sender_constraint" ON "Message" IS 'If ''author_on_behalf_of_organization'' is NULL, then the sender is a person, instead, if ''author_on_behalf_of_organization'' is not NULL, the sender is a organization';
|
||||
|
||||
-- Table: Tag
|
||||
CREATE TABLE IF NOT EXISTS "Tag" (
|
||||
id SERIAL,
|
||||
tag character varying(256) NOT NULL,
|
||||
CONSTRAINT "Tag_pkey" PRIMARY KEY (id)
|
||||
id SERIAL PRIMARY KEY,
|
||||
tag CHARACTER VARYING(256) NOT NULL
|
||||
);
|
||||
|
||||
-- Table: JobOffer
|
||||
CREATE TABLE IF NOT EXISTS "JobOffer" (
|
||||
id SERIAL,
|
||||
title character varying(2048) NOT NULL,
|
||||
description character varying(4096),
|
||||
requirements character varying(4096),
|
||||
salary money NOT NULL,
|
||||
salary_frequency character varying(64) NOT NULL,
|
||||
salary_currency character varying(64) NOT NULL,
|
||||
location character varying(256),
|
||||
organization_id integer,
|
||||
CONSTRAINT "JobOffer_pkey" PRIMARY KEY (id),
|
||||
id SERIAL PRIMARY KEY,
|
||||
organization_id INTEGER,
|
||||
title CHARACTER VARYING(2048) NOT NULL,
|
||||
description CHARACTER VARYING(4096),
|
||||
salary int4range,
|
||||
salary_currency SalaryCurrency,
|
||||
location CHARACTER VARYING(256),
|
||||
remote RemotePosition NOT NULL,
|
||||
contract_type ContractType,
|
||||
CONSTRAINT "OrganizationFK" FOREIGN KEY (organization_id) REFERENCES "Organization" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE NOT VALID
|
||||
);
|
||||
|
||||
-- Table: JobOfferTag
|
||||
-- This table allows to create a N-to-N map between Tag and JobOffer
|
||||
CREATE TABLE IF NOT EXISTS "JobOfferTag" (
|
||||
id serial,
|
||||
job_offer_id integer NOT NULL,
|
||||
tag_id integer NOT NULL,
|
||||
CONSTRAINT "JobOfferTag_pkey" PRIMARY KEY (id),
|
||||
id SERIAL PRIMARY KEY,
|
||||
job_offer_id INTEGER NOT NULL,
|
||||
tag_id INTEGER NOT NULL,
|
||||
CONSTRAINT "JobOfferFk" FOREIGN KEY (job_offer_id) REFERENCES "JobOffer" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
CONSTRAINT "TagFk" FOREIGN KEY (tag_id) REFERENCES "Tag" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Table: Applicant
|
||||
CREATE TABLE IF NOT EXISTS "Applicant" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
person_id INTEGER NOT NULL,
|
||||
job_offer_id INTEGER NOT NULL,
|
||||
CONSTRAINT "PersonFk" FOREIGN KEY (person_id) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
CONSTRAINT "JobOfferFk" FOREIGN KEY (job_offer_id) REFERENCES "JobOffer" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Table: Skills
|
||||
CREATE TABLE IF NOT EXISTS "Skill" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
person_id INTEGER NOT NULL,
|
||||
tag_id INTEGER NOT NULL,
|
||||
CONSTRAINT "PersonFk" FOREIGN KEY (person_id) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
CONSTRAINT "TagFk" FOREIGN KEY (tag_id) REFERENCES "Tag" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Table: RequestResetPassword
|
||||
CREATE TABLE IF NOT EXISTS "RequestResetPassword" (
|
||||
id serial,
|
||||
email character varying(128) NOT NULL,
|
||||
secret character varying NOT NULL,
|
||||
id SERIAL,
|
||||
email CHARACTER VARYING(128) NOT NULL,
|
||||
secret CHARACTER VARYING NOT NULL,
|
||||
time_of_request timestamp without time zone NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "RequestResetPassword_pkey" PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
-- Table: Experience
|
||||
CREATE TABLE IF NOT EXISTS "Experience" (
|
||||
id serial,
|
||||
title character varying(128) NOT NULL,
|
||||
description text NOT NULL,
|
||||
organization character varying(128) NOT NULL,
|
||||
id SERIAL PRIMARY KEY,
|
||||
title CHARACTER VARYING(128) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
organization CHARACTER VARYING(128) NOT NULL,
|
||||
organization_id INTEGER,
|
||||
date daterange NOT NULL,
|
||||
organization_id integer,
|
||||
person_id integer NOT NULL,
|
||||
type character varying(32) NOT NULL,
|
||||
CONSTRAINT "Experience_pkey" PRIMARY KEY (id),
|
||||
CONSTRAINT "OrganizationFk" FOREIGN KEY (organization_id) REFERENCES "Organization" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE
|
||||
SET
|
||||
NULL,
|
||||
person_id INTEGER NOT NULL,
|
||||
type ExperienceType NOT NULL,
|
||||
CONSTRAINT "OrganizationFk" FOREIGN KEY (organization_id) REFERENCES "Organization" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
CONSTRAINT "PersonFk" FOREIGN KEY (person_id) REFERENCES "Person" (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue