feature/client-template #2

Merged
homeburger merged 27 commits from feature/client-template into main 2026-03-14 23:10:46 -05:00
12 changed files with 172 additions and 61 deletions
Showing only changes of commit 9b6a4c75b9 - Show all commits

View File

@@ -17,4 +17,5 @@ COPY --from=build /app/publish ./
EXPOSE 5000 EXPOSE 5000
ENV ASPNETCORE_ENVIRONMENT="Production"
ENTRYPOINT ["dotnet", "agologum-api.dll"] ENTRYPOINT ["dotnet", "agologum-api.dll"]

View File

@@ -1,8 +1,13 @@
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using agologumApi.Stores;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSingleton<UserStore>();
// configuration for behind my nginx proxy // configuration for behind my nginx proxy
builder.Services.Configure<ForwardedHeadersOptions>(options => builder.Services.Configure<ForwardedHeadersOptions>(options =>
{ {
@@ -10,7 +15,7 @@ builder.Services.Configure<ForwardedHeadersOptions>(options =>
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto; ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear(); options.KnownIPNetworks.Clear();
options.KnownProxies.Clear(); options.KnownProxies.Clear();
}); });
@@ -18,42 +23,34 @@ builder.Services.Configure<ForwardedHeadersOptions>(options =>
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi(); builder.Services.AddOpenApi();
builder.Services.AddCors(options =>
{
options.AddPolicy("dev",
policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build(); var app = builder.Build();
app.UseForwardedHeaders(); app.UseForwardedHeaders();
app.UseCors("dev");
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsEnvironment("Development")) {
{
app.MapOpenApi(); app.MapOpenApi();
app.UseSwagger();
app.UseSwaggerUI();
} else {
app.UseHttpsRedirection();
} }
app.UseHttpsRedirection(); app.MapControllers();
// below is a placeholder endpoint
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapGet("api/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");
app.Run(); app.Run();
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

View File

@@ -1,23 +1,23 @@
{ {
"$schema": "https://json.schemastore.org/launchsettings.json", "$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": { "profiles": {
"http": { "http-dev": {
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": false, "launchBrowser": false,
"applicationUrl": "http://localhost:5227", "applicationUrl": "http://0.0.0.0:5227",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
}, },
"https": { "https-dev": {
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": false, "launchBrowser": false,
"applicationUrl": "https://localhost:7182;http://localhost:5227", "applicationUrl": "https://0.0.0.0:7182;http://0.0.0.0:5227",
"environmentVariables": { "environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development" "ASPNETCORE_ENVIRONMENT": "Development"
} }
} },
} }
} }

View File

@@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.3" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.5" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,4 +1,4 @@
@agologum_api_HostAddress = http://localhost:5227 @agologum_api_HostAddress = http://0.0.0.0:5227
GET {{agologum_api_HostAddress}}/weatherforecast/ GET {{agologum_api_HostAddress}}/weatherforecast/
Accept: application/json Accept: application/json

View File

@@ -0,0 +1,67 @@
using Microsoft.AspNetCore.Mvc;
using agologumApi.Models;
using agologumApi.Stores;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly UserStore store_;
public UsersController(UserStore store)
{
store_ = store;
}
[HttpGet]
public ActionResult<List<User>> getUsers()
{
return Ok(store_.getUsers());
}
[HttpGet("{id:int}")]
public ActionResult<User> getUser(int id)
{
var user = store_.getUser(id);
if (user == null)
return NotFound();
return Ok(user);
}
[HttpPost]
public ActionResult<User> createUser(User user)
{
var created = store_.createUser(user);
return CreatedAtAction(
nameof(getUser),
new { id = created.Id },
created
);
}
[HttpPut("{id}")]
public ActionResult<User> updateUser(int id, User user)
{
var updated = store_.updateUser(id, user);
if (updated == null)
return NotFound();
return Ok(updated);
}
[HttpDelete("{id}")]
public ActionResult deleteUser(int id)
{
var success = store_.deleteUser(id);
if (!success)
return NotFound();
return NoContent();
}
}

10
api/src/Models/User.cs Normal file
View File

@@ -0,0 +1,10 @@
namespace agologumApi.Models;
public class User {
public int Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
};

View File

@@ -0,0 +1,46 @@
// temporary state management
// this will eventually change into a service that uses EnitityFramework for database interactions
using agologumApi.Models;
namespace agologumApi.Stores;
public class UserStore {
private readonly List<User> users_ = new();
private int nextId_ = 1;
public List<User> getUsers() {
return users_;
}
public User? getUser(int id) {
return users_.FirstOrDefault(x => x.Id == id);
}
public User createUser(User user) {
user.Id = nextId_++;
users_.Add(user);
return user;
}
public User? updateUser(int id, User updated) {
var existing = users_.FirstOrDefault(x => x.Id == id);
if(existing == null) return null;
existing.Name = updated.Name;
existing.Email = updated.Email;
return existing;
}
public bool deleteUser(int id) {
var existing = users_.FirstOrDefault(x => x.Id == id);
if(existing == null) return false;
users_.Remove(existing);
return true;
}
}

View File

@@ -1,15 +0,0 @@
// this is a test program for making sure your dotnet environment is working properly
/*
using System.Diagnostics;
using System;
Stopwatch sw = new Stopwatch();
sw.Start();
Console.WriteLine("Hi mom !");
Console.WriteLine("doing a schmunguss");
sw.Stop();
Console.WriteLine("Time elapsed: {0}", sw.Elapsed);
*/

View File

@@ -7,14 +7,18 @@ import axios from "axios";
import type {AxiosResponse } from "axios"; import type {AxiosResponse } from "axios";
import type { User } from "../models/User.ts"; import type { User } from "../models/User.ts";
const API_URL: string = "/api/items"; const API_URL: string = "/users";
export const getUsers = () => axios.get<User[]>(API_URL); const api = axios.create({
baseURL: "http://10.145.164.106:5227/api"
});
export const getUser = (id: number) => axios.get<User>(`${API_URL}/${id}`); export const getUsers = () => api.get<User[]>(`${API_URL}`);
export const createUser = (data: User) => axios.post<User>(API_URL, data); export const getUser = (id: number) => api.get<User>(`${API_URL}/${id}`);
export const updateUser = (id: number, data: User) => axios.put<User>(`${API_URL}/${id}`, data); export const createUser = (data: User) => api.post<User>(`${API_URL}`, data);
export const deleteUser = (id: number) => axios.delete<User>(`${API_URL}/${id}`); export const updateUser = (id: number, data: User) => api.put<User>(`${API_URL}/${id}`, data);
export const deleteUser = (id: number) => api.delete<User>(`${API_URL}/${id}`);

View File

@@ -17,13 +17,13 @@ onMounted(() => { // register callback for when component is loaded on page
<div> <div>
<h1>Users</h1> <h1>Users</h1>
<router-link to="/users/new">Create User</router-link> <router-link to="/user/new">Create User</router-link>
<table> <table>
<tr v-for="user in store.users" :key="user.id"> <tr v-for="user in store.users" :key="user.id">
<td>{{ user.name }}</td> <td>{{ user.name }}</td>
<td> <td>
<router-link :to="`/users/${user.id}`">Edit</router-link> <router-link :to="`/user/${user.id}`">Edit</router-link>
<button @click="store.removeUser(user.id)">Delete</button> <button @click="store.removeUser(user.id)">Delete</button>
</td> </td>
</tr> </tr>

View File

@@ -16,13 +16,13 @@ onMounted(() => {
<div> <div>
<h1>Users</h1> <h1>Users</h1>
<router-link to="/users/new">Create User</router-link> <router-link to="/user/new">Create User</router-link>
<table> <table>
<tr v-for="user in store.users" :key="user.id"> <tr v-for="user in store.users" :key="user.id">
<td>{{ user.name }}</td> <td>{{ user.name }}</td>
<td> <td>
<router-link :to="`/users/${user.id}`">Edit</router-link> <router-link :to="`/user/${user.id}`">Edit</router-link>
<button @click="store.removeUser(user.id)">Delete</button> <button @click="store.removeUser(user.id)">Delete</button>
</td> </td>
</tr> </tr>