diff --git a/Makefile b/Makefile index f97671d..bc6334b 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,6 @@ client/dist/index.html: build-client: client/dist/index.html start-server: build-client - -mkdir db.mount docker compose up --build -d clean: diff --git a/client/src/components/NavBar.vue b/client/src/components/NavBar.vue index 145a817..e11af91 100644 --- a/client/src/components/NavBar.vue +++ b/client/src/components/NavBar.vue @@ -1,7 +1,5 @@ @@ -11,27 +9,20 @@ import { state } from '../state' - - - Go Back - - + + + + Go Back + + - - - Add Track - - - - - - Log Out - - - - - + + + Add Track + + + - + \ No newline at end of file diff --git a/client/src/components/TickComponent.vue b/client/src/components/TickComponent.vue index 47d57e0..8b1d863 100644 --- a/client/src/components/TickComponent.vue +++ b/client/src/components/TickComponent.vue @@ -15,8 +15,8 @@ const className = computed(() => isSet.value ? "button is-rounded is-info" : "bu async function toggle() { if (isSet.value) { - await props.track.markIncomplete(props.date) + await state.taskMarkedIncomplete(props.track, props.date) } else - await props.track.markComplete(props.date) + await state.taskCompleted(props.track, props.date) } \ No newline at end of file diff --git a/client/src/components/TrackIcon.vue b/client/src/components/TrackIcon.vue index e5aeabf..273987e 100644 --- a/client/src/components/TrackIcon.vue +++ b/client/src/components/TrackIcon.vue @@ -1,12 +1,12 @@ diff --git a/client/src/state.ts b/client/src/state.ts index 028ae4b..67c0955 100644 --- a/client/src/state.ts +++ b/client/src/state.ts @@ -2,8 +2,6 @@ import { reactive } from "vue" import { Track } from "./track" import { Tick } from './ticks' import { error } from "./error" -import { getCookie } from "./util"; -import router from './router' enum State { Unfetched, @@ -11,6 +9,15 @@ enum State { Fetched, } +function dateQuery(date: Date): URLSearchParams { + let query = new URLSearchParams() + query.set("year", date.getUTCFullYear().toString()) + query.set("month", (date.getUTCMonth() + 1).toString()) + // good thing I still had this ^^^^^^^^^^^^^^ in mind when I wrote this 😬 + query.set("day", date.getUTCDate().toString()) + return query +} + interface LoggedInUser { name: string } @@ -24,8 +31,6 @@ class AppState { constructor() { this.tracks = new Array this.state = State.Unfetched - const name = getCookie("name") - if (name) this.user = { name } } streamUpdatesFromServer() { const source = new EventSource("/api/v1/updates") @@ -92,13 +97,36 @@ class AppState { if (this.state != State.Unfetched) return await this.repopulate() } - async logOut() { - const result = await fetch('/api/v1/auth', {method: 'DELETE'}) - if(!result.ok) return error('failed to log out') - this.user = undefined - router.push('/login') + async taskCompleted(track: Track, date: Date): Promise { + const query = dateQuery(date) + const response: Response = await fetch(`/api/v1/tracks/${track.id}/ticked?${query.toString()}`, { method: "PATCH" }) + const body = await response.text() + if (!response.ok) { + error(body) + throw new Error(`error setting tick for track ${track.id} ("${track.name}"): ${response.status} ${response.statusText}`) + } + return JSON.parse(body) + } + async taskMarkedIncomplete(track: Track, date: Date) { + const query = dateQuery(date) + const { ok, status, statusText } = await fetch(`/api/v1/tracks/${track.id}/all-ticks?${query.toString()}`, { method: 'DELETE' }) + if (!ok) + error(`error deleting ticks for ${track.id}: ${statusText} (${status})`) + } + async addTrack(track: Track): Promise { + const response = await fetch('/api/v1/tracks', { + method: "POST", + body: JSON.stringify(track), + headers: { "Content-Type": "application/json" } + }) + if (!response.ok) + error(`error submitting track: ${track}: ${response.statusText} (${response.status})`) + return response.ok + } + async removeTrack(trackID: number) { + const response = await fetch(`/api/v1/tracks/${trackID}`, { method: "DELETE" }) + if (!response.ok) error(`error deleting track with ID ${trackID}: ${response.statusText} (${response.status})`) } } - export const state = reactive(new AppState) diff --git a/client/src/track.ts b/client/src/track.ts index f7525ad..9893eca 100644 --- a/client/src/track.ts +++ b/client/src/track.ts @@ -1,6 +1,5 @@ import { error } from "./error" import { Tick, ITick } from './ticks' -import { dateQuery } from "./util" export interface ITrack { id?: number @@ -49,34 +48,6 @@ export class Track implements ITrack { this.fetchTicks = this.fetchTicks.bind(this) } - /** - * Add this track to the database. A `TrackAdded` event should have been - * received from the server on the event stream by the time this returns. - * - * @returns whether or not the query succeeded - */ - async create(): Promise { - // note that this.id is expected to be `undefined` here. - const response = await fetch('/api/v1/tracks', { - method: "POST", - body: JSON.stringify(this), - headers: { "Content-Type": "application/json" } - }) - if (!response.ok) - error(`error submitting track ${this.name}: ${response.statusText} (${response.status})`) - return response.ok - } - - async delete() { - const id = this.id - if (id) await Track.deleteById(id) - } - - static async deleteById(id: number) { - const response = await fetch(`/api/v1/tracks/${id}`, { method: "DELETE" }) - if (!response.ok) error(`error deleting track with ID ${id}: ${response.statusText} (${response.status})`) - } - static fromJSON(track: ITrack): Track { return new Track(track.id, track.name, track.description, track.icon, track.enabled, track.multiple_entries_per_day, track.color, track.order) } @@ -127,36 +98,4 @@ export class Track implements ITrack { } return [] } - /** - * Mark this track as being completed on the given date. A `TickAdded` event - * should have been received from the server on the event stream by the time - * this returns. - * - * @param date the date the task was completed - * @returns the decoded server API response - */ - async markComplete(date: Date) { - const query = dateQuery(date) - const response: Response = await fetch(`/api/v1/tracks/${this.id}/ticked?${query.toString()}`, { method: "PATCH" }) - const body = await response.text() - if (!response.ok) { - error(body) - throw new Error(`error setting tick for track ${this.id} ("${this.name}"): ${response.status} ${response.statusText}`) - } - return JSON.parse(body) - } - /** - * Mark this track as being incomplete on the given date. A `TickAdded` event - * should have been received from the server on the event stream by the time - * this returns. - * - * @param date the date the task was completed - * @returns the decoded server API response - */ - async markIncomplete(date: Date) { - const query = dateQuery(date) - const { ok, status, statusText } = await fetch(`/api/v1/tracks/${this.id}/all-ticks?${query.toString()}`, { method: 'DELETE' }) - if (!ok) - error(`error deleting ticks for ${this.id}: ${statusText} (${status})`) - } } diff --git a/client/src/util.ts b/client/src/util.ts deleted file mode 100644 index 023079c..0000000 --- a/client/src/util.ts +++ /dev/null @@ -1,17 +0,0 @@ -export function getCookie(key: string): string | null { - const start = document.cookie.indexOf(key + '=') - if(start === -1) return null - let end: number | undefined = document.cookie.indexOf(';', start) - if(end === -1) - end = undefined - return document.cookie.substring(start + key.length + 1, end) -} - -export function dateQuery(date: Date): URLSearchParams { - let query = new URLSearchParams() - query.set("year", date.getUTCFullYear().toString()) - query.set("month", (date.getUTCMonth() + 1).toString()) - // good thing I still had this ^^^^^^^^^^^^^^ in mind when I wrote this 😬 - query.set("day", date.getUTCDate().toString()) - return query -} diff --git a/client/src/views/Login.vue b/client/src/views/Login.vue index 84d09b7..9f21ab0 100644 --- a/client/src/views/Login.vue +++ b/client/src/views/Login.vue @@ -1,47 +1,39 @@ @@ -52,22 +44,22 @@ if(state.user?.name) router.push("/") - Name + Name - + Password - + diff --git a/client/src/views/NewTrackView.vue b/client/src/views/NewTrackView.vue index a15e26a..8b2ed62 100644 --- a/client/src/views/NewTrackView.vue +++ b/client/src/views/NewTrackView.vue @@ -2,6 +2,7 @@ import { RouterLink, useRouter } from 'vue-router'; import { Track } from '../track'; import { computed, ref } from 'vue'; +import { state } from '../state'; const props = defineProps<{ initialState?: Track }>() const router = useRouter() @@ -25,7 +26,7 @@ const submit = async () => { const track = new Track(undefined, name.value, description.value, icon.value, Number(enabled.value), Number(multipleEntriesPerDay.value), color.value, order.value) - if (await track.create()) + if (await state.addTrack(track)) router.push('/') } diff --git a/docker-compose_prod.yml b/docker-compose_prod.yml index 09483f7..71405e1 100644 --- a/docker-compose_prod.yml +++ b/docker-compose_prod.yml @@ -14,7 +14,7 @@ services: POSTGRES_USER: kalkutago POSTGRES_DB: kalkutago POSTGRES_HOST: database - secrets: [ postgres-password, cookie-secret ] + secrets: [ postgres-password ] depends_on: [ database ] volumes: - ./client/dist:/src/public:ro diff --git a/server/src/api/tracks.rs b/server/src/api/tracks.rs index 5d01ea5..b168c2d 100644 --- a/server/src/api/tracks.rs +++ b/server/src/api/tracks.rs @@ -32,7 +32,7 @@ async fn get_track_check_user( track_id: i32, user: &users::Model, ) -> Result, Either> { - if let Some(Some(track)) = user + if let Some(Some(user)) = user .find_related(Tracks) .filter(tracks::Column::Id.eq(track_id)) .one(db) @@ -40,7 +40,7 @@ async fn get_track_check_user( .transpose() .map(|it| it.ok()) { - Ok(Json(track)) + Ok(Json(user)) } else { Err(Left(Status::NotFound)) } diff --git a/server/src/api/update.rs b/server/src/api/update.rs index 7cacb7b..b68204d 100644 --- a/server/src/api/update.rs +++ b/server/src/api/update.rs @@ -95,7 +95,6 @@ impl Update { if receiver_count > 0 { trace!(receiver_count = receiver_count, update = as_serde!(self); "sending update"); let count = tx.send(self.clone())?; - trace!(count = count; "update sent"); } else { trace!("no update receivers, skipping message"); } diff --git a/server/src/entities/users.rs b/server/src/entities/users.rs index 57e0d6a..5805910 100644 --- a/server/src/entities/users.rs +++ b/server/src/entities/users.rs @@ -53,7 +53,7 @@ impl ActiveModel { pub fn new(name: impl AsRef, password: impl AsRef) -> error::Result { use sea_orm::ActiveValue::Set; let name = Set(name.as_ref().to_string()); - let password_hash = Set(hash(password.as_ref(), DEFAULT_COST)?); + let password_hash = Set(hash(password.as_ref(), DEFAULT_COST + 2)?); Ok(Self { name, password_hash, diff --git a/shell.nix b/shell.nix index f7556e2..be0c83f 100644 --- a/shell.nix +++ b/shell.nix @@ -2,16 +2,13 @@ { pkgs ? import {} }: pkgs.mkShell { - name = "kalkutago"; - nativeBuildInputs = with pkgs.buildPackages; [ - clang - yarn nodejs - openssl - python3 - python3Packages.requests - python3Packages.ipython - rustup - docker - gnumake + nativeBuildInputs = with pkgs.buildPackages; [ + clang + yarn nodejs + openssl + python3 + python3Packages.requests + python3Packages.ipython ]; } +