Przeglądaj źródła

Add VendorEvents
Change DateTime to DateTimeOffset where necessary

Andrew Klopper 8 lat temu
rodzic
commit
bfdd066f69

+ 8 - 8
BulkPrintingAPI/Controllers/BatchesController.cs

@@ -55,24 +55,24 @@ namespace BulkPrintingAPI.Controllers
55 55
         }
56 56
 
57 57
         [HttpGet]
58
-        public async Task<Page<Batch>> GetBatches([FromQuery] int page = 1,
58
+        public async Task<Page<Batch>> GetBatchesAsync([FromQuery] int page = 1,
59 59
             [FromQuery] int pageSize = 100)
60 60
         {
61
-            var credentials = await Utils.GetLoginCredentialsFromRequest(HttpContext, _context);
61
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
62 62
             return await Page<Batch>.GetPageAsync(
63 63
                 BatchesForVendor(credentials.Vendor.Id).OrderByDescending(b => b.OrderDate),
64 64
                 page, pageSize);
65 65
         }
66 66
 
67 67
         [HttpGet("{id}")]
68
-        public async Task<IActionResult> GetBatch([FromRoute] int id)
68
+        public async Task<IActionResult> GetBatchAsync([FromRoute] int id)
69 69
         {
70 70
             if (!ModelState.IsValid)
71 71
             {
72 72
                 return BadRequest(ModelState);
73 73
             }
74 74
 
75
-            var credentials = await Utils.GetLoginCredentialsFromRequest(HttpContext, _context);
75
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
76 76
             var batch = await BatchesForVendor(credentials.Vendor.Id)
77 77
                 .Include(b => b.Account)
78 78
                 .SingleOrDefaultAsync(m => m.Id == id);
@@ -91,21 +91,21 @@ namespace BulkPrintingAPI.Controllers
91 91
         }
92 92
 
93 93
         [HttpPost]
94
-        public async Task<IActionResult> PlaceOrder([FromBody] OrderRequest order)
94
+        public async Task<IActionResult> PlaceOrderAsync([FromBody] OrderRequest order)
95 95
         {
96 96
             if (!ModelState.IsValid)
97 97
             {
98 98
                 return BadRequest(ModelState);
99 99
             }
100 100
 
101
-            var credentials = await Utils.GetLoginCredentialsFromRequest(HttpContext, _context);
101
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
102 102
             var catalogue = await Utils.GetProductCatalogueAsync(_clientFactory, _logger, _cache,
103
-                credentials);
103
+                credentials, false);
104 104
 
105 105
             Product product;
106 106
             if (!catalogue.ProductMap.TryGetValue(order.ProductId.Value, out product))
107 107
             {
108
-                return BadRequest("Invalid product ID");
108
+                return BadRequest(new { error = "Invalid product ID" });
109 109
             }
110 110
 
111 111
             var orderResponse = await MAX.Utils.PlaceOrderAsync(_clientFactory, _logger,

+ 90 - 18
BulkPrintingAPI/Controllers/LoginController.cs

@@ -90,7 +90,7 @@ namespace BulkPrintingAPI.Controllers
90 90
             MAX.LoginCredentials credentials;
91 91
             try
92 92
             {
93
-                credentials = await Utils.SyncUserAndVendorWithDb(_context, _dataEncryptionOptions,
93
+                credentials = await Utils.SyncUserAndVendorWithDbAsync(_context, _dataEncryptionOptions,
94 94
                     user, loginRequest.Password, loginRequest.VendorId.Value, loginRequest.SerialNumber);
95 95
             }
96 96
             catch (Exception e)
@@ -103,22 +103,18 @@ namespace BulkPrintingAPI.Controllers
103 103
                 return Unauthorized();
104 104
             }
105 105
 
106
-            var now = DateTime.UtcNow;
107
-            var encodedJwt = new JwtSecurityTokenHandler().CreateEncodedJwt(
108
-                issuer: _tokenAuthenticationOptions.Issuer,
109
-                audience: _tokenAuthenticationOptions.Audience,
110
-                subject: new ClaimsIdentity(new Claim[] {
111
-                       new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
112
-                       new Claim("xpwd", loginRequest.Password),
113
-                       new Claim("xvid", loginRequest.VendorId.ToString()),
114
-                       new Claim(JwtRegisteredClaimNames.Jti, await _tokenAuthenticationOptions.NonceGenerator())
115
-                }),
116
-                notBefore: now,
117
-                expires: now.Add(_tokenAuthenticationOptions.Lifetime),
118
-                issuedAt: now,
119
-                signingCredentials: _tokenAuthenticationOptions.SigningCredentials,
120
-                encryptingCredentials: _tokenAuthenticationOptions.EncryptingCredentials
121
-            );
106
+            var now = DateTimeOffset.UtcNow;
107
+
108
+            _context.VendorEvents.Add(new MAX.Models.VendorEvent()
109
+            {
110
+                EventDate = now,
111
+                EventType = MAX.Models.VendorEvent.VendorEventType.Login,
112
+                UserId = credentials.User.Id,
113
+                VendorId = credentials.Vendor.Id
114
+            });
115
+            await _context.SaveChangesAsync();
116
+
117
+            var encodedJwt = await GetJwtAsync(now.DateTime, credentials);
122 118
 
123 119
             using (var derivedBytes = DataEncryptionOptions.DeriveKey(credentials.Password +
124 120
                 _dataEncryptionOptions.PasswordNoise, _dataEncryptionOptions.SaltLength,
@@ -130,7 +126,7 @@ namespace BulkPrintingAPI.Controllers
130 126
                     JsonConvert.SerializeObject(
131 127
                         new
132 128
                         {
133
-                            date = DateTime.Now,
129
+                            date = now,
134 130
                             nonce = await _tokenAuthenticationOptions.NonceGenerator(),
135 131
                             user = credentials.User,
136 132
                             vendor = credentials.Vendor,
@@ -156,6 +152,7 @@ namespace BulkPrintingAPI.Controllers
156 152
 
157 153
                 return Ok(new
158 154
                 {
155
+                    date = now,
159 156
                     access_token = encodedJwt,
160 157
                     credentials = new
161 158
                     {
@@ -168,5 +165,80 @@ namespace BulkPrintingAPI.Controllers
168 165
                 });
169 166
             }
170 167
         }
168
+
169
+        [HttpGet("extend")]
170
+        public async Task<IActionResult> ExtendTokenAsync()
171
+        {
172
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
173
+
174
+            MAX.Models.User user;
175
+            try
176
+            {
177
+                user = await MAX.Utils.AuthenticateUserAsync(_clientFactory, _logger,
178
+                    credentials.User.Id, credentials.User.Username, credentials.Vendor.Id,
179
+                    credentials.Vendor.SerialNumber, credentials.Password);
180
+            }
181
+            catch (Exception e)
182
+            {
183
+                _logger.LogError(
184
+                    "AuthenticateUserAsync failed for {0}: {1}",
185
+                    MAX.LoginCredentials.Format(credentials.User.Id, credentials.User.Username, credentials.Vendor.Id,
186
+                        credentials.Vendor.SerialNumber), e.Message);
187
+                return Unauthorized();
188
+            }
189
+
190
+            MAX.LoginCredentials possiblyUpdatedCredentials;
191
+            try
192
+            {
193
+                possiblyUpdatedCredentials = await Utils.SyncUserAndVendorWithDbAsync(_context, _dataEncryptionOptions,
194
+                    user, credentials.Password, credentials.Vendor.Id, credentials.Vendor.SerialNumber);
195
+            }
196
+            catch (Exception e)
197
+            {
198
+                _logger.LogError(
199
+                    "SyncUserAndVendorWithDb failed for {0}: {1}",
200
+                    MAX.LoginCredentials.Format(credentials.User.Id, credentials.User.Username, credentials.Vendor.Id,
201
+                        credentials.Vendor.SerialNumber), e.Message);
202
+                return Unauthorized();
203
+            }
204
+
205
+            var now = DateTimeOffset.UtcNow;
206
+
207
+            _context.VendorEvents.Add(new MAX.Models.VendorEvent()
208
+            {
209
+                EventDate = now,
210
+                EventType = MAX.Models.VendorEvent.VendorEventType.ExtendLogin,
211
+                UserId = possiblyUpdatedCredentials.User.Id,
212
+                VendorId = possiblyUpdatedCredentials.Vendor.Id
213
+            });
214
+            await _context.SaveChangesAsync();
215
+
216
+            return Ok(new
217
+            {
218
+                date = now,
219
+                access_token = await GetJwtAsync(now.DateTime, possiblyUpdatedCredentials),
220
+                user = possiblyUpdatedCredentials.User,
221
+                expires_in = (int)_tokenAuthenticationOptions.Lifetime.TotalSeconds
222
+            });
223
+        }
224
+
225
+        private async Task<string> GetJwtAsync(DateTime date, MAX.LoginCredentials credentials)
226
+        {
227
+            return new JwtSecurityTokenHandler().CreateEncodedJwt(
228
+                issuer: _tokenAuthenticationOptions.Issuer,
229
+                audience: _tokenAuthenticationOptions.Audience,
230
+                subject: new ClaimsIdentity(new Claim[] {
231
+                       new Claim(JwtRegisteredClaimNames.Sub, credentials.User.Id.ToString()),
232
+                       new Claim("xpwd", credentials.Password),
233
+                       new Claim("xvid", credentials.Vendor.Id.ToString()),
234
+                       new Claim(JwtRegisteredClaimNames.Jti, await _tokenAuthenticationOptions.NonceGenerator())
235
+                }),
236
+                notBefore: date,
237
+                expires: date.Add(_tokenAuthenticationOptions.Lifetime),
238
+                issuedAt: date,
239
+                signingCredentials: _tokenAuthenticationOptions.SigningCredentials,
240
+                encryptingCredentials: _tokenAuthenticationOptions.EncryptingCredentials
241
+            );
242
+        }
171 243
     }
172 244
 }

+ 3 - 3
BulkPrintingAPI/Controllers/ProductsController.cs

@@ -26,11 +26,11 @@ namespace BulkPrintingAPI.Controllers
26 26
         }
27 27
 
28 28
         [HttpGet]
29
-        public async Task<List<Network>> Get()
29
+        public async Task<List<Network>> GetAsync([FromQuery] bool refresh = false)
30 30
         {
31
-            var credentials = await Utils.GetLoginCredentialsFromRequest(HttpContext, _context);
31
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
32 32
             var catalogue = await Utils.GetProductCatalogueAsync(_clientFactory, _logger, _cache,
33
-                credentials);
33
+                credentials, refresh);
34 34
             return catalogue.Networks;
35 35
         }
36 36
     }

+ 9 - 6
BulkPrintingAPI/Controllers/Utils.cs

@@ -125,7 +125,7 @@ namespace BulkPrintingAPI.Controllers
125 125
             }
126 126
         }
127 127
 
128
-        public static async Task<MAX.LoginCredentials> SyncUserAndVendorWithDb(MAXContext context,
128
+        public static async Task<MAX.LoginCredentials> SyncUserAndVendorWithDbAsync(MAXContext context,
129 129
             DataEncryptionOptions dataEncryptionOptions, User user, string password, int vendorId,
130 130
             string serialNumber)
131 131
         {
@@ -219,7 +219,7 @@ namespace BulkPrintingAPI.Controllers
219 219
             };
220 220
         }
221 221
 
222
-        public static async Task<MAX.LoginCredentials> GetLoginCredentialsFromRequest(
222
+        public static async Task<MAX.LoginCredentials> GetLoginCredentialsFromRequestAsync(
223 223
             HttpContext httpContext, MAXContext dbContext)
224 224
         {
225 225
             var authInfo = await httpContext.Authentication.GetAuthenticateInfoAsync("Bearer");
@@ -273,11 +273,14 @@ namespace BulkPrintingAPI.Controllers
273 273
 
274 274
         public static async Task<ProductCatalogue> GetProductCatalogueAsync(
275 275
             MAX.ClientFactory clientFactory, ILogger logger, IMemoryCache cache,
276
-            MAX.LoginCredentials credentials)
276
+            MAX.LoginCredentials credentials, bool forceRefresh)
277 277
         {
278
-            return await cache.GetOrCreateAsync(
279
-                CacheKeys.GetAccountProductsKey(credentials.User.AccountId),
280
-                entry =>
278
+            var key = CacheKeys.GetAccountProductsKey(credentials.User.AccountId);
279
+            if (forceRefresh)
280
+            {
281
+                cache.Remove(key);
282
+            }
283
+            return await cache.GetOrCreateAsync(key, entry =>
281 284
                 {
282 285
                     // TODO: set expiry time, etc.
283 286
                     return MAX.Utils.GetProductCatalogueAsync(clientFactory, logger, credentials);

+ 117 - 0
BulkPrintingAPI/Controllers/VendorEventsController.cs

@@ -0,0 +1,117 @@
1
+using BulkPrintingAPI.Pagination;
2
+using MAX.Models;
3
+using Microsoft.AspNetCore.Mvc;
4
+using System;
5
+using System.Collections.Generic;
6
+using System.ComponentModel.DataAnnotations;
7
+using System.Linq;
8
+using System.Threading.Tasks;
9
+
10
+namespace BulkPrintingAPI.Controllers
11
+{
12
+    [Produces("application/json")]
13
+    [Route("api/[controller]")]
14
+    public class VendorEventsController : Controller
15
+    {
16
+        private readonly MAXContext _context;
17
+
18
+        public class RemoteVendorEvent
19
+        {
20
+            [Required]
21
+            public int? Id { get; set; }
22
+
23
+            [Required]
24
+            public int? VendorId { get; set; }
25
+
26
+            [Required]
27
+            public int? UserId { get; set; }
28
+
29
+            public int? VoucherId { get; set; }
30
+
31
+            [Required]
32
+            public DateTimeOffset? EventDate { get; set; }
33
+
34
+            [Required]
35
+            public VendorEvent.VendorEventType? EventType { get; set; }
36
+        }
37
+
38
+        public VendorEventsController(MAXContext context)
39
+        {
40
+            _context = context;
41
+        }
42
+
43
+        [HttpGet]
44
+        public async Task<Page<VendorEvent>> GetVendorEventsAsync([FromQuery] int page = 1,
45
+            [FromQuery] int pageSize = 100)
46
+        {
47
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
48
+            return await Page<VendorEvent>.GetPageAsync(
49
+                VendorEventsForVendor(credentials.Vendor.Id).OrderByDescending(e => e.EventDate),
50
+                page, pageSize);
51
+        }
52
+
53
+        [HttpPost]
54
+        public async Task<IActionResult> PostRemoteVendorEventsAsync([FromBody] List<RemoteVendorEvent> remoteVendorEvents)
55
+        {
56
+            if (! ModelState.IsValid)
57
+            {
58
+                return BadRequest(ModelState);
59
+            }
60
+
61
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
62
+            var vendorId = credentials.Vendor.Id;
63
+            var lastVendorEventRemoteId = credentials.Vendor.LastVendorEventRemoteId;
64
+            var maxRemoteId = lastVendorEventRemoteId;
65
+            foreach (var remoteEvent in remoteVendorEvents)
66
+            {
67
+                if (vendorId != remoteEvent.VendorId)
68
+                {
69
+                    return BadRequest(new { error = "Vendor ID mismatch" });
70
+                }
71
+                if (lastVendorEventRemoteId.HasValue && (lastVendorEventRemoteId.Value >= remoteEvent.Id.Value))
72
+                {
73
+                    return BadRequest(new { error = "A supplied ID value is less than or equal to lastVendorRemoteId" });
74
+                }
75
+                if (! maxRemoteId.HasValue || (remoteEvent.Id.Value > maxRemoteId.Value))
76
+                {
77
+                    maxRemoteId = remoteEvent.Id.Value;
78
+                }
79
+                _context.VendorEvents.Add(new VendorEvent()
80
+                {
81
+                    RemoteId = remoteEvent.Id.Value,
82
+                    UserId = remoteEvent.UserId.Value,
83
+                    VendorId = remoteEvent.VendorId.Value,
84
+                    VoucherId = remoteEvent.VoucherId,
85
+                    EventType = remoteEvent.EventType.Value,
86
+                    EventDate = remoteEvent.EventDate.Value.ToUniversalTime()
87
+                });
88
+            }
89
+
90
+            // FIXME: race condition for simultaneous requests, although we should never have any from the same vendor
91
+            credentials.Vendor.LastVendorEventRemoteId = maxRemoteId;
92
+            await _context.SaveChangesAsync();
93
+
94
+            return Ok(new
95
+            {
96
+                lastVendorEventRemoteId = credentials.Vendor.LastVendorEventRemoteId
97
+            });
98
+        }
99
+
100
+        [HttpGet("meta")]
101
+        public async Task<IActionResult> GetVendorEventMetadata()
102
+        {
103
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
104
+            return Ok(new
105
+            {
106
+                date = DateTimeOffset.UtcNow,
107
+                vendorId = credentials.Vendor.Id,
108
+                lastVendorEventRemoteId = credentials.Vendor.LastVendorEventRemoteId
109
+            });
110
+        }
111
+
112
+        private IQueryable<VendorEvent> VendorEventsForVendor(int vendorId)
113
+        {
114
+            return _context.VendorEvents.Where(e => e.VendorId == vendorId);
115
+        }
116
+    }
117
+}

+ 33 - 6
BulkPrintingAPI/Controllers/VouchersController.cs

@@ -4,6 +4,7 @@ using MAX.Models;
4 4
 using Microsoft.AspNetCore.Mvc;
5 5
 using Microsoft.EntityFrameworkCore;
6 6
 using Microsoft.Extensions.Logging;
7
+using System;
7 8
 using System.Linq;
8 9
 using System.Threading.Tasks;
9 10
 
@@ -26,7 +27,7 @@ namespace BulkPrintingAPI.Controllers
26 27
         }
27 28
 
28 29
         [HttpGet]
29
-        public async Task<IActionResult> GetVouchers([FromRoute] int batchId,
30
+        public async Task<IActionResult> GetVouchersAsync([FromRoute] int batchId,
30 31
             [FromQuery] int page = 1, [FromQuery] int pageSize = 100)
31 32
         {
32 33
             if (!ModelState.IsValid)
@@ -34,22 +35,38 @@ namespace BulkPrintingAPI.Controllers
34 35
                 return BadRequest(ModelState);
35 36
             }
36 37
 
37
-            var credentials = await Utils.GetLoginCredentialsFromRequest(HttpContext, _context);
38
-            return Ok(await Page<Voucher>.GetPageAsync(
38
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
39
+            var result = await Page<Voucher>.GetPageAsync(
39 40
                 VouchersForBatchAndVendor(batchId, credentials.Vendor.Id)
40 41
                     .OrderBy(v => new { v.BatchId, v.SequenceNumber }),
41
-                page, pageSize));
42
+                page, pageSize);
43
+
44
+            var now = DateTimeOffset.UtcNow;
45
+            foreach (var voucher in result.Items)
46
+            {
47
+                _context.VendorEvents.Add(new VendorEvent()
48
+                {
49
+                    EventDate = now,
50
+                    EventType = VendorEvent.VendorEventType.DownloadVoucher,
51
+                    UserId = credentials.User.Id,
52
+                    VendorId = credentials.Vendor.Id,
53
+                    VoucherId = voucher.Id
54
+                });
55
+            }
56
+            await _context.SaveChangesAsync();
57
+
58
+            return Ok(result);
42 59
         }
43 60
 
44 61
         [HttpGet("{sequenceNumber}")]
45
-        public async Task<IActionResult> GetVoucher([FromRoute] int batchId, [FromRoute] int sequenceNumber)
62
+        public async Task<IActionResult> GetVoucherAsync([FromRoute] int batchId, [FromRoute] int sequenceNumber)
46 63
         {
47 64
             if (!ModelState.IsValid)
48 65
             {
49 66
                 return BadRequest(ModelState);
50 67
             }
51 68
 
52
-            var credentials = await Utils.GetLoginCredentialsFromRequest(HttpContext, _context);
69
+            var credentials = await Utils.GetLoginCredentialsFromRequestAsync(HttpContext, _context);
53 70
             var voucher = await VouchersForBatchAndVendor(batchId, credentials.Vendor.Id)
54 71
                 .SingleOrDefaultAsync(v => (v.BatchId == batchId) && (v.SequenceNumber == sequenceNumber));
55 72
 
@@ -58,6 +75,16 @@ namespace BulkPrintingAPI.Controllers
58 75
                 return NotFound();
59 76
             }
60 77
 
78
+            _context.VendorEvents.Add(new VendorEvent()
79
+            {
80
+                EventDate = DateTimeOffset.UtcNow,
81
+                EventType = VendorEvent.VendorEventType.DownloadVoucher,
82
+                UserId = credentials.User.Id,
83
+                VendorId = credentials.Vendor.Id,
84
+                VoucherId = voucher.Id
85
+            });
86
+            await _context.SaveChangesAsync();
87
+
61 88
             return Ok(voucher);
62 89
         }
63 90
 

+ 35 - 3
BulkPrintingAPI/Migrations/20170705205151_InitialCreate.Designer.cs

@@ -8,7 +8,7 @@ using MAX.Models;
8 8
 namespace BulkPrintingAPI.Migrations
9 9
 {
10 10
     [DbContext(typeof(MAXContext))]
11
-    [Migration("20170705205151_InitialCreate")]
11
+    [Migration("20170711104915_InitialCreate")]
12 12
     partial class InitialCreate
13 13
     {
14 14
         protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -62,7 +62,9 @@ namespace BulkPrintingAPI.Migrations
62 62
                         .IsRequired()
63 63
                         .HasMaxLength(20);
64 64
 
65
-                    b.Property<DateTime>("OrderDate");
65
+                    b.Property<DateTimeOffset>("OrderDate");
66
+
67
+                    b.Property<Guid?>("OrderGuid");
66 68
 
67 69
                     b.Property<string>("OrderReference")
68 70
                         .IsRequired()
@@ -88,6 +90,9 @@ namespace BulkPrintingAPI.Migrations
88 90
 
89 91
                     b.HasIndex("AccountId");
90 92
 
93
+                    b.HasIndex("OrderGuid")
94
+                        .IsUnique();
95
+
91 96
                     b.HasIndex("ReadyForDownload", "OrderDate");
92 97
 
93 98
                     b.ToTable("Batches");
@@ -113,7 +118,7 @@ namespace BulkPrintingAPI.Migrations
113 118
                         .IsRequired()
114 119
                         .HasMaxLength(50);
115 120
 
116
-                    b.Property<DateTime>("LastLogin");
121
+                    b.Property<DateTimeOffset>("LastLogin");
117 122
 
118 123
                     b.Property<int>("Level");
119 124
 
@@ -156,6 +161,8 @@ namespace BulkPrintingAPI.Migrations
156 161
                         .IsRequired()
157 162
                         .HasMaxLength(32);
158 163
 
164
+                    b.Property<int?>("LastVendorEventRemoteId");
165
+
159 166
                     b.Property<string>("SerialNumber")
160 167
                         .IsRequired()
161 168
                         .HasMaxLength(50);
@@ -167,6 +174,31 @@ namespace BulkPrintingAPI.Migrations
167 174
                     b.ToTable("Vendors");
168 175
                 });
169 176
 
177
+            modelBuilder.Entity("MAX.Models.VendorEvent", b =>
178
+                {
179
+                    b.Property<int>("Id")
180
+                        .ValueGeneratedOnAdd();
181
+
182
+                    b.Property<DateTimeOffset>("EventDate");
183
+
184
+                    b.Property<int>("EventType");
185
+
186
+                    b.Property<int?>("RemoteId");
187
+
188
+                    b.Property<int>("UserId");
189
+
190
+                    b.Property<int>("VendorId");
191
+
192
+                    b.Property<int?>("VoucherId");
193
+
194
+                    b.HasKey("Id");
195
+
196
+                    b.HasIndex("VendorId", "RemoteId")
197
+                        .IsUnique();
198
+
199
+                    b.ToTable("VendorEvents");
200
+                });
201
+
170 202
             modelBuilder.Entity("MAX.Models.Voucher", b =>
171 203
                 {
172 204
                     b.Property<int>("Id");

+ 38 - 2
BulkPrintingAPI/Migrations/20170705205151_InitialCreate.cs

@@ -1,6 +1,7 @@
1 1
 using System;
2 2
 using System.Collections.Generic;
3 3
 using Microsoft.EntityFrameworkCore.Migrations;
4
+using Microsoft.EntityFrameworkCore.Metadata;
4 5
 
5 6
 namespace BulkPrintingAPI.Migrations
6 7
 {
@@ -9,6 +10,24 @@ namespace BulkPrintingAPI.Migrations
9 10
         protected override void Up(MigrationBuilder migrationBuilder)
10 11
         {
11 12
             migrationBuilder.CreateTable(
13
+                name: "VendorEvents",
14
+                columns: table => new
15
+                {
16
+                    Id = table.Column<int>(nullable: false)
17
+                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
18
+                    EventDate = table.Column<DateTimeOffset>(nullable: false),
19
+                    EventType = table.Column<int>(nullable: false),
20
+                    RemoteId = table.Column<int>(nullable: true),
21
+                    UserId = table.Column<int>(nullable: false),
22
+                    VendorId = table.Column<int>(nullable: false),
23
+                    VoucherId = table.Column<int>(nullable: true)
24
+                },
25
+                constraints: table =>
26
+                {
27
+                    table.PrimaryKey("PK_VendorEvents", x => x.Id);
28
+                });
29
+
30
+            migrationBuilder.CreateTable(
12 31
                 name: "Warehouses",
13 32
                 columns: table => new
14 33
                 {
@@ -54,7 +73,8 @@ namespace BulkPrintingAPI.Migrations
54 73
                     FaceValue = table.Column<decimal>(nullable: false),
55 74
                     NetworkId = table.Column<int>(nullable: false),
56 75
                     NetworkName = table.Column<string>(maxLength: 20, nullable: false),
57
-                    OrderDate = table.Column<DateTime>(nullable: false),
76
+                    OrderDate = table.Column<DateTimeOffset>(nullable: false),
77
+                    OrderGuid = table.Column<Guid>(nullable: true),
58 78
                     OrderReference = table.Column<string>(maxLength: 20, nullable: false),
59 79
                     OrderedById = table.Column<int>(nullable: false),
60 80
                     ProductDescription = table.Column<string>(maxLength: 50, nullable: false),
@@ -87,7 +107,7 @@ namespace BulkPrintingAPI.Migrations
87 107
                     CanReprintOnline = table.Column<bool>(nullable: false),
88 108
                     Enabled = table.Column<bool>(nullable: false),
89 109
                     FirstName = table.Column<string>(maxLength: 50, nullable: false),
90
-                    LastLogin = table.Column<DateTime>(nullable: false),
110
+                    LastLogin = table.Column<DateTimeOffset>(nullable: false),
91 111
                     Level = table.Column<int>(nullable: false),
92 112
                     OfflinePrintValue = table.Column<decimal>(nullable: false),
93 113
                     OfflineReprintValue = table.Column<decimal>(nullable: false),
@@ -116,6 +136,7 @@ namespace BulkPrintingAPI.Migrations
116 136
                     AccountId = table.Column<int>(nullable: false),
117 137
                     EncryptedDatabasePassword = table.Column<byte[]>(maxLength: 32, nullable: false),
118 138
                     EncryptedVoucherKey = table.Column<byte[]>(maxLength: 32, nullable: false),
139
+                    LastVendorEventRemoteId = table.Column<int>(nullable: true),
119 140
                     SerialNumber = table.Column<string>(maxLength: 50, nullable: false)
120 141
                 },
121 142
                 constraints: table =>
@@ -162,6 +183,12 @@ namespace BulkPrintingAPI.Migrations
162 183
                 column: "AccountId");
163 184
 
164 185
             migrationBuilder.CreateIndex(
186
+                name: "IX_Batches_OrderGuid",
187
+                table: "Batches",
188
+                column: "OrderGuid",
189
+                unique: true);
190
+
191
+            migrationBuilder.CreateIndex(
165 192
                 name: "IX_Batches_ReadyForDownload_OrderDate",
166 193
                 table: "Batches",
167 194
                 columns: new[] { "ReadyForDownload", "OrderDate" });
@@ -177,6 +204,12 @@ namespace BulkPrintingAPI.Migrations
177 204
                 column: "AccountId");
178 205
 
179 206
             migrationBuilder.CreateIndex(
207
+                name: "IX_VendorEvents_VendorId_RemoteId",
208
+                table: "VendorEvents",
209
+                columns: new[] { "VendorId", "RemoteId" },
210
+                unique: true);
211
+
212
+            migrationBuilder.CreateIndex(
180 213
                 name: "IX_Vouchers_BatchId_SequenceNumber",
181 214
                 table: "Vouchers",
182 215
                 columns: new[] { "BatchId", "SequenceNumber" },
@@ -192,6 +225,9 @@ namespace BulkPrintingAPI.Migrations
192 225
                 name: "Vendors");
193 226
 
194 227
             migrationBuilder.DropTable(
228
+                name: "VendorEvents");
229
+
230
+            migrationBuilder.DropTable(
195 231
                 name: "Vouchers");
196 232
 
197 233
             migrationBuilder.DropTable(

+ 34 - 2
BulkPrintingAPI/Migrations/MAXContextModelSnapshot.cs

@@ -61,7 +61,9 @@ namespace BulkPrintingAPI.Migrations
61 61
                         .IsRequired()
62 62
                         .HasMaxLength(20);
63 63
 
64
-                    b.Property<DateTime>("OrderDate");
64
+                    b.Property<DateTimeOffset>("OrderDate");
65
+
66
+                    b.Property<Guid?>("OrderGuid");
65 67
 
66 68
                     b.Property<string>("OrderReference")
67 69
                         .IsRequired()
@@ -87,6 +89,9 @@ namespace BulkPrintingAPI.Migrations
87 89
 
88 90
                     b.HasIndex("AccountId");
89 91
 
92
+                    b.HasIndex("OrderGuid")
93
+                        .IsUnique();
94
+
90 95
                     b.HasIndex("ReadyForDownload", "OrderDate");
91 96
 
92 97
                     b.ToTable("Batches");
@@ -112,7 +117,7 @@ namespace BulkPrintingAPI.Migrations
112 117
                         .IsRequired()
113 118
                         .HasMaxLength(50);
114 119
 
115
-                    b.Property<DateTime>("LastLogin");
120
+                    b.Property<DateTimeOffset>("LastLogin");
116 121
 
117 122
                     b.Property<int>("Level");
118 123
 
@@ -155,6 +160,8 @@ namespace BulkPrintingAPI.Migrations
155 160
                         .IsRequired()
156 161
                         .HasMaxLength(32);
157 162
 
163
+                    b.Property<int?>("LastVendorEventRemoteId");
164
+
158 165
                     b.Property<string>("SerialNumber")
159 166
                         .IsRequired()
160 167
                         .HasMaxLength(50);
@@ -166,6 +173,31 @@ namespace BulkPrintingAPI.Migrations
166 173
                     b.ToTable("Vendors");
167 174
                 });
168 175
 
176
+            modelBuilder.Entity("MAX.Models.VendorEvent", b =>
177
+                {
178
+                    b.Property<int>("Id")
179
+                        .ValueGeneratedOnAdd();
180
+
181
+                    b.Property<DateTimeOffset>("EventDate");
182
+
183
+                    b.Property<int>("EventType");
184
+
185
+                    b.Property<int?>("RemoteId");
186
+
187
+                    b.Property<int>("UserId");
188
+
189
+                    b.Property<int>("VendorId");
190
+
191
+                    b.Property<int?>("VoucherId");
192
+
193
+                    b.HasKey("Id");
194
+
195
+                    b.HasIndex("VendorId", "RemoteId")
196
+                        .IsUnique();
197
+
198
+                    b.ToTable("VendorEvents");
199
+                });
200
+
169 201
             modelBuilder.Entity("MAX.Models.Voucher", b =>
170 202
                 {
171 203
                     b.Property<int>("Id");

+ 20 - 0
BulkPrintingAPI/Properties/PublishProfiles/FolderProfile.pubxml

@@ -0,0 +1,20 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<!--
3
+This file is used by the publish/package process of your Web project. You can customize the behavior of this process
4
+by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121. 
5
+-->
6
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
7
+  <PropertyGroup>
8
+    <WebPublishMethod>FileSystem</WebPublishMethod>
9
+    <PublishProvider>FileSystem</PublishProvider>
10
+    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
11
+    <LastUsedPlatform>Any CPU</LastUsedPlatform>
12
+    <SiteUrlToLaunchAfterPublish />
13
+    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
14
+    <ExcludeApp_Data>False</ExcludeApp_Data>
15
+    <PublishFramework />
16
+    <ProjectGuid>92eeb85e-10ff-4902-9f73-ba4d58344824</ProjectGuid>
17
+    <publishUrl>bin\Release\PublishOutput</publishUrl>
18
+    <DeleteExistingFiles>False</DeleteExistingFiles>
19
+  </PropertyGroup>
20
+</Project>

+ 1 - 2
BulkPrintingAPI/appsettings.json

@@ -11,8 +11,7 @@
11 11
   "TokenAuthentication": {
12 12
     "Audience": "bulk",
13 13
     "Issuer": "bulk",
14
-    "SecretKey": "SET IN secrets.json",
15
-    "Salt": "SET IN secrets.json",
14
+    "Key": "SET IN secrets.json",
16 15
     "TokenLifetime": 86400
17 16
   }
18 17
 }

+ 1 - 1
MAXClient/Client.cs

@@ -380,7 +380,7 @@ namespace MAX
380 380
                         DiscountPercentage = product.DiscountPercentage,
381 381
                         NetworkId = product.Network.Id,
382 382
                         NetworkName = product.Network.Name,
383
-                        OrderDate = DateTime.Now,
383
+                        OrderDate = DateTimeOffset.UtcNow,
384 384
                         OrderedById = _userId,
385 385
                         ReadyForDownload = false
386 386
                     },

+ 4 - 4
MAXData/Models/Account.cs

@@ -9,10 +9,10 @@ namespace MAX.Models
9 9
     {
10 10
         public enum AccountStatus
11 11
         {
12
-            Unknown,
13
-            Enabled,
14
-            Suspended,
15
-            Closed
12
+            Unknown = 0,
13
+            Enabled = 1,
14
+            Suspended = 2,
15
+            Closed = 3
16 16
         }
17 17
 
18 18
         [DatabaseGenerated(DatabaseGeneratedOption.None)]

+ 5 - 3
MAXData/Models/Batch.cs

@@ -11,8 +11,8 @@ namespace MAX.Models
11 11
         public enum Vouchertype
12 12
         {
13 13
             Voucher = 1,
14
-            SMS,
15
-            Data
14
+            SMS = 2,
15
+            Data = 3
16 16
         }
17 17
 
18 18
         [DatabaseGenerated(DatabaseGeneratedOption.None)]
@@ -38,7 +38,9 @@ namespace MAX.Models
38 38
         //[IgnoreDataMember]
39 39
         //public User OrderedBy { get; set; }
40 40
 
41
-        public DateTime OrderDate { get; set; }
41
+        public DateTimeOffset OrderDate { get; set; }
42
+
43
+        public Guid? OrderGuid { get; set; }
42 44
 
43 45
         [Required, MaxLength(20)]
44 46
         public string OrderReference { get; set; }

+ 10 - 1
MAXData/Models/MAXContext.cs

@@ -1,5 +1,4 @@
1 1
 using Microsoft.EntityFrameworkCore;
2
-using Microsoft.EntityFrameworkCore.Metadata;
3 2
 
4 3
 namespace MAX.Models
5 4
 {
@@ -14,6 +13,14 @@ namespace MAX.Models
14 13
             modelBuilder.Entity<Batch>()
15 14
                 .HasIndex(b => new { b.ReadyForDownload, b.OrderDate });
16 15
 
16
+            modelBuilder.Entity<Batch>()
17
+                .HasIndex(b => new { b.OrderGuid })
18
+                .IsUnique();
19
+
20
+            modelBuilder.Entity<VendorEvent>()
21
+                .HasIndex(e => new { e.VendorId, e.RemoteId })
22
+                .IsUnique();
23
+
17 24
             modelBuilder.Entity<Voucher>()
18 25
                 .HasIndex(v => new { v.BatchId, v.SequenceNumber })
19 26
                 .IsUnique();
@@ -25,6 +32,8 @@ namespace MAX.Models
25 32
 
26 33
         public DbSet<User> Users { get; set; }
27 34
 
35
+        public DbSet<VendorEvent> VendorEvents { get; set; }
36
+
28 37
         public DbSet<Vendor> Vendors { get; set; }
29 38
 
30 39
         public DbSet<Voucher> Vouchers { get; set; }

+ 1 - 1
MAXData/Models/User.cs

@@ -37,7 +37,7 @@ namespace MAX.Models
37 37
 
38 38
         public int System { get; set; }
39 39
 
40
-        public DateTime LastLogin { get; set; }
40
+        public DateTimeOffset LastLogin { get; set; }
41 41
 
42 42
         public bool CanPrintOnline { get; set; }
43 43
 

+ 2 - 0
MAXData/Models/Vendor.cs

@@ -24,5 +24,7 @@ namespace MAX.Models
24 24
         [IgnoreDataMember]
25 25
         [Required, MaxLength(32)]
26 26
         public byte[] EncryptedVoucherKey { get; set; }
27
+
28
+        public int? LastVendorEventRemoteId { get; set; }
27 29
     }
28 30
 }

+ 33 - 0
MAXData/Models/VendorEvent.cs

@@ -0,0 +1,33 @@
1
+using System;
2
+
3
+namespace MAX.Models
4
+{
5
+    public class VendorEvent
6
+    {
7
+        public enum VendorEventType
8
+        {
9
+            Unknown = 0,
10
+            Login = 1,
11
+            OfflineLogin = 2,
12
+            ExtendLogin = 3,
13
+            Logout = 4,
14
+            DownloadVoucher = 5,
15
+            PrintVoucher = 6,
16
+            ViewVoucherPIN = 7
17
+        }
18
+
19
+        public int Id { get; set; }
20
+
21
+        public int VendorId { get; set; }
22
+
23
+        public int UserId { get; set; }
24
+
25
+        public int? VoucherId { get; set; }
26
+
27
+        public int? RemoteId { get; set; }
28
+
29
+        public DateTimeOffset EventDate { get; set; }
30
+
31
+        public VendorEventType EventType { get; set; }
32
+    }
33
+}