chore: started working on the api
This commit is contained in:
parent
0fe7c51bab
commit
1c0d6a309b
1
go.mod
1
go.mod
@ -15,6 +15,7 @@ require (
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.9.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
|
2
go.sum
2
go.sum
@ -16,6 +16,8 @@ github.com/galeone/tfgo v0.0.0-20230715013254-16113111dc99 h1:8Bt1P/zy1gb37L4n8C
|
||||
github.com/galeone/tfgo v0.0.0-20230715013254-16113111dc99/go.mod h1:3YgYBeIX42t83uP27Bd4bSMxTnQhSbxl0pYSkCDB1tc=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/goccy/go-json"
|
||||
)
|
||||
|
||||
func Mul(n1 int, n2 int) int {
|
||||
@ -347,6 +348,43 @@ type Context struct {
|
||||
Db *sql.DB
|
||||
}
|
||||
|
||||
func (c Context) ToJSON(r *http.Request, dat any) *Error {
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
||||
err := decoder.Decode(dat)
|
||||
if err != nil {
|
||||
return c.Error500(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Context) SendJSON(w http.ResponseWriter, dat any) *Error {
|
||||
w.Header().Add("content-type", "application/json")
|
||||
text, err := json.Marshal(dat)
|
||||
if err != nil {
|
||||
return c.Error500(err)
|
||||
}
|
||||
if _, err = w.Write(text); err != nil {
|
||||
return c.Error500(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Context) SendJSONStatus(w http.ResponseWriter, status int, dat any) *Error {
|
||||
w.Header().Add("content-type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
text, err := json.Marshal(dat)
|
||||
if err != nil {
|
||||
return c.Error500(err)
|
||||
}
|
||||
if _, err = w.Write(text); err != nil {
|
||||
return c.Error500(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c Context) Error400(err error, message string, w http.ResponseWriter, path string, base string, data AnyMap) *Error {
|
||||
c.SetReportCaller(true)
|
||||
c.Logger.Error(message)
|
||||
@ -609,6 +647,10 @@ func NewHandler(db *sql.DB) *Handle {
|
||||
if r.Header.Get("Request-Type") == "htmlfull" {
|
||||
ans = HTMLFULL
|
||||
}
|
||||
if r.Header.Get("content-type") == "application/json" {
|
||||
ans = JSON
|
||||
}
|
||||
|
||||
//TODO JSON
|
||||
|
||||
//Login state
|
||||
@ -618,6 +660,9 @@ func NewHandler(db *sql.DB) *Handle {
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Add("Access-Control-Allow-Headers", "*")
|
||||
|
||||
if r.Method == "GET" {
|
||||
x.handleGets(w, r, context)
|
||||
return
|
||||
@ -630,6 +675,9 @@ func NewHandler(db *sql.DB) *Handle {
|
||||
x.handleDeletes(w, r, context)
|
||||
return
|
||||
}
|
||||
if r.Method == "OPTIONS" {
|
||||
return
|
||||
}
|
||||
panic("TODO handle method: " + r.Method)
|
||||
})
|
||||
|
||||
|
36
users.go
36
users.go
@ -4,7 +4,6 @@ import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
@ -79,8 +78,39 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
||||
handle.GetHTML("/login", AnswerTemplate("login.html", nil, 0))
|
||||
handle.Post("/login", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
||||
if c.Mode == JSON {
|
||||
fmt.Println("Handle JSON")
|
||||
return &Error{Code: 404}
|
||||
|
||||
type UserLogin struct {
|
||||
Email string `json:email`
|
||||
Password string `json:password`
|
||||
}
|
||||
|
||||
var dat UserLogin
|
||||
|
||||
if err := c.ToJSON(r, &dat); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
/*if (dat["email"] == nil || dat["password"] == nil) {
|
||||
// TODO improve this
|
||||
c.Logger.Warn("Email or password are empty")
|
||||
return c.Error500(nil)
|
||||
}*/
|
||||
|
||||
// TODO Give this to the generateToken function
|
||||
expiration := time.Now().Add(24 * time.Hour)
|
||||
token, login := generateToken(db, dat.Email, dat.Password)
|
||||
if !login {
|
||||
return c.SendJSONStatus(w, http.StatusUnauthorized, "Email or password are incorrect")
|
||||
}
|
||||
|
||||
|
||||
cookie := &http.Cookie{Name: "auth", Value: token, HttpOnly: false, Expires: expiration}
|
||||
http.SetCookie(w, cookie)
|
||||
|
||||
w.Header().Set("Location", "/")
|
||||
w.WriteHeader(http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
r.ParseForm()
|
||||
|
13
webpage/.eslintignore
Normal file
13
webpage/.eslintignore
Normal file
@ -0,0 +1,13 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
31
webpage/.eslintrc.cjs
Normal file
31
webpage/.eslintrc.cjs
Normal file
@ -0,0 +1,31 @@
|
||||
/** @type { import("eslint").Linter.Config } */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:svelte/recommended',
|
||||
'prettier'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020,
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2017: true,
|
||||
node: true
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.svelte'],
|
||||
parser: 'svelte-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
10
webpage/.gitignore
vendored
Normal file
10
webpage/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
1
webpage/.npmrc
Normal file
1
webpage/.npmrc
Normal file
@ -0,0 +1 @@
|
||||
engine-strict=true
|
4
webpage/.prettierignore
Normal file
4
webpage/.prettierignore
Normal file
@ -0,0 +1,4 @@
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
8
webpage/.prettierrc
Normal file
8
webpage/.prettierrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
38
webpage/README.md
Normal file
38
webpage/README.md
Normal file
@ -0,0 +1,38 @@
|
||||
# create-svelte
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm create svelte@latest
|
||||
|
||||
# create a new project in my-app
|
||||
npm create svelte@latest my-app
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
BIN
webpage/bun.lockb
Executable file
BIN
webpage/bun.lockb
Executable file
Binary file not shown.
34
webpage/package.json
Normal file
34
webpage/package.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "webpage",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@types/eslint": "^8.56.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.36.0-next.4",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"sass": "^1.71.1",
|
||||
"svelte": "^5.0.0-next.1",
|
||||
"svelte-check": "^3.6.0",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.3"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
60
webpage/src/NavBar.svelte
Normal file
60
webpage/src/NavBar.svelte
Normal file
@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
import { userStore } from './routes/UserStore.svelte';
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/"> Index </a>
|
||||
</li>
|
||||
{#if userStore.user}
|
||||
<li>
|
||||
<a href="/models"> Models </a>
|
||||
</li>
|
||||
{/if}
|
||||
<li class="expand"></li>
|
||||
{#if userStore.user}
|
||||
<li>
|
||||
<a href="/user/info"> User Info </a>
|
||||
<a href="/logout"> Logout </a>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<a href="/login"> Login </a>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<style class="scss">
|
||||
nav {
|
||||
background: #ececec;
|
||||
margin: 0;
|
||||
box-shadow: 0 0 8px 1px #888888ef;
|
||||
height: 60px;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
padding: 20px 40px;
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
padding-left: 10px;
|
||||
|
||||
&:first-child {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.expand {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
13
webpage/src/app.d.ts
vendored
Normal file
13
webpage/src/app.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
12
webpage/src/app.html
Normal file
12
webpage/src/app.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
1
webpage/src/lib/index.ts
Normal file
1
webpage/src/lib/index.ts
Normal file
@ -0,0 +1 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
42
webpage/src/lib/requests.svelte.ts
Normal file
42
webpage/src/lib/requests.svelte.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { userStore } from 'routes/UserStore.svelte';
|
||||
|
||||
const API = "http://localhost:8000";
|
||||
|
||||
export async function get(url: string) {
|
||||
const headers = new Headers();
|
||||
//headers.append('content-type', 'application/json');
|
||||
if (userStore.user) {
|
||||
headers.append('token', userStore.user.token);
|
||||
}
|
||||
|
||||
let r = await fetch(`${API}/${url}`, {
|
||||
method: 'GET',
|
||||
headers: headers,
|
||||
});
|
||||
|
||||
if (r.status !== 200) {
|
||||
throw r;
|
||||
}
|
||||
|
||||
return r.json();
|
||||
}
|
||||
|
||||
export async function post(url: string, body: any) {
|
||||
const headers = new Headers();
|
||||
headers.append('content-type', 'application/json');
|
||||
if (userStore.user) {
|
||||
headers.append('token', userStore.user.token);
|
||||
}
|
||||
|
||||
let r = await fetch(`${API}/${url}`, {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (r.status !== 200) {
|
||||
throw r;
|
||||
}
|
||||
|
||||
return r.json();
|
||||
}
|
17
webpage/src/routes/+error.svelte
Normal file
17
webpage/src/routes/+error.svelte
Normal file
@ -0,0 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import Page404 from './Page404.svelte';
|
||||
</script>
|
||||
|
||||
{#if $page.error}
|
||||
{#if $page.status == 404}
|
||||
<Page404 />
|
||||
{:else}
|
||||
<h1>
|
||||
{$page.status}
|
||||
</h1>
|
||||
<h2>
|
||||
{$page.error.message}
|
||||
</h2>
|
||||
{/if}
|
||||
{/if}
|
9
webpage/src/routes/+layout.svelte
Normal file
9
webpage/src/routes/+layout.svelte
Normal file
@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
import '../styles/fonts.css';
|
||||
import '../styles/app.css';
|
||||
import NavBar from "../NavBar.svelte";
|
||||
</script>
|
||||
|
||||
<NavBar />
|
||||
|
||||
<slot />
|
4
webpage/src/routes/+layout.ts
Normal file
4
webpage/src/routes/+layout.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export const prerender = true;
|
||||
export const ssr = false;
|
||||
export const csr = true;
|
||||
//export const trailingSlash = '';
|
1
webpage/src/routes/+page.svelte
Normal file
1
webpage/src/routes/+page.svelte
Normal file
@ -0,0 +1 @@
|
||||
<h1>Main Web Page</h1>
|
54
webpage/src/routes/Page404.svelte
Normal file
54
webpage/src/routes/Page404.svelte
Normal file
@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
let { message, goBackLink } = $props<{message?: string, goBackLink?: string}>();
|
||||
</script>
|
||||
|
||||
<div class="page404">
|
||||
<div>
|
||||
<h1>
|
||||
404
|
||||
</h1>
|
||||
{#if message}
|
||||
<h2>
|
||||
{message}
|
||||
</h2>
|
||||
{#if goBackLink}
|
||||
<div class="description">
|
||||
<a href={goBackLink}>
|
||||
👈 Go back
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<h2>
|
||||
Page Not found
|
||||
</h2>
|
||||
<div class="description">
|
||||
The page you were looking for does not exist
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.page404 {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
height: calc(100vh - 60px);
|
||||
text-align: center;
|
||||
|
||||
h1 {
|
||||
font-size: 10em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 5em;
|
||||
margin: 0;
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
div.description {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
55
webpage/src/routes/UserStore.svelte.ts
Normal file
55
webpage/src/routes/UserStore.svelte.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
type User = {
|
||||
token: string,
|
||||
id: string,
|
||||
user_type: number,
|
||||
username: string,
|
||||
email: string,
|
||||
};
|
||||
|
||||
export function createUserStore() {
|
||||
let user = $state<User | undefined>(undefined);
|
||||
|
||||
function getValue() {
|
||||
if (user == undefined) {
|
||||
let storage = localStorage.getItem('user');
|
||||
if (storage) {
|
||||
try {
|
||||
user = JSON.parse(storage);
|
||||
} catch {
|
||||
user = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
return {
|
||||
get user() {
|
||||
return getValue();
|
||||
},
|
||||
set user(value: User | undefined) {
|
||||
if (value) {
|
||||
localStorage.setItem('user', JSON.stringify(value));
|
||||
} else {
|
||||
localStorage.removeItem('user');
|
||||
}
|
||||
user = value;
|
||||
},
|
||||
checkUser(pathOnFail: string, level?: number) {
|
||||
if (user && user.level > (level ?? 2) ) {
|
||||
goto(pathOnFail);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
isLogin() {
|
||||
if (getValue()) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const userStore = createUserStore();
|
83
webpage/src/routes/login/+page.svelte
Normal file
83
webpage/src/routes/login/+page.svelte
Normal file
@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
import { post } from 'src/lib/requests.svelte';
|
||||
import 'src/styles/forms.css';
|
||||
import { userStore } from '../UserStore.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
let submitted = $state(false);
|
||||
|
||||
let loginData = $state({
|
||||
email: '',
|
||||
password: '',
|
||||
});
|
||||
|
||||
let errorMessage = $state("");
|
||||
|
||||
async function onSubmit() {
|
||||
submitted = true;
|
||||
errorMessage = "";
|
||||
|
||||
try {
|
||||
const req = await post('login', loginData);
|
||||
userStore.user = req;
|
||||
goto("/");
|
||||
} catch (e) {
|
||||
if (e instanceof Response) {
|
||||
errorMessage = await e.json();
|
||||
} else {
|
||||
errorMessage = "Server could not perform login";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title> Login </title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="login-page">
|
||||
<div>
|
||||
<h1>
|
||||
Login
|
||||
</h1>
|
||||
<form on:submit|preventDefault={onSubmit} class:submitted >
|
||||
<fieldset>
|
||||
<label for="email">Email</label>
|
||||
<input type="email" required name="email" bind:value={loginData.email} />
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<label for="password">Password</label>
|
||||
<input required name="password" type="password" bind:value={loginData.password} />
|
||||
{#if errorMessage}
|
||||
<span class="form-msg error">
|
||||
{errorMessage}
|
||||
<!--Either the password or the email are incorrect-->
|
||||
</span>
|
||||
{/if}
|
||||
</fieldset>
|
||||
<button>
|
||||
Login
|
||||
</button>
|
||||
<div class="spacer"></div>
|
||||
<a class="simple-link text-center w100 spacer" href="/register">
|
||||
Register
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
/* Login Page */
|
||||
.login-page {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-bottom: 40px;
|
||||
& > div {
|
||||
width: 40vw;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
10
webpage/src/routes/logout/+page.svelte
Normal file
10
webpage/src/routes/logout/+page.svelte
Normal file
@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import { userStore } from "../UserStore.svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
onMount(() => {
|
||||
userStore.user = undefined;
|
||||
goto('/');
|
||||
});
|
||||
</script>
|
88
webpage/src/styles/app.css
Normal file
88
webpage/src/styles/app.css
Normal file
@ -0,0 +1,88 @@
|
||||
*{box-sizing: border-box;font-family: 'Roboto', sans-serif;}
|
||||
|
||||
:root {
|
||||
--nav-bar-size: 54px;
|
||||
--white: #ffffff;
|
||||
--grey: #dbdcde;
|
||||
--light-grey: #fafafa;
|
||||
--main: #fca311;
|
||||
--sec: #14213d;
|
||||
--black: #000000;
|
||||
--red: 212, 38, 38;
|
||||
--green: 92, 199, 30;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.button,
|
||||
button {
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
padding: 3px 6px;
|
||||
border: none;
|
||||
box-shadow: 0 2px 8px 1px #66666655;
|
||||
background: var(--main);
|
||||
color: var(--black);
|
||||
|
||||
&.padded {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: rgb(var(--red));
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-start {
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.grow-1 {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.w100 {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.simple-link {
|
||||
color: var(--sec);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bigger {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: red;
|
||||
}
|
8
webpage/src/styles/fonts.css
Normal file
8
webpage/src/styles/fonts.css
Normal file
@ -0,0 +1,8 @@
|
||||
@font-face {
|
||||
font-family: MedievalSharp;
|
||||
src: url(/MedievalSharp-Regular.ttf);
|
||||
}
|
||||
|
||||
.font-medieval {
|
||||
font-family: MedievalSharp, cursive;
|
||||
}
|
147
webpage/src/styles/forms.css
Normal file
147
webpage/src/styles/forms.css
Normal file
@ -0,0 +1,147 @@
|
||||
/* forms */
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card form {
|
||||
padding: 0;
|
||||
border-radius: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
form {
|
||||
padding: 30px;
|
||||
margin: 20px 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: 2px 5px 8px 2px #66666655;
|
||||
|
||||
label,
|
||||
fieldset legend {
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
input {
|
||||
border: none;
|
||||
box-shadow: 0 2px 5px 1px #66666655;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input:invalid:focus,
|
||||
&.submitted input:invalid {
|
||||
box-shadow: 0 2px 5px 1px rgba(var(--red), 0.2);
|
||||
}
|
||||
|
||||
&.submitted input:valid {
|
||||
box-shadow: 0 2px 5px 1px rgba(var(--green), 0.2);
|
||||
}
|
||||
|
||||
& .spacer {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
padding-bottom: 15px;
|
||||
border: none;
|
||||
|
||||
.form-msg {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: rgb(var(--red))
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 1.2rem;
|
||||
margin-left: 50%;
|
||||
width: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.input-radial {
|
||||
label {
|
||||
display: inline;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
width: auto;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Upload files */
|
||||
fieldset.file-upload {
|
||||
input[type="file"] {
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.icon-holder {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
font-size: 1rem;
|
||||
|
||||
.icon {
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
background: none;
|
||||
transform: none;
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
transition: all 1s;
|
||||
|
||||
&.adapt {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
min-height: 150px;
|
||||
min-width: 150px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 80%;
|
||||
object-fit: contain;
|
||||
text-align: center;
|
||||
transition: all 1s;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fieldset.file-upload:has(input[type="file"]:invalid:focus) .icon,
|
||||
&.submitted fieldset.file-upload:has(input[type="file"]:invalid:focus) .icon {
|
||||
box-shadow: 0 2px 5px 1px rgba(var(--red), 0.2);
|
||||
}
|
||||
|
||||
&.submitted fieldset.file-upload:has(input[type="file"]:valid:focus) .icon {
|
||||
box-shadow: 0 2px 5px 1px rgba(var(--green), 0.2);
|
||||
}
|
||||
|
||||
}
|
BIN
webpage/static/favicon.png
Normal file
BIN
webpage/static/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
22
webpage/svelte.config.js
Normal file
22
webpage/svelte.config.js
Normal file
@ -0,0 +1,22 @@
|
||||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter(),
|
||||
alias: {
|
||||
src: "src",
|
||||
routes: "src/routes",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
18
webpage/tsconfig.json
Normal file
18
webpage/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
}
|
6
webpage/vite.config.ts
Normal file
6
webpage/vite.config.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()]
|
||||
});
|
Loading…
Reference in New Issue
Block a user