Data access improvements
- fixed votes - added column constraints - added post not found error to votepull/2/head
parent
298940cc32
commit
c68f11080f
@ -1,73 +0,0 @@
|
|||||||
import * as crypto from "crypto";
|
|
||||||
import {EventEmitter} from "events";
|
|
||||||
|
|
||||||
export class MemoryCache extends EventEmitter {
|
|
||||||
private cacheItems: any = {};
|
|
||||||
private cacheExpires: any = {};
|
|
||||||
private expireCheck: NodeJS.Timeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates interval function.
|
|
||||||
* @param ttl
|
|
||||||
*/
|
|
||||||
constructor(private ttl: number = 500) {
|
|
||||||
super();
|
|
||||||
this.expireCheck = setInterval(() => this.checkExpires(), ttl / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a md5 hash of the given key.
|
|
||||||
* @param key
|
|
||||||
*/
|
|
||||||
public hashKey(key: string): string {
|
|
||||||
const hash = crypto.createHash("sha1");
|
|
||||||
const data = hash.update(key, "utf8");
|
|
||||||
return data.digest("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an entry.
|
|
||||||
* @param key
|
|
||||||
* @param value
|
|
||||||
*/
|
|
||||||
public set(key: string, value: any) {
|
|
||||||
this.cacheItems[key] = value;
|
|
||||||
this.cacheExpires[key] = Date.now() + this.ttl;
|
|
||||||
this.emit("set", key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the entry stored with the given key.
|
|
||||||
* @param key
|
|
||||||
*/
|
|
||||||
public get(key: string) {
|
|
||||||
if (this.cacheItems.hasOwnProperty(key)) {
|
|
||||||
this.emit("hit", key, this.cacheItems[key]);
|
|
||||||
return this.cacheItems[key];
|
|
||||||
} else {
|
|
||||||
this.emit("miss", key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes a cache item.
|
|
||||||
* @param key
|
|
||||||
*/
|
|
||||||
public delete(key: string) {
|
|
||||||
this.emit("delete", key);
|
|
||||||
delete this.cacheItems[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks expires and clears items that are over the expire value.
|
|
||||||
*/
|
|
||||||
private checkExpires() {
|
|
||||||
for (const [key, value] of Object.entries(this.cacheExpires)) {
|
|
||||||
if (value < Date.now()) {
|
|
||||||
this.emit("delete", key);
|
|
||||||
delete this.cacheItems[key];
|
|
||||||
delete this.cacheExpires[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,9 @@
|
|||||||
import {Sequelize} from "sequelize-typescript";
|
import {Sequelize} from "sequelize-typescript";
|
||||||
import {ChatNotFoundError} from "../errors/ChatNotFoundError";
|
import {ChatNotFoundError} from "./errors/ChatNotFoundError";
|
||||||
import {EmailAlreadyRegisteredError} from "../errors/EmailAlreadyRegisteredError";
|
import {EmailAlreadyRegisteredError} from "./errors/EmailAlreadyRegisteredError";
|
||||||
import {UserNotFoundError} from "../errors/UserNotFoundError";
|
import {UserNotFoundError} from "./errors/UserNotFoundError";
|
||||||
import globals from "../globals";
|
import globals from "./globals";
|
||||||
import {InternalEvents} from "../InternalEvents";
|
import {InternalEvents} from "./InternalEvents";
|
||||||
import * as models from "./models";
|
import * as models from "./models";
|
||||||
|
|
||||||
/**
|
/**
|
@ -1,8 +1,13 @@
|
|||||||
import {GraphQLError} from "graphql";
|
import {GraphQLError} from "graphql";
|
||||||
|
|
||||||
export class NotLoggedInGqlError extends GraphQLError {
|
export class NotLoggedInGqlError extends GraphQLError {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Not logged in");
|
super("Not logged in");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class PostNotFoundGqlError extends GraphQLError {
|
||||||
|
constructor(postId: number) {
|
||||||
|
super(`Post '${postId}' not found!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import {Column, ForeignKey, Model, Table,} from "sequelize-typescript";
|
import {Column, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
|
||||||
import {ChatRoom} from "./ChatRoom";
|
import {ChatRoom} from "./ChatRoom";
|
||||||
import {User} from "./User";
|
import {User} from "./User";
|
||||||
|
|
||||||
@Table({underscored: true})
|
@Table({underscored: true})
|
||||||
export class ChatMember extends Model<ChatMember> {
|
export class ChatMember extends Model<ChatMember> {
|
||||||
@ForeignKey(() => User)
|
@ForeignKey(() => User)
|
||||||
@Column
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
public userId: number;
|
public userId: number;
|
||||||
|
|
||||||
@ForeignKey(() => ChatRoom)
|
@ForeignKey(() => ChatRoom)
|
||||||
@Column
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
public chatId: number;
|
public chatId: number;
|
||||||
}
|
}
|
@ -1,21 +1,24 @@
|
|||||||
import * as sqz from "sequelize";
|
import * as sqz from "sequelize";
|
||||||
import {BelongsTo, Column, CreatedAt, ForeignKey, Model, Table,} from "sequelize-typescript";
|
import {BelongsTo, Column, CreatedAt, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
|
||||||
import markdown from "../../markdown";
|
import markdown from "../markdown";
|
||||||
import {ChatRoom} from "./ChatRoom";
|
import {ChatRoom} from "./ChatRoom";
|
||||||
import {User} from "./User";
|
import {User} from "./User";
|
||||||
|
|
||||||
@Table({underscored: true})
|
@Table({underscored: true})
|
||||||
export class ChatMessage extends Model<ChatMessage> {
|
export class ChatMessage extends Model<ChatMessage> {
|
||||||
|
|
||||||
@Column(sqz.STRING(512))
|
@NotNull
|
||||||
|
@Column({type: sqz.STRING(512), allowNull: false})
|
||||||
public content: string;
|
public content: string;
|
||||||
|
|
||||||
@ForeignKey(() => ChatRoom)
|
@ForeignKey(() => ChatRoom)
|
||||||
@Column
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
public chatId: number;
|
public chatId: number;
|
||||||
|
|
||||||
@ForeignKey(() => User)
|
@ForeignKey(() => User)
|
||||||
@Column
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
public authorId: number;
|
public authorId: number;
|
||||||
|
|
||||||
@BelongsTo(() => ChatRoom, "chatId")
|
@BelongsTo(() => ChatRoom, "chatId")
|
@ -1,14 +1,16 @@
|
|||||||
import {Column, ForeignKey, Model, Table} from "sequelize-typescript";
|
import {Column, ForeignKey, Model, NotNull, Table} from "sequelize-typescript";
|
||||||
import {User} from "./User";
|
import {User} from "./User";
|
||||||
|
|
||||||
@Table({underscored: true})
|
@Table({underscored: true})
|
||||||
export class Friendship extends Model<Friendship> {
|
export class Friendship extends Model<Friendship> {
|
||||||
|
|
||||||
@ForeignKey(() => User)
|
@ForeignKey(() => User)
|
||||||
@Column
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
public userId: number;
|
public userId: number;
|
||||||
|
|
||||||
@ForeignKey(() => User)
|
@ForeignKey(() => User)
|
||||||
@Column
|
@NotNull
|
||||||
|
@Column({allowNull: false})
|
||||||
public friendId: number;
|
public friendId: number;
|
||||||
}
|
}
|
@ -1,137 +0,0 @@
|
|||||||
--create functions
|
|
||||||
DO $$BEGIN
|
|
||||||
|
|
||||||
IF NOT EXISTS(SELECT 1 from pg_proc WHERE proname = 'function_exists') THEN
|
|
||||||
CREATE FUNCTION function_exists(text) RETURNS boolean LANGUAGE plpgsql AS $BODY$
|
|
||||||
BEGIN
|
|
||||||
RETURN EXISTS(SELECT 1 from pg_proc WHERE proname = $1);
|
|
||||||
END $BODY$;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT function_exists('type_exists') THEN
|
|
||||||
CREATE FUNCTION type_exists(text) RETURNS boolean LANGUAGE plpgsql AS $BODY$
|
|
||||||
BEGIN
|
|
||||||
RETURN EXISTS (SELECT 1 FROM pg_type WHERE typname = $1);
|
|
||||||
END $BODY$;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END$$;
|
|
||||||
|
|
||||||
--create types
|
|
||||||
DO $$ BEGIN
|
|
||||||
|
|
||||||
IF NOT type_exists('votetype') THEN
|
|
||||||
CREATE TYPE votetype AS enum ('DOWNVOTE', 'UPVOTE');
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT type_exists('posttype') THEN
|
|
||||||
CREATE TYPE posttype AS enum ('MISC', 'ACTION', 'IMAGE', 'TEXT');
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT type_exists('requesttype') THEN
|
|
||||||
CREATE TYPE requesttype AS enum ('FRIENDREQUEST');
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END$$;
|
|
||||||
|
|
||||||
-- create functions relying on types
|
|
||||||
|
|
||||||
DO $$ BEGIN
|
|
||||||
|
|
||||||
IF NOT function_exists('cast_to_votetype') THEN
|
|
||||||
CREATE FUNCTION cast_to_votetype(text) RETURNS votetype LANGUAGE plpgsql AS $BODY$
|
|
||||||
BEGIN
|
|
||||||
RETURN CASE WHEN $1::votetype IS NULL THEN 'UPVOTE' ELSE $1::votetype END;
|
|
||||||
END $BODY$;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF NOT function_exists('cast_to_posttype') THEN
|
|
||||||
CREATE FUNCTION cast_to_posttype(text) RETURNS posttype LANGUAGE plpgsql AS $BODY$
|
|
||||||
BEGIN
|
|
||||||
RETURN CASE WHEN $1::posttype IS NULL THEN 'MISC' ELSE $1::posttype END;
|
|
||||||
END $BODY$;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
END$$;
|
|
||||||
|
|
||||||
-- create tables
|
|
||||||
DO $$ BEGIN
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "user_sessions" (
|
|
||||||
"sid" varchar NOT NULL,
|
|
||||||
"sess" json NOT NULL,
|
|
||||||
"expire" timestamp(6) NOT NULL,
|
|
||||||
PRIMARY KEY ("sid") NOT DEFERRABLE INITIALLY IMMEDIATE
|
|
||||||
) WITH (OIDS=FALSE);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
name varchar(128) NOT NULL,
|
|
||||||
handle varchar(128) UNIQUE NOT NULL,
|
|
||||||
password varchar(1024) NOT NULL,
|
|
||||||
email varchar(128) UNIQUE NOT NULL,
|
|
||||||
greenpoints INTEGER DEFAULT 0,
|
|
||||||
joined_at TIMESTAMP DEFAULT now()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS posts (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
upvotes INTEGER DEFAULT 0,
|
|
||||||
downvotes INTEGER DEFAULT 0,
|
|
||||||
created_at TIMESTAMP DEFAULT now(),
|
|
||||||
content text,
|
|
||||||
author SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
type posttype NOT NULL DEFAULT 'MISC'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS votes (
|
|
||||||
user_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
item_id BIGSERIAL REFERENCES posts (id) ON DELETE CASCADE,
|
|
||||||
vote_type votetype DEFAULT 'DOWNVOTE',
|
|
||||||
PRIMARY KEY (user_id, item_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS events (
|
|
||||||
id BIGSERIAL PRIMARY KEY,
|
|
||||||
time TIMESTAMP,
|
|
||||||
owner SERIAL REFERENCES users (id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS event_members (
|
|
||||||
event BIGSERIAL REFERENCES events (id),
|
|
||||||
member SERIAL REFERENCES users (id),
|
|
||||||
PRIMARY KEY (event, member)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS chats (
|
|
||||||
id BIGSERIAL PRIMARY KEY
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS chat_messages (
|
|
||||||
chat BIGSERIAL REFERENCES chats (id) ON DELETE CASCADE,
|
|
||||||
author SERIAL REFERENCES users (id) ON DELETE SET NULL,
|
|
||||||
content VARCHAR(1024) NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT now(),
|
|
||||||
PRIMARY KEY (chat, author, created_at)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS chat_members (
|
|
||||||
chat BIGSERIAL REFERENCES chats (id) ON DELETE CASCADE,
|
|
||||||
member SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
PRIMARY KEY (chat, member)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS user_friends (
|
|
||||||
user_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
friend_id SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
PRIMARY KEY (user_id, friend_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS requests (
|
|
||||||
sender SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
receiver SERIAL REFERENCES users (id) ON DELETE CASCADE,
|
|
||||||
type requesttype DEFAULT 'FRIENDREQUEST',
|
|
||||||
PRIMARY KEY (sender, receiver, type)
|
|
||||||
);
|
|
||||||
|
|
||||||
END $$;
|
|
@ -1,19 +0,0 @@
|
|||||||
DO $$ BEGIN
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS votes
|
|
||||||
ADD COLUMN IF NOT EXISTS vote_type votetype DEFAULT 'UPVOTE',
|
|
||||||
ALTER COLUMN vote_type TYPE votetype USING cast_to_votetype(vote_type::text),
|
|
||||||
ALTER COLUMN vote_type DROP DEFAULT,
|
|
||||||
ALTER COLUMN vote_type SET DEFAULT 'UPVOTE';
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS posts
|
|
||||||
ALTER COLUMN type TYPE posttype USING cast_to_posttype(type::text),
|
|
||||||
ALTER COLUMN type DROP DEFAULT,
|
|
||||||
ALTER COLUMN type SET DEFAULT 'MISC',
|
|
||||||
DROP COLUMN IF EXISTS upvotes,
|
|
||||||
DROP COLUMN IF EXISTS downvotes;
|
|
||||||
|
|
||||||
ALTER TABLE requests
|
|
||||||
ADD COLUMN IF NOT EXISTS type requesttype DEFAULT 'FRIENDREQUEST';
|
|
||||||
|
|
||||||
END $$;
|
|
Loading…
Reference in New Issue