This commit is contained in:
38
Controllers/ApiMetaController.cs
Normal file
38
Controllers/ApiMetaController.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using rockfishCore.Util;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/meta")]
|
||||
public class ApiMetaController : Controller
|
||||
{
|
||||
//This controller is for fetching information *about* the server and the api itself
|
||||
|
||||
public ApiMetaController()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// GET: api/meta/serverversion
|
||||
[HttpGet("server_version")]
|
||||
public ActionResult Get()
|
||||
{
|
||||
return Ok(new {server_version=RfVersion.NumberOnly});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
118
Controllers/AuthController.cs
Normal file
118
Controllers/AuthController.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
//required to inject configuration in constructor
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
//Authentication controller
|
||||
public class AuthController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
public AuthController(rockfishContext context, IConfiguration configuration)//these two are injected, see startup.cs
|
||||
{
|
||||
_context = context;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
//AUTHENTICATE CREDS
|
||||
//RETURN JWT
|
||||
|
||||
[HttpPost("/authenticate")]
|
||||
public JsonResult PostCreds(string login, string password) //if was a json body then //public JsonResult PostCreds([FromBody] string login, [FromBody] string password)
|
||||
{
|
||||
int nFailedAuthDelay = 10000;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
//Make a failed pw wait
|
||||
System.Threading.Thread.Sleep(nFailedAuthDelay);
|
||||
return Json(new { msg = "authentication failed", error = 1 });
|
||||
}
|
||||
|
||||
//DANGER DANGER - this assumes all logins are different...boo bad code
|
||||
//FIXME
|
||||
//BUGBUG
|
||||
//TODO
|
||||
//ETC
|
||||
/*HOW TO FIX: because of the salt during login it must assume multiple users with the same login and
|
||||
fetch each in turn, get the salt, hash the entered password and compare to the password stored password
|
||||
|
||||
So, instead of singleordefault must be assumed to be a collection of users returned in this code:
|
||||
*/
|
||||
var user = _context.User.SingleOrDefault(m => m.Login == login);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
//Make a failed pw wait
|
||||
System.Threading.Thread.Sleep(nFailedAuthDelay);
|
||||
return Json(new { msg = "authentication failed", error = 1 });
|
||||
}
|
||||
|
||||
// string pwnew=Hasher.hash(user.Salt,"2df5cc611ee485d4aa897350daa045caa4015147ae34c6b7b363f1def605d305");
|
||||
|
||||
string hashed = Hasher.hash(user.Salt, password);
|
||||
if (hashed == user.Password)
|
||||
{
|
||||
//get teh secret from appsettings.json
|
||||
var secret = _configuration.GetSection("JWT").GetValue<string>("secret");
|
||||
byte[] secretKey = System.Text.Encoding.ASCII.GetBytes(secret);
|
||||
|
||||
var iat = new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds();
|
||||
var exp = new DateTimeOffset(DateTime.Now.AddDays(30)).ToUnixTimeSeconds();
|
||||
|
||||
//Generate a download token and store it with the user account and return it for the client
|
||||
Guid g = Guid.NewGuid();
|
||||
string dlkey = Convert.ToBase64String(g.ToByteArray());
|
||||
dlkey = dlkey.Replace("=", "");
|
||||
dlkey = dlkey.Replace("+", "");
|
||||
|
||||
user.DlKey = dlkey;
|
||||
user.DlKeyExp=exp;
|
||||
_context.User.Update(user);
|
||||
_context.SaveChanges();
|
||||
|
||||
|
||||
var payload = new Dictionary<string, object>()
|
||||
{
|
||||
{ "iat", iat.ToString() },
|
||||
{ "exp", exp.ToString() },
|
||||
{ "iss", "rockfishCore" },
|
||||
{ "id", user.Id.ToString() }
|
||||
};
|
||||
|
||||
|
||||
//NOTE: probably don't need Jose.JWT as am using Microsoft jwt stuff to validate routes so it should also be able to
|
||||
//issue tokens as well, but it looked cmplex and this works so unless need to remove in future keeping it.
|
||||
string token = Jose.JWT.Encode(payload, secretKey, Jose.JwsAlgorithm.HS256);
|
||||
//string jsonDecoded = Jose.JWT.Decode(token, secretKey);
|
||||
|
||||
return Json(new
|
||||
{
|
||||
ok = 1,
|
||||
issued = iat,
|
||||
expires = exp,
|
||||
token = token,
|
||||
dlkey = dlkey,
|
||||
name = user.Name,
|
||||
id = user.Id
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
//Make a failed pw wait
|
||||
System.Threading.Thread.Sleep(nFailedAuthDelay);
|
||||
return Json(new { msg = "authentication failed", error = 1 });
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
97
Controllers/AutocompleteController.cs
Normal file
97
Controllers/AutocompleteController.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
//requried to inject configuration in constructor
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/autocomplete")]
|
||||
[Authorize]
|
||||
public class AutocompleteController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
public AutocompleteController(rockfishContext context, IConfiguration configuration)//these two are injected, see startup.cs
|
||||
{
|
||||
_context = context;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpGet]
|
||||
public JsonResult GetAutocomplete(string acget, string query) //get parameters from url not body
|
||||
{
|
||||
//TODO: if, in future need more might want to make acget a csv string for multi table / field search
|
||||
//ACGET is a collection name and field 'purchase.name'
|
||||
//QUERY is just the characters typed so far, could be one or more
|
||||
|
||||
//default return value, emtpy suggestions
|
||||
var ret = new { suggestions = new string[] { } };
|
||||
|
||||
if (string.IsNullOrWhiteSpace(acget) || string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return Json(ret);
|
||||
}
|
||||
|
||||
|
||||
string[] acgetsplit = acget.Split('.');
|
||||
string col = acgetsplit[0];
|
||||
string fld = acgetsplit[1];
|
||||
|
||||
List<string> resultList = new List<string>();
|
||||
|
||||
using (var command = _context.Database.GetDbConnection().CreateCommand())
|
||||
{
|
||||
|
||||
command.CommandText = "SELECT DISTINCT " + fld + " From " + col + " where " + fld + " like @q ORDER BY " + fld + ";";
|
||||
var parameter = command.CreateParameter();
|
||||
parameter.ParameterName = "@q";
|
||||
parameter.Value = "%" + query + "%";
|
||||
command.Parameters.Add(parameter);
|
||||
|
||||
_context.Database.OpenConnection();
|
||||
using (var res = command.ExecuteReader())
|
||||
{
|
||||
|
||||
if (res.HasRows)
|
||||
{
|
||||
while (res.Read())
|
||||
{
|
||||
resultList.Add(res.GetString(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
_context.Database.CloseConnection();
|
||||
}
|
||||
return Json(new { suggestions = resultList });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
|
||||
|
||||
//enter 'a' in purchase product name get:
|
||||
|
||||
/*
|
||||
GET request url:
|
||||
https://rockfish.ayanova.com/api/autocomplete?acget=purchase.name&query=A
|
||||
|
||||
Response:
|
||||
{"suggestions":["CANCELED Up to 5","CANCELED RI","CANCELED WBI","QBI Renewal","OLI Renewal","MBI Renewal","CANCELED Outlook Schedule Export","Quick Notification Renewal","RI Renewal","WBI Renewal","Up to 5 Renewal","Outlook Schedule Export Renewal","Single Renewal","Export to XLS Renewal","Importexport.csv duplicate Renewal","Export To XLS Renewal","Up to 10 Renewal","Up to 20 RENEWAL","CANCELED Single","Quick Notification","Importexport.csv duplicate","CANCELED MBI","CANCELED OLI","CANCELED QBI","CANCELED Quick Notification","Quick Notification ","Key Administration","AyaNova Lite","PTI - Renewal"]} */
|
||||
271
Controllers/CustomerController.cs
Normal file
271
Controllers/CustomerController.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/Customer")]
|
||||
[Authorize]
|
||||
public class CustomerController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public CustomerController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
//Get api/customer/list
|
||||
[HttpGet("list")]
|
||||
public IEnumerable<dtoNameIdActiveItem> GetList()
|
||||
{
|
||||
|
||||
var res = from c in _context.Customer.OrderBy(c => c.Name)
|
||||
select new dtoNameIdActiveItem
|
||||
{
|
||||
active = c.Active,
|
||||
id = c.Id,
|
||||
name = c.Name
|
||||
};
|
||||
return res.ToList();
|
||||
}
|
||||
|
||||
|
||||
//Get api/customer/77/sitelist
|
||||
[HttpGet("{id}/sitelist")]
|
||||
public IEnumerable<dtoNameIdItem> GetSiteList([FromRoute] long id)
|
||||
{
|
||||
|
||||
var res = from c in _context.Site
|
||||
.Where(c => c.CustomerId.Equals(id)).OrderBy(c => c.Name)
|
||||
select new dtoNameIdItem
|
||||
{
|
||||
id = c.Id,
|
||||
name = c.Name
|
||||
};
|
||||
return res.ToList();
|
||||
}
|
||||
|
||||
|
||||
//Get api/customer/77/activesubbysite
|
||||
[HttpGet("{id}/activesubforsites")]
|
||||
public IEnumerable<dtoNameIdChildrenItem> GetActiveSubsForSites([FromRoute] long id)
|
||||
{
|
||||
|
||||
var res = from c in _context.Site
|
||||
.Where(c => c.CustomerId.Equals(id)).OrderBy(c => c.Name)
|
||||
select new dtoNameIdChildrenItem
|
||||
{
|
||||
id = c.Id,
|
||||
name = c.Name
|
||||
};
|
||||
|
||||
//Force immediate query execution
|
||||
var resList = res.ToList();
|
||||
|
||||
foreach (dtoNameIdChildrenItem child in resList)
|
||||
{
|
||||
var subs = from c in _context.Purchase
|
||||
.Where(c => c.SiteId.Equals(child.id))
|
||||
.Where(c => c.CancelDate == null)
|
||||
.OrderByDescending(c => c.PurchaseDate)
|
||||
select new dtoNameIdItem
|
||||
{
|
||||
id = c.Id,
|
||||
name = c.Name + " exp: " + DateUtil.EpochToString(c.ExpireDate, "d")
|
||||
};
|
||||
|
||||
foreach (dtoNameIdItem sub in subs)
|
||||
{
|
||||
child.children.Add(sub);
|
||||
}
|
||||
}
|
||||
|
||||
return resList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Get api/customer/77/sites
|
||||
[HttpGet("{id}/sites")]
|
||||
public IEnumerable<Site> GetSites([FromRoute] long id)
|
||||
{
|
||||
//from https://docs.microsoft.com/en-us/ef/core/querying/basic
|
||||
var sites = _context.Site
|
||||
.Where(b => b.CustomerId.Equals(id))
|
||||
.OrderByDescending(b => b.Id)
|
||||
.ToList();
|
||||
return sites;
|
||||
}
|
||||
|
||||
|
||||
// //Get api/customer/77/contacts
|
||||
// [HttpGet("{id}/contacts")]
|
||||
// public IEnumerable<Contact> GetContacts([FromRoute] long id)
|
||||
// {
|
||||
// var contacts = _context.Contact
|
||||
// .Where(b => b.CustomerId.Equals(id))
|
||||
// .OrderByDescending(b => b.Id)
|
||||
// .ToList();
|
||||
// return contacts;
|
||||
// }
|
||||
|
||||
|
||||
// //Get api/customer/77/notifications
|
||||
// [HttpGet("{id}/notifications")]
|
||||
// public IEnumerable<Notification> GetNotifications([FromRoute] long id)
|
||||
// {
|
||||
// var notifications = _context.Notification
|
||||
// .Where(b => b.CustomerId.Equals(id))
|
||||
// .OrderByDescending(b => b.SentDate)
|
||||
// .ToList();
|
||||
// return notifications;
|
||||
// }
|
||||
|
||||
|
||||
//Get api/customer/77/name
|
||||
[HttpGet("{id}/name")]
|
||||
public async Task<IActionResult> GetName([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
//BUGBUG: this is bombing once in a while
|
||||
//System.InvalidOperationException: Sequence contains no elements
|
||||
//on this client url
|
||||
//http://localhost:5000/default.htm#!/customerSites/85
|
||||
|
||||
var ret = await _context.Customer
|
||||
.Select(r => new { r.Id, r.Name })
|
||||
.Where(r => r.Id == id)
|
||||
.FirstAsync();
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//-------------
|
||||
//CRUD ROUTES
|
||||
//-------------
|
||||
|
||||
// GET: api/Customer
|
||||
//????? DA FUCK IS THIS ROUTE FOR ?????
|
||||
[HttpGet]
|
||||
public IEnumerable<Customer> GetCustomer()
|
||||
{
|
||||
return _context.Customer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// GET: api/Customer/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetCustomer([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var customer = await _context.Customer.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (customer == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(customer);
|
||||
}
|
||||
|
||||
// PUT: api/Customer/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutCustomer([FromRoute] long id, [FromBody] Customer customer)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != customer.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(customer).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!CustomerExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/Customer
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostCustomer([FromBody] Customer customer)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.Customer.Add(customer);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetCustomer", new { id = customer.Id }, customer);
|
||||
}
|
||||
|
||||
// DELETE: api/Customer/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteCustomer([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var customer = await _context.Customer.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (customer == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.Customer.Remove(customer);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(customer);
|
||||
}
|
||||
|
||||
private bool CustomerExists(long id)
|
||||
{
|
||||
return _context.Customer.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Controllers/FetchController.cs
Normal file
84
Controllers/FetchController.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("text/plain")]
|
||||
[Route("fetch")]
|
||||
|
||||
public class FetchController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public FetchController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// GET: fetch/somecode/bob@bob.com
|
||||
[HttpGet("{code}/{email}")]
|
||||
public async Task<IActionResult> Get([FromRoute] string code, [FromRoute] string email)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var rec = await _context.License.SingleOrDefaultAsync(m => m.Code == code.Trim() && m.Email == email.Trim().ToLowerInvariant() && m.Fetched == false);
|
||||
|
||||
if (rec == null)
|
||||
{
|
||||
//delay, could be someone fishing for a key, make it painful
|
||||
//Have verified this is safe, won't affect other jobs on server
|
||||
//happening concurrently or other requests to server
|
||||
System.Threading.Thread.Sleep(10000);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
|
||||
rec.Fetched = true;
|
||||
rec.DtFetched = DateUtil.NowAsEpoch();
|
||||
//This might be flaky if behind some other stuff
|
||||
//rec.FetchFrom = HttpContext.Connection.RemoteIpAddress.ToString();
|
||||
_context.Entry(rec).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!LicenseExists(rec.Id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(rec.Key);
|
||||
//return Ok(new {key=rec.Key});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private bool LicenseExists(long id)
|
||||
{
|
||||
return _context.License.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
238
Controllers/LicenseController.cs
Normal file
238
Controllers/LicenseController.cs
Normal file
@@ -0,0 +1,238 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;//required for authorize attribute
|
||||
using System.Security.Claims;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
//case 3233
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
//requried to inject configuration in constructor
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
//This is an 80 character line of text:
|
||||
//##############################################################################
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
//Authentication controller
|
||||
[Produces("application/json")]
|
||||
[Route("api/License")]
|
||||
[Authorize]
|
||||
public class LicenseController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
public LicenseController(rockfishContext context, IConfiguration configuration)//these two are injected, see startup.cs
|
||||
{
|
||||
_context = context;//Keeping db context here for future where I will be inserting the keys into the db upon generation
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//KEYGEN ROUTES
|
||||
|
||||
//Given key options return the message ready to send to the user
|
||||
//Note this returns a key as plain text content result
|
||||
//called by rockfish client app.license.js (who calls app.api.createLicense)
|
||||
[HttpPost("generate")]
|
||||
public ContentResult Generate([FromBody] dtoKeyOptions ko)
|
||||
{
|
||||
var templates = _context.LicenseTemplates.ToList()[0];
|
||||
ko.authorizedUserKeyGeneratorStamp = GetRFAuthorizedUserStamp();
|
||||
string sKey = KeyFactory.GetKeyReply(ko, templates, _context);
|
||||
return Content(sKey);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Fetch key request emails
|
||||
[HttpGet("requests")]
|
||||
public JsonResult GetRequests()
|
||||
{
|
||||
return Json(TrialKeyRequestHandler.Requests());
|
||||
}
|
||||
|
||||
//Fetch generated responses
|
||||
//Generate a key from a license key request email
|
||||
//called by rockfish client app.licenseRequestEdit.js (who calls app.api.generateFromRequest)
|
||||
[HttpGet("generateFromRequest/{uid}")]
|
||||
public JsonResult GenerateFromRequest([FromRoute] uint uid)
|
||||
{
|
||||
var templates = _context.LicenseTemplates.ToList()[0];
|
||||
return Json(TrialKeyRequestHandler.GenerateFromRequest(uid, templates, GetRFAuthorizedUserStamp(), _context));
|
||||
}
|
||||
|
||||
// SEND REQUESTED KEY ROUTE
|
||||
//app.post('/api/license/email_response', function (req, res) {
|
||||
[HttpPost("email_response")]
|
||||
public JsonResult EmailResponse([FromBody] dtoKeyRequestResponse k)
|
||||
{
|
||||
return Json(TrialKeyRequestHandler.SendTrialRequestResponse(k));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// STORED LICENSE KEY CRUD ROUTES
|
||||
//
|
||||
|
||||
//case 3233 Get api/license/list a list of generated licenses
|
||||
[HttpGet("list")]
|
||||
public IEnumerable<dtoLicenseListItem> GetList()
|
||||
{
|
||||
var res = from c in _context.License.OrderByDescending(c => c.DtCreated)
|
||||
select new dtoLicenseListItem
|
||||
{
|
||||
id = c.Id,
|
||||
created = c.DtCreated,
|
||||
regto = c.RegTo,
|
||||
fetched = c.Fetched,
|
||||
trial = (c.CustomerId==0)
|
||||
};
|
||||
return res.ToList();
|
||||
}
|
||||
|
||||
//case 3233 GET: api/License/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetLicense([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var l = await _context.License.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (l == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
string customerName = "<TRIAL>";
|
||||
|
||||
if (l.CustomerId != 0)
|
||||
{
|
||||
if (_context.Customer.Any(e => e.Id == l.CustomerId))
|
||||
{
|
||||
|
||||
var cust = await _context.Customer
|
||||
.Select(r => new { r.Id, r.Name })
|
||||
.Where(r => r.Id == l.CustomerId)
|
||||
.FirstAsync();
|
||||
customerName=cust.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
customerName = "< Customer " + l.CustomerId.ToString() + " not found (deleted?) >";
|
||||
}
|
||||
}
|
||||
|
||||
var ret = new
|
||||
{
|
||||
regTo = l.RegTo,
|
||||
customerName = customerName,
|
||||
dtcreated = l.DtCreated,
|
||||
email = l.Email,
|
||||
code = l.Code,
|
||||
fetched = l.Fetched,
|
||||
dtfetched = l.DtFetched,
|
||||
fetchFrom = l.FetchFrom,
|
||||
key = l.Key
|
||||
};
|
||||
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
// DELETE: api/License/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteLicense([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var rec = await _context.License.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (rec == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.License.Remove(rec);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(rec);
|
||||
}
|
||||
|
||||
|
||||
// PUT: api/license/5/true
|
||||
//Update a license and set it's fetched property only
|
||||
//used by client to make a license fetchable or not ad-hoc
|
||||
[HttpPut("fetched/{id}/{isFetched}")]
|
||||
public async Task<IActionResult> PutLicenseFetched([FromRoute] long id, [FromRoute] bool isFetched)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var rec = await _context.License.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (rec == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
rec.Fetched = isFetched;
|
||||
|
||||
_context.Entry(rec).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!LicenseExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private bool LicenseExists(long id)
|
||||
{
|
||||
return _context.License.Any(e => e.Id == id);
|
||||
}
|
||||
|
||||
//===================== UTILITY =============
|
||||
private string GetRFAuthorizedUserStamp()
|
||||
{
|
||||
foreach (Claim c in User.Claims)
|
||||
{
|
||||
if (c.Type == "id")
|
||||
{
|
||||
return "RFID" + c.Value;
|
||||
}
|
||||
}
|
||||
return "RFID unknown";
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
127
Controllers/LicenseTemplatesController.cs
Normal file
127
Controllers/LicenseTemplatesController.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/LicenseTemplates")]
|
||||
[Authorize]
|
||||
public class LicenseTemplatesController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public LicenseTemplatesController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET: api/LicenseTemplates
|
||||
[HttpGet]
|
||||
public IEnumerable<LicenseTemplates> GetLicenseTemplates()
|
||||
{
|
||||
return _context.LicenseTemplates;
|
||||
}
|
||||
|
||||
// GET: api/LicenseTemplates/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetLicenseTemplates([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var licenseTemplates = await _context.LicenseTemplates.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (licenseTemplates == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(licenseTemplates);
|
||||
}
|
||||
|
||||
// PUT: api/LicenseTemplates/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutLicenseTemplates([FromRoute] long id, [FromBody] LicenseTemplates licenseTemplates)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != licenseTemplates.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(licenseTemplates).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!LicenseTemplatesExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/LicenseTemplates
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostLicenseTemplates([FromBody] LicenseTemplates licenseTemplates)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.LicenseTemplates.Add(licenseTemplates);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetLicenseTemplates", new { id = licenseTemplates.Id }, licenseTemplates);
|
||||
}
|
||||
|
||||
// DELETE: api/LicenseTemplates/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteLicenseTemplates([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var licenseTemplates = await _context.LicenseTemplates.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (licenseTemplates == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.LicenseTemplates.Remove(licenseTemplates);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(licenseTemplates);
|
||||
}
|
||||
|
||||
private bool LicenseTemplatesExists(long id)
|
||||
{
|
||||
return _context.LicenseTemplates.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
80
Controllers/MailController.cs
Normal file
80
Controllers/MailController.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;//required for authorize attribute
|
||||
using System.Security.Claims;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
//requried to inject configuration in constructor
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
//Authentication controller
|
||||
[Produces("application/json")]
|
||||
[Route("api/Mail")]
|
||||
[Authorize]
|
||||
public class MailController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
public MailController(rockfishContext context, IConfiguration configuration)
|
||||
{
|
||||
_context = context;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
//Fetch inbox emails from sales and support
|
||||
[HttpGet("salesandsupportsummaries")]
|
||||
public JsonResult GetSalesAndSupportSummaries()
|
||||
{
|
||||
return Json(Util.RfMail.GetSalesAndSupportSummaries());
|
||||
}
|
||||
|
||||
//Fetch a preview of a message
|
||||
[HttpGet("preview/{account}/{folder}/{id}")]
|
||||
public JsonResult GetPreview([FromRoute] string account, [FromRoute] string folder, [FromRoute] uint id)
|
||||
{
|
||||
return new JsonResult(Util.RfMail.GetMessagePreview(account, folder, id));
|
||||
//return Json(new { message = Util.RfMail.GetMessagePreview(account, folder, id) });
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("reply/{account}/{id}")]
|
||||
public JsonResult Reply([FromRoute] string account, [FromRoute] uint id, [FromBody] dtoReplyMessageItem m)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(m.composition))
|
||||
{
|
||||
return Json(new { msg = "MailController:Reply->There is no reply text", error = 1 });
|
||||
}
|
||||
RfMail.rfMailAccount acct = RfMail.rfMailAccount.support;
|
||||
if (account.Contains("sales"))
|
||||
{
|
||||
acct = RfMail.rfMailAccount.sales;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
RfMail.ReplyMessage(id, acct, m.composition, true, m.trackDelivery);
|
||||
return Json(new { msg = "message sent", ok = 1 });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new { msg = ex.Message, error = 1 });
|
||||
}
|
||||
}
|
||||
// //------------------------------------------------------
|
||||
|
||||
public class dtoReplyMessageItem
|
||||
{
|
||||
public string composition;
|
||||
public bool trackDelivery;
|
||||
|
||||
}
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
129
Controllers/ProductController.cs
Normal file
129
Controllers/ProductController.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/Product")]
|
||||
[Authorize]
|
||||
public class ProductController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public ProductController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// GET: api/Product
|
||||
[HttpGet]
|
||||
public IEnumerable<Product> GetProduct()
|
||||
{
|
||||
return _context.Product;
|
||||
}
|
||||
|
||||
// GET: api/Product/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetProduct([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var Product = await _context.Product.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (Product == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(Product);
|
||||
}
|
||||
|
||||
// PUT: api/Product/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutProduct([FromRoute] long id, [FromBody] Product Product)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != Product.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(Product).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!ProductExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/Product
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostProduct([FromBody] Product Product)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.Product.Add(Product);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetProduct", new { id = Product.Id }, Product);
|
||||
}
|
||||
|
||||
// DELETE: api/Product/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteProduct([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var Product = await _context.Product.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (Product == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.Product.Remove(Product);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(Product);
|
||||
}
|
||||
|
||||
private bool ProductExists(long id)
|
||||
{
|
||||
return _context.Product.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
248
Controllers/PurchaseController.cs
Normal file
248
Controllers/PurchaseController.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
using System.IO;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/Purchase")]
|
||||
[Authorize]
|
||||
public class PurchaseController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public PurchaseController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
//Get unique product code / name combinations
|
||||
[HttpGet("productcodes")]
|
||||
public JsonResult GetUniqueProductCodes()
|
||||
{
|
||||
//Note: will return dupes as comparer sees misspellings as unique in name, but I'm keeping it that way
|
||||
//so the name misspellings are apparent and can be fixed up
|
||||
var l = _context.Purchase.Select(p => new { p.ProductCode, p.Name }).Distinct().OrderBy(p => p.ProductCode);
|
||||
return Json(l);
|
||||
}
|
||||
|
||||
// GET: api/Purchase
|
||||
[HttpGet]
|
||||
public IEnumerable<Purchase> GetPurchase()
|
||||
{
|
||||
return _context.Purchase;
|
||||
}
|
||||
|
||||
// GET: api/Purchase/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetPurchase([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var purchase = await _context.Purchase.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (purchase == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(purchase);
|
||||
}
|
||||
|
||||
// PUT: api/Purchase/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutPurchase([FromRoute] long id, [FromBody] Purchase purchase)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != purchase.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(purchase).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!PurchaseExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
updateCustomerActive(purchase.CustomerId);
|
||||
ParseOrderAndUpdateEmail(purchase);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/Purchase
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostPurchase([FromBody] Purchase purchase)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.Purchase.Add(purchase);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
updateCustomerActive(purchase.CustomerId);
|
||||
ParseOrderAndUpdateEmail(purchase);
|
||||
|
||||
return CreatedAtAction("GetPurchase", new { id = purchase.Id }, purchase);
|
||||
}
|
||||
|
||||
// DELETE: api/Purchase/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeletePurchase([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var purchase = await _context.Purchase.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (purchase == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var customerId = purchase.CustomerId;
|
||||
|
||||
_context.Purchase.Remove(purchase);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
updateCustomerActive(customerId);
|
||||
return Ok(purchase);
|
||||
}
|
||||
|
||||
private bool PurchaseExists(long id)
|
||||
{
|
||||
return _context.Purchase.Any(e => e.Id == id);
|
||||
}
|
||||
|
||||
//check if customer has any active subs and flag accordingly
|
||||
private void updateCustomerActive(long customerId)
|
||||
{
|
||||
var cust = _context.Customer.First(m => m.Id == customerId);
|
||||
bool active = hasActiveSubs(customerId);
|
||||
if (cust.Active != active)
|
||||
{
|
||||
cust.Active = active;
|
||||
_context.Customer.Update(cust);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
//check if a customer has active subscriptions
|
||||
private bool hasActiveSubs(long customerId)
|
||||
{
|
||||
return _context.Purchase.Where(m => m.CustomerId == customerId && m.CancelDate == null).Count() > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ParseOrderAndUpdateEmail(Purchase purchase)
|
||||
{
|
||||
Dictionary<string, string> d = ParseShareItOrderData(purchase.Notes);
|
||||
if (d.Count < 1)
|
||||
return;
|
||||
|
||||
if (d.ContainsKey("E-Mail"))
|
||||
{
|
||||
string email = d["E-Mail"];
|
||||
if (!string.IsNullOrWhiteSpace(email))
|
||||
{
|
||||
//append or set it to the customer adminAddress if it isn't already present there
|
||||
//in this way the last address listed is the most recent purchase address there
|
||||
var cust = _context.Customer.First(m => m.Id == purchase.CustomerId);
|
||||
Util.CustomerUtils.AddAdminEmailIfNotPresent(cust, email);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// parse out the x=y values in a ShareIt order document
|
||||
/// </summary>
|
||||
/// <param name="order"></param>
|
||||
/// <returns></returns>
|
||||
Dictionary<string, string> ParseShareItOrderData(string order)
|
||||
{
|
||||
Dictionary<string, string> ret = new Dictionary<string, string>();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(order))
|
||||
{
|
||||
//parse the email address out of the order
|
||||
StringReader sr = new StringReader(order);
|
||||
string aLine = string.Empty;
|
||||
while (true)
|
||||
{
|
||||
aLine = sr.ReadLine();
|
||||
if (aLine != null)
|
||||
{
|
||||
if (aLine.Contains("="))
|
||||
{
|
||||
string[] item = aLine.Split("=");
|
||||
ret.Add(item[0].Trim(), item[1].Trim());
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
Original JS code for this from client end of things:
|
||||
// Loop through all lines
|
||||
for (var j = 0; j < lines.length; j++) {
|
||||
var thisLine = lines[j];
|
||||
if (thisLine.includes("=")) {
|
||||
var thisElement = thisLine.split("=");
|
||||
purchaseData[thisElement[0].trim()] = thisElement[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
//Now have an object with the value pairs in it
|
||||
if (purchaseData["ShareIt Ref #"]) {
|
||||
$("#salesOrderNumber").val(purchaseData["ShareIt Ref #"]);
|
||||
}
|
||||
|
||||
// if (purchaseData["E-Mail"]) {
|
||||
// $("#email").val(purchaseData["E-Mail"]);
|
||||
// }
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
201
Controllers/ReportController.cs
Normal file
201
Controllers/ReportController.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
//requried to inject configuration in constructor
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/report")]
|
||||
[Authorize]
|
||||
public class ReportController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
public ReportController(rockfishContext context, IConfiguration configuration)//these two are injected, see startup.cs
|
||||
{
|
||||
_context = context;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
//Get expiring subscriptions list
|
||||
//
|
||||
[HttpGet("expires")]
|
||||
public JsonResult Get()
|
||||
{
|
||||
var customerList = _context.Customer.Select(p => new { p.Id, p.Name });
|
||||
|
||||
var rawExpiresList = _context.Purchase
|
||||
.Where(p => p.ExpireDate != null && p.CancelDate == null)
|
||||
.OrderBy(p => p.ExpireDate)
|
||||
.Select(p => new expiresResultItem { id = p.Id, expireDate = p.ExpireDate, name = p.Name, site_id = p.SiteId, customerId = p.CustomerId })
|
||||
.ToList();
|
||||
|
||||
foreach (expiresResultItem i in rawExpiresList)
|
||||
{
|
||||
i.Customer = customerList.First(p => p.Id == i.customerId).Name;
|
||||
}
|
||||
|
||||
return Json(rawExpiresList);
|
||||
}
|
||||
|
||||
//dto classes for route
|
||||
public class expiresResultItem
|
||||
{
|
||||
public long id;
|
||||
public string name;
|
||||
public string Customer;
|
||||
public long customerId;
|
||||
public long? expireDate;
|
||||
public long site_id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
//Get unique product code / name combinations
|
||||
//
|
||||
[HttpPost("emailsforproductcodes")]
|
||||
public JsonResult GetUniqueEmailsByProductCodes([FromBody] requestEmailsForProductCodes req)
|
||||
{
|
||||
var customerList = _context.Customer.Select(p => new { p.Id, p.DoNotContact });
|
||||
|
||||
List<long> rawCustomerIds = new List<long>();
|
||||
|
||||
foreach (string pcode in req.products)
|
||||
{
|
||||
//fetch all customer id's from purchase collection that match product codes submitted
|
||||
var l = _context.Purchase.Where(p => p.ProductCode == pcode).Select(p => p.CustomerId).Distinct();
|
||||
rawCustomerIds.AddRange(l.ToList());
|
||||
}
|
||||
|
||||
//uniquify the customer list
|
||||
List<long> uniqueCustomerIds = rawCustomerIds.Distinct().ToList();
|
||||
|
||||
//container for the raw email lists built serially
|
||||
List<string> rawEmails = new List<string>();
|
||||
|
||||
foreach (long cid in uniqueCustomerIds)
|
||||
{
|
||||
//skip if do not contact and not explicitly including do not contact
|
||||
if (customerList.First(p => p.Id == cid).DoNotContact && req.ckNoContact != true)
|
||||
continue;
|
||||
|
||||
//get all raw email values for this client from db
|
||||
//there may be dupes or even multiple in one
|
||||
rawEmails.AddRange(getEmailsForClient(cid));
|
||||
}
|
||||
|
||||
//Now clean up the list and sort and uniquify it
|
||||
List<string> cleanedEmails = cleanupRawEmailList(rawEmails);
|
||||
|
||||
return Json(cleanedEmails);
|
||||
}
|
||||
|
||||
|
||||
//Given a client id find all unique email address in db for that client
|
||||
private List<string> getEmailsForClient(long id)
|
||||
{
|
||||
List<string> ret = new List<string>();
|
||||
|
||||
//New for RF 6
|
||||
var cust = _context.Customer.Where(p => p.Id == id).FirstOrDefault();
|
||||
if (cust != null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(cust.AdminEmail))
|
||||
ret.Add(cust.AdminEmail);
|
||||
if (!string.IsNullOrWhiteSpace(cust.SupportEmail))
|
||||
ret.Add(cust.SupportEmail);
|
||||
}
|
||||
|
||||
//TOASTED for RF 6
|
||||
//search contact, trial, purchase, incident (optionally)
|
||||
// ret.AddRange(_context.Purchase.Where(p => p.CustomerId == id).Select(p => p.Email).Distinct().ToList());
|
||||
// ret.AddRange(_context.Contact.Where(p => p.CustomerId == id).Select(p => p.Email).Distinct().ToList());
|
||||
// ret.AddRange(_context.Trial.Where(p => p.CustomerId == id).Select(p => p.Email).Distinct().ToList());
|
||||
// if (includeIncidentEmails)
|
||||
// ret.AddRange(_context.Incident.Where(p => p.CustomerId == id).Select(p => p.Email).Distinct().ToList());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
//break out multiple also trim whitespace and lowercase and uniquify them
|
||||
private List<string> cleanupRawEmailList(List<string> src)
|
||||
{
|
||||
List<string> ret = new List<string>();
|
||||
foreach (string rawAddress in src)
|
||||
{
|
||||
//count the @'s, if there are more than one then get splittn'
|
||||
int count = rawAddress.Count(f => f == '@');
|
||||
|
||||
if (count < 1)
|
||||
continue;//no address, skip this one
|
||||
|
||||
//a little cleanup based on what I had to do in og rockfish
|
||||
string semiRawAddress = rawAddress.Replace(", ", ",").TrimEnd('>').TrimEnd(',').Trim();
|
||||
|
||||
//there's at least one address in there, maybe more
|
||||
if (count == 1)
|
||||
ret.Add(semiRawAddress.ToLowerInvariant());
|
||||
else
|
||||
{
|
||||
//there are multiple so break it apart
|
||||
//determine break character could be a space or a comma
|
||||
char breakChar;
|
||||
if (semiRawAddress.Contains(','))
|
||||
{
|
||||
breakChar = ',';
|
||||
}
|
||||
else if (semiRawAddress.Contains(' '))
|
||||
{
|
||||
breakChar = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
//no break character, it's a bad address, highlight it so it can be fixed when seen
|
||||
ret.Add("_BAD_EMAIL_" + semiRawAddress + "_BAD_EMAIL_");
|
||||
continue;
|
||||
}
|
||||
|
||||
//Ok if we made it here then we can split out the emails
|
||||
string[] splits = semiRawAddress.Split(breakChar);
|
||||
foreach (string s in splits)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(s) && s.Contains("@"))
|
||||
ret.Add(s.ToLowerInvariant().Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//return distinct values only that are ordered by the email domain
|
||||
return ret.Distinct().OrderBy(email => email.Split('@')[1]).ToList();
|
||||
}
|
||||
|
||||
|
||||
//dto classes for route
|
||||
public class requestEmailsForProductCodes
|
||||
{
|
||||
public string[] products;
|
||||
public bool ckIncidental;
|
||||
public bool ckNoContact;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
|
||||
220
Controllers/RfCaseBlobController.cs
Normal file
220
Controllers/RfCaseBlobController.cs
Normal file
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/RfCaseBlob")]
|
||||
public class RfCaseBlobController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public RfCaseBlobController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET: api/RfCaseBlob
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public IEnumerable<RfCaseBlob> GetRfCaseBlob()
|
||||
{
|
||||
var c = from s in _context.RfCaseBlob select s;
|
||||
c = c.OrderBy(s => s.Name);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpPost("upload")]
|
||||
public IActionResult UploadFilesAjax([FromQuery] string rfcaseid)
|
||||
{//http://www.binaryintellect.net/articles/f1cee257-378a-42c1-9f2f-075a3aed1d98.aspx
|
||||
|
||||
//need a proper case ID to do this
|
||||
if (string.IsNullOrWhiteSpace(rfcaseid) || rfcaseid == "new")
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
var files = Request.Form.Files;
|
||||
int nCount=0;
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (file.Length > 0)
|
||||
{
|
||||
using (var fileStream = file.OpenReadStream())
|
||||
using (var ms = new System.IO.MemoryStream())
|
||||
{
|
||||
fileStream.CopyTo(ms);
|
||||
var fileBytes = ms.ToArray();
|
||||
RfCaseBlob blob=new RfCaseBlob();
|
||||
blob.RfCaseId=Convert.ToInt64(rfcaseid);
|
||||
blob.Name=file.FileName;
|
||||
blob.File=fileBytes;
|
||||
_context.RfCaseBlob.Add(blob);
|
||||
_context.SaveChanges();
|
||||
nCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string message = $"{nCount} file(s) uploaded successfully!";
|
||||
|
||||
return Json(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpGet("download/{id}")]
|
||||
public ActionResult Download([FromRoute] long id, [FromQuery] string dlkey)
|
||||
{//https://dotnetcoretutorials.com/2017/03/12/uploading-files-asp-net-core/
|
||||
//https://stackoverflow.com/questions/45763149/asp-net-core-jwt-in-uri-query-parameter/45811270#45811270
|
||||
|
||||
if (string.IsNullOrWhiteSpace(dlkey))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
//get user by key, if not found then reject
|
||||
//If user dlkeyexp has not expired then return file
|
||||
var user = _context.User.SingleOrDefault(m => m.DlKey == dlkey);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var unixdtnow = new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds();
|
||||
if (user.DlKeyExp < unixdtnow)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
//Ok, user has a valid download key and it's not expired yet so get the file
|
||||
var f = _context.RfCaseBlob.SingleOrDefault(m => m.Id == id);
|
||||
if (f == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var extension = System.IO.Path.GetExtension(f.Name);
|
||||
|
||||
string mimetype = "application/x-msdownload";
|
||||
if (!string.IsNullOrWhiteSpace(extension))
|
||||
{
|
||||
mimetype = Util.MimeTypeMap.GetMimeType(extension);
|
||||
}
|
||||
|
||||
Response.Headers.Add("Content-Disposition", "inline; filename=" + f.Name);
|
||||
return File(f.File, mimetype);//NOTE: if you don't specify a filename here then the above content disposition header takes effect, if you do then the 'File(' method sets it as attachment automatically
|
||||
|
||||
}
|
||||
|
||||
|
||||
// GET: api/RfCaseBlob/5
|
||||
[HttpGet("{id}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> GetRfCaseBlob([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var RfCaseBlob = await _context.RfCaseBlob.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (RfCaseBlob == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(RfCaseBlob);
|
||||
}
|
||||
|
||||
// PUT: api/RfCaseBlob/5
|
||||
[HttpPut("{id}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> PutRfCaseBlob([FromRoute] long id, [FromBody] RfCaseBlob RfCaseBlob)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != RfCaseBlob.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(RfCaseBlob).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!RfCaseBlobExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/RfCaseBlob
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> PostRfCaseBlob([FromBody] RfCaseBlob RfCaseBlob)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.RfCaseBlob.Add(RfCaseBlob);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetRfCaseBlob", new { id = RfCaseBlob.Id }, RfCaseBlob);
|
||||
}
|
||||
|
||||
// DELETE: api/RfCaseBlob/5
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> DeleteRfCaseBlob([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var RfCaseBlob = await _context.RfCaseBlob.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (RfCaseBlob == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.RfCaseBlob.Remove(RfCaseBlob);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(RfCaseBlob);
|
||||
}
|
||||
|
||||
private bool RfCaseBlobExists(long id)
|
||||
{
|
||||
return _context.RfCaseBlob.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
230
Controllers/RfCaseController.cs
Normal file
230
Controllers/RfCaseController.cs
Normal file
@@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/RfCase")]
|
||||
[Authorize]
|
||||
public class RfCaseController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public RfCaseController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
//Get api/rfcase/list
|
||||
[HttpGet("list")]
|
||||
public JsonResult GetList(long? Project, bool? Open, int? Priority, string Search)
|
||||
{
|
||||
//NOTE: Unlike FogBugz, does not open a case directly from the search
|
||||
//uses a separate route (simple get), so this route doesn't need to worry about it.
|
||||
|
||||
|
||||
//FILTERS
|
||||
var cases = _context.RfCase.AsQueryable();
|
||||
|
||||
|
||||
//TODO: this is case sensitive currently
|
||||
//need to figure out a way to make it insensitive
|
||||
if (!string.IsNullOrWhiteSpace(Search))
|
||||
{
|
||||
cases = _context.RfCase.Where(s => s.Notes.Contains(Search)
|
||||
|| s.ReleaseNotes.Contains(Search)
|
||||
|| s.ReleaseVersion.Contains(Search)
|
||||
|| s.Title.Contains(Search)
|
||||
);
|
||||
}
|
||||
|
||||
//project
|
||||
if (Project != null && Project != 0)
|
||||
{
|
||||
cases = cases.Where(s => s.RfCaseProjectId == Project);
|
||||
}
|
||||
|
||||
//open
|
||||
if (Open != null)
|
||||
{
|
||||
if (Open == true)
|
||||
{
|
||||
cases = cases.Where(s => s.DtClosed == null);
|
||||
}
|
||||
else
|
||||
{
|
||||
cases = cases.Where(s => s.DtClosed != null);
|
||||
}
|
||||
}
|
||||
|
||||
//priority
|
||||
if (Priority != null && Priority > 0 && Priority < 6)
|
||||
{
|
||||
cases = cases.Where(s => s.Priority == Priority);
|
||||
|
||||
}
|
||||
|
||||
cases = cases.OrderBy(s => s.Priority).ThenByDescending(s => s.Id);
|
||||
return new JsonResult(cases);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// //Get api/rfcase/77/attachments
|
||||
// [HttpGet("{id}/attachments")]
|
||||
// public IEnumerable<dtoNameIdItem> GetAttachmentList([FromRoute] long id)
|
||||
// {
|
||||
// var res = from c in _context.RfCaseBlob
|
||||
// .Where(c => c.RfCaseId.Equals(id)).OrderBy(c => c.Id)//order by entry order
|
||||
// select new dtoNameIdItem
|
||||
// {
|
||||
// id = c.Id,
|
||||
// name = c.Name
|
||||
// };
|
||||
// return res.ToList();
|
||||
// }
|
||||
|
||||
|
||||
//Get api/rfcase/77/attachments
|
||||
[HttpGet("{id}/attachments")]
|
||||
public ActionResult GetAttachmentList([FromRoute] long id)
|
||||
{
|
||||
var res = from c in _context.RfCaseBlob
|
||||
.Where(c => c.RfCaseId.Equals(id)).OrderBy(c => c.Id)//order by entry order
|
||||
select new dtoNameIdItem
|
||||
{
|
||||
id = c.Id,
|
||||
name = c.Name
|
||||
};
|
||||
|
||||
//Took forever to find this out
|
||||
//How to get user id from jwt token in controller
|
||||
//http://www.jerriepelser.com/blog/aspnetcore-jwt-saving-bearer-token-as-claim/
|
||||
var userId = User.FindFirst("id")?.Value;
|
||||
long luserId=long.Parse(userId);
|
||||
|
||||
|
||||
var user = _context.User.SingleOrDefault(m => m.Id == luserId);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return new JsonResult(new { dlkey = user.DlKey, attach = res.ToList() });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// GET: api/RfCase
|
||||
[HttpGet]
|
||||
public IEnumerable<RfCase> GetRfCase()
|
||||
{
|
||||
return _context.RfCase;
|
||||
}
|
||||
|
||||
|
||||
// GET: api/RfCase/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetRfCase([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var RfCase = await _context.RfCase.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (RfCase == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(RfCase);
|
||||
}
|
||||
|
||||
// PUT: api/RfCase/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutRfCase([FromRoute] long id, [FromBody] RfCase RfCase)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != RfCase.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(RfCase).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!RfCaseExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/RfCase
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostRfCase([FromBody] RfCase RfCase)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.RfCase.Add(RfCase);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetRfCase", new { id = RfCase.Id }, RfCase);
|
||||
}
|
||||
|
||||
// DELETE: api/RfCase/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteRfCase([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var RfCase = await _context.RfCase.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (RfCase == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.RfCase.Remove(RfCase);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(RfCase);
|
||||
}
|
||||
|
||||
private bool RfCaseExists(long id)
|
||||
{
|
||||
return _context.RfCase.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
130
Controllers/RfCaseProjectController.cs
Normal file
130
Controllers/RfCaseProjectController.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/RfCaseProject")]
|
||||
[Authorize]
|
||||
public class RfCaseProjectController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public RfCaseProjectController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET: api/RfCaseProject
|
||||
[HttpGet]
|
||||
public IEnumerable<RfCaseProject> GetRfCaseProject()
|
||||
{
|
||||
var c = from s in _context.RfCaseProject select s;
|
||||
c = c.OrderBy(s => s.Name);
|
||||
return c;
|
||||
//return _context.RfCaseProject;
|
||||
}
|
||||
|
||||
// GET: api/RfCaseProject/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetRfCaseProject([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var RfCaseProject = await _context.RfCaseProject.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (RfCaseProject == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(RfCaseProject);
|
||||
}
|
||||
|
||||
// PUT: api/RfCaseProject/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutRfCaseProject([FromRoute] long id, [FromBody] RfCaseProject RfCaseProject)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != RfCaseProject.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(RfCaseProject).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!RfCaseProjectExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/RfCaseProject
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostRfCaseProject([FromBody] RfCaseProject RfCaseProject)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.RfCaseProject.Add(RfCaseProject);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetRfCaseProject", new { id = RfCaseProject.Id }, RfCaseProject);
|
||||
}
|
||||
|
||||
// DELETE: api/RfCaseProject/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteRfCaseProject([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var RfCaseProject = await _context.RfCaseProject.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (RfCaseProject == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.RfCaseProject.Remove(RfCaseProject);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(RfCaseProject);
|
||||
}
|
||||
|
||||
private bool RfCaseProjectExists(long id)
|
||||
{
|
||||
return _context.RfCaseProject.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Controllers/RvfController.cs
Normal file
60
Controllers/RvfController.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("text/plain")]
|
||||
[Route("rvf")]
|
||||
|
||||
public class RvfController : Controller //RAVEN License fetch route
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public RvfController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("{dbid}")]
|
||||
public ActionResult Get([FromRoute] Guid dbid)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
//This is to simulate the scenarios where there is no license to return
|
||||
//either due to no current account / canceled or simply no license exists
|
||||
//Changes here must be reflected in RAVEN Util.License.Fetch block
|
||||
bool bTestStatusOtherThanOk = false;
|
||||
if (bTestStatusOtherThanOk)
|
||||
{
|
||||
return Json(new {Status="NONE", Reason="No license"});
|
||||
//return Json(new {Status="Canceled", Reason="Non payment"});
|
||||
}
|
||||
else
|
||||
{
|
||||
return Ok(RavenKeyFactory.GetRavenTestKey(dbid));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private bool LicenseExists(long id)
|
||||
{
|
||||
return _context.License.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Controllers/RvrController.cs
Normal file
60
Controllers/RvrController.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("text/plain")]
|
||||
[Route("rvr")]
|
||||
public class RvrController : Controller //RAVEN trial license request
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public RvrController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult Get([FromQuery] Guid dbid, [FromQuery] string email, [FromQuery] string regto)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (dbid == Guid.Empty)
|
||||
{
|
||||
return BadRequest("The requested DB ID was empty and not valid");
|
||||
}
|
||||
|
||||
//TODO: closer to release as ROCKFISH might change before then
|
||||
|
||||
//Attempt to match customer by dbid if not then create new customer of trial type for the indicated dbid and regto
|
||||
//ensure this email goes into the adminEmail array if not already there
|
||||
|
||||
//Flag customer record has having an unfulfilled trial request
|
||||
|
||||
//If it's a new customer or an existing one with non-matching email then send a verification email to the customer
|
||||
|
||||
//When a response is spotted in email then Rockfish should see it and flag the customer as verified
|
||||
//Probably some general purpose email Verify code would be helpful here as it would likely be useful for all manner of things
|
||||
|
||||
//When user releases trial in Rockfish a license key will be generated ready for the customers RAVEN to retrieve
|
||||
|
||||
return Ok("Request accepted. Awaiting email verification and approval.");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
184
Controllers/SearchController.cs
Normal file
184
Controllers/SearchController.cs
Normal file
@@ -0,0 +1,184 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
//requried to inject configuration in constructor
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/search")]
|
||||
[Authorize]
|
||||
public class SearchController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
public SearchController(rockfishContext context, IConfiguration configuration)//these two are injected, see startup.cs
|
||||
{
|
||||
_context = context;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpGet]
|
||||
public JsonResult GetSearchResults(string query) //get parameters from url not body
|
||||
{
|
||||
|
||||
//default return value, emtpy suggestions
|
||||
var noResults = new resultItem[0];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return Json(noResults);
|
||||
}
|
||||
|
||||
//CACHE CUSTOMER NAME LIST
|
||||
var custData = _context.Customer.Select(p => new { p.Id, p.Name });
|
||||
Dictionary<long, string> customerDict = new Dictionary<long, string>();
|
||||
customerDict.Add(0, "TRIAL CUSTOMER");
|
||||
foreach (var cust in custData)
|
||||
{
|
||||
customerDict.Add(cust.Id, cust.Name);
|
||||
}
|
||||
|
||||
|
||||
// List<SearchItem> l = searchItems;
|
||||
List<resultItem> resultList = new List<resultItem>();
|
||||
using (var command = _context.Database.GetDbConnection().CreateCommand())
|
||||
{
|
||||
_context.Database.OpenConnection();
|
||||
|
||||
foreach (SearchItem searchItem in searchItems)
|
||||
{
|
||||
string getColumns = "id, " + searchItem.field;
|
||||
if (searchItem.getCustomer_id)
|
||||
{
|
||||
//fuckery due to column name not consistent
|
||||
if (searchItem.table == "license")
|
||||
getColumns += ", customerid";
|
||||
else
|
||||
getColumns += ", customer_id";
|
||||
}
|
||||
|
||||
if (searchItem.getSite_id)
|
||||
getColumns += ", site_id";
|
||||
|
||||
command.CommandText = "select distinct " + getColumns + " from " + searchItem.table + " where " + searchItem.field + " like @q;";
|
||||
command.Parameters.Clear();
|
||||
var parameter = command.CreateParameter();
|
||||
parameter.ParameterName = "@q";
|
||||
parameter.Value = "%" + query + "%";
|
||||
command.Parameters.Add(parameter);
|
||||
|
||||
using (var res = command.ExecuteReader())
|
||||
{
|
||||
if (res.HasRows)
|
||||
{
|
||||
while (res.Read())
|
||||
{
|
||||
var r = new resultItem();
|
||||
r.obj = searchItem.table;
|
||||
r.fld = searchItem.field;
|
||||
r.id = Convert.ToInt64(res["id"]);
|
||||
if (searchItem.getCustomer_id)
|
||||
{
|
||||
//fucked up the scheme of customer_id as table name with license
|
||||
//which is customerid instead so have to workaround as don't want to bother
|
||||
//with the considerable amount of rigamarole to rename the column in the db
|
||||
if (searchItem.table == "license")
|
||||
{
|
||||
r.customerId = Convert.ToInt64(res["customerid"]);
|
||||
r.name = customerDict[r.customerId];//get name from customer id
|
||||
}
|
||||
else
|
||||
{
|
||||
r.customerId = Convert.ToInt64(res["customer_id"]);
|
||||
r.name = customerDict[r.customerId];//get name from customer id
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r.name = customerDict[r.id];//here id is the customer id
|
||||
}
|
||||
if (searchItem.getSite_id)
|
||||
r.site_id = Convert.ToInt64(res["site_id"]);
|
||||
|
||||
resultList.Add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_context.Database.CloseConnection();
|
||||
}
|
||||
|
||||
var filteredAndSorted = resultList.GroupBy(o => new { o.id, o.obj })
|
||||
.Select(o => o.FirstOrDefault())
|
||||
.OrderBy(o => o.name);
|
||||
return Json(filteredAndSorted);
|
||||
}
|
||||
//------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//Full text searchable items
|
||||
private static List<SearchItem> searchItems
|
||||
{
|
||||
get
|
||||
{
|
||||
List<SearchItem> l = new List<SearchItem>();
|
||||
|
||||
l.Add(new SearchItem { table = "customer", field = "name", getCustomer_id = false, getSite_id = false });
|
||||
l.Add(new SearchItem { table = "customer", field = "notes", getCustomer_id = false, getSite_id = false });
|
||||
//case 3607
|
||||
l.Add(new SearchItem { table = "customer", field = "supportEmail", getCustomer_id = false, getSite_id = false });
|
||||
l.Add(new SearchItem { table = "customer", field = "adminEmail", getCustomer_id = false, getSite_id = false });
|
||||
l.Add(new SearchItem { table = "license", field = "code", getCustomer_id = true, getSite_id = false });
|
||||
l.Add(new SearchItem { table = "license", field = "key", getCustomer_id = true, getSite_id = false });
|
||||
l.Add(new SearchItem { table = "license", field = "regto", getCustomer_id = true, getSite_id = false });
|
||||
l.Add(new SearchItem { table = "license", field = "email", getCustomer_id = true, getSite_id = false });
|
||||
|
||||
l.Add(new SearchItem { table = "purchase", field = "productCode", getCustomer_id = true, getSite_id = true });
|
||||
l.Add(new SearchItem { table = "purchase", field = "salesOrderNumber", getCustomer_id = true, getSite_id = true });
|
||||
l.Add(new SearchItem { table = "purchase", field = "notes", getCustomer_id = true, getSite_id = true });
|
||||
l.Add(new SearchItem { table = "site", field = "name", getCustomer_id = true, getSite_id = false });
|
||||
l.Add(new SearchItem { table = "site", field = "notes", getCustomer_id = true, getSite_id = false });
|
||||
|
||||
return l;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class SearchItem
|
||||
{
|
||||
public string table;
|
||||
public string field;
|
||||
public bool getCustomer_id;
|
||||
public bool getSite_id;
|
||||
}
|
||||
|
||||
private class resultItem
|
||||
{
|
||||
public long id;
|
||||
public long site_id;
|
||||
public long customerId;
|
||||
public string obj;//Table name basically
|
||||
public string name;//always customer name
|
||||
public string fld;//the name of the field the value was found in
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
|
||||
212
Controllers/SiteController.cs
Normal file
212
Controllers/SiteController.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/Site")]
|
||||
[Authorize]
|
||||
public class SiteController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public SiteController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
|
||||
//Get api/site/77/purchases
|
||||
[HttpGet("{id}/purchases")]
|
||||
public IEnumerable<Purchase> GetPurchases([FromRoute] long id)
|
||||
{
|
||||
var l = _context.Purchase
|
||||
.Where(b => b.SiteId.Equals(id))
|
||||
.OrderByDescending(b => b.PurchaseDate)
|
||||
.ToList();
|
||||
return l;
|
||||
}
|
||||
|
||||
// //Get api/site/77/activepurchases
|
||||
// [HttpGet("{id}/activepurchases")]
|
||||
// public IEnumerable<Purchase> GetActivePurchases([FromRoute] long id)
|
||||
// {
|
||||
// var l = _context.Purchase
|
||||
// .Where(b => b.SiteId.Equals(id))
|
||||
// .Where(b => b.CancelDate==null)
|
||||
// .OrderByDescending(b => b.PurchaseDate)
|
||||
// .ToList();
|
||||
// return l;
|
||||
// }
|
||||
|
||||
//Get api/site/77/activepurchases
|
||||
[HttpGet("{id}/activepurchases")]
|
||||
public IEnumerable<dtoNameIdItem> GetActivePurchases([FromRoute] long id)
|
||||
{
|
||||
var res = from c in _context.Purchase
|
||||
.Where(c => c.SiteId.Equals(id))
|
||||
.Where(c => c.CancelDate==null)
|
||||
.OrderByDescending(c => c.PurchaseDate)
|
||||
select new dtoNameIdItem
|
||||
{
|
||||
id = c.Id,
|
||||
name = c.Name
|
||||
};
|
||||
return res.ToList();
|
||||
}
|
||||
|
||||
// //Get api/site/77/incidents
|
||||
// [HttpGet("{id}/incidents")]
|
||||
// public IEnumerable<Incident> GetIncidents([FromRoute] long id)
|
||||
// {
|
||||
// var l = _context.Incident
|
||||
// .Where(b => b.SiteId.Equals(id))
|
||||
// .OrderByDescending(b => b.Id)//no single suitable date to order by
|
||||
// .ToList();
|
||||
// return l;
|
||||
// }
|
||||
|
||||
|
||||
// //Get api/site/77/trials
|
||||
// [HttpGet("{id}/trials")]
|
||||
// public IEnumerable<Trial> GetTrials([FromRoute] long id)
|
||||
// {
|
||||
// var l = _context.Trial
|
||||
// .Where(b => b.SiteId.Equals(id))
|
||||
// .OrderByDescending(b => b.Id)
|
||||
// .ToList();
|
||||
// return l;
|
||||
// }
|
||||
|
||||
//Get api/site/77/name
|
||||
[HttpGet("{id}/name")]
|
||||
public async Task<IActionResult> GetName([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var ret = await _context.Site
|
||||
.Select(r => new { r.Id, r.Name, r.CustomerId })
|
||||
.Where(r => r.Id == id)
|
||||
.FirstAsync();
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// GET: api/Site
|
||||
[HttpGet]
|
||||
public IEnumerable<Site> GetSite()
|
||||
{
|
||||
return _context.Site;
|
||||
}
|
||||
|
||||
// GET: api/Site/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetSite([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var site = await _context.Site.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (site == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(site);
|
||||
}
|
||||
|
||||
// PUT: api/Site/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutSite([FromRoute] long id, [FromBody] Site site)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != site.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(site).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!SiteExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/Site
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostSite([FromBody] Site site)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.Site.Add(site);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetSite", new { id = site.Id }, site);
|
||||
}
|
||||
|
||||
// DELETE: api/Site/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteSite([FromRoute] long id)
|
||||
{
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
|
||||
var site = await _context.Site.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (site == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
|
||||
_context.Site.Remove(site);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(site);
|
||||
}
|
||||
|
||||
private bool SiteExists(long id)
|
||||
{
|
||||
return _context.Site.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Controllers/SubscriptionController.cs
Normal file
128
Controllers/SubscriptionController.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/subscription")]
|
||||
[Authorize]
|
||||
public class SubscriptionController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
public SubscriptionController(rockfishContext context, IConfiguration configuration)//these two are injected, see startup.cs
|
||||
{
|
||||
_context = context;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
//**************************
|
||||
//TODO: ASYNCIFY ALL OF THIS
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
//Get notification list for expiring subscriptions
|
||||
//
|
||||
[HttpGet("notifylist")]
|
||||
public JsonResult Get()
|
||||
{
|
||||
var customerList = _context.Customer.Select(p => new { p.Id, p.Name });
|
||||
|
||||
/*Query: purchases with no renewal warning flag set, not cancelled and that are expiring subs in next 30 day window grouped by customer and then by purchase date
|
||||
Take that list show in UI, with button beside each customer group, press button, it generates a renewal warning email
|
||||
and puts it into the drafts folder, tags all the purchases with renewal warning sent true. */
|
||||
|
||||
//from three days ago to a month from now
|
||||
long windowStart = DateUtil.DateToEpoch(DateTime.Now.AddDays(-3));
|
||||
long windowEnd = DateUtil.DateToEpoch(DateTime.Now.AddMonths(1));
|
||||
|
||||
var rawExpiresList = _context.Purchase
|
||||
.Where(p => p.RenewNoticeSent == false)
|
||||
.Where(p => p.ExpireDate != null)
|
||||
.Where(p => p.ExpireDate > windowStart)
|
||||
.Where(p => p.ExpireDate < windowEnd)
|
||||
.Where(p => p.CancelDate == null)
|
||||
.OrderBy(p => p.CustomerId)
|
||||
.Select(p => new { id = p.Id, name = p.Name, customerId = p.CustomerId })
|
||||
.ToList();
|
||||
|
||||
|
||||
|
||||
//Initiate an empty list for return
|
||||
List<subnotifyResultItem> retList = new List<subnotifyResultItem>();
|
||||
//Bail if nothing to see here
|
||||
if (rawExpiresList.Count < 1)
|
||||
{
|
||||
return Json(retList);
|
||||
}
|
||||
|
||||
|
||||
long lastCustomerId = -1;
|
||||
subnotifyResultItem sn = new subnotifyResultItem();
|
||||
|
||||
//flatten the list and project it into return format
|
||||
foreach (var i in rawExpiresList)
|
||||
{
|
||||
//first loop?
|
||||
if (lastCustomerId == -1) lastCustomerId = i.customerId;
|
||||
|
||||
//New customer?
|
||||
if (i.customerId != lastCustomerId)
|
||||
{
|
||||
sn.purchasenames = sn.purchasenames.TrimStart(',').Trim();
|
||||
retList.Add(sn);
|
||||
sn = new subnotifyResultItem();
|
||||
lastCustomerId = i.customerId;
|
||||
}
|
||||
|
||||
if (sn.customerId == 0)
|
||||
{
|
||||
//get the full data
|
||||
sn.Customer = customerList.First(p => p.Id == i.customerId).Name;
|
||||
sn.customerId = i.customerId;
|
||||
sn.purchaseidlist = new List<long>();
|
||||
}
|
||||
sn.purchasenames += ", " + i.name;
|
||||
sn.purchaseidlist.Add(i.id);
|
||||
}
|
||||
//Add the last one
|
||||
sn.purchasenames = sn.purchasenames.TrimStart(',').Trim();
|
||||
retList.Add(sn);
|
||||
|
||||
return Json(retList);
|
||||
}
|
||||
|
||||
//dto classes for route
|
||||
public class subnotifyResultItem
|
||||
{
|
||||
public long id;
|
||||
public string Customer;
|
||||
public string purchasenames;
|
||||
public List<long> purchaseidlist;
|
||||
public long customerId;
|
||||
|
||||
}
|
||||
|
||||
//SEND SELECTED RENEWAL NOTIFICATIONS
|
||||
[HttpPost("sendnotify")]
|
||||
public JsonResult Generate([FromBody] List<long> purchaseidlist)
|
||||
{
|
||||
return Json(RfNotify.SendSubscriptionRenewalNotice(_context, purchaseidlist));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
}//eoc
|
||||
}//eons
|
||||
|
||||
137
Controllers/TextTemplateController.cs
Normal file
137
Controllers/TextTemplateController.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/TextTemplate")]
|
||||
[Authorize]
|
||||
public class TextTemplateController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public TextTemplateController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
|
||||
//ui ordered list
|
||||
[HttpGet("list")]
|
||||
public IEnumerable<TextTemplate> GetList()
|
||||
{
|
||||
var tt = from s in _context.TextTemplate select s;
|
||||
tt = tt.OrderBy(s => s.Name);
|
||||
return tt;
|
||||
}
|
||||
|
||||
// GET: api/TextTemplate
|
||||
[HttpGet]
|
||||
public IEnumerable<TextTemplate> GetTextTemplate()
|
||||
{
|
||||
return _context.TextTemplate;
|
||||
}
|
||||
|
||||
// GET: api/TextTemplate/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetTextTemplate([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var TextTemplate = await _context.TextTemplate.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (TextTemplate == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(TextTemplate);
|
||||
}
|
||||
|
||||
// PUT: api/TextTemplate/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutTextTemplate([FromRoute] long id, [FromBody] TextTemplate TextTemplate)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != TextTemplate.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(TextTemplate).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!TextTemplateExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/TextTemplate
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostTextTemplate([FromBody] TextTemplate TextTemplate)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.TextTemplate.Add(TextTemplate);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetTextTemplate", new { id = TextTemplate.Id }, TextTemplate);
|
||||
}
|
||||
|
||||
// DELETE: api/TextTemplate/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteTextTemplate([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var TextTemplate = await _context.TextTemplate.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (TextTemplate == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.TextTemplate.Remove(TextTemplate);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(TextTemplate);
|
||||
}
|
||||
|
||||
private bool TextTemplateExists(long id)
|
||||
{
|
||||
return _context.TextTemplate.Any(e => e.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
172
Controllers/UserController.cs
Normal file
172
Controllers/UserController.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using rockfishCore.Models;
|
||||
using rockfishCore.Util;
|
||||
|
||||
namespace rockfishCore.Controllers
|
||||
{
|
||||
[Produces("application/json")]
|
||||
[Route("api/User")]
|
||||
[Authorize]
|
||||
public class UserController : Controller
|
||||
{
|
||||
private readonly rockfishContext _context;
|
||||
|
||||
public UserController(rockfishContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET: api/User
|
||||
[HttpGet]
|
||||
public IEnumerable<User> GetUser()
|
||||
{
|
||||
return _context.User;
|
||||
}
|
||||
|
||||
// GET: api/User/5
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetUser([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var user = await _context.User.SingleOrDefaultAsync(m => m.Id == id);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
// PUT: api/User/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutUser([FromRoute] long id, [FromBody] User user)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (id != user.Id)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
_context.Entry(user).State = EntityState.Modified;
|
||||
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
{
|
||||
if (!UserExists(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
// POST: api/User
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostUser([FromBody] User user)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
_context.User.Add(user);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return CreatedAtAction("GetUser", new { id = user.Id }, user);
|
||||
}
|
||||
|
||||
// DELETE: api/User/5
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> DeleteUser([FromRoute] long id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var user = await _context.User.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_context.User.Remove(user);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
private bool UserExists(long id)
|
||||
{
|
||||
return _context.User.Any(e => e.Id == id);
|
||||
}
|
||||
|
||||
|
||||
//------------
|
||||
|
||||
[HttpPost("{id}/changepassword")]
|
||||
public JsonResult ChangePassword([FromRoute] long id, [FromBody] dtoChangePassword cp)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(cp.oldpassword) || string.IsNullOrWhiteSpace(cp.newpassword))
|
||||
{
|
||||
return Json(new { msg = "UserController:ChangePassword->A required value is missing", error = 1 });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var user = _context.User.SingleOrDefault(m => m.Id == id);
|
||||
string oldhash = Hasher.hash(user.Salt, cp.oldpassword);
|
||||
if (oldhash == user.Password)
|
||||
{
|
||||
string newhash = Hasher.hash(user.Salt, cp.newpassword);
|
||||
user.Password = newhash;
|
||||
_context.User.Update(user);
|
||||
_context.SaveChanges();
|
||||
return Json(new { msg = "success", ok = 1 });
|
||||
}
|
||||
else
|
||||
{
|
||||
return Json(new { msg = "UserController:ChangePassword->current password does not match", error = 1 });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new { msg = ex.Message, error = 1 });
|
||||
}
|
||||
}
|
||||
|
||||
public class dtoChangePassword
|
||||
{
|
||||
public string oldpassword;
|
||||
public string newpassword;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user