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 {ChatNotFoundError} from "../errors/ChatNotFoundError";
|
||||
import {EmailAlreadyRegisteredError} from "../errors/EmailAlreadyRegisteredError";
|
||||
import {UserNotFoundError} from "../errors/UserNotFoundError";
|
||||
import globals from "../globals";
|
||||
import {InternalEvents} from "../InternalEvents";
|
||||
import {ChatNotFoundError} from "./errors/ChatNotFoundError";
|
||||
import {EmailAlreadyRegisteredError} from "./errors/EmailAlreadyRegisteredError";
|
||||
import {UserNotFoundError} from "./errors/UserNotFoundError";
|
||||
import globals from "./globals";
|
||||
import {InternalEvents} from "./InternalEvents";
|
||||
import * as models from "./models";
|
||||
|
||||
/**
|
@ -1,8 +1,13 @@
|
||||
import {GraphQLError} from "graphql";
|
||||
|
||||
export class NotLoggedInGqlError extends GraphQLError {
|
||||
|
||||
constructor() {
|
||||
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 {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class ChatMember extends Model<ChatMember> {
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@ForeignKey(() => ChatRoom)
|
||||
@Column
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public chatId: number;
|
||||
}
|
@ -1,21 +1,24 @@
|
||||
import * as sqz from "sequelize";
|
||||
import {BelongsTo, Column, CreatedAt, ForeignKey, Model, Table,} from "sequelize-typescript";
|
||||
import markdown from "../../markdown";
|
||||
import {BelongsTo, Column, CreatedAt, ForeignKey, Model, NotNull, Table,} from "sequelize-typescript";
|
||||
import markdown from "../markdown";
|
||||
import {ChatRoom} from "./ChatRoom";
|
||||
import {User} from "./User";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class ChatMessage extends Model<ChatMessage> {
|
||||
|
||||
@Column(sqz.STRING(512))
|
||||
@NotNull
|
||||
@Column({type: sqz.STRING(512), allowNull: false})
|
||||
public content: string;
|
||||
|
||||
@ForeignKey(() => ChatRoom)
|
||||
@Column
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public chatId: number;
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public authorId: number;
|
||||
|
||||
@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";
|
||||
|
||||
@Table({underscored: true})
|
||||
export class Friendship extends Model<Friendship> {
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
public userId: number;
|
||||
|
||||
@ForeignKey(() => User)
|
||||
@Column
|
||||
@NotNull
|
||||
@Column({allowNull: false})
|
||||
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