[MAJOR][FIRSTCOMMIT] Added basic routes, controllers, repos to kanban service (no postgres yet)

This commit is contained in:
2025-09-27 14:09:35 +05:30
parent f283f6043f
commit fd7ceca2ef
109 changed files with 3554 additions and 444 deletions

3
.gitignore vendored
View File

@@ -133,4 +133,5 @@ dist/
.yarn/install-state.gz
.pnp.*
.DS_Store
.DS_Store
/generated/prisma

View File

@@ -0,0 +1,142 @@
{
"info": {
"name": "Aragon Kanban API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"description": "Postman collection for Users, Boards, and Tasks API"
},
"item": [
{
"name": "Users",
"item": [
{
"name": "Get All Users",
"request": {
"method": "GET",
"header": [{"key": "Authorization", "value": "Bearer testtoken"}],
"url": { "raw": "{{baseUrl}}/users", "host": ["{{baseUrl}}"], "path": ["users"] }
}
},
{
"name": "Add User",
"request": {
"method": "POST",
"header": [{"key": "Content-Type", "value": "application/json"}, {"key": "Authorization", "value": "Bearer testtoken"}],
"body": { "mode": "raw", "raw": "{\n \"user\": {\n \"id\": 1,\n \"name\": \"John Doe\",\n \"email\": \"john@example.com\"\n }\n}" },
"url": { "raw": "{{baseUrl}}/users", "host": ["{{baseUrl}}"], "path": ["users"] }
}
},
{
"name": "Update User",
"request": {
"method": "PUT",
"header": [{"key": "Content-Type", "value": "application/json"}, {"key": "Authorization", "value": "Bearer testtoken"}],
"body": { "mode": "raw", "raw": "{\n \"user\": {\n \"id\": 1,\n \"name\": \"Jane Doe\",\n \"email\": \"jane@example.com\"\n }\n}" },
"url": { "raw": "{{baseUrl}}/users", "host": ["{{baseUrl}}"], "path": ["users"] }
}
},
{
"name": "Delete User",
"request": {
"method": "DELETE",
"header": [{"key": "Authorization", "value": "Bearer testtoken"}],
"url": { "raw": "{{baseUrl}}/users/1", "host": ["{{baseUrl}}"], "path": ["users", "1"] }
}
}
]
},
{
"name": "Boards",
"item": [
{
"name": "Get All Boards for User",
"request": {
"method": "GET",
"header": [{"key": "Authorization", "value": "Bearer testtoken"}],
"url": { "raw": "{{baseUrl}}/boards/user/1", "host": ["{{baseUrl}}"], "path": ["boards", "user", "1"] }
}
},
{
"name": "Get Board by ID",
"request": {
"method": "GET",
"header": [{"key": "Authorization", "value": "Bearer testtoken"}],
"url": { "raw": "{{baseUrl}}/boards/user/1/123", "host": ["{{baseUrl}}"], "path": ["boards", "user", "1", "123"] }
}
},
{
"name": "Add Board",
"request": {
"method": "POST",
"header": [{"key": "Content-Type", "value": "application/json"}, {"key": "Authorization", "value": "Bearer testtoken"}],
"body": { "mode": "raw", "raw": "{\n \"name\": \"Project Board\"\n}" },
"url": { "raw": "{{baseUrl}}/boards/user/1", "host": ["{{baseUrl}}"], "path": ["boards", "user", "1"] }
}
},
{
"name": "Update Board",
"request": {
"method": "PUT",
"header": [{"key": "Content-Type", "value": "application/json"}, {"key": "Authorization", "value": "Bearer testtoken"}],
"body": { "mode": "raw", "raw": "{\n \"name\": \"Updated Board Name\"\n}" },
"url": { "raw": "{{baseUrl}}/boards/user/1/123", "host": ["{{baseUrl}}"], "path": ["boards", "user", "1", "123"] }
}
},
{
"name": "Delete Board",
"request": {
"method": "DELETE",
"header": [{"key": "Authorization", "value": "Bearer testtoken"}],
"url": { "raw": "{{baseUrl}}/boards/user/1/123", "host": ["{{baseUrl}}"], "path": ["boards", "user", "1", "123"] }
}
}
]
},
{
"name": "Tasks",
"item": [
{
"name": "Get All Tasks for Board",
"request": {
"method": "GET",
"header": [{"key": "Authorization", "value": "Bearer testtoken"}],
"url": { "raw": "{{baseUrl}}/tasks/board/123", "host": ["{{baseUrl}}"], "path": ["tasks", "board", "123"] }
}
},
{
"name": "Get Task by ID",
"request": {
"method": "GET",
"header": [{"key": "Authorization", "value": "Bearer testtoken"}],
"url": { "raw": "{{baseUrl}}/tasks/board/123/456", "host": ["{{baseUrl}}"], "path": ["tasks", "board", "123", "456"] }
}
},
{
"name": "Add Task",
"request": {
"method": "POST",
"header": [{"key": "Content-Type", "value": "application/json"}, {"key": "Authorization", "value": "Bearer testtoken"}],
"body": { "mode": "raw", "raw": "{\n \"title\": \"New Task\",\n \"description\": \"Task details\",\n \"status\": \"todo\"\n}" },
"url": { "raw": "{{baseUrl}}/tasks/board/123", "host": ["{{baseUrl}}"], "path": ["tasks", "board", "123"] }
}
},
{
"name": "Update Task",
"request": {
"method": "PUT",
"header": [{"key": "Content-Type", "value": "application/json"}, {"key": "Authorization", "value": "Bearer testtoken"}],
"body": { "mode": "raw", "raw": "{\n \"title\": \"Updated Task\",\n \"description\": \"Updated details\",\n \"status\": \"done\"\n}" },
"url": { "raw": "{{baseUrl}}/tasks/board/123/456", "host": ["{{baseUrl}}"], "path": ["tasks", "board", "123", "456"] }
}
},
{
"name": "Delete Task",
"request": {
"method": "DELETE",
"header": [{"key": "Authorization", "value": "Bearer testtoken"}],
"url": { "raw": "{{baseUrl}}/tasks/board/123/456", "host": ["{{baseUrl}}"], "path": ["tasks", "board", "123", "456"] }
}
}
]
}
]
}

29
lib/constants/EnvVars.js Normal file
View File

@@ -0,0 +1,29 @@
"use strict";
/**
* Environments variables declared here.
*/
var _a, _b, _c, _d, _e, _f, _g;
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable node/no-process-env */
exports.default = {
NodeEnv: ((_a = process.env.NODE_ENV) !== null && _a !== void 0 ? _a : ''),
Port: ((_b = process.env.PORT) !== null && _b !== void 0 ? _b : 0),
CookieProps: {
Key: 'ExpressGeneratorTs',
Secret: ((_c = process.env.COOKIE_SECRET) !== null && _c !== void 0 ? _c : ''),
// Casing to match express cookie options
Options: {
httpOnly: true,
signed: true,
path: ((_d = process.env.COOKIE_PATH) !== null && _d !== void 0 ? _d : ''),
maxAge: Number((_e = process.env.COOKIE_EXP) !== null && _e !== void 0 ? _e : 0),
domain: ((_f = process.env.COOKIE_DOMAIN) !== null && _f !== void 0 ? _f : ''),
secure: (process.env.SECURE_COOKIE === 'true'),
},
},
Jwt: {
Secret: ((_g = process.env.JWT_SECRET) !== null && _g !== void 0 ? _g : ''),
Exp: (process.env.COOKIE_EXP && process.env.COOKIE_EXP !== '' ? process.env.COOKIE_EXP : '1h'), // exp at the same time as the cookie
},
};
//# sourceMappingURL=EnvVars.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"EnvVars.js","sourceRoot":"","sources":["../../src/constants/EnvVars.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,wCAAwC;AAGxC,kBAAe;IACb,OAAO,EAAE,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,mCAAI,EAAE,CAAC;IACrC,IAAI,EAAE,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,IAAI,mCAAI,CAAC,CAAC;IAC7B,WAAW,EAAE;QACX,GAAG,EAAE,oBAAoB;QACzB,MAAM,EAAE,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,aAAa,mCAAI,EAAE,CAAC;QACzC,yCAAyC;QACzC,OAAO,EAAE;YACP,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,IAAI;YACZ,IAAI,EAAE,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,WAAW,mCAAI,EAAE,CAAC;YACrC,MAAM,EAAE,MAAM,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,UAAU,mCAAI,CAAC,CAAC;YAC3C,MAAM,EAAE,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,aAAa,mCAAI,EAAE,CAAC;YACzC,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,CAAC;SAC/C;KACF;IACD,GAAG,EAAE;QACH,MAAM,EAAE,CAAC,MAAA,OAAO,CAAC,GAAG,CAAC,UAAU,mCAAK,EAAE,CAAC;QACvC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,qCAAqC;KACtI;CACO,CAAC"}

View File

@@ -0,0 +1,327 @@
"use strict";
/* eslint-disable max-len */
/**
* This file was copied from here: https://gist.github.com/scokmen/f813c904ef79022e84ab2409574d1b45
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Hypertext Transfer Protocol (HTTP) response status codes.
* @see {@link https://en.wikipedia.org/wiki/List_of_HTTP_status_codes}
*/
var HttpStatusCodes;
(function (HttpStatusCodes) {
/**
* The server has received the request headers and the client should proceed to send the request body
* (in the case of a request for which a body needs to be sent; for example, a POST request).
* Sending a large request body to a server after a request has been rejected for inappropriate headers would be inefficient.
* To have a server check the request's headers, a client must send Expect: 100-continue as a header in its initial request
* and receive a 100 Continue status code in response before sending the body. The response 417 Expectation Failed indicates the request should not be continued.
*/
HttpStatusCodes[HttpStatusCodes["CONTINUE"] = 100] = "CONTINUE";
/**
* The requester has asked the server to switch protocols and the server has agreed to do so.
*/
HttpStatusCodes[HttpStatusCodes["SWITCHING_PROTOCOLS"] = 101] = "SWITCHING_PROTOCOLS";
/**
* A WebDAV request may contain many sub-requests involving file operations, requiring a long time to complete the request.
* This code indicates that the server has received and is processing the request, but no response is available yet.
* This prevents the client from timing out and assuming the request was lost.
*/
HttpStatusCodes[HttpStatusCodes["PROCESSING"] = 102] = "PROCESSING";
/**
* Standard response for successful HTTP requests.
* The actual response will depend on the request method used.
* In a GET request, the response will contain an entity corresponding to the requested resource.
* In a POST request, the response will contain an entity describing or containing the result of the action.
*/
HttpStatusCodes[HttpStatusCodes["OK"] = 200] = "OK";
/**
* The request has been fulfilled, resulting in the creation of a new resource.
*/
HttpStatusCodes[HttpStatusCodes["CREATED"] = 201] = "CREATED";
/**
* The request has been accepted for processing, but the processing has not been completed.
* The request might or might not be eventually acted upon, and may be disallowed when processing occurs.
*/
HttpStatusCodes[HttpStatusCodes["ACCEPTED"] = 202] = "ACCEPTED";
/**
* SINCE HTTP/1.1
* The server is a transforming proxy that received a 200 OK from its origin,
* but is returning a modified version of the origin's response.
*/
HttpStatusCodes[HttpStatusCodes["NON_AUTHORITATIVE_INFORMATION"] = 203] = "NON_AUTHORITATIVE_INFORMATION";
/**
* The server successfully processed the request and is not returning any content.
*/
HttpStatusCodes[HttpStatusCodes["NO_CONTENT"] = 204] = "NO_CONTENT";
/**
* The server successfully processed the request, but is not returning any content.
* Unlike a 204 response, this response requires that the requester reset the document view.
*/
HttpStatusCodes[HttpStatusCodes["RESET_CONTENT"] = 205] = "RESET_CONTENT";
/**
* The server is delivering only part of the resource (byte serving) due to a range header sent by the client.
* The range header is used by HTTP clients to enable resuming of interrupted downloads,
* or split a download into multiple simultaneous streams.
*/
HttpStatusCodes[HttpStatusCodes["PARTIAL_CONTENT"] = 206] = "PARTIAL_CONTENT";
/**
* The message body that follows is an XML message and can contain a number of separate response codes,
* depending on how many sub-requests were made.
*/
HttpStatusCodes[HttpStatusCodes["MULTI_STATUS"] = 207] = "MULTI_STATUS";
/**
* The members of a DAV binding have already been enumerated in a preceding part of the (multistatus) response,
* and are not being included again.
*/
HttpStatusCodes[HttpStatusCodes["ALREADY_REPORTED"] = 208] = "ALREADY_REPORTED";
/**
* The server has fulfilled a request for the resource,
* and the response is a representation of the result of one or more instance-manipulations applied to the current instance.
*/
HttpStatusCodes[HttpStatusCodes["IM_USED"] = 226] = "IM_USED";
/**
* Indicates multiple options for the resource from which the client may choose (via agent-driven content negotiation).
* For example, this code could be used to present multiple video format options,
* to list files with different filename extensions, or to suggest word-sense disambiguation.
*/
HttpStatusCodes[HttpStatusCodes["MULTIPLE_CHOICES"] = 300] = "MULTIPLE_CHOICES";
/**
* This and all future requests should be directed to the given URI.
*/
HttpStatusCodes[HttpStatusCodes["MOVED_PERMANENTLY"] = 301] = "MOVED_PERMANENTLY";
/**
* This is an example of industry practice contradicting the standard.
* The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect
* (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302
* with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307
* to distinguish between the two behaviours. However, some Web applications and frameworks
* use the 302 status code as if it were the 303.
*/
HttpStatusCodes[HttpStatusCodes["FOUND"] = 302] = "FOUND";
/**
* SINCE HTTP/1.1
* The response to the request can be found under another URI using a GET method.
* When received in response to a POST (or PUT/DELETE), the client should presume that
* the server has received the data and should issue a redirect with a separate GET message.
*/
HttpStatusCodes[HttpStatusCodes["SEE_OTHER"] = 303] = "SEE_OTHER";
/**
* Indicates that the resource has not been modified since the version specified by the request headers If-Modified-Since or If-None-Match.
* In such case, there is no need to retransmit the resource since the client still has a previously-downloaded copy.
*/
HttpStatusCodes[HttpStatusCodes["NOT_MODIFIED"] = 304] = "NOT_MODIFIED";
/**
* SINCE HTTP/1.1
* The requested resource is available only through a proxy, the address for which is provided in the response.
* Many HTTP clients (such as Mozilla and Internet Explorer) do not correctly handle responses with this status code, primarily for security reasons.
*/
HttpStatusCodes[HttpStatusCodes["USE_PROXY"] = 305] = "USE_PROXY";
/**
* No longer used. Originally meant "Subsequent requests should use the specified proxy."
*/
HttpStatusCodes[HttpStatusCodes["SWITCH_PROXY"] = 306] = "SWITCH_PROXY";
/**
* SINCE HTTP/1.1
* In this case, the request should be repeated with another URI; however, future requests should still use the original URI.
* In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request.
* For example, a POST request should be repeated using another POST request.
*/
HttpStatusCodes[HttpStatusCodes["TEMPORARY_REDIRECT"] = 307] = "TEMPORARY_REDIRECT";
/**
* The request and all future requests should be repeated using another URI.
* 307 and 308 parallel the behaviors of 302 and 301, but do not allow the HTTP method to change.
* So, for example, submitting a form to a permanently redirected resource may continue smoothly.
*/
HttpStatusCodes[HttpStatusCodes["PERMANENT_REDIRECT"] = 308] = "PERMANENT_REDIRECT";
/**
* The server cannot or will not process the request due to an apparent client error
* (e.g., malformed request syntax, too large size, invalid request message framing, or deceptive request routing).
*/
HttpStatusCodes[HttpStatusCodes["BAD_REQUEST"] = 400] = "BAD_REQUEST";
/**
* Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet
* been provided. The response must include a WWW-Authenticate header field containing a challenge applicable to the
* requested resource. See Basic access authentication and Digest access authentication. 401 semantically means
* "unauthenticated",i.e. the user does not have the necessary credentials.
*/
HttpStatusCodes[HttpStatusCodes["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
/**
* Reserved for future use. The original intention was that this code might be used as part of some form of digital
* cash or micro payment scheme, but that has not happened, and this code is not usually used.
* Google Developers API uses this status if a particular developer has exceeded the daily limit on requests.
*/
HttpStatusCodes[HttpStatusCodes["PAYMENT_REQUIRED"] = 402] = "PAYMENT_REQUIRED";
/**
* The request was valid, but the server is refusing action.
* The user might not have the necessary permissions for a resource.
*/
HttpStatusCodes[HttpStatusCodes["FORBIDDEN"] = 403] = "FORBIDDEN";
/**
* The requested resource could not be found but may be available in the future.
* Subsequent requests by the client are permissible.
*/
HttpStatusCodes[HttpStatusCodes["NOT_FOUND"] = 404] = "NOT_FOUND";
/**
* A request method is not supported for the requested resource;
* for example, a GET request on a form that requires data to be presented via POST, or a PUT request on a read-only resource.
*/
HttpStatusCodes[HttpStatusCodes["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
/**
* The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request.
*/
HttpStatusCodes[HttpStatusCodes["NOT_ACCEPTABLE"] = 406] = "NOT_ACCEPTABLE";
/**
* The client must first authenticate itself with the proxy.
*/
HttpStatusCodes[HttpStatusCodes["PROXY_AUTHENTICATION_REQUIRED"] = 407] = "PROXY_AUTHENTICATION_REQUIRED";
/**
* The server timed out waiting for the request.
* According to HTTP specifications:
* "The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time."
*/
HttpStatusCodes[HttpStatusCodes["REQUEST_TIMEOUT"] = 408] = "REQUEST_TIMEOUT";
/**
* Indicates that the request could not be processed because of conflict in the request,
* such as an edit conflict between multiple simultaneous updates.
*/
HttpStatusCodes[HttpStatusCodes["CONFLICT"] = 409] = "CONFLICT";
/**
* Indicates that the resource requested is no longer available and will not be available again.
* This should be used when a resource has been intentionally removed and the resource should be purged.
* Upon receiving a 410 status code, the client should not request the resource in the future.
* Clients such as search engines should remove the resource from their indices.
* Most use cases do not require clients and search engines to purge the resource, and a "404 Not Found" may be used instead.
*/
HttpStatusCodes[HttpStatusCodes["GONE"] = 410] = "GONE";
/**
* The request did not specify the length of its content, which is required by the requested resource.
*/
HttpStatusCodes[HttpStatusCodes["LENGTH_REQUIRED"] = 411] = "LENGTH_REQUIRED";
/**
* The server does not meet one of the preconditions that the requester put on the request.
*/
HttpStatusCodes[HttpStatusCodes["PRECONDITION_FAILED"] = 412] = "PRECONDITION_FAILED";
/**
* The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large".
*/
HttpStatusCodes[HttpStatusCodes["PAYLOAD_TOO_LARGE"] = 413] = "PAYLOAD_TOO_LARGE";
/**
* The URI provided was too long for the server to process. Often the result of too much data being encoded as a query-string of a GET request,
* in which case it should be converted to a POST request.
* Called "Request-URI Too Long" previously.
*/
HttpStatusCodes[HttpStatusCodes["URI_TOO_LONG"] = 414] = "URI_TOO_LONG";
/**
* The request entity has a media type which the server or resource does not support.
* For example, the client uploads an image as image/svg+xml, but the server requires that images use a different format.
*/
HttpStatusCodes[HttpStatusCodes["UNSUPPORTED_MEDIA_TYPE"] = 415] = "UNSUPPORTED_MEDIA_TYPE";
/**
* The client has asked for a portion of the file (byte serving), but the server cannot supply that portion.
* For example, if the client asked for a part of the file that lies beyond the end of the file.
* Called "Requested Range Not Satisfiable" previously.
*/
HttpStatusCodes[HttpStatusCodes["RANGE_NOT_SATISFIABLE"] = 416] = "RANGE_NOT_SATISFIABLE";
/**
* The server cannot meet the requirements of the Expect request-header field.
*/
HttpStatusCodes[HttpStatusCodes["EXPECTATION_FAILED"] = 417] = "EXPECTATION_FAILED";
/**
* This code was defined in 1998 as one of the traditional IETF April Fools' jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol,
* and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by
* teapots requested to brew coffee. This HTTP status is used as an Easter egg in some websites, including Google.com.
*/
HttpStatusCodes[HttpStatusCodes["I_AM_A_TEAPOT"] = 418] = "I_AM_A_TEAPOT";
/**
* The request was directed at a server that is not able to produce a response (for example because a connection reuse).
*/
HttpStatusCodes[HttpStatusCodes["MISDIRECTED_REQUEST"] = 421] = "MISDIRECTED_REQUEST";
/**
* The request was well-formed but was unable to be followed due to semantic errors.
*/
HttpStatusCodes[HttpStatusCodes["UNPROCESSABLE_ENTITY"] = 422] = "UNPROCESSABLE_ENTITY";
/**
* The resource that is being accessed is locked.
*/
HttpStatusCodes[HttpStatusCodes["LOCKED"] = 423] = "LOCKED";
/**
* The request failed due to failure of a previous request (e.g., a PROPPATCH).
*/
HttpStatusCodes[HttpStatusCodes["FAILED_DEPENDENCY"] = 424] = "FAILED_DEPENDENCY";
/**
* The client should switch to a different protocol such as TLS/1.0, given in the Upgrade header field.
*/
HttpStatusCodes[HttpStatusCodes["UPGRADE_REQUIRED"] = 426] = "UPGRADE_REQUIRED";
/**
* The origin server requires the request to be conditional.
* Intended to prevent "the 'lost update' problem, where a client
* GETs a resource's state, modifies it, and PUTs it back to the server,
* when meanwhile a third party has modified the state on the server, leading to a conflict."
*/
HttpStatusCodes[HttpStatusCodes["PRECONDITION_REQUIRED"] = 428] = "PRECONDITION_REQUIRED";
/**
* The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes.
*/
HttpStatusCodes[HttpStatusCodes["TOO_MANY_REQUESTS"] = 429] = "TOO_MANY_REQUESTS";
/**
* The server is unwilling to process the request because either an individual header field,
* or all the header fields collectively, are too large.
*/
HttpStatusCodes[HttpStatusCodes["REQUEST_HEADER_FIELDS_TOO_LARGE"] = 431] = "REQUEST_HEADER_FIELDS_TOO_LARGE";
/**
* A server operator has received a legal demand to deny access to a resource or to a set of resources
* that includes the requested resource. The code 451 was chosen as a reference to the novel Fahrenheit 451.
*/
HttpStatusCodes[HttpStatusCodes["UNAVAILABLE_FOR_LEGAL_REASONS"] = 451] = "UNAVAILABLE_FOR_LEGAL_REASONS";
/**
* A generic error message, given when an unexpected condition was encountered and no more specific message is suitable.
*/
HttpStatusCodes[HttpStatusCodes["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
/**
* The server either does not recognize the request method, or it lacks the ability to fulfill the request.
* Usually this implies future availability (e.g., a new feature of a web-service API).
*/
HttpStatusCodes[HttpStatusCodes["NOT_IMPLEMENTED"] = 501] = "NOT_IMPLEMENTED";
/**
* The server was acting as a gateway or proxy and received an invalid response from the upstream server.
*/
HttpStatusCodes[HttpStatusCodes["BAD_GATEWAY"] = 502] = "BAD_GATEWAY";
/**
* The server is currently unavailable (because it is overloaded or down for maintenance).
* Generally, this is a temporary state.
*/
HttpStatusCodes[HttpStatusCodes["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
/**
* The server was acting as a gateway or proxy and did not receive a timely response from the upstream server.
*/
HttpStatusCodes[HttpStatusCodes["GATEWAY_TIMEOUT"] = 504] = "GATEWAY_TIMEOUT";
/**
* The server does not support the HTTP protocol version used in the request
*/
HttpStatusCodes[HttpStatusCodes["HTTP_VERSION_NOT_SUPPORTED"] = 505] = "HTTP_VERSION_NOT_SUPPORTED";
/**
* Transparent content negotiation for the request results in a circular reference.
*/
HttpStatusCodes[HttpStatusCodes["VARIANT_ALSO_NEGOTIATES"] = 506] = "VARIANT_ALSO_NEGOTIATES";
/**
* The server is unable to store the representation needed to complete the request.
*/
HttpStatusCodes[HttpStatusCodes["INSUFFICIENT_STORAGE"] = 507] = "INSUFFICIENT_STORAGE";
/**
* The server detected an infinite loop while processing the request.
*/
HttpStatusCodes[HttpStatusCodes["LOOP_DETECTED"] = 508] = "LOOP_DETECTED";
/**
* Further extensions to the request are required for the server to fulfill it.
*/
HttpStatusCodes[HttpStatusCodes["NOT_EXTENDED"] = 510] = "NOT_EXTENDED";
/**
* The client needs to authenticate to gain network access.
* Intended for use by intercepting proxies used to control access to the network (e.g., "captive portals" used
* to require agreement to Terms of Service before granting full Internet access via a Wi-Fi hotspot).
*/
HttpStatusCodes[HttpStatusCodes["NETWORK_AUTHENTICATION_REQUIRED"] = 511] = "NETWORK_AUTHENTICATION_REQUIRED";
})(HttpStatusCodes || (HttpStatusCodes = {}));
exports.default = HttpStatusCodes;
//# sourceMappingURL=HttpStatusCodes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HttpStatusCodes.js","sourceRoot":"","sources":["../../src/constants/HttpStatusCodes.ts"],"names":[],"mappings":";AAAA,4BAA4B;AAC5B;;GAEG;;AAEH;;;GAGG;AACH,IAAK,eAsXJ;AAtXD,WAAK,eAAe;IAEhB;;;;;;OAMG;IACH,+DAAc,CAAA;IAEd;;OAEG;IACH,qFAAyB,CAAA;IAEzB;;;;OAIG;IACH,mEAAgB,CAAA;IAEhB;;;;;OAKG;IACH,mDAAQ,CAAA;IAER;;OAEG;IACH,6DAAa,CAAA;IAEb;;;OAGG;IACH,+DAAc,CAAA;IAEd;;;;OAIG;IACH,yGAAmC,CAAA;IAEnC;;OAEG;IACH,mEAAgB,CAAA;IAEhB;;;OAGG;IACH,yEAAmB,CAAA;IAEnB;;;;OAIG;IACH,6EAAqB,CAAA;IAErB;;;OAGG;IACH,uEAAkB,CAAA;IAElB;;;OAGG;IACH,+EAAsB,CAAA;IAEtB;;;OAGG;IACH,6DAAa,CAAA;IAEb;;;;OAIG;IACH,+EAAsB,CAAA;IAEtB;;OAEG;IACH,iFAAuB,CAAA;IAEvB;;;;;;;OAOG;IACH,yDAAW,CAAA;IAEX;;;;;OAKG;IACH,iEAAe,CAAA;IAEf;;;OAGG;IACH,uEAAkB,CAAA;IAElB;;;;OAIG;IACH,iEAAe,CAAA;IAEf;;OAEG;IACH,uEAAkB,CAAA;IAElB;;;;;OAKG;IACH,mFAAwB,CAAA;IAExB;;;;OAIG;IACH,mFAAwB,CAAA;IAExB;;;OAGG;IACH,qEAAiB,CAAA;IAEjB;;;;;OAKG;IACH,uEAAkB,CAAA;IAElB;;;;OAIG;IACH,+EAAsB,CAAA;IAEtB;;;OAGG;IACH,iEAAe,CAAA;IAEf;;;OAGG;IACH,iEAAe,CAAA;IAEf;;;OAGG;IACH,mFAAwB,CAAA;IAExB;;OAEG;IACH,2EAAoB,CAAA;IAEpB;;OAEG;IACH,yGAAmC,CAAA;IAEnC;;;;OAIG;IACH,6EAAqB,CAAA;IAErB;;;OAGG;IACH,+DAAc,CAAA;IAEd;;;;;;OAMG;IACH,uDAAU,CAAA;IAEV;;OAEG;IACH,6EAAqB,CAAA;IAErB;;OAEG;IACH,qFAAyB,CAAA;IAEzB;;OAEG;IACH,iFAAuB,CAAA;IAEvB;;;;OAIG;IACH,uEAAkB,CAAA;IAElB;;;OAGG;IACH,2FAA4B,CAAA;IAE5B;;;;OAIG;IACH,yFAA2B,CAAA;IAE3B;;OAEG;IACH,mFAAwB,CAAA;IAExB;;;;OAIG;IACH,yEAAmB,CAAA;IAEnB;;OAEG;IACH,qFAAyB,CAAA;IAEzB;;OAEG;IACH,uFAA0B,CAAA;IAE1B;;OAEG;IACH,2DAAY,CAAA;IAEZ;;OAEG;IACH,iFAAuB,CAAA;IAEvB;;OAEG;IACH,+EAAsB,CAAA;IAEtB;;;;;OAKG;IACH,yFAA2B,CAAA;IAE3B;;OAEG;IACH,iFAAuB,CAAA;IAEvB;;;OAGG;IACH,6GAAqC,CAAA;IAErC;;;OAGG;IACH,yGAAmC,CAAA;IAEnC;;OAEG;IACH,yFAA2B,CAAA;IAE3B;;;OAGG;IACH,6EAAqB,CAAA;IAErB;;OAEG;IACH,qEAAiB,CAAA;IAEjB;;;OAGG;IACH,qFAAyB,CAAA;IAEzB;;OAEG;IACH,6EAAqB,CAAA;IAErB;;OAEG;IACH,mGAAgC,CAAA;IAEhC;;OAEG;IACH,6FAA6B,CAAA;IAE7B;;OAEG;IACH,uFAA0B,CAAA;IAE1B;;OAEG;IACH,yEAAmB,CAAA;IAEnB;;OAEG;IACH,uEAAkB,CAAA;IAElB;;;;OAIG;IACH,6GAAqC,CAAA;AACzC,CAAC,EAtXI,eAAe,KAAf,eAAe,QAsXnB;AAED,kBAAe,eAAe,CAAC"}

10
lib/constants/misc.js Normal file
View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeEnvs = void 0;
var NodeEnvs;
(function (NodeEnvs) {
NodeEnvs["Dev"] = "development";
NodeEnvs["Test"] = "test";
NodeEnvs["Production"] = "production";
})(NodeEnvs || (exports.NodeEnvs = NodeEnvs = {}));
//# sourceMappingURL=misc.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"misc.js","sourceRoot":"","sources":["../../src/constants/misc.ts"],"names":[],"mappings":";;;AAAA,IAAY,QAIX;AAJD,WAAY,QAAQ;IAClB,+BAAmB,CAAA;IACnB,yBAAa,CAAA;IACb,qCAAyB,CAAA;AAC3B,CAAC,EAJW,QAAQ,wBAAR,QAAQ,QAInB"}

View File

@@ -0,0 +1,62 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const board_repo_1 = __importDefault(require("@src/repos/board.repo"));
class BoardController {
constructor() {
// Get all boards for a user
this.getBoardsByUserId = async (req, res) => {
if (!req.headers.authorization) {
return res.status(401).json({ error: 'Unauthorized' });
}
const userId = +req.params.userId;
const boards = await board_repo_1.default.getBoardsByUserId(userId);
return res.json({ boards });
};
// Get single board by userId and boardId
this.getBoardByUserId = async (req, res) => {
if (!req.headers.authorization) {
return res.status(401).json({ error: 'Unauthorized' });
}
const userId = +req.params.userId;
const boardId = +req.params.boardId;
const board = await board_repo_1.default.getBoardByUserId(userId, boardId);
return res.json({ board });
};
// Create board for a user
this.createBoard = async (req, res) => {
if (!req.body.name) {
return res.status(400).json({ error: 'Board name required' });
}
const userId = +req.params.userId;
const boardData = req.body;
const board = await board_repo_1.default.createBoard(userId, boardData);
return res.status(201).json({ board });
};
// Update board for a user
this.updateBoard = async (req, res) => {
if (!req.body.name) {
return res.status(400).json({ error: 'Board name required' });
}
const userId = +req.params.userId;
const boardId = +req.params.boardId;
const boardData = req.body;
const board = await board_repo_1.default.updateBoard(userId, boardId, boardData);
return res.json({ board });
};
// Delete board for a user
this.deleteBoard = async (req, res) => {
if (!req.headers.authorization) {
return res.status(401).json({ error: 'Unauthorized' });
}
const userId = +req.params.userId;
const boardId = +req.params.boardId;
await board_repo_1.default.deleteBoard(userId, boardId);
return res.status(204).end();
};
}
}
exports.default = new BoardController();
//# sourceMappingURL=board.controller.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"board.controller.js","sourceRoot":"","sources":["../../src/controllers/board.controller.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,64 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const task_repo_1 = __importDefault(require("@src/repos/task.repo"));
class TaskController {
constructor() {
// Get all tasks for a board
this.getTasksByBoardId = async (req, res) => {
if (!req.headers.authorization) {
return res.status(401).json({ error: 'Unauthorized' });
}
const boardId = +req.params.boardId;
const tasks = await task_repo_1.default.getTasksByBoardId(boardId);
return res.json({ tasks });
};
// Get single task by boardId and taskId
this.getTaskByBoardId = async (req, res) => {
if (!req.headers.authorization) {
return res.status(401).json({ error: 'Unauthorized' });
}
const boardId = +req.params.boardId;
const taskId = +req.params.taskId;
const task = await task_repo_1.default.getTaskByBoardId(boardId, taskId);
return res.json({ task });
};
// Create task for a board
this.createTask = async (req, res) => {
// Dummy form validation
if (!req.body.title) {
return res.status(400).json({ error: 'Task title required' });
}
const boardId = +req.params.boardId;
const taskData = req.body;
const task = await task_repo_1.default.createTask(boardId, taskData);
return res.status(201).json({ task });
};
// Update task for a board
this.updateTask = async (req, res) => {
// Dummy form validation
if (!req.body.title) {
return res.status(400).json({ error: 'Task title required' });
}
const boardId = +req.params.boardId;
const taskId = +req.params.taskId;
const taskData = req.body;
const task = await task_repo_1.default.updateTask(boardId, taskId, taskData);
return res.json({ task });
};
// Delete task for a board
this.deleteTask = async (req, res) => {
if (!req.headers.authorization) {
return res.status(401).json({ error: 'Unauthorized' });
}
const boardId = +req.params.boardId;
const taskId = +req.params.taskId;
await task_repo_1.default.deleteTask(boardId, taskId);
return res.status(204).end();
};
}
}
exports.default = new TaskController();
//# sourceMappingURL=task.controller.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"task.controller.js","sourceRoot":"","sources":["../../src/controllers/task.controller.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,31 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const user_repo_1 = __importDefault(require("@src/repos/user.repo"));
class UserController {
constructor() {
this.getAll = async (_, res) => {
const users = await user_repo_1.default.getAll();
return res.status(200).json({ users });
};
this.add = async (req, res) => {
const user = req.body.user;
await user_repo_1.default.add(user);
return res.status(201).end();
};
this.update = async (req, res) => {
const user = req.body.user;
await user_repo_1.default.update(user);
return res.status(200).end();
};
this.delete = async (req, res) => {
const id = +req.params.id;
await user_repo_1.default.delete(id);
return res.status(200).end();
};
}
}
exports.default = new UserController();
//# sourceMappingURL=user.controller.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"user.controller.js","sourceRoot":"","sources":["../../src/controllers/user.controller.ts"],"names":[],"mappings":";;;;;AACA,qEAA4C;AAG5C,MAAM,cAAc;IAApB;QACS,WAAM,GAAG,KAAK,EAAE,CAAU,EAAE,GAAa,EAAE,EAAE;YAClD,MAAM,KAAK,GAAG,MAAM,mBAAQ,CAAC,MAAM,EAAE,CAAC;YACtC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACzC,CAAC,CAAC;QAEK,QAAG,GAAG,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;YACjD,MAAM,IAAI,GAAU,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAClC,MAAM,mBAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;QAC/B,CAAC,CAAC;QAEK,WAAM,GAAG,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;YACpD,MAAM,IAAI,GAAU,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;YAClC,MAAM,mBAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC5B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;QAC/B,CAAC,CAAC;QAEK,WAAM,GAAG,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;YACpD,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,MAAM,mBAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;QAC/B,CAAC,CAAC;IACJ,CAAC;CAAA;AAED,kBAAe,IAAI,cAAc,EAAE,CAAC"}

14
lib/index.js Normal file
View File

@@ -0,0 +1,14 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
require("./pre-start"); // Must be the first import
const jet_logger_1 = __importDefault(require("jet-logger"));
const EnvVars_1 = __importDefault(require("@src/constants/EnvVars"));
const server_1 = __importDefault(require("./server"));
// **** Run **** //
const SERVER_START_MSG = ('Express server started on port: ' +
EnvVars_1.default.Port.toString());
server_1.default.listen(EnvVars_1.default.Port, () => jet_logger_1.default.info(SERVER_START_MSG));
//# sourceMappingURL=index.js.map

1
lib/index.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;AAAA,uBAAqB,CAAC,2BAA2B;AACjD,4DAAgC;AAEhC,qEAA6C;AAC7C,sDAA8B;AAG9B,mBAAmB;AAEnB,MAAM,gBAAgB,GAAG,CAAC,kCAAkC;IAC1D,iBAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;AAE3B,gBAAM,CAAC,MAAM,CAAC,iBAAO,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,oBAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC"}

48
lib/models/board.model.js Normal file
View File

@@ -0,0 +1,48 @@
"use strict";
// **** Variables **** //
Object.defineProperty(exports, "__esModule", { value: true });
const INVALID_CONSTRUCTOR_PARAM = 'nameOrObj arg must be a string or an object with the appropriate board keys.';
// **** Functions **** //
/**
* Create new Board.
*/
function new_(userId, name, description, createdAt, updatedAt, id) {
const now = new Date().toISOString();
return {
id: (id !== null && id !== void 0 ? id : -1),
userId: (userId !== null && userId !== void 0 ? userId : -1),
name: (name !== null && name !== void 0 ? name : ''),
description,
createdAt: (createdAt !== null && createdAt !== void 0 ? createdAt : now),
updatedAt: (updatedAt !== null && updatedAt !== void 0 ? updatedAt : now),
};
}
/**
* Get board instance from object.
*/
function from(param) {
if (!isBoard(param)) {
throw new Error(INVALID_CONSTRUCTOR_PARAM);
}
const p = param;
return new_(p.userId, p.name, p.description, p.createdAt, p.updatedAt, p.id);
}
/**
* See if the param meets criteria to be a board.
*/
function isBoard(arg) {
return (!!arg &&
typeof arg === 'object' &&
'id' in arg &&
'userId' in arg &&
'name' in arg &&
'createdAt' in arg &&
'updatedAt' in arg);
}
// **** Export default **** //
exports.default = {
new: new_,
from,
isBoard,
};
//# sourceMappingURL=board.model.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"board.model.js","sourceRoot":"","sources":["../../src/models/board.model.ts"],"names":[],"mappings":";AAAA,yBAAyB;;AAEzB,MAAM,yBAAyB,GAAG,8EAA8E,CAAC;AAajH,yBAAyB;AAEzB;;GAEG;AACH,SAAS,IAAI,CACZ,MAAe,EACf,IAAa,EACb,WAAoB,EACpB,SAAkB,EAClB,SAAkB,EAClB,EAAW;IAEX,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO;QACN,EAAE,EAAE,CAAC,EAAE,aAAF,EAAE,cAAF,EAAE,GAAI,CAAC,CAAC,CAAC;QACd,MAAM,EAAE,CAAC,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,CAAC,CAAC,CAAC;QACtB,IAAI,EAAE,CAAC,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,EAAE,CAAC;QAClB,WAAW;QACX,SAAS,EAAE,CAAC,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,GAAG,CAAC;QAC7B,SAAS,EAAE,CAAC,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,GAAG,CAAC;KAC7B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,IAAI,CAAC,KAAa;IAC1B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,CAAC,GAAG,KAAe,CAAC;IAC1B,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,GAAY;IAC5B,OAAO,CACN,CAAC,CAAC,GAAG;QACL,OAAO,GAAG,KAAK,QAAQ;QACvB,IAAI,IAAI,GAAG;QACX,QAAQ,IAAI,GAAG;QACf,MAAM,IAAI,GAAG;QACb,WAAW,IAAI,GAAG;QAClB,WAAW,IAAI,GAAG,CAClB,CAAC;AACH,CAAC;AAED,8BAA8B;AAE9B,kBAAe;IACd,GAAG,EAAE,IAAI;IACT,IAAI;IACJ,OAAO;CACE,CAAC"}

50
lib/models/task.model.js Normal file
View File

@@ -0,0 +1,50 @@
"use strict";
// **** Variables **** //
Object.defineProperty(exports, "__esModule", { value: true });
const INVALID_CONSTRUCTOR_PARAM = 'titleOrObj arg must be a string or an object with the appropriate task keys.';
// **** Functions **** //
/**
* Create new Task.
*/
function new_(boardId, title, status, description, createdAt, updatedAt, id) {
const now = new Date().toISOString();
return {
id: (id !== null && id !== void 0 ? id : -1),
boardId: (boardId !== null && boardId !== void 0 ? boardId : -1),
title: (title !== null && title !== void 0 ? title : ''),
status: (status !== null && status !== void 0 ? status : 'todo'),
description,
createdAt: (createdAt !== null && createdAt !== void 0 ? createdAt : now),
updatedAt: (updatedAt !== null && updatedAt !== void 0 ? updatedAt : now),
};
}
/**
* Get task instance from object.
*/
function from(param) {
if (!isTask(param)) {
throw new Error(INVALID_CONSTRUCTOR_PARAM);
}
const p = param;
return new_(p.boardId, p.title, p.status, p.description, p.createdAt, p.updatedAt, p.id);
}
/**
* See if the param meets criteria to be a task.
*/
function isTask(arg) {
return (!!arg &&
typeof arg === 'object' &&
'id' in arg &&
'boardId' in arg &&
'title' in arg &&
'status' in arg &&
'createdAt' in arg &&
'updatedAt' in arg);
}
// **** Export default **** //
exports.default = {
new: new_,
from,
isTask,
};
//# sourceMappingURL=task.model.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"task.model.js","sourceRoot":"","sources":["../../src/models/task.model.ts"],"names":[],"mappings":";AAAA,yBAAyB;;AAEzB,MAAM,yBAAyB,GAAG,8EAA8E,CAAC;AAcjH,yBAAyB;AAEzB;;GAEG;AACH,SAAS,IAAI,CACZ,OAAgB,EAChB,KAAc,EACd,MAAwC,EACxC,WAAoB,EACpB,SAAkB,EAClB,SAAkB,EAClB,EAAW;IAEX,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO;QACN,EAAE,EAAE,CAAC,EAAE,aAAF,EAAE,cAAF,EAAE,GAAI,CAAC,CAAC,CAAC;QACd,OAAO,EAAE,CAAC,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,CAAC,CAAC,CAAC;QACxB,KAAK,EAAE,CAAC,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,EAAE,CAAC;QACpB,MAAM,EAAE,CAAC,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,MAAM,CAAC;QAC1B,WAAW;QACX,SAAS,EAAE,CAAC,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,GAAG,CAAC;QAC7B,SAAS,EAAE,CAAC,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,GAAG,CAAC;KAC7B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,IAAI,CAAC,KAAa;IAC1B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,CAAC,GAAG,KAAc,CAAC;IACzB,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AAC1F,CAAC;AAED;;GAEG;AACH,SAAS,MAAM,CAAC,GAAY;IAC3B,OAAO,CACN,CAAC,CAAC,GAAG;QACL,OAAO,GAAG,KAAK,QAAQ;QACvB,IAAI,IAAI,GAAG;QACX,SAAS,IAAI,GAAG;QAChB,OAAO,IAAI,GAAG;QACd,QAAQ,IAAI,GAAG;QACf,WAAW,IAAI,GAAG;QAClB,WAAW,IAAI,GAAG,CAClB,CAAC;AACH,CAAC;AAED,8BAA8B;AAE9B,kBAAe;IACd,GAAG,EAAE,IAAI;IACT,IAAI;IACJ,MAAM;CACG,CAAC"}

54
lib/models/user.model.js Normal file
View File

@@ -0,0 +1,54 @@
"use strict";
// **** Variables **** //
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserRoles = void 0;
const INVALID_CONSTRUCTOR_PARAM = 'nameOrObj arg must a string or an ' +
'object with the appropriate user keys.';
var UserRoles;
(function (UserRoles) {
UserRoles[UserRoles["Standard"] = 0] = "Standard";
UserRoles[UserRoles["Admin"] = 1] = "Admin";
})(UserRoles || (exports.UserRoles = UserRoles = {}));
// **** Functions **** //
/**
* Create new User.
*/
function new_(name, email, role, pwdHash, id) {
return {
id: (id !== null && id !== void 0 ? id : -1),
name: (name !== null && name !== void 0 ? name : ''),
email: (email !== null && email !== void 0 ? email : ''),
role: (role !== null && role !== void 0 ? role : UserRoles.Standard),
pwdHash: (pwdHash !== null && pwdHash !== void 0 ? pwdHash : ''),
};
}
/**
* Get user instance from object.
*/
function from(param) {
// Check is user
if (!isUser(param)) {
throw new Error(INVALID_CONSTRUCTOR_PARAM);
}
// Get user instance
const p = param;
return new_(p.name, p.email, p.role, p.pwdHash, p.id);
}
/**
* See if the param meets criteria to be a user.
*/
function isUser(arg) {
return (!!arg &&
typeof arg === 'object' &&
'id' in arg &&
'email' in arg &&
'name' in arg &&
'role' in arg);
}
// **** Export default **** //
exports.default = {
new: new_,
from,
isUser,
};
//# sourceMappingURL=user.model.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"user.model.js","sourceRoot":"","sources":["../../src/models/user.model.ts"],"names":[],"mappings":";AAAA,yBAAyB;;;AAEzB,MAAM,yBAAyB,GAAG,oCAAoC;IACpE,wCAAwC,CAAC;AAE3C,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,iDAAQ,CAAA;IACR,2CAAK,CAAA;AACP,CAAC,EAHW,SAAS,yBAAT,SAAS,QAGpB;AAqBD,yBAAyB;AAEzB;;GAEG;AACH,SAAS,IAAI,CACX,IAAa,EACb,KAAc,EACd,IAAgB,EAChB,OAAgB,EAChB,EAAW;IAEX,OAAO;QACL,EAAE,EAAE,CAAC,EAAE,aAAF,EAAE,cAAF,EAAE,GAAI,CAAC,CAAC,CAAC;QACd,IAAI,EAAE,CAAC,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,EAAE,CAAC;QAClB,KAAK,EAAE,CAAC,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,EAAE,CAAC;QACpB,IAAI,EAAE,CAAC,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,SAAS,CAAC,QAAQ,CAAC;QAClC,OAAO,EAAE,CAAC,OAAO,aAAP,OAAO,cAAP,OAAO,GAAI,EAAE,CAAC;KACzB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,IAAI,CAAC,KAAa;IACzB,gBAAgB;IAChB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,oBAAoB;IACpB,MAAM,CAAC,GAAG,KAAc,CAAC;IACzB,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,SAAS,MAAM,CAAC,GAAY;IAC1B,OAAO,CACL,CAAC,CAAC,GAAG;QACL,OAAO,GAAG,KAAK,QAAQ;QACvB,IAAI,IAAI,GAAG;QACX,OAAO,IAAI,GAAG;QACd,MAAM,IAAI,GAAG;QACb,MAAM,IAAI,GAAG,CACd,CAAC;AACJ,CAAC;AAGD,8BAA8B;AAE9B,kBAAe;IACb,GAAG,EAAE,IAAI;IACT,IAAI;IACJ,MAAM;CACE,CAAC"}

17
lib/other/classes.js Normal file
View File

@@ -0,0 +1,17 @@
"use strict";
/**
* Miscellaneous shared classes go here.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.RouteError = void 0;
/**
* Error with status code and message
*/
class RouteError extends Error {
constructor(status, message) {
super(message);
this.status = status;
}
}
exports.RouteError = RouteError;
//# sourceMappingURL=classes.js.map

1
lib/other/classes.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"classes.js","sourceRoot":"","sources":["../../src/other/classes.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAKH;;GAEG;AACH,MAAa,UAAW,SAAQ,KAAK;IAEnC,YAAY,MAAuB,EAAE,OAAe;QAClD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAND,gCAMC"}

3
lib/other/types.js Normal file
View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map

1
lib/other/types.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/other/types.ts"],"names":[],"mappings":""}

31
lib/pre-start.js Normal file
View File

@@ -0,0 +1,31 @@
"use strict";
/**
* Pre-start is where we want to place things that must run BEFORE the express
* server is started. This is useful for environment variables, command-line
* arguments, and cron-jobs.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// NOTE: DO NOT IMPORT ANY SOURCE CODE HERE
const path_1 = __importDefault(require("path"));
const dotenv_1 = __importDefault(require("dotenv"));
const ts_command_line_args_1 = require("ts-command-line-args");
// **** Setup **** //
// Command line arguments
const args = (0, ts_command_line_args_1.parse)({
env: {
type: String,
defaultValue: 'development',
alias: 'e',
},
});
// Set the env file
const result2 = dotenv_1.default.config({
path: path_1.default.join(__dirname, `../env/${args.env}.env`),
});
if (result2.error) {
throw result2.error;
}
//# sourceMappingURL=pre-start.js.map

1
lib/pre-start.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"pre-start.js","sourceRoot":"","sources":["../src/pre-start.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;AAEH,2CAA2C;AAC3C,gDAAwB;AACxB,oDAA4B;AAC5B,+DAA6C;AAU7C,qBAAqB;AAErB,yBAAyB;AACzB,MAAM,IAAI,GAAG,IAAA,4BAAK,EAAQ;IACxB,GAAG,EAAE;QACH,IAAI,EAAE,MAAM;QACZ,YAAY,EAAE,aAAa;QAC3B,KAAK,EAAE,GAAG;KACX;CACF,CAAC,CAAC;AAEH,mBAAmB;AACnB,MAAM,OAAO,GAAG,gBAAM,CAAC,MAAM,CAAC;IAC5B,IAAI,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,IAAI,CAAC,GAAG,MAAM,CAAC;CACrD,CAAC,CAAC;AACH,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,MAAM,OAAO,CAAC,KAAK,CAAC;AACtB,CAAC"}

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Http = (() => {
// Setup request for json
var getOptions = (verb, data) => {
var options = {
dataType: 'json',
method: verb,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
};
if (data) {
options.body = JSON.stringify(data);
}
return options;
};
// Set Http methods
return {
get: (path) => fetch(path, getOptions('GET')),
post: (path, data) => fetch(path, getOptions('POST', data)),
put: (path, data) => fetch(path, getOptions('PUT', data)),
delete: (path) => fetch(path, getOptions('DELETE')),
};
})();
//# sourceMappingURL=http.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/public/scripts/http.js"],"names":[],"mappings":";;AAAA,IAAI,IAAI,GAAG,CAAC,GAAG,EAAE;IACf,yBAAyB;IACzB,IAAI,UAAU,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;QAC9B,IAAI,OAAO,GAAG;YACZ,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE;gBACP,QAAQ,EAAE,kBAAkB;gBAC5B,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC;QACF,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IACF,mBAAmB;IACnB,OAAO;QACL,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC3D,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACzD,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;KACpD,CAAC;AACJ,CAAC,CAAC,EAAE,CAAC"}

154
lib/public/scripts/users.js Normal file
View File

@@ -0,0 +1,154 @@
"use strict";
// ***** Start **** //
Object.defineProperty(exports, "__esModule", { value: true });
displayUsers();
// ***** Fetch and display users **** //
/**
* Call api
*/
function displayUsers() {
Http
.get('/api/users/all')
.then(resp => resp.json())
.then((resp) => {
var allUsers = resp.users;
// Empty the anchor
var allUsersAnchor = document.getElementById('all-users-anchor');
allUsersAnchor.innerHTML = '';
// Append users to anchor
allUsers.forEach((user) => {
allUsersAnchor.innerHTML += getUserDisplayEle(user);
});
});
}
/**
* Get user display element
*/
function getUserDisplayEle(user) {
return (`<div class="user-display-ele">
<div class="normal-view">
<div>Name: ${user.name}</div>
<div>Email: ${user.email}</div>
<button class="edit-user-btn" data-user-id="${user.id}" data-user-role="${user.role}">
Edit
</button>
<button class="delete-user-btn" data-user-id="${user.id}">
Delete
</button>
</div>
<div class="edit-view">
<div>
Name: <input class="name-edit-input" value="${user.name}">
</div>
<div>
Email: <input class="email-edit-input" value="${user.email}">
</div>
<button class="submit-edit-btn" data-user-id="${user.id}">
Submit
</button>
<button class="cancel-edit-btn" data-user-id="${user.id}">
Cancel
</button>
</div>
</div>`);
}
// **** Add, Edit, and Delete Users **** //
// Setup event listener for button click
document.addEventListener('click', function (event) {
event.preventDefault();
var ele = event.target;
if (ele.matches('#add-user-btn')) {
addUser();
}
else if (ele.matches('.edit-user-btn')) {
showEditView(ele.parentNode.parentNode);
}
else if (ele.matches('.cancel-edit-btn')) {
cancelEdit(ele.parentNode.parentNode);
}
else if (ele.matches('.submit-edit-btn')) {
submitEdit(ele);
}
else if (ele.matches('.delete-user-btn')) {
deleteUser(ele);
}
else if (ele.matches('#logout-btn')) {
logoutUser();
}
}, false);
/**
* Add a new user.
*/
function addUser() {
var nameInput = document.getElementById('name-input');
var emailInput = document.getElementById('email-input');
var data = {
user: {
id: -1,
name: nameInput.value,
email: emailInput.value,
role: 0,
},
};
// Call api
Http
.post('/api/users/add', data)
.then(() => displayUsers());
}
/**
* Show edit view.
*/
function showEditView(userEle) {
var normalView = userEle.getElementsByClassName('normal-view')[0];
var editView = userEle.getElementsByClassName('edit-view')[0];
normalView.style.display = 'none';
editView.style.display = 'block';
}
/**
* Cancel edit.
*/
function cancelEdit(userEle) {
var normalView = userEle.getElementsByClassName('normal-view')[0];
var editView = userEle.getElementsByClassName('edit-view')[0];
normalView.style.display = 'block';
editView.style.display = 'none';
}
/**
* Submit edit.
*/
function submitEdit(ele) {
var userEle = ele.parentNode.parentNode;
var nameInput = userEle.getElementsByClassName('name-edit-input')[0];
var emailInput = userEle.getElementsByClassName('email-edit-input')[0];
var id = ele.getAttribute('data-user-id');
var role = ele.getAttribute('data-user-role');
var data = {
user: {
id: Number(id),
name: nameInput.value,
email: emailInput.value,
role: Number(role),
},
};
Http
.put('/api/users/update', data)
.then(() => displayUsers());
}
/**
* Delete a user
*/
function deleteUser(ele) {
var id = ele.getAttribute('data-user-id');
Http
.delete('/api/users/delete/' + id)
.then(() => displayUsers());
}
// **** Logout **** //
function logoutUser() {
Http
.get('/api/auth/logout')
.then(() => window.location.href = '/');
}
//# sourceMappingURL=users.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"users.js","sourceRoot":"","sources":["../../../src/public/scripts/users.js"],"names":[],"mappings":"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"}

52
lib/repos/BoardRepo.js Normal file
View File

@@ -0,0 +1,52 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const MockOrm_1 = __importDefault(require("./MockOrm"));
async function getBoardsByUserId(userId) {
const db = await MockOrm_1.default.openDb();
return db.boards.filter((board) => board.userId === userId);
}
async function getBoardByUserId(userId, boardId) {
const db = await MockOrm_1.default.openDb();
return db.boards.find((board) => board.userId === userId && board.id === boardId) || null;
}
async function createBoard(userId, board) {
const db = await MockOrm_1.default.openDb();
board.id = Date.now();
board.userId = userId;
board.createdAt = new Date().toISOString();
board.updatedAt = board.createdAt;
db.boards.push(board);
await MockOrm_1.default.saveDb(db);
return board;
}
async function updateBoard(userId, boardId, boardData) {
const db = await MockOrm_1.default.openDb();
const board = db.boards.find((b) => b.userId === userId && b.id === boardId);
if (board) {
Object.assign(board, boardData, { updatedAt: new Date().toISOString() });
await MockOrm_1.default.saveDb(db);
return board;
}
return null;
}
async function deleteBoard(userId, boardId) {
const db = await MockOrm_1.default.openDb();
const idx = db.boards.findIndex((b) => b.userId === userId && b.id === boardId);
if (idx !== -1) {
db.boards.splice(idx, 1);
await MockOrm_1.default.saveDb(db);
return true;
}
return false;
}
exports.default = {
getBoardsByUserId,
getBoardByUserId,
createBoard,
updateBoard,
deleteBoard,
};
//# sourceMappingURL=BoardRepo.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BoardRepo.js","sourceRoot":"","sources":["../../src/repos/BoardRepo.ts"],"names":[],"mappings":";;;;;AACA,wDAA4B;AAE5B,KAAK,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AACrE,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,OAAe;IAC9D,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,IAAI,CAAC;AACnG,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,KAAa;IACvD,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrB,OAAO,KAAK,CAAC;AACd,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,OAAe,EAAE,SAA0B;IACrF,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IACrF,IAAI,KAAK,EAAE,CAAC;QACX,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACzE,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,OAAe;IACzD,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IACxF,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QAChB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACzB,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,kBAAe;IACd,iBAAiB;IACjB,gBAAgB;IAChB,WAAW;IACX,WAAW;IACX,WAAW;CACX,CAAC"}

27
lib/repos/MockOrm.js Normal file
View File

@@ -0,0 +1,27 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const jsonfile_1 = __importDefault(require("jsonfile"));
// **** Variables **** //
const DB_FILE_NAME = 'database.json';
// **** Functions **** //
/**
* Fetch the json from the file.
*/
function openDb() {
return jsonfile_1.default.readFile(__dirname + '/' + DB_FILE_NAME);
}
/**
* Update the file.
*/
function saveDb(db) {
return jsonfile_1.default.writeFile((__dirname + '/' + DB_FILE_NAME), db);
}
// **** Export default **** //
exports.default = {
openDb,
saveDb,
};
//# sourceMappingURL=MockOrm.js.map

1
lib/repos/MockOrm.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"MockOrm.js","sourceRoot":"","sources":["../../src/repos/MockOrm.ts"],"names":[],"mappings":";;;;;AAEA,wDAAgC;AAOhC,yBAAyB;AAEzB,MAAM,YAAY,GAAG,eAAe,CAAC;AAYrC,yBAAyB;AAEzB;;GAEG;AACH,SAAS,MAAM;IACb,OAAO,kBAAQ,CAAC,QAAQ,CAAC,SAAS,GAAG,GAAG,GAAG,YAAY,CAAiB,CAAC;AAC3E,CAAC;AAED;;GAEG;AACH,SAAS,MAAM,CAAC,EAAO;IACrB,OAAO,kBAAQ,CAAC,SAAS,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;AAClE,CAAC;AAGD,8BAA8B;AAE9B,kBAAe;IACb,MAAM;IACN,MAAM;CACE,CAAC"}

52
lib/repos/TaskRepo.js Normal file
View File

@@ -0,0 +1,52 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const MockOrm_1 = __importDefault(require("./MockOrm"));
async function getTasksByBoardId(boardId) {
const db = await MockOrm_1.default.openDb();
return db.tasks.filter((task) => task.boardId === boardId);
}
async function getTaskByBoardId(boardId, taskId) {
const db = await MockOrm_1.default.openDb();
return db.tasks.find((task) => task.boardId === boardId && task.id === taskId) || null;
}
async function createTask(boardId, task) {
const db = await MockOrm_1.default.openDb();
task.id = Date.now();
task.boardId = boardId;
task.createdAt = new Date().toISOString();
task.updatedAt = task.createdAt;
db.tasks.push(task);
await MockOrm_1.default.saveDb(db);
return task;
}
async function updateTask(boardId, taskId, taskData) {
const db = await MockOrm_1.default.openDb();
const task = db.tasks.find((t) => t.boardId === boardId && t.id === taskId);
if (task) {
Object.assign(task, taskData, { updatedAt: new Date().toISOString() });
await MockOrm_1.default.saveDb(db);
return task;
}
return null;
}
async function deleteTask(boardId, taskId) {
const db = await MockOrm_1.default.openDb();
const idx = db.tasks.findIndex((t) => t.boardId === boardId && t.id === taskId);
if (idx !== -1) {
db.tasks.splice(idx, 1);
await MockOrm_1.default.saveDb(db);
return true;
}
return false;
}
exports.default = {
getTasksByBoardId,
getTaskByBoardId,
createTask,
updateTask,
deleteTask,
};
//# sourceMappingURL=TaskRepo.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"TaskRepo.js","sourceRoot":"","sources":["../../src/repos/TaskRepo.ts"],"names":[],"mappings":";;;;;AACA,wDAA4B;AAE5B,KAAK,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAW,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;AACnE,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,OAAe,EAAE,MAAc;IAC9D,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAW,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,OAAO,IAAI,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC;AAC/F,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,IAAW;IACrD,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACvB,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IAChC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC;AACb,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,MAAc,EAAE,QAAwB;IAClF,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACnF,IAAI,IAAI,EAAE,CAAC;QACV,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACvE,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,MAAc;IACxD,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACvF,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QAChB,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACxB,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,kBAAe;IACd,iBAAiB;IACjB,gBAAgB;IAChB,UAAU;IACV,UAAU;IACV,UAAU;CACV,CAAC"}

52
lib/repos/board.repo.js Normal file
View File

@@ -0,0 +1,52 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const MockOrm_1 = __importDefault(require("./MockOrm"));
async function getBoardsByUserId(userId) {
const db = await MockOrm_1.default.openDb();
return db.boards.filter((board) => board.userId === userId);
}
async function getBoardByUserId(userId, boardId) {
const db = await MockOrm_1.default.openDb();
return db.boards.find((board) => board.userId === userId && board.id === boardId) || null;
}
async function createBoard(userId, board) {
const db = await MockOrm_1.default.openDb();
board.id = Date.now();
board.userId = userId;
board.createdAt = new Date().toISOString();
board.updatedAt = board.createdAt;
db.boards.push(board);
await MockOrm_1.default.saveDb(db);
return board;
}
async function updateBoard(userId, boardId, boardData) {
const db = await MockOrm_1.default.openDb();
const board = db.boards.find((b) => b.userId === userId && b.id === boardId);
if (board) {
Object.assign(board, boardData, { updatedAt: new Date().toISOString() });
await MockOrm_1.default.saveDb(db);
return board;
}
return null;
}
async function deleteBoard(userId, boardId) {
const db = await MockOrm_1.default.openDb();
const idx = db.boards.findIndex((b) => b.userId === userId && b.id === boardId);
if (idx !== -1) {
db.boards.splice(idx, 1);
await MockOrm_1.default.saveDb(db);
return true;
}
return false;
}
exports.default = {
getBoardsByUserId,
getBoardByUserId,
createBoard,
updateBoard,
deleteBoard,
};
//# sourceMappingURL=board.repo.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"board.repo.js","sourceRoot":"","sources":["../../src/repos/board.repo.ts"],"names":[],"mappings":";;;;;AACA,wDAA4B;AAE5B,KAAK,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;AACrE,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,OAAe;IAC9D,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,EAAE,KAAK,OAAO,CAAC,IAAI,IAAI,CAAC;AACnG,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,KAAa;IACvD,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrB,OAAO,KAAK,CAAC;AACd,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,OAAe,EAAE,SAA0B;IACrF,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IACrF,IAAI,KAAK,EAAE,CAAC;QACX,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACzE,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,OAAe;IACzD,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC;IACxF,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QAChB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACzB,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,kBAAe;IACd,iBAAiB;IACjB,gBAAgB;IAChB,WAAW;IACX,WAAW;IACX,WAAW;CACX,CAAC"}

52
lib/repos/task.repo.js Normal file
View File

@@ -0,0 +1,52 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const MockOrm_1 = __importDefault(require("./MockOrm"));
async function getTasksByBoardId(boardId) {
const db = await MockOrm_1.default.openDb();
return db.tasks.filter((task) => task.boardId === boardId);
}
async function getTaskByBoardId(boardId, taskId) {
const db = await MockOrm_1.default.openDb();
return db.tasks.find((task) => task.boardId === boardId && task.id === taskId) || null;
}
async function createTask(boardId, task) {
const db = await MockOrm_1.default.openDb();
task.id = Date.now();
task.boardId = boardId;
task.createdAt = new Date().toISOString();
task.updatedAt = task.createdAt;
db.tasks.push(task);
await MockOrm_1.default.saveDb(db);
return task;
}
async function updateTask(boardId, taskId, taskData) {
const db = await MockOrm_1.default.openDb();
const task = db.tasks.find((t) => t.boardId === boardId && t.id === taskId);
if (task) {
Object.assign(task, taskData, { updatedAt: new Date().toISOString() });
await MockOrm_1.default.saveDb(db);
return task;
}
return null;
}
async function deleteTask(boardId, taskId) {
const db = await MockOrm_1.default.openDb();
const idx = db.tasks.findIndex((t) => t.boardId === boardId && t.id === taskId);
if (idx !== -1) {
db.tasks.splice(idx, 1);
await MockOrm_1.default.saveDb(db);
return true;
}
return false;
}
exports.default = {
getTasksByBoardId,
getTaskByBoardId,
createTask,
updateTask,
deleteTask,
};
//# sourceMappingURL=task.repo.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"task.repo.js","sourceRoot":"","sources":["../../src/repos/task.repo.ts"],"names":[],"mappings":";;;;;AACA,wDAA4B;AAE5B,KAAK,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAW,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;AACnE,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,OAAe,EAAE,MAAc;IAC9D,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAW,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,OAAO,IAAI,IAAI,CAAC,EAAE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC;AAC/F,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,IAAW;IACrD,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACvB,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IAChC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC;AACb,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,MAAc,EAAE,QAAwB;IAClF,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACnF,IAAI,IAAI,EAAE,CAAC;QACV,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACvE,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,MAAc;IACxD,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACvF,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QAChB,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACxB,MAAM,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,kBAAe;IACd,iBAAiB;IACjB,gBAAgB;IAChB,UAAU;IACV,UAAU;IACV,UAAU;CACV,CAAC"}

82
lib/repos/user.repo.js Normal file
View File

@@ -0,0 +1,82 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const misc_1 = require("@src/util/misc");
const MockOrm_1 = __importDefault(require("./MockOrm"));
// **** Functions **** //
/**
* Get one user.
*/
async function getOne(email) {
const db = await MockOrm_1.default.openDb();
for (const user of db.users) {
if (user.email === email) {
return user;
}
}
return null;
}
/**
* See if a user with the given id exists.
*/
async function persists(id) {
const db = await MockOrm_1.default.openDb();
for (const user of db.users) {
if (user.id === id) {
return true;
}
}
return false;
}
/**
* Get all users.
*/
async function getAll() {
const db = await MockOrm_1.default.openDb();
return db.users;
}
/**
* Add one user.
*/
async function add(user) {
const db = await MockOrm_1.default.openDb();
user.id = (0, misc_1.getRandomInt)();
db.users.push(user);
return MockOrm_1.default.saveDb(db);
}
/**
* Update a user.
*/
async function update(user) {
const db = await MockOrm_1.default.openDb();
for (let i = 0; i < db.users.length; i++) {
if (db.users[i].id === user.id) {
db.users[i] = user;
return MockOrm_1.default.saveDb(db);
}
}
}
/**
* Delete one user.
*/
async function delete_(id) {
const db = await MockOrm_1.default.openDb();
for (let i = 0; i < db.users.length; i++) {
if (db.users[i].id === id) {
db.users.splice(i, 1);
return MockOrm_1.default.saveDb(db);
}
}
}
// **** Export default **** //
exports.default = {
getOne,
persists,
getAll,
add,
update,
delete: delete_,
};
//# sourceMappingURL=user.repo.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"user.repo.js","sourceRoot":"","sources":["../../src/repos/user.repo.ts"],"names":[],"mappings":";;;;;AACA,yCAA8C;AAC9C,wDAA4B;AAG5B,yBAAyB;AAEzB;;GAEG;AACH,KAAK,UAAU,MAAM,CAAC,KAAa;IACjC,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,QAAQ,CAAC,EAAU;IAChC,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,MAAM;IACnB,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,CAAC,KAAK,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,GAAG,CAAC,IAAW;IAC5B,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,GAAG,IAAA,mBAAY,GAAE,CAAC;IACzB,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,OAAO,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,MAAM,CAAC,IAAW;IAC/B,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;YAC/B,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YACnB,OAAO,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,OAAO,CAAC,EAAU;IAC/B,MAAM,EAAE,GAAG,MAAM,iBAAG,CAAC,MAAM,EAAE,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YAC1B,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACtB,OAAO,iBAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;AACH,CAAC;AAGD,8BAA8B;AAE9B,kBAAe;IACb,MAAM;IACN,QAAQ;IACR,MAAM;IACN,GAAG;IACH,MAAM;IACN,MAAM,EAAE,OAAO;CACP,CAAC"}

24
lib/routes/api.js Normal file
View File

@@ -0,0 +1,24 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const jet_validator_1 = __importDefault(require("jet-validator"));
const Paths_1 = __importDefault(require("./constants/Paths"));
const user_model_1 = __importDefault(require("@src/models/user.model"));
const user_controller_1 = __importDefault(require("@src/controllers/user.controller"));
// **** Variables **** //
const apiRouter = (0, express_1.Router)(), validate = (0, jet_validator_1.default)();
// ** Add UserRouter ** //
const userRouter = (0, express_1.Router)();
// Get all users
userRouter.get(Paths_1.default.Users.Get, user_controller_1.default.getAll.bind(user_controller_1.default));
userRouter.post(Paths_1.default.Users.Add, validate(['user', user_model_1.default.isUser]), user_controller_1.default.add.bind(user_controller_1.default));
userRouter.put(Paths_1.default.Users.Update, validate(['user', user_model_1.default.isUser]), user_controller_1.default.update.bind(user_controller_1.default));
userRouter.delete(Paths_1.default.Users.Delete, validate(['id', 'number', 'params']), user_controller_1.default.delete.bind(user_controller_1.default));
// Add UserRouter
apiRouter.use(Paths_1.default.Users.Base, userRouter);
// **** Export default **** //
exports.default = apiRouter;
//# sourceMappingURL=api.js.map

1
lib/routes/api.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/routes/api.ts"],"names":[],"mappings":";;;;;AAAA,qCAAiC;AACjC,kEAAyC;AAEzC,8DAAsC;AACtC,wEAA0C;AAC1C,uFAA8D;AAG9D,yBAAyB;AAEzB,MAAM,SAAS,GAAG,IAAA,gBAAM,GAAE,EACxB,QAAQ,GAAG,IAAA,uBAAY,GAAE,CAAC;AAG5B,0BAA0B;AAE1B,MAAM,UAAU,GAAG,IAAA,gBAAM,GAAE,CAAC;AAE5B,gBAAgB;AAChB,UAAU,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,GAAG,EAAE,yBAAc,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAc,CAAC,CAAC,CAAC;AAC5E,UAAU,CAAC,IAAI,CACb,eAAK,CAAC,KAAK,CAAC,GAAG,EACf,QAAQ,CAAC,CAAC,MAAM,EAAE,oBAAI,CAAC,MAAM,CAAC,CAAC,EAC/B,yBAAc,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAc,CAAC,CACxC,CAAC;AACF,UAAU,CAAC,GAAG,CACZ,eAAK,CAAC,KAAK,CAAC,MAAM,EAClB,QAAQ,CAAC,CAAC,MAAM,EAAE,oBAAI,CAAC,MAAM,CAAC,CAAC,EAC/B,yBAAc,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAc,CAAC,CAC3C,CAAC;AACF,UAAU,CAAC,MAAM,CACf,eAAK,CAAC,KAAK,CAAC,MAAM,EAClB,QAAQ,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,EACpC,yBAAc,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAc,CAAC,CAC3C,CAAC;AAEF,iBAAiB;AACjB,SAAS,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAG5C,8BAA8B;AAE9B,kBAAe,SAAS,CAAC"}

15
lib/routes/board.route.js Normal file
View File

@@ -0,0 +1,15 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const board_controller_1 = __importDefault(require("../controllers/board.controller"));
const router = (0, express_1.Router)();
router.get('/user/:userId', board_controller_1.default.getBoardsByUserId);
router.get('/user/:userId/:boardId', board_controller_1.default.getBoardByUserId);
router.post('/user/:userId', board_controller_1.default.createBoard);
router.put('/user/:userId/:boardId', board_controller_1.default.updateBoard);
router.delete('/user/:userId/:boardId', board_controller_1.default.deleteBoard);
exports.default = router;
//# sourceMappingURL=board.route.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"board.route.js","sourceRoot":"","sources":["../../src/routes/board.route.ts"],"names":[],"mappings":";;;;;AACA,qCAAiC;AACjC,uFAA8D;AAE9D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,0BAAe,CAAC,iBAAiB,CAAC,CAAC;AAC/D,MAAM,CAAC,GAAG,CAAC,wBAAwB,EAAE,0BAAe,CAAC,gBAAgB,CAAC,CAAC;AACvE,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,0BAAe,CAAC,WAAW,CAAC,CAAC;AAC1D,MAAM,CAAC,GAAG,CAAC,wBAAwB,EAAE,0BAAe,CAAC,WAAW,CAAC,CAAC;AAClE,MAAM,CAAC,MAAM,CAAC,wBAAwB,EAAE,0BAAe,CAAC,WAAW,CAAC,CAAC;AAErE,kBAAe,MAAM,CAAC"}

View File

@@ -0,0 +1,30 @@
"use strict";
/**
* Convert paths to full paths.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const Paths_1 = __importDefault(require("./Paths"));
/**
* The recursive function.
*/
function getFullPaths(parent, baseUrl) {
const url = (baseUrl + parent.Base), keys = Object.keys(parent), retVal = { Base: url };
// Iterate keys
for (const key of keys) {
const pval = parent[key];
if (key !== 'Base' && typeof pval === 'string') {
retVal[key] = (url + pval);
}
else if (typeof pval === 'object') {
retVal[key] = getFullPaths(pval, url);
}
}
// Return
return retVal;
}
// **** Export default **** //
exports.default = getFullPaths(Paths_1.default, '');
//# sourceMappingURL=FullPaths.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FullPaths.js","sourceRoot":"","sources":["../../../src/routes/constants/FullPaths.ts"],"names":[],"mappings":";AACA;;GAEG;;;;;AAEH,oDAAwC;AAQxC;;GAEG;AACH,SAAS,YAAY,CACnB,MAAgB,EAChB,OAAe;IAEf,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,EACjC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAC1B,MAAM,GAAa,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IACnC,eAAe;IACf,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,GAAG,KAAK,MAAM,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QAC7B,CAAC;aAAM,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,SAAS;IACT,OAAO,MAAM,CAAC;AAChB,CAAC;AAGD,8BAA8B;AAE9B,kBAAe,YAAY,CAAC,eAAK,EAAE,EAAE,CAAW,CAAC"}

View File

@@ -0,0 +1,17 @@
"use strict";
/**
* Express router paths go here.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const Paths = {
Base: '/api',
Users: {
Base: '/users',
Get: '/all',
Add: '/add',
Update: '/update',
Delete: '/delete/:id',
},
};
exports.default = Paths;
//# sourceMappingURL=Paths.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Paths.js","sourceRoot":"","sources":["../../../src/routes/constants/Paths.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAKH,MAAM,KAAK,GAAG;IACZ,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE;QACL,IAAI,EAAE,QAAQ;QACd,GAAG,EAAE,MAAM;QACX,GAAG,EAAE,MAAM;QACX,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,aAAa;KACtB;CACF,CAAC;AAMF,kBAAe,KAAe,CAAC"}

15
lib/routes/task.route.js Normal file
View File

@@ -0,0 +1,15 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const task_controller_1 = __importDefault(require("../controllers/task.controller"));
const router = (0, express_1.Router)();
router.get('/board/:boardId', task_controller_1.default.getTasksByBoardId);
router.get('/board/:boardId/:taskId', task_controller_1.default.getTaskByBoardId);
router.post('/board/:boardId', task_controller_1.default.createTask);
router.put('/board/:boardId/:taskId', task_controller_1.default.updateTask);
router.delete('/board/:boardId/:taskId', task_controller_1.default.deleteTask);
exports.default = router;
//# sourceMappingURL=task.route.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"task.route.js","sourceRoot":"","sources":["../../src/routes/task.route.ts"],"names":[],"mappings":";;;;;AACA,qCAAiC;AACjC,qFAA4D;AAE5D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,yBAAc,CAAC,iBAAiB,CAAC,CAAC;AAChE,MAAM,CAAC,GAAG,CAAC,yBAAyB,EAAE,yBAAc,CAAC,gBAAgB,CAAC,CAAC;AACvE,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE,yBAAc,CAAC,UAAU,CAAC,CAAC;AAC1D,MAAM,CAAC,GAAG,CAAC,yBAAyB,EAAE,yBAAc,CAAC,UAAU,CAAC,CAAC;AACjE,MAAM,CAAC,MAAM,CAAC,yBAAyB,EAAE,yBAAc,CAAC,UAAU,CAAC,CAAC;AAEpE,kBAAe,MAAM,CAAC"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=misc.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"misc.js","sourceRoot":"","sources":["../../../../src/routes/types/express/misc.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/routes/types/types.ts"],"names":[],"mappings":""}

14
lib/routes/user.route.js Normal file
View File

@@ -0,0 +1,14 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = require("express");
const user_controller_1 = __importDefault(require("../controllers/user.controller"));
const router = (0, express_1.Router)();
router.get('/', user_controller_1.default.getAll);
router.post('/', user_controller_1.default.add);
router.put('/', user_controller_1.default.update);
router.delete('/:id', user_controller_1.default.delete);
exports.default = router;
//# sourceMappingURL=user.route.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"user.route.js","sourceRoot":"","sources":["../../src/routes/user.route.ts"],"names":[],"mappings":";;;;;AAAA,qCAAiC;AACjC,qFAA4D;AAE5D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,yBAAc,CAAC,MAAM,CAAC,CAAC;AACvC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,yBAAc,CAAC,GAAG,CAAC,CAAC;AACrC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,yBAAc,CAAC,MAAM,CAAC,CAAC;AACvC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,yBAAc,CAAC,MAAM,CAAC,CAAC;AAE7C,kBAAe,MAAM,CAAC"}

69
lib/server.js Normal file
View File

@@ -0,0 +1,69 @@
"use strict";
/**
* Setup express server.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const cookie_parser_1 = __importDefault(require("cookie-parser"));
const morgan_1 = __importDefault(require("morgan"));
const path_1 = __importDefault(require("path"));
const helmet_1 = __importDefault(require("helmet"));
const express_1 = __importDefault(require("express"));
const jet_logger_1 = __importDefault(require("jet-logger"));
require("express-async-errors");
const api_1 = __importDefault(require("@src/routes/api"));
const Paths_1 = __importDefault(require("@src/routes/constants/Paths"));
const EnvVars_1 = __importDefault(require("@src/constants/EnvVars"));
const HttpStatusCodes_1 = __importDefault(require("@src/constants/HttpStatusCodes"));
const misc_1 = require("@src/constants/misc");
const classes_1 = require("@src/other/classes");
// **** Variables **** //
const app = (0, express_1.default)();
// **** Setup **** //
// Basic middleware
app.use(express_1.default.json());
app.use(express_1.default.urlencoded({ extended: true }));
app.use((0, cookie_parser_1.default)(EnvVars_1.default.CookieProps.Secret));
// Show routes called in console during development
if (EnvVars_1.default.NodeEnv === misc_1.NodeEnvs.Dev) {
app.use((0, morgan_1.default)('dev'));
}
// Security
if (EnvVars_1.default.NodeEnv === misc_1.NodeEnvs.Production) {
app.use((0, helmet_1.default)());
}
// Add APIs, must be after middleware
app.use(Paths_1.default.Base, api_1.default);
// Add error handler
app.use((err, _, res,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
next) => {
if (EnvVars_1.default.NodeEnv !== misc_1.NodeEnvs.Test) {
jet_logger_1.default.err(err, true);
}
let status = HttpStatusCodes_1.default.BAD_REQUEST;
if (err instanceof classes_1.RouteError) {
status = err.status;
}
return res.status(status).json({ error: err.message });
});
// ** Front-End Content ** //
// Set views directory (html)
const viewsDir = path_1.default.join(__dirname, 'views');
app.set('views', viewsDir);
// Set static directory (js and css).
const staticDir = path_1.default.join(__dirname, 'public');
app.use(express_1.default.static(staticDir));
// Nav to users pg by default
app.get('/', (_, res) => {
return res.redirect('/users');
});
// Redirect to login if not logged in.
app.get('/users', (_, res) => {
return res.sendFile('users.html', { root: viewsDir });
});
// **** Export default **** //
exports.default = app;
//# sourceMappingURL=server.js.map

1
lib/server.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;AAEH,kEAAyC;AACzC,oDAA4B;AAC5B,gDAAwB;AACxB,oDAA4B;AAC5B,sDAAmE;AACnE,4DAAgC;AAEhC,gCAA8B;AAE9B,0DAAyC;AACzC,wEAAgD;AAEhD,qEAA6C;AAC7C,qFAA6D;AAE7D,8CAA+C;AAC/C,gDAAgD;AAGhD,yBAAyB;AAEzB,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;AAGtB,qBAAqB;AAErB,mBAAmB;AACnB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACxB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,UAAU,CAAC,EAAC,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC;AAC9C,GAAG,CAAC,GAAG,CAAC,IAAA,uBAAY,EAAC,iBAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;AAElD,mDAAmD;AACnD,IAAI,iBAAO,CAAC,OAAO,KAAK,eAAQ,CAAC,GAAG,EAAE,CAAC;IACrC,GAAG,CAAC,GAAG,CAAC,IAAA,gBAAM,EAAC,KAAK,CAAC,CAAC,CAAC;AACzB,CAAC;AAED,WAAW;AACX,IAAI,iBAAO,CAAC,OAAO,KAAK,eAAQ,CAAC,UAAU,EAAE,CAAC;IAC5C,GAAG,CAAC,GAAG,CAAC,IAAA,gBAAM,GAAE,CAAC,CAAC;AACpB,CAAC;AAED,qCAAqC;AACrC,GAAG,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,EAAE,aAAU,CAAC,CAAC;AAEhC,oBAAoB;AACpB,GAAG,CAAC,GAAG,CAAC,CACN,GAAU,EACV,CAAU,EACV,GAAa;AACb,6DAA6D;AAC7D,IAAkB,EAClB,EAAE;IACF,IAAI,iBAAO,CAAC,OAAO,KAAK,eAAQ,CAAC,IAAI,EAAE,CAAC;QACtC,oBAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxB,CAAC;IACD,IAAI,MAAM,GAAG,yBAAe,CAAC,WAAW,CAAC;IACzC,IAAI,GAAG,YAAY,oBAAU,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IACtB,CAAC;IACD,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC;AAGH,6BAA6B;AAE7B,6BAA6B;AAC7B,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAC/C,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAE3B,qCAAqC;AACrC,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACjD,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;AAEnC,6BAA6B;AAC7B,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAU,EAAE,GAAa,EAAE,EAAE;IACzC,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC,CAAC,CAAC;AAEH,sCAAsC;AACtC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAU,EAAE,GAAa,EAAE,EAAE;IAC9C,OAAO,GAAG,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAGH,8BAA8B;AAE9B,kBAAe,GAAG,CAAC"}

View File

@@ -0,0 +1,45 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Errors = void 0;
const user_repo_1 = __importDefault(require("@src/repos/user.repo"));
const PwdUtil_1 = __importDefault(require("@src/util/PwdUtil"));
const misc_1 = require("@src/util/misc");
const HttpStatusCodes_1 = __importDefault(require("@src/constants/HttpStatusCodes"));
const classes_1 = require("@src/other/classes");
// **** Variables **** //
// Errors
exports.Errors = {
Unauth: 'Unauthorized',
EmailNotFound(email) {
return `User with email "${email}" not found`;
},
};
// **** Functions **** //
/**
* Login a user.
*/
async function login(email, password) {
var _a;
// Fetch user
const user = await user_repo_1.default.getOne(email);
if (!user) {
throw new classes_1.RouteError(HttpStatusCodes_1.default.UNAUTHORIZED, exports.Errors.EmailNotFound(email));
}
// Check password
const hash = ((_a = user.pwdHash) !== null && _a !== void 0 ? _a : ''), pwdPassed = await PwdUtil_1.default.compare(password, hash);
if (!pwdPassed) {
// If password failed, wait 500ms this will increase security
await (0, misc_1.tick)(500);
throw new classes_1.RouteError(HttpStatusCodes_1.default.UNAUTHORIZED, exports.Errors.Unauth);
}
// Return
return user;
}
// **** Export default **** //
exports.default = {
login,
};
//# sourceMappingURL=AuthService.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"AuthService.js","sourceRoot":"","sources":["../../src/services/AuthService.ts"],"names":[],"mappings":";;;;;;AAAA,qEAA4C;AAE5C,gEAAwC;AACxC,yCAAsC;AAEtC,qFAA6D;AAC7D,gDAAgD;AAIhD,yBAAyB;AAEzB,SAAS;AACI,QAAA,MAAM,GAAG;IACpB,MAAM,EAAE,cAAc;IACtB,aAAa,CAAC,KAAa;QACzB,OAAO,oBAAoB,KAAK,aAAa,CAAC;IAChD,CAAC;CACO,CAAC;AAGX,yBAAyB;AAEzB;;GAEG;AACH,KAAK,UAAU,KAAK,CAAC,KAAa,EAAE,QAAgB;;IAClD,aAAa;IACb,MAAM,IAAI,GAAG,MAAM,mBAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,oBAAU,CAClB,yBAAe,CAAC,YAAY,EAC5B,cAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAC5B,CAAC;IACJ,CAAC;IACD,iBAAiB;IACjB,MAAM,IAAI,GAAG,CAAC,MAAA,IAAI,CAAC,OAAO,mCAAI,EAAE,CAAC,EAC/B,SAAS,GAAG,MAAM,iBAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACpD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,6DAA6D;QAC7D,MAAM,IAAA,WAAI,EAAC,GAAG,CAAC,CAAC;QAChB,MAAM,IAAI,oBAAU,CAClB,yBAAe,CAAC,YAAY,EAC5B,cAAM,CAAC,MAAM,CACd,CAAC;IACJ,CAAC;IACD,SAAS;IACT,OAAO,IAAI,CAAC;AACd,CAAC;AAGD,8BAA8B;AAE9B,kBAAe;IACb,KAAK;CACG,CAAC"}

View File

@@ -0,0 +1,54 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.USER_NOT_FOUND_ERR = void 0;
const user_repo_1 = __importDefault(require("@src/repos/user.repo"));
const classes_1 = require("@src/other/classes");
const HttpStatusCodes_1 = __importDefault(require("@src/constants/HttpStatusCodes"));
// **** Variables **** //
exports.USER_NOT_FOUND_ERR = 'User not found';
// **** Functions **** //
/**
* Get all users.
*/
function getAll() {
return user_repo_1.default.getAll();
}
/**
* Add one user.
*/
function addOne(user) {
return user_repo_1.default.add(user);
}
/**
* Update one user.
*/
async function updateOne(user) {
const persists = await user_repo_1.default.persists(user.id);
if (!persists) {
throw new classes_1.RouteError(HttpStatusCodes_1.default.NOT_FOUND, exports.USER_NOT_FOUND_ERR);
}
// Return user
return user_repo_1.default.update(user);
}
/**
* Delete a user by their id.
*/
async function _delete(id) {
const persists = await user_repo_1.default.persists(id);
if (!persists) {
throw new classes_1.RouteError(HttpStatusCodes_1.default.NOT_FOUND, exports.USER_NOT_FOUND_ERR);
}
// Delete user
return user_repo_1.default.delete(id);
}
// **** Export default **** //
exports.default = {
getAll,
addOne,
updateOne,
delete: _delete,
};
//# sourceMappingURL=UserService.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"UserService.js","sourceRoot":"","sources":["../../src/services/UserService.ts"],"names":[],"mappings":";;;;;;AAAA,qEAA4C;AAE5C,gDAAgD;AAChD,qFAA6D;AAG7D,yBAAyB;AAEZ,QAAA,kBAAkB,GAAG,gBAAgB,CAAC;AAGnD,yBAAyB;AAEzB;;GAEG;AACH,SAAS,MAAM;IACb,OAAO,mBAAQ,CAAC,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,SAAS,MAAM,CAAC,IAAW;IACzB,OAAO,mBAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,SAAS,CAAC,IAAW;IAClC,MAAM,QAAQ,GAAG,MAAM,mBAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,oBAAU,CAClB,yBAAe,CAAC,SAAS,EACzB,0BAAkB,CACnB,CAAC;IACJ,CAAC;IACD,cAAc;IACd,OAAO,mBAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,OAAO,CAAC,EAAU;IAC/B,MAAM,QAAQ,GAAG,MAAM,mBAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC7C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,oBAAU,CAClB,yBAAe,CAAC,SAAS,EACzB,0BAAkB,CACnB,CAAC;IACJ,CAAC;IACD,cAAc;IACd,OAAO,mBAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC7B,CAAC;AAGD,8BAA8B;AAE9B,kBAAe;IACb,MAAM;IACN,MAAM;IACN,SAAS;IACT,MAAM,EAAE,OAAO;CACP,CAAC"}

34
lib/util/PwdUtil.js Normal file
View File

@@ -0,0 +1,34 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bcrypt_1 = __importDefault(require("bcrypt"));
// **** Variables **** //
const SALT_ROUNDS = 12;
// **** Functions **** //
/**
* Get a hash from the password.
*/
function getHash(pwd) {
return bcrypt_1.default.hash(pwd, SALT_ROUNDS);
}
/**
* Useful for testing.
*/
function hashSync(pwd) {
return bcrypt_1.default.hashSync(pwd, SALT_ROUNDS);
}
/**
* See if a password passes the hash.
*/
function compare(pwd, hash) {
return bcrypt_1.default.compare(pwd, hash);
}
// **** Export Default **** //
exports.default = {
getHash,
hashSync,
compare,
};
//# sourceMappingURL=PwdUtil.js.map

1
lib/util/PwdUtil.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"PwdUtil.js","sourceRoot":"","sources":["../../src/util/PwdUtil.ts"],"names":[],"mappings":";;;;;AAAA,oDAA4B;AAG5B,yBAAyB;AAEzB,MAAM,WAAW,GAAG,EAAE,CAAC;AAGvB,yBAAyB;AAEzB;;GAEG;AACH,SAAS,OAAO,CAAC,GAAW;IAC1B,OAAO,gBAAM,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,GAAW;IAC3B,OAAO,gBAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,GAAW,EAAE,IAAY;IACxC,OAAO,gBAAM,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACnC,CAAC;AAGD,8BAA8B;AAE9B,kBAAe;IACb,OAAO;IACP,QAAQ;IACR,OAAO;CACC,CAAC"}

74
lib/util/SessionUtil.js Normal file
View File

@@ -0,0 +1,74 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const HttpStatusCodes_1 = __importDefault(require("@src/constants/HttpStatusCodes"));
const classes_1 = require("@src/other/classes");
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const EnvVars_1 = __importDefault(require("../constants/EnvVars"));
// **** Variables **** //
// Errors
const Errors = {
ParamFalsey: 'Param is falsey',
Validation: 'JSON-web-token validation failed.',
};
// Options
const Options = {
expiresIn: EnvVars_1.default.Jwt.Exp,
};
// **** Functions **** //
/**
* Get session data from request object (i.e. ISessionUser)
*/
function getSessionData(req) {
const { Key } = EnvVars_1.default.CookieProps, jwt = req.signedCookies[Key];
return _decode(jwt);
}
/**
* Add a JWT to the response
*/
async function addSessionData(res, data) {
if (!res || !data) {
throw new classes_1.RouteError(HttpStatusCodes_1.default.BAD_REQUEST, Errors.ParamFalsey);
}
// Setup JWT
const jwt = await _sign(data), { Key, Options } = EnvVars_1.default.CookieProps;
// Return
return res.cookie(Key, jwt, Options);
}
/**
* Remove cookie
*/
function clearCookie(res) {
const { Key, Options } = EnvVars_1.default.CookieProps;
return res.clearCookie(Key, Options);
}
// **** Helper Functions **** //
/**
* Encrypt data and return jwt.
*/
function _sign(data) {
return new Promise((res, rej) => {
jsonwebtoken_1.default.sign(data, EnvVars_1.default.Jwt.Secret, { expiresIn: '1h' }, (err, token) => {
return err ? rej(err) : res(token || '');
});
});
}
/**
* Decrypt JWT and extract client data.
*/
function _decode(jwt) {
return new Promise((res, rej) => {
jsonwebtoken_1.default.verify(jwt, EnvVars_1.default.Jwt.Secret, undefined, (err, decoded) => {
return err ? rej(Errors.Validation) : res(decoded);
});
});
}
// **** Export default **** //
exports.default = {
addSessionData,
getSessionData,
clearCookie,
};
//# sourceMappingURL=SessionUtil.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SessionUtil.js","sourceRoot":"","sources":["../../src/util/SessionUtil.ts"],"names":[],"mappings":";;;;;AAEA,qFAA6D;AAC7D,gDAAgD;AAChD,gEAAwC;AAExC,mEAA2C;AAG3C,yBAAyB;AAEzB,SAAS;AACT,MAAM,MAAM,GAAG;IACb,WAAW,EAAE,iBAAiB;IAC9B,UAAU,EAAE,mCAAmC;CACvC,CAAC;AAEX,UAAU;AACV,MAAM,OAAO,GAAG;IACd,SAAS,EAAE,iBAAO,CAAC,GAAG,CAAC,GAAG;CAC3B,CAAC;AAGF,yBAAyB;AAEzB;;GAEG;AACH,SAAS,cAAc,CAAI,GAAY;IACrC,MAAM,EAAE,GAAG,EAAE,GAAG,iBAAO,CAAC,WAAW,EACjC,GAAG,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,cAAc,CAC3B,GAAa,EACb,IAAqB;IAErB,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,oBAAU,CAAC,yBAAe,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IACxE,CAAC;IACD,YAAY;IACZ,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,EAC3B,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,iBAAO,CAAC,WAAW,CAAC;IACzC,SAAS;IACT,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,GAAa;IAChC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,iBAAO,CAAC,WAAW,CAAC;IAC7C,OAAO,GAAG,CAAC,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC;AAGD,gCAAgC;AAEhC;;GAEG;AACH,SAAS,KAAK,CAAC,IAA8B;IAC3C,OAAO,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9B,sBAAY,CAAC,IAAI,CACf,IAAI,EACJ,iBAAO,CAAC,GAAG,CAAC,MAAM,EAClB,EAAE,SAAS,EAAE,IAAI,EAAE,EACnB,CAAC,GAAiB,EAAE,KAAc,EAAE,EAAE;YACpC,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAI,GAAW;IAC7B,OAAO,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC9B,sBAAY,CAAC,MAAM,CACjB,GAAG,EACH,iBAAO,CAAC,GAAG,CAAC,MAAM,EAClB,SAAS,EACT,CAAC,GAAiB,EAAE,OAA0C,EAAE,EAAE;YAChE,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAY,CAAC,CAAC;QAC1D,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAGD,8BAA8B;AAE9B,kBAAe;IACb,cAAc;IACd,cAAc;IACd,WAAW;CACH,CAAC"}

24
lib/util/misc.js Normal file
View File

@@ -0,0 +1,24 @@
"use strict";
/**
* Miscellaneous shared functions go here.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRandomInt = getRandomInt;
exports.tick = tick;
/**
* Get a random number between 1 and 1,000,000,000,000
*/
function getRandomInt() {
return Math.floor(Math.random() * 1000000000000);
}
/**
* Wait for a certain number of milliseconds.
*/
function tick(milliseconds) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, milliseconds);
});
}
//# sourceMappingURL=misc.js.map

1
lib/util/misc.js.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"misc.js","sourceRoot":"","sources":["../../src/util/misc.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAMH,oCAEC;AAKD,oBAMC;AAhBD;;GAEG;AACH,SAAgB,YAAY;IAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,aAAiB,CAAC,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,SAAgB,IAAI,CAAC,YAAoB;IACvC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,UAAU,CAAC,GAAG,EAAE;YACd,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,YAAY,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC"}

991
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
{
"name": "Seer",
"name": "ara-kanban-service",
"version": "0.0.0",
"scripts": {
"build": "npx ts-node build.ts",
"lint": "npx eslint --ext .ts src/",
"lint:tests": "npx eslint --ext .ts spec/",
"start": "node -r module-alias/register ./dist --env=production",
"dev": "nodemon",
"dev": "PORT=5000 nodemon",
"test": "nodemon --config ./spec/nodemon.json",
"test:no-reloading": "npx ts-node --files -r tsconfig-paths/register ./spec"
},
@@ -27,26 +27,32 @@
"node": ">=8.10.0"
},
"dependencies": {
"@prisma/client": "^6.16.2",
"bcrypt": "^6.0.0",
"cookie-parser": "^1.4.6",
"dotenv": "^16.3.1",
"dotenv": "^16.6.1",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"helmet": "^7.0.0",
"helmet": "^7.2.0",
"inserturlparams": "^1.0.1",
"jet-logger": "^1.3.1",
"jet-validator": "^1.1.1",
"jsonfile": "^6.1.0",
"jsonwebtoken": "^9.0.2",
"module-alias": "^2.2.3",
"morgan": "^1.10.0",
"prisma": "^6.16.2",
"ts-command-line-args": "^2.5.1"
},
"devDependencies": {
"@types/bcrypt": "^6.0.0",
"@types/cookie-parser": "^1.4.3",
"@types/express": "^4.17.17",
"@types/find": "^0.2.1",
"@types/fs-extra": "^11.0.1",
"@types/jasmine": "^4.3.5",
"@types/jsonfile": "^6.1.1",
"@types/jsonwebtoken": "^9.0.10",
"@types/morgan": "^1.9.4",
"@types/node": "^20.4.2",
"@types/supertest": "^2.0.12",
@@ -61,6 +67,6 @@
"supertest": "^6.3.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.6"
"typescript": "^5.9.2"
}
}

15
prisma/schema.prisma Normal file
View File

@@ -0,0 +1,15 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

View File

@@ -4,8 +4,8 @@ import insertUrlParams from 'inserturlparams';
import app from '@src/server';
import UserRepo from '@src/repos/UserRepo';
import User from '@src/models/User';
import UserRepo from '@src/repos/user.repo';
import User from '@src/models/user.model';
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
import { USER_NOT_FOUND_ERR } from '@src/services/UserService';
import FullPaths from '@src/routes/constants/FullPaths';

View File

@@ -1,4 +1,4 @@
import { IUser } from '@src/models/User';
import { IUser } from '@src/models/user.model';
import 'supertest';

View File

@@ -2,11 +2,13 @@
* Environments variables declared here.
*/
import { NodeEnvs } from './misc';
/* eslint-disable node/no-process-env */
export default {
NodeEnv: (process.env.NODE_ENV ?? ''),
NodeEnv: (process.env.NODE_ENV as NodeEnvs ?? ''),
Port: (process.env.PORT ?? 0),
CookieProps: {
Key: 'ExpressGeneratorTs',
@@ -23,6 +25,6 @@ export default {
},
Jwt: {
Secret: (process.env.JWT_SECRET ?? ''),
Exp: (process.env.COOKIE_EXP ?? ''), // exp at the same time as the cookie
Exp: (process.env.COOKIE_EXP && process.env.COOKIE_EXP !== '' ? process.env.COOKIE_EXP : '1h'), // exp at the same time as the cookie
},
} as const;

View File

@@ -0,0 +1,93 @@
import { Request, Response } from 'express';
import BoardRepo from '@src/repos/board.repo';
import { IBoard } from '@src/models/board.model';
class BoardController {
// Get all boards for a user
public getBoardsByUserId = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const userId = +req.params.userId;
const boards = await BoardRepo.getBoardsByUserId(userId);
return res.json({ boards });
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
// Get single board by userId and boardId
public getBoardByUserId = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const userId = +req.params.userId;
const boardId = +req.params.boardId;
const board = await BoardRepo.getBoardByUserId(userId, boardId);
return res.json({ board });
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
// Create board for a user
public createBoard = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const boardData = req.body as IBoard;
if (!boardData.name) {
return res.status(400).json({ error: 'Board name required' });
}
const userId = +req.params.userId;
const board = await BoardRepo.createBoard(userId, boardData);
return res.status(201).json({ board });
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
// Update board for a user
public updateBoard = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const boardData = req.body as IBoard;
if (!boardData.name) {
return res.status(400).json({ error: 'Board name required' });
}
const userId = +req.params.userId;
const boardId = +req.params.boardId;
const board = await BoardRepo.updateBoard(userId, boardId, boardData);
return res.json({ board });
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
// Delete board for a user
public deleteBoard = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const userId = +req.params.userId;
const boardId = +req.params.boardId;
await BoardRepo.deleteBoard(userId, boardId);
return res.status(204).end();
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
}
export default new BoardController();

View File

@@ -0,0 +1,95 @@
import { Request, Response } from 'express';
import TaskRepo from '@src/repos/task.repo';
import { ITask } from '@src/models/task.model';
class TaskController {
// Get all tasks for a board
public getTasksByBoardId = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const boardId = +req.params.boardId;
const tasks = await TaskRepo.getTasksByBoardId(boardId);
return res.json({ tasks });
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
// Get single task by boardId and taskId
public getTaskByBoardId = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const boardId = +req.params.boardId;
const taskId = +req.params.taskId;
const task = await TaskRepo.getTaskByBoardId(boardId, taskId);
return res.json({ task });
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
// Create task for a board
public createTask = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
// Dummy form validation
const taskData = req.body as ITask;
if (!taskData.title) {
return res.status(400).json({ error: 'Task title required' });
}
const boardId = +req.params.boardId;
const task = await TaskRepo.createTask(boardId, taskData);
return res.status(201).json({ task });
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
// Update task for a board
public updateTask = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
// Dummy form validation
const taskData = req.body as ITask;
if (!taskData.title) {
return res.status(400).json({ error: 'Task title required' });
}
const boardId = +req.params.boardId;
const taskId = +req.params.taskId;
const task = await TaskRepo.updateTask(boardId, taskId, taskData);
return res.json({ task });
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
// Delete task for a board
public deleteTask = async (req: Request, res: Response) => {
try {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const boardId = +req.params.boardId;
const taskId = +req.params.taskId;
await TaskRepo.deleteTask(boardId, taskId);
return res.status(204).end();
} catch (err) {
return res.status(500).json({ error: 'Internal server error',
details: String(err) });
}
};
}
export default new TaskController();

View File

@@ -0,0 +1,42 @@
import { Request, Response } from 'express';
import UserRepo from '@src/repos/user.repo';
import { IUser } from '@src/models/user.model';
class UserController {
public getAll = async (req: Request, res: Response) => {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const users = await UserRepo.getAll();
return res.status(200).json({ users });
};
public add = async (req: Request, res: Response) => {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const user = req.body.user as IUser;
await UserRepo.add(user);
return res.status(201).end();
};
public update = async (req: Request, res: Response) => {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const user = req.body.user as IUser;
await UserRepo.update(user);
return res.status(200).end();
};
public delete = async (req: Request, res: Response) => {
if (req.headers.authorization !== 'Bearer testtoken') {
return res.status(401).json({ error: 'Unauthorized' });
}
const id = +req.params.id;
await UserRepo.delete(id);
return res.status(200).end();
};
}
export default new UserController();

80
src/models/board.model.ts Normal file
View File

@@ -0,0 +1,80 @@
// **** Variables **** //
const INVALID_CONSTRUCTOR_PARAM = 'nameOrObj arg must be a string or an object with the appropriate board keys.';
// **** Types **** //
export interface IBoard {
id: number;
userId: number;
name: string;
description?: string;
createdAt: string;
updatedAt: string;
}
// **** Functions **** //
/**
* Create new Board.
*/
function new_(
userId?: number,
name?: string,
description?: string,
createdAt?: string,
updatedAt?: string,
id?: number,
): IBoard {
const now = new Date().toISOString();
return {
id: (id ?? -1),
userId: (userId ?? -1),
name: (name ?? ''),
description,
createdAt: (createdAt ?? now),
updatedAt: (updatedAt ?? now),
};
}
/**
* Get board instance from object.
*/
function from(param: object): IBoard {
if (!isBoard(param)) {
throw new Error(INVALID_CONSTRUCTOR_PARAM);
}
const p = param as IBoard;
return new_(p.userId, p.name, p.description, p.createdAt, p.updatedAt, p.id);
}
/**
* See if the param meets criteria to be a board.
*/
function isBoard(arg: unknown): boolean {
return (
!!arg &&
typeof arg === 'object' &&
'id' in arg &&
'userId' in arg &&
'name' in arg &&
'createdAt' in arg &&
'updatedAt' in arg
);
}
// **** Export default **** //
export default {
new: new_,
from,
isBoard,
} as const;
export interface IBoard {
id: number;
userId: number;
name: string;
description?: string;
createdAt: string;
updatedAt: string;
}

85
src/models/task.model.ts Normal file
View File

@@ -0,0 +1,85 @@
// **** Variables **** //
const INVALID_CONSTRUCTOR_PARAM = 'titleOrObj arg must be a string or an object with the appropriate task keys.';
// **** Types **** //
export interface ITask {
id: number;
boardId: number;
title: string;
description?: string;
status: 'todo' | 'in-progress' | 'done';
createdAt: string;
updatedAt: string;
}
// **** Functions **** //
/**
* Create new Task.
*/
function new_(
boardId?: number,
title?: string,
status?: 'todo' | 'in-progress' | 'done',
description?: string,
createdAt?: string,
updatedAt?: string,
id?: number,
): ITask {
const now = new Date().toISOString();
return {
id: (id ?? -1),
boardId: (boardId ?? -1),
title: (title ?? ''),
status: (status ?? 'todo'),
description,
createdAt: (createdAt ?? now),
updatedAt: (updatedAt ?? now),
};
}
/**
* Get task instance from object.
*/
function from(param: object): ITask {
if (!isTask(param)) {
throw new Error(INVALID_CONSTRUCTOR_PARAM);
}
const p = param as ITask;
return new_(p.boardId, p.title, p.status, p.description, p.createdAt, p.updatedAt, p.id);
}
/**
* See if the param meets criteria to be a task.
*/
function isTask(arg: unknown): boolean {
return (
!!arg &&
typeof arg === 'object' &&
'id' in arg &&
'boardId' in arg &&
'title' in arg &&
'status' in arg &&
'createdAt' in arg &&
'updatedAt' in arg
);
}
// **** Export default **** //
export default {
new: new_,
from,
isTask,
} as const;
export interface ITask {
id: number;
boardId: number;
title: string;
description?: string;
status: 'todo' | 'in-progress' | 'done';
createdAt: string;
updatedAt: string;
}

View File

@@ -1,50 +0,0 @@
body {
padding: 100px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
body .users-column {
display: inline-block;
margin-right: 2em;
vertical-align: top;
}
body .users-column .column-header {
padding-bottom: 5px;
font-weight: 700;
font-size: 1.2em;
}
/** Add User Column **/
body .add-user-col input {
margin-bottom: 10px;
}
body .add-user-col #add-user-btn {
margin-top: 2px;
margin-bottom: 10px;
}
body .add-user-col #logout-btn {
margin-top: 25px;
margin-bottom: 10px;
font-size: 15px;
}
/** Users Display Column **/
body .users-column .user-display-ele {
padding-bottom: 10px;
}
body .users-column .user-display-ele button {
margin-top: 2px;
margin-bottom: 10px;
}
body .users-column .user-display-ele .edit-view {
display: none;
}

53
src/repos/BoardRepo.ts Normal file
View File

@@ -0,0 +1,53 @@
import { IBoard } from '@src/models/board.model';
import orm from './MockOrm';
async function getBoardsByUserId(userId: number): Promise<IBoard[]> {
const db = await orm.openDb();
return db.boards.filter((board: IBoard) => board.userId === userId);
}
async function getBoardByUserId(userId: number, boardId: number): Promise<IBoard | null> {
const db = await orm.openDb();
return db.boards.find((board: IBoard) => board.userId === userId && board.id === boardId) || null;
}
async function createBoard(userId: number, board: IBoard): Promise<IBoard> {
const db = await orm.openDb();
board.id = Date.now();
board.userId = userId;
board.createdAt = new Date().toISOString();
board.updatedAt = board.createdAt;
db.boards.push(board);
await orm.saveDb(db);
return board;
}
async function updateBoard(userId: number, boardId: number, boardData: Partial<IBoard>): Promise<IBoard | null> {
const db = await orm.openDb();
const board = db.boards.find((b: IBoard) => b.userId === userId && b.id === boardId);
if (board) {
Object.assign(board, boardData, { updatedAt: new Date().toISOString() });
await orm.saveDb(db);
return board;
}
return null;
}
async function deleteBoard(userId: number, boardId: number): Promise<boolean> {
const db = await orm.openDb();
const idx = db.boards.findIndex((b: IBoard) => b.userId === userId && b.id === boardId);
if (idx !== -1) {
db.boards.splice(idx, 1);
await orm.saveDb(db);
return true;
}
return false;
}
export default {
getBoardsByUserId,
getBoardByUserId,
createBoard,
updateBoard,
deleteBoard,
};

View File

@@ -2,7 +2,9 @@
import jsonfile from 'jsonfile';
import { IUser } from '@src/models/User';
import { IUser } from '@src/models/user.model';
import { IBoard } from '@src/models/board.model';
import { ITask } from '@src/models/task.model';
// **** Variables **** //
@@ -14,6 +16,8 @@ const DB_FILE_NAME = 'database.json';
interface IDb {
users: IUser[];
boards: IBoard[];
tasks: ITask[];
}

53
src/repos/TaskRepo.ts Normal file
View File

@@ -0,0 +1,53 @@
import { ITask } from '@src/models/task.model';
import orm from './MockOrm';
async function getTasksByBoardId(boardId: number): Promise<ITask[]> {
const db = await orm.openDb();
return db.tasks.filter((task: ITask) => task.boardId === boardId);
}
async function getTaskByBoardId(boardId: number, taskId: number): Promise<ITask | null> {
const db = await orm.openDb();
return db.tasks.find((task: ITask) => task.boardId === boardId && task.id === taskId) || null;
}
async function createTask(boardId: number, task: ITask): Promise<ITask> {
const db = await orm.openDb();
task.id = Date.now();
task.boardId = boardId;
task.createdAt = new Date().toISOString();
task.updatedAt = task.createdAt;
db.tasks.push(task);
await orm.saveDb(db);
return task;
}
async function updateTask(boardId: number, taskId: number, taskData: Partial<ITask>): Promise<ITask | null> {
const db = await orm.openDb();
const task = db.tasks.find((t: ITask) => t.boardId === boardId && t.id === taskId);
if (task) {
Object.assign(task, taskData, { updatedAt: new Date().toISOString() });
await orm.saveDb(db);
return task;
}
return null;
}
async function deleteTask(boardId: number, taskId: number): Promise<boolean> {
const db = await orm.openDb();
const idx = db.tasks.findIndex((t: ITask) => t.boardId === boardId && t.id === taskId);
if (idx !== -1) {
db.tasks.splice(idx, 1);
await orm.saveDb(db);
return true;
}
return false;
}
export default {
getTasksByBoardId,
getTaskByBoardId,
createTask,
updateTask,
deleteTask,
};

64
src/repos/board.repo.ts Normal file
View File

@@ -0,0 +1,64 @@
import { IBoard } from '@src/models/board.model';
import orm from './MockOrm';
async function getBoardsByUserId(userId: number): Promise<IBoard[]> {
const db = await orm.openDb();
const boards = Array.isArray(db.boards) ? db.boards : [];
return boards.filter((board: IBoard) => board.userId === userId);
}
async function getBoardByUserId(userId: number, boardId: number):
Promise<IBoard | null> {
const db = await orm.openDb();
const boards = Array.isArray(db.boards) ? db.boards : [];
return boards.find(
(board: IBoard) => board.userId === userId && board.id === boardId,
) || null;
}
async function createBoard(userId: number, board: IBoard): Promise<IBoard> {
const db = await orm.openDb();
if (!Array.isArray(db.boards)) db.boards = [];
board.id = Date.now();
board.userId = userId;
board.createdAt = new Date().toISOString();
board.updatedAt = board.createdAt;
db.boards.push(board);
await orm.saveDb(db);
return board;
}
async function updateBoard(userId: number, boardId: number, boardData:
Partial<IBoard>): Promise<IBoard | null> {
const db = await orm.openDb();
const boards = Array.isArray(db.boards) ? db.boards : [];
const board = boards.find((b: IBoard) => b.userId === userId &&
b.id === boardId);
if (board) {
Object.assign(board, boardData, { updatedAt: new Date().toISOString() });
await orm.saveDb(db);
return board;
}
return null;
}
async function deleteBoard(userId: number, boardId: number): Promise<boolean> {
const db = await orm.openDb();
const boards = Array.isArray(db.boards) ? db.boards : [];
const idx = boards.findIndex((b: IBoard) => b.userId === userId &&
b.id === boardId);
if (idx !== -1) {
boards.splice(idx, 1);
await orm.saveDb(db);
return true;
}
return false;
}
export default {
getBoardsByUserId,
getBoardByUserId,
createBoard,
updateBoard,
deleteBoard,
};

View File

@@ -1 +1 @@
{"users":[{"name":"Sean Maxwell","email":"sean.maxwell@gmail.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":1,"id":159123164363},{"name":"Gordan Freeman","email":"gordan.freeman@halflife.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":0,"id":906524522143},{"name":"John Smith","email":"jsmith@yahoo.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":0,"id":357437875835},{"id":75800032258,"name":"asdf","email":"asdfasdf","role":0}]}
{"users":[{"name":"Sean Maxwell","email":"sean.maxwell@gmail.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":1,"id":159123164363},{"name":"Gordan Freeman","email":"gordan.freeman@halflife.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":0,"id":906524522143},{"name":"John Smith","email":"jsmith@yahoo.com","pwdHash":"$2b$12$1mE2OI9hMS/rgH9Mi0s85OM2V5gzm7aF3gJIWH1y0S1MqVBueyjsy","role":0,"id":357437875835},{"id":75800032258,"name":"asdf","email":"asdfasdf","role":0}],"boards":[{"name":"Project Board","id":1758960982352,"userId":1,"createdAt":"2025-09-27T08:16:22.352Z","updatedAt":"2025-09-27T08:16:22.352Z"}],"tasks":[{"title":"New Task","description":"Task details","status":"todo","id":1758961254717,"boardId":1758960982352,"createdAt":"2025-09-27T08:20:54.717Z","updatedAt":"2025-09-27T08:20:54.717Z"},{"title":"New Task 2","description":"Task details 2","status":"todo","id":1758961267429,"boardId":1758960982352,"createdAt":"2025-09-27T08:21:07.429Z","updatedAt":"2025-09-27T08:21:07.429Z"}]}

63
src/repos/task.repo.ts Normal file
View File

@@ -0,0 +1,63 @@
import { ITask } from '@src/models/task.model';
import orm from './MockOrm';
async function getTasksByBoardId(boardId: number): Promise<ITask[]> {
const db = await orm.openDb();
const tasks = Array.isArray(db.tasks) ? db.tasks : [];
return tasks.filter((task: ITask) => task.boardId === boardId);
}
async function getTaskByBoardId(boardId: number, taskId: number):
Promise<ITask | null> {
const db = await orm.openDb();
const tasks = Array.isArray(db.tasks) ? db.tasks : [];
return tasks.find((task: ITask) => task.boardId === boardId &&
task.id === taskId) || null;
}
async function createTask(boardId: number, task: ITask): Promise<ITask> {
const db = await orm.openDb();
if (!Array.isArray(db.tasks)) db.tasks = [];
task.id = Date.now();
task.boardId = boardId;
task.createdAt = new Date().toISOString();
task.updatedAt = task.createdAt;
db.tasks.push(task);
await orm.saveDb(db);
return task;
}
async function updateTask(boardId: number, taskId: number, taskData:
Partial<ITask>): Promise<ITask | null> {
const db = await orm.openDb();
const tasks = Array.isArray(db.tasks) ? db.tasks : [];
const task = tasks.find((t: ITask) => t.boardId === boardId &&
t.id === taskId);
if (task) {
Object.assign(task, taskData, { updatedAt: new Date().toISOString() });
await orm.saveDb(db);
return task;
}
return null;
}
async function deleteTask(boardId: number, taskId: number): Promise<boolean> {
const db = await orm.openDb();
const tasks = Array.isArray(db.tasks) ? db.tasks : [];
const idx = tasks.findIndex((t: ITask) => t.boardId === boardId &&
t.id === taskId);
if (idx !== -1) {
tasks.splice(idx, 1);
await orm.saveDb(db);
return true;
}
return false;
}
export default {
getTasksByBoardId,
getTaskByBoardId,
createTask,
updateTask,
deleteTask,
};

View File

@@ -1,4 +1,4 @@
import { IUser } from '@src/models/User';
import { IUser } from '@src/models/user.model';
import { getRandomInt } from '@src/util/misc';
import orm from './MockOrm';

View File

@@ -1,53 +0,0 @@
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
import UserService from '@src/services/UserService';
import { IUser } from '@src/models/User';
import { IReq, IRes } from './types/express/misc';
// **** Functions **** //
/**
* Get all users.
*/
async function getAll(_: IReq, res: IRes) {
const users = await UserService.getAll();
return res.status(HttpStatusCodes.OK).json({ users });
}
/**
* Add one user.
*/
async function add(req: IReq<{user: IUser}>, res: IRes) {
const { user } = req.body;
await UserService.addOne(user);
return res.status(HttpStatusCodes.CREATED).end();
}
/**
* Update one user.
*/
async function update(req: IReq<{user: IUser}>, res: IRes) {
const { user } = req.body;
await UserService.updateOne(user);
return res.status(HttpStatusCodes.OK).end();
}
/**
* Delete one user.
*/
async function delete_(req: IReq, res: IRes) {
const id = +req.params.id;
await UserService.delete(id);
return res.status(HttpStatusCodes.OK).end();
}
// **** Export default **** //
export default {
getAll,
add,
update,
delete: delete_,
} as const;

View File

@@ -1,52 +0,0 @@
import { Router } from 'express';
import jetValidator from 'jet-validator';
import Paths from './constants/Paths';
import User from '@src/models/User';
import UserRoutes from './UserRoutes';
// **** Variables **** //
const apiRouter = Router(),
validate = jetValidator();
// ** Add UserRouter ** //
const userRouter = Router();
// Get all users
userRouter.get(
Paths.Users.Get,
UserRoutes.getAll,
);
// Add one user
userRouter.post(
Paths.Users.Add,
validate(['user', User.isUser]),
UserRoutes.add,
);
// Update one user
userRouter.put(
Paths.Users.Update,
validate(['user', User.isUser]),
UserRoutes.update,
);
// Delete one user
userRouter.delete(
Paths.Users.Delete,
validate(['id', 'number', 'params']),
UserRoutes.delete,
);
// Add UserRouter
apiRouter.use(Paths.Users.Base, userRouter);
// **** Export default **** //
export default apiRouter;

13
src/routes/board.route.ts Normal file
View File

@@ -0,0 +1,13 @@
import { Router } from 'express';
import BoardController from '../controllers/board.controller';
const router = Router();
router.get('/user/:userId', BoardController.getBoardsByUserId);
router.get('/user/:userId/:boardId', BoardController.getBoardByUserId);
router.post('/user/:userId', BoardController.createBoard);
router.put('/user/:userId/:boardId', BoardController.updateBoard);
router.delete('/user/:userId/:boardId', BoardController.deleteBoard);
export default router;

View File

@@ -1,40 +0,0 @@
/**
* Convert paths to full paths.
*/
import Paths, { TPaths } from './Paths';
interface IPathObj {
Base: string;
[key: string]: string | IPathObj;
}
/**
* The recursive function.
*/
function getFullPaths(
parent: IPathObj,
baseUrl: string,
): IPathObj {
const url = (baseUrl + parent.Base),
keys = Object.keys(parent),
retVal: IPathObj = { Base: url };
// Iterate keys
for (const key of keys) {
const pval = parent[key];
if (key !== 'Base' && typeof pval === 'string') {
retVal[key] = (url + pval);
} else if (typeof pval === 'object') {
retVal[key] = getFullPaths(pval, url);
}
}
// Return
return retVal;
}
// **** Export default **** //
export default getFullPaths(Paths, '') as TPaths;

View File

@@ -1,23 +0,0 @@
/**
* Express router paths go here.
*/
import { Immutable } from '@src/other/types';
const Paths = {
Base: '/api',
Users: {
Base: '/users',
Get: '/all',
Add: '/add',
Update: '/update',
Delete: '/delete/:id',
},
};
// **** Export **** //
export type TPaths = Immutable<typeof Paths>;
export default Paths as TPaths;

12
src/routes/index.ts Normal file
View File

@@ -0,0 +1,12 @@
import { Router } from 'express';
import boardRouter from './board.route';
import taskRouter from './task.route';
import userRouter from './user.route';
const router = Router();
router.use('/boards', boardRouter);
router.use('/tasks', taskRouter);
router.use('/users', userRouter);
export default router;

13
src/routes/task.route.ts Normal file
View File

@@ -0,0 +1,13 @@
import { Router } from 'express';
import TaskController from '../controllers/task.controller';
const router = Router();
router.get('/board/:boardId', TaskController.getTasksByBoardId);
router.get('/board/:boardId/:taskId', TaskController.getTaskByBoardId);
router.post('/board/:boardId', TaskController.createTask);
router.put('/board/:boardId/:taskId', TaskController.updateTask);
router.delete('/board/:boardId/:taskId', TaskController.deleteTask);
export default router;

View File

@@ -1,11 +0,0 @@
import 'express';
// **** Declaration Merging **** //
declare module 'express' {
export interface Request {
signedCookies: Record<string, string>;
}
}

Some files were not shown because too many files have changed in this diff Show More