Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc45c35dbe | ||
|
|
202dc16a52 | ||
|
|
cf37f33a1e |
@@ -360,19 +360,4 @@ MigrationBackup/
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# User-uploaded content (keep folder structure via .gitkeep, ignore actual files)
|
||||
Content/uploads/affiliate-logos/*
|
||||
!Content/uploads/affiliate-logos/.gitkeep
|
||||
Content/uploads/projects/*
|
||||
!Content/uploads/projects/.gitkeep
|
||||
Content/uploads/projects-media/*
|
||||
!Content/uploads/projects-media/.gitkeep
|
||||
Content/uploads/wallpaper/*
|
||||
!Content/uploads/wallpaper/.gitkeep
|
||||
App_Data/Uploads/*
|
||||
!App_Data/Uploads/.gitkeep
|
||||
|
||||
# Old backup folder, not needed
|
||||
bin-bckp/
|
||||
FodyWeavers.xsd
|
||||
|
After Width: | Height: | Size: 372 KiB |
@@ -115,7 +115,7 @@ namespace atakanozbancom.App_GlobalResources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Affiliate Links.
|
||||
/// Looks up a localized string similar to Support my work 💙.
|
||||
/// </summary>
|
||||
public static string AffiliateLinksTitle {
|
||||
get {
|
||||
@@ -250,7 +250,7 @@ namespace atakanozbancom.App_GlobalResources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Affiliate Links.
|
||||
/// Looks up a localized string similar to Support my work 💙.
|
||||
/// </summary>
|
||||
public static string NavAffiliateLinks {
|
||||
get {
|
||||
|
||||
@@ -151,10 +151,10 @@
|
||||
<value>My Projects</value>
|
||||
</data>
|
||||
<data name="NavAffiliateLinks" xml:space="preserve">
|
||||
<value>Affiliate Links</value>
|
||||
<value>Support my work 💙</value>
|
||||
</data>
|
||||
<data name="AffiliateLinksTitle" xml:space="preserve">
|
||||
<value>Affiliate Links</value>
|
||||
<value>Support my work 💙</value>
|
||||
</data>
|
||||
<data name="AffiliateLinksIntro" xml:space="preserve">
|
||||
<value>If you'd like to support my work, you can use the links below. It costs money for me to run and maintain these services, and using them may earn me a small benefit at no extra cost to you.</value>
|
||||
|
||||
@@ -151,10 +151,10 @@
|
||||
<value>Projelerim</value>
|
||||
</data>
|
||||
<data name="NavAffiliateLinks" xml:space="preserve">
|
||||
<value>Referans Linklerim</value>
|
||||
<value>Çalışmamı Destekleyin</value>
|
||||
</data>
|
||||
<data name="AffiliateLinksTitle" xml:space="preserve">
|
||||
<value>Affiliate Linklerim</value>
|
||||
<value>Çalışmamı Destekleyin</value>
|
||||
</data>
|
||||
<data name="AffiliateLinksIntro" xml:space="preserve">
|
||||
<value>Çalışmalarımı desteklemek isterseniz aşağıdaki linkleri kullanabilirsiniz. Bu servisleri çalıştırmak ve sürdürmek benim için maliyetli, bu linkleri kullanmanız size ekstra bir maliyet çıkarmadan bana küçük bir fayda sağlayabilir.</value>
|
||||
|
||||
@@ -13,6 +13,14 @@ namespace atakanozbancom
|
||||
{
|
||||
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
|
||||
|
||||
// Keep old /affiliate URLs working
|
||||
routes.MapRoute(
|
||||
name: "AffiliateRedirect",
|
||||
url: "affiliate/{action}/{id}",
|
||||
defaults: new { controller = "support", action = "Index", id = UrlParameter.Optional },
|
||||
new[] { "atakanozbancom.Controllers" }
|
||||
);
|
||||
|
||||
routes.MapRoute(
|
||||
name: "Default",
|
||||
url: "{controller}/{action}/{id}",
|
||||
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 854 B |
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 3.5 MiB |
|
After Width: | Height: | Size: 986 KiB |
|
After Width: | Height: | Size: 722 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 526 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 544 KiB |
|
After Width: | Height: | Size: 372 KiB |
|
After Width: | Height: | Size: 5.5 MiB |
@@ -62,6 +62,7 @@ namespace atakanozbancom.Controllers
|
||||
iframe = p.iframe
|
||||
};
|
||||
|
||||
// NEW MEDIA TABLE
|
||||
if (mediaLookup.TryGetValue(p.id, out var list))
|
||||
{
|
||||
vm.medias = list.Select(m => new ProjectMediaVM
|
||||
@@ -76,6 +77,7 @@ namespace atakanozbancom.Controllers
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
// fallback legacy
|
||||
if (vm.medias == null || vm.medias.Count == 0)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(p.iframe))
|
||||
|
||||
@@ -10,10 +10,11 @@ using atakanozbancom.Models.classes;
|
||||
|
||||
namespace atakanozbancom.Controllers
|
||||
{
|
||||
public class AffiliateController : Controller
|
||||
public class SupportController : Controller
|
||||
{
|
||||
private readonly Context db = new Context();
|
||||
|
||||
// GET: /Support
|
||||
[HttpGet]
|
||||
public ActionResult Index()
|
||||
{
|
||||
@@ -56,6 +57,7 @@ namespace atakanozbancom.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// POST: /Support/ChangeLanguage
|
||||
[HttpPost]
|
||||
public ActionResult ChangeLanguage(string lang)
|
||||
{
|
||||
@@ -6,6 +6,8 @@ using System.Web.Helpers;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Optimization;
|
||||
using System.Web.Routing;
|
||||
using System.Diagnostics;
|
||||
using System.Web.Configuration;
|
||||
|
||||
namespace atakanozbancom
|
||||
{
|
||||
@@ -13,10 +15,20 @@ namespace atakanozbancom
|
||||
{
|
||||
protected void Application_Start()
|
||||
{
|
||||
var mk = WebConfigurationManager.GetSection("system.web/machineKey")
|
||||
as MachineKeySection;
|
||||
|
||||
Debug.WriteLine(
|
||||
$"[MK] validation={mk.Validation}, decryption={mk.Decryption}, " +
|
||||
$"validationKeyLen={mk.ValidationKey?.Length}, decryptionKeyLen={mk.DecryptionKey?.Length}"
|
||||
);
|
||||
|
||||
// your existing stuff...
|
||||
AreaRegistration.RegisterAllAreas();
|
||||
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
|
||||
RouteConfig.RegisterRoutes(RouteTable.Routes);
|
||||
BundleConfig.RegisterBundles(BundleTable.Bundles);
|
||||
|
||||
}
|
||||
|
||||
protected void Application_BeginRequest()
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Atakan Doğan Özban
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -32,8 +32,10 @@ namespace atakanozbancom.Models.classes
|
||||
[Display(Name = "Sort Order")]
|
||||
public int sort_order { get; set; }
|
||||
|
||||
// Path of the already-uploaded logo (used when editing)
|
||||
public string existingImage { get; set; }
|
||||
|
||||
// New logo file (required on create, optional on edit to replace it)
|
||||
public HttpPostedFileBase logoFile { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ namespace atakanozbancom.Models.classes
|
||||
public string DescTR { get; set; }
|
||||
public string DescEN { get; set; }
|
||||
|
||||
// NEW:
|
||||
public List<projectmedia> medias { get; set; } = new List<projectmedia>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,17 @@ using System.Web;
|
||||
|
||||
namespace atakanozbancom.Models.classes
|
||||
{
|
||||
/// <summary>
|
||||
/// Renders user-entered project/affiliate descriptions as safe HTML.
|
||||
/// The whole input is HTML-encoded first (so no raw tags/scripts can ever
|
||||
/// survive), then a very small Markdown-style link syntax is turned into
|
||||
/// real anchor tags: [Link text](https://example.com)
|
||||
/// Only http:// and https:// URLs are accepted; anything else is left as
|
||||
/// plain (encoded) text.
|
||||
/// </summary>
|
||||
public static class DescriptionFormatter
|
||||
{
|
||||
// [label](https://url) - label can't contain "]", url must be http(s) and can't contain whitespace or ")"
|
||||
private static readonly Regex LinkPattern = new Regex(
|
||||
@"\[([^\]\r\n]{1,200})\]\((https?://[^\s)]{1,2000})\)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
@@ -14,8 +23,12 @@ namespace atakanozbancom.Models.classes
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
return string.Empty;
|
||||
|
||||
// 1) Neutralize everything (script/tags/attributes) up front.
|
||||
var encoded = HttpUtility.HtmlEncode(raw);
|
||||
|
||||
// 2) Re-introduce ONLY safe <a> links for the [text](url) syntax.
|
||||
// Both groups are already HTML-encoded, so this can't break out
|
||||
// of the href attribute or inject new tags/attributes.
|
||||
var withLinks = LinkPattern.Replace(encoded, m =>
|
||||
{
|
||||
var label = m.Groups[1].Value;
|
||||
@@ -23,6 +36,7 @@ namespace atakanozbancom.Models.classes
|
||||
return $"<a href=\"{url}\" class=\"text-secondary text-decoration-none\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">{label}</a>";
|
||||
});
|
||||
|
||||
// 3) Preserve line breaks the admin typed in the textarea.
|
||||
var withBreaks = withLinks
|
||||
.Replace("\r\n", "\n")
|
||||
.Replace("\n", "<br />");
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace atakanozbancom.Models.classes
|
||||
public string image { get; set; }
|
||||
public string iframe { get; set; }
|
||||
|
||||
// NEW
|
||||
public List<ProjectMediaVM> medias { get; set; } = new List<ProjectMediaVM>();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace atakanozbancom.Models.classes
|
||||
[Key]
|
||||
public int id { get; set; }
|
||||
|
||||
// Public URL of the current wallpaper. Null/empty falls back to the bundled default.
|
||||
[StringLength(500)]
|
||||
[Column("wallpaper")]
|
||||
public string wallpaper { get; set; }
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace atakanozbancom.Models.classes
|
||||
[Key]
|
||||
public int id { get; set; }
|
||||
|
||||
// Font Awesome class(es), e.g. "fa-brands fa-youtube"
|
||||
[Required, StringLength(100)]
|
||||
[Column("icon")]
|
||||
public string icon { get; set; }
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
# atakanozban.com
|
||||
|
||||
The source code for my personal portfolio website: an ASP.NET MVC 5 site with a small
|
||||
custom admin panel for managing projects, affiliate links, social icons and the site
|
||||
wallpaper, plus English/Turkish localization.
|
||||
|
||||
## Features
|
||||
|
||||
- Public site: home, about, my projects (with an image/iframe carousel per project),
|
||||
affiliate links, dynamic social icons in the footer, and a changeable background wallpaper.
|
||||
- Admin panel (`/admin`) protected by Forms Authentication:
|
||||
- My Projects (bilingual posts + media carousel)
|
||||
- Affiliate Links (bilingual cards with logos)
|
||||
- Social Icons (footer icons)
|
||||
- Wallpaper (site-wide background image)
|
||||
- Optional TOTP two-factor authentication (Security page)
|
||||
- My Projects: create/edit posts with EN/TR translations and a media manager
|
||||
(upload images or embed iframes, drag-free reordering via a sort order field).
|
||||
- Affiliate Links: create/edit links with a logo, title, description and EN/TR translations.
|
||||
- Social Icons: manage the icon row shown in the site footer (Font Awesome classes + URLs).
|
||||
- Wallpaper: upload a new background image for the whole site, or reset to the default.
|
||||
- Security: optional TOTP two-factor authentication (Google Authenticator / Authy / etc.).
|
||||
- Image uploads everywhere support both the regular file picker **and** pasting an
|
||||
image straight from the clipboard (Ctrl+V).
|
||||
- Public my-projects images open in a larger lightbox when clicked.
|
||||
- English / Turkish localization via `.resx` resource files and a culture cookie.
|
||||
|
||||
## Tech stack
|
||||
|
||||
- ASP.NET MVC 5 / .NET Framework 4.7.2
|
||||
- Entity Framework 6 (Code First, but with automatic migrations disabled — see
|
||||
[Database setup](#database-setup) below)
|
||||
- Microsoft SQL Server
|
||||
- Bootstrap 5.3.3 and Font Awesome 6 (loaded from CDN)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Windows with [Visual Studio 2019+](https://visualstudio.microsoft.com/) (the ".NET desktop
|
||||
development" / "ASP.NET and web development" workload) or MSBuild + IIS/IIS Express
|
||||
- SQL Server (LocalDB, SQL Server Express, or a full instance)
|
||||
- (Optional) An FTP/hosting environment if you want to deploy it somewhere other than IIS Express
|
||||
|
||||
## Getting started
|
||||
|
||||
### 1. Clone and restore packages
|
||||
|
||||
```bash
|
||||
git clone <this-repo-url>
|
||||
cd atakanozbancom
|
||||
```
|
||||
|
||||
Open `atakanozbancom.sln` in Visual Studio. NuGet packages will restore automatically on
|
||||
build (or run `nuget restore atakanozbancom.sln` from a Developer Command Prompt).
|
||||
|
||||
### 2. Database setup
|
||||
|
||||
This project uses EF6 Code First **with `Database.SetInitializer<Context>(null)`**, meaning
|
||||
Entity Framework will never create or alter tables for you — the schema is managed by hand.
|
||||
|
||||
1. Create an empty database (SQL Server Management Studio, Azure Data Studio, or `sqlcmd`):
|
||||
|
||||
```sql
|
||||
CREATE DATABASE atakanozbancomdb;
|
||||
```
|
||||
|
||||
2. Run [`database/schema.sql`](database/schema.sql) against that database. It creates every
|
||||
table the app needs (admins, projects, affiliate links, social icons, site settings) and
|
||||
inserts one seed admin account (`admin` / `change-this-password`).
|
||||
|
||||
3. **Change that seed password** (see [Security notes](#security-notes) — logins currently
|
||||
compare passwords as plain text, so don't reuse a real password here).
|
||||
|
||||
### 3. Configure `Web.config`
|
||||
|
||||
Open `Web.config` and update the `connectionStrings` section to point at your SQL Server
|
||||
instance:
|
||||
|
||||
```xml
|
||||
<connectionStrings>
|
||||
<add name="Context" connectionString="Data Source=YOUR_SERVER\INSTANCE;Initial Catalog=atakanozbancomdb;Integrated Security=true;" providerName="System.Data.SqlClient" />
|
||||
</connectionStrings>
|
||||
```
|
||||
|
||||
Then generate your own `<machineKey />` (see the comment above `<customErrors>` in
|
||||
`Web.config` for a couple of ways to do this) and paste it into `<system.web>`. This keeps
|
||||
authentication cookies and ViewState valid across app restarts/deployments — don't skip it,
|
||||
and don't reuse a sample key you find online.
|
||||
|
||||
### 4. Run it
|
||||
|
||||
Press F5 in Visual Studio (or `Ctrl+F5` to skip the debugger). The site starts at the URL
|
||||
shown in the IIS Express system tray icon / Visual Studio's toolbar. Log in at `/login`
|
||||
with the admin account you created above, then manage content from `/admin`.
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
Controllers/ MVC controllers (public site + admin panel)
|
||||
Models/classes/ Entity Framework models, view models, helpers
|
||||
Views/ Razor views, organized by controller
|
||||
App_GlobalResources/ English (.resx) and Turkish (.tr-TR.resx) translation strings
|
||||
App_Start/ Routing, bundling and filter configuration
|
||||
Content/uploads/ User-uploaded images (logos, project media, wallpaper) — gitignored
|
||||
database/schema.sql Full database schema + seed admin account
|
||||
web_atakanozbancom/ Static assets (CSS/JS/images) for the public site
|
||||
```
|
||||
|
||||
## Uploaded content
|
||||
|
||||
Anything uploaded through the admin panel (project media, affiliate link logos, the
|
||||
wallpaper) is written to folders under `Content/uploads/`. Those folders are tracked with
|
||||
a `.gitkeep` placeholder but their contents are gitignored, since uploaded files are
|
||||
runtime data, not source code.
|
||||
|
||||
## Security notes
|
||||
|
||||
This project was open-sourced as a personal portfolio/reference, not as a hardened
|
||||
multi-tenant product. A few things worth knowing if you deploy your own copy or build on it:
|
||||
|
||||
- Optional **TOTP 2FA** and **password change** live under Admin → username menu → User Settings.
|
||||
Existing installs can run `database/add-2fa.sql` then `database/harden-auth.sql`.
|
||||
- Passwords are stored with **PBKDF2-SHA256** (`PasswordHasher`). On first successful login after
|
||||
upgrade, any leftover plaintext password is rehashed automatically.
|
||||
- **Passwords used to be stored in plain text.** If you fork an old clone, force a password change
|
||||
and rotate machine keys before going live.
|
||||
- The admin panel already includes `[Authorize]` on every management action and
|
||||
`[ValidateAntiForgeryToken]` on every state-changing POST, so it's protected against CSRF
|
||||
once you're logged in — but access is still gated by a single shared admin login rather
|
||||
than per-user accounts/roles.
|
||||
- File uploads are restricted to an image extension allowlist (`.jpg`, `.jpeg`, `.png`,
|
||||
`.webp`, `.gif`) — SVG is intentionally excluded since it can carry executable script.
|
||||
- User-entered descriptions are HTML-encoded before rendering; a small `[text](https://...)`
|
||||
markdown-style syntax is the only way to add links, via `DescriptionFormatter`.
|
||||
|
||||
## License
|
||||
|
||||
MIT — see [LICENSE](LICENSE).
|
||||
@@ -199,7 +199,7 @@
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="main" asp-action="ChangeLanguage" asp-route-lang="en" href="@Url.Action("index","main")">@atakanozbancom.App_GlobalResources.Resource.NavHome</a>
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="main" asp-action="ChangeLanguage" asp-route-lang="en" aria-current="page" href="@Url.Action("index","about")">@atakanozbancom.App_GlobalResources.Resource.NavAboutMe</a>
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="main" asp-action="ChangeLanguage" asp-route-lang="en" href="@Url.Action("index","myprojects")">@atakanozbancom.App_GlobalResources.Resource.NavMyProjects</a>
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="main" asp-action="ChangeLanguage" asp-route-lang="en" href="@Url.Action("index","affiliate")">@atakanozbancom.App_GlobalResources.Resource.NavAffiliateLinks</a> @{
|
||||
<a class="nav-link fw-bold py-1 px-0" asp-controller="main" asp-action="ChangeLanguage" asp-route-lang="en" href="@Url.Action("index","support")">@atakanozbancom.App_GlobalResources.Resource.NavAffiliateLinks</a> @{
|
||||
var currentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
|
||||
var back = Request.RawUrl;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
|
||||
<system.web>
|
||||
<httpRuntime maxRequestLength="25600" executionTimeout="600" />
|
||||
<!-- Generate your own keys for production. Never commit real keys to a public repo. -->
|
||||
<machineKey validationKey="REPLACE_WITH_YOUR_VALIDATION_KEY"
|
||||
decryptionKey="REPLACE_WITH_YOUR_DECRYPTION_KEY"
|
||||
<machineKey validationKey="317412946B993448C673500FE9A36F856519ABFC991E4476091AE530AA2DCE25BDFCF3E9251279FA95F898D84AAD6656B68127FC8EA62B1487EBD07D1B4A5653"
|
||||
decryptionKey="33D1829CBAA044E19E39B70253390BB66DBCB59FCC8946F3EA122F87A289EE85"
|
||||
validation="HMACSHA256"
|
||||
decryption="AES" />
|
||||
<customErrors mode="RemoteOnly" />
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<Compile Include="App_Start\RouteConfig.cs" />
|
||||
<Compile Include="Controllers\aboutController.cs" />
|
||||
<Compile Include="Controllers\adminController.cs" />
|
||||
<Compile Include="Controllers\affiliateController.cs" />
|
||||
<Compile Include="Controllers\supportController.cs" />
|
||||
<Compile Include="Controllers\loginController.cs" />
|
||||
<Compile Include="Controllers\mainController.cs" />
|
||||
<Compile Include="Controllers\languageController.cs" />
|
||||
@@ -264,7 +264,7 @@
|
||||
<Content Include="Views\login\verify.cshtml" />
|
||||
<Content Include="Views\Shared\_PortfolioLayout.cshtml" />
|
||||
<Content Include="Views\about\index.cshtml" />
|
||||
<Content Include="Views\affiliate\index.cshtml" />
|
||||
<Content Include="Views\support\index.cshtml" />
|
||||
<Content Include="Views\main\index.cshtml" />
|
||||
<Content Include="Views\main\socialicons.cshtml" />
|
||||
<Content Include="Views\main\wallpaper.cshtml" />
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
-- atakanozban.com - full database schema
|
||||
-- Target: Microsoft SQL Server
|
||||
--
|
||||
-- This project uses EF6 Code First with Database.SetInitializer(null), which means
|
||||
-- Entity Framework will NOT create or migrate the database automatically. Run this
|
||||
-- script once against a fresh database to create every table the application needs.
|
||||
--
|
||||
-- Usage:
|
||||
-- 1. Create an empty database, e.g.:
|
||||
-- CREATE DATABASE atakanozbancomdb;
|
||||
-- 2. Run this whole script against that database (SSMS, Azure Data Studio, sqlcmd, etc.)
|
||||
|
||||
USE atakanozbancomdb;
|
||||
GO
|
||||
|
||||
-- =========================================================
|
||||
-- Admin users (used for the /login and /admin panel)
|
||||
-- =========================================================
|
||||
CREATE TABLE dbo.admins (
|
||||
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
||||
username NVARCHAR(100) NOT NULL,
|
||||
password NVARCHAR(255) NOT NULL,
|
||||
two_factor_enabled BIT NOT NULL CONSTRAINT DF_admins_two_factor_enabled DEFAULT(0),
|
||||
two_factor_secret NVARCHAR(512) NULL,
|
||||
login_failed_count INT NOT NULL CONSTRAINT DF_admins_login_failed_count DEFAULT(0),
|
||||
login_lock_until DATETIME2 NULL,
|
||||
totp_failed_count INT NOT NULL CONSTRAINT DF_admins_totp_failed_count DEFAULT(0),
|
||||
totp_lock_until DATETIME2 NULL
|
||||
);
|
||||
GO
|
||||
|
||||
-- =========================================================
|
||||
-- My Projects
|
||||
-- =========================================================
|
||||
CREATE TABLE dbo.myprojects (
|
||||
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
||||
image NVARCHAR(MAX) NULL, -- legacy, kept for backward compatibility
|
||||
iframe NVARCHAR(MAX) NULL, -- legacy, kept for backward compatibility
|
||||
title NVARCHAR(255) NULL,
|
||||
description NVARCHAR(MAX) NULL
|
||||
);
|
||||
GO
|
||||
|
||||
CREATE TABLE dbo.myprojectstranslations (
|
||||
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
||||
project_id INT NOT NULL,
|
||||
culture NVARCHAR(10) NOT NULL,
|
||||
title NVARCHAR(255) NULL,
|
||||
description NVARCHAR(MAX) NULL,
|
||||
CONSTRAINT FK_myprojectstranslations_myprojects
|
||||
FOREIGN KEY (project_id) REFERENCES dbo.myprojects(id) ON DELETE CASCADE
|
||||
);
|
||||
GO
|
||||
|
||||
CREATE TABLE dbo.projectmedia (
|
||||
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
||||
projectid INT NOT NULL,
|
||||
mediatype INT NOT NULL, -- 1 = Image, 2 = Iframe
|
||||
mediaurl NVARCHAR(1000) NOT NULL,
|
||||
sortorder INT NOT NULL DEFAULT 0,
|
||||
CONSTRAINT FK_projectmedia_myprojects
|
||||
FOREIGN KEY (projectid) REFERENCES dbo.myprojects(id) ON DELETE CASCADE
|
||||
);
|
||||
GO
|
||||
|
||||
-- =========================================================
|
||||
-- Affiliate Links
|
||||
-- =========================================================
|
||||
CREATE TABLE dbo.affiliatelinks (
|
||||
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
||||
image NVARCHAR(500) NOT NULL,
|
||||
title NVARCHAR(200) NOT NULL,
|
||||
description NVARCHAR(500) NULL,
|
||||
url NVARCHAR(1000) NOT NULL,
|
||||
sortorder INT NOT NULL DEFAULT 0
|
||||
);
|
||||
GO
|
||||
|
||||
CREATE TABLE dbo.affiliatelinktranslations (
|
||||
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
||||
affiliatelink_id INT NOT NULL,
|
||||
culture NVARCHAR(10) NOT NULL,
|
||||
title NVARCHAR(255) NULL,
|
||||
description NVARCHAR(MAX) NULL,
|
||||
CONSTRAINT FK_affiliatelinktranslations_affiliatelinks
|
||||
FOREIGN KEY (affiliatelink_id) REFERENCES dbo.affiliatelinks(id) ON DELETE CASCADE
|
||||
);
|
||||
GO
|
||||
|
||||
-- =========================================================
|
||||
-- Social Icons (footer icon row)
|
||||
-- =========================================================
|
||||
CREATE TABLE dbo.socialicons (
|
||||
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
||||
icon NVARCHAR(100) NOT NULL, -- Font Awesome class(es), e.g. "fa-brands fa-github"
|
||||
url NVARCHAR(500) NOT NULL,
|
||||
sortorder INT NOT NULL DEFAULT 0
|
||||
);
|
||||
GO
|
||||
|
||||
-- =========================================================
|
||||
-- Site Settings (currently just the wallpaper; single row table)
|
||||
-- =========================================================
|
||||
CREATE TABLE dbo.sitesettings (
|
||||
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
||||
wallpaper NVARCHAR(500) NULL -- public URL of the current wallpaper; NULL = use bundled default
|
||||
);
|
||||
GO
|
||||
|
||||
-- =========================================================
|
||||
-- Seed data: one admin account so you can log in to /admin
|
||||
-- IMPORTANT: change the username/password below before/after running this,
|
||||
-- and see the "Security notes" section in README.md - passwords are currently
|
||||
-- stored and compared in plain text, so pick something you don't reuse elsewhere.
|
||||
-- =========================================================
|
||||
INSERT INTO dbo.admins (username, password) VALUES ('admin', 'change-this-password');
|
||||
GO
|
||||
@@ -79,6 +79,10 @@ body {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Affiliate links
|
||||
*/
|
||||
|
||||
.affiliate-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||