Compare commits
28 Commits
f9ec722f2e
...
59b5344377
| Author | SHA1 | Date | |
|---|---|---|---|
| 59b5344377 | |||
| d5aa15ccca | |||
| 4b38a5e597 | |||
| fef9af6e91 | |||
| 60215782c0 | |||
| ea0afc4e62 | |||
| 2011d1fb36 | |||
| 28f222ee28 | |||
| c8c0a46bb6 | |||
| dd8864c56c | |||
| 509b8b003c | |||
| 0da09d7594 | |||
| 58ff76fdc6 | |||
| 3c65a825db | |||
| 2a066b7209 | |||
| b1865afced | |||
| 9b6a4c75b9 | |||
| 728258465d | |||
| 242872cba0 | |||
| 31225b51b2 | |||
| 533571859f | |||
| 28eba3f0fd | |||
| 67598f60bf | |||
| fdac0859ea | |||
| c807412076 | |||
| 20ea31e63a | |||
| 2128d5dfa4 | |||
| 8db6ea4754 |
4
.env
Normal file
4
.env
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
sike you thought I was like that
|
||||
|
||||
hehehehee (urp so full)
|
||||
@@ -5,8 +5,8 @@ run-name: "${{ gitea.event.head_commit.message }}: Deploy API"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
#branches:
|
||||
# - main
|
||||
paths:
|
||||
- "api/**"
|
||||
- ".gitea/workflows/deploy-api.yaml"
|
||||
@@ -38,5 +38,6 @@ jobs:
|
||||
|
||||
- name: Deploy container
|
||||
run: |
|
||||
export POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}
|
||||
docker compose -f ./api/docker-compose.prod.yaml pull agologum-api
|
||||
docker compose -f ./api/docker-compose.prod.yaml up -d --force-recreate agologum-api
|
||||
|
||||
@@ -5,8 +5,8 @@ run-name: "${{ gitea.event.head_commit.message }}: Deploy Client"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
#branches:
|
||||
# - main
|
||||
paths:
|
||||
- "client/**"
|
||||
- ".gitea/workflows/deploy-client.yaml"
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,2 +1,7 @@
|
||||
|
||||
# empty for now
|
||||
|
||||
# .env # urrp I eated it :33
|
||||
|
||||
# this one is for real though
|
||||
client/.env
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## agologum
|
||||
A web server/web client template.
|
||||
Backend: a .NET web API with a [READACTED] database (probably sql for tinkering)
|
||||
Backend: a .NET web API with a [READACTED] database (probably prostgressql for tinkering)
|
||||
Frontend: Vue.js because I enjoy life and the splendor of God's creation
|
||||
|
||||
1
api/.gitignore
vendored
1
api/.gitignore
vendored
@@ -25,6 +25,7 @@ bld/
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
#[Mm]igrations/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
|
||||
@@ -17,4 +17,5 @@ COPY --from=build /app/publish ./
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
ENV ASPNETCORE_ENVIRONMENT="Production"
|
||||
ENTRYPOINT ["dotnet", "agologum-api.dll"]
|
||||
|
||||
49
api/Migrations/20260314152859_InitialCreate.Designer.cs
generated
Normal file
49
api/Migrations/20260314152859_InitialCreate.Designer.cs
generated
Normal file
@@ -0,0 +1,49 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace agologum_api.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
[Migration("20260314152859_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.5")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("agologumApi.Models.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
36
api/Migrations/20260314152859_InitialCreate.cs
Normal file
36
api/Migrations/20260314152859_InitialCreate.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace agologum_api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
Name = table.Column<string>(type: "text", nullable: false),
|
||||
Email = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
46
api/Migrations/AppDbContextModelSnapshot.cs
Normal file
46
api/Migrations/AppDbContextModelSnapshot.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace agologum_api.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDbContext))]
|
||||
partial class AppDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.5")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("agologumApi.Models.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,17 @@
|
||||
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using agologumApi.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDbContext<AppDbContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddScoped<UserService>();
|
||||
|
||||
// configuration for behind my nginx proxy
|
||||
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
{
|
||||
@@ -10,7 +19,7 @@ builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
ForwardedHeaders.XForwardedFor |
|
||||
ForwardedHeaders.XForwardedProto;
|
||||
|
||||
options.KnownNetworks.Clear();
|
||||
options.KnownIPNetworks.Clear();
|
||||
options.KnownProxies.Clear();
|
||||
});
|
||||
|
||||
@@ -18,42 +27,49 @@ builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
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();
|
||||
|
||||
app.UseForwardedHeaders();
|
||||
app.UseCors("dev");
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
if (app.Environment.IsEnvironment("Development")) {
|
||||
app.MapOpenApi();
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
} else {
|
||||
app.UseHttpsRedirection();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.MapControllers();
|
||||
|
||||
// attempt enitity-framework migrations at startup. love you stack overflow
|
||||
using (var scope = app.Services.CreateScope()) {
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
// 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");
|
||||
var retries = 10;
|
||||
while (retries-- > 0) {
|
||||
try {
|
||||
db.Database.Migrate();
|
||||
break;
|
||||
} catch {
|
||||
Thread.Sleep(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.Run();
|
||||
|
||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"http-dev": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://localhost:5227",
|
||||
"applicationUrl": "http://0.0.0.0:5227",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"https-dev": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "https://localhost:7182;http://localhost:5227",
|
||||
"applicationUrl": "https://0.0.0.0:7182;http://0.0.0.0:5227",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,17 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@agologum_api_HostAddress = http://localhost:5227
|
||||
@agologum_api_HostAddress = http://0.0.0.0:5227
|
||||
|
||||
GET {{agologum_api_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": "Host=agologum-net;Port=5432;Database=agologum;Username=agologum;Password=${POSTGRES_PASSWORD}"
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"https_port": 443
|
||||
}
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
agologum-api:
|
||||
image: git.vxbard.net/homeburger/agologum-api:latest
|
||||
container_name: agologum-api
|
||||
restart: always
|
||||
environment:
|
||||
ConnectionStrings__DefaultConnection: Host=agologum-db;Port=5432;Database=agologum;Username=agologum;Password=${POSTGRES_PASSWORD}
|
||||
ports:
|
||||
- "5000:5000"
|
||||
networks:
|
||||
- agologum-net
|
||||
|
||||
networks:
|
||||
agologum-net:
|
||||
external: true
|
||||
64
api/src/Controllers/UsersController.cs
Normal file
64
api/src/Controllers/UsersController.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using agologumApi.Models;
|
||||
using agologumApi.Services;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private readonly UserService service_;
|
||||
|
||||
public UsersController(UserService service)
|
||||
{
|
||||
service_ = service;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<List<User>>> getUsers()
|
||||
{
|
||||
return Ok(await service_.GetAll());
|
||||
}
|
||||
|
||||
[HttpGet("{id:int}")]
|
||||
public async Task<ActionResult<User>> getUser(int id)
|
||||
{
|
||||
var user = await service_.Get(id);
|
||||
|
||||
if (user == null) return NotFound();
|
||||
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<User>> createUser(User user)
|
||||
{
|
||||
var created = await service_.Create(user);
|
||||
|
||||
return CreatedAtAction(
|
||||
nameof(getUser),
|
||||
new { id = created.Id },
|
||||
created
|
||||
);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<ActionResult<User>> updateUser(int id, User user)
|
||||
{
|
||||
var updated = await service_.Update(user);
|
||||
|
||||
if (updated == null) return NotFound();
|
||||
|
||||
return Ok(updated);
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<ActionResult> deleteUser(int id)
|
||||
{
|
||||
var success = await service_.Delete(id);
|
||||
|
||||
if (!success) return NotFound();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
13
api/src/Data/AppDbContext.cs
Normal file
13
api/src/Data/AppDbContext.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
using agologumApi.Models;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class AppDbContext : DbContext {
|
||||
|
||||
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {
|
||||
|
||||
}
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
}
|
||||
10
api/src/Models/User.cs
Normal file
10
api/src/Models/User.cs
Normal 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; } = "";
|
||||
|
||||
};
|
||||
47
api/src/Services/UserService.cs
Normal file
47
api/src/Services/UserService.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using agologumApi.Models;
|
||||
|
||||
namespace agologumApi.Services;
|
||||
|
||||
public class UserService {
|
||||
|
||||
private readonly AppDbContext db_;
|
||||
|
||||
public UserService(AppDbContext db) {
|
||||
db_ = db;
|
||||
}
|
||||
|
||||
public async Task<List<User>> GetAll() {
|
||||
return await db_.Users.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<User?> Get(int id) {
|
||||
return await db_.Users.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<User> Create(User user) {
|
||||
db_.Users.Add(user);
|
||||
await db_.SaveChangesAsync();
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<User> Update(User user) {
|
||||
db_.Users.Update(user);
|
||||
await db_.SaveChangesAsync();
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task<bool> Delete(int id) {
|
||||
User? user = await db_.Users.FindAsync(id);
|
||||
if(user != null) {
|
||||
db_.Users.Remove(user);
|
||||
await db_.SaveChangesAsync();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
15
api/test.cs
15
api/test.cs
@@ -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);
|
||||
*/
|
||||
@@ -5,5 +5,5 @@ services:
|
||||
container_name: agologum-client
|
||||
restart: always
|
||||
ports:
|
||||
- "8080:80"
|
||||
- "7000:80"
|
||||
|
||||
341
client/package-lock.json
generated
341
client/package-lock.json
generated
@@ -8,6 +8,8 @@
|
||||
"name": "agologum",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.13.6",
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "^3.5.29",
|
||||
"vue-router": "^5.0.3"
|
||||
},
|
||||
@@ -1760,6 +1762,23 @@
|
||||
"url": "https://github.com/sponsors/sxzz"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.6",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
|
||||
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.11",
|
||||
"form-data": "^4.0.5",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
|
||||
@@ -1832,6 +1851,19 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001774",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz",
|
||||
@@ -1868,6 +1900,18 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/confbox": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz",
|
||||
@@ -2001,6 +2045,29 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.302",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
|
||||
@@ -2030,6 +2097,51 @@
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
|
||||
@@ -2111,6 +2223,42 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
|
||||
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@@ -2126,6 +2274,15 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@@ -2136,6 +2293,94 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/hookable": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||
@@ -2314,6 +2559,15 @@
|
||||
"url": "https://github.com/sponsors/sxzz"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/memorystream": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
|
||||
@@ -2323,6 +2577,27 @@
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mitt": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
||||
@@ -2529,6 +2804,66 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
|
||||
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^7.7.7"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.5.0",
|
||||
"vue": "^3.5.11"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/@vue/devtools-api": {
|
||||
"version": "7.7.9",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
|
||||
"integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-kit": "^7.7.9"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/@vue/devtools-kit": {
|
||||
"version": "7.7.9",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
|
||||
"integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-shared": "^7.7.9",
|
||||
"birpc": "^2.3.0",
|
||||
"hookable": "^5.5.3",
|
||||
"mitt": "^3.0.1",
|
||||
"perfect-debounce": "^1.0.0",
|
||||
"speakingurl": "^14.0.1",
|
||||
"superjson": "^2.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/@vue/devtools-shared": {
|
||||
"version": "7.7.9",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
|
||||
"integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"rfdc": "^1.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/perfect-debounce": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
|
||||
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pkg-types": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
|
||||
@@ -2568,6 +2903,12 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/quansync": {
|
||||
"version": "0.2.11",
|
||||
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
"type-check": "vue-tsc --build"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.13.6",
|
||||
"pinia": "^3.0.4",
|
||||
"vue": "^3.5.29",
|
||||
"vue-router": "^5.0.3"
|
||||
},
|
||||
|
||||
9
client/scripts/launch_dev.sh
Normal file
9
client/scripts/launch_dev.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
# this script builds and launches the vue client served raw and locally
|
||||
# (without docker) on localhost (default) or via the machine's ip given the argument -I
|
||||
|
||||
|
||||
# launch the app
|
||||
npm run dev
|
||||
|
||||
# TODO: more configuration
|
||||
@@ -1,11 +1,8 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import index from './pages/index.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>You did it!</h1>
|
||||
<p>
|
||||
Visit <a href="https://vuejs.org/" target="_blank" rel="noopener">vuejs.org</a> to read the
|
||||
documentation
|
||||
</p>
|
||||
<router-view /> <!-- Routed components appear here -->
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
|
||||
25
client/src/api/UsersApi.ts
Normal file
25
client/src/api/UsersApi.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
// services are kinda whatever, but in general its a good idea for all api calls to be within a service (at least thats how angular handles it)
|
||||
// this user service will handle all to <-> from the server when handling user objects
|
||||
// should be injected with the http client (I think its axios ?)
|
||||
|
||||
import axios from "axios";
|
||||
import type {AxiosResponse } from "axios";
|
||||
import type { User } from "../models/User.ts";
|
||||
|
||||
const API_URL: string = "/users";
|
||||
|
||||
const baseUrl: string = import.meta.env.DEV ? import.meta.env.VITE_DEV_API_URL : "https://app.vxbard.net/api" // TODO: overarching api service
|
||||
const api = axios.create({
|
||||
baseURL: baseUrl
|
||||
});
|
||||
|
||||
export const getUsers = () => api.get<User[]>(`${API_URL}`);
|
||||
|
||||
export const getUser = (id: number) => api.get<User>(`${API_URL}/${id}`);
|
||||
|
||||
export const createUser = (data: User) => api.post<User>(`${API_URL}`, data);
|
||||
|
||||
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}`);
|
||||
3
client/src/assets/mobus.txt
Normal file
3
client/src/assets/mobus.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
// assets will contain common public resources
|
||||
// icons, fonts (if needed locally), images, whatever
|
||||
13
client/src/components/Header.vue
Normal file
13
client/src/components/Header.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
<!-- you know what components are :D -->
|
||||
<!-- though I do miss angular's support for separating the file ( though i guess its technically possible in vue but it gets disorganized fast ) -->
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button @click="count++">You clicked me {{ count }} times.</button>
|
||||
</template>
|
||||
33
client/src/components/UsersTable.vue
Normal file
33
client/src/components/UsersTable.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { onMounted } from "vue";
|
||||
import { useUsersStore} from "../stores/UsersStore.ts";
|
||||
|
||||
const store = useUsersStore();
|
||||
|
||||
onMounted(() => { // register callback for when component is loaded on page
|
||||
store.fetchUsers();
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
|
||||
<router-link to="/user/new">Create User</router-link>
|
||||
|
||||
<table>
|
||||
<tr v-for="user in store.users" :key="user.id">
|
||||
<td>{{ user.name }}</td>
|
||||
<td>
|
||||
<router-link :to="`/user/${user.id}`">Edit</router-link>
|
||||
<button @click="store.removeUser(user.id)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
3
client/src/composables/what.ts
Normal file
3
client/src/composables/what.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
// idk really what composables are but I think its extra service code that can be used in components
|
||||
// I think they're useful for moving data from a data store to the component but I could just be trolling
|
||||
@@ -1,9 +1,11 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from "pinia"
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
8
client/src/models/User.ts
Normal file
8
client/src/models/User.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
// models are the data objects stored in the database. models defined here must match models defined in api/models
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string
|
||||
}
|
||||
54
client/src/pages/UserForm.vue
Normal file
54
client/src/pages/UserForm.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<!-- pages/views in vue are basically root-level full-page components -->
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
import { useUsersStore } from "../stores/UsersStore.ts";
|
||||
import type { User } from "../models/User.ts";
|
||||
|
||||
const store = useUsersStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const user = ref<User>({
|
||||
name: "",
|
||||
id: 0,
|
||||
email: "",
|
||||
});
|
||||
|
||||
const id: string | undefined = route.params.id as string | undefined
|
||||
|
||||
onMounted(() => {
|
||||
if(id) {
|
||||
const existing = store.users.find(i => i.id == Number(id));
|
||||
if (existing) user.value = { ...existing };
|
||||
}
|
||||
});
|
||||
|
||||
async function save(): Promise<void> {
|
||||
if(id) {
|
||||
await store.updateUser(Number(id), user.value);
|
||||
} else {
|
||||
await store.addUser(user.value);
|
||||
}
|
||||
|
||||
router.push("/users"); // redirect
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<div>
|
||||
<h2>{{ id ? "Edit User" : "Create User" }}</h2> <!-- omg I love ternary operator :D -->
|
||||
|
||||
<form @submit.prevent="save">
|
||||
<input v-model="user.name" placeholder="Name" />
|
||||
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
35
client/src/pages/UsersList.vue
Normal file
35
client/src/pages/UsersList.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
import { onMounted } from "vue"
|
||||
import { useUsersStore } from "../stores/UsersStore.ts"
|
||||
|
||||
const store = useUsersStore()
|
||||
|
||||
onMounted(() => {
|
||||
store.fetchUsers()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
|
||||
<router-link to="/user/new">Create User</router-link>
|
||||
|
||||
<table>
|
||||
<tr v-for="user in store.users" :key="user.id">
|
||||
<td>{{ user.name }}</td>
|
||||
<td>
|
||||
|
||||
<router-link :to="`/user/${user.id}`" custom v-slot="{ navigate }">
|
||||
<button @click="navigate" role="link">Edit</button>
|
||||
</router-link>
|
||||
|
||||
<button @click="store.removeUser(user.id)">Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
16
client/src/pages/index.vue
Normal file
16
client/src/pages/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
<!-- pages are the base instance for routes. an endpoint serves a page, and the page loads components -->
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>straight up gargoyling it !</h1>
|
||||
<h3>yeah im so cool rn</h3>
|
||||
<h1>imagining what I could do with themes :o</h1>
|
||||
|
||||
<router-link to="/users" custom v-slot="{ navigate }">
|
||||
<button @click="navigate" role="link">Users</button>
|
||||
</router-link>
|
||||
</template>
|
||||
@@ -1,8 +1,22 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
// the router creates front-end endpoints and serves pages to them
|
||||
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import UsersList from "../pages/UsersList.vue";
|
||||
import UserForm from "../pages/UserForm.vue";
|
||||
import index from "../pages/index.vue";
|
||||
|
||||
// link path to the page component
|
||||
const routes = [
|
||||
{ path: "/", component: index },
|
||||
{ path: "/users", component: UsersList },
|
||||
{ path: "/user/new", component: UserForm },
|
||||
{ path: "/user/:id", component: UserForm }
|
||||
]; // I really like this
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [],
|
||||
})
|
||||
routes: routes,
|
||||
});
|
||||
|
||||
export default router
|
||||
export default router;
|
||||
|
||||
48
client/src/stores/UsersStore.ts
Normal file
48
client/src/stores/UsersStore.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
// stores are for component state management
|
||||
// Pinia (?) i kinda dont get it because in angular you just hook a component to a service and that's it,
|
||||
// though I guess the service handled the state management
|
||||
// sighh
|
||||
|
||||
import { defineStore } from "pinia";
|
||||
import type { User } from "../models/User.ts";
|
||||
import * as api from "../api/UsersApi";
|
||||
|
||||
interface UserState {
|
||||
users: User[];
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export const useUsersStore = defineStore("users", {
|
||||
|
||||
state: (): UserState => ({
|
||||
users: [],
|
||||
loading: false
|
||||
}),
|
||||
|
||||
actions: {
|
||||
async fetchUsers() {
|
||||
this.loading = true;
|
||||
const response = await api.getUsers();
|
||||
this.users = response.data;
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
async addUser(user: User) {
|
||||
const response = await api.createUser(user);
|
||||
this.users.push(response.data);
|
||||
},
|
||||
|
||||
async updateUser(id: number, user: User) {
|
||||
await api.updateUser(id, user);
|
||||
const index = this.users.findIndex(i => i.id === id);
|
||||
this.users[index] = user;
|
||||
},
|
||||
|
||||
async removeUser(id: number) {
|
||||
await api.deleteUser(id);
|
||||
this.users = this.users.filter(i => i.id !== id);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -15,4 +15,7 @@ export default defineConfig({
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
},
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
|
||||
# idk yet
|
||||
|
||||
dotnet ./api/test.cs
|
||||
#dotnet ./api/test.cs
|
||||
|
||||
set ASPNETCORE_ENVIRONMENT=Development
|
||||
dotnet run
|
||||
|
||||
npm run dev
|
||||
|
||||
Reference in New Issue
Block a user