Fix admin route access and backend configuration
- Added /admin redirect to login page in nginx config - Fixed backend server.js route ordering for proper admin handling - Updated authentication middleware and routes - Added user management routes - Configured PostgreSQL integration - Updated environment configuration
This commit is contained in:
43
Sky_Art_shop/Views/AdminPortfolio/Categories.cshtml
Normal file
43
Sky_Art_shop/Views/AdminPortfolio/Categories.cshtml
Normal file
@@ -0,0 +1,43 @@
|
||||
@model List<SkyArtShop.Models.PortfolioCategory>
|
||||
@{
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
ViewData["Title"] = "Portfolio Categories";
|
||||
}
|
||||
<div class="card">
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Categories</h5>
|
||||
<a class="btn btn-primary" href="/admin/portfolio/category/create">Create Category</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Slug</th>
|
||||
<th>Order</th>
|
||||
<th>Active</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var c in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>@c.Name</td>
|
||||
<td>@c.Slug</td>
|
||||
<td>@c.DisplayOrder</td>
|
||||
<td>@(c.IsActive ? "Yes" : "No")</td>
|
||||
<td>
|
||||
<a class="btn btn-sm btn-secondary" href="/admin/portfolio/category/edit/@c.Id">Edit</a>
|
||||
<form method="post" action="/admin/portfolio/category/delete/@c.Id" class="d-inline" onsubmit="return confirm('Delete this category?');">
|
||||
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
81
Sky_Art_shop/Views/AdminPortfolio/CreateCategory.cshtml
Normal file
81
Sky_Art_shop/Views/AdminPortfolio/CreateCategory.cshtml
Normal file
@@ -0,0 +1,81 @@
|
||||
@model SkyArtShop.Models.PortfolioCategory
|
||||
@{
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
ViewData["Title"] = "Create Category";
|
||||
}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All" class="text-danger mb-3"></div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Name</label>
|
||||
<input class="form-control" name="Name" value="@Model.Name" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description</label>
|
||||
<textarea class="form-control" id="categoryDescription" name="Description">@Model.Description</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Display Order</label>
|
||||
<input type="number" class="form-control" name="DisplayOrder" value="@Model.DisplayOrder" />
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="IsActive" @(Model.IsActive ? "checked" : "") />
|
||||
<label class="form-check-label">Active</label>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<a class="btn btn-secondary" href="/admin/portfolio/categories">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5/40.1.0/classic/ckeditor.js"></script>
|
||||
<script>
|
||||
let categoryEditor;
|
||||
ClassicEditor
|
||||
.create(document.querySelector('#categoryDescription'), {
|
||||
toolbar: {
|
||||
items: [
|
||||
'heading', '|',
|
||||
'bold', 'italic', 'underline', 'strikethrough', '|',
|
||||
'link', 'blockQuote', '|',
|
||||
'bulletedList', 'numberedList', '|',
|
||||
'outdent', 'indent', '|',
|
||||
'alignment', '|',
|
||||
'insertTable', '|',
|
||||
'fontSize', 'fontColor', 'fontBackgroundColor', '|',
|
||||
'removeFormat', '|',
|
||||
'undo', 'redo', '|',
|
||||
'sourceEditing'
|
||||
],
|
||||
shouldNotGroupWhenFull: true
|
||||
},
|
||||
heading: {
|
||||
options: [
|
||||
{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
|
||||
{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
|
||||
{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
|
||||
{ model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
|
||||
{ model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' }
|
||||
]
|
||||
},
|
||||
fontSize: {
|
||||
options: ['small', 'default', 'big']
|
||||
},
|
||||
table: {
|
||||
contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells']
|
||||
},
|
||||
htmlSupport: {
|
||||
allow: [{ name: /.*/, attributes: true, classes: true, styles: true }]
|
||||
}
|
||||
})
|
||||
.then(editor => {
|
||||
categoryEditor = editor;
|
||||
document.querySelector('form').addEventListener('submit', function(e) {
|
||||
document.querySelector('#categoryDescription').value = categoryEditor.getData();
|
||||
});
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
</script>
|
||||
}
|
||||
87
Sky_Art_shop/Views/AdminPortfolio/CreateProject.cshtml
Normal file
87
Sky_Art_shop/Views/AdminPortfolio/CreateProject.cshtml
Normal file
@@ -0,0 +1,87 @@
|
||||
@model SkyArtShop.Models.PortfolioProject
|
||||
@{
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
ViewData["Title"] = "Create Project";
|
||||
var categories = ViewBag.Categories as List<SkyArtShop.Models.PortfolioCategory> ?? new();
|
||||
}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All" class="text-danger mb-3"></div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Title</label>
|
||||
<input class="form-control" name="Title" value="@Model.Title" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Category</label>
|
||||
<select class="form-select" name="CategoryId">
|
||||
@foreach (var c in categories)
|
||||
{
|
||||
<option value="@c.Id">@c.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description</label>
|
||||
<textarea class="form-control" id="portfolioDescription" name="Description">@Model.Description</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Display Order</label>
|
||||
<input type="number" class="form-control" name="DisplayOrder" value="@Model.DisplayOrder" />
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<a class="btn btn-secondary" href="/admin/portfolio/projects">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5/40.1.0/classic/ckeditor.js"></script>
|
||||
<script>
|
||||
let portfolioEditor;
|
||||
ClassicEditor
|
||||
.create(document.querySelector('#portfolioDescription'), {
|
||||
toolbar: {
|
||||
items: [
|
||||
'heading', '|',
|
||||
'bold', 'italic', 'underline', 'strikethrough', '|',
|
||||
'link', 'blockQuote', '|',
|
||||
'bulletedList', 'numberedList', '|',
|
||||
'outdent', 'indent', '|',
|
||||
'alignment', '|',
|
||||
'insertTable', '|',
|
||||
'fontSize', 'fontColor', 'fontBackgroundColor', '|',
|
||||
'removeFormat', '|',
|
||||
'undo', 'redo', '|',
|
||||
'sourceEditing'
|
||||
],
|
||||
shouldNotGroupWhenFull: true
|
||||
},
|
||||
heading: {
|
||||
options: [
|
||||
{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
|
||||
{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
|
||||
{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
|
||||
{ model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
|
||||
{ model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' }
|
||||
]
|
||||
},
|
||||
fontSize: {
|
||||
options: ['small', 'default', 'big']
|
||||
},
|
||||
table: {
|
||||
contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells']
|
||||
},
|
||||
htmlSupport: {
|
||||
allow: [{ name: /.*/, attributes: true, classes: true, styles: true }]
|
||||
}
|
||||
})
|
||||
.then(editor => {
|
||||
portfolioEditor = editor;
|
||||
document.querySelector('form').addEventListener('submit', function(e) {
|
||||
document.querySelector('#portfolioDescription').value = portfolioEditor.getData();
|
||||
});
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
</script>
|
||||
}
|
||||
81
Sky_Art_shop/Views/AdminPortfolio/EditCategory.cshtml
Normal file
81
Sky_Art_shop/Views/AdminPortfolio/EditCategory.cshtml
Normal file
@@ -0,0 +1,81 @@
|
||||
@model SkyArtShop.Models.PortfolioCategory
|
||||
@{
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
ViewData["Title"] = "Edit Category";
|
||||
}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All" class="text-danger mb-3"></div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Name</label>
|
||||
<input class="form-control" name="Name" value="@Model.Name" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description</label>
|
||||
<textarea class="form-control" id="categoryDescription" name="Description">@Model.Description</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Display Order</label>
|
||||
<input type="number" class="form-control" name="DisplayOrder" value="@Model.DisplayOrder" />
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="IsActive" @(Model.IsActive ? "checked" : "") />
|
||||
<label class="form-check-label">Active</label>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<a class="btn btn-secondary" href="/admin/portfolio/categories">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5/40.1.0/classic/ckeditor.js"></script>
|
||||
<script>
|
||||
let categoryEditor;
|
||||
ClassicEditor
|
||||
.create(document.querySelector('#categoryDescription'), {
|
||||
toolbar: {
|
||||
items: [
|
||||
'heading', '|',
|
||||
'bold', 'italic', 'underline', 'strikethrough', '|',
|
||||
'link', 'blockQuote', '|',
|
||||
'bulletedList', 'numberedList', '|',
|
||||
'outdent', 'indent', '|',
|
||||
'alignment', '|',
|
||||
'insertTable', '|',
|
||||
'fontSize', 'fontColor', 'fontBackgroundColor', '|',
|
||||
'removeFormat', '|',
|
||||
'undo', 'redo', '|',
|
||||
'sourceEditing'
|
||||
],
|
||||
shouldNotGroupWhenFull: true
|
||||
},
|
||||
heading: {
|
||||
options: [
|
||||
{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
|
||||
{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
|
||||
{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
|
||||
{ model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
|
||||
{ model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' }
|
||||
]
|
||||
},
|
||||
fontSize: {
|
||||
options: ['small', 'default', 'big']
|
||||
},
|
||||
table: {
|
||||
contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells']
|
||||
},
|
||||
htmlSupport: {
|
||||
allow: [{ name: /.*/, attributes: true, classes: true, styles: true }]
|
||||
}
|
||||
})
|
||||
.then(editor => {
|
||||
categoryEditor = editor;
|
||||
document.querySelector('form').addEventListener('submit', function(e) {
|
||||
document.querySelector('#categoryDescription').value = categoryEditor.getData();
|
||||
});
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
</script>
|
||||
}
|
||||
87
Sky_Art_shop/Views/AdminPortfolio/EditProject.cshtml
Normal file
87
Sky_Art_shop/Views/AdminPortfolio/EditProject.cshtml
Normal file
@@ -0,0 +1,87 @@
|
||||
@model SkyArtShop.Models.PortfolioProject
|
||||
@{
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
ViewData["Title"] = "Edit Project";
|
||||
var categories = ViewBag.Categories as List<SkyArtShop.Models.PortfolioCategory> ?? new();
|
||||
}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All" class="text-danger mb-3"></div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Title</label>
|
||||
<input class="form-control" name="Title" value="@Model.Title" />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Category</label>
|
||||
<select class="form-select" name="CategoryId">
|
||||
@foreach (var c in categories)
|
||||
{
|
||||
<option value="@c.Id" selected="@(Model.CategoryId == c.Id ? "selected" : null)">@c.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Description</label>
|
||||
<textarea class="form-control" id="portfolioDescription" name="Description">@Model.Description</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Display Order</label>
|
||||
<input type="number" class="form-control" name="DisplayOrder" value="@Model.DisplayOrder" />
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">Save</button>
|
||||
<a class="btn btn-secondary" href="/admin/portfolio/projects">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script src="https://cdn.ckeditor.com/ckeditor5/40.1.0/classic/ckeditor.js"></script>
|
||||
<script>
|
||||
let portfolioEditor;
|
||||
ClassicEditor
|
||||
.create(document.querySelector('#portfolioDescription'), {
|
||||
toolbar: {
|
||||
items: [
|
||||
'heading', '|',
|
||||
'bold', 'italic', 'underline', 'strikethrough', '|',
|
||||
'link', 'blockQuote', '|',
|
||||
'bulletedList', 'numberedList', '|',
|
||||
'outdent', 'indent', '|',
|
||||
'alignment', '|',
|
||||
'insertTable', '|',
|
||||
'fontSize', 'fontColor', 'fontBackgroundColor', '|',
|
||||
'removeFormat', '|',
|
||||
'undo', 'redo', '|',
|
||||
'sourceEditing'
|
||||
],
|
||||
shouldNotGroupWhenFull: true
|
||||
},
|
||||
heading: {
|
||||
options: [
|
||||
{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
|
||||
{ model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
|
||||
{ model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
|
||||
{ model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
|
||||
{ model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' }
|
||||
]
|
||||
},
|
||||
fontSize: {
|
||||
options: ['small', 'default', 'big']
|
||||
},
|
||||
table: {
|
||||
contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells']
|
||||
},
|
||||
htmlSupport: {
|
||||
allow: [{ name: /.*/, attributes: true, classes: true, styles: true }]
|
||||
}
|
||||
})
|
||||
.then(editor => {
|
||||
portfolioEditor = editor;
|
||||
document.querySelector('form').addEventListener('submit', function(e) {
|
||||
document.querySelector('#portfolioDescription').value = portfolioEditor.getData();
|
||||
});
|
||||
})
|
||||
.catch(error => { console.error(error); });
|
||||
</script>
|
||||
}
|
||||
58
Sky_Art_shop/Views/AdminPortfolio/Projects.cshtml
Normal file
58
Sky_Art_shop/Views/AdminPortfolio/Projects.cshtml
Normal file
@@ -0,0 +1,58 @@
|
||||
@model List<SkyArtShop.Models.PortfolioProject>
|
||||
@{
|
||||
Layout = "~/Views/Shared/_AdminLayout.cshtml";
|
||||
ViewData["Title"] = "Portfolio Projects";
|
||||
var categories = ViewBag.Categories as List<SkyArtShop.Models.PortfolioCategory> ?? new();
|
||||
var selected = ViewBag.SelectedCategory as string;
|
||||
}
|
||||
<div class="card mb-3">
|
||||
<div class="card-body d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Projects</h5>
|
||||
<a class="btn btn-primary" href="/admin/portfolio/project/create">Create Project</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="get" class="row g-2 mb-3">
|
||||
<div class="col-auto">
|
||||
<select name="categoryId" class="form-select" onchange="this.form.submit()">
|
||||
<option value="">All Categories</option>
|
||||
@foreach (var c in categories)
|
||||
{
|
||||
<option value="@c.Id" selected="@(selected == c.Id ? "selected" : null)">@c.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a class="btn btn-secondary" href="/admin/portfolio/projects">Reset</a>
|
||||
</div>
|
||||
</form>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Category</th>
|
||||
<th>Order</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var p in Model)
|
||||
{
|
||||
var catName = categories.FirstOrDefault(c => c.Id == p.CategoryId)?.Name ?? "-";
|
||||
<tr>
|
||||
<td>@p.Title</td>
|
||||
<td>@catName</td>
|
||||
<td>@p.DisplayOrder</td>
|
||||
<td>
|
||||
<a class="btn btn-sm btn-secondary" href="/admin/portfolio/project/edit/@p.Id">Edit</a>
|
||||
<form method="post" action="/admin/portfolio/project/delete/@p.Id" class="d-inline" onsubmit="return confirm('Delete this project?');">
|
||||
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user