Compare commits
2 Commits
master
...
dotnetBackend
| Author | SHA1 | Date | |
|---|---|---|---|
| e11af3ad2a | |||
| d0ea12ff8c |
@@ -0,0 +1,48 @@
|
||||
using System.Net;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class CreateComment : SecuredRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> bodyParamNames = ["message", "post"];
|
||||
string user_id = ExtractUserId(request);
|
||||
var bodyParamValues = ExtractBody(request, bodyParamNames);
|
||||
ValidateParams(bodyParamValues);
|
||||
|
||||
bodyParamNames.Add("user_id");
|
||||
bodyParamValues["user_id"] = user_id;
|
||||
|
||||
MySqlCommand cmd = new(CreateInsertQuery("comment", bodyParamNames));
|
||||
|
||||
cmd = AddValuesToCmd(bodyParamValues, cmd);
|
||||
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateParams(Dictionary<string, string> paramsToValidate)
|
||||
{
|
||||
{
|
||||
if (!int.TryParse(paramsToValidate["post"], out int myInt) || myInt < 0)
|
||||
throw new Exception("Incorect post");
|
||||
}
|
||||
if (paramsToValidate["message"].Length > 1000)
|
||||
{
|
||||
throw new Exception("Wrong parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Net;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class CreateEducation : SecuredRoute
|
||||
{
|
||||
private static void ValidateParams(Dictionary<string, string> paramsToValidate)
|
||||
{
|
||||
string format = "yyyy-MM-dd";
|
||||
if (
|
||||
paramsToValidate["school"].Length > 70
|
||||
|| string.IsNullOrEmpty(paramsToValidate["school"])
|
||||
|| paramsToValidate["degree"].Length > 120
|
||||
|| string.IsNullOrEmpty(paramsToValidate["degree"])
|
||||
|| paramsToValidate["field"].Length > 100
|
||||
|| string.IsNullOrEmpty(paramsToValidate["field"])
|
||||
|| !DateTime.TryParseExact(
|
||||
paramsToValidate["from_date"],
|
||||
format,
|
||||
null,
|
||||
System.Globalization.DateTimeStyles.None,
|
||||
out _
|
||||
)
|
||||
|| !DateTime.TryParseExact(
|
||||
paramsToValidate["to_date"],
|
||||
format,
|
||||
null,
|
||||
System.Globalization.DateTimeStyles.None,
|
||||
out _
|
||||
)
|
||||
|| paramsToValidate["description"].Length > 1000
|
||||
)
|
||||
{
|
||||
throw new Exception("Wrong parameters");
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> bodyParamNames =
|
||||
[
|
||||
"school",
|
||||
"degree",
|
||||
"field",
|
||||
"from_date",
|
||||
"to_date",
|
||||
"description",
|
||||
];
|
||||
string user_id = ExtractUserId(request);
|
||||
var bodyParamValues = ExtractBody(request, bodyParamNames);
|
||||
ValidateParams(bodyParamValues);
|
||||
|
||||
bodyParamNames.Add("user_id");
|
||||
bodyParamValues["user_id"] = user_id;
|
||||
|
||||
MySqlCommand cmd = new(CreateInsertQuery("education", bodyParamNames));
|
||||
|
||||
cmd = AddValuesToCmd(bodyParamValues, cmd);
|
||||
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Net;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class CreateExperience : SecuredRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> bodyParamNames =
|
||||
[
|
||||
"job",
|
||||
"company",
|
||||
"location",
|
||||
"from_date",
|
||||
"to_date",
|
||||
"description",
|
||||
];
|
||||
string user_id = ExtractUserId(request);
|
||||
var bodyParamValues = ExtractBody(request, bodyParamNames);
|
||||
ValidateParams(bodyParamValues);
|
||||
|
||||
bodyParamNames.Add("user_id");
|
||||
bodyParamValues["user_id"] = user_id;
|
||||
|
||||
MySqlCommand cmd = new(CreateInsertQuery("experience", bodyParamNames));
|
||||
|
||||
cmd = AddValuesToCmd(bodyParamValues, cmd);
|
||||
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateParams(Dictionary<string, string> paramsToValidate)
|
||||
{
|
||||
string format = "yyyy-MM-dd";
|
||||
if (
|
||||
paramsToValidate["job"].Length > 70
|
||||
|| string.IsNullOrEmpty(paramsToValidate["job"])
|
||||
|| paramsToValidate["company"].Length > 120
|
||||
|| string.IsNullOrEmpty(paramsToValidate["company"])
|
||||
|| paramsToValidate["location"].Length > 100
|
||||
|| string.IsNullOrEmpty(paramsToValidate["location"])
|
||||
|| !DateTime.TryParseExact(
|
||||
paramsToValidate["from_date"],
|
||||
format,
|
||||
null,
|
||||
System.Globalization.DateTimeStyles.None,
|
||||
out _
|
||||
)
|
||||
|| !DateTime.TryParseExact(
|
||||
paramsToValidate["to_date"],
|
||||
format,
|
||||
null,
|
||||
System.Globalization.DateTimeStyles.None,
|
||||
out _
|
||||
)
|
||||
|| paramsToValidate["description"].Length > 1000
|
||||
)
|
||||
{
|
||||
throw new Exception("Wrong parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Net;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class CreatePost : SecuredRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> bodyParamNames = ["message"];
|
||||
string user_id = ExtractUserId(request);
|
||||
var bodyParamValues = ExtractBody(request, bodyParamNames);
|
||||
ValidateParams(bodyParamValues);
|
||||
|
||||
bodyParamNames.Add("user_id");
|
||||
bodyParamValues["user_id"] = user_id;
|
||||
|
||||
MySqlCommand cmd = new(CreateInsertQuery("post", bodyParamNames));
|
||||
|
||||
cmd = AddValuesToCmd(bodyParamValues, cmd);
|
||||
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateParams(Dictionary<string, string> paramsToValidate)
|
||||
{
|
||||
if (paramsToValidate["message"].Length > 1000)
|
||||
{
|
||||
throw new Exception("Wrong parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using System.Net;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class CreateProfile : SecuredRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> bodyParamNames =
|
||||
[
|
||||
"f_name",
|
||||
"l_name",
|
||||
"company",
|
||||
"website",
|
||||
"location",
|
||||
"github",
|
||||
"status",
|
||||
"bio",
|
||||
"skills",
|
||||
"twitter",
|
||||
"facebook",
|
||||
"youtube",
|
||||
"linkedin",
|
||||
"instagram",
|
||||
];
|
||||
string user_id = ExtractUserId(request);
|
||||
var bodyParamValues = ExtractBody(request, bodyParamNames);
|
||||
ValidateParams(bodyParamValues);
|
||||
|
||||
bodyParamNames.Add("user_id");
|
||||
bodyParamValues["user_id"] = user_id;
|
||||
|
||||
MySqlCommand cmd = new(CreateInsertQuery("profile", bodyParamNames));
|
||||
|
||||
cmd = AddValuesToCmd(bodyParamValues, cmd);
|
||||
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateParams(Dictionary<string, string> paramsToValidate)
|
||||
{
|
||||
if (
|
||||
paramsToValidate["f_name"].Length > 30
|
||||
|| string.IsNullOrEmpty(paramsToValidate["f_name"])
|
||||
|| paramsToValidate["l_name"].Length > 30
|
||||
|| string.IsNullOrEmpty(paramsToValidate["l_name"])
|
||||
|| paramsToValidate["company"].Length > 70
|
||||
|| string.IsNullOrEmpty(paramsToValidate["company"])
|
||||
|| paramsToValidate["website"].Length > 120
|
||||
|| paramsToValidate["location"].Length > 100
|
||||
|| string.IsNullOrEmpty(paramsToValidate["location"])
|
||||
|| paramsToValidate["skills"].Length > 300
|
||||
|| paramsToValidate["github"].Length > 120
|
||||
|| paramsToValidate["status"].Length > 20
|
||||
|| string.IsNullOrEmpty(paramsToValidate["status"])
|
||||
|| paramsToValidate["bio"].Length > 1000
|
||||
|| paramsToValidate["twitter"].Length > 100
|
||||
|| paramsToValidate["facebook"].Length > 100
|
||||
|| paramsToValidate["youtube"].Length > 100
|
||||
|| paramsToValidate["linkedin"].Length > 100
|
||||
|| paramsToValidate["instagram"].Length > 100
|
||||
)
|
||||
{
|
||||
throw new Exception("Wrong parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class DeleteComment : DeleteRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteFromDB(request, "comment", ["id"], true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class DeleteEducation : DeleteRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteFromDB(request, "education", ["id"], true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class DeleteExperience : DeleteRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteFromDB(request, "education", ["id"], true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class DeletePost : DeleteRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteFromDB(request, "post", ["id"], true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class DeleteProfile : DeleteRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteFromDB(request, "profile", ["id"], false);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Net;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class DeleteRoute : SecuredRoute
|
||||
{
|
||||
protected static void DeleteFromDB(
|
||||
HttpListenerRequest request,
|
||||
string table,
|
||||
List<string> validParamNames,
|
||||
bool requireId
|
||||
)
|
||||
// TODO should return error when it cant find the comment
|
||||
{
|
||||
// extract userid compare userid to the comment userid
|
||||
string user_id = ExtractUserId(request);
|
||||
var bodyParamValues = ExtractBody(request, validParamNames);
|
||||
|
||||
if (requireId && bodyParamValues["id"] is null)
|
||||
throw new Exception("missing id");
|
||||
|
||||
validParamNames.Add("user_id");
|
||||
bodyParamValues["user_id"] = user_id;
|
||||
table += requireId ? " Where user_id=@user_id;" : " WHERE id=@id AND user_id=@user_id;";
|
||||
MySqlCommand cmd = new("DELETE from " + table);
|
||||
|
||||
cmd = AddValuesToCmd(bodyParamValues, cmd);
|
||||
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
FROM node:20
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY --chown=node:node . .
|
||||
|
||||
RUN npm ci
|
||||
|
||||
ENV DEBUG=express:*
|
||||
|
||||
USER node
|
||||
|
||||
CMD npm start
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MySql.Data.MySqlClient;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class Login : SecuredRoute
|
||||
{
|
||||
private static void ValidateParams(Dictionary<string, string> paramsToValidate)
|
||||
{
|
||||
if (
|
||||
string.IsNullOrEmpty(paramsToValidate["email"])
|
||||
|| string.IsNullOrEmpty(paramsToValidate["password"])
|
||||
)
|
||||
throw new Exception("Invalid parameters");
|
||||
}
|
||||
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> bodyParamNames = ["email", "password"];
|
||||
var bodyParamValues = ExtractBody(request, bodyParamNames);
|
||||
ValidateParams(bodyParamValues);
|
||||
|
||||
string query =
|
||||
@"SELECT id, password FROM user
|
||||
WHERE email=@email;";
|
||||
MySqlCommand cmd = new(query);
|
||||
cmd.Parameters.AddWithValue("@email", bodyParamValues["email"]);
|
||||
|
||||
var userId = ExtractUserIdFromDB(cmd, bodyParamValues["password"]);
|
||||
|
||||
string? jsonResponse = JsonConvert.SerializeObject(GenerateToken(userId));
|
||||
// prepare response
|
||||
SendSuccess(response, jsonResponse);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ExtractUserIdFromDB(MySqlCommand cmd, string password)
|
||||
{
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
cmd.Connection = conn;
|
||||
conn.Open();
|
||||
// execute query and read results
|
||||
MySqlDataReader reader = cmd.ExecuteReader();
|
||||
string? userId = "";
|
||||
string? hashedPass = "";
|
||||
while (reader.Read())
|
||||
{
|
||||
userId = Convert.ToString(reader["id"]);
|
||||
hashedPass = reader.GetString("password");
|
||||
}
|
||||
// check username
|
||||
if (string.IsNullOrEmpty(userId))
|
||||
{
|
||||
throw new Exception("Invalid Username or Password");
|
||||
}
|
||||
//check password
|
||||
if (
|
||||
string.IsNullOrEmpty(password)
|
||||
|| string.IsNullOrEmpty(hashedPass)
|
||||
|| !VerifyPassword(password, hashedPass)
|
||||
)
|
||||
{
|
||||
throw new Exception("Invalid Username or Password");
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
public static string GenerateToken(string user)
|
||||
{
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: "TimeLogServer",
|
||||
audience: "TimeLogWebsite",
|
||||
claims: [new Claim("user", user)],
|
||||
expires: DateTime.Now.AddHours(2),
|
||||
signingCredentials: creds
|
||||
);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
public static bool VerifyPassword(string enteredPassword, string storedHash)
|
||||
{
|
||||
byte[] hashBytes = Convert.FromBase64String(storedHash);
|
||||
|
||||
byte[] salt = new byte[16];
|
||||
Array.Copy(hashBytes, 0, salt, 0, 16);
|
||||
|
||||
using var pbkdf2 = new Rfc2898DeriveBytes(
|
||||
enteredPassword,
|
||||
salt,
|
||||
10000,
|
||||
HashAlgorithmName.SHA256
|
||||
);
|
||||
byte[] newHash = pbkdf2.GetBytes(32);
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if (newHash[i] != hashBytes[i + 16])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace Server;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main()
|
||||
{
|
||||
// create server
|
||||
HttpListener listener = new();
|
||||
// routes need to be added first
|
||||
listener.Prefixes.Add("http://localhost:5000/api/login/");
|
||||
listener.Prefixes.Add("http://localhost:5000/api/register/");
|
||||
listener.Prefixes.Add("http://localhost:5000/api/posts/");
|
||||
listener.Prefixes.Add("http://localhost:5000/api/posts/like/");
|
||||
listener.Prefixes.Add("http://localhost:5000/api/posts/unlike/");
|
||||
listener.Prefixes.Add("http://localhost:5000/api/comment/");
|
||||
listener.Prefixes.Add("http://localhost:5000/api/profile/");
|
||||
listener.Prefixes.Add("http://localhost:5000/api/profile/experience/");
|
||||
listener.Prefixes.Add("http://localhost:5000/api/profile/education/");
|
||||
// listen
|
||||
listener.Start();
|
||||
Console.WriteLine("Server is listening on http://localhost:5000/");
|
||||
while (true)
|
||||
{
|
||||
HttpListenerContext context = listener.GetContext();
|
||||
HttpListenerRequest request = context.Request;
|
||||
HttpListenerResponse response = context.Response;
|
||||
response.Headers.Add("Access-Control-Allow-Origin", "http://localhost:5173");
|
||||
response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
||||
response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
||||
|
||||
// url after localhost:5000/
|
||||
string uri;
|
||||
if (request != null && request.Url != null)
|
||||
uri = request.Url.AbsolutePath;
|
||||
else
|
||||
return;
|
||||
switch (request.HttpMethod)
|
||||
{
|
||||
case "GET":
|
||||
HandleGet(uri, request, response);
|
||||
break;
|
||||
case "POST":
|
||||
HandlePost(uri, request, response);
|
||||
break;
|
||||
case "DELETE":
|
||||
HandleDelete(uri, request, response);
|
||||
break;
|
||||
case "PUT":
|
||||
HandlePut(uri, request, response);
|
||||
break;
|
||||
default:
|
||||
HandleMissingPath(response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandlePut(
|
||||
string uri,
|
||||
HttpListenerRequest request,
|
||||
HttpListenerResponse response
|
||||
)
|
||||
{
|
||||
if (request.HasEntityBody)
|
||||
switch (uri)
|
||||
{
|
||||
case "/api/profile":
|
||||
UpdateProfile.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/education":
|
||||
UpdateEducation.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/experience":
|
||||
UpdateExperience.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/post":
|
||||
UpdatePost.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/comment":
|
||||
UpdateComment.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/posts/like":
|
||||
UpdatePost.HandleLikes(request, response);
|
||||
break;
|
||||
// case "/api/posts/unlike":
|
||||
// RemoveLike.HandleRequest(request, response);
|
||||
// break;
|
||||
default:
|
||||
HandleMissingPath(response);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleMissingPath(response);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleDelete(
|
||||
string uri,
|
||||
HttpListenerRequest request,
|
||||
HttpListenerResponse response
|
||||
)
|
||||
{
|
||||
if (request.HasEntityBody)
|
||||
switch (uri)
|
||||
{
|
||||
case "/api/profile":
|
||||
DeleteProfile.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/profile/education":
|
||||
DeleteEducation.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/profile/experience":
|
||||
DeleteExperience.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/posts":
|
||||
DeletePost.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/comment":
|
||||
DeleteComment.HandleRequest(request, response);
|
||||
break;
|
||||
default:
|
||||
HandleMissingPath(response);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleMissingPath(response);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandlePost(
|
||||
string uri,
|
||||
HttpListenerRequest request,
|
||||
HttpListenerResponse response
|
||||
)
|
||||
{
|
||||
if (request.HasEntityBody)
|
||||
switch (uri)
|
||||
{
|
||||
case "/api/profile":
|
||||
CreateProfile.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/profile/education":
|
||||
CreateEducation.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/profile/experience":
|
||||
CreateExperience.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/posts":
|
||||
CreatePost.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/comment":
|
||||
CreateComment.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/register":
|
||||
Register.HandleRequest(request, response);
|
||||
break;
|
||||
case "/api/login":
|
||||
Login.HandleRequest(request, response);
|
||||
break;
|
||||
default:
|
||||
HandleMissingPath(response);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleMissingPath(response);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleGet(
|
||||
string uri,
|
||||
HttpListenerRequest request,
|
||||
HttpListenerResponse response
|
||||
)
|
||||
{
|
||||
switch (uri)
|
||||
{
|
||||
default:
|
||||
HandleMissingPath(response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleMissingPath(HttpListenerResponse response)
|
||||
{
|
||||
response.StatusCode = 404;
|
||||
string errorMessage = "Not Found";
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(errorMessage);
|
||||
response.ContentType = "text/plain";
|
||||
response.ContentLength64 = buffer.Length;
|
||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class Register : SecuredRoute
|
||||
{
|
||||
private static void ValidateParams(Dictionary<string, string> paramsToValidate)
|
||||
{
|
||||
if (
|
||||
string.IsNullOrEmpty(paramsToValidate["username"])
|
||||
|| paramsToValidate["username"].Length > 30
|
||||
|| paramsToValidate["username"].Length < 4
|
||||
|| string.IsNullOrEmpty(paramsToValidate["email"])
|
||||
|| paramsToValidate["email"].Length > 50
|
||||
|| paramsToValidate["email"].Length < 6
|
||||
|| string.IsNullOrEmpty(paramsToValidate["password"])
|
||||
|| paramsToValidate["password"].Length > 50
|
||||
|| paramsToValidate["password"].Length < 10
|
||||
)
|
||||
{
|
||||
throw new Exception("Wrong parameters");
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> bodyParamNames = ["username", "email", "password"];
|
||||
var bodyParamValues = ExtractBody(request, bodyParamNames);
|
||||
ValidateParams(bodyParamValues);
|
||||
|
||||
MySqlCommand cmd = new(CreateInsertQuery("user", bodyParamNames));
|
||||
bodyParamValues["password"] = HashPassword(bodyParamValues["password"]);
|
||||
cmd = AddValuesToCmd(bodyParamValues, cmd);
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteNonQuery();
|
||||
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static string HashPassword(string password)
|
||||
{
|
||||
byte[] salt = new byte[16];
|
||||
RandomNumberGenerator.Fill(salt);
|
||||
|
||||
using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000, HashAlgorithmName.SHA256);
|
||||
byte[] hash = pbkdf2.GetBytes(32);
|
||||
|
||||
byte[] hashBytes = new byte[48]; // 16 (salt) + 32 (hash)
|
||||
Array.Copy(salt, 0, hashBytes, 0, 16);
|
||||
Array.Copy(hash, 0, hashBytes, 16, 32);
|
||||
|
||||
return Convert.ToBase64String(hashBytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public abstract class Route
|
||||
{
|
||||
public static readonly string connectionString =
|
||||
"server=127.0.0.1;uid=monty;pwd=some_pass;database=devcon";
|
||||
|
||||
public static void SendError(HttpListenerResponse response, Exception ex)
|
||||
{
|
||||
response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
string errorMessage = $"Error: {ex.Message}";
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(errorMessage);
|
||||
response.ContentType = "text/plain";
|
||||
response.ContentLength64 = buffer.Length;
|
||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
response.Close();
|
||||
}
|
||||
|
||||
public static void SendSuccess(HttpListenerResponse response)
|
||||
{
|
||||
response.StatusCode = (int)HttpStatusCode.OK;
|
||||
response.StatusDescription = "Status OK";
|
||||
response.Close();
|
||||
}
|
||||
|
||||
public static void SendSuccess(HttpListenerResponse response, string jsonResponse)
|
||||
{
|
||||
response.StatusCode = (int)HttpStatusCode.OK;
|
||||
response.StatusDescription = "Status OK";
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(jsonResponse);
|
||||
response.ContentType = "application/json";
|
||||
response.ContentLength64 = buffer.Length;
|
||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
response.Close();
|
||||
}
|
||||
|
||||
public static bool ValidateDate(string date)
|
||||
{
|
||||
Regex regex = new(@"^\d{4}-\d{2}-\d{2}$");
|
||||
return regex.IsMatch(date);
|
||||
}
|
||||
|
||||
protected static Dictionary<string, string> ExtractBody(
|
||||
HttpListenerRequest request,
|
||||
List<string> allowedParams
|
||||
)
|
||||
{
|
||||
using StreamReader bodyReader = new(request.InputStream, request.ContentEncoding);
|
||||
JObject bodyJO = JObject.Parse(bodyReader.ReadToEnd());
|
||||
|
||||
Dictionary<string, string> bodyParamValues = [];
|
||||
foreach (var prop in bodyJO.Properties())
|
||||
{
|
||||
if (allowedParams.Contains(prop.Name))
|
||||
bodyParamValues[prop.Name] = prop.Value.ToString();
|
||||
}
|
||||
return bodyParamValues;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class SecuredRoute : Route
|
||||
{
|
||||
protected static readonly string secretKey =
|
||||
"stronk-key-much-sercret-much-more-stronk-stronk-key-much-sercret-much-more-stronk";
|
||||
protected delegate void DelegateValidate(Dictionary<string, string> bodyparamValues);
|
||||
|
||||
protected static string ExtractUserId(HttpListenerRequest request)
|
||||
{
|
||||
var headers = request.Headers;
|
||||
string token = headers["token"] ?? "";
|
||||
string? usernameClaim = GetUserFromToken(token);
|
||||
if (
|
||||
!string.IsNullOrEmpty(token)
|
||||
&& !ValidateToken(token)
|
||||
&& string.IsNullOrEmpty(usernameClaim)
|
||||
)
|
||||
return "";
|
||||
else
|
||||
return usernameClaim;
|
||||
}
|
||||
|
||||
protected static MySqlCommand AddValuesToCmd(
|
||||
Dictionary<string, string> values,
|
||||
MySqlCommand cmd
|
||||
)
|
||||
{
|
||||
foreach (var item in values)
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@" + item.Key, item.Value);
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
// create an insert route and move this func there
|
||||
protected static string CreateInsertQuery(string table, List<string> valuesToAdd)
|
||||
{
|
||||
string query =
|
||||
"INSERT INTO "
|
||||
+ table
|
||||
+ "("
|
||||
+ string.Join(",", valuesToAdd)
|
||||
+ ") VALUES(@"
|
||||
+ string.Join(",@", valuesToAdd)
|
||||
+ ");";
|
||||
return query;
|
||||
}
|
||||
|
||||
private static bool ValidateToken(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var validationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidIssuer = "TimeLogServer",
|
||||
ValidAudience = "TimeLogWebsite",
|
||||
IssuerSigningKey = key,
|
||||
};
|
||||
|
||||
var principal = tokenHandler.ValidateToken(
|
||||
token,
|
||||
validationParameters,
|
||||
out SecurityToken validatedToken
|
||||
);
|
||||
return validatedToken != null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetUserFromToken(string token)
|
||||
{
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwtToken = handler.ReadJwtToken(token);
|
||||
string? usernameClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == "user")?.Value;
|
||||
return string.IsNullOrEmpty(usernameClaim) ? "" : usernameClaim;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" />
|
||||
<PackageReference Include="MySql.Data" Version="9.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,20 @@
|
||||
GET api/auth/ -- get token
|
||||
GET api/posts
|
||||
GET api/posts/:id
|
||||
GET api/profile/me
|
||||
GET api/profile/user/:user_id
|
||||
GET api/profile/github/:username
|
||||
# PUT api/profile/experience
|
||||
# PUT api/profile/education
|
||||
# PUT api/posts/like/:id
|
||||
# PUT api/posts/unlike/:id
|
||||
POST api/users -- register user
|
||||
POST api/auth/ -- login
|
||||
# POST api/profile
|
||||
# POST api/posts
|
||||
# POST api/posts/comment/:id
|
||||
DELETE api/profile -- delete everything the user has done
|
||||
# DELETE api/profile/education/:exp_id
|
||||
# DELETE api/posts/comment/:id/:comment_id
|
||||
# DELETE api/profile/experience/:exp_id
|
||||
# DELETE api/posts/:id
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class UpdateComment : UpdateRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> validParamNames = ["message", "post", "id"];
|
||||
|
||||
UpdateDb(request, "comment", validParamNames, true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LikeComment(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> validParamNames = ["likes", "id"];
|
||||
|
||||
UpdateDb(request, "comment", validParamNames, true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class UpdateEducation : UpdateRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
List<string> validParamNames =
|
||||
[
|
||||
"school",
|
||||
"degree",
|
||||
"field",
|
||||
"from_date",
|
||||
"to_date",
|
||||
"description",
|
||||
"id",
|
||||
];
|
||||
|
||||
try
|
||||
{
|
||||
UpdateDb(request, "education", validParamNames, true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class UpdateExperience : UpdateRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
List<string> validParamNames =
|
||||
[
|
||||
"job",
|
||||
"company",
|
||||
"location",
|
||||
"from_date",
|
||||
"to_date",
|
||||
"description",
|
||||
"id",
|
||||
];
|
||||
|
||||
try
|
||||
{
|
||||
UpdateDb(request, "experience", validParamNames, true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class UpdateLikes : UpdateRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
List<string> validParamNames = ["id"];
|
||||
|
||||
try
|
||||
{
|
||||
UpdateLikes(request, "post", validParamNames, true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class UpdatePost : UpdateRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
List<string> validParamNames =
|
||||
[
|
||||
"f_name",
|
||||
"l_name",
|
||||
"company",
|
||||
"website",
|
||||
"location",
|
||||
"github",
|
||||
"status",
|
||||
"bio",
|
||||
"skills",
|
||||
"twitter",
|
||||
"facebook",
|
||||
"youtube",
|
||||
"linkedin",
|
||||
"instagram",
|
||||
"id",
|
||||
];
|
||||
|
||||
try
|
||||
{
|
||||
UpdateDb(request, "post", validParamNames, true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleLikes(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
List<string> validParamNames = ["id"];
|
||||
|
||||
try
|
||||
{
|
||||
UpdateLikes(request, "post", validParamNames, true);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class UpdateProfile : UpdateRoute
|
||||
{
|
||||
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
|
||||
{
|
||||
List<string> validParamNames =
|
||||
[
|
||||
"f_name",
|
||||
"l_name",
|
||||
"company",
|
||||
"website",
|
||||
"location",
|
||||
"github",
|
||||
"status",
|
||||
"bio",
|
||||
"skills",
|
||||
"twitter",
|
||||
"facebook",
|
||||
"youtube",
|
||||
"linkedin",
|
||||
"instagram",
|
||||
];
|
||||
|
||||
try
|
||||
{
|
||||
UpdateDb(request, "profile", validParamNames, false);
|
||||
SendSuccess(response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SendError(response, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Net;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Server;
|
||||
|
||||
public class UpdateRoute : SecuredRoute
|
||||
{
|
||||
//TODO create editied time on field in db for comments and posts
|
||||
//TODO all updates need validation and deletes
|
||||
protected static void UpdateDb(
|
||||
HttpListenerRequest request,
|
||||
string table,
|
||||
List<string> validParamNames,
|
||||
bool requireId
|
||||
)
|
||||
{
|
||||
string user_id = ExtractUserId(request);
|
||||
var bodyParamValues = ExtractBody(request, validParamNames);
|
||||
|
||||
if (requireId && bodyParamValues["id"] is null)
|
||||
throw new Exception("missing id");
|
||||
|
||||
string temp = "";
|
||||
foreach (var item in bodyParamValues)
|
||||
{
|
||||
temp += item.Key + "=\"" + item.Value + "\",";
|
||||
}
|
||||
// remove last chat from str
|
||||
temp = temp[..^1];
|
||||
|
||||
validParamNames.Add("user_id");
|
||||
bodyParamValues["user_id"] = user_id;
|
||||
|
||||
temp += requireId ? " WHERE user_id=@user_id AND id=@id;" : " WHERE user_id=@user_id;";
|
||||
|
||||
MySqlCommand cmd = new("UPDATE " + table + " SET " + temp);
|
||||
cmd = AddValuesToCmd(bodyParamValues, cmd);
|
||||
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
protected static void UpdateLikes(
|
||||
HttpListenerRequest request,
|
||||
string table,
|
||||
List<string> validParamNames,
|
||||
bool requireId
|
||||
)
|
||||
{
|
||||
var bodyParamValues = ExtractBody(request, validParamNames);
|
||||
if (requireId && bodyParamValues["id"] is null)
|
||||
throw new Exception("missing id");
|
||||
|
||||
string query = "SELECT likes from post Where id=@id;";
|
||||
MySqlCommand cmd2 = new(query);
|
||||
using MySqlConnection conn = new(connectionString);
|
||||
cmd2.Connection = conn;
|
||||
conn.Open();
|
||||
cmd2.Parameters.AddWithValue("@id", bodyParamValues["id"]);
|
||||
MySqlDataReader reader = cmd2.ExecuteReader();
|
||||
string? id = "";
|
||||
string? likes = "";
|
||||
while (reader.Read())
|
||||
{
|
||||
id = Convert.ToString(reader["id"]);
|
||||
likes = Convert.ToString(reader["likes"]);
|
||||
}
|
||||
Console.WriteLine(id);
|
||||
|
||||
query = "Update post SET likes=2 where id=1;";
|
||||
MySqlCommand cmd = new(query);
|
||||
cmd = AddValuesToCmd(bodyParamValues, cmd);
|
||||
|
||||
cmd.Connection = conn;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
# register
|
||||
curl -X POST localhost:5000/api/register
|
||||
-d
|
||||
{
|
||||
"username":"tombo" ,
|
||||
"password":"1234567890" ,
|
||||
"email":"temp@mail.com"
|
||||
}
|
||||
# login
|
||||
curl -X POST localhost:5000/api/login
|
||||
-d
|
||||
{
|
||||
"password":"1234567890" ,
|
||||
"email":"temp@mail.com"
|
||||
}
|
||||
# add profile
|
||||
curl -X POST localhost:5000/api/profile
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"f_name":"somef_name" ,
|
||||
"l_name":"somel_name" ,
|
||||
"company":"somecompany" ,
|
||||
"website":"somewebsite" ,
|
||||
"location":"somelocation" ,
|
||||
"skills":"someskills" ,
|
||||
"github":"somegithub" ,
|
||||
"status":"somestatus" ,
|
||||
"bio":"somebio" ,
|
||||
"twitter":"sometwitter" ,
|
||||
"youtube":"someyoutube" ,
|
||||
"facebook":"somefacebook" ,
|
||||
"linkedin":"somelinkedi" ,
|
||||
"instagram":"someinstagram"
|
||||
}
|
||||
|
||||
#update profile
|
||||
curl -X PUT localhost:5000/api/profile/update
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"f_name":"Rombo" ,
|
||||
"l_name":"Tombo" ,
|
||||
"website":"TOMBOBOBOBO"
|
||||
}
|
||||
# "company":"somecompany" ,
|
||||
# "website":"somewebsite" ,
|
||||
# "location":"somelocation" ,
|
||||
# "skills":"someskills" ,
|
||||
# "github":"somegithub" ,
|
||||
# "status":"somestatus" ,
|
||||
# "bio":"somebio" ,
|
||||
# "twitter":"sometwitter" ,
|
||||
# "youtube":"someyoutube" ,
|
||||
# "facebook":"somefacebook" ,
|
||||
# "linkedin":"somelinkedi" ,
|
||||
# "instagram":"someinstagram"
|
||||
# }
|
||||
|
||||
# add education
|
||||
curl -X POST localhost:5000/api/profile/education
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"school": "someschool" ,
|
||||
"degree": "somedegree" ,
|
||||
"field": "somefield" ,
|
||||
"from_date": "2020-01-01" ,
|
||||
"to_date": "2020-02-02" ,
|
||||
"description": "somedescription"
|
||||
}
|
||||
#update education
|
||||
curl -X PUT localhost:5000/api/profile/education
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"school": "TOOBOBOOB" ,
|
||||
"degree": "somedegree" ,
|
||||
"field": "somefield" ,
|
||||
"from_date": "2020-01-01" ,
|
||||
"to_date": "2020-02-02" ,
|
||||
"description": "somedescription",
|
||||
"id":"1"
|
||||
}
|
||||
|
||||
# add exp
|
||||
curl -X POST localhost:5000/api/profile/experience
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"job":"somejob" ,
|
||||
"company":"somecompany" ,
|
||||
"location":"somelocation" ,
|
||||
"from_date":"2020-01-01" ,
|
||||
"to_date":"2020-02-02" ,
|
||||
"description":"12312312312312312312"
|
||||
}
|
||||
|
||||
# add post
|
||||
curl -X POST localhost:5000/api/post
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"message":"lskadfjalsk;djf;laksdjf;lsa"
|
||||
}
|
||||
#update profile
|
||||
curl -X PUT localhost:5000/api/post/like
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"id":"4"
|
||||
}
|
||||
|
||||
# add comment
|
||||
curl -X POST localhost:5000/api/comment
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"message":"lskadfjalsk;djf;laksdjf;lsa" ,
|
||||
"post":"1"
|
||||
}
|
||||
|
||||
# remove comment
|
||||
curl -X DELETE localhost:5000/api/comment
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"id":"2"
|
||||
}
|
||||
|
||||
# remove education
|
||||
curl -X DELETE localhost:5000/api/profile/education
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"id":"2"
|
||||
}
|
||||
|
||||
# remove experience
|
||||
curl -X DELETE localhost:5000/api/profile/experience
|
||||
-H "token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiMSIsImV4cCI6MTczNDEyOTM5MSwiaXNzIjoiVGltZUxvZ1NlcnZlciIsImF1ZCI6IlRpbWVMb2dXZWJzaXRlIn0.TZQQUaMBhL3PO3BvwpANMCImCk_RxGg7B5rTcbs9gRg"
|
||||
-d
|
||||
{
|
||||
"id":"2"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
import config from "config";
|
||||
|
||||
const db = process.env.MONGO_URL
|
||||
? process.env.MONGO_URL
|
||||
: config.get("mongoURI");
|
||||
|
||||
const connectDB = async () => {
|
||||
try {
|
||||
if (typeof db === "string") await mongoose.connect(db);
|
||||
|
||||
console.log("MongoDB Connected...");
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
export default connectDB;
|
||||
@@ -1,13 +0,0 @@
|
||||
FROM node:20
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY --chown=node:node . .
|
||||
|
||||
RUN npm i
|
||||
|
||||
ENV DEBUG=express:*
|
||||
|
||||
USER node
|
||||
|
||||
CMD npm run server
|
||||
@@ -1,32 +0,0 @@
|
||||
import config from "config";
|
||||
import jwt from "jsonwebtoken";
|
||||
import type { Request, Response, NextFunction } from "express";
|
||||
|
||||
interface ResponseAndUser extends Request {
|
||||
user?: string;
|
||||
}
|
||||
|
||||
function auth(req: ResponseAndUser, res: Response, next: NextFunction) {
|
||||
// Get token from header
|
||||
const token = req.header("x-auth-token");
|
||||
// Check if not token
|
||||
if (!token) {
|
||||
return res.status(401).json({ msg: "No token, authorization denied" });
|
||||
}
|
||||
|
||||
// Verify token
|
||||
try {
|
||||
jwt.verify(token, config.get("jwtSecret"), (error, decoded) => {
|
||||
if (error) {
|
||||
return res.status(401).json({ msg: "Token is not valid" });
|
||||
} else {
|
||||
if (decoded && typeof decoded !== "string") req.user = decoded?.user;
|
||||
next();
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("something wrong with auth middleware");
|
||||
res.status(500).json({ msg: "Server Error" });
|
||||
}
|
||||
}
|
||||
export default auth;
|
||||
@@ -1,10 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
// middleware to check for a valid object id
|
||||
import type { Request, Response, NextFunction } from "express";
|
||||
const checkObjectId = (idToCheck: string) => (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!mongoose.Types.ObjectId.isValid(req.params[idToCheck]))
|
||||
return res.status(400).json({ msg: 'Invalid ID' });
|
||||
next();
|
||||
};
|
||||
|
||||
export default checkObjectId
|
||||
@@ -1,54 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
const PostSchema = new Schema({
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
},
|
||||
likes: [
|
||||
{
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
},
|
||||
},
|
||||
],
|
||||
comments: [
|
||||
{
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
},
|
||||
date: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
},
|
||||
],
|
||||
date: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
});
|
||||
|
||||
const Post = mongoose.model("post", PostSchema);
|
||||
export default Post
|
||||
@@ -1,114 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
const ProfileSchema = new mongoose.Schema({
|
||||
user: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "user",
|
||||
},
|
||||
company: {
|
||||
type: String,
|
||||
},
|
||||
website: {
|
||||
type: String,
|
||||
},
|
||||
location: {
|
||||
type: String,
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
skills: {
|
||||
type: [String],
|
||||
required: true,
|
||||
},
|
||||
bio: {
|
||||
type: String,
|
||||
},
|
||||
githubusername: {
|
||||
type: String,
|
||||
},
|
||||
experience: [
|
||||
{
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
company: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
location: {
|
||||
type: String,
|
||||
},
|
||||
from: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
to: {
|
||||
type: Date,
|
||||
},
|
||||
current: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
],
|
||||
education: [
|
||||
{
|
||||
school: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
degree: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
fieldofstudy: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
from: {
|
||||
type: Date,
|
||||
required: true,
|
||||
},
|
||||
to: {
|
||||
type: Date,
|
||||
},
|
||||
current: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
],
|
||||
social: {
|
||||
youtube: {
|
||||
type: String,
|
||||
},
|
||||
twitter: {
|
||||
type: String,
|
||||
},
|
||||
facebook: {
|
||||
type: String,
|
||||
},
|
||||
linkedin: {
|
||||
type: String,
|
||||
},
|
||||
instagram: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
date: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
});
|
||||
|
||||
const Profile = mongoose.model("profile", ProfileSchema);
|
||||
export default Profile
|
||||
@@ -1,28 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
const UserSchema = new mongoose.Schema({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
},
|
||||
date: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
},
|
||||
});
|
||||
|
||||
const User = mongoose.model("user", UserSchema);
|
||||
|
||||
export default User
|
||||
Generated
-3697
File diff suppressed because it is too large
Load Diff
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "devconnectts",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "server.ts",
|
||||
"scripts": {
|
||||
"start": "npx ts-node server.ts",
|
||||
"server": "npx ts-node-dev server.ts"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.4.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"config": "^3.3.9",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"express-validator": "^7.0.1",
|
||||
"gravatar": "^1.8.2",
|
||||
"jsonwebtoken": "^9.0.1",
|
||||
"mongoose": "^7.4.1",
|
||||
"normalize-url": "^5.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"ts-node-dev": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/config": "^3.3.0",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/gravatar": "^1.8.3",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/uuid": "^9.0.2"
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import express from "express";
|
||||
import bcrypt from "bcryptjs";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
import auth from "../../middleware/auth";
|
||||
import config from "config";
|
||||
import { check, validationResult } from "express-validator";
|
||||
|
||||
import User from "../../models/User";
|
||||
import type { Request, Response } from "express";
|
||||
import { isUserId } from "../../utils";
|
||||
|
||||
const router = express.Router();
|
||||
// @route GET api/auth
|
||||
// @desc Get user by token
|
||||
// @access Private
|
||||
router.get("/", auth, async (req: any, res) => {
|
||||
try {
|
||||
let user: unknown = null;
|
||||
if (isUserId(req)) {
|
||||
user = await User.findById(req.user.id).select("-password");
|
||||
res.json(user);
|
||||
} else {
|
||||
throw new Error("missing id in request");
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
});
|
||||
|
||||
// @route POST api/auth
|
||||
// @desc Authenticate user & get token
|
||||
// @access Public
|
||||
router.post(
|
||||
"/",
|
||||
[
|
||||
check("email", "Please include a valid email").isEmail(),
|
||||
check("password", "Password is required").exists(),
|
||||
],
|
||||
async (req: Request, res: Response) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
const { email, password } = req.body;
|
||||
|
||||
try {
|
||||
let user = await User.findOne({ email });
|
||||
|
||||
if (!user) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ errors: [{ msg: "Invalid Credentials" }] });
|
||||
}
|
||||
|
||||
const isMatch = await bcrypt.compare(password, user.password);
|
||||
|
||||
if (!isMatch) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ errors: [{ msg: "Invalid Credentials" }] });
|
||||
}
|
||||
|
||||
const payload = {
|
||||
user: {
|
||||
id: user.id,
|
||||
},
|
||||
};
|
||||
|
||||
const jwtSecret = process.env.JWT_SECRET
|
||||
? process.env.JWT_SECRET
|
||||
: config.get("jwtSecret");
|
||||
|
||||
if (typeof jwtSecret === "string")
|
||||
jwt.sign(payload, jwtSecret, { expiresIn: 360000 }, (err, token) => {
|
||||
if (err) throw err;
|
||||
res.json({ token });
|
||||
});
|
||||
else throw new Error("Error signing the jwt token");
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server error");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,257 +0,0 @@
|
||||
import express, { Request, Response } from "express";
|
||||
import { check, validationResult } from "express-validator";
|
||||
import auth from "../../middleware/auth";
|
||||
import Post from "../../models/Post";
|
||||
import User from "../../models/User";
|
||||
import checkObjectId from "../../middleware/checkObjectId";
|
||||
import { isUserId } from "../../utils";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// @route POST api/posts
|
||||
// @desc Create a post
|
||||
// @access Private
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
auth,
|
||||
check("text", "Text is required").notEmpty(),
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
try {
|
||||
if (isUserId(req)) {
|
||||
const user = await User.findById(req.user.id).select("-password");
|
||||
if (user) {
|
||||
const newPost = new Post({
|
||||
text: req.body.text,
|
||||
name: user.name,
|
||||
avatar: user.avatar,
|
||||
user: req.user.id,
|
||||
});
|
||||
const post = await newPost.save();
|
||||
res.json(post);
|
||||
} else {
|
||||
throw new Error("Error finding the user");
|
||||
}
|
||||
} else {
|
||||
throw new Error("Error finding the user");
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// @route GET api/posts
|
||||
// @desc Get all posts
|
||||
// @access Private
|
||||
router.get("/", auth, async (req, res) => {
|
||||
try {
|
||||
const posts = await Post.find().sort({ date: -1 });
|
||||
res.json(posts);
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET api/posts/:id
|
||||
// @desc Get post by ID
|
||||
// @access Private
|
||||
router.get("/:id", auth, checkObjectId("id"), async (req, res) => {
|
||||
try {
|
||||
const post = await Post.findById(req.params.id);
|
||||
|
||||
if (!post) {
|
||||
return res.status(404).json({ msg: "Post not found" });
|
||||
}
|
||||
|
||||
res.json(post);
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
});
|
||||
|
||||
// @route DELETE api/posts/:id
|
||||
// @desc Delete a post
|
||||
// @access Private
|
||||
router.delete(
|
||||
"/:id",
|
||||
[auth, checkObjectId("id")],
|
||||
async (req: Request, res: Response) => {
|
||||
try {
|
||||
const post = await Post.findOne({ _id: req.params.id });
|
||||
|
||||
if (!post) {
|
||||
return res.status(404).json({ msg: "Post not found" });
|
||||
}
|
||||
|
||||
// Check user
|
||||
if (post.user && isUserId(req)) {
|
||||
if (post.user.toString() !== req.user.id) {
|
||||
return res.status(401).json({ msg: "User not authorized" });
|
||||
}
|
||||
} else {
|
||||
throw new Error("Error in req.user");
|
||||
}
|
||||
await post.deleteOne();
|
||||
|
||||
res.json({ msg: "Post removed" });
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// @route PUT api/posts/like/:id
|
||||
// @desc Like a post
|
||||
// @access Private
|
||||
router.put("/like/:id", auth, checkObjectId("id"), async (req, res) => {
|
||||
try {
|
||||
const post = await Post.findById(req.params.id);
|
||||
|
||||
// Check if the post has already been liked
|
||||
if (post && isUserId(req)) {
|
||||
if (post.likes.some((like) => like.user?.toString() === req.user.id)) {
|
||||
return res.status(400).json({ msg: "Post already liked" });
|
||||
}
|
||||
|
||||
const user: any = req.user.id;
|
||||
// can't make string into ObjectID
|
||||
post.likes.unshift({ user });
|
||||
|
||||
await post.save();
|
||||
|
||||
return res.json(post.likes);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
});
|
||||
|
||||
// @route PUT api/posts/unlike/:id
|
||||
// @desc Unlike a post
|
||||
// @access Private
|
||||
router.put("/unlike/:id", auth, checkObjectId("id"), async (req, res) => {
|
||||
try {
|
||||
const post = await Post.findById(req.params.id);
|
||||
|
||||
// Check if the post has not yet been liked
|
||||
if (post && isUserId(req)) {
|
||||
if (!post.likes.some((like) => like.user?.toString() === req.user.id)) {
|
||||
return res.status(400).json({ msg: "Post has not yet been liked" });
|
||||
}
|
||||
|
||||
// remove the like
|
||||
post.likes = post.likes.filter(({ user }) => {
|
||||
if (user) return user.toString() !== req.user.id;
|
||||
return false;
|
||||
});
|
||||
|
||||
await post.save();
|
||||
|
||||
return res.json(post.likes);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
});
|
||||
|
||||
// @route POST api/posts/comment/:id
|
||||
// @desc Comment on a post
|
||||
// @access Private
|
||||
router.post(
|
||||
"/comment/:id",
|
||||
auth,
|
||||
checkObjectId("id"),
|
||||
check("text", "Text is required").notEmpty(),
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
try {
|
||||
if (isUserId(req) && req.params) {
|
||||
const user = await User.findById(req.user.id).select("-password");
|
||||
const post = await Post.findById(req.params.id);
|
||||
if (user) {
|
||||
const newComment = {
|
||||
text: req.body.text,
|
||||
name: user.name,
|
||||
avatar: user.avatar,
|
||||
user: req.user.id,
|
||||
};
|
||||
if (post) {
|
||||
post.comments.unshift(newComment as any);
|
||||
await post.save();
|
||||
res.json(post.comments);
|
||||
} else throw new Error("Error in finding post");
|
||||
} else throw new Error("Error in finding user");
|
||||
} else throw new Error("Error in parsing the req");
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// @route DELETE api/posts/comment/:id/:comment_id
|
||||
// @desc Delete comment
|
||||
// @access Private
|
||||
router.delete("/comment/:id/:comment_id", auth, async (req, res) => {
|
||||
try {
|
||||
const post = await Post.findById(req.params.id);
|
||||
|
||||
// Pull out comment
|
||||
if (post) {
|
||||
const comment = post.comments.find(
|
||||
(comment: any) => comment.id === req.params.comment_id,
|
||||
);
|
||||
// Make sure comment exists
|
||||
if (!comment) {
|
||||
return res.status(404).json({ msg: "Comment does not exist" });
|
||||
}
|
||||
// Check user
|
||||
if (
|
||||
"user" in comment &&
|
||||
comment.user &&
|
||||
isUserId(req) &&
|
||||
comment.user.toString() !== req.user.id
|
||||
) {
|
||||
return res.status(401).json({ msg: "User not authorized" });
|
||||
}
|
||||
|
||||
post.comments = post.comments.filter(
|
||||
({ id }: any) => id !== req.params.comment_id,
|
||||
);
|
||||
|
||||
await post.save();
|
||||
|
||||
return res.json(post.comments);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
return res.status(500).send("Server Error");
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,319 +0,0 @@
|
||||
import express from "express";
|
||||
import axios from "axios";
|
||||
import config from "config";
|
||||
|
||||
import auth from "../../middleware/auth";
|
||||
import { check, validationResult } from "express-validator";
|
||||
|
||||
// bring in normalize to give us a proper url, regardless of what user entered
|
||||
import normalize from "normalize-url";
|
||||
import checkObjectId from "../../middleware/checkObjectId";
|
||||
|
||||
import Profile from "../../models/Profile";
|
||||
import User from "../../models/User";
|
||||
import Post from "../../models/Post";
|
||||
import { isUserId } from "../../utils";
|
||||
const router = express.Router();
|
||||
// @route GET api/profile/me
|
||||
// @desc Get current users profile
|
||||
// @access Private
|
||||
router.get("/me", auth, async (req, res) => {
|
||||
try {
|
||||
if (isUserId(req)) {
|
||||
const profile = await Profile.findOne({
|
||||
user: req.user.id,
|
||||
}).populate("user", ["name", "avatar"]);
|
||||
|
||||
if (!profile) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ msg: "There is no profile for this user" });
|
||||
}
|
||||
|
||||
res.json(profile);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
});
|
||||
|
||||
// @route POST api/profile
|
||||
// @desc Create or update user profile
|
||||
// @access Private
|
||||
router.post(
|
||||
"/",
|
||||
auth,
|
||||
check("status", "Status is required").notEmpty(),
|
||||
check("skills", "Skills is required").notEmpty(),
|
||||
check("website", "Not a valid website").isURL(),
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
// destructure the request
|
||||
const {
|
||||
website,
|
||||
skills,
|
||||
youtube,
|
||||
twitter,
|
||||
instagram,
|
||||
linkedin,
|
||||
facebook,
|
||||
// spread the rest of the fields we don't need to check
|
||||
...rest
|
||||
} = req.body;
|
||||
|
||||
// build a profile
|
||||
if (isUserId(req)) {
|
||||
const profileFields = {
|
||||
user: req.user.id,
|
||||
website:
|
||||
website && website !== ""
|
||||
? normalize(website, { forceHttps: true })
|
||||
: "",
|
||||
skills: Array.isArray(skills)
|
||||
? skills
|
||||
: skills.split(",").map((skill: string) => " " + skill.trim()),
|
||||
...rest,
|
||||
};
|
||||
|
||||
// Build socialFields object
|
||||
const socialFields: { [key: string]: any } = {
|
||||
youtube,
|
||||
twitter,
|
||||
instagram,
|
||||
linkedin,
|
||||
facebook,
|
||||
};
|
||||
|
||||
// normalize social fields to ensure valid url
|
||||
for (const [key, value] of Object.entries(socialFields)) {
|
||||
if (value && value.length > 0)
|
||||
socialFields[key] = normalize(value, { forceHttps: true });
|
||||
}
|
||||
// add to profileFields
|
||||
profileFields.social = socialFields;
|
||||
|
||||
try {
|
||||
// Using upsert option (creates new doc if no match is found):
|
||||
let profile = await Profile.findOneAndUpdate(
|
||||
{ user: req.user.id },
|
||||
{ $set: profileFields },
|
||||
{ new: true, upsert: true, setDefaultsOnInsert: true },
|
||||
);
|
||||
return res.json(profile);
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
return res.status(500).send("Server Error");
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// @route GET api/profile
|
||||
// @desc Get all profiles
|
||||
// @access Public
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
const profiles = await Profile.find().populate("user", ["name", "avatar"]);
|
||||
res.json(profiles);
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET api/profile/user/:user_id
|
||||
// @desc Get profile by user ID
|
||||
// @access Public
|
||||
router.get(
|
||||
"/user/:user_id",
|
||||
checkObjectId("user_id"),
|
||||
async ({ params: { user_id } }, res) => {
|
||||
try {
|
||||
const profile = await Profile.findOne({
|
||||
user: user_id,
|
||||
}).populate("user", ["name", "avatar"]);
|
||||
|
||||
if (!profile) return res.status(400).json({ msg: "Profile not found" });
|
||||
|
||||
return res.json(profile);
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
return res.status(500).json({ msg: "Server error" });
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// @route DELETE api/profile
|
||||
// @desc Delete profile, user & posts
|
||||
// @access Private
|
||||
router.delete("/", auth, async (req, res) => {
|
||||
try {
|
||||
// Remove user posts
|
||||
// Remove profile
|
||||
// Remove user
|
||||
if (isUserId(req))
|
||||
await Promise.all([
|
||||
Post.deleteMany({ user: req.user.id }),
|
||||
Profile.findOneAndRemove({ user: req.user.id }),
|
||||
User.findOneAndRemove({ _id: req.user.id }),
|
||||
]);
|
||||
|
||||
res.json({ msg: "User deleted" });
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
});
|
||||
|
||||
// @route PUT api/profile/experience
|
||||
// @desc Add profile experience
|
||||
// @access Private
|
||||
router.put(
|
||||
"/experience",
|
||||
auth,
|
||||
check("title", "Title is required").notEmpty(),
|
||||
check("company", "Company is required").notEmpty(),
|
||||
check("from", "From date is required and needs to be from the past")
|
||||
.notEmpty()
|
||||
.custom((value, { req }) => (req.body.to ? value < req.body.to : true)),
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
try {
|
||||
if (isUserId(req)) {
|
||||
const profile = await Profile.findOne({ user: req.user.id });
|
||||
|
||||
if (profile) {
|
||||
profile.experience.unshift(req.body);
|
||||
|
||||
await profile.save();
|
||||
}
|
||||
res.json(profile);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// @route DELETE api/profile/experience/:exp_id
|
||||
// @desc Delete experience from profile
|
||||
// @access Private
|
||||
|
||||
router.delete("/experience/:exp_id", auth, async (req, res) => {
|
||||
try {
|
||||
if (isUserId(req)) {
|
||||
const foundProfile = await Profile.findOne({ user: req.user.id });
|
||||
if (foundProfile) {
|
||||
foundProfile.experience = foundProfile.experience.filter(
|
||||
(exp: any) => exp._id.toString() !== req.params.exp_id,
|
||||
);
|
||||
|
||||
await foundProfile.save();
|
||||
}
|
||||
return res.status(200).json(foundProfile);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
return res.status(500).json({ msg: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// @route PUT api/profile/education
|
||||
// @desc Add profile education
|
||||
// @access Private
|
||||
router.put(
|
||||
"/education",
|
||||
auth,
|
||||
check("school", "School is required").notEmpty(),
|
||||
check("degree", "Degree is required").notEmpty(),
|
||||
check("fieldofstudy", "Field of study is required").notEmpty(),
|
||||
check("from", "From date is required and needs to be from the past")
|
||||
.notEmpty()
|
||||
.custom((value, { req }) => (req.body.to ? value < req.body.to : true)),
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
try {
|
||||
if (isUserId(req)) {
|
||||
const profile = await Profile.findOne({ user: req.user.id });
|
||||
if (profile) {
|
||||
profile.education.unshift(req.body);
|
||||
|
||||
await profile.save();
|
||||
}
|
||||
res.json(profile);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server Error");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// @route DELETE api/profile/education/:edu_id
|
||||
// @desc Delete education from profile
|
||||
// @access Private
|
||||
|
||||
router.delete("/education/:edu_id", auth, async (req, res) => {
|
||||
try {
|
||||
if (isUserId(req)) {
|
||||
const foundProfile = await Profile.findOne({ user: req.user.id });
|
||||
if (foundProfile) {
|
||||
foundProfile.education = foundProfile.education.filter(
|
||||
(edu: any) => edu._id.toString() !== req.params.edu_id,
|
||||
);
|
||||
await foundProfile.save();
|
||||
}
|
||||
return res.status(200).json(foundProfile);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
return res.status(500).json({ msg: "Server error" });
|
||||
}
|
||||
});
|
||||
|
||||
// @route GET api/profile/github/:username
|
||||
// @desc Get user repos from Github
|
||||
// @access Public
|
||||
router.get("/github/:username", async (req, res) => {
|
||||
try {
|
||||
const uri = encodeURI(
|
||||
`https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc`,
|
||||
);
|
||||
const headers = {
|
||||
"user-agent": "node.js",
|
||||
Authorization: `token ${config.get("githubToken")}`,
|
||||
};
|
||||
|
||||
const gitHubResponse = await axios.get(uri, { headers });
|
||||
return res.json(gitHubResponse.data);
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
return res.status(404).json({ msg: "No Github profile found" });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,86 +0,0 @@
|
||||
import express from "express";
|
||||
import gravatar from "gravatar";
|
||||
import bcrypt from "bcryptjs";
|
||||
import jwt from "jsonwebtoken";
|
||||
import config from "config";
|
||||
import { check, validationResult } from "express-validator";
|
||||
import User from "../../models/User";
|
||||
|
||||
import normalizeUrl from "normalize-url";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// @route POST api/users
|
||||
// @desc Register user
|
||||
// @access Public
|
||||
router.post(
|
||||
"/",
|
||||
check("name", "Name is required").notEmpty(),
|
||||
check("email", "Please include a valid email").isEmail(),
|
||||
check(
|
||||
"password",
|
||||
"Please enter a password with 6 or more characters",
|
||||
).isLength({ min: 6 }),
|
||||
async (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
const { name, email, password } = req.body;
|
||||
|
||||
try {
|
||||
let user = await User.findOne({ email });
|
||||
|
||||
if (user) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ errors: [{ msg: "User already exists" }] });
|
||||
}
|
||||
|
||||
const avatar = normalizeUrl(
|
||||
gravatar.url(email, {
|
||||
s: "200",
|
||||
r: "pg",
|
||||
d: "mm",
|
||||
}),
|
||||
{ forceHttps: true },
|
||||
);
|
||||
|
||||
user = new User({
|
||||
name,
|
||||
email,
|
||||
avatar,
|
||||
password,
|
||||
});
|
||||
|
||||
const salt = await bcrypt.genSalt(10);
|
||||
|
||||
user.password = await bcrypt.hash(password, salt);
|
||||
|
||||
await user.save();
|
||||
|
||||
const payload = {
|
||||
user: {
|
||||
id: user.id,
|
||||
},
|
||||
};
|
||||
|
||||
const jwtSecret = process.env.JWT_SECRET
|
||||
? process.env.JWT_SECRET
|
||||
: config.get("jwtSecret");
|
||||
|
||||
if (typeof jwtSecret === "string")
|
||||
jwt.sign(payload, jwtSecret, { expiresIn: "5 days" }, (err, token) => {
|
||||
if (err) throw err;
|
||||
res.json({ token });
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
if (typeof err === "string") console.error(err);
|
||||
else if (err instanceof Error) console.error(err.message);
|
||||
res.status(500).send("Server error");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,31 +0,0 @@
|
||||
import express from "express";
|
||||
import connectDB from "./config/db";
|
||||
import path from "path";
|
||||
import cors from "cors";
|
||||
|
||||
const app = express();
|
||||
|
||||
// add cors otherwise fronend cannot access backedn
|
||||
app.use(cors());
|
||||
|
||||
connectDB();
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
app.use("/api/users", require("./routers/api/users"));
|
||||
app.use("/api/auth", require("./routers/api/auth"));
|
||||
app.use("/api/profile", require("./routers/api/profile"));
|
||||
app.use("/api/posts", require("./routers/api/posts"));
|
||||
|
||||
// Serve static assets in production
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
console.log("in production");
|
||||
app.use(express.static("client/build"));
|
||||
app.get("*", (_, res) => [
|
||||
res.sendFile(path.resolve(__dirname, "client", "build", "index.html")),
|
||||
]);
|
||||
}
|
||||
|
||||
const PORT = process.env.PORT || 5000;
|
||||
|
||||
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
|
||||
@@ -1,103 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2018", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
export const isUserId = function(req: unknown): req is { user: { id: string } } {
|
||||
let user: unknown = null
|
||||
let id: unknown = null
|
||||
if (typeof req === 'object' && req)
|
||||
if ('user' in req) {
|
||||
user = req.user
|
||||
if (typeof user === 'object' && user && 'id' in user) {
|
||||
id = user.id
|
||||
if (typeof id === 'string') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user