An awesome software tool for interacting with the dean office for getting student's certificate!
Explore the docs »
View Demo
·
Report Bug
·
Request Feature
Table of Contents
This is a piece of software which is aimed to facilitate the interaction with the dean office in USV. It uses USV Google oAuth2 authentication which makes it even easier to use.
Admin user could do the managing for the dictionaries and basic settings:
Student would use the same auth screen and inside the app will see the list of already made requests and the button for creating a new one.
Dean office secretary will receive the certificate request and will follow up it according to the procedure.
This project is build as a Client-API application with a client developed using the VueJs 3 front-end framework and API developed by using .NET 6 C#. We used Entity Framework as main ORM and the chosen approach was Code First. This choice is dictated by the usability and maintainability of this approach. It creates uniform namings across the DB, there's no need to write down SQL code and maintain SQL code base.
The project is a solution that consists of 5 .NEt projects that corresponds to the architectural layers.
For this project we have chosen 3-tier / Layer architecure. It comprise of 3-tiers, Presentation Layer, Business Logic Layer, and Data Access Layer. Each Layer will have its own namespace, assembly and project (classes and folders)in the solution.
We considered this architecture becuase there are some benefits of N-tier Architecture:
- Reuse of the code
- Improve of the Maintainability
- Looser Coupling
In our project we have next sub-projects:
- USVStudDocs.DAL - Data access layer, where the entity configurations, migrations and EF Context is saved.
- USVStudDocs.Entities – a class project where the Entities are grouped. They are suffixed by "*Entity"
- USVStudDocs.Models - a class project where the Data Transfer Objects (DTO) are located
- USVStudDocs.BLL - Business logic layer, a class project where the services and other business logic (authentication, 3rd party API integration, validation) is stored.
- USVStudDocs.Web – API project, presentation layer. A set of Controllers with exposed API endpoints, where the routing, authentication guards are implemented. USVStudDocs.UI – UI project, VueJs. Is what the user could see.
Some notable libraries (NuGets) used in our application are:
- FluentValidation – for DTO validation
- RestSharp - for sending API requests during the validation of oAuth2 code
- Serilog and Serilog.Sinks.Console and Serilog.Sinks.Telegram – for collecting and sending logs to console, and warnings and up – to Telegram.
- JWt - for generating user-facing JWT
Business layer contains logic for Authentication, Custom Exceptions, Extension methods, Helpers, Mappes (between Entities and DTOs and vice-versa) and the most important – Services.
Validators are created based on FluentValidation.
Among services, there are CRUD Services for administration, for creating the certificate requeste, the sevices for use by dean office secretary. There is also AwsMinioClient service that is used for uploading files to MinIO
Some notable places in BLL are:
- use of
IHttpContextAccessorin BLL for getting the current user.
var subValue = _httpContextAccessor.HttpContext.User.FindFirst(JwtRegisteredClaimNames.Sub)?.Value;
bool parseRes = int.TryParse(subValue, out var userId);
if (!parseRes)
{
Log.ForContext<AuthorizationService>().Error("Cannot parse user Id from {SubValue}", subValue);
throw new ValidationException("User id is not an integer");
}
return userId;
}- JWT generation
var jwtSecretKey = Environment.GetEnvironmentVariable("JwtSecretKey") ?? _jwtSettings.SecretKey;
var tokenBuilder = new JwtBuilder()
.WithAlgorithm(new HMACSHA256Algorithm())
.WithSecret(jwtSecretKey)
.AddClaim(JwtRegisteredClaimNames.Exp,
DateTimeOffset.UtcNow.AddMinutes(_jwtSettings.ExpirationMinutes).ToUnixTimeSeconds())
.AddClaim(JwtRegisteredClaimNames.Iss, _jwtSettings.Issuer)
.AddClaim(JwtRegisteredClaimNames.Aud, _jwtSettings.Audience)
.AddClaim(JwtRegisteredClaimNames.Sub, userEntity.Id)
.AddClaim(JwtRegisteredClaimNames.UniqueName, userEntity.Username)
.AddClaim(JwtRegisteredClaimNames.Email, userEntity.Email)
.AddClaim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.AddMilliseconds(1).ToUnixTimeSeconds());
tokenBuilder.AddClaim("role", roles);
return tokenBuilder.Encode();-
Splitter of the Surname, Patornymic initials and name
var studentSurnameNamePatronymic = res.Result.SurnameNamePatronymic.Trim() .Replace("`", "'").Replace("'", "'") .Replace("’", "'").Replace(" ", " ") .Split(" "); List<string> surname = new List<string>(); List<string> name = new List<string>(); List<string> patronymic = new List<string>(); foreach (var studentString in studentSurnameNamePatronymic) { if (!studentString.Contains('.') && patronymic.Count == 0) { surname.Add(studentString); } else if (studentString.Contains('.') && patronymic.Count == 0) { patronymic.Add(studentString); } else if (!studentString.Contains('.') && patronymic.Count > 0) { name.Add(studentString); } }
- Authorizing e-mail service to use Gmail account
var clientSecrets = new ClientSecrets
{
ClientId = clientId,
ClientSecret = clientSecret
};
var token = new TokenResponse
{
AccessToken = accessToken,
RefreshToken = refreshToken
};
var credential = CreateUserCredential(clientSecrets, token, oAuthEmailSenderEmail.Value);
// Check if the access token has expired
if (credential.Token.IsExpired(credential.Flow.Clock))
{
// Refresh the access token
if (credential.RefreshTokenAsync(CancellationToken.None).Result)
{
accessToken = credential.Token.AccessToken;
oAuthEmailAccessToken.Value = accessToken;
_context.Settings.Update(oAuthEmailAccessToken);
refreshToken = credential.Token.RefreshToken;
oAuthEmailRefreshToken.Value = refreshToken;
_context.Settings.Update(oAuthEmailRefreshToken);
_context.SaveChanges();
}
else
{
throw new Exception("Failed to refresh access token.");
}
}
var service = new GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = credential
});For the project deployment we used Docker containers. In Docker_files you'll find configurations for the API and UI.
One of the simplest ways to run the app on the server is running by the means of Docker Swarm, using docker-composer.yml. As a server we used Ubuntu Server 22.04, and nginx as a reverse-proxy server. The following instruction will be for Ubuntu 22.04.
- Please install Docker engine.
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin- you may not like to use "sudo" every time you execute Docker commmands.
If you want to omit this, then execute the following:
sudo groupadd docker
sudo gpasswd -a $USER docker- Initialize the Swarm:
sudo docker swarm initMake a note of the command output, as it will contain the token needed to add other nodes to the Swarm.
-
Create a directory on your home directory or another which you prefer (e.g.
/home/usv-stud-docs) -
Copy the file
docker-compose.ymlto this directory on the server. -
Update the ENVs in the docker-compose.yml.
POSTGRES_PASSWORD- the desired PostresDB instance password.ASPNETCORE_ENVIRONMENT- to Development or ProductionDbConnectionString- according to this sample:Host=<host ip>;Port=15432;Database=usv-stud-docs;Username=usv-user;Password=<password>indicated inPOSTGRES_PASSWORDGoogleClientId- which you obtained by visiting https://console.cloud.google.com/apis/credentials and creating oAuth2 credentials (after you created the project)GoogleClientSecret- the secret from the previous placeGoogleRedirectUri- should correspond to the one indicated in Google Console, and should be according to this sample: https://<host_name>/auth/redirectGoogleEmailRedirectUri- this is used for authorizing for sending emails, should correspond to the one indicated in Google Console, and should be according to this sample: https://<host_name>/auth/redirectJwtSecretKey- a random string 16-32 chars length
-
Create new directory called
db, assign the ownership to the user that will run the app (e.g. usvdocs) and assign 777 access rights.
mkdir db
sudo chown usvdocs db
sudo chmod 777 db-
In real-world scenario you'd need to push the Docker image to your own private DockerHub, where the Docker images are stored. In our case, you could use the already stored public images, that are indicated in the
docker-compose.yml(this needs no changes). This would require to exeutedocker login -u {username}commane. -
You need to start your services by executing next command:
docker stack deploy --compose-file docker-compose.yml usv-docs --with-registry-auth- You may want to check the services status:
docker service ls- After the services were created, please connect to the DB (postgresql), don't forget to use the corresponding port from the docker-compose.yml and exeucte the file
USVStudDocs.DAL/MigrationSQL/migration.sql. - After this your app instance will be populated with the data structure and the initial data.
- You may want to set-up the load balancer, to serve the L7 network, e.g. nginx. You could find a sample Nginx set-up in Docker_files/nginx_lb/usvstud.conf which should be places in
/etc/nginx/sites-availableand then a symbolic link (ln -s) should be created to/etc/nginx/sites-enabled. - You may also want to install some LetsEncypt SSL cetificates in case no other options are available:
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginxThis is a description of how you should run up this project locally. To get a local copy up and running follow these simple example steps.
First of all, make sure you have installed NodeJs major version 16. You could download it from the official site or by using nvm. I highly recommend you to use Yarn as a package manager tool. Please install it by next command:
npm install --global yarnHere you could see the Swagger API documentation for the back-end end-points: View API documentaion
There are two projects in the Git repository – UI and API. You'll find the UI project into RemoteFinder.UI directory.
-
git clone https://github.com/adrianbarbe/RemoteFinder.git
- Install the NPM packages by executing
yarn install
- Run the project for development
yarn serve
To run the API project, you'll need to create a .env file in RemoteFinder.Web with the following keys:
ASPNETCORE_ENVIRONMENTwhich could beDevelopmentorProductionDbConnectionStringthe connection string to your PostgreSQL database engine installation.GoogleClientIdis the client id for the Google oAuth2 Credentials. For creating new credentials please access Google Developers ConsoleGoogleClientSecretclient secret generated in Google Developers ConsoleGoogleRedirectUriredirect URI which you indicated in the Google oAuth2 application settingsGoogleEmailRedirectUriredirect URI for email sending, which you indicated in the Google oAuth2 application settingsJwtSettings:SecretKeyandJwtSecretKey– a hash string for JWT tokens.
After creating the .env file you could start your project locally by running dotnet run command or by using the configuration profile for JetBrains Rider which is stored in the project repository.
In the project root you'll find a directory Docker_files that contains Docker definitions for projects and a docker-compose.yml file. You could use it for deploying the project into a Docker Swarm cluster. You could create it by installing Docker on the server and initialize a Swarm by doker swarm init command. Then, by placing the docker-compose.yml file in the desired directory, execute next command to initialize the Docker Swarm services:
docker stack deploy --compose-file docker-compose.yml usv-docs --with-registry-authDon't forget to update correspondingly
the environment variables in the docker-compose.yml file for be service.
Here you could see the usage examples for the student.
To add a new request for a certificate to your dean office just click the main button above the list of the request and you'll see this Add New Certificate request modal:

By default student will see the list of already made the requests. Stuent is able to add new request only then the previous one was solved:

- Add Basic functionality
- Add Multiple Faculties
- Add possibility to check the status of the certificate request
- Receive email when the status is changed
- Administration of the data from the UI
- Make mobile-ready web-version
- Multi-language Support
See the open issues for a full list of proposed features (and known issues).
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Distributed under the GNU GPL License. See LICENSE.txt for more information.
Adrian Barbe - @adryanbarbe - adryanbarbe@gmail.com
Project Link: https://github.com/adrianbarbe/usv-stud-docs







