Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# SolarWatch

SolarWatch is a React-based web application that allows users to track sunrise, sunset, solar noon, and day length for a given location and date. The project is built using Vite for fast development and includes features such as user authentication, protected routes, and an admin panel for managing data.

## Features

- **User Authentication**: Login and register functionality with JWT-based authentication.
- **Solar Data Tracking**: View sunrise, sunset, solar noon, and day length for a specific location and date.
- **Admin Panel**: Manage location and solar data (delete functionality included).
- **Responsive Design**: Styled with CSS for a clean and user-friendly interface.
- **Protected Routes**: Role-based access control for user and admin routes.
- **Docker Support**: Easily deployable using Docker.

## Project Structure

```
src/
├── App.jsx # Main application component
├── components/ # Reusable components
│ ├── AdminPanel/ # Admin panel components
│ ├── Footer/ # Footer component
│ ├── Home/ # Home page component
│ ├── Login/ # Login page component
│ ├── Navbar/ # Navigation bar component
│ ├── NotFound/ # 404 page component
│ ├── ProtectedRoute/ # Protected route logic
│ ├── Register/ # Registration page component
│ └── SolarWatch/ # Solar data tracking component
├── assets/ # Static assets (e.g., SVGs)
├── App.css # Global styles
├── main.jsx # Application entry point
└── index.css # Global CSS variables and styles
```

## Installation

## Docker Deployment

1. Use Docker Compose to build:
```powershell
docker compose build
```

2. Then run:
```powershell
docker compose up -d
```

## Scripts

- `npm run dev`: Start the development server.
- `npm run build`: Build the project for production.
- `npm run preview`: Preview the production build.
- `npm run lint`: Run ESLint to check for code issues.

## Technologies Used

- **Frontend**: React, React Router, Vite
- **Styling**: CSS
- **State Management**: React Hooks
- **Authentication**: JWT
- **Deployment**: Docker, Nginx

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

## Acknowledgments

- SVG icons are sourced from [svgrepo.com](https://www.svgrepo.com/).
- This is a project for me to showcase my full stack skills.
12 changes: 5 additions & 7 deletions SolarWatch/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static void Main(string[] args)

// for appSettings.json
var configuration = builder.Configuration; // this here in program.cs to ready out connection string
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings")); // this so it can be used anywhere NOT in program.cs
builder.Services.Configure<AppSettings>(configuration.GetSection("AppSettings")); // this so it can be used anywhere NOT in program.cs

// Add services to the container.
builder.Services.AddControllers();
Expand Down Expand Up @@ -101,19 +101,15 @@ public static void Main(string[] args)
};
});



if (!builder.Environment.IsEnvironment("Test"))
{
builder.Services.AddDbContext<SolarWatchApiContext>(options =>
{

var connectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING_DOCKER")
?? configuration["AppSettings:CONNECTION_STRING_DOCKER"];

options.UseNpgsql(connectionString);
});
}

});

builder.Services
.AddIdentityCore<IdentityUser>(options =>
Expand All @@ -136,6 +132,7 @@ public static void Main(string[] args)
// register appsettings
var config = scope.ServiceProvider.GetRequiredService<IOptions<AppSettings>>().Value;

/*
// do automatic migraions
var services = scope.ServiceProvider;
try
Expand All @@ -148,6 +145,7 @@ public static void Main(string[] args)
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while applying database migrations.");
}
*/

// run role seeder
var authenticationSeeder = scope.ServiceProvider.GetRequiredService<AuthenticationSeeder>();
Expand Down
38 changes: 37 additions & 1 deletion SolarWatch/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,37 @@
No starter code is provided. Start from scratch!
# SolarWatch

SolarWatch is a .NET 9.0 web application that provides solar data and geocoding information. The application includes authentication and authorization features using JWT tokens.

## Features

- User registration and login
- Fetch geocoding data for a given location
- Fetch solar data for a given location and date
- Integration tests for authentication and data fetching

## Technologies Used

- .NET 9.0
- ASP.NET Core
- Entity Framework Core
- In-memory database for testing
- JWT Authentication
- FluentAssertions for testing
- xUnit for unit testing

## Getting Started

### Prerequisites

- .NET 9.0 SDK
- Visual Studio 2022


## Project Structure

- `SolarWatch/`: The main application project.
- `SolarWatchTests/`: The test project containing integration tests.

## Configuration

The application uses an in-memory database for testing purposes. You can configure the database and other settings in the `appsettings.json` file.
6 changes: 4 additions & 2 deletions SolarWatch/Services/Authentication/AuthenticationSeeder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ public class AuthenticationSeeder

private RoleManager<IdentityRole> roleManager;
private UserManager<IdentityUser> userManager;
private readonly IConfiguration iConfiguration;

public AuthenticationSeeder(RoleManager<IdentityRole> roleManager, UserManager<IdentityUser> userManager)
public AuthenticationSeeder(RoleManager<IdentityRole> roleManager, UserManager<IdentityUser> userManager, IConfiguration iConfiguration)
{
this.roleManager = roleManager;
this.userManager = userManager;
this.iConfiguration = iConfiguration;
}

public void AddRoles()
Expand Down Expand Up @@ -45,7 +47,7 @@ private async Task CreateAdminIfNotExists()
if (adminInDb == null)
{
var admin = new IdentityUser { UserName = "admin", Email = "admin@admin.com" };
var adminCreated = await userManager.CreateAsync(admin, "admin123!420");
var adminCreated = await userManager.CreateAsync(admin, iConfiguration["AppSettings:ADMIN_PASSWORD"]);

if (adminCreated.Succeeded)
{
Expand Down
14 changes: 7 additions & 7 deletions SolarWatch/SolarWatch.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>65b42dee-4409-492b-9f67-eb2d386eaf15</UserSecretsId>
Expand All @@ -10,17 +10,17 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.13">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.11" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.13" />

</ItemGroup>

Expand Down
4 changes: 1 addition & 3 deletions SolarWatch/SolarWatch.sln.DotSettings.user
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=37a66527_002D53cb_002D4d9d_002Da6cd_002D814193180dff/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="TestForLocations" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
&lt;TestAncestor&gt;&#xD;
&lt;TestId&gt;NUnit3x::799DCF59-930B-4FF3-BF8D-EDF88B1AAEB7::net9.0::SolarWatchTests.SolarWatchControllerTests&lt;/TestId&gt;&#xD;
&lt;TestId&gt;xUnit::799DCF59-930B-4FF3-BF8D-EDF88B1AAEB7::net9.0::SolarWatchTests.IntegrationTests&lt;/TestId&gt;&#xD;
&lt;TestId&gt;xUnit::799DCF59-930B-4FF3-BF8D-EDF88B1AAEB7::net9.0::SolarWatchTests.IntegrationTestForAuth&lt;/TestId&gt;&#xD;
&lt;TestId&gt;xUnit::799DCF59-930B-4FF3-BF8D-EDF88B1AAEB7::net8.0::SolarWatchTests.IntegrationTests&lt;/TestId&gt;&#xD;
&lt;/TestAncestor&gt;&#xD;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>
3 changes: 2 additions & 1 deletion SolarWatch/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"AppSettings": {
"MyVariable": "Hello, World!",
"CONNECTION_STRING_DOCKER": "Server=localhost;Port=29777;Password=123456;User ID=admin;Database=solar_watch",
"OpenWeatherMapApiKey": "592d954883ee58cc9e8d109051b444b5"
"OpenWeatherMapApiKey": "123456",
"ADMIN_PASSWORD": "admin123"
},
"AllowedHosts": "*"
}
4 changes: 2 additions & 2 deletions SolarWatchTests/IntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ public IntegrationTestForAuth()

var webAppFactory = factory.WithWebHostBuilder(builder =>
{
builder.UseEnvironment("Test");
builder.ConfigureServices(services =>
{
/*
// Remove all DbContextOptions registrations
var descriptors = services.Where(d => d.ServiceType == typeof(DbContextOptions<SolarWatchApiContext>)).ToList();
foreach (var descriptor in descriptors)
{
services.Remove(descriptor);
}

*/
// Remove any existing DbContext registration
var dbContextDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(SolarWatchApiContext));
if (dbContextDescriptor != null)
Expand Down
8 changes: 5 additions & 3 deletions SolarWatchTests/SolarWatchTests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand All @@ -12,14 +12,15 @@
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="FluentAssertions" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.13" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>

<ItemGroup>
Expand All @@ -28,6 +29,7 @@

<ItemGroup>
<Using Include="NUnit.Framework" />
<Using Include="Xunit" />
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions SolarWatchTests/UnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using SolarWatch.Models;
using SolarWatch.Repositories;
using SolarWatch.Services;
using Assert = NUnit.Framework.Assert;

namespace SolarWatchTests
{
Expand Down
30 changes: 19 additions & 11 deletions SolarWatch_Frontend/index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SolarWatch</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="MyWebSite" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SolarWatch</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>

</html>
Binary file added SolarWatch_Frontend/public/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added SolarWatch_Frontend/public/favicon-96x96.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added SolarWatch_Frontend/public/favicon.ico
Binary file not shown.
1 change: 1 addition & 0 deletions SolarWatch_Frontend/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions SolarWatch_Frontend/public/site.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "MyWebSite",
"short_name": "MySite",
"icons": [
{
"src": "/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}
1 change: 0 additions & 1 deletion SolarWatch_Frontend/public/vite.svg

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions SolarWatch_Frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import NotFound from './components/NotFound/NotFound'
import Navbar from './components/Navbar/Navbar'
import Footer from './components/Footer/Footer'
import AdminPanel from './components/AdminPanel/AdminPanel'
import Tos from './components/Tos/Tos'

function App() {

Expand All @@ -22,6 +23,7 @@ function App() {
<div className="main-content">
<Routes>
<Route path='/' element={<Home />} />
<Route path='/tos' element={<Tos />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/unauthorized" element={<Unauthorized />} />
Expand Down
2 changes: 2 additions & 0 deletions SolarWatch_Frontend/src/assets/noon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading