Merge pull request 'dotnet' (#1) from dotnet into master

Reviewed-on: phoenix/textsender-api#1
This commit is contained in:
phoenix
2023-10-17 01:07:04 +00:00
24 changed files with 1166 additions and 106 deletions
+5
View File
@@ -0,0 +1,5 @@
.vscode
appsettings.Development.json
*.json
bin
obj
+67
View File
@@ -0,0 +1,67 @@
using TextSender_API.Models;
namespace TextSender_API.Controllers;
public class ContactManagement
{
#region Fields
private IConfiguration _config;
#endregion
#region Constructors
public ContactManagement(IConfiguration config)
{
this._config = config;
}
#endregion
#region Methods
// Queues the contact file to be processed latter
public ContactCreationQueue QueueContact(IFormFile file)
{
var queue = new ContactCreationQueue();
queue.Processed = false;
var directory = this._config["Paths:ContactQueueDirectory"];
if (directory!.ElementAt(directory!.Length - 1) != '/')
{
directory.Append('/');
}
if (!System.IO.Directory.Exists(directory))
{
queue.Status = "Missing directory";
// Directory does not exist
return queue;
}
var length = 16;
var chars = "ABCDEF0123456789";
var random = new Random();
var filename = new string(Enumerable.Repeat(chars, length).Select(s =>
s[random.Next(s.Length)]).ToArray());
var extension = ".txt";
var contactPath = $"{directory}/{filename}{extension}";
queue.Filepath = contactPath;
if (System.IO.File.Exists(contactPath))
{
// Already exists
queue.Status = "File exists";
return queue;
}
queue.Status = "Created";
using (var filestream = new FileStream(contactPath, FileMode.Create))
{
file.CopyTo(filestream);
}
return queue;
}
#endregion
}
@@ -0,0 +1,88 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using TextSender_API.Models;
using TextSender_API.Repositories;
namespace TextSender_API.Controllers;
[Authorize]
[ApiController]
[Route("api/v1/contact/queue")]
public class ContactQueueCreationController : ControllerBase
{
#region Fields
private IConfiguration _config;
private readonly ILogger<ContactQueueCreationController> _logger;
#endregion
#region Constructors
public ContactQueueCreationController(ILogger<ContactQueueCreationController> logger, IConfiguration config)
{
this._logger = logger;
this._config = config;
}
#endregion
#region Methods
[HttpPost("upload"), DisableRequestSizeLimit]
public IActionResult Upload([FromForm(Name = "file")] List<IFormFile> contactFile,
[FromForm(Name = "user_id")] string userID)
{
// Save the file on the filesystem to be processed by a separate
// system
var response = new CreateQueueResponse();
var connString = this._config.GetConnectionString("MongoDBURI");
var userRepo = new UsersRepository(connString!);
var queueRepo = new ContactCreationQueueRepository(connString!);
try
{
var ctMgr = new ContactManagement(this._config);
var user = userRepo.Exists(userID);
if (!user.Item1)
{
return NotFound(response);
}
foreach (var file in contactFile)
{
var queue = ctMgr.QueueContact(file);
queue.UserId = user.Item2.Id;
queue.DateCreated = DateTime.Now;
queueRepo.Create(queue);
response.Data.Add(queue);
}
}
catch (Exception ex)
{
this._logger.LogError($"An error occurred: {ex.Message}");
}
return Ok(response);
}
#endregion
#region Responses
public class CreateQueueResponse
{
#region Properties
[JsonProperty("data")]
public List<ContactCreationQueue> Data { get; set; }
#endregion
#region Constructors
public CreateQueueResponse()
{
this.Data = new List<ContactCreationQueue>();
}
#endregion
}
#endregion
}
+94
View File
@@ -0,0 +1,94 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using TextSender_API.Models;
using TextSender_API.Repositories;
namespace TextSender_API.Controllers;
[Authorize]
[ApiController]
[Route("api/v1/contact")]
public class ContactsController : ControllerBase
{
#region Fields
private IConfiguration _config;
private readonly ILogger<ContactsController> _logger;
#endregion
#region Constructors
public ContactsController(ILogger<ContactsController> logger, IConfiguration config)
{
this._logger = logger;
this._config = config;
}
#endregion
#region Methods
[HttpPost("new"), DisableRequestSizeLimit]
public IActionResult CreateContact([FromBody] Contact contact)
{
var response = new CreateContactResponse();
var connString = this._config.GetConnectionString("MongoDBURI");
var userRepo = new UsersRepository(connString!);
var contactsRepo = new ContactsRepository(connString!);
try
{
var contacts = contactsRepo.RetrieveAll();
var users = userRepo.RetrieveAllUsers();
if (users.Count() == 0)
{
return BadRequest(response);
}
if (contacts.Count() > 0)
{
if (contacts.Exists(c => c.PhoneNumber!.Equals(contact.PhoneNumber)) ||
string.IsNullOrEmpty(contact.UserID))
{
// The Contact already exists
return BadRequest(response);
}
}
if (!userRepo.Exists(contact.UserID!).Item1)
{
return BadRequest(response);
}
contact.DateCreated = DateTime.Now;
contactsRepo.Create(contact);
response.Data.Add(contact);
}
catch (Exception ex)
{
this._logger.LogError($"An error occurred: {ex.Message}");
}
return Ok(response);
}
#endregion
#region Responses
public class CreateContactResponse
{
#region Properties
[JsonProperty("data")]
public List<Contact> Data { get; set; }
#endregion
#region Constructors
public CreateContactResponse()
{
this.Data = new List<Contact>();
}
#endregion
}
#endregion
}
+64
View File
@@ -0,0 +1,64 @@
using System.Security.Cryptography;
using System.Text;
using TextSender_API.Models;
using TextSender_API.Repositories;
namespace TextSender_API.Controllers;
public class PasswordVerification
{
#region Fiends
private UsersRepository _userRepo;
private SaltRepository _saltRepo;
private int _keySize = 64;
private int _iterations = 350000;
#endregion
#region Constructors
public PasswordVerification(UsersRepository userRepo, SaltRepository saltRepo)
{
this._userRepo = userRepo;
this._saltRepo = saltRepo;
}
#endregion
#region Methods
public bool VerifyPassword(User user, string password)
{
var salt = this._saltRepo.Retrieve(user.Id!);
var hashedPassword = this.HashPassword(user, salt, password);
return hashedPassword.Equals(user.Password);
}
public Salt CreateSalt(User user)
{
var salt = new Salt();
salt.Key = RandomNumberGenerator.GetBytes(this._keySize);
return salt;
}
public string HashPassword(User user, Salt salt, string password = "")
{
var hashAlgorithm = HashAlgorithmName.SHA512;
var dbHashedPassword = user.Password;
if (!string.IsNullOrEmpty(password))
{
user.Password = password;
}
var hash = Rfc2898DeriveBytes.Pbkdf2(
Encoding.UTF8.GetBytes(user.Password!),
salt.Key!,
this._iterations,
hashAlgorithm,
this._keySize);
user.Password = dbHashedPassword;
return Convert.ToHexString(hash);
}
#endregion
}
+228
View File
@@ -0,0 +1,228 @@
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using TextSender_API.Models;
using TextSender_API.Repositories;
namespace TextSender_API.Controllers;
[ApiController]
[Route("api/v1/user")]
public class UsersController : ControllerBase
{
#region Fields
private readonly ILogger<UsersController> _logger;
private IConfiguration _config;
#endregion
#region Constructors
public UsersController(ILogger<UsersController> logger, IConfiguration config)
{
this._logger = logger;
this._config = config;
}
#endregion
#region Methods
private string AllScopes()
{
var allScopes = new List<String>()
{
"create:contact"
};
var scopes = string.Empty;
for (var i = 0; i < allScopes.Count; i++)
{
if (i == allScopes.Count - 1)
{
scopes += allScopes[i];
}
else
{
scopes += allScopes[i] + " ";
}
}
return scopes;
}
private List<Claim> Payload()
{
var expLimit = 30;
var currentDate = DateTime.Now;
var expiredDate = currentDate.AddMinutes(expLimit);
var issued = Math.Floor((currentDate - DateTime.UnixEpoch).TotalSeconds);
var expires = Math.Floor((expiredDate - DateTime.UnixEpoch).TotalSeconds);
var issuer = this._config["JWT:Issuer"];
var audience = this._config["JWT:Audience"];
var subject = this._config["JWT:Subject"];
var claim = new List<System.Security.Claims.Claim>()
{
new System.Security.Claims.Claim("scope", AllScopes(), "string"),
new System.Security.Claims.Claim(JwtRegisteredClaimNames.Exp, expiredDate.ToString(), "string"),
new System.Security.Claims.Claim(JwtRegisteredClaimNames.Aud, audience!),
new System.Security.Claims.Claim(JwtRegisteredClaimNames.Iss, issuer!),
new Claim(JwtRegisteredClaimNames.Sub, subject!)
};
return claim;
}
[HttpPost("login")]
public IActionResult Login([FromBody] User userRequest)
{
var response = new LoginResponse();
try
{
var connString = this._config.GetConnectionString("MongoDBURI");
var userRepo = new UsersRepository(connString!);
var saltRepo = new SaltRepository(connString!);
var pwdVerify = new PasswordVerification(userRepo, saltRepo);
var result = userRepo.Exists(userRequest);
if (result.Item1 && pwdVerify.VerifyPassword(result.Item2, userRequest.Password!))
{
var user = result.Item2;
var token = new Token();
token.Issued = DateTime.Now;
token.UserId = user.Id;
token.TokenType = "JWT";
var payload = Payload();
payload.Add(new System.Security.Claims.Claim("user_id", user.Id!, ClaimValueTypes.String));
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this._config["JWT:Secret"]!));
var signIn = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var tok = new JwtSecurityToken(
this._config["JWT:Issuer"],
this._config["JWT:Audience"],
payload,
expires: DateTime.UtcNow.AddMinutes(30),
signingCredentials: signIn);
token.AccessToken = new JwtSecurityTokenHandler().WriteToken(tok);
var expClaim = payload.FirstOrDefault(cl =>
{
return cl.Type.Equals("exp");
});
if (expClaim != null)
{
var expiredDate = DateTime.Parse(expClaim!.Value);
var exp = Math.Floor((expiredDate - DateTime.UnixEpoch).TotalSeconds);
}
response.Data.Add(token);
Ok(response);
}
else
{
return NotFound(response);
}
}
catch (Exception ex)
{
this._logger.LogError($"An error occurred: {ex.Message}");
}
return Ok(response);
}
[HttpPost("register"), DisableRequestSizeLimit]
public IActionResult Upload([FromBody] User userRequest)
{
var response = new RegisterUserResponse();
try
{
var connString = this._config.GetConnectionString("MongoDBURI");
var userRepo = new UsersRepository(connString!);
var saltRepo = new SaltRepository(connString!);
var allUsers = userRepo.RetrieveAllUsers();
var result = allUsers.Exists(ea => ea.Username!.Equals(userRequest.Username));
if (!result)
{
var pwdVerify = new PasswordVerification(userRepo, saltRepo);
var salt = pwdVerify.CreateSalt(userRequest);
var hashedPassword = pwdVerify.HashPassword(userRequest, salt);
this._logger.LogInformation("Creating user");
userRequest.Password = hashedPassword;
userRequest.DateCreated = DateTime.Now;
userRepo.CreateUser(userRequest);
response.Data.Add(userRequest);
salt.UserId = userRequest.Id;
saltRepo.Create(salt);
}
else
{
this._logger.LogInformation("User already exists");
}
}
catch (Exception ex)
{
this._logger.LogError($"An error occurred: {ex.Message}");
}
return Ok(response);
}
#endregion
#region Responses
public class LoginResponse
{
#region Properties
[JsonProperty("data")]
public List<Token> Data { get; set; }
#endregion
#region Constructors
public LoginResponse()
{
this.Data = new List<Token>();
}
#endregion
#region Methods
#endregion
}
public class RegisterUserResponse
{
#region Properties
[JsonProperty("data")]
public List<User> Data { get; set; }
#endregion
#region Constructors
public RegisterUserResponse()
{
this.Data = new List<User>();
}
#endregion
#region Methods
#endregion
}
#endregion
}
+53
View File
@@ -0,0 +1,53 @@
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
namespace TextSender_API.Models;
public class Contact
{
#region Properties
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
[JsonProperty("id")]
public string? Id { get; set; }
[BsonElement("firstname")]
[JsonProperty("firstname")]
public string? Firstname { get; set; }
[BsonElement("lastname")]
[JsonProperty("lastname")]
public string? Lastname { get; set; }
[BsonElement("phonenumber")]
[JsonProperty("phonenumber")]
public string? PhoneNumber { get; set; }
[BsonElement("date_created")]
[JsonProperty("date_created")]
public DateTime DateCreated { get; set; }
// [BsonRepresentation(BsonType.ObjectId)]
[BsonElement("user_id")]
[JsonProperty("user_id")]
public string? UserID { get; set; }
#endregion
}
public class ContactCreationQueue
{
#region Properties
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
[BsonElement("filepath")]
public string? Filepath { get; set; }
[BsonElement("status")]
public string? Status { get; set; }
[BsonElement("user_id")]
public string? UserId { get; set; }
[BsonElement("date_created")]
public DateTime? DateCreated { get; set; }
[BsonElement("processed")]
public bool? Processed { get; set; }
#endregion
}
+14
View File
@@ -0,0 +1,14 @@
using System;
namespace TextSender_API.Models;
public class Text
{
#region Properties
public int TextID { get; set; }
public string? Subject { get; set; }
public string? Message { get; set; }
public int UserID { get; set; }
public DateTime DateCreated { get; set; }
#endregion
}
+16
View File
@@ -0,0 +1,16 @@
using System;
namespace TextSender_API.Models;
public class TextQueue
{
#region Properties
public int TextQueueID { get; set; }
public string? Status { get; set; }
public string? ContactPhoneNumber { get; set; }
public int TextID { get; set; }
public int UserID { get; set; }
public DateTime DateCreated { get; set; }
public DateTime ExecutionDate { get; set; }
#endregion
}
+17
View File
@@ -0,0 +1,17 @@
using Newtonsoft.Json;
namespace TextSender_API.Models;
public class Token
{
#region Properties
[JsonProperty("access_token")]
public string? AccessToken { get; set; }
[JsonProperty("token_type")]
public string? TokenType { get; set; }
[JsonProperty("issued")]
public DateTime? Issued { get; set; }
[JsonProperty("id")]
public string? UserId { get; set; }
#endregion
}
+46
View File
@@ -0,0 +1,46 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
namespace TextSender_API.Models;
public class User
{
#region Properties
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
[JsonProperty("id")]
public string? Id { get; set; }
[BsonElement("firstname")]
[JsonProperty("firstname")]
public string? Firstname { get; set; }
[BsonElement("lastname")]
[JsonProperty("lastname")]
public string? Lastname { get; set; }
[BsonElement("phonenumber")]
[JsonProperty("phone_number")]
public string? PhoneNumber { get; set; }
[BsonElement("username")]
[JsonProperty("username")]
public string? Username { get; set; }
[BsonElement("password")]
[JsonProperty("password")]
public string? Password { get; set; }
[BsonElement("datecreated")]
[JsonProperty("date_created")]
public DateTime? DateCreated { get; set; }
#endregion
}
public class Salt
{
#region Properties
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
[BsonElement("key")]
public byte[]? Key { get; set; }
[BsonElement("user_id")]
public string? UserId { get; set; }
#endregion
}
+63
View File
@@ -0,0 +1,63 @@
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using TextSender_API.Repositories;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddControllers().AddNewtonsoftJson();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = builder.Configuration["JWT:Audience"],
ValidIssuer = builder.Configuration["JWT:Issuer"],
RequireExpirationTime = false,
ValidateIssuerSigningKey = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"]!))
};
});
// var connectionString = builder.Configuration.GetConnectionString("MongoDBURI");
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
// app.MapControllers();
app.Run();
+41
View File
@@ -0,0 +1,41 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:22514",
"sslPort": 44361
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5168",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7154;http://localhost:5168",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
+6 -2
View File
@@ -5,7 +5,11 @@ An API used to send texts messages to multiple contacts.
## Getting started
Installing dependencies
`npm install`
```BASH
dotnet restore
```
Running server
`node app.js`
```BASH
dotnet run
```
+11
View File
@@ -0,0 +1,11 @@
namespace TextSender_API.Repositories;
public class BaseRepository
{
#region Fields
protected string? _connectionString;
protected string _databaseName = "textSender";
protected string? _tableName;
#endregion
}
+131
View File
@@ -0,0 +1,131 @@
using MongoDB.Driver;
using TextSender_API.Models;
namespace TextSender_API.Repositories;
public class ContactCreationQueueRepository : BaseRepository
{
#region Fields
private readonly IMongoCollection<ContactCreationQueue> _bookCollection;
#endregion
#region Constructors
public ContactCreationQueueRepository(string connectionString)
{
this._connectionString = connectionString;
this._tableName = "contactCreationQueue";
var client = this.InitializeClient();
this._bookCollection = client.GetDatabase(this._databaseName).GetCollection<ContactCreationQueue>(this._tableName);
}
#endregion
#region Metods
public Tuple<bool, ContactCreationQueue> Exists(string id)
{
var allQueue = this.RetrieveAll();
var result = allQueue.FirstOrDefault(q => q.Id!.Equals(id));
if (result != null)
{
return new Tuple<bool, ContactCreationQueue>(true, result!);
}
else
{
return new Tuple<bool, ContactCreationQueue>(false, new ContactCreationQueue());
}
}
public void Create(ContactCreationQueue queue)
{
this._bookCollection.InsertOne(queue);
}
public List<ContactCreationQueue> RetrieveAll()
{
return this._bookCollection.Find(_ => true).ToList();
}
public void Update(ContactCreationQueue queue)
{
var filter = Builders<ContactCreationQueue>.Filter.Eq(q => q.Id, queue.Id);
this._bookCollection.ReplaceOne(filter, queue);
}
public void Delete(ContactCreationQueue queue)
{
var filter = Builders<ContactCreationQueue>.Filter.Eq(q => q.Id, queue.Id);
this._bookCollection.DeleteOne(filter);
}
private MongoClient InitializeClient()
{
return new MongoClient(this._connectionString);
}
#endregion
}
public class ContactsRepository : BaseRepository
{
#region Fields
private readonly IMongoCollection<Contact> _bookCollection;
#endregion
#region Constructors
public ContactsRepository(string connectionString)
{
this._connectionString = connectionString;
this._tableName = "contacts";
var client = this.InitializeClient();
this._bookCollection = client.GetDatabase(this._databaseName).GetCollection<Contact>(this._tableName);
}
#endregion
#region Metods
public Tuple<bool, Contact> Exists(string id)
{
var allContacts= this.RetrieveAll();
var result = allContacts.FirstOrDefault(c => c.Id!.Equals(id));
if (result != null)
{
return new Tuple<bool, Contact>(true, result!);
}
else
{
return new Tuple<bool, Contact>(false, new Contact());
}
}
public void Create(Contact contact)
{
this._bookCollection.InsertOne(contact);
}
public List<Contact> RetrieveAll()
{
return this._bookCollection.Find(_ => true).ToList();
}
public void Update(Contact contact)
{
var filter = Builders<Contact>.Filter.Eq(c => c.Id, contact.Id);
this._bookCollection.ReplaceOne(filter, contact);
}
public void Delete(Contact contact)
{
var filter = Builders<Contact>.Filter.Eq(c => c.Id, contact.Id);
this._bookCollection.DeleteOne(filter);
}
private MongoClient InitializeClient()
{
return new MongoClient(this._connectionString);
}
#endregion
}
+136
View File
@@ -0,0 +1,136 @@
using MongoDB.Driver;
using TextSender_API.Models;
namespace TextSender_API.Repositories;
public class UsersRepository : BaseRepository
{
#region Fields
private readonly IMongoCollection<User> _bookCollection;
#endregion
#region Constructors
public UsersRepository(string connectionString)
{
this._connectionString = connectionString;
this._tableName = "users";
var client = this.InitializeClient();
this._bookCollection = client.GetDatabase(this._databaseName).GetCollection<User>(this._tableName);
}
#endregion
#region Metods
public Tuple<bool, User> Exists(User user)
{
var allUsers = this.RetrieveAllUsers();
var result = allUsers.FirstOrDefault(ea => ea.Username!.Equals(user.Username));
if (result != null)
{
return new Tuple<bool, User>(true, result!);
}
else
{
return new Tuple<bool, User>(false, new User());
}
}
public Tuple<bool, User> Exists(string userId)
{
var allUsers = this.RetrieveAllUsers();
var result = allUsers.FirstOrDefault(ea => ea.Id!.Equals(userId));
if (result != null)
{
return new Tuple<bool, User>(true, result!);
}
else
{
return new Tuple<bool, User>(false, new User());
}
}
public void CreateUser(User user)
{
this._bookCollection.InsertOne(user);
}
public List<User> RetrieveAllUsers()
{
return this._bookCollection.Find(_ => true).ToList();
}
public void Update(User user)
{
var filter = Builders<User>.Filter.Eq(u => u.Id, user.Id);
this._bookCollection.ReplaceOne(filter, user);
}
public void Delete(User user)
{
var filter = Builders<User>.Filter.Eq(u => u.Id, user.Id);
this._bookCollection.DeleteOne(filter);
}
private MongoClient InitializeClient()
{
return new MongoClient(this._connectionString);
}
#endregion
}
public class SaltRepository : BaseRepository
{
#region Fields
private readonly IMongoCollection<Salt> _bookCollection;
#endregion
#region Constructors
public SaltRepository(string connectionString)
{
this._connectionString = connectionString;
this._tableName = "salt";
var client = this.InitializeClient();
this._bookCollection = client.GetDatabase(this._databaseName).GetCollection<Salt>(this._tableName);
}
#endregion
#region Metods
public void Create(Salt salt)
{
this._bookCollection.InsertOne(salt);
}
public List<Salt> RetrieveAll()
{
return this._bookCollection.Find(_ => true).ToList();
}
public Salt Retrieve(string userId)
{
return this._bookCollection.Find(s => s.UserId!.Equals(userId)).FirstOrDefault();
}
public void Update(Salt salt)
{
var filter = Builders<Salt>.Filter.Eq(u => u.Id, salt.Id);
this._bookCollection.ReplaceOne(filter, salt);
}
public void Delete(Salt salt)
{
var filter = Builders<Salt>.Filter.Eq(u => u.Id, salt.Id);
this._bookCollection.DeleteOne(filter);
}
private MongoClient InitializeClient()
{
return new MongoClient(this._connectionString);
}
#endregion
}
+20
View File
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>TextSender_API</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.11" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.11" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.11" />
<PackageReference Include="MongoDB.Driver" Version="2.21.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.0.2" />
</ItemGroup>
</Project>
-74
View File
@@ -1,74 +0,0 @@
require('./models.js')
const express = require('express')
const app = express()
const port = 3000
app.use(express.json())
// User Routes
app.post('/api/v1/user/new', (req, res) => {
res.send('Posting')
console.log(req.body)
// const usr = new User()
let username = req.body['username']
// usr.username = username
console.log(`Username: ${username}`)
})
// User routes
// TODO: Not implemented
app.post('/api/v1/login', (req, res) => {
res.send('Implement');
})
// Text Routes
// Add a new text
// TODO: Not implemented
app.post('/api/v1/text/new', (req, res) => {
res.send('Create new text')
})
// Retrieve texts
// TODO: Not implemented
app.get('/api/v1/text', (req, res) => {
res.send('Create new text')
})
// Queue a text for sending
// TODO: Not implemented
app.post('/api/v1/text/queue', (req, res) => {
res.send('Queue text')
})
// Create contact
// TODO: Not implemented
app.post('/api/v1/contacts/new', (req, res) => {
res.send('Creating contact')
})
// Retrieving contacts
// TODO: Not implemented
app.get('/api/v1/contacts', (req, res) => {
res.send('Retrieving contact')
})
// Editing a contact
// TODO: Not implemented
app.patch('/api/v1/contacts', (req, res) => {
res.send('Editing contact')
})
// Deleting a contact
// TODO: Not implemented
app.delete('/api/v1/contacts', (req, res) => {
res.send('Delete contact')
})
app.listen(port, () => {
console.log(`textsender-api listening on port ${port}`)
})
+20
View File
@@ -0,0 +1,20 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"JWT": {
"Audience": "",
"Issuer": "",
"Secret": "",
"Subject": ""
},
"Paths": {
"ContactQueueDirectory": "/home/user/path/"
},
"ConnectionStrings": {
"MongoDBURI": "mongodb+srv://<username>:<password>@cluster0.abc.mongodb.net/?retryWrites=true&w=majority"
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"JWT": {
"Audience": "",
"Issuer": "",
"Secret": "",
"Subject": ""
},
"Paths": {
"ContactQueueDirectory": "/home/user/path/"
},
"ConnectionStrings": {
"MongoDBURI": "mongodb+srv://<username>:<password>@cluster0.abc.mongodb.net/?retryWrites=true&w=majority"
}
}
-14
View File
@@ -1,14 +0,0 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ObjectId = Schema.ObjectId;
const User = new Schema({
userID: { type: String },
firstName: { type: String },
lastName: { type: String },
phoneNumber: { type: String },
username: { type: String },
password: { type: String },
dateCreated: { type: Date, default: Date.now }
});
-16
View File
@@ -1,16 +0,0 @@
{
"name": "textsender-api",
"version": "0.1.0",
"description": "Web API for queueing text message notifications",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "kdeng00",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
"mongodb": "^5.3.0",
"mongoose": "^7.0.5"
}
}
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.002.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TextSender-API", "TextSender-API.csproj", "{AD184BBA-C715-4112-95A9-530F05323BEE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AD184BBA-C715-4112-95A9-530F05323BEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD184BBA-C715-4112-95A9-530F05323BEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD184BBA-C715-4112-95A9-530F05323BEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD184BBA-C715-4112-95A9-530F05323BEE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {70126048-DECC-43EE-9956-2CEF937B7AA1}
EndGlobalSection
EndGlobal