start frontend
This commit is contained in:
parent
81b824bb79
commit
7417a2c35c
34 changed files with 3592 additions and 14 deletions
179
.gitignore
vendored
179
.gitignore
vendored
|
|
@ -1,7 +1,174 @@
|
|||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.log
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
*.pubxml
|
||||
*.azurePubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
||||
packages/
|
||||
## TODO: If the tool you use requires repositories.config, also uncomment the next line
|
||||
!packages/repositories.config
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
![Ss]tyle[Cc]op.targets
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.[Pp]ublish.xml
|
||||
|
||||
*.publishsettings
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
App_Data/*.mdf
|
||||
App_Data/*.ldf
|
||||
|
||||
# =========================
|
||||
# Windows detritus
|
||||
# =========================
|
||||
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Mac desktop service store files
|
||||
.DS_Store
|
||||
|
||||
_NCrunch*
|
||||
|
||||
.idea/
|
||||
mizuki.db
|
||||
|
||||
appsettings.json
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
PinkSea.Gateway/wwwroot/
|
||||
|
||||
mizuki.db*
|
||||
|
||||
uploads/
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Mizuki.Dtos;
|
||||
using Mizuki.Services;
|
||||
|
|
@ -24,7 +25,7 @@ public class LoginController(
|
|||
[HttpPost]
|
||||
[Route("login")]
|
||||
public async Task<RedirectResult> Login(
|
||||
LoginDataDto dto)
|
||||
[FromForm] LoginDataDto dto)
|
||||
{
|
||||
if (!await userService.CheckPasswordForUser(dto.Username, dto.Password))
|
||||
{
|
||||
|
|
@ -34,6 +35,8 @@ public class LoginController(
|
|||
var user = await userService.GetUserForUsername(dto.Username);
|
||||
await loginService.LoginAsUser(user);
|
||||
|
||||
Console.WriteLine($"Logged in as {user.Username}");
|
||||
|
||||
return Redirect("/");
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +60,7 @@ public class LoginController(
|
|||
[Route("register")]
|
||||
[HttpPost]
|
||||
public async Task<RedirectResult> Register(
|
||||
LoginDataDto dto)
|
||||
[FromForm] LoginDataDto dto)
|
||||
{
|
||||
if (await userService.UsernameTaken(dto.Username))
|
||||
return Redirect("/register?error=This user already exists.");
|
||||
|
|
@ -78,4 +81,25 @@ public class LoginController(
|
|||
|
||||
return Redirect("/");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether we have logged in.
|
||||
/// </summary>
|
||||
/// <returns>Either an OK or a forbidden result.</returns>
|
||||
[Route("check")]
|
||||
[HttpGet]
|
||||
public async Task<Results<Ok, ForbidHttpResult>> Check()
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = await loginService.GetActiveUser();
|
||||
Console.WriteLine($"Get active user returned {user.Username}");
|
||||
return TypedResults.Ok();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine($"Get active user returned FORBID");
|
||||
return TypedResults.Forbid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,8 @@ public class ServeController(
|
|||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <returns>The file, or an error.</returns>
|
||||
[Route("/{filename}")]
|
||||
[Route("{filename}")]
|
||||
[HttpGet]
|
||||
public async Task<Results<FileStreamHttpResult, NotFound>> GetFile(
|
||||
[FromRoute] string filename)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Mizuki.Database.Models;
|
||||
|
||||
namespace Mizuki.Database;
|
||||
|
|
@ -23,6 +24,28 @@ public class MizukiDbContext : DbContext
|
|||
{
|
||||
modelBuilder.Entity<User>();
|
||||
modelBuilder.Entity<Upload>();
|
||||
|
||||
if (Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite")
|
||||
{
|
||||
// SQLite does not have proper support for DateTimeOffset via Entity Framework Core, see the limitations
|
||||
// here: https://docs.microsoft.com/en-us/ef/core/providers/sqlite/limitations#query-limitations
|
||||
// To work around this, when the Sqlite database provider is used, all model properties of type DateTimeOffset
|
||||
// use the DateTimeOffsetToBinaryConverter
|
||||
// Based on: https://github.com/aspnet/EntityFrameworkCore/issues/10784#issuecomment-415769754
|
||||
// This only supports millisecond precision, but should be sufficient for most use cases.
|
||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||
{
|
||||
var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(DateTimeOffset)
|
||||
|| p.PropertyType == typeof(DateTimeOffset?));
|
||||
foreach (var property in properties)
|
||||
{
|
||||
modelBuilder
|
||||
.Entity(entityType.Name)
|
||||
.Property(property.Name)
|
||||
.HasConversion(new DateTimeOffsetToBinaryConverter());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
10
Program.cs
10
Program.cs
|
|
@ -5,6 +5,9 @@ using Mizuki.Services;
|
|||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddControllers();
|
||||
|
||||
builder.Services.AddValidatorsFromAssembly(
|
||||
Assembly.GetCallingAssembly());
|
||||
|
||||
|
|
@ -14,9 +17,6 @@ builder.Services.AddScoped<UploadService>();
|
|||
builder.Services.AddScoped<UserService>();
|
||||
builder.Services.AddDbContext<MizukiDbContext>();
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
builder.Services.AddAuthentication("MizukiAuth")
|
||||
.AddCookie("MizukiAuth", options =>
|
||||
{
|
||||
|
|
@ -25,8 +25,10 @@ builder.Services.AddAuthentication("MizukiAuth")
|
|||
options.AccessDeniedPath = "/";
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapGet("/", () => "Hello World!");
|
||||
app.MapControllers();
|
||||
app.MapFallbackToFile("index.html");
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
|
|
|||
|
|
@ -47,4 +47,17 @@ public class DriveService
|
|||
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a file with the given filename.
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename.</param>
|
||||
/// <param name="fileStream">The file stream.</param>
|
||||
public void SaveFileByFilename(string filename, Stream fileStream)
|
||||
{
|
||||
var actualFilename = Path.GetFileName(filename);
|
||||
var path = Path.Combine(UploadsFolder, actualFilename);
|
||||
using var writeStream = File.OpenWrite(path);
|
||||
fileStream.CopyTo(writeStream);
|
||||
}
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ public class LoginService(
|
|||
var username = httpContextAccessor.HttpContext!
|
||||
.User
|
||||
.FindFirst(ClaimTypes.Name)!
|
||||
.ToString();
|
||||
.Value;
|
||||
|
||||
return await userService.GetUserForUsername(username);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ public class UploadService(
|
|||
|
||||
await dbContext.Uploads.AddAsync(upload);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
driveService.SaveFileByFilename(upload.Filename, file.OpenReadStream());
|
||||
return upload;
|
||||
}
|
||||
|
||||
|
|
|
|||
30
mizuki-frontend/.gitignore
vendored
Normal file
30
mizuki-frontend/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
3
mizuki-frontend/.vscode/extensions.json
vendored
Normal file
3
mizuki-frontend/.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
||||
33
mizuki-frontend/README.md
Normal file
33
mizuki-frontend/README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# mizuki-frontend
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
1
mizuki-frontend/env.d.ts
vendored
Normal file
1
mizuki-frontend/env.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
||||
13
mizuki-frontend/index.html
Normal file
13
mizuki-frontend/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
2929
mizuki-frontend/package-lock.json
generated
Normal file
2929
mizuki-frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
28
mizuki-frontend/package.json
Normal file
28
mizuki-frontend/package.json
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "mizuki-frontend",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --build"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node22": "^22.0.0",
|
||||
"@types/node": "^22.10.2",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vue/tsconfig": "^0.7.0",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"typescript": "~5.6.3",
|
||||
"vite": "^6.0.5",
|
||||
"vite-plugin-vue-devtools": "^7.6.8",
|
||||
"vue-tsc": "^2.1.10"
|
||||
}
|
||||
}
|
||||
BIN
mizuki-frontend/public/favicon.ico
Normal file
BIN
mizuki-frontend/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
11
mizuki-frontend/src/App.vue
Normal file
11
mizuki-frontend/src/App.vue
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
import { RouterView } from 'vue-router'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
0
mizuki-frontend/src/assets/base.css
Normal file
0
mizuki-frontend/src/assets/base.css
Normal file
1
mizuki-frontend/src/assets/main.css
Normal file
1
mizuki-frontend/src/assets/main.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
@import './base.css';
|
||||
25
mizuki-frontend/src/components/FileList.vue
Normal file
25
mizuki-frontend/src/components/FileList.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import {onBeforeMount, ref} from "vue";
|
||||
import type FileDto from "@/dto/file-dto.ts";
|
||||
import FileListItem from "@/components/FileListItem.vue";
|
||||
|
||||
const files = ref<FileDto[]>([]);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const items = await fetch('/api/file/all')
|
||||
.then(i => i.json())
|
||||
.then(j => j as FileDto[]);
|
||||
|
||||
files.value = items;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="files.length > 0">
|
||||
<FileListItem v-for="file of files" :item="file" :key="file.filename" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
21
mizuki-frontend/src/components/FileListItem.vue
Normal file
21
mizuki-frontend/src/components/FileListItem.vue
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
import type FileDto from "@/dto/file-dto.ts";
|
||||
import {computed} from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
item: FileDto
|
||||
}>();
|
||||
|
||||
const url = computed(() => {
|
||||
return `http://localhost:5118/f/${props.item.filename}`;
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>{{ item.filename }} - {{ item.originalFilename }}</div>
|
||||
<a :href="url">Download</a>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
37
mizuki-frontend/src/components/Uploader.vue
Normal file
37
mizuki-frontend/src/components/Uploader.vue
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<script setup lang="ts">
|
||||
const sendData = async () => {
|
||||
const input = document.querySelector('input[type="file"]') as HTMLInputElement;
|
||||
|
||||
// noinspection JSIncompatibleTypesComparison
|
||||
if (input.files === null || input.files.length < 1) {
|
||||
return;
|
||||
}
|
||||
const data = new FormData();
|
||||
data.append('formFile', input.files[0]);
|
||||
|
||||
const resp = await fetch('/api/file/upload', {
|
||||
method: 'POST',
|
||||
body: data,
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
const json = resp.json();
|
||||
|
||||
if ('filename' in json) {
|
||||
alert(`Your file is able to be downloaded at https://localhost:5881/f/${json.filename}!`);
|
||||
} else if ('reason' in json) {
|
||||
alert(json.reason);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<input type="file" name="formFile" />
|
||||
<input type="submit" v-on:click.prevent="sendData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
5
mizuki-frontend/src/dto/file-dto.ts
Normal file
5
mizuki-frontend/src/dto/file-dto.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export default interface FileDto {
|
||||
filename: string,
|
||||
originalFilename: string,
|
||||
sizeInBytes: number
|
||||
}
|
||||
11
mizuki-frontend/src/helpers/api.ts
Normal file
11
mizuki-frontend/src/helpers/api.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
export const checkIfLoggedIn = async () => {
|
||||
try {
|
||||
const resp = await fetch("/api/user/check", {
|
||||
redirect: 'error',
|
||||
credentials: 'include'
|
||||
});
|
||||
return resp.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
11
mizuki-frontend/src/main.ts
Normal file
11
mizuki-frontend/src/main.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import './assets/main.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
27
mizuki-frontend/src/router/index.ts
Normal file
27
mizuki-frontend/src/router/index.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
import LoginView from "@/views/LoginView.vue";
|
||||
import RegisterView from "@/views/RegisterView.vue";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeView,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: LoginView,
|
||||
},
|
||||
{
|
||||
path: '/register',
|
||||
name: 'register',
|
||||
component: RegisterView,
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
export default router
|
||||
32
mizuki-frontend/src/views/HomeView.vue
Normal file
32
mizuki-frontend/src/views/HomeView.vue
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
import { checkIfLoggedIn } from "@/helpers/api.ts";
|
||||
import { useRouter } from "vue-router";
|
||||
import Uploader from "@/components/Uploader.vue";
|
||||
import FileList from "@/components/FileList.vue";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
onMounted(async () => {
|
||||
const loggedIn = await checkIfLoggedIn();
|
||||
if (!loggedIn) {
|
||||
await router.push('/login');
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
const logout = async () => {
|
||||
await fetch('/api/user/logout');
|
||||
window.location.reload();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FileList />
|
||||
<Uploader />
|
||||
<a href="#" v-on:click.prevent="logout">Log out</a>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
28
mizuki-frontend/src/views/LoginView.vue
Normal file
28
mizuki-frontend/src/views/LoginView.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<script setup lang="ts">
|
||||
import { onBeforeMount, ref } from "vue";
|
||||
import { checkIfLoggedIn } from "@/helpers/api.ts";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const loggedIn = ref<boolean>(true);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
loggedIn.value = await checkIfLoggedIn();
|
||||
if (loggedIn.value) {
|
||||
await router.push('/');
|
||||
return;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form v-if="!loggedIn" action="/api/user/login" method="post">
|
||||
<span>Username</span> <input type="text" name="Username" /><br />
|
||||
<span>Password</span> <input type="password" name="Password" />
|
||||
<input type="submit" value="Login" />
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
29
mizuki-frontend/src/views/RegisterView.vue
Normal file
29
mizuki-frontend/src/views/RegisterView.vue
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<script setup lang="ts">
|
||||
import { onBeforeMount, ref } from "vue";
|
||||
import { checkIfLoggedIn } from "@/helpers/api.ts";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const loggedIn = ref<boolean>(true);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
loggedIn.value = await checkIfLoggedIn();
|
||||
if (loggedIn.value) {
|
||||
await router.push('/');
|
||||
return;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form v-if="!loggedIn" method="post" action="/api/user/register">
|
||||
<span>Username</span> <input type="text" name="Username" /><br />
|
||||
<span>Password</span> <input type="password" name="Password" /><br />
|
||||
<span>Repeat Password</span> <input type="password" name="ValidatePassword" /><br />
|
||||
<input type="submit" value="Register" />
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
12
mizuki-frontend/tsconfig.app.json
Normal file
12
mizuki-frontend/tsconfig.app.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"exclude": ["src/**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
11
mizuki-frontend/tsconfig.json
Normal file
11
mizuki-frontend/tsconfig.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
18
mizuki-frontend/tsconfig.node.json
Normal file
18
mizuki-frontend/tsconfig.node.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "@tsconfig/node22/tsconfig.json",
|
||||
"include": [
|
||||
"vite.config.*",
|
||||
"vitest.config.*",
|
||||
"cypress.config.*",
|
||||
"nightwatch.conf.*",
|
||||
"playwright.config.*"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
29
mizuki-frontend/vite.config.ts
Normal file
29
mizuki-frontend/vite.config.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
vueDevTools(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
},
|
||||
},
|
||||
server: {
|
||||
proxy: {
|
||||
'^/api': {
|
||||
target: 'http://localhost:5118',
|
||||
changeOrigin: true,
|
||||
cookiePathRewrite: {
|
||||
"*": "/",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
1
wwwroot/index.html
Normal file
1
wwwroot/index.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<h1>test</h1>
|
||||
Loading…
Reference in a new issue