Gisle Aune
2 years ago
37 changed files with 34553 additions and 34 deletions
-
4entities/user.go
-
8frontend/.gitignore
-
1frontend/.npmrc
-
38frontend/README.md
-
33660frontend/package-lock.json
-
30frontend/package.json
-
17frontend/src/app.d.ts
-
26frontend/src/app.html
-
52frontend/src/hooks.ts
-
74frontend/src/lib/clients/sl3.ts
-
38frontend/src/lib/components/common/Card.svelte
-
29frontend/src/lib/components/common/CardHeader.svelte
-
14frontend/src/lib/components/common/LinkCard.svelte
-
11frontend/src/lib/components/frontpage/ScopeLink.svelte
-
64frontend/src/lib/components/layout/Background.svelte
-
11frontend/src/lib/components/layout/Column.svelte
-
26frontend/src/lib/components/layout/Columns.svelte
-
46frontend/src/lib/components/layout/Header.svelte
-
22frontend/src/lib/components/layout/Row.svelte
-
24frontend/src/lib/css/colors.sass
-
16frontend/src/lib/models/scope.ts
-
12frontend/src/lib/models/stat.ts
-
12frontend/src/lib/models/user.ts
-
20frontend/src/routes/__layout.svelte
-
35frontend/src/routes/api/[...any].ts
-
56frontend/src/routes/index.svelte
-
42frontend/src/routes/login.svelte
-
BINfrontend/static/background.jpg
-
BINfrontend/static/favicon.png
-
15frontend/svelte.config.js
-
36frontend/tsconfig.json
-
3go.mod
-
2go.sum
-
106ports/cognitoauth/client.go
-
32ports/httpapi/auth.go
-
1usecases/auth/provider.go
-
4usecases/auth/service.go
@ -1,12 +1,14 @@ |
|||
package entities |
|||
|
|||
type User struct { |
|||
ID string `json:"id"` |
|||
ID string `json:"id"` |
|||
Name string `json:"name"` |
|||
} |
|||
|
|||
type AuthResult struct { |
|||
User *User `json:"user"` |
|||
Token string `json:"token,omitempty"` |
|||
RefreshToken string `json:"refreshToken,omitempty"` |
|||
Session string `json:"session,omitempty"` |
|||
PasswordChangeRequired bool `json:"passwordChangeRequired"` |
|||
} |
@ -0,0 +1,8 @@ |
|||
.DS_Store |
|||
node_modules |
|||
/build |
|||
/.svelte-kit |
|||
/package |
|||
.env |
|||
.env.* |
|||
!.env.example |
@ -0,0 +1 @@ |
|||
engine-strict=true |
@ -0,0 +1,38 @@ |
|||
# create-svelte |
|||
|
|||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/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 init svelte |
|||
|
|||
# create a new project in my-app |
|||
npm init svelte 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. |
33660
frontend/package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,30 @@ |
|||
{ |
|||
"name": "webui", |
|||
"version": "0.0.1", |
|||
"scripts": { |
|||
"dev": "svelte-kit dev", |
|||
"build": "svelte-kit build", |
|||
"package": "svelte-kit package", |
|||
"preview": "svelte-kit preview", |
|||
"prepare": "svelte-kit sync", |
|||
"check": "svelte-check --tsconfig ./tsconfig.json", |
|||
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch" |
|||
}, |
|||
"devDependencies": { |
|||
"@aws-amplify/auth": "^4.5.6", |
|||
"@aws-amplify/core": "^4.5.6", |
|||
"@sveltejs/adapter-auto": "next", |
|||
"@sveltejs/kit": "next", |
|||
"@types/cookie": "^0.5.1", |
|||
"aws-amplify": "^4.3.24", |
|||
"cookie": "^0.5.0", |
|||
"node-sass": "^7.0.1", |
|||
"sass": "^1.52.3", |
|||
"svelte": "^3.44.0", |
|||
"svelte-check": "^2.7.1", |
|||
"svelte-preprocess": "^4.10.6", |
|||
"tslib": "^2.3.1", |
|||
"typescript": "^4.7.2" |
|||
}, |
|||
"type": "module" |
|||
} |
@ -0,0 +1,17 @@ |
|||
/// <reference types="@sveltejs/kit" />
|
|||
|
|||
// See https://kit.svelte.dev/docs/types#app
|
|||
// for information about these interfaces
|
|||
declare namespace App { |
|||
interface Locals { |
|||
user?: import("$lib/models/user").default |
|||
idToken?: string |
|||
scopes?: import("$lib/models/scope").default[] |
|||
} |
|||
// interface Platform {}
|
|||
interface Session { |
|||
idToken?: string |
|||
req?: any |
|||
} |
|||
// interface Stuff {}
|
|||
} |
@ -0,0 +1,26 @@ |
|||
<!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" /> |
|||
<style> |
|||
body { |
|||
background-color: hsl(240, 12%, 5%); |
|||
color: hsl(240, 5%, 70%); |
|||
padding: 0; |
|||
margin: 0; |
|||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; |
|||
} |
|||
</style> |
|||
<script> |
|||
if (global === undefined) { |
|||
var global = window; |
|||
} |
|||
</script> |
|||
%sveltekit.head% |
|||
</head> |
|||
<body> |
|||
<div>%sveltekit.body%</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,52 @@ |
|||
import cookie from 'cookie'; |
|||
import { Amplify, withSSRContext } from 'aws-amplify'; |
|||
import type { GetSession, Handle } from '@sveltejs/kit'; |
|||
|
|||
Amplify.configure({ |
|||
Auth: { |
|||
region: import.meta.env.VITE_STUFFLOG3_AWS_POOL_REGION, |
|||
userPoolId: import.meta.env.VITE_STUFFLOG3_AWS_POOL_ID, |
|||
userPoolWebClientId: import.meta.env.VITE_STUFFLOG3_AWS_POOL_CLIENT_ID, |
|||
}, |
|||
ssr: true, |
|||
}); |
|||
|
|||
|
|||
export const getSession: GetSession = ({ locals, request }) => { |
|||
return { |
|||
idToken: locals.idToken, |
|||
req: { headers: { cookie: request.headers.get("Cookie") } } |
|||
}; |
|||
}; |
|||
|
|||
export const handle: Handle = async({ event, resolve }) => { |
|||
const {Auth} = withSSRContext({ |
|||
req: { headers: { cookie: event.request.headers.get("Cookie") } } |
|||
}); |
|||
|
|||
const newCookies = {}; |
|||
|
|||
try { |
|||
const curr = await Auth.currentAuthenticatedUser(); |
|||
event.locals.idToken = curr.signInUserSession?.idToken?.jwtToken; |
|||
|
|||
const store = curr?.storage?.store || {}; |
|||
const cookies = cookie.parse(event.request.headers.get("Cookie")); |
|||
for (const key in store) { |
|||
if (store[key] != cookies[key]) { |
|||
newCookies[key] = store[key] |
|||
console.log("Updating", key, store[key]?.slice(-8), cookies[key]?.slice(-8)); |
|||
} |
|||
} |
|||
} catch(err) { |
|||
console.warn(err) |
|||
} |
|||
|
|||
const response = await resolve(event); |
|||
|
|||
for (const key in newCookies) { |
|||
response.headers.append("Set-Cookie", cookie.serialize(key, newCookies[key])) |
|||
} |
|||
|
|||
return response; |
|||
} |
@ -0,0 +1,74 @@ |
|||
import type Scope from "$lib/models/scope"; |
|||
import type User from "$lib/models/user"; |
|||
import type { AuthResult } from "$lib/models/user"; |
|||
|
|||
type FetchImpl = (info: RequestInfo, init?: RequestInit) => Promise<Response>; |
|||
|
|||
export default class SL3APIClient { |
|||
private fetcher: FetchImpl |
|||
private root: string |
|||
private tokenPromise: Promise<string> |
|||
|
|||
constructor(fetcher: FetchImpl, tokenPromise?: Promise<string>, root?: string | boolean) { |
|||
this.fetcher = fetcher; |
|||
|
|||
if (typeof(root) === "string") { |
|||
this.root = root || ""; |
|||
} else if (root === true) { |
|||
this.root = process.env.STUFFLOG3_API; |
|||
} else { |
|||
this.root = "" |
|||
} |
|||
|
|||
this.tokenPromise = tokenPromise || null; |
|||
} |
|||
|
|||
async findScope(id: number): Promise<Scope> { |
|||
return this.fetch<{scope: Scope}>("GET", `scopes/${id}`).then(r => r.scope); |
|||
} |
|||
|
|||
async listScopes(): Promise<Scope[]> { |
|||
return this.fetch<{scopes: Scope[]}>("GET", "scopes").then(r => r.scopes); |
|||
} |
|||
|
|||
async autchCheck(): Promise<User> { |
|||
return this.fetch<{user: User}>("GET", "auth").then(r => r.user); |
|||
} |
|||
|
|||
async authLogin(username: string, password: string): Promise<AuthResult> { |
|||
return this.fetch<{auth: AuthResult}>("POST", "auth/login", {username, password}).then(r => r.auth); |
|||
} |
|||
|
|||
async authRefresh(refreshToken: string): Promise<AuthResult> { |
|||
return this.fetch<{auth: AuthResult}>("POST", "auth/refresh", {refreshToken}).then(r => r.auth); |
|||
} |
|||
|
|||
async fetch<T>(method: string, path: string, body: any = null): Promise<T> { |
|||
const headers: Record<string, string> = {}; |
|||
let bodyData: string | undefined = void(0); |
|||
if (body != null) { |
|||
headers["Content-Type"] = "application/json"; |
|||
bodyData = JSON.stringify(body); |
|||
} |
|||
|
|||
if (this.tokenPromise != null) { |
|||
headers["Authorization"] = `Bearer ${await this.tokenPromise}`; |
|||
} |
|||
|
|||
const res = await this.fetcher(`${this.root}/api/v1/${path}`, { |
|||
method, headers, |
|||
body: bodyData, |
|||
credentials: "same-origin", |
|||
}); |
|||
|
|||
if (!res.ok) { |
|||
if (!(res.headers.get("content-type")||"").includes("application/json")) { |
|||
throw await res.text(); |
|||
} |
|||
|
|||
throw await res.json(); |
|||
} |
|||
|
|||
return res.json(); |
|||
} |
|||
} |
@ -0,0 +1,38 @@ |
|||
<div class="entry"> |
|||
<slot></slot> |
|||
</div> |
|||
|
|||
<style lang="scss"> |
|||
@import "../../css/colors.sass"; |
|||
|
|||
div.entry { |
|||
display: block; |
|||
text-decoration: none; |
|||
color: $color-entry9; |
|||
background-color: $color-entry1-transparent; |
|||
border-bottom-right-radius: 0.75em; |
|||
margin: 0.5em 0; |
|||
padding: 0.25em 0.5ch; |
|||
transition: 250ms; |
|||
overflow-y: hidden; |
|||
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); |
|||
} |
|||
div.entry:hover { |
|||
background-color: $color-entry2-transparent; |
|||
color: $color-entry11; |
|||
} |
|||
|
|||
:global(div.entry div.entry) { |
|||
margin: 0.25em 0; |
|||
} |
|||
:global(div.entry div.entry) { |
|||
border-bottom-right-radius: 0.5em; |
|||
} |
|||
:global(div.entry div.entry:hover) { |
|||
background-color: $color-entry3; |
|||
} |
|||
|
|||
div.entry:last-of-type { |
|||
margin-bottom: 0.1em; |
|||
} |
|||
</style> |
@ -0,0 +1,29 @@ |
|||
<script lang="ts"> |
|||
export let subtitle: string = "" |
|||
</script> |
|||
|
|||
<div class="name"> |
|||
<span><slot></slot></span> |
|||
<span class="abbreviation">{subtitle}</span> |
|||
</div> |
|||
|
|||
<style lang="scss"> |
|||
@import "../../css/colors.sass"; |
|||
|
|||
div.name { |
|||
font-size: 1em; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
span.abbreviation { |
|||
font-size: 1em; |
|||
opacity: $opacity-entry4; |
|||
} |
|||
|
|||
@media screen and (max-width: 1200px) { |
|||
span.abbreviation { |
|||
display: block; |
|||
font-size: 0.75em; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,14 @@ |
|||
<script lang="ts"> |
|||
import Card from "./Card.svelte"; |
|||
|
|||
export let href: string; |
|||
</script> |
|||
|
|||
<a href={href}><Card><slot></slot></Card></a> |
|||
|
|||
<style> |
|||
a { |
|||
text-decoration: none; |
|||
color: inherit; |
|||
} |
|||
</style> |
@ -0,0 +1,11 @@ |
|||
<script lang="ts"> |
|||
import type Scope from "$lib/models/scope"; |
|||
import CardHeader from "../common/CardHeader.svelte"; |
|||
import LinkCard from "../common/LinkCard.svelte"; |
|||
|
|||
export let scope: Scope; |
|||
</script> |
|||
|
|||
<LinkCard href="/{scope.id}-{scope.abbreviation.toLocaleLowerCase()}/"> |
|||
<CardHeader subtitle={scope.abbreviation}>{scope.name}</CardHeader> |
|||
</LinkCard> |
@ -0,0 +1,64 @@ |
|||
<script lang="ts"> |
|||
import {onMount} from "svelte"; |
|||
|
|||
export let initialOrientation: "portrait" | "landscape" = "portrait"; |
|||
export let src = "/background.jpg"; |
|||
export let opacity = 1.0; |
|||
|
|||
let landscape = (initialOrientation === "landscape"); |
|||
let image: HTMLImageElement; |
|||
|
|||
onMount(() => { |
|||
if (image.complete && typeof(image.naturalHeight) !== 'undefined' && image.naturalHeight !== 0) { |
|||
const width = image.naturalWidth; |
|||
const height = image.naturalHeight; |
|||
|
|||
landscape = (width > (height * 1.50)); |
|||
} else { |
|||
image.onload = () => { |
|||
const width = image.naturalWidth; |
|||
const height = image.naturalHeight; |
|||
|
|||
landscape = (width > (height * 1.50)); |
|||
} |
|||
} |
|||
}); |
|||
</script> |
|||
|
|||
<img |
|||
bind:this={image} |
|||
class:landscape |
|||
src={src} |
|||
style={`opacity: ${opacity};`} |
|||
alt="Background" |
|||
/> |
|||
|
|||
<style> |
|||
|
|||
img { |
|||
/* Set rules to fill background */ |
|||
width: 100%; |
|||
/* Set up proportionate scaling */ |
|||
min-height: 100%; |
|||
height: auto; |
|||
/* Set up positioning */ |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
user-select: none; |
|||
-webkit-user-drag: none; |
|||
|
|||
transition: 250ms; |
|||
|
|||
z-index: -1; |
|||
} |
|||
|
|||
img.landscape { |
|||
/* Set rules to fill background */ |
|||
height: 100%; |
|||
/* Set up proportionate scaling */ |
|||
min-width: 100%; |
|||
width: auto; |
|||
} |
|||
|
|||
</style> |
@ -0,0 +1,11 @@ |
|||
<div class="column"> |
|||
<slot></slot> |
|||
</div> |
|||
|
|||
<style lang="sass"> |
|||
div.column |
|||
display: flex |
|||
flex-direction: column |
|||
flex-basis: 50 |
|||
flex: 1 |
|||
</style> |
@ -0,0 +1,26 @@ |
|||
<script lang="ts"> |
|||
export let wide = false; |
|||
</script> |
|||
|
|||
<div class="columns" class:wide> |
|||
<slot></slot> |
|||
</div> |
|||
|
|||
<style lang="sass"> |
|||
div.columns |
|||
display: flex |
|||
flex-direction: row |
|||
flex-basis: 100% |
|||
flex: 1 |
|||
|
|||
width: 80ch |
|||
max-width: 95% |
|||
margin: auto |
|||
|
|||
@media screen and (max-width: 800px) |
|||
width: 100% |
|||
flex-direction: column |
|||
|
|||
div.columns.wide |
|||
width: 120ch |
|||
</style> |
@ -0,0 +1,46 @@ |
|||
<script lang="ts"> |
|||
export let subtitle: string = ""; |
|||
export let fullwidth: boolean = false; |
|||
export let wide: boolean = false; |
|||
</script> |
|||
|
|||
<div class="header-wrapper" class:fullwidth class:wide> |
|||
<h1><slot></slot></h1> |
|||
<h2>{subtitle}</h2> |
|||
</div> |
|||
|
|||
<style lang="scss"> |
|||
@import "../../css/colors.sass"; |
|||
|
|||
div.header-wrapper { |
|||
&.fullwidth { |
|||
width: 80ch; |
|||
max-width: 95%; |
|||
margin: auto; |
|||
margin-top: 2em; |
|||
|
|||
&.wide { |
|||
width: 120ch; |
|||
} |
|||
} |
|||
|
|||
h1 { |
|||
margin: 0.333em 0; |
|||
margin-bottom: 0; |
|||
font-size: 3em; |
|||
font-weight: 100; |
|||
} |
|||
|
|||
h2 { |
|||
font-size: 1em; |
|||
margin: 1em 0; |
|||
margin-top: 0; |
|||
font-style: italic; |
|||
font-weight: 100; |
|||
|
|||
&:empty { |
|||
display: none; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,22 @@ |
|||
<script lang="ts"> |
|||
export let title = ""; |
|||
</script> |
|||
|
|||
<div class="row"> |
|||
{#if title != ""} |
|||
<h2>{title}</h2> |
|||
{/if} |
|||
|
|||
<slot></slot> |
|||
</div> |
|||
|
|||
<style lang="sass"> |
|||
div.row |
|||
margin: 0.5em 1ch |
|||
padding: 0.5em 1ch |
|||
|
|||
> h2 |
|||
font-size: 1.25em |
|||
margin: 0 |
|||
margin-bottom: 0.25em |
|||
</style> |
@ -0,0 +1,24 @@ |
|||
$color-entry0: hsl(240, 5%, 7%) |
|||
$color-entry0-transparent: hsla(240, 5%, 10%, 0.7) |
|||
$color-entry1: hsl(240, 5%, 14%) |
|||
$color-entry1-transparent: hsla(240, 5%, 17%, 0.8) |
|||
$color-entry2: hsl(240, 5%, 21%) |
|||
$color-entry2-transparent: hsla(240, 5%, 24%, 0.8) |
|||
$color-entry3: hsl(240, 5%, 28%) |
|||
$color-entry4: hsl(240, 5%, 35%) |
|||
$color-entry5: hsl(240, 5%, 42%) |
|||
$color-entry6: hsl(240, 5%, 49%) |
|||
$color-entry7: hsl(240, 5%, 56%) |
|||
$color-entry8: hsl(240, 5%, 63%) |
|||
$color-entry9: hsl(240, 5%, 70%) // Default |
|||
$color-entry10: hsl(240, 5%, 77%) |
|||
$color-entry11: hsl(240, 5%, 84%) |
|||
$color-entry12: hsl(240, 5%, 91%) |
|||
$color-entry13: hsl(240, 5%, 98%) |
|||
|
|||
$opacity-entry4: 0.40 |
|||
$opacity-entry5: 0.60 |
|||
$opacity-entry6: 0.80 |
|||
|
|||
$color-green: hsl(120, 50%, 74%) |
|||
$color-green-dark: hsl(120, 50%, 35%) |
@ -0,0 +1,16 @@ |
|||
import type Stat from "./stat" |
|||
|
|||
export default interface Scope { |
|||
id: number |
|||
name: string |
|||
abbreviation: string |
|||
customLabels: Record<string, string> |
|||
members: Record<string, ScopeMember> |
|||
stats: Stat[] |
|||
} |
|||
|
|||
export interface ScopeMember { |
|||
id: string |
|||
name: string |
|||
owner: boolean |
|||
} |
@ -0,0 +1,12 @@ |
|||
export default interface Stat { |
|||
id: number |
|||
name: string |
|||
weight: number |
|||
description: string |
|||
allowedAmounts?: StatAllowedAmount[] |
|||
} |
|||
|
|||
export interface StatAllowedAmount { |
|||
label: string |
|||
value: number |
|||
} |
@ -0,0 +1,12 @@ |
|||
export default interface User { |
|||
id: string |
|||
name: string |
|||
} |
|||
|
|||
export interface AuthResult { |
|||
user?: User |
|||
token?: string |
|||
refreshToken?: string |
|||
session?: string |
|||
passwordChangeRequired?: boolean |
|||
} |
@ -0,0 +1,20 @@ |
|||
<script> |
|||
import { onMount } from "svelte"; |
|||
import { page } from "$app/stores"; |
|||
|
|||
import Background from "$lib/components/layout/Background.svelte" |
|||
import SL3APIClient from "$lib/clients/sl3"; |
|||
|
|||
onMount(() => { |
|||
setInterval(async() => { |
|||
await new SL3APIClient(fetch).autchCheck(); |
|||
}, 30000) |
|||
}) |
|||
|
|||
let opacity = 0.175; |
|||
|
|||
$: opacity = $page.url.pathname === "/" ? 0.3 : 0.2; |
|||
</script> |
|||
|
|||
<Background initialOrientation="landscape" opacity={opacity} /> |
|||
<slot></slot> |
@ -0,0 +1,35 @@ |
|||
import type { RequestHandler } from "@sveltejs/kit"; |
|||
|
|||
export const get: RequestHandler = async({ request, params, locals }) => { |
|||
const proxyUrl = `${process.env.STUFFLOG3_API}/api/${params.any}`; |
|||
|
|||
const headers = { |
|||
...request.headers, |
|||
}; |
|||
|
|||
if (locals.idToken != null) { |
|||
headers["Authorization"] = `Bearer ${locals.idToken}`; |
|||
} |
|||
|
|||
const res = await fetch(proxyUrl, { |
|||
method: request.method, |
|||
headers: headers, |
|||
body: request.body, |
|||
}); |
|||
|
|||
const result = { |
|||
status: res.status, |
|||
body: await res.text(), |
|||
headers: {}, |
|||
} |
|||
res.headers.forEach((v, k) => { |
|||
result.headers[k] = v; |
|||
}) |
|||
|
|||
return result; |
|||
} |
|||
|
|||
export const options = get; |
|||
export const post = get; |
|||
export const put = get; |
|||
export const del = get; |
@ -0,0 +1,56 @@ |
|||
<script lang="ts" context="module"> |
|||
import type { Load } from "@sveltejs/kit/types/internal"; |
|||
import SL3APIClient from "$lib/clients/sl3"; |
|||
|
|||
export const load: Load = async({ fetch, session }) => { |
|||
const sl3 = new SL3APIClient(fetch); |
|||
|
|||
try { |
|||
const scopes = await sl3.listScopes(); |
|||
|
|||
return { |
|||
props: {scopes} |
|||
}; |
|||
} catch(err) { |
|||
if (err.statusCode === 403 || err.toString()?.includes("not authenticated")) { |
|||
return { status: 302, redirect: "/login" } |
|||
} else { |
|||
throw err; |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<script lang="ts"> |
|||
import Column from "$lib/components/layout/Column.svelte"; |
|||
import Columns from "$lib/components/layout/Columns.svelte"; |
|||
import Header from "$lib/components/layout/Header.svelte"; |
|||
import Row from "$lib/components/layout/Row.svelte"; |
|||
import type Scope from "$lib/models/scope"; |
|||
import ScopeLink from "$lib/components/frontpage/ScopeLink.svelte"; |
|||
|
|||
export let scopes: Scope[] = []; |
|||
</script> |
|||
|
|||
<Header fullwidth subtitle="Logging your stuff">Stufflog</Header> |
|||
|
|||
<Columns> |
|||
<Column> |
|||
<Row title="Scopes"> |
|||
{#each scopes as scope (scope.id)} |
|||
<ScopeLink scope={scope} /> |
|||
{/each} |
|||
</Row> |
|||
<Row title="Schedule"> |
|||
|
|||
</Row> |
|||
<Row title="Today"> |
|||
|
|||
</Row> |
|||
</Column> |
|||
<Column> |
|||
<Row title="Sprints"> |
|||
<p>Stuff</p> |
|||
</Row> |
|||
</Column> |
|||
</Columns> |
@ -0,0 +1,42 @@ |
|||
<script lang="ts"> |
|||
import { Amplify, Auth } from 'aws-amplify'; |
|||
import { goto } from '$app/navigation'; |
|||
|
|||
Amplify.configure({ |
|||
Auth: { |
|||
region: import.meta.env.VITE_STUFFLOG3_AWS_POOL_REGION, |
|||
userPoolId: import.meta.env.VITE_STUFFLOG3_AWS_POOL_ID, |
|||
userPoolWebClientId: import.meta.env.VITE_STUFFLOG3_AWS_POOL_CLIENT_ID, |
|||
}, |
|||
ssr: true, |
|||
}); |
|||
|
|||
let username = ''; |
|||
let password = ''; |
|||
|
|||
async function handleSignIn() { |
|||
try { |
|||
const res = await Auth.signIn(username, password); |
|||
} catch (error) { |
|||
console.log(error); |
|||
} |
|||
|
|||
goto('/'); |
|||
} |
|||
|
|||
</script> |
|||
|
|||
<form on:submit|preventDefault={handleSignIn}> |
|||
<input |
|||
type="text" |
|||
bind:value={username} |
|||
/> |
|||
<input |
|||
id="Password" |
|||
label="Password" |
|||
type="password" |
|||
bind:value={password} |
|||
/> |
|||
|
|||
<button type="submit">Sign In</button> |
|||
</form> |
After Width: 1920 | Height: 1080 | Size: 495 KiB |
After Width: 128 | Height: 128 | Size: 1.5 KiB |
@ -0,0 +1,15 @@ |
|||
import adapter from '@sveltejs/adapter-auto'; |
|||
import preprocess from 'svelte-preprocess'; |
|||
|
|||
/** @type {import('@sveltejs/kit').Config} */ |
|||
const config = { |
|||
// Consult https://github.com/sveltejs/svelte-preprocess
|
|||
// for more information about preprocessors
|
|||
preprocess: preprocess(), |
|||
|
|||
kit: { |
|||
adapter: adapter() |
|||
} |
|||
}; |
|||
|
|||
export default config; |
@ -0,0 +1,36 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"moduleResolution": "node", |
|||
"module": "es2020", |
|||
"lib": ["es2020", "DOM"], |
|||
"target": "es2020", |
|||
/** |
|||
svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript |
|||
to enforce using \`import type\` instead of \`import\` for Types. |
|||
*/ |
|||
"importsNotUsedAsValues": "error", |
|||
/** |
|||
TypeScript doesn't know about import usages in the template because it only sees the |
|||
script of a Svelte file. Therefore preserve all value imports. Requires TS 4.5 or higher. |
|||
*/ |
|||
"preserveValueImports": true, |
|||
"isolatedModules": true, |
|||
"resolveJsonModule": true, |
|||
/** |
|||
To have warnings/errors of the Svelte compiler at the correct position, |
|||
enable source maps by default. |
|||
*/ |
|||
"sourceMap": true, |
|||
"esModuleInterop": true, |
|||
"skipLibCheck": true, |
|||
"forceConsistentCasingInFileNames": true, |
|||
"baseUrl": ".", |
|||
"allowJs": true, |
|||
"checkJs": true, |
|||
"paths": { |
|||
"$lib": ["src/lib"], |
|||
"$lib/*": ["src/lib/*"] |
|||
} |
|||
}, |
|||
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue