Compare commits
15 Commits
50ca3bc2bc
...
master
Author | SHA1 | Date | |
---|---|---|---|
ebe4868645 | |||
c7db2be6d0 | |||
8346e8cd3a | |||
5256083f1d | |||
325364f0b3 | |||
ca804ab425 | |||
663fb3424e | |||
9ef4520fae | |||
024b5d5b3d | |||
329381ad10 | |||
7e7716e4e8 | |||
46f91ea364 | |||
b0767c8794 | |||
30a174c4da | |||
4317f237b5 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -2,3 +2,6 @@ data
|
||||
main.log
|
||||
input
|
||||
frontend/.vscode
|
||||
backend/vendor
|
||||
backend/deploy.tar
|
||||
frontend/deploy.tar
|
||||
|
4
backend/captain-definition
Normal file
4
backend/captain-definition
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"dockerfilePath": "./dockerfile"
|
||||
}
|
205
backend/ddl.sql
205
backend/ddl.sql
@@ -1,7 +1,4 @@
|
||||
create table guild (
|
||||
id integer primary key,
|
||||
name blob
|
||||
);
|
||||
create table guild (id integer primary key, name blob);
|
||||
create unique index idx_guild_name on guild(name);
|
||||
|
||||
create table player (
|
||||
@@ -23,4 +20,202 @@ create table note (
|
||||
content blob,
|
||||
timestamp string,
|
||||
player integer references player(id)
|
||||
);
|
||||
);
|
||||
|
||||
create table audit_log (
|
||||
id integer primary key,
|
||||
table_name text not null,
|
||||
row_id integer not null,
|
||||
action text not null,
|
||||
changes text,
|
||||
timestamp text default current_timestamp
|
||||
);
|
||||
|
||||
create trigger guild_insert_audit
|
||||
after
|
||||
insert on guild begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'guild',
|
||||
new.id,
|
||||
'INSERT',
|
||||
json_object('name', new.name)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger guild_update_audit
|
||||
after
|
||||
update on guild begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'guild',
|
||||
new.id,
|
||||
'UPDATE',
|
||||
json_object('old_name', old.name, 'new_name', new.name)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger guild_delete_audit
|
||||
after delete on guild begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'guild',
|
||||
old.id,
|
||||
'DELETE',
|
||||
json_object('name', old.name)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger player_insert_audit
|
||||
after
|
||||
insert on player begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'player',
|
||||
new.id,
|
||||
'INSERT',
|
||||
json_object('name', new.name, 'guild', new.guild)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger player_update_audit
|
||||
after
|
||||
update on player begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'player',
|
||||
new.id,
|
||||
'UPDATE',
|
||||
json_object(
|
||||
'old_name',
|
||||
old.name,
|
||||
'new_name',
|
||||
new.name,
|
||||
'old_guild',
|
||||
old.guild,
|
||||
'new_guild',
|
||||
new.guild
|
||||
)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger player_delete_audit
|
||||
after delete on player begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'player',
|
||||
old.id,
|
||||
'DELETE',
|
||||
json_object('name', old.name, 'guild', old.guild)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger association_insert_audit
|
||||
after
|
||||
insert on association begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'association',
|
||||
new.id,
|
||||
'INSERT',
|
||||
json_object('lhs', new.lhs, 'rhs', new.rhs, 'note', new.note)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger association_update_audit
|
||||
after
|
||||
update on association begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'association',
|
||||
new.id,
|
||||
'UPDATE',
|
||||
json_object(
|
||||
'old_lhs',
|
||||
old.lhs,
|
||||
'new_lhs',
|
||||
new.lhs,
|
||||
'old_rhs',
|
||||
old.rhs,
|
||||
'new_rhs',
|
||||
new.rhs,
|
||||
'old_note',
|
||||
old.note,
|
||||
'new_note',
|
||||
new.note
|
||||
)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger association_delete_audit
|
||||
after delete on association begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'association',
|
||||
old.id,
|
||||
'DELETE',
|
||||
json_object('lhs', old.lhs, 'rhs', old.rhs, 'note', old.note)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger note_insert_audit
|
||||
after
|
||||
insert on note begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'note',
|
||||
new.id,
|
||||
'INSERT',
|
||||
json_object(
|
||||
'content',
|
||||
new.content,
|
||||
'timestamp',
|
||||
new.timestamp,
|
||||
'player',
|
||||
new.player
|
||||
)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger note_update_audit
|
||||
after
|
||||
update on note begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'note',
|
||||
new.id,
|
||||
'UPDATE',
|
||||
json_object(
|
||||
'old_content',
|
||||
old.content,
|
||||
'new_content',
|
||||
new.content,
|
||||
'old_timestamp',
|
||||
old.timestamp,
|
||||
'new_timestamp',
|
||||
new.timestamp,
|
||||
'old_player',
|
||||
old.player,
|
||||
'new_player',
|
||||
new.player
|
||||
)
|
||||
);
|
||||
end;
|
||||
|
||||
create trigger note_delete_audit
|
||||
after delete on note begin
|
||||
insert into audit_log (table_name, row_id, action, changes)
|
||||
values (
|
||||
'note',
|
||||
old.id,
|
||||
'DELETE',
|
||||
json_object(
|
||||
'content',
|
||||
old.content,
|
||||
'timestamp',
|
||||
old.timestamp,
|
||||
'player',
|
||||
old.player
|
||||
)
|
||||
);
|
||||
end;
|
1
backend/deploy.sh
Normal file
1
backend/deploy.sh
Normal file
@@ -0,0 +1 @@
|
||||
go mod vendor && tar -cf deploy.tar *.sql *.go dockerfile captain-definition go.mod go.sum vendor
|
56
backend/dockerfile
Normal file
56
backend/dockerfile
Normal file
@@ -0,0 +1,56 @@
|
||||
# Build stage
|
||||
# FROM golang:1.23-bullseye as base
|
||||
FROM golang:bullseye as base
|
||||
|
||||
RUN adduser \
|
||||
--disabled-password \
|
||||
--gecos "" \
|
||||
--home "/nonexistent" \
|
||||
--shell "/sbin/nologin" \
|
||||
--no-create-home \
|
||||
--uid 65532 \
|
||||
small-user
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
# RUN go mod download
|
||||
# RUN go mod verify
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc \
|
||||
libc6-dev
|
||||
|
||||
RUN GOFLAGS=-mod=vendor \
|
||||
CGO_ENABLED=1 \
|
||||
GOOS=linux \
|
||||
GOARCH=amd64 \
|
||||
CGO_LDFLAGS="-static -w -s" \
|
||||
go build \
|
||||
-ldflags '-extldflags "-static"' \
|
||||
-o main .
|
||||
|
||||
# Final stage
|
||||
# Pick your poison
|
||||
# Ordered by size desc
|
||||
# FROM ubuntu:latest
|
||||
FROM alpine:latest
|
||||
# FROM scratch
|
||||
RUN apk add --no-cache libc6-compat
|
||||
|
||||
# Run this for ubuntu
|
||||
# RUN apt-get update && apt-get install -y curl vim git && apt-get clean
|
||||
|
||||
# Copy the binary from the build stage
|
||||
COPY --from=base /app/main /main
|
||||
|
||||
# Copy necessary files from the build stage
|
||||
COPY --from=base /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
COPY --from=base /etc/passwd /etc/passwd
|
||||
COPY --from=base /etc/group /etc/group
|
||||
|
||||
# Use the non-root user
|
||||
# USER small-user
|
||||
|
||||
CMD ["/main"]
|
@@ -39,6 +39,36 @@ func (ps *PlayerService) Query(query PlayerServiceQuery) ([]Player, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (ps *PlayerService) Save(player Player) (Player, error) {
|
||||
log.Printf("Saving player %s", player.Name)
|
||||
res, err := ps.db.writeConn.Exec("insert into player (name, guild) values (?, ?) on conflict (name) do update set guild = ?", player.Name, player.Guild.ID, player.Guild.ID)
|
||||
if err != nil {
|
||||
return player, fmt.Errorf("failed to update player: %v", err)
|
||||
}
|
||||
|
||||
id := player.ID
|
||||
if id == 0 {
|
||||
id, err = res.LastInsertId()
|
||||
if err != nil {
|
||||
return player, fmt.Errorf("failed to get last insert id: %v", err)
|
||||
}
|
||||
}
|
||||
log.Printf("Updated player %s with id %d", player.Name, id)
|
||||
|
||||
qres, err := ps.Query(PlayerServiceQuery{ID: &id})
|
||||
if err != nil {
|
||||
return player, fmt.Errorf("failed getting player for id %d: %v", id, err)
|
||||
}
|
||||
if len(qres) > 1 {
|
||||
return player, fmt.Errorf("more than one player found for id %d", id)
|
||||
}
|
||||
if len(qres) == 0 {
|
||||
return player, fmt.Errorf("no player found for id %d", id)
|
||||
}
|
||||
|
||||
return qres[0], nil
|
||||
}
|
||||
|
||||
func (ps *PlayerService) Create(name string, guild Guild) (Player, error) {
|
||||
log.Printf("Creating player %s in guild %s", name, guild.Name)
|
||||
player := Player{}
|
||||
|
@@ -40,6 +40,7 @@ func BuildWhereQuery(params interface{}) string {
|
||||
}
|
||||
|
||||
var associationRe = regexp.MustCompile(`r:(\w+)(?:\(([^)]+)\))?`)
|
||||
|
||||
func ParseNote(data string) (NoteData, error) {
|
||||
log.Printf("Parsing note: %q", data)
|
||||
res := NoteData{}
|
||||
@@ -67,6 +68,9 @@ func ParseNote(data string) (NoteData, error) {
|
||||
}
|
||||
}
|
||||
res.Note = strings.Join(note, "\n")
|
||||
if res.Date.IsZero() {
|
||||
res.Date = time.Now()
|
||||
}
|
||||
|
||||
matches := associationRe.FindAllStringSubmatch(res.Note, -1)
|
||||
for _, match := range matches {
|
||||
@@ -96,6 +100,10 @@ func PersistNoteData(note NoteData) (Note, []Association, error) {
|
||||
}
|
||||
res.Player = player
|
||||
player.Guild = guild
|
||||
player, err = ps.Save(player)
|
||||
if err != nil {
|
||||
return res, ass, fmt.Errorf("failed saving player: %v", err)
|
||||
}
|
||||
|
||||
for _, assoc := range note.Associations {
|
||||
assocPlayer, err := ps.GetOrCreate(assoc.Player, Guild{})
|
||||
@@ -115,4 +123,4 @@ func PersistNoteData(note NoteData) (Note, []Association, error) {
|
||||
}
|
||||
|
||||
return res, ass, nil
|
||||
}
|
||||
}
|
||||
|
76
backend/utils_test.go
Normal file
76
backend/utils_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestParseNote(t *testing.T) {
|
||||
data := `p:Мундиль
|
||||
g:Культурные люди
|
||||
d:2024-10-30
|
||||
Worgen balance druid
|
||||
Came to orgrimmar trying to kill saurfang and killing people with <1m hp
|
||||
18:04:24 > r:Myxich
|
||||
Is very weak and scared
|
||||
Has like 7M hp but refuses to fight
|
||||
Refuses to fight anyone >1M hp that is
|
||||
Tried hiding tried flying and failed`
|
||||
note, err := ParseNote(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if note.Player != "Мундиль" {
|
||||
t.Errorf("Expected player to be Мундиль, got %s", note.Player)
|
||||
}
|
||||
if note.Guild != "Культурные люди" {
|
||||
t.Errorf("Expected guild to be Культурные люди, got %s", note.Guild)
|
||||
}
|
||||
if note.Date.Format(time.DateOnly) != "2024-10-30" {
|
||||
t.Errorf("Expected date to be 2024-10-30, got %s", note.Date.Format(time.DateOnly))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNoteNoGuild(t *testing.T) {
|
||||
data := `p:Kulmi
|
||||
d:2024-10-30
|
||||
Came to orgrimmar trying to kill saurfang
|
||||
Also killed some weak ass paladin, obviously interested in stomping noobs
|
||||
Is one of those wpvp heroes, fully kitted out and shit`
|
||||
note, err := ParseNote(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if note.Player != "Kulmi" {
|
||||
t.Errorf("Expected player to be Kulmi, got %s", note.Player)
|
||||
}
|
||||
if note.Guild != "" {
|
||||
t.Errorf("Expected guild to be empty, got %s", note.Guild)
|
||||
}
|
||||
if note.Date.Format(time.DateOnly) != "2024-10-30" {
|
||||
t.Errorf("Expected date to be 2024-10-30, got %s", note.Date.Format(time.DateOnly))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseNoteNoDate(t *testing.T) {
|
||||
data := `p:Ashripper
|
||||
g:The Inferno
|
||||
Came to orgrimmar causing trouble
|
||||
This is Criminallad and probably Criminaldad
|
||||
There's nothing I can do to stop him and he knows it
|
||||
He is deeply rooted with THE guild`
|
||||
note, err := ParseNote(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if note.Player != "Ashripper" {
|
||||
t.Errorf("Expected player to be Ashripper, got %s", note.Player)
|
||||
}
|
||||
if note.Guild != "The Inferno" {
|
||||
t.Errorf("Expected guild to be The Inferno, got %s", note.Guild)
|
||||
}
|
||||
now := time.Now()
|
||||
if note.Date.Format(time.DateOnly) != now.Format(time.DateOnly) {
|
||||
t.Errorf("Expected date to be %s got %s", now.Format(time.DateOnly), note.Date.Format(time.DateOnly))
|
||||
}
|
||||
}
|
@@ -3,8 +3,17 @@ import { defineConfig } from 'astro/config';
|
||||
|
||||
import tailwind from "@astrojs/tailwind";
|
||||
|
||||
import node from "@astrojs/node";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
output: "server",
|
||||
integrations: [tailwind()],
|
||||
|
||||
adapter: node({
|
||||
mode: "standalone",
|
||||
}),
|
||||
redirects: {
|
||||
"/": "/player"
|
||||
}
|
||||
});
|
4
frontend/captain-definition
Normal file
4
frontend/captain-definition
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"dockerfilePath": "./dockerfile"
|
||||
}
|
1
frontend/deploy.sh
Normal file
1
frontend/deploy.sh
Normal file
@@ -0,0 +1 @@
|
||||
pnpm build && tar -cf deploy.tar dist dockerfile package*.json pnpm-lock.yaml captain-definition node_modules
|
17
frontend/dockerfile
Normal file
17
frontend/dockerfile
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM node:20-slim
|
||||
|
||||
WORKDIR /app
|
||||
RUN npm install -g pnpm
|
||||
|
||||
COPY dist ./dist
|
||||
COPY package*.json ./
|
||||
|
||||
ENV HOST=0.0.0.0
|
||||
ENV PORT=4321
|
||||
|
||||
RUN pnpm i
|
||||
|
||||
EXPOSE 4321
|
||||
#CMD ["node", "./dist/server/entry.mjs"]
|
||||
# This might not be necessary but it is working now so I'm not touching it
|
||||
CMD ["sh", "-c", "HOST=0.0.0.0 PORT=4321 node ./dist/server/entry.mjs"]
|
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.4",
|
||||
"@astrojs/node": "^8.3.4",
|
||||
"@astrojs/tailwind": "^5.1.2",
|
||||
"astro": "^4.16.7",
|
||||
"typescript": "^5.6.3"
|
||||
|
155
frontend/pnpm-lock.yaml
generated
155
frontend/pnpm-lock.yaml
generated
@@ -11,6 +11,9 @@ importers:
|
||||
'@astrojs/check':
|
||||
specifier: ^0.9.4
|
||||
version: 0.9.4(typescript@5.6.3)
|
||||
'@astrojs/node':
|
||||
specifier: ^8.3.4
|
||||
version: 8.3.4(astro@4.16.7(rollup@4.24.2)(typescript@5.6.3))
|
||||
'@astrojs/tailwind':
|
||||
specifier: ^5.1.2
|
||||
version: 5.1.2(astro@4.16.7(rollup@4.24.2)(typescript@5.6.3))(tailwindcss@3.4.14)
|
||||
@@ -68,6 +71,11 @@ packages:
|
||||
'@astrojs/markdown-remark@5.3.0':
|
||||
resolution: {integrity: sha512-r0Ikqr0e6ozPb5bvhup1qdWnSPUvQu6tub4ZLYaKyG50BXZ0ej6FhGz3GpChKpH7kglRFPObJd/bDyf2VM9pkg==}
|
||||
|
||||
'@astrojs/node@8.3.4':
|
||||
resolution: {integrity: sha512-xzQs39goN7xh9np9rypGmbgZj3AmmjNxEMj9ZWz5aBERlqqFF3n8A/w/uaJeZ/bkHS60l1BXVS0tgsQt9MFqBA==}
|
||||
peerDependencies:
|
||||
astro: ^4.2.0
|
||||
|
||||
'@astrojs/prism@3.1.0':
|
||||
resolution: {integrity: sha512-Z9IYjuXSArkAUx3N6xj6+Bnvx8OdUSHA8YoOgyepp3+zJmtVYJIl/I18GozdJVW1p5u/CNpl3Km7/gwTJK85cw==}
|
||||
engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0}
|
||||
@@ -849,6 +857,14 @@ packages:
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
|
||||
debug@2.6.9:
|
||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
|
||||
debug@4.3.7:
|
||||
resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
@@ -861,10 +877,18 @@ packages:
|
||||
decode-named-character-reference@1.0.2:
|
||||
resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
|
||||
|
||||
depd@2.0.0:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
dequal@2.0.3:
|
||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
destroy@1.2.0:
|
||||
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
|
||||
detect-libc@2.0.3:
|
||||
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -896,6 +920,9 @@ packages:
|
||||
eastasianwidth@0.2.0:
|
||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||
|
||||
ee-first@1.1.1:
|
||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||
|
||||
electron-to-chromium@1.5.47:
|
||||
resolution: {integrity: sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==}
|
||||
|
||||
@@ -911,6 +938,10 @@ packages:
|
||||
emoji-regex@9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
|
||||
encodeurl@2.0.0:
|
||||
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
@@ -927,6 +958,9 @@ packages:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
escape-html@1.0.3:
|
||||
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
|
||||
|
||||
escape-string-regexp@5.0.0:
|
||||
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -942,6 +976,10 @@ packages:
|
||||
estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
|
||||
etag@1.8.1:
|
||||
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
eventemitter3@5.0.1:
|
||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||
|
||||
@@ -991,6 +1029,10 @@ packages:
|
||||
fraction.js@4.3.7:
|
||||
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
||||
|
||||
fresh@0.5.2:
|
||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
@@ -1080,9 +1122,16 @@ packages:
|
||||
http-cache-semantics@4.1.1:
|
||||
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
|
||||
|
||||
http-errors@2.0.0:
|
||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
import-meta-resolve@4.1.0:
|
||||
resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==}
|
||||
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
|
||||
is-arrayish@0.3.2:
|
||||
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
|
||||
|
||||
@@ -1371,6 +1420,11 @@ packages:
|
||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
mime@1.6.0:
|
||||
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
|
||||
mimic-function@5.0.1:
|
||||
resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1387,6 +1441,9 @@ packages:
|
||||
resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
ms@2.0.0:
|
||||
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@@ -1427,6 +1484,10 @@ packages:
|
||||
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
on-finished@2.4.1:
|
||||
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
onetime@7.0.0:
|
||||
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1580,6 +1641,10 @@ packages:
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
range-parser@1.2.1:
|
||||
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
read-cache@1.0.0:
|
||||
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
||||
|
||||
@@ -1681,6 +1746,16 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
send@0.19.1:
|
||||
resolution: {integrity: sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
server-destroy@1.0.1:
|
||||
resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==}
|
||||
|
||||
setprototypeof@1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
|
||||
sharp@0.33.5:
|
||||
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@@ -1716,6 +1791,10 @@ packages:
|
||||
sprintf-js@1.0.3:
|
||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||
|
||||
statuses@2.0.1:
|
||||
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
stdin-discarder@0.2.2:
|
||||
resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1779,6 +1858,10 @@ packages:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
toidentifier@1.0.1:
|
||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
trim-lines@3.0.1:
|
||||
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
|
||||
|
||||
@@ -2166,6 +2249,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@astrojs/node@8.3.4(astro@4.16.7(rollup@4.24.2)(typescript@5.6.3))':
|
||||
dependencies:
|
||||
astro: 4.16.7(rollup@4.24.2)(typescript@5.6.3)
|
||||
send: 0.19.1
|
||||
server-destroy: 1.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@astrojs/prism@3.1.0':
|
||||
dependencies:
|
||||
prismjs: 1.29.0
|
||||
@@ -2970,6 +3061,10 @@ snapshots:
|
||||
|
||||
cssesc@3.0.0: {}
|
||||
|
||||
debug@2.6.9:
|
||||
dependencies:
|
||||
ms: 2.0.0
|
||||
|
||||
debug@4.3.7:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
@@ -2978,8 +3073,12 @@ snapshots:
|
||||
dependencies:
|
||||
character-entities: 2.0.2
|
||||
|
||||
depd@2.0.0: {}
|
||||
|
||||
dequal@2.0.3: {}
|
||||
|
||||
destroy@1.2.0: {}
|
||||
|
||||
detect-libc@2.0.3:
|
||||
optional: true
|
||||
|
||||
@@ -3003,6 +3102,8 @@ snapshots:
|
||||
|
||||
eastasianwidth@0.2.0: {}
|
||||
|
||||
ee-first@1.1.1: {}
|
||||
|
||||
electron-to-chromium@1.5.47: {}
|
||||
|
||||
emmet@2.4.11:
|
||||
@@ -3016,6 +3117,8 @@ snapshots:
|
||||
|
||||
emoji-regex@9.2.2: {}
|
||||
|
||||
encodeurl@2.0.0: {}
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
es-module-lexer@1.5.4: {}
|
||||
@@ -3048,6 +3151,8 @@ snapshots:
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
escape-html@1.0.3: {}
|
||||
|
||||
escape-string-regexp@5.0.0: {}
|
||||
|
||||
esprima@4.0.1: {}
|
||||
@@ -3058,6 +3163,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
|
||||
etag@1.8.1: {}
|
||||
|
||||
eventemitter3@5.0.1: {}
|
||||
|
||||
extend-shallow@2.0.1:
|
||||
@@ -3107,6 +3214,8 @@ snapshots:
|
||||
|
||||
fraction.js@4.3.7: {}
|
||||
|
||||
fresh@0.5.2: {}
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
@@ -3245,8 +3354,18 @@ snapshots:
|
||||
|
||||
http-cache-semantics@4.1.1: {}
|
||||
|
||||
http-errors@2.0.0:
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
inherits: 2.0.4
|
||||
setprototypeof: 1.2.0
|
||||
statuses: 2.0.1
|
||||
toidentifier: 1.0.1
|
||||
|
||||
import-meta-resolve@4.1.0: {}
|
||||
|
||||
inherits@2.0.4: {}
|
||||
|
||||
is-arrayish@0.3.2:
|
||||
optional: true
|
||||
|
||||
@@ -3686,6 +3805,8 @@ snapshots:
|
||||
braces: 3.0.3
|
||||
picomatch: 2.3.1
|
||||
|
||||
mime@1.6.0: {}
|
||||
|
||||
mimic-function@5.0.1: {}
|
||||
|
||||
minimatch@9.0.5:
|
||||
@@ -3696,6 +3817,8 @@ snapshots:
|
||||
|
||||
mrmime@2.0.0: {}
|
||||
|
||||
ms@2.0.0: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
muggle-string@0.4.1: {}
|
||||
@@ -3724,6 +3847,10 @@ snapshots:
|
||||
|
||||
object-hash@3.0.0: {}
|
||||
|
||||
on-finished@2.4.1:
|
||||
dependencies:
|
||||
ee-first: 1.1.1
|
||||
|
||||
onetime@7.0.0:
|
||||
dependencies:
|
||||
mimic-function: 5.0.1
|
||||
@@ -3866,6 +3993,8 @@ snapshots:
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
range-parser@1.2.1: {}
|
||||
|
||||
read-cache@1.0.0:
|
||||
dependencies:
|
||||
pify: 2.3.0
|
||||
@@ -4027,6 +4156,28 @@ snapshots:
|
||||
|
||||
semver@7.6.3: {}
|
||||
|
||||
send@0.19.1:
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
destroy: 1.2.0
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
fresh: 0.5.2
|
||||
http-errors: 2.0.0
|
||||
mime: 1.6.0
|
||||
ms: 2.1.3
|
||||
on-finished: 2.4.1
|
||||
range-parser: 1.2.1
|
||||
statuses: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
server-destroy@1.0.1: {}
|
||||
|
||||
setprototypeof@1.2.0: {}
|
||||
|
||||
sharp@0.33.5:
|
||||
dependencies:
|
||||
color: 4.2.3
|
||||
@@ -4084,6 +4235,8 @@ snapshots:
|
||||
|
||||
sprintf-js@1.0.3: {}
|
||||
|
||||
statuses@2.0.1: {}
|
||||
|
||||
stdin-discarder@0.2.2: {}
|
||||
|
||||
string-width@4.2.3:
|
||||
@@ -4174,6 +4327,8 @@ snapshots:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
toidentifier@1.0.1: {}
|
||||
|
||||
trim-lines@3.0.1: {}
|
||||
|
||||
trough@2.2.0: {}
|
||||
|
3
frontend/src/env.ts
Normal file
3
frontend/src/env.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const API_URL = import.meta.env.API_URL;
|
||||
|
||||
export { API_URL };
|
@@ -1,127 +1,2 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import Card from "../components/Card.astro";
|
||||
---
|
||||
|
||||
<Layout title="Welcome to Astro.">
|
||||
<main>
|
||||
<svg
|
||||
class="astro-a"
|
||||
width="495"
|
||||
height="623"
|
||||
viewBox="0 0 495 623"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M167.19 364.254C83.4786 364.254 0 404.819 0 404.819C0 404.819 141.781 19.4876 142.087 18.7291C146.434 7.33701 153.027 0 162.289 0H332.441C341.703 0 348.574 7.33701 352.643 18.7291C352.92 19.5022 494.716 404.819 494.716 404.819C494.716 404.819 426.67 364.254 327.525 364.254L264.41 169.408C262.047 159.985 255.147 153.581 247.358 153.581C239.569 153.581 232.669 159.985 230.306 169.408L167.19 364.254ZM160.869 530.172C160.877 530.18 160.885 530.187 160.894 530.195L160.867 530.181C160.868 530.178 160.868 530.175 160.869 530.172ZM136.218 411.348C124.476 450.467 132.698 504.458 160.869 530.172C160.997 529.696 161.125 529.242 161.248 528.804C161.502 527.907 161.737 527.073 161.917 526.233C165.446 509.895 178.754 499.52 195.577 500.01C211.969 500.487 220.67 508.765 223.202 527.254C224.141 534.12 224.23 541.131 224.319 548.105C224.328 548.834 224.337 549.563 224.347 550.291C224.563 566.098 228.657 580.707 237.264 593.914C245.413 606.426 256.108 615.943 270.749 622.478C270.593 621.952 270.463 621.508 270.35 621.126C270.045 620.086 269.872 619.499 269.685 618.911C258.909 585.935 266.668 563.266 295.344 543.933C298.254 541.971 301.187 540.041 304.12 538.112C310.591 533.854 317.059 529.599 323.279 525.007C345.88 508.329 360.09 486.327 363.431 457.844C364.805 446.148 363.781 434.657 359.848 423.275C358.176 424.287 356.587 425.295 355.042 426.275C351.744 428.366 348.647 430.33 345.382 431.934C303.466 452.507 259.152 455.053 214.03 448.245C184.802 443.834 156.584 436.019 136.218 411.348Z"
|
||||
fill="url(#paint0_linear_1805_24383)"></path>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_1805_24383"
|
||||
x1="247.358"
|
||||
y1="0"
|
||||
x2="247.358"
|
||||
y2="622.479"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-opacity="0.9"></stop>
|
||||
<stop offset="1" stop-opacity="0.2"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<h1>Welcome to <span class="text-gradient">Astro</span></h1>
|
||||
<p class="instructions">
|
||||
To get started, open the directory <code>src/pages</code> in your project.<br />
|
||||
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
|
||||
</p>
|
||||
<ul role="list" class="link-card-grid">
|
||||
<Card
|
||||
href="https://docs.astro.build/"
|
||||
title="Documentation"
|
||||
body="Learn how Astro works and explore the official API docs."
|
||||
/>
|
||||
<Card
|
||||
href="https://astro.build/integrations/"
|
||||
title="Integrations"
|
||||
body="Supercharge your project with new frameworks and libraries."
|
||||
/>
|
||||
<Card
|
||||
href="https://astro.build/themes/"
|
||||
title="Themes"
|
||||
body="Explore a galaxy of community-built starter themes."
|
||||
/>
|
||||
<Card
|
||||
href="https://astro.build/chat/"
|
||||
title="Community"
|
||||
body="Come say hi to our amazing Discord community. ❤️"
|
||||
/>
|
||||
</ul>
|
||||
</main>
|
||||
|
||||
<div class="min-h-screen bg-gray-100 flex items-center justify-center">
|
||||
<h1 class="text-4xl font-bold text-blue-600 hover:text-blue-800">Hello Tailwind + Astro!</h1>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
main {
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
width: 800px;
|
||||
max-width: calc(100% - 2rem);
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.astro-a {
|
||||
position: absolute;
|
||||
top: -32px;
|
||||
left: 50%;
|
||||
transform: translatex(-50%);
|
||||
width: 220px;
|
||||
height: auto;
|
||||
z-index: -1;
|
||||
}
|
||||
h1 {
|
||||
font-size: 4rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.text-gradient {
|
||||
background-image: var(--accent-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-size: 400%;
|
||||
background-position: 0%;
|
||||
}
|
||||
.instructions {
|
||||
margin-bottom: 2rem;
|
||||
border: 1px solid rgba(var(--accent-light), 25%);
|
||||
background: linear-gradient(rgba(var(--accent-dark), 66%), rgba(var(--accent-dark), 33%));
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.instructions code {
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
background: rgba(var(--accent-light), 12%);
|
||||
color: rgb(var(--accent-light));
|
||||
border-radius: 4px;
|
||||
padding: 0.3em 0.4em;
|
||||
}
|
||||
.instructions strong {
|
||||
color: rgb(var(--accent-light));
|
||||
}
|
||||
.link-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
|
||||
gap: 2rem;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,15 +1,16 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import type { APIResponse, Player, Note } from "../types";
|
||||
import { API_URL } from "../env";
|
||||
|
||||
const res = await fetch("http://localhost:3000/player");
|
||||
const res = await fetch(`${API_URL}/player`);
|
||||
const data: APIResponse<Player[]> = await res.json();
|
||||
|
||||
const request = { success: true, message: "" };
|
||||
if (Astro.request.method === "POST") {
|
||||
try {
|
||||
const body = await Astro.request.text();
|
||||
const res = await fetch("http://localhost:3000/note/new", {
|
||||
const res = await fetch(`${API_URL}/note/new`, {
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
@@ -32,7 +33,7 @@ console.log(request);
|
||||
if (form) {
|
||||
form.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
const note = document.querySelector("#note");
|
||||
const note: HTMLElement | null = document.querySelector("#note");
|
||||
if (!note) {
|
||||
console.error("No note found");
|
||||
return;
|
||||
@@ -45,13 +46,18 @@ console.log(request);
|
||||
if (res.status != 200) {
|
||||
console.error(res);
|
||||
const err = res.statusText;
|
||||
const kaput = document.querySelector("#kaput");
|
||||
kaput.classList.remove("display-none");
|
||||
const kaput: HTMLElement | null = document.querySelector("#kaput");
|
||||
if (!kaput) {
|
||||
console.error("No kaput found");
|
||||
return;
|
||||
}
|
||||
kaput?.classList.remove("display-none");
|
||||
kaput.innerText = err;
|
||||
} else {
|
||||
const kaput = document.querySelector("#kaput");
|
||||
kaput.classList.add("display-none");
|
||||
kaput?.classList.add("display-none");
|
||||
note.innerText = "";
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -59,8 +65,7 @@ console.log(request);
|
||||
</script>
|
||||
|
||||
<Layout title="Players">
|
||||
<div id="kaput" class="display-none text-center text-2xl text-red-600 py-2">
|
||||
</div>
|
||||
<div id="kaput" class="display-none text-center text-2xl text-red-600 py-2"></div>
|
||||
<form class="p-5 min-h-[25rem] h-[25rem]">
|
||||
<div class="h-full w-full mb-4 border rounded-lg shadow-lg overflow-hidden flex flex-col">
|
||||
<div
|
||||
@@ -95,7 +100,7 @@ console.log(request);
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.data.map((player) => (
|
||||
<tr key={player.name} class="hover:bg-gray-50">
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="p-4 border border-gray-300">
|
||||
<a href={`/player/${player.name}`} class="text-blue-600 hover:underline">
|
||||
{player.name}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
---
|
||||
const { name } = Astro.params;
|
||||
import Layout from '../../layouts/Layout.astro';
|
||||
import { API_URL } from "../../env";
|
||||
---
|
||||
|
||||
<Layout title={name || "Player"}>
|
||||
|
Reference in New Issue
Block a user