Commit graph

173 commits

Author SHA1 Message Date
3f492edea2 Separate rotation and expiry of sessions
If a session is rotate in the middle of a server side rendering then
some random portions of requests made on the server side will fail with
a session taken error as the server is not going to update the cookies
of the client during these requests.

To avoid this pitfall extend the expiry time of sessions to be 10
seconds after the session has been rotated.  This is accomplished by
introducing a new timestamp on sessions called the rotateAt at time
alongside the expiresAt time.  Sessions used after rotateAt that haven't
been rotated get rotated into a new session and the existing session
gets the expiresAt time set to 10 seconds in the future.  Sessions that
are past the expiredAt time have no access.

This makes the logic around session expiry simpler, and also makes it
possible to audit when a session got rotated, and to mark sessions as
expired without a chance to rotate to a new session without having to
resort to a finished flag.
2025-07-09 14:54:54 +02:00
352362b9c3 Ignore deleted users when looking up a user
After the change to converting users to tombstones instead of removing
them from the database several places would accidentally use deleted
user accounts instead of ignoring them.
2025-07-08 16:23:31 +02:00
f4e4dc9f11 Allow abandoning anonymous taken sessions
If an anonymous session is detected as taken the logic preventing the
session from being accidentally deleted would also prevent the user from
recovering from a taken anonymous session.
2025-07-08 16:13:46 +02:00
ebeedff5d0 Add error page for when a session has been taken
Describe to the user what it means when a session has been detected as
taken and provide a means to abandoned the session and log in again.
2025-07-08 16:13:46 +02:00
011687b391 Close event streams for expired sessions
When a session expires close any event streams that have been opened
with that session.  This prevents an attacker with a leaked session
cookie from opening a stream and receiving updates indefinitely without
being detected.

By sending the session the event stream is opened with when the stream
is established this closure on session expiry also serves as a way for
a user agent to be notified whenever its own access level changes.
2025-07-08 16:13:46 +02:00
2d5af78568 Update dependencies 2025-07-07 23:40:27 +02:00
ce1eab7ede Fix syntax error in .editorconfig 2025-07-07 22:51:15 +02:00
1775fac5fd Refactor sessions to frequently rotate
In order to minimise the window of opportunity to steal a session,
automatically rotate it onto a new session on a frequent basis.  This
makes a session cookie older than the automatic rollover time less
likely to grant access and more likely to be detected.

Should a stolen session cookie get rotated while the attacker is using
it, the user will be notificed that their session has been taken the
next time they open the app if the user re-visits the website before the
session is discarded.
2025-07-07 22:50:59 +02:00
d9b78bff69 Move container image to public/owltide
All checks were successful
/ build (push) Successful in 1m33s
/ deploy (push) Successful in 16s
2025-07-02 00:25:43 +02:00
4e6468adac Move the mascot above the title in README.md
All checks were successful
/ build (push) Successful in 1m42s
/ deploy (push) Successful in 16s
2025-07-01 19:51:28 +02:00
526a60c040 Add short-eared owl as the Owltide mascot and icon
All checks were successful
/ build (push) Successful in 2m29s
/ deploy (push) Successful in 26s
I've settled on a short-eared owl (Asio Flammeus) to serve as the mascot
and icon for Owltide. This is not a settled and final decision, but
rather a branding that's good enough for now.

To create the mascot I sketched some owls that I have saved to
/assets/mascot/owl-sketch.png and then selected one I liked.  Going
clockwise from the top right the sketches were based on the reference
photos [1], [2], and [3] found by searching for "owl" on Wikimedia
Commons.  I then lightly refined it to work as a small icon.

To avoid confusing terms that only make sense for software the artworks
are licensed under the Creative Commons Attribution-ShareAlike 4.0
License.  This has a similar spirit to the AGPL and ensures that should
improvements be made upon them they can be incorporated in the project.

[1]: https://commons.wikimedia.org/wiki/File:Athene_cunicularia_-near_Goiania,_Goias,_Brazil-8_edit.jpg
[2]: https://commons.wikimedia.org/wiki/File:Hibou_des_marais.jpg
[3]: https://commons.wikimedia.org/wiki/File:Uhu-muc.jpg
2025-07-01 19:41:59 +02:00
e6cda256ae Set html lang attribute to en 2025-07-01 18:48:01 +02:00
9592cd3160 Name the application Owltide
The name is inspired by the watchful owl perching from the tree tops
with complete overview of all that's going on combined with -tide in
the sense it's used for in words like summertide and eastertide.
2025-07-01 18:41:24 +02:00
1b5fd9f72a Group .license files in VsCode explorer
Add fileNesting rule to group the .license files with the files they
belong to.
2025-06-30 19:29:36 +02:00
e52972853d License under AGPL version 3 or later
I firmly believe in free software.

The application I'm making here have capabilities that I've not seen in
any system.  It presents itself as an opportunity to collaborate on a
tool that serves the people rather than corporations.  Whose incentives
are to help people rather, not make the most money.  And whose terms
ensure that these freedoms and incentives cannot be taken back or
subverted.

I license this software under the AGPL.
2025-06-30 18:58:24 +02:00
344b9acbf8 Render events without a location
All checks were successful
/ build (push) Successful in 1m31s
/ deploy (push) Successful in 16s
Show events that have no locations set. This is useful when drafting a
schedule and having events who's location have yet to be decided.
2025-06-30 16:39:51 +02:00
5144bf2b37 Allow shifts without a role
Allow a shift to have no role associated with it in order to simplify
conflict resolution around situations like a shift being created while
the role it was assoiated with was deleted. This also allows for shifts
that are freestanding to be created in case having a role doesn't make
sense for it.
2025-06-30 16:36:47 +02:00
1d2edf7535 Add dialog showing diff of changes to save
Add a save dialog at the bottom of the screen that is present whenever
there are unsaved changes.  This dialog provides a diff between the
client and server state so that the user can easily confirm the changes
they are about to make are the correct changes before applying them to
the server.
2025-06-30 15:43:15 +02:00
60f898e986 Set updatedAt in the past in demo schedule
Make sure that all of the entities generated in the demo schedule are
updated in the past. Otherwise updates for them will end up rejected
for being before the current entity's updateAt property.

This also sets the origin for the schedule to be yesterday to make it
easiser to demo the schedule between friday and tuesday.
2025-06-30 15:20:57 +02:00
f24dbae5eb Fix logic errors from mixing up client and server
All checks were successful
/ build (push) Successful in 1m33s
/ deploy (push) Successful in 16s
Fix discard and apiUpdate in Location, Role and Shift sometimes breaking
due to the field and serverField versions of the fields being mixed up.
2025-06-29 20:39:05 +02:00
f79f49b0f6 Fix schedule breaking on 2001-09-09 01:46:40Z
Array.sort() sorts by UTF-16 code points even when the items in the
array are numbers.  Fix the schedule breaking when events cross
different powers of 10 in Unix time which caused the junctions to no
longer be sorted by the numeric value of their Unix time.
2025-06-29 20:30:39 +02:00
27c4720328 Fix changes not being detected in SelectDropdown
When modifying the set instead of replacing it with a new set the change
detection logic in Vue.js doesn't properly propagate the change, causing
certain computed properties that depend on them to go stale.

Fix by creating a new set here, which will emit a modelValue:update
event which will propagate through the v-model bindings.
2025-06-29 20:26:32 +02:00
0c5b4c756f Make the first user created an admin
All checks were successful
/ build (push) Successful in 1m31s
/ deploy (push) Successful in 16s
To easily bootstrap of administration of the system make the first
regular user account created into an admin account.
2025-06-28 01:30:39 +02:00
e5e923bc8d Implement database administration
Add routes and admin panel elements for creating a database backup,
restoring from a backup, deleting the existing schedule, and replacing
the database with the demo schedule.  These server as crude ways to
manage the data stored in the system.
2025-06-28 01:30:39 +02:00
b2f48e98e0 Add API utility for requiring an admin session 2025-06-28 00:55:26 +02:00
b0d5cdf791 Refactor slot editing to use searchable selections
All checks were successful
/ build (push) Successful in 1m36s
/ deploy (push) Successful in 16s
Instead of having to type in exactly the name of events or shifts and
then hope you remembered it right, replace these interactions with the
custom select component that gives a complete list of the available
choices and allows quickly searching for the right one.
2025-06-27 18:59:23 +02:00
da65103e05 Use toRaw when calling Set.difference
Vue's proxy handling interferres with the new Set methods causing them
to throw a TypeError when used on Set objects that are replaced by
proxies. Workaround by dropping the proxy when using these.

See https://github.com/vuejs/core/issues/11398
2025-06-27 18:52:16 +02:00
73b28834a1 Allow orphaned event and shit slots
When editing the slots of events and shifts there are certain situations
where the event or shift a slot should belong to becomes unclear or
difficult to reliably assign.  For example when adding a new slot in the
UI it may be desirable to do so before the user has input the event
or shift the slot should belong to.

In these cases, not being able to store the slot into the schedule makes
the UI logic needlessly complicated.  Allow slots to be added that do
not have its assiated relation linked up to make editing and handling
such scenarios easier.
2025-06-27 18:34:37 +02:00
d49ed38185 Add custom select component
The selection of locations, events, roles, shifts and users using the
native <select> element makes for awkward and difficult interactions.
Add an alternative select control that fixes the issues with the poor
handling and navigation of the control when having many options.

The custom select component can handle the selection of either one or
many entity from a ClientMap of entiteis with a name. Typing into the
text box searches the entities by name, arrow keys can navigate and
enter confirms the chosen entity by toggling it's presence in the
selection.
2025-06-27 18:20:24 +02:00
3f9f218ed0 Add composable for string sort and search
To avoid hydration matches string operations need to do the same thing
on the client and server. Add utilities for performing sorting and fuzzy
matching of strings that uses the account's locale.
2025-06-27 18:13:51 +02:00
016930f933 Render overlapping events/shifts in separate rows
All checks were successful
/ build (push) Successful in 1m33s
/ deploy (push) Successful in 15s
Instead of merging overlapping events and shifts when displaying them in
the timetable which causes a very confusing display, add new rows when
events overlap so that each event can be fully displayed without any
overlapping in the table.
2025-06-25 15:38:47 +02:00
5662b890de Fix userStore fetching when not allowed
All checks were successful
/ build (push) Successful in 1m32s
/ deploy (push) Successful in 26s
Fix userStore attempting to fetch the users list when it's either not
logged in or not allowed to fetch the users, causing the schedule page
to not render.
2025-06-24 16:18:28 +02:00
e01e162367 Add About page with info about the project
Provide a rudimentary description of what this project does, existing
features and future planned work.
2025-06-24 16:00:55 +02:00
9299fa2682 Add cards for shifts 2025-06-24 15:41:53 +02:00
afd7aeea04 De-duplicate serverUserToApi
All checks were successful
/ build (push) Successful in 1m32s
/ deploy (push) Successful in 15s
2025-06-24 15:31:47 +02:00
985b8e0950 Refactor base types for entities and tombstones
Rename the base Entity type to ApiEntity, and the base EntityToombstone
to ApiTombstone to better reflect the reality that its only used in the
API interface and that the client and server types uses its own base if
any.

Remove EntityLiving and pull EntityTombstone out of of the base entity
type so that the types based on ApiEntity are always living entities and
if it's possible for it to contain tombstone this will be explicitly
told with the type including a union with ApiTombstone.

Refactor the types of the ClientEntity and ClientMap to better reflect
the types of the entities it stores and converts to/from.
2025-06-24 15:19:11 +02:00
e3ff872b5c Refactor ClientSchedule to mutable types
All checks were successful
/ build (push) Successful in 1m30s
/ deploy (push) Successful in 16s
Use a single mutable location, event, slot, etc, for each unique
resource that keeps track of the local editable client copy and
the server copy of the data contained in it.

This makes it much simpler to update these data structures as I can take
advantage of the v-model bindings in Vue.js and work with the system
instead of against it.
2025-06-24 00:07:18 +02:00
d48fb035b4 Remove type from Api serialisation of ClientMap
All checks were successful
/ build (push) Successful in 1m34s
/ deploy (push) Successful in 16s
Move the logic that converts the EntityClass of a map to a string and
then back into the class to the payload plugin in order to avoid a
circular dependency where the ClientMap needs to know the entity classes
and the entity classes needs to know the ClientMap.

The only place that doesn't know the type of the entities stored in the
client map is the payload reviver, so it makes sense to keep this logic
contained to the payload plugin.
2025-06-23 18:17:23 +02:00
930d93a95f Fix typo in deleteDatabase function 2025-06-23 12:55:06 +02:00
b1053a95ba Fix import statements
Remove unused or unneeded imports and change imports of luxon APIs to
use the wrapper.
2025-06-23 12:54:09 +02:00
87525a6ef5 Add admin page that can edit users
All checks were successful
/ build (push) Successful in 1m30s
/ deploy (push) Successful in 16s
Add admin page that's only accessible to admins with a listing of users
and the ability to edit the access types of those users.
2025-06-23 00:28:59 +02:00
3be7f8be05 Refactor user storage and update
Rename accounts to users to be consistent with the new naming scheme
where account only referes to the logged in user of the session and
implement live updates of users via a user store which listens for
updates from the event stream.
2025-06-23 00:28:58 +02:00
6336ccdb96 Implement mutable mapping for client entities
Create a simple Map like class for storing and keeping track of client
entities that are synced from the server and have local editable state.
This will form the basis for storing entities on the client and should
replace the immutable concept used be the ClientSchedule class.
2025-06-23 00:28:58 +02:00
5edea4dd72 Implement ClientUser based on a new concept
Create a new mutable ClientEntity type and implement ClientUser on top
of it. The mutable concept is intended to replace the immutable concept
used by the ClientSchedule entities as updating immutable types in a
deep interconnected structure is a lot of hassle for little benefit.
2025-06-23 00:28:58 +02:00
ebf7bdcc9c Add line indicating now to Timetable
All checks were successful
/ build (push) Successful in 1m33s
/ deploy (push) Successful in 16s
Add a red vertical line indicated the current time and date in the
timetable with the label "now" on top of it.
2025-06-18 18:17:03 +02:00
bea8e77742 Shift hour display to be on the lines
The hour headers indicate the time on the left line of the cell, this
makes them confusing to read. Shift the displayed hour to be in the
middle of the left line of the cell so that it is clear which line is
the start of which hour.
2025-06-18 15:13:18 +02:00
a087172d39 Add vue-tsc for running TypeScript typechecks
All checks were successful
/ build (push) Successful in 2m57s
/ deploy (push) Successful in 17s
The nuxi typecheck command requires vue-tsc to work.  Add it to the
project so the command doesn't ask to install it.
2025-06-18 01:02:58 +02:00
e821100987 Add back wrongly removed shiftSlotFilter 2025-06-18 01:00:51 +02:00
bfb9acaa78 Use a stable scroll gutter
All checks were successful
/ build (push) Successful in 1m32s
/ deploy (push) Successful in 16s
Prevent horizontal page shifts when switching tabs due to the scrollbar
changing visibility.
2025-06-18 00:24:51 +02:00
6ef3800a53 Organise edit page into tabs
Use tabs for the various sections on the edit page so that the schedule
timetable is more easily visible at the same time as the editable tables.
2025-06-18 00:24:45 +02:00