Remodeled database and api to prepare for move
This commit is contained in:
12
components/calendar/Dashboard.vue
Normal file
12
components/calendar/Dashboard.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
const route = useRoute()
|
||||
const worldId = route.params.id
|
||||
|
||||
const { data: calendar } = await useLazyFetch(`/api/calendars/query?world_id=${worldId}`)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<pre>
|
||||
{{ calendar }}
|
||||
</pre>
|
||||
</template>
|
||||
55
components/profile/Dashboard.vue
Normal file
55
components/profile/Dashboard.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<script lang="ts" setup>
|
||||
const user = useSupabaseUser()
|
||||
|
||||
const { data: worlds } = await useLazyFetch('/api/worlds')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="p-8">
|
||||
<Heading>{{ user?.user_metadata.full_name }}</Heading>
|
||||
|
||||
<section v-if="worlds?.data" class="mt-4">
|
||||
<h2 class="mb-4 text-lg font-bold">
|
||||
Mondes
|
||||
</h2>
|
||||
|
||||
<ul class="grid md:grid-cols-3 gap-2">
|
||||
<li v-for="(world, i) in worlds.data" :key="i">
|
||||
<UiCard
|
||||
class="w-full transition-all"
|
||||
:link="`/calendar/${world.id}`"
|
||||
:class="{
|
||||
'hover:bg-slate-50 dark:hover:bg-sky-950 dark:focus-within:outline-sky-900': !world.color,
|
||||
'bg-red-100 dark:bg-red-950 border-red-200 hover:bg-red-50 dark:hover:bg-red-900 dark:border-red-900 dark:focus-within:outline-red-900': world.color === 'red',
|
||||
'bg-orange-100 dark:bg-orange-950 border-orange-200 hover:bg-orange-50 dark:hover:bg-orange-900 dark:border-orange-900 dark:focus-within:outline-orange-900': world.color === 'orange',
|
||||
'bg-amber-100 dark:bg-amber-950 border-amber-200 hover:bg-amber-50 dark:hover:bg-amber-900 dark:border-amber-900 dark:focus-within:outline-amber-900': world.color === 'amber',
|
||||
'bg-yellow-100 dark:bg-yellow-950 border-yellow-200 hover:bg-yellow-50 dark:hover:bg-yellow-900 dark:border-yellow-900 dark:focus-within:outline-yellow-900': world.color === 'yellow',
|
||||
'bg-lime-100 dark:bg-lime-950 border-lime-200 hover:bg-lime-50 dark:hover:bg-lime-900 dark:border-lime-900 dark:focus-within:outline-lime-900': world.color === 'lime',
|
||||
' bg-green-100 dark:bg-green-950 border-green-200 hover:bg-green-50 dark:hover:bg-green-900 dark:border-green-900 dark:focus-within:outline-green-900': world.color === 'green',
|
||||
'bg-emerald-100 dark:bg-emerald-950 border-emerald-200 hover:bg-emerald-50 dark:hover:bg-emerald-900 dark:border-emerald-900 dark:focus-within:outline-emerald-900': world.color === 'emerald',
|
||||
'bg-teal-100 dark:bg-teal-950 border-teal-200 hover:bg-teal-50 dark:hover:bg-teal-900 dark:border-teal-900 dark:focus-within:outline-teal-900': world.color === 'teal',
|
||||
'bg-cyan-100 dark:bg-cyan-950 border-cyan-200 hover:bg-cyan-50 dark:hover:bg-cyan-900 dark:border-cyan-900 dark:focus-within:outline-cyan-900': world.color === 'cyan',
|
||||
'bg-sky-100 dark:bg-sky-950 border-sky-200 hover:bg-sky-50 dark:hover:bg-sky-900 dark:border-sky-900 dark:focus-within:outline-sky-900': world.color === 'sky',
|
||||
'bg-blue-100 dark:bg-blue-950 border-blue-200 hover:bg-blue-50 dark:hover:bg-blue-900 dark:border-blue-900 dark:focus-within:outline-blue-900': world.color === 'blue',
|
||||
'bg-indigo-100 dark:bg-indigo-950 border-indigo-200 hover:bg-indigo-50 dark:hover:bg-indigo-900 dark:border-indigo-900 dark:focus-within:outline-indigo-900': world.color === 'indigo',
|
||||
'bg-violet-100 dark:bg-violet-950 border-violet-200 hover:bg-violet-50 dark:hover:bg-violet-900 dark:border-violet-900 dark:focus-within:outline-violet-900': world.color === 'violet',
|
||||
'bg-purple-100 dark:bg-purple-950 border-purple-200 hover:bg-purple-50 dark:hover:bg-purple-900 dark:border-purple-900 dark:focus-within:outline-purple-900': world.color === 'purple',
|
||||
'bg-fuchsia-100 dark:bg-fuchsia-950 border-fuchsia-200 hover:bg-fuchsia-50 dark:hover:bg-fuchsia-900 dark:border-fuchsia-900 dark:focus-within:outline-fuchsia-900': world.color === 'fuchsia',
|
||||
'bg-pink-100 dark:bg-pink-950 border-pink-200 hover:bg-pink-50 dark:hover:bg-pink-900 dark:border-pink-900 dark:focus-within:outline-pink-900': world.color === 'pink',
|
||||
'bg-pink-100 dark:bg-rose-950 border-rose-200 hover:bg-rose-50 dark:hover:bg-rose-900 dark:border-rose-900 dark:focus-within:outline-rose-900': world.color === 'rose',
|
||||
'text-slate-100 bg-slate-900 border-slate-700 hover:bg-slate-700 dark:hover:bg-slate-800 dark:border-slate-900 dark:focus-within:outline-slate-900': world.color === 'black',
|
||||
' hover:bg-slate-50 dark:hover:text-slate-900 dark:hover:bg-slate-300 dark:border-slate-500 dark:focus-within:outline-slate-100': world.color === 'white',
|
||||
}"
|
||||
>
|
||||
<UiCardHeader>
|
||||
<UiCardTitle>{{ world.name }}</UiCardTitle>
|
||||
</UiCardHeader>
|
||||
<UiCardContent>
|
||||
<p class="italic">{{ world.description }}</p>
|
||||
</UiCardContent>
|
||||
</UiCard>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
@@ -1,9 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
const user = useSupabaseUser()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-8">
|
||||
<Heading>{{ user?.user_metadata.full_name }}</Heading>
|
||||
</div>
|
||||
</template>
|
||||
@@ -12,7 +12,7 @@ const props = defineProps<{
|
||||
<div
|
||||
:class="
|
||||
cn('rounded-lg border bg-card text-card-foreground shadow-sm transition-all', props.class, {
|
||||
'relative outline outline-2 outline-offset-4 outline-transparent hover:bg-sky-950 hover:-translate-y-[.2rem] focus-within:outline-sky-900':
|
||||
'relative outline outline-2 outline-offset-4 outline-transparent hover:-translate-y-[.2rem]':
|
||||
props.link
|
||||
})
|
||||
"
|
||||
|
||||
6
models/CalendarConfig.ts
Normal file
6
models/CalendarConfig.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface CalendarConfig {
|
||||
months: string[]
|
||||
monthsPerYear: number
|
||||
daysPerMonth: number
|
||||
// todayDate: any
|
||||
}
|
||||
7
models/World.ts
Normal file
7
models/World.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface World {
|
||||
id: number
|
||||
uuid: string
|
||||
name: string
|
||||
description?: string
|
||||
color?: string
|
||||
}
|
||||
@@ -28,7 +28,8 @@
|
||||
"tailwind-merge": "^2.3.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vue": "^3.4.27",
|
||||
"vue-router": "^4.3.2"
|
||||
"vue-router": "^4.3.2",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/color-mode": "^3.4.1",
|
||||
|
||||
30
pages/calendar/[id].vue
Normal file
30
pages/calendar/[id].vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts" setup>
|
||||
import type { MenuItem } from '~/components/global/SidebarProps';
|
||||
|
||||
const user = useSupabaseUser()
|
||||
|
||||
useHead({
|
||||
title: 'Calendrier'
|
||||
})
|
||||
|
||||
definePageMeta({
|
||||
middleware: ['auth-guard']
|
||||
})
|
||||
|
||||
// Redirect user back home when they log out on the page
|
||||
watch(user, (n, _o) => {
|
||||
if (!n) {
|
||||
navigateTo('/')
|
||||
}
|
||||
})
|
||||
|
||||
const sidebarMenu: MenuItem[] = []
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="h-full grid grid-cols-[auto_1fr]">
|
||||
<Sidebar :menu-items="sidebarMenu" />
|
||||
|
||||
<CalendarDashboard />
|
||||
</div>
|
||||
</template>
|
||||
@@ -13,7 +13,7 @@ const sidebarMenu: MenuItem[] = []
|
||||
<Sidebar :menu-items="sidebarMenu" is-home />
|
||||
|
||||
<div class="h-full grid place-items-center">
|
||||
<UiCard class="w-1/3 min-w-72" link="/calendar">
|
||||
<UiCard class="w-1/3 min-w-72 hover:bg-sky-950 focus-within:outline-sky-900" link="/calendar">
|
||||
<UiCardHeader>
|
||||
<UiCardTitle>Calendrier</UiCardTitle>
|
||||
<UiCardDescription>
|
||||
|
||||
@@ -21,9 +21,9 @@ const sidebarMenu: MenuItem[] = []
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="h-full grid grid-cols-[auto_1fr]">
|
||||
<div class="h-full grid grid-cols-[auto_1fr]">
|
||||
<Sidebar :menu-items="sidebarMenu" />
|
||||
|
||||
<ProfileLayout />
|
||||
</main>
|
||||
<ProfileDashboard />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -56,6 +56,9 @@ importers:
|
||||
vue-router:
|
||||
specifier: ^4.3.2
|
||||
version: 4.3.2(vue@3.4.27(typescript@5.4.5))
|
||||
zod:
|
||||
specifier: ^3.23.8
|
||||
version: 3.23.8
|
||||
devDependencies:
|
||||
'@nuxtjs/color-mode':
|
||||
specifier: ^3.4.1
|
||||
@@ -4734,6 +4737,9 @@ packages:
|
||||
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
zod@3.23.8:
|
||||
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@alloc/quick-lru@5.2.0': {}
|
||||
@@ -10202,3 +10208,5 @@ snapshots:
|
||||
archiver-utils: 5.0.2
|
||||
compress-commons: 6.0.2
|
||||
readable-stream: 4.5.2
|
||||
|
||||
zod@3.23.8: {}
|
||||
|
||||
23
server/api/calendars/query.get.ts
Normal file
23
server/api/calendars/query.get.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { serverSupabaseClient } from "#supabase/server";
|
||||
import { z } from 'zod'
|
||||
|
||||
const querySchema = z.object({
|
||||
world_id: z.number({ coerce: true }).positive().int()
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const client = await serverSupabaseClient(event)
|
||||
const query = await getValidatedQuery(event, querySchema.parse)
|
||||
|
||||
const output = client
|
||||
.from('world_calendars')
|
||||
.select(`
|
||||
id,
|
||||
months,
|
||||
days_per_year,
|
||||
events (*)
|
||||
`)
|
||||
.eq('world_id', query.world_id)
|
||||
|
||||
return output
|
||||
})
|
||||
@@ -1,252 +0,0 @@
|
||||
export default defineEventHandler(() => {
|
||||
return [
|
||||
/**
|
||||
* Player characters
|
||||
*/
|
||||
// "Les Milles Cages"
|
||||
{
|
||||
name: 'Sulvan Trois-Barbes',
|
||||
birth: { day: 20, month: 3, year: 3169 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Sulvan_Trois-Barbes',
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['criminel', 'étincelle']
|
||||
},
|
||||
{
|
||||
name: 'Vascylly',
|
||||
birth: { day: 3, month: 5, year: 3181 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Vascylly',
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Tara Belyus',
|
||||
birth: { day: 14, month: 9, year: 3186 },
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
// "Les Cloches de Cantane"
|
||||
{
|
||||
name: 'Malik',
|
||||
birth: { day: 22, month: 8, year: 3181 },
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['scientifique']
|
||||
},
|
||||
{
|
||||
name: 'Elie',
|
||||
birth: { day: 5, month: 6, year: 3192 },
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['ecclésiastique']
|
||||
},
|
||||
{
|
||||
name: 'Clayron',
|
||||
birth: { day: 3, month: 5, year: 3178 },
|
||||
category: 'joueur',
|
||||
secondaryCategories: ['sentinelle']
|
||||
},
|
||||
|
||||
/**
|
||||
* NPC
|
||||
*/
|
||||
// Counts of the Alliance
|
||||
{
|
||||
name: 'Alfrid de Lagarde',
|
||||
birth: { day: 29, month: 7, year: 3193 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Alfrid_de_Lagarde',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['sentinelle']
|
||||
},
|
||||
{
|
||||
name: 'Alaric de Lagarde',
|
||||
birth: { day: 23, month: 8, year: 3192 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Alaric_de_Lagarde',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['sentinelle']
|
||||
},
|
||||
{
|
||||
name: 'Anastael III de Quillon',
|
||||
birth: { day: 1, month: 5, year: 3184 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Anastael_III_de_Quillon',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['sentinelle', 'buse blanche']
|
||||
},
|
||||
{
|
||||
name: 'Antoine de Mireloin',
|
||||
birth: { day: 31, month: 5, year: 3190 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Antoine_de_Mireloin',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Armance Ronchère',
|
||||
birth: { day: 1, month: 1, year: 3132 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Armance_Ronchère',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Baranne Rougefer',
|
||||
birth: { day: 1, month: 0, year: 3175 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Baranne_Rougefer',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['sentinelle']
|
||||
},
|
||||
{
|
||||
name: 'Béatrice II de Grandlac',
|
||||
birth: { day: 21, month: 4, year: 3158 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Béatrice_II_de_Grandlac',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Favio Asharos-Losantelle',
|
||||
birth: { day: 17, month: 3, year: 3091 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Favio_Asharos-Losantelle',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Firmin de Montardieu',
|
||||
birth: { day: 9, month: 2, year: 3203 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Firmin_de_Montardieu',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Laura de Montardieu',
|
||||
birth: { day: 32, month: 3, year: 3167 },
|
||||
death: { day: 1, month: 4, year: 3217 },
|
||||
hiddenDeath: true,
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Laura_de_Montardieu',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Lazarus Tymos',
|
||||
birth: { day: 29, month: 9, year: 3145 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Lazarus_Tymos',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['mage', 'buse blanche']
|
||||
},
|
||||
{
|
||||
name: 'Marion de Corambre',
|
||||
birth: { day: 14, month: 7, year: 3190 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Marion_de_Corambre',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['scientifique', 'ecclésiastique']
|
||||
},
|
||||
{
|
||||
name: 'Relforg Pergaré',
|
||||
birth: { day: 18, month: 9, year: 3182 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Relforg_Pergaré',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Vilgarde de Ternâcre',
|
||||
birth: { day: 3, month: 3, year: 2998 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Vilgarde_de_Ternâcre',
|
||||
category: 'comte'
|
||||
},
|
||||
{
|
||||
name: 'Ysildy Milopée',
|
||||
birth: { day: 3, month: 1, year: 3187 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Ysildy_Milopée',
|
||||
category: 'comte',
|
||||
secondaryCategories: ['mage', 'scientifique', 'professeur']
|
||||
},
|
||||
|
||||
// Sparks
|
||||
{
|
||||
name: 'Izàc Tymos',
|
||||
birth: { day: 13, month: 6, year: 3192 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Izàc_Tymos',
|
||||
category: 'mage',
|
||||
secondaryCategories: ['étincelle', 'criminel', 'professeur', 'scientifique']
|
||||
},
|
||||
{
|
||||
name: 'Tvernée',
|
||||
birth: { day: 19, month: 2, year: 3205 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Tvernée',
|
||||
category: 'étincelle',
|
||||
secondaryCategories: ['criminel']
|
||||
},
|
||||
|
||||
// Pirates
|
||||
{
|
||||
name: 'Räzal',
|
||||
birth: { day: 13, month: 8, year: 3178 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Räzal'
|
||||
},
|
||||
|
||||
// Legends
|
||||
{
|
||||
name: 'Jorhas Kirendre',
|
||||
birth: { day: 2, month: 9, year: -452 },
|
||||
death: { day: 2, month: 2, year: -419 },
|
||||
category: 'sentinelle',
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Jorhas_Kirendre'
|
||||
},
|
||||
|
||||
// "Les Milles Cages"
|
||||
{
|
||||
name: 'Ernestin Pomel',
|
||||
birth: { day: 11, month: 2, year: 3179 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Ernestin_Pomel',
|
||||
category: 'mage'
|
||||
},
|
||||
{
|
||||
name: 'Quacille Lévios',
|
||||
birth: { day: 3, month: 6, year: 3162 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Quacille_Lévios',
|
||||
category: 'professeur',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Morel Lévios',
|
||||
birth: { day: 26, month: 3, year: 3157 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Morel_Lévios',
|
||||
category: 'sentinelle',
|
||||
secondaryCategories: ['mage', 'buse blanche']
|
||||
},
|
||||
{
|
||||
name: 'Anastael Simon',
|
||||
birth: { day: 32, month: 2, year: 3166 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Anastael_Simon',
|
||||
category: 'étincelle',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Grestain',
|
||||
birth: { day: 9, month: 2, year: 3162 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Grestain',
|
||||
category: 'criminel',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Lucien Malanth',
|
||||
birth: { day: 31, month: 4, year: 3167 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Lucien_Malanth',
|
||||
category: 'criminel',
|
||||
secondaryCategories: ['mage']
|
||||
},
|
||||
{
|
||||
name: 'Tivian Rodhus',
|
||||
birth: { day: 13, month: 3, year: 3157 },
|
||||
wiki: 'https://alexcreates.fr/leim/index.php/Tivian_Rodhus',
|
||||
category: 'professeur',
|
||||
secondaryCategories: ['mage', 'criminel']
|
||||
},
|
||||
|
||||
// "Les Cloches de Cantane"
|
||||
{
|
||||
name: 'Bénédicte Vaht',
|
||||
description: "Moine d'Ikami ayant entrepris la construction du Pilier Blanc de Cantane",
|
||||
birth: { day: 28, month: 6, year: 3139 },
|
||||
category: 'ecclésiastique'
|
||||
},
|
||||
{
|
||||
name: 'Taleb Vaht',
|
||||
description:
|
||||
"Fils de Bénédicte, il s'est engagé auprès des Étincelles comme alchimiste artificier.",
|
||||
birth: { day: 9, month: 1, year: 3180 },
|
||||
death: { day: 28, month: 7, year: 3209 },
|
||||
category: 'étincelle',
|
||||
secondaryCategories: ['scientifique']
|
||||
}
|
||||
]
|
||||
})
|
||||
18
server/api/characters/index.get.ts
Normal file
18
server/api/characters/index.get.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { serverSupabaseClient } from "#supabase/server";
|
||||
import type { Character } from "~/models/Characters";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const client = await serverSupabaseClient(event)
|
||||
|
||||
return client.from('characters')
|
||||
.select(`
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
birth,
|
||||
death,
|
||||
wiki,
|
||||
characters_category (id, name)
|
||||
`)
|
||||
.returns<Character[]>()
|
||||
})
|
||||
29
server/api/characters/query.get.ts
Normal file
29
server/api/characters/query.get.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { serverSupabaseClient } from "#supabase/server";
|
||||
import { z } from 'zod'
|
||||
|
||||
const querySchema = z.object({
|
||||
world_id: z.number({ coerce: true }).positive().int()
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const client = await serverSupabaseClient(event)
|
||||
const query = await getValidatedQuery(event, querySchema.parse)
|
||||
|
||||
const output = client
|
||||
.from('characters')
|
||||
.select(`
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
birth,
|
||||
death,
|
||||
wiki,
|
||||
characters_category (id, name)
|
||||
`)
|
||||
|
||||
if (query.world_id) {
|
||||
output.eq('world_id', query.world_id)
|
||||
}
|
||||
|
||||
return output
|
||||
})
|
||||
23
server/api/events/query.get.ts
Normal file
23
server/api/events/query.get.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { serverSupabaseClient } from "#supabase/server";
|
||||
import { z } from 'zod'
|
||||
|
||||
const querySchema = z.object({
|
||||
world_id: z.number({ coerce: true }).positive().int()
|
||||
})
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const client = await serverSupabaseClient(event)
|
||||
const query = await getValidatedQuery(event, querySchema.parse)
|
||||
|
||||
const output = client
|
||||
.from('events')
|
||||
.select(`
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
world_calendars (id, world_id)
|
||||
`)
|
||||
.eq('world_calendars.world_id', query.world_id)
|
||||
|
||||
return output
|
||||
})
|
||||
10
server/api/worlds/index.get.ts
Normal file
10
server/api/worlds/index.get.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { serverSupabaseClient } from "#supabase/server";
|
||||
import type { World } from "~/models/World";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const client = await serverSupabaseClient(event)
|
||||
|
||||
return client.from('worlds')
|
||||
.select(`id, name, description, color`)
|
||||
.returns<World[]>()
|
||||
})
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
-- Custom types
|
||||
create type public.app_permission as enum ('events.see.hidden', 'users.ban');
|
||||
create type public.app_role as enum ('sa', 'gm');
|
||||
create type public.app_role as enum ('sa', 'admin', 'moderator', 'user');
|
||||
create type public.app_colors as enum ('red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose', 'black', 'white');
|
||||
|
||||
-- USERS
|
||||
-- DATA STRUCTURES
|
||||
-- Users
|
||||
create table public.users (
|
||||
id uuid references auth.users not null primary key, -- UUID from auth.users
|
||||
username text
|
||||
@@ -14,16 +16,16 @@ create table public.users (
|
||||
comment on table public.users is 'Profile data for each user.';
|
||||
comment on column public.users.id is 'References the internal Supabase Auth user.';
|
||||
|
||||
-- USER ROLES
|
||||
-- Roles
|
||||
create table public.user_roles (
|
||||
id bigint generated by default as identity primary key,
|
||||
user_id uuid references public.users on delete cascade not null,
|
||||
role app_role not null,
|
||||
user_id uuid references public.users on delete cascade not null,
|
||||
unique (user_id, role)
|
||||
);
|
||||
comment on table public.user_roles is 'Application roles for each user.';
|
||||
|
||||
-- ROLE PERMISSIONS
|
||||
-- Permissions
|
||||
create table public.role_permissions (
|
||||
id bigint generated by default as identity primary key,
|
||||
role app_role not null,
|
||||
@@ -32,6 +34,70 @@ create table public.role_permissions (
|
||||
);
|
||||
comment on table public.role_permissions is 'Application permissions for each role.';
|
||||
|
||||
-- Worlds
|
||||
create table public.worlds (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
description text,
|
||||
color app_colors,
|
||||
gm_id uuid references public.users on delete cascade
|
||||
);
|
||||
comment on table public.worlds is 'Worlds belonging to a single user ; a game master.';
|
||||
|
||||
-- World Calendars
|
||||
create table public.world_calendars (
|
||||
id bigint generated by default as identity primary key,
|
||||
months text array,
|
||||
days_per_year int not null,
|
||||
world_id bigint references public.worlds on delete cascade not null
|
||||
);
|
||||
comment on table public.world_calendars is 'Calendar settings and configuration attached to a single world.';
|
||||
|
||||
-- Event categories
|
||||
create table public.events_category (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
unique (name)
|
||||
);
|
||||
comment on table public.events_category is 'Categories describing events';
|
||||
|
||||
-- Events
|
||||
create table public.events (
|
||||
id bigint generated by default as identity primary key,
|
||||
title text not null,
|
||||
description text,
|
||||
start_date json not null,
|
||||
end_date json,
|
||||
category bigint references public.events_category on delete cascade,
|
||||
hidden boolean,
|
||||
wiki text,
|
||||
calendar_id bigint references public.world_calendars on delete cascade not null
|
||||
);
|
||||
comment on table public.events is 'Events linked to a world';
|
||||
|
||||
-- Character categories
|
||||
create table public.characters_category (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
unique (name)
|
||||
);
|
||||
comment on table public.characters_category is 'Categories describing characters';
|
||||
|
||||
-- Characters
|
||||
create table public.characters (
|
||||
id bigint generated by default as identity primary key,
|
||||
name text not null,
|
||||
description text,
|
||||
birth json not null,
|
||||
death json,
|
||||
category bigint references public.characters_category on delete cascade,
|
||||
hidden_birth boolean,
|
||||
hidden_death boolean,
|
||||
wiki text,
|
||||
world_id bigint references public.worlds on delete cascade not null
|
||||
);
|
||||
comment on table public.characters is 'Characters linked to a world';
|
||||
|
||||
-- authorize with role-based access control (RBAC)
|
||||
create function public.authorize(
|
||||
requested_permission app_permission
|
||||
@@ -54,11 +120,84 @@ $$ language plpgsql security definer set search_path = public;
|
||||
alter table public.users enable row level security;
|
||||
alter table public.user_roles enable row level security;
|
||||
alter table public.role_permissions enable row level security;
|
||||
alter table public.worlds enable row level security;
|
||||
alter table public.world_calendars enable row level security;
|
||||
alter table public.events_category enable row level security;
|
||||
alter table public.characters_category enable row level security;
|
||||
alter table public.characters enable row level security;
|
||||
alter table public.events enable row level security;
|
||||
|
||||
-- User policies
|
||||
create policy "Allow logged-in read access" on public.users for select using ( auth.role() = 'authenticated' );
|
||||
create policy "Allow individual insert access" on public.users for insert with check ( auth.uid() = id );
|
||||
create policy "Allow individual update access" on public.users for update using ( auth.uid() = id );
|
||||
create policy "Allow individual read access" on public.user_roles for select using ( auth.uid() = user_id );
|
||||
|
||||
-- World policies
|
||||
create policy "Allow individual read access for GMs" on public.worlds for select using ( ( auth.uid() = gm_id ) );
|
||||
create policy "Allow individual insert access for GMs" on public.worlds for insert with check ( auth.uid() = gm_id );
|
||||
create policy "Allow individual update access for GMs" on public.worlds for update using ( auth.uid() = gm_id );
|
||||
|
||||
-- Calendar policies
|
||||
create policy "Allow individual read access for GMs" on public.world_calendars for select using (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = world_calendars.world_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual insert access for GMs" on public.world_calendars for insert with check (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = world_calendars.world_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual update access for GMs" on public.world_calendars for update with check (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = world_calendars.world_id
|
||||
)
|
||||
);
|
||||
|
||||
-- Event policies
|
||||
create policy "Allow individual read access for GMs" on public.events for select using (
|
||||
exists (
|
||||
select 1 from world_calendars
|
||||
where world_calendars.id = events.calendar_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual insert access for GMs" on public.events for insert with check (
|
||||
exists (
|
||||
select 1 from world_calendars
|
||||
where world_calendars.id = events.calendar_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual update access for GMs" on public.events for update with check (
|
||||
exists (
|
||||
select 1 from world_calendars
|
||||
where world_calendars.id = events.calendar_id
|
||||
)
|
||||
);
|
||||
|
||||
-- Character policies
|
||||
create policy "Allow individual read access for GMs" on public.characters for select using (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = characters.world_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual insert access for GMs" on public.characters for insert with check (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = characters.world_id
|
||||
)
|
||||
);
|
||||
create policy "Allow individual update access for GMs" on public.characters for update with check (
|
||||
exists (
|
||||
select 1 from worlds
|
||||
where worlds.id = characters.world_id
|
||||
)
|
||||
);
|
||||
|
||||
-- Send "previous data" on change
|
||||
alter table public.users replica identity full;
|
||||
|
||||
@@ -69,6 +208,8 @@ declare is_admin boolean;
|
||||
begin
|
||||
insert into public.users (id, username)
|
||||
values (new.id, new.email);
|
||||
insert into public.user_roles (role, user_id)
|
||||
values ('user', new.id);
|
||||
|
||||
return new;
|
||||
end;
|
||||
|
||||
@@ -1,4 +1,107 @@
|
||||
insert into public.role_permissions (role, permission) values ('gm', 'events.see.hidden');
|
||||
|
||||
insert into public.role_permissions (role, permission) values ('sa', 'events.see.hidden');
|
||||
insert into public.role_permissions (role, permission) values ('sa', 'users.ban');
|
||||
|
||||
-- Event categories
|
||||
insert into public.events_category (name) values ('naissance');
|
||||
insert into public.events_category (name) values ('mort');
|
||||
insert into public.events_category (name) values ('catastrophe');
|
||||
insert into public.events_category (name) values ('catastrophe naturelle');
|
||||
insert into public.events_category (name) values ('inauguration');
|
||||
insert into public.events_category (name) values ('religion');
|
||||
insert into public.events_category (name) values ('invention');
|
||||
insert into public.events_category (name) values ('science');
|
||||
insert into public.events_category (name) values ('bénédiction');
|
||||
insert into public.events_category (name) values ('joueurs');
|
||||
insert into public.events_category (name) values ('découverte');
|
||||
insert into public.events_category (name) values ('exploration');
|
||||
insert into public.events_category (name) values ('construction');
|
||||
insert into public.events_category (name) values ('arcanologie');
|
||||
insert into public.events_category (name) values ('criminalité');
|
||||
insert into public.events_category (name) values ('scandale');
|
||||
insert into public.events_category (name) values ('commerce');
|
||||
insert into public.events_category (name) values ('législation');
|
||||
|
||||
-- Character categories
|
||||
insert into public.characters_category (name) values ('joueur');
|
||||
insert into public.characters_category (name) values ('comte');
|
||||
insert into public.characters_category (name) values ('scientifique');
|
||||
insert into public.characters_category (name) values ('mage');
|
||||
insert into public.characters_category (name) values ('professeur');
|
||||
insert into public.characters_category (name) values ('criminel');
|
||||
insert into public.characters_category (name) values ('étincelle');
|
||||
insert into public.characters_category (name) values ('buse blanche');
|
||||
insert into public.characters_category (name) values ('ecclésiastique');
|
||||
insert into public.characters_category (name) values ('militaire');
|
||||
insert into public.characters_category (name) values ('activiste');
|
||||
insert into public.characters_category (name) values ('commerçant');
|
||||
|
||||
-- Worlds
|
||||
insert into public.worlds (name, description, color) values ('Léïm', 'Monde d''aventures et d''intrigues med-fan', 'black');
|
||||
|
||||
-- Worlds' calendars
|
||||
insert into public.world_calendars (world_id, months, days_per_year) values (1, ARRAY['Jalen', 'Malsen', 'Verlys', 'Nalys', 'Verdore', 'Sidore', 'Lyllion', 'Rion', 'Farene', 'Dalvene'], 320);
|
||||
|
||||
-- Events
|
||||
insert into public.events (title, description, start_date, category, hidden, wiki, calendar_id) values (
|
||||
'Laurdieu devient la première cité de l''empire de Kaliatos',
|
||||
'L''empire de Kaliatos établi sa capitale dans la cité de Laurdieu, qui connaitra une prospérité nouvelle au sein d''Aldys.',
|
||||
'{ "day": 3, "month": 4, "year": -1932 }',
|
||||
18,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Laurdieu',
|
||||
1
|
||||
);
|
||||
insert into public.events (title, description, start_date, category, hidden, wiki, calendar_id) values (
|
||||
'Apparition d''Asménys',
|
||||
'La défunte chanteuse Asménys apparaît à un barde pendant son jeune public, démarrant la religion des Prêtresses d''Asménys.',
|
||||
'{ "day": 19, "month": 7, "year": -1358 }',
|
||||
6,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Pr%C3%AAtresses_d%27Asm%C3%A9nys',
|
||||
1
|
||||
);
|
||||
insert into public.events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'La Rupture',
|
||||
'Les Abysses se déversent à la surface de Léim, à travers plusieurs brèches. Plusieurs hordes de démons se rapprochent des villes, et ce qu''on appellera l''Âge des Abysses commencent alors sur la planète entière.',
|
||||
'{ "day": 26, "month": 5, "year": -756 }',
|
||||
'{ "day": 4, "month": 9, "year": 29 }',
|
||||
3,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Seconde_Rupture',
|
||||
1
|
||||
);
|
||||
insert into public.events (title, description, start_date, end_date, category, hidden, wiki, calendar_id) values (
|
||||
'Marche du sang',
|
||||
'L''empereur de Kaliatos ordonne personnellement la traque de Jorhas Kirendre pour connivence avec les démons. Plusieurs bataillons sont affectés à la chasse de Jorhas, qui se terminera par son incarcération ainsi que la mort de plusieurs centaines de soldats.',
|
||||
'{ "day": 18, "month": 9, "year": -420 }',
|
||||
'{ "day": 27, "month": 1, "year": -419 }',
|
||||
15,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Jorhas_Kirendre',
|
||||
1
|
||||
);
|
||||
insert into public.events (title, description, start_date, category, hidden, wiki, calendar_id) values (
|
||||
'Exécution de Tyhos',
|
||||
'Le léviathan Tyhos rend l''âme après un combat de plusieurs années contre Lystos, le dieu du Soleil.',
|
||||
'{ "day": 1, "month": 0, "year": 0 }',
|
||||
9,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Tyhos',
|
||||
1
|
||||
);
|
||||
insert into public.events (title, start_date, category, wiki, calendar_id) values (
|
||||
'Traité de Kadel',
|
||||
'{ "day": 29, "month": 4, "year": 100 }',
|
||||
5,
|
||||
'https://alexcreates.fr/leim/index.php/Alliance_Kald%C3%A9lienne#Trait%C3%A9_de_Kadel',
|
||||
1
|
||||
);
|
||||
insert into public.events (title, description, start_date, category, hidden, wiki, calendar_id) values (
|
||||
'Découverte des Plaines de Poussières',
|
||||
'Les troupes de la reconquête aldienne explorent le littoral d''une immense étendue grise et inerte.',
|
||||
'{ "day": 17, "month": 7, "year": 305 }',
|
||||
11,
|
||||
true,
|
||||
'https://alexcreates.fr/leim/index.php/Plaines_de_poussi%C3%A8re',
|
||||
1
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user