(feat): image service

This commit is contained in:
Maksym Sadovnychyy 2022-09-11 04:12:35 +02:00
parent d9d0a14083
commit 697c7692d0
45 changed files with 976 additions and 373 deletions

View File

@ -25,16 +25,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/Content/404c8232-9048-4519-bfba-6e78dc7005ca?locale=en-US",
"protocol": "https",
"raw": "{{baseUrl}}/Content/{{siteId}}?locale=en-US",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"Content",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
],
"query": [
{
@ -68,16 +65,14 @@
}
],
"url": {
"raw": "https://localhost:7151/api/CategoryItem/404c8232-9048-4519-bfba-6e78dc7005ca/e154e33f-3cc7-468d-bb66-e0390ddb9ae0",
"protocol": "https",
"raw": "{{baseUrl}}//CategoryItem/{{siteId}}/e154e33f-3cc7-468d-bb66-e0390ddb9ae0",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"",
"CategoryItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"e154e33f-3cc7-468d-bb66-e0390ddb9ae0"
]
}
@ -101,16 +96,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/CategoryItem/404c8232-9048-4519-bfba-6e78dc7005ca?slug=default",
"protocol": "https",
"raw": "{{baseUrl}}/CategoryItem/{{siteId}}?slug=default",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"CategoryItem",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
],
"query": [
{
@ -143,16 +135,14 @@
"raw": "{\r\n \"l10n\": [\r\n {\r\n \"locale\": \"\",\r\n \"slug\": \"guides\",\r\n \"text\": \"Guides\"\r\n }\r\n ]\r\n}"
},
"url": {
"raw": "https://localhost:7151/api/CategoryItem/404c8232-9048-4519-bfba-6e78dc7005ca",
"protocol": "https",
"raw": "{{baseUrl}}//CategoryItem/{{siteId}}",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"",
"CategoryItem",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
]
}
},
@ -179,16 +169,14 @@
"raw": "{\r\n \"l10n\": [\r\n {\r\n \"locale\": \"en-US\",\r\n \"slug\": \"guides\",\r\n \"text\": \"Guides\"\r\n },\r\n {\r\n \"locale\": \"it-IT\",\r\n \"slug\": \"guide\",\r\n \"text\": \"Guide\"\r\n }\r\n ]\r\n}"
},
"url": {
"raw": "https://localhost:7151/api/CategoryItem/404c8232-9048-4519-bfba-6e78dc7005ca/2c50f4de-70f2-4414-aabc-7a0d2eb0e203",
"protocol": "https",
"raw": "{{baseUrl}}//CategoryItem/{{siteId}}/2c50f4de-70f2-4414-aabc-7a0d2eb0e203",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"",
"CategoryItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"2c50f4de-70f2-4414-aabc-7a0d2eb0e203"
]
}
@ -212,17 +200,14 @@
}
],
"url": {
"raw": "https://localhost:7151/api/CategoryItem/404c8232-9048-4519-bfba-6e78dc7005ca/2c50f4de-70f2-4414-aabc-7a0d2eb0e203",
"protocol": "https",
"raw": "{{baseUrl}}/CategoryItem/{{siteId}}/e154e33f-3cc7-468d-bb66-e0390ddb9ae0",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"CategoryItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"2c50f4de-70f2-4414-aabc-7a0d2eb0e203"
"{{siteId}}",
"e154e33f-3cc7-468d-bb66-e0390ddb9ae0"
]
}
},
@ -250,16 +235,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/CategoryItems/404c8232-9048-4519-bfba-6e78dc7005ca",
"protocol": "https",
"raw": "{{baseUrl}}/CategoryItems/{{siteId}}",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"CategoryItems",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
]
}
},
@ -282,16 +264,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/CategoryItems/404c8232-9048-4519-bfba-6e78dc7005ca?locale=en-US",
"protocol": "https",
"raw": "{{baseUrl}}/CategoryItems/{{siteId}}?locale=en-US",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"CategoryItems",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
],
"query": [
{
@ -320,16 +299,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/CategoryItems/404c8232-9048-4519-bfba-6e78dc7005ca",
"protocol": "https",
"raw": "{{baseUrl}}/CategoryItems/{{siteId}}",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"CategoryItems",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
]
}
},
@ -341,78 +317,7 @@
"name": "ShopItem",
"item": [
{
"name": "01-Get",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"type": "default"
},
{
"key": "Accept",
"value": "application/json",
"type": "default"
}
],
"url": {
"raw": "https://localhost:7151/api/ShopItem/404c8232-9048-4519-bfba-6e78dc7005ca/SKU-01",
"protocol": "https",
"host": [
"localhost"
],
"port": "7151",
"path": [
"api",
"ShopItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"SKU-01"
]
}
},
"response": []
},
{
"name": "02-GetSlug",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"type": "default"
},
{
"key": "Accept",
"value": "application/json",
"type": "default"
}
],
"url": {
"raw": "https://localhost:7151/api/ShopItem/404c8232-9048-4519-bfba-6e78dc7005ca?slug=shop-catalog-item-01",
"protocol": "https",
"host": [
"localhost"
],
"port": "7151",
"path": [
"api",
"ShopItem",
"404c8232-9048-4519-bfba-6e78dc7005ca"
],
"query": [
{
"key": "slug",
"value": "shop-catalog-item-01"
}
]
}
},
"response": []
},
{
"name": "03-Post",
"name": "01-Post",
"request": {
"method": "POST",
"header": [
@ -429,19 +334,16 @@
],
"body": {
"mode": "raw",
"raw": "{\r\n \"l10n\":[\r\n {\r\n \"locale\":\"en-US\",\r\n \"slug\":\"shop-catalog-item-05\",\r\n \"description\": \"Descritpion...\",\r\n \"title\":\"Shop item title\",\r\n \"shortText\":\"Shor text...\",\r\n \"text\":\"Text...\",\r\n \"contentType\": \"HTML\",\r\n \"badges\":[\r\n \"sale\"\r\n ]\r\n }\r\n ],\r\n \r\n \"tags\":[\r\n \"react\",\r\n \"redux\",\r\n \"webapi\"\r\n ],\r\n \"categories\":[\r\n \"e154e33f-3cc7-468d-bb66-e0390ddb9ae0\"\r\n ],\r\n \"brandName\":\"Mongodb Brand & Name\",\r\n \"rating\":4.5,\r\n \"price\":20,\r\n \"newPrice\":10,\r\n \"quantity\":100\r\n}"
"raw": "{\r\n \"siteId\": \"404c8232-9048-4519-bfba-6e78dc7005ca\",\r\n \"l10n\": [\r\n {\r\n \"locale\": \"en-US\",\r\n \"slug\": \"shop-catalog-item-05\",\r\n \"description\": \"Lorem ipsum, dolor sit amet consectetur adipisicing elit.\",\r\n \"title\": \"Shop item title\",\r\n \"shortText\": \"Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...\",\r\n \"text\": \"<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit.</p>\",\r\n \"textFormat\": \"HTML\",\r\n \"plainText\": \"Lorem ipsum, dolor sit amet consectetur adipisicing elit.\",\r\n \"badges\": [\r\n \"sale\"\r\n ]\r\n }\r\n ],\r\n\t\"mediaAttachments\": [\r\n\t {\r\n\t\t\"src\": \"api/Image/450x300/5033d24e-a7c3-4960-ac3c-f396d6773692\",\r\n\t\t\"mediaType\": \"image\",\r\n\t\t\"l10n\": [\r\n {\r\n \"locale\": \"en-US\",\r\n \"alt\": \"...\",\r\n\t\t\t\"target\": \"/title-image\",\r\n\t\t\t\"title\": \"Title image\",\r\n\t\t\t\"description\": \"Title image description.\"\r\n\t }\r\n ]\r\n\t }\r\n\t],\r\n \"author\": \"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60\",\r\n \"created\": {\r\n \"$date\": \"2022-01-01T00:00:00.000Z\"\r\n },\r\n \"tags\": [\r\n \"react\",\r\n \"redux\",\r\n \"webapi\"\r\n ],\r\n \"brandName\": \"Mongodb Brand & Name\",\r\n \"sku\": \"SKU-05\",\r\n \"rating\": 4.5,\r\n \"price\": 20,\r\n \"newPrice\": 10,\r\n \"quantity\": 100\r\n }"
},
"url": {
"raw": "https://localhost:7151/api/ShopItem/404c8232-9048-4519-bfba-6e78dc7005ca/SKU-05",
"protocol": "https",
"raw": "{{baseUrl}}/ShopItem/{{siteId}}/SKU-05",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"SKU-05"
]
}
@ -449,7 +351,74 @@
"response": []
},
{
"name": "04-Put",
"name": "02-Get",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"type": "default"
},
{
"key": "Accept",
"value": "application/json",
"type": "default"
}
],
"url": {
"raw": "{{baseUrl}}//ShopItem/{{siteId}}/SKU-05",
"host": [
"{{baseUrl}}"
],
"path": [
"",
"ShopItem",
"{{siteId}}",
"SKU-05"
]
}
},
"response": []
},
{
"name": "03-GetSlug",
"request": {
"method": "GET",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"type": "default"
},
{
"key": "Accept",
"value": "application/json",
"type": "default"
}
],
"url": {
"raw": "{{baseUrl}}/ShopItem/{{siteId}}?slug=shop-catalog-item-05",
"host": [
"{{baseUrl}}"
],
"path": [
"",
"ShopItem",
"{{siteId}}"
],
"query": [
{
"key": "slug",
"value": "shop-catalog-item-05"
}
]
}
},
"response": []
},
{
"name": "05-Put",
"request": {
"method": "PUT",
"header": [
@ -466,19 +435,16 @@
],
"body": {
"mode": "raw",
"raw": "{\r\n \"l10n\":[\r\n {\r\n \"locale\":\"en-US\",\r\n \"slug\":\"shop-catalog-item-05\",\r\n \"description\": \"New Descritpion...\",\r\n \"title\":\"Shop item title\",\r\n \"shortText\":\"New Shor text...\",\r\n \"text\":\"New Text...\",\r\n \"contentType\": \"HTML\",\r\n \"badges\":[\r\n \"sale\"\r\n ]\r\n }\r\n ],\r\n \r\n \"tags\":[\r\n \"react\",\r\n \"redux\",\r\n \"webapi\"\r\n ],\r\n \"categories\":[\r\n \"e154e33f-3cc7-468d-bb66-e0390ddb9ae0\"\r\n ],\r\n \"brandName\":\"Mongodb Brand & Name\",\r\n \"rating\":4.5,\r\n \"price\":20,\r\n \"newPrice\":10,\r\n \"quantity\":100\r\n}"
"raw": "{\r\n \"siteId\": \"404c8232-9048-4519-bfba-6e78dc7005ca\",\r\n \"l10n\": [\r\n {\r\n \"locale\": \"en-US\",\r\n \"slug\": \"shop-catalog-item-05\",\r\n \"description\": \"Lorem ipsum, dolor sit amet consectetur adipisicing elit.\",\r\n \"title\": \"Shop item title\",\r\n \"shortText\": \"Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eaque fugit ratione dicta mollitia. Officiis ad...\",\r\n \"text\": \"<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit.</p>\",\r\n \"textFormat\": \"HTML\",\r\n \"plainText\": \"Lorem ipsum, dolor sit amet consectetur adipisicing elit.\",\r\n \"badges\": [\r\n \"sale\"\r\n ]\r\n }\r\n ],\r\n\t\"mediaAttachments\": [\r\n\t {\r\n\t\t\"src\": \"api/Image/450x300/5033d24e-a7c3-4960-ac3c-f396d6773692\",\r\n\t\t\"mediaType\": \"image\",\r\n\t\t\"l10n\": [\r\n {\r\n \"locale\": \"en-US\",\r\n \"alt\": \"...\",\r\n\t\t\t\"target\": \"/title-image\",\r\n\t\t\t\"title\": \"Title image\",\r\n\t\t\t\"description\": \"Title image description.\"\r\n\t }\r\n ]\r\n\t }\r\n\t],\r\n \"author\": \"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60\",\r\n \"created\": {\r\n \"$date\": \"2022-01-01T00:00:00.000Z\"\r\n },\r\n \"tags\": [\r\n \"react\",\r\n \"redux\",\r\n \"webapi\"\r\n ],\r\n \"categories\": [\r\n \"e154e33f-3cc7-468d-bb66-e0390ddb9ae0\"\r\n ],\r\n \"brandName\": \"Mongodb Brand & Name\",\r\n \"sku\": \"SKU-05\",\r\n \"rating\": 4.5,\r\n \"price\": 20,\r\n \"newPrice\": 10,\r\n \"quantity\": 100\r\n }"
},
"url": {
"raw": "https://localhost:7151/api/ShopItem/404c8232-9048-4519-bfba-6e78dc7005ca/SKU-05",
"protocol": "https",
"raw": "{{baseUrl}}/ShopItem/{{siteId}}/SKU-05",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"SKU-05"
]
}
@ -486,7 +452,7 @@
"response": []
},
{
"name": "05-Delete",
"name": "06-Delete",
"request": {
"method": "DELETE",
"header": [
@ -502,16 +468,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/ShopItem/404c8232-9048-4519-bfba-6e78dc7005ca/SKU-05",
"protocol": "https",
"raw": "{{baseUrl}}/ShopItem/{{siteId}}/SKU-05",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"SKU-05"
]
}
@ -540,16 +503,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/ShopItems/404c8232-9048-4519-bfba-6e78dc7005ca?currentPage=1&itemsPerPage=4",
"protocol": "https",
"raw": "{{baseUrl}}/ShopItems/{{siteId}}?currentPage=1&itemsPerPage=4",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopItems",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
],
"query": [
{
@ -582,16 +542,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/ShopItems/404c8232-9048-4519-bfba-6e78dc7005ca?currentPage=2&itemsPerPage=2&locale=en-US",
"protocol": "https",
"raw": "{{baseUrl}}/ShopItems/{{siteId}}?currentPage=2&itemsPerPage=2&locale=en-US",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopItems",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
],
"query": [
{
@ -628,16 +585,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/ShopItems/404c8232-9048-4519-bfba-6e78dc7005ca",
"protocol": "https",
"raw": "{{baseUrl}}/ShopItems/{{siteId}}",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopItems",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
]
}
},
@ -665,16 +619,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/ShopCartItem/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/SKU-04?locale=en-US",
"protocol": "https",
"raw": "{{baseUrl}}/ShopCartItem/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/SKU-04?locale=en-US",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopCartItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"SKU-04"
],
@ -709,16 +660,14 @@
"raw": "{\r\n \"quantity\": 1\r\n}"
},
"url": {
"raw": "https://localhost:7151/api/ShopCartItem/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/SKU-05",
"protocol": "https",
"raw": "{{baseUrl}}//ShopCartItem/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/SKU-05",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"",
"ShopCartItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"SKU-05"
]
@ -747,16 +696,13 @@
"raw": "{\r\n \"quantity\": 5\r\n}"
},
"url": {
"raw": "https://localhost:7151/api/ShopCartItem/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/SKU-04",
"protocol": "https",
"raw": "{{baseUrl}}/ShopCartItem/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/SKU-04",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopCartItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"SKU-04"
]
@ -781,16 +727,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/ShopCartItem/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/SKU-04",
"protocol": "https",
"raw": "{{baseUrl}}/ShopCartItem/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/SKU-04",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopCartItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"SKU-04"
]
@ -820,16 +763,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/ShopCartItems/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60?locale=en-US",
"protocol": "https",
"raw": "{{baseUrl}}/ShopCartItems/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60?locale=en-US",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopCartItems",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60"
],
"query": [
@ -859,16 +799,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/ShopCartItems/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"protocol": "https",
"raw": "{{baseUrl}}/ShopCartItems/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"ShopCartItems",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60"
]
}
@ -897,16 +834,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/BlogItem/404c8232-9048-4519-bfba-6e78dc7005ca/11f2f5f8-1270-4640-b082-c2e7ea8e60b4",
"protocol": "https",
"raw": "{{baseUrl}}/BlogItem/{{siteId}}/11f2f5f8-1270-4640-b082-c2e7ea8e60b4",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"BlogItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"11f2f5f8-1270-4640-b082-c2e7ea8e60b4"
]
}
@ -930,16 +864,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/BlogItem/404c8232-9048-4519-bfba-6e78dc7005ca?slug=privacy-terms-and-conditions",
"protocol": "https",
"raw": "{{baseUrl}}/BlogItem/{{siteId}}?slug=privacy-terms-and-conditions",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"BlogItem",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
],
"query": [
{
@ -972,16 +903,13 @@
"raw": "{\r\n\r\n \"l10n\": [\r\n {\r\n \"locale\": \"en-US\",\r\n \"slug\": \"hello-world\",\r\n \"description\": \"Hello world description\",\r\n \"title\": \"Hello world title\",\r\n \"shortText\": \"Hello world short text\",\r\n \"text\": \"<p>Hello World</p>\",\r\n \"contentType\": \"HTML\",\r\n \"badges\": [\r\n \"post\"\r\n ]\r\n }\r\n ],\r\n \"images\": [\r\n {\r\n \"l10n\": [\r\n {\r\n \"locale\": \"en-US\",\r\n \"alt\": \"...\"\r\n },\r\n {\r\n \"locale\": \"it-IT\",\r\n \"alt\": \"...\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"tags\": [\r\n \"privacy\",\r\n \"terms\"\r\n ],\r\n \"categories\": [\r\n \"e154e33f-3cc7-468d-bb66-e0390ddb9ae0\" \r\n ],\r\n \"familyFriendly\": true\r\n}"
},
"url": {
"raw": "https://localhost:7151/api/BlogItem/404c8232-9048-4519-bfba-6e78dc7005ca",
"protocol": "https",
"raw": "{{baseUrl}}/BlogItem/{{siteId}}",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"BlogItem",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
]
}
},
@ -1008,16 +936,13 @@
"raw": "{\r\n\r\n \"l10n\": [\r\n {\r\n \"locale\": \"en-US\",\r\n \"slug\": \"hello-world\",\r\n \"description\": \"Hello world description\",\r\n \"title\": \"Hello world title\",\r\n \"shortText\": \"Hello world short text\",\r\n \"text\": \"<p>New Hello World</p>\",\r\n \"contentType\": \"HTML\",\r\n \"badges\": [\r\n \"post\"\r\n ]\r\n }\r\n ],\r\n \"images\": [\r\n {\r\n \"l10n\": [\r\n {\r\n \"locale\": \"en-US\",\r\n \"alt\": \"...\"\r\n },\r\n {\r\n \"locale\": \"it-IT\",\r\n \"alt\": \"...\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"tags\": [\r\n \"privacy\",\r\n \"terms\"\r\n ],\r\n \"categories\": [\r\n \"e154e33f-3cc7-468d-bb66-e0390ddb9ae0\" \r\n ],\r\n \"familyFriendly\": true\r\n}"
},
"url": {
"raw": "https://localhost:7151/api/BlogItem/404c8232-9048-4519-bfba-6e78dc7005ca/94feda90-ddf6-4717-8879-9c7139e7ff30",
"protocol": "https",
"raw": "{{baseUrl}}/BlogItem/{{siteId}}/94feda90-ddf6-4717-8879-9c7139e7ff30",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"BlogItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"94feda90-ddf6-4717-8879-9c7139e7ff30"
]
}
@ -1041,16 +966,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/BlogItem/404c8232-9048-4519-bfba-6e78dc7005ca/94feda90-ddf6-4717-8879-9c7139e7ff30",
"protocol": "https",
"raw": "{{baseUrl}}/BlogItem/{{siteId}}/94feda90-ddf6-4717-8879-9c7139e7ff30",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"BlogItem",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"94feda90-ddf6-4717-8879-9c7139e7ff30"
]
}
@ -1079,16 +1001,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/BlogItems/404c8232-9048-4519-bfba-6e78dc7005ca?currentPage=2&itemsPerPage=2",
"protocol": "https",
"raw": "{{baseUrl}}/BlogItems/{{siteId}}?currentPage=2&itemsPerPage=2",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"BlogItems",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
],
"query": [
{
@ -1121,16 +1040,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/BlogItems/404c8232-9048-4519-bfba-6e78dc7005ca?currentPage=2&itemsPerPage=2&locale=en-US",
"protocol": "https",
"raw": "{{baseUrl}}/BlogItems/{{siteId}}?currentPage=2&itemsPerPage=2&locale=en-US",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"BlogItems",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
],
"query": [
{
@ -1167,16 +1083,13 @@
}
],
"url": {
"raw": "https://localhost:7151/api/BlogItems/404c8232-9048-4519-bfba-6e78dc7005ca",
"protocol": "https",
"raw": "{{baseUrl}}/BlogItems/{{siteId}}",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"BlogItems",
"404c8232-9048-4519-bfba-6e78dc7005ca"
"{{siteId}}"
]
}
},
@ -1204,16 +1117,84 @@
}
],
"url": {
"raw": "https://localhost:7151/api/File/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/3930ff55-67e1-4763-be59-37407f91e0a9",
"protocol": "https",
"raw": "{{baseUrl}}/File/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/4f163ea9-1786-4192-8efa-3b8ea9ca88fa",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"File",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"4f163ea9-1786-4192-8efa-3b8ea9ca88fa"
]
}
},
"response": []
},
{
"name": "02-Post",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"type": "default"
},
{
"key": "Accept",
"value": "application/ecmascript",
"type": "default"
}
],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "file",
"type": "file",
"src": "/C:/Users/maksym/Desktop/Arpeggios.gp"
}
]
},
"url": {
"raw": "{{baseUrl}}/File/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"host": [
"{{baseUrl}}"
],
"path": [
"File",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60"
]
}
},
"response": []
},
{
"name": "04-Delete",
"request": {
"method": "DELETE",
"header": [
{
"key": "Content-Type",
"value": "application/json",
"type": "default"
},
{
"key": "Accept",
"value": "application/json",
"type": "default"
}
],
"url": {
"raw": "{{baseUrl}}/File/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60/3930ff55-67e1-4763-be59-37407f91e0a9",
"host": [
"{{baseUrl}}"
],
"path": [
"File",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"3930ff55-67e1-4763-be59-37407f91e0a9"
]
@ -1254,16 +1235,13 @@
]
},
"url": {
"raw": "https://localhost:7151/api/Files/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"protocol": "https",
"raw": "{{baseUrl}}/Files/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"Files",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60"
]
}
@ -1298,16 +1276,13 @@
]
},
"url": {
"raw": "https://localhost:7151/api/Files/404c8232-9048-4519-bfba-6e78dc7005ca/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"protocol": "https",
"raw": "{{baseUrl}}/Files/{{siteId}}/fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60",
"host": [
"localhost"
"{{baseUrl}}"
],
"port": "7151",
"path": [
"api",
"Files",
"404c8232-9048-4519-bfba-6e78dc7005ca",
"{{siteId}}",
"fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60"
]
}
@ -1315,6 +1290,63 @@
"response": []
}
]
},
{
"name": "Image",
"item": [
{
"name": "01-Get",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/Image/{{siteId}}/450x300/55ecc564-c086-4e77-a8b2-ef32c2a8c280",
"host": [
"{{baseUrl}}"
],
"path": [
"Image",
"{{siteId}}",
"450x300",
"55ecc564-c086-4e77-a8b2-ef32c2a8c280"
]
}
},
"response": []
}
]
}
],
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
""
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
""
]
}
}
],
"variable": [
{
"key": "baseUrl",
"value": "https://localhost:7151/api",
"type": "default"
},
{
"key": "siteId",
"value": "404c8232-9048-4519-bfba-6e78dc7005ca",
"type": "default"
}
]
}

View File

@ -41,11 +41,11 @@ namespace DataProviders.Abstractions {
#endregion
#region Download
private protected (BucketFile?, IDomainResult) Download(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
DownloadAsync(siteId, userId, fileId, bucketName).Result;
private protected (List<BucketFile>?, IDomainResult) Download(FilterDefinition<GridFSFileInfo> filter, string bucketName) =>
DownloadAsync(filter, bucketName).Result;
private protected Task<(BucketFile?, IDomainResult)> DownloadAsync(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
DownloadAsyncCore(siteId, userId, fileId, bucketName);
private protected Task<(List<BucketFile>?, IDomainResult)> DownloadAsync(FilterDefinition<GridFSFileInfo> filter, string bucketName) =>
DownloadAsyncCore(filter, bucketName);
#endregion
#region Delete
@ -115,49 +115,49 @@ namespace DataProviders.Abstractions {
}
}
private async Task<(BucketFile?, IDomainResult)> DownloadAsyncCore(Guid siteId, Guid userId, Guid fileId, string bucketName) {
private async Task<(List<BucketFile>?, IDomainResult)> DownloadAsyncCore(FilterDefinition<GridFSFileInfo> filter, string bucketName) {
try {
var filter = Builders<GridFSFileInfo>.Filter.And(
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}"),
Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
);
var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime);
var bucket = CreateBucket(bucketName);
using var cursor = await bucket.FindAsync(filter, new GridFSFindOptions {
Limit = 1,
Sort = sort,
});
var result = new List<BucketFile>();
// fileInfo either has the matching file information or is null
var fileInfo = (await cursor.ToListAsync()).FirstOrDefault();
foreach (var fileInfo in await cursor.ToListAsync()) {
if (fileInfo == null)
return IDomainResult.NotFound<BucketFile?>();
return IDomainResult.NotFound<List<BucketFile>?>();
var fileName = fileInfo.Metadata["fileName"].ToString() ?? "";
if (fileName == null)
return IDomainResult.Failed<BucketFile?>();
return IDomainResult.Failed<List<BucketFile>?>();
var contentType = fileInfo.Metadata["contentType"].ToString() ?? "";
if (contentType == null)
return IDomainResult.Failed<BucketFile?>();
return IDomainResult.Failed<List<BucketFile>?>();
var bytes = await bucket.DownloadAsBytesByNameAsync($"{fileId}", new GridFSDownloadByNameOptions {
var bytes = await bucket.DownloadAsBytesByNameAsync($"{fileInfo.Filename}", new GridFSDownloadByNameOptions {
Revision = -1
});
if (bytes == null)
return IDomainResult.Failed<BucketFile?>();
return IDomainResult.Failed<List<BucketFile>?>();
return IDomainResult.Success(new BucketFile(fileName, bytes, contentType));
result.Add(new BucketFile(fileName, bytes, contentType));
}
return result.Count > 0
? IDomainResult.Success(result)
: IDomainResult.NotFound<List<BucketFile>?>();
}
catch (Exception ex) {
_logger.LogError(ex, "Bucket data provider error");
return IDomainResult.Failed<BucketFile?>();
return IDomainResult.Failed<List<BucketFile>?>();
}
}

View File

@ -5,6 +5,7 @@ using DomainResults.Common;
using MongoDB.Driver;
using DataProviders.Abstractions;
using MongoDB.Driver.GridFS;
namespace DataProviders.Buckets {
@ -46,6 +47,29 @@ namespace DataProviders.Buckets {
/// <returns></returns>
public (List<Guid>?, IDomainResult) UploadMany(Guid siteId, Guid userId, List<BucketFile> files) => UploadMany(siteId, userId, files, _bucketName);
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
public (BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId) {
var filter = Builders<GridFSFileInfo>.Filter.And(
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["private"], false),
Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
);
var (list, result) = Download(filter, _bucketName);
if (!result.IsSuccess || list == null)
return (null, result);
return (list.First(), result);
}
/// <summary>
///
/// </summary>
@ -53,7 +77,21 @@ namespace DataProviders.Buckets {
/// <param name="userId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
public (BucketFile?, IDomainResult) Download(Guid siteId, Guid userId, Guid fileId) => Download(siteId, userId, fileId, _bucketName);
public (BucketFile?, IDomainResult) Download(Guid siteId, Guid userId, Guid fileId) {
var filter = Builders<GridFSFileInfo>.Filter.And(
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}"),
Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
);
var (list, result) = Download(filter, _bucketName);
if (!result.IsSuccess || list == null)
return (null, result);
return (list.First(), result);
}
/// <summary>
///

View File

@ -1,10 +1,17 @@
using DomainResults.Common;
using Microsoft.Extensions.Logging;
using DomainResults.Common;
using MongoDB.Driver;
using DataProviders.Abstractions;
namespace DataProviders.Buckets {
/// <summary>
///
/// </summary>
public interface IBucketDataProvider {
public interface IImageBucketDataProvider {
/// <summary>
///
@ -24,6 +31,14 @@ namespace DataProviders.Buckets {
/// <returns></returns>
(List<Guid>?, IDomainResult) UploadMany(Guid siteId, Guid userId, List<BucketFile> files);
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="fileId"></param>
/// <returns></returns>
(BucketFile?, IDomainResult) Download(Guid siteId, Guid fileId);
/// <summary>
///
/// </summary>
@ -50,4 +65,20 @@ namespace DataProviders.Buckets {
/// <returns></returns>
IDomainResult DeletMany(Guid siteId, Guid userId);
}
/// <summary>
///
/// </summary>
public class ImageBucketDataProvider : SpecificBucketDataProviderBase, IImageBucketDataProvider {
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="client"></param>
public ImageBucketDataProvider(
ILogger<BucketDataProviderBase> logger,
IMongoClient client
) : base(logger, client, "images") { }
}
}

View File

@ -1,26 +0,0 @@
using Microsoft.Extensions.Logging;
using DomainResults.Common;
using MongoDB.Driver;
using DataProviders.Abstractions;
namespace DataProviders.Buckets {
/// <summary>
///
/// </summary>
public class ImagesBucketDataProvider : SpecificBucketDataProviderBase, IBucketDataProvider {
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="client"></param>
public ImagesBucketDataProvider(
ILogger<BucketDataProviderBase> logger,
IMongoClient client
) : base(logger, client, "images") { }
}
}

View File

@ -11,7 +11,7 @@ using Core.DomainObjects.Documents;
namespace DataProviders.Collections {
public interface IContentDataProvider {
(Content?, IDomainResult) Get(Guid siteId, string locale);
(List<Content>?, IDomainResult) Get(Guid siteId);
}
public class ContentDataProvider : CollectionDataProviderBase<Content>, IContentDataProvider {
@ -24,14 +24,6 @@ namespace DataProviders.Collections {
ISessionService sessionService) : base(logger, client, idGenerator, sessionService) {
}
public (Content?, IDomainResult) Get(Guid siteId, string locale) {
var (list, result) = GetWithPredicate(x => x.SiteId == siteId
&& (x.Localization.Locale == null || x.Localization.Locale.ToLower() == locale.ToLower()), 0, 0, _collectionName);
if (!result.IsSuccess || list == null)
return (null, result);
return (list.First(), result);
}
public (List<Content>?, IDomainResult) Get(Guid siteId) => GetWithPredicate(x => x.SiteId == siteId, _collectionName);
}
}

View File

@ -29,7 +29,7 @@ namespace DataProviders.Extensions
#endregion
#region Buckets
services.AddSingleton<IBucketDataProvider, ImagesBucketDataProvider>();
services.AddSingleton<IImageBucketDataProvider, ImageBucketDataProvider>();
#endregion
}
}

View File

@ -0,0 +1,107 @@
using System;
using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Processing;
namespace ImageProvider.Extensions {
internal static class IImageProcessingContextExtensions {
internal static IImageProcessingContext ApplyScalingWaterMark(this IImageProcessingContext processingContext, Font font, string text, Color color, float padding, bool wordwrap) {
if (wordwrap) {
return processingContext.ApplyScalingWaterMarkWordWrap(font, text, color, padding);
}
else {
return processingContext.ApplyScalingWaterMarkSimple(font, text, color, padding);
}
}
internal static IImageProcessingContext ApplyScalingWaterMarkSimple(this IImageProcessingContext processingContext,
Font font,
string text,
Color color,
float padding) {
Size imgSize = processingContext.GetCurrentSize();
float targetWidth = imgSize.Width - (padding * 2);
float targetHeight = imgSize.Height - (padding * 2);
// measure the text size
FontRectangle size = TextMeasurer.Measure(text, new RendererOptions(font));
//find out how much we need to scale the text to fill the space (up or down)
float scalingFactor = Math.Min(imgSize.Width / size.Width, imgSize.Height / size.Height);
//create a new font
Font scaledFont = new Font(font, scalingFactor * font.Size);
var center = new PointF(imgSize.Width / 2, imgSize.Height / 2);
var textGraphicOptions = new TextGraphicsOptions() {
TextOptions = {
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
}
};
return processingContext.DrawText(textGraphicOptions, text, scaledFont, color, center);
}
internal static IImageProcessingContext ApplyScalingWaterMarkWordWrap(this IImageProcessingContext processingContext,
Font font,
string text,
Color color,
float padding) {
Size imgSize = processingContext.GetCurrentSize();
float targetWidth = imgSize.Width - (padding * 2);
float targetHeight = imgSize.Height - (padding * 2);
float targetMinHeight = imgSize.Height - (padding * 3); // must be with in a margin width of the target height
// now we are working i 2 dimensions at once and can't just scale because it will cause the text to
// reflow we need to just try multiple times
var scaledFont = font;
FontRectangle s = new FontRectangle(0, 0, float.MaxValue, float.MaxValue);
float scaleFactor = (scaledFont.Size / 2); // every time we change direction we half this size
int trapCount = (int)scaledFont.Size * 2;
if (trapCount < 10) {
trapCount = 10;
}
bool isTooSmall = false;
while ((s.Height > targetHeight || s.Height < targetMinHeight) && trapCount > 0) {
if (s.Height > targetHeight) {
if (isTooSmall) {
scaleFactor = scaleFactor / 2;
}
scaledFont = new Font(scaledFont, scaledFont.Size - scaleFactor);
isTooSmall = false;
}
if (s.Height < targetMinHeight) {
if (!isTooSmall) {
scaleFactor = scaleFactor / 2;
}
scaledFont = new Font(scaledFont, scaledFont.Size + scaleFactor);
isTooSmall = true;
}
trapCount--;
s = TextMeasurer.Measure(text, new RendererOptions(scaledFont) {
WrappingWidth = targetWidth
});
}
var center = new PointF(padding, imgSize.Height / 2);
var textGraphicOptions = new TextGraphicsOptions() {
TextOptions = {
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Center,
WrapTextWidth = targetWidth
}
};
return processingContext.DrawText(textGraphicOptions, text, scaledFont, color, center);
}
}
}

View File

@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
using SixLabors.Fonts;
namespace ImageProvider.Extensions {
public static class ServiceCollectionExtensions {
public static void RegisterImageProvider(this IServiceCollection services) {
services.AddSingleton<IFontCollection>(x => {
var fontCollection = new FontCollection();
foreach (var font in Directory.EnumerateFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Fonts"), "*.ttf", SearchOption.AllDirectories))
fontCollection.Install(font);
return fontCollection;
});
services.AddSingleton<IImageProvider, ImageProvider>();
}
}
}

View File

@ -0,0 +1,23 @@
using Core.Abstractions;
using SixLabors.Fonts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ImageProvider.Fonts {
public class FontStylesEnum : Enumeration {
public FontStyle FontStyle { get; private set; }
public static FontStylesEnum Regular = new(0, "Regular", FontStyle.Regular);
public static FontStylesEnum Bold = new(0, "Bold", FontStyle.Bold);
public static FontStylesEnum Italic = new(0, "Italic", FontStyle.Italic);
public static FontStylesEnum BoldItalic = new(0, "BoldItalic", FontStyle.BoldItalic);
private FontStylesEnum(int id, string displayName, FontStyle fontStyle) : base(id, displayName) {
FontStyle = fontStyle;
}
}
}

View File

@ -0,0 +1,10 @@
using Core.Abstractions;
namespace ImageProvider.Fonts {
public class FontsEnum : Enumeration {
public static FontsEnum Montserrat = new(0, "Montserrat");
private FontsEnum(int id, string displayName) : base(id, displayName) { }
}
}

View File

@ -0,0 +1,101 @@
using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Processing;
using ImageProvider.Extensions;
namespace ImageProvider {
public class ImageBuilder : IDisposable {
private Image _image;
/// <summary>
///
/// </summary>
/// <param name="bytes"></param>
public ImageBuilder(byte[] bytes) {
_image = Image.Load(bytes);
}
public void Load(byte[] bytes) {
_image = Image.Load(bytes);
}
/// <summary>
///
/// </summary>
/// <param name="font"> for scaling water mark size is largely ignored.</param>
/// <param name="text"></param>
public void Watermark(Font font, string text) =>
_image = _image.Clone(ctx => ctx.ApplyScalingWaterMark(font, text, Color.HotPink, 5, false));
/// <summary>
///
/// </summary>
/// <param name="font"> for scaling water mark size is largely ignored.</param>
/// <param name="longText"></param>
public void WatermarkWordWrap(Font font, string longText) =>
_image = _image.Clone(ctx => ctx.ApplyScalingWaterMark(font, longText, Color.HotPink, 5, true));
/// <summary>
///
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public void Resize(int width, int height) =>
_image.Mutate(img => img.Resize(width, height));
public void FitWidthResize(int width) {
_image.Mutate(x => x.Resize(new ResizeOptions {
Size = new Size(width, width),
Mode = ResizeMode.Max
}));
}
public void FitHeightResize(int height) {
_image.Mutate(x => x.Resize(new ResizeOptions {
Size = new Size(height, height),
Mode = ResizeMode.Max
}));
}
public void Crop(int cWidth, int cHeigth) =>
Crop((_image.Width - cWidth) / 2, (_image.Height - cHeigth) / 2, cWidth, cHeigth);
public void Crop(int x, int y, int cWidth, int cHeigth) =>
_image.Mutate(img => img.Crop(new Rectangle(x, y, cWidth, cHeigth)));
private byte[] Buid(IImageEncoder encoder) {
using var ms = new MemoryStream();
_image.Save(ms, encoder);
return ms.ToArray();
}
public byte[] ToGif() => Buid(new GifEncoder());
public byte[] ToJpeg() => Buid(new JpegEncoder());
public byte[] ToPbm() => Buid(new PbmEncoder());
public byte[] ToPng() => Buid(new PngEncoder());
public byte[] ToTga() => Buid(new TgaEncoder());
public byte[] ToTiff() => Buid(new TiffEncoder());
public byte[] ToWebp() => Buid(new WebpEncoder());
public void Dispose() => _image.Dispose();
}
}

View File

@ -0,0 +1,69 @@
using DomainResults.Common;
using ImageProvider.Fonts;
using Microsoft.Extensions.Logging;
using SixLabors.Fonts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ImageProvider {
public interface IImageProvider {
(byte[]?, IDomainResult) Resize(byte[] bytes, int width, int height);
(byte[]?, IDomainResult) ResizeAndWatermark(byte[] bytes, int width, int height, FontsEnum fontName, FontStylesEnum fontStyle, string text);
}
public class ImageProvider : IImageProvider {
private readonly ILogger<ImageProvider> _logger;
private readonly IFontCollection _fontCollection;
public ImageProvider(
ILogger<ImageProvider> logger,
IFontCollection fontCollection
) {
_logger = logger;
_fontCollection = fontCollection;
}
/// <summary>
///
/// </summary>
/// <param name="bytes"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public (byte[]?, IDomainResult) Resize(byte[] bytes, int width, int height) {
try {
using var imageBuilder = new ImageBuilder(bytes);
imageBuilder.Resize(width, height);
return IDomainResult.Success(imageBuilder.ToJpeg());
}
catch (Exception ex) {
_logger.LogError("Unhandled exception", ex);
return IDomainResult.Failed<byte[]?>();
}
}
public (byte[]?, IDomainResult) ResizeAndWatermark(byte[] bytes, int width, int height, FontsEnum fontName, FontStylesEnum fontStyle, string text) {
try {
var font = _fontCollection.Families.Single(x => x.Name == fontName.Name)
.CreateFont(10, fontStyle.FontStyle);
using var imageBuilder = new ImageBuilder(bytes);
imageBuilder.FitWidthResize(width);
imageBuilder.Crop(width, height);
// imageBuilder.Watermark(font, text);
return IDomainResult.Success(imageBuilder.ToJpeg());
}
catch (Exception ex) {
_logger.LogError("Unhandled exception", ex);
return IDomainResult.Failed<byte[]?>();
}
}
}
}

View File

@ -0,0 +1,78 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DomainResult.Common" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta0013" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\DataProviders\DataProviders.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="Fonts\Montserrat\Montserrat-Black.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-BlackItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-Bold.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-BoldItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-ExtraBold.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-ExtraBoldItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-ExtraLight.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-ExtraLightItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-Italic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-Light.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-LightItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-Medium.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-MediumItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-Regular.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-SemiBold.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-SemiBoldItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-Thin.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Montserrat\Montserrat-ThinItalic.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -1,5 +0,0 @@
namespace ImageService {
public class Class1 {
}
}

View File

@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -27,7 +27,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExtensionsTests", "Tests\Ex
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreTests", "Tests\Core\CoreTests.csproj", "{04CB9827-AA6D-4708-A26D-8420C842506D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageService", "Services\ImageService\ImageService.csproj", "{16552644-D7EE-4B4A-A725-79909A8103DE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageProvider", "Services\ImageProvider\ImageProvider.csproj", "{16552644-D7EE-4B4A-A725-79909A8103DE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileSecurityService", "Services\FileSecurityService\FileSecurityService.csproj", "{AD515653-9145-4894-9017-0ABA5A5892F4}"
EndProject

View File

@ -0,0 +1,52 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using WeatherForecast.Services;
using DomainResults.Mvc;
namespace WeatherForecast.Controllers {
/// <summary>
///
/// </summary>
[AllowAnonymous]
[Route("api/[controller]")]
[ApiController]
public class ImageController : ControllerBase {
private readonly IImageService _imageService;
/// <summary>
///
/// </summary>
/// <param name="imgeService"></param>
public ImageController(
IImageService imgeService
) {
_imageService = imgeService;
}
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="imageId"></param>
/// <returns></returns>
[HttpGet("{siteId}/{width}x{height}/{imageId}")]
public IActionResult Get([FromRoute] Guid siteId, [FromRoute] int width, [FromRoute] int height, [FromRoute] Guid imageId) {
var (file, result) = _imageService.Get(siteId, width, height, imageId);
if (!result.IsSuccess || file == null)
return result.ToActionResult();
var stream = new MemoryStream(file.Bytes);
return new FileStreamResult(stream, file.ContentType) {
FileDownloadName = file.Name
};
}
}
}

View File

@ -49,11 +49,10 @@ namespace WeatherForecast.Models.Requests {
return new ShopItem() {
L10n = L10n.Select(x => x.ToDomainObject()).ToList(),
// Images
MediaAttachments = MediaAttachments?.Select(x => x.ToDomainObject()).ToList(),
// Author
Created = DateTime.UtcNow,
Tags = Tags,
Categories = Categories,
FamilyFriendly = FamilyFriendly,
BrandName = BrandName,

View File

@ -87,12 +87,12 @@ namespace WeatherForecast.Services {
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="requestModel"></param>
/// <param name="requestData"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public (Guid?, IDomainResult) Post(Guid siteId, BlogItemRequestModel requestModel) {
public (Guid?, IDomainResult) Post(Guid siteId, BlogItemRequestModel requestData) {
try {
var item = requestModel.ToDomainObject();
var item = requestData.ToDomainObject();
item.SiteId = siteId;
var (_, getResult) = _blogCatalogDataProvider.GetBySlugs(item.SiteId, item.L10n.Select(x => x.Slug).ToList());
@ -102,7 +102,7 @@ namespace WeatherForecast.Services {
// TODO: should be recovered from users by jwt
item.Author = "fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60".ToGuid();
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, item.Categories);
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, requestData.Categories);
if (!addCategoriesResult.IsSuccess || categories == null)
return (null, addCategoriesResult);
@ -194,7 +194,7 @@ namespace WeatherForecast.Services {
newItem.Created = item.Created;
newItem.Author = item.Author;
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, item.Categories);
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, requestData.Categories);
if (!addCategoriesResult.IsSuccess || categories == null)
return (null, addCategoriesResult);

View File

@ -49,12 +49,11 @@ namespace WeatherForecast.Services {
/// <returns></returns>
public (ContentResponseModel?, IDomainResult) GetContent(Guid siteId, string locale) {
try {
var (content, result) = _contentDataProvider.Get(siteId, locale);
var (content, result) = _contentDataProvider.Get(siteId);
if (!result.IsSuccess || content == null)
return (null, result);
return IDomainResult.Success(new ContentResponseModel(content));
return IDomainResult.Success(new ContentResponseModel(content.Single(x => x.Localization.Locale == locale)));
}
catch (Exception ex) {
_logger.LogError(ex, "Unhandled exception");

View File

@ -44,7 +44,7 @@ namespace WeatherForecast.Services {
private readonly ILogger<FilesService> _logger;
private readonly IFilesService _filesService;
private readonly IBucketDataProvider _imageBucketDataProvider;
private readonly IImageBucketDataProvider _imageBucketDataProvider;
/// <summary>
///
@ -55,7 +55,7 @@ namespace WeatherForecast.Services {
public FileService(
ILogger<FilesService> logger,
IFilesService filesService,
IBucketDataProvider imageBucketDataProvider
IImageBucketDataProvider imageBucketDataProvider
) {
_logger = logger;
_filesService = filesService;

View File

@ -35,7 +35,7 @@ namespace WeatherForecast.Services {
private readonly ILogger<FilesService> _logger;
private readonly IFileSecurityService _fileSecurityService;
private readonly IBucketDataProvider _imageBucketDataProvider;
private readonly IImageBucketDataProvider _imageBucketDataProvider;
/// <summary>
///
@ -45,7 +45,7 @@ namespace WeatherForecast.Services {
public FilesService(
ILogger<FilesService> logger,
IFileSecurityService fileSecurityService,
IBucketDataProvider imageBucketDataProvider
IImageBucketDataProvider imageBucketDataProvider
) {
_logger = logger;
_fileSecurityService = fileSecurityService;

View File

@ -0,0 +1,88 @@

using DataProviders;
using DataProviders.Buckets;
using DataProviders.Collections;
using DomainResults.Common;
using ImageProvider;
using ImageProvider.Fonts;
namespace WeatherForecast.Services {
/// <summary>
///
/// </summary>
public interface IImageService {
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="imageId"></param>
/// <returns></returns>
(BucketFile?, IDomainResult) Get(Guid siteId, int width, int height, Guid imageId);
}
/// <summary>
///
/// </summary>
public class ImageService : IImageService {
private readonly ILogger<ImageService> _logger;
private readonly IImageBucketDataProvider _imageBucketDataProvider;
private readonly IImageProvider _imageProvider;
private readonly IContentDataProvider _contentDataProvider;
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="imageBucketDataProvider"></param>
/// <param name="imageProvider"></param>
public ImageService(
ILogger<ImageService> logger,
IImageBucketDataProvider imageBucketDataProvider,
IImageProvider imageProvider,
IContentDataProvider contentDataProvider
) {
_logger = logger;
_imageBucketDataProvider = imageBucketDataProvider;
_imageProvider = imageProvider;
_contentDataProvider = contentDataProvider;
}
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="imageId"></param>
/// <returns></returns>
public (BucketFile?, IDomainResult) Get(Guid siteId, int width, int height, Guid imageId) {
try {
var (file, downloadResult) = _imageBucketDataProvider.Download(siteId, imageId);
if (!downloadResult.IsSuccess || file == null)
return IDomainResult.Failed<BucketFile?> ();
var (content, getContentResult) = _contentDataProvider.Get(siteId);
if (!getContentResult.IsSuccess || content == null)
return IDomainResult.Failed<BucketFile?>();
var (image, transformResult) = _imageProvider.ResizeAndWatermark(file.Bytes, width, height, FontsEnum.Montserrat, FontStylesEnum.Regular, content.First().SiteName);
if (!transformResult.IsSuccess || image == null)
return IDomainResult.Failed<BucketFile?>();
return IDomainResult.Success(new BucketFile(file.Name, image, "image/jpeg"));
}
catch (Exception ex) {
_logger.LogError(ex, "Unhandled exception");
return IDomainResult.Failed<BucketFile?> (ex.Message);
}
}
}
}

View File

@ -89,11 +89,11 @@ namespace WeatherForecast.Services {
/// </summary>
/// <param name="siteId"></param>
/// <param name="sku"></param>
/// <param name="requestModel"></param>
/// <param name="requestData"></param>
/// <returns></returns>
public (Guid?, IDomainResult) Post(Guid siteId, string sku, ShopItemRequestModel requestModel) {
public (Guid?, IDomainResult) Post(Guid siteId, string sku, ShopItemRequestModel requestData) {
try {
var item = requestModel.ToDomainObject();
var item = requestData.ToDomainObject();
item.SiteId = siteId;
item.Sku = sku;
@ -105,7 +105,7 @@ namespace WeatherForecast.Services {
// TODO: should be recovered from users by jwt
item.Author = "fdc5aa50-ee68-4bae-a8e6-b8ae2c258f60".ToGuid();
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, item.Categories);
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, requestData.Categories);
if (!addCategoriesResult.IsSuccess || categories == null)
return (null, addCategoriesResult);
@ -197,7 +197,7 @@ namespace WeatherForecast.Services {
newItem.Created = item.Created;
newItem.Author = item.Author;
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, item.Categories);
var (categories, addCategoriesResult) = AddCategoryIfNullOrEmpty(siteId, requestData.Categories);
if (!addCategoriesResult.IsSuccess || categories == null)
return (null, addCategoriesResult);

View File

@ -7,6 +7,7 @@ using WeatherForecast.Services;
using DataProviders.Extensions;
using System.Text.Json.Serialization;
using FileSecurityService.Extensions;
using ImageProvider.Extensions;
namespace WeatherForecast {
@ -83,9 +84,11 @@ namespace WeatherForecast {
services.AddScoped<IFileService, FileService>();
services.AddScoped<IFilesService, FilesService>();
services.AddScoped<IImageService, ImageService>();
services.RegisterDataproviders(appSettings);
services.RegisterFileSecurityService();
services.RegisterImageProvider();
#region Swagger
services.ConfigureSwaggerGen(options => {

View File

@ -29,6 +29,7 @@
<ProjectReference Include="..\DataProviders\DataProviders.csproj" />
<ProjectReference Include="..\Extensions\Extensions.csproj" />
<ProjectReference Include="..\Services\FileSecurityService\FileSecurityService.csproj" />
<ProjectReference Include="..\Services\ImageProvider\ImageProvider.csproj" />
</ItemGroup>
</Project>