Beginning campaign UI and WS to get player state.

This commit is contained in:
Clément Pons 2025-11-12 17:53:48 +01:00
parent 3ed9ab3dce
commit dd4191bea6
32 changed files with 4225 additions and 132 deletions

View File

@ -1,11 +1,13 @@
<template>
<div class="text-light-100 dark:text-dark-100 flex bg-light-0 dark:bg-dark-0 h-screen overflow-hidden">
<NuxtRouteAnnouncer/>
<TooltipProvider>
<NuxtLayout>
<div class="xl:px-12 xl:py-8 lg:px-8 lg:py-6 px-6 py-3 flex flex-1 justify-center overflow-auto max-h-full max-w-full relative" id="mainContainer">
<NuxtPage />
</div>
</NuxtLayout>
</TooltipProvider>
</div>
</template>

View File

@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "d-any",

BIN
db.sqlite

Binary file not shown.

View File

@ -86,7 +86,10 @@ export const campaignTable = table("campaign", {
name: text().notNull(),
description: text(),
owner: int().notNull().references(() => usersTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
joinby: text({ enum: [ 'link', 'invite' ] }).default('invite'),
link: text().notNull(),
status: text({ enum: ['PREPARING', 'PLAYING', 'ARCHIVED'] }).default('PREPARING'),
inventory: text({ mode: 'json' }).default('[]'),
money: int().default(0),
});
export const campaignMembersTable = table("campaign_members", {
id: int().references(() => campaignTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
@ -97,6 +100,13 @@ export const campaignCharactersTable = table("campaign_characters", {
id: int().references(() => campaignTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
character: int().references(() => characterTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
}, (table) => [primaryKey({ columns: [table.id, table.character] })]);
export const campaignLogsTable = table("campaign_logs", {
id: int().references(() => campaignTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
from: int().references(() => campaignCharactersTable.character, { onDelete: 'cascade', onUpdate: 'cascade' }),
timestamp: int({ mode: 'timestamp_ms' }).notNull(),
type: text({ enum: ['ITEM', 'CHARACTER', 'PLACE', 'EVENT', 'FIGHT', 'TEXT'] }),
details: text().notNull(),
}, (table) => [primaryKey({ columns: [table.id, table.from, table.timestamp] })]);
export const usersRelation = relations(usersTable, ({ one, many }) => ({
data: one(usersDataTable, { fields: [usersTable.id], references: [usersDataTable.id], }),
@ -142,13 +152,18 @@ export const characterChoicesRelation = relations(characterChoicesTable, ({ one
export const campaignRelation = relations(campaignTable, ({ one, many }) => ({
members: many(campaignMembersTable),
characters: many(campaignCharactersTable),
logs: many(campaignLogsTable),
owner: one(usersTable, { fields: [campaignTable.owner], references: [usersTable.id], }),
}));
export const campaignMembersRelation = relations(campaignMembersTable, ({ one }) => ({
campaign: one(campaignTable, { fields: [campaignMembersTable.id], references: [campaignTable.id], }),
member: one(usersTable, { fields: [campaignMembersTable.user], references: [usersTable.id], })
}))
}));
export const campaignCharacterRelation = relations(campaignCharactersTable, ({ one }) => ({
campaign: one(campaignTable, { fields: [campaignCharactersTable.id], references: [campaignTable.id], }),
character: one(characterTable, { fields: [campaignCharactersTable.character], references: [characterTable.id], })
}))
character: one(characterTable, { fields: [campaignCharactersTable.character], references: [characterTable.id], }),
}));
export const campaignLogsRelation = relations(campaignLogsTable, ({ one }) => ({
campaign: one(campaignTable, { fields: [campaignLogsTable.id], references: [campaignTable.id], }),
character: one(campaignCharactersTable, { fields: [campaignLogsTable.from], references: [campaignCharactersTable.character], }),
}));

View File

@ -0,0 +1 @@
ALTER TABLE `campaign` ADD `joinby` text DEFAULT 'invite';

View File

@ -0,0 +1,2 @@
ALTER TABLE `campaign` ADD `link` text NOT NULL;--> statement-breakpoint
ALTER TABLE `campaign` DROP COLUMN `joinby`;

View File

@ -0,0 +1,14 @@
CREATE TABLE `campaign_logs` (
`id` integer,
`from` integer,
`timestamp` integer NOT NULL,
`type` text,
`details` text NOT NULL,
PRIMARY KEY(`id`, `from`, `timestamp`),
FOREIGN KEY (`id`) REFERENCES `campaign`(`id`) ON UPDATE cascade ON DELETE cascade,
FOREIGN KEY (`from`) REFERENCES `campaign_characters`(`id`) ON UPDATE cascade ON DELETE cascade
);
--> statement-breakpoint
ALTER TABLE `campaign` ADD `status` text DEFAULT 'PREPARING';--> statement-breakpoint
ALTER TABLE `campaign` ADD `inventory` text DEFAULT '[]';--> statement-breakpoint
ALTER TABLE `campaign` ADD `money` integer DEFAULT 0;

View File

@ -0,0 +1,16 @@
PRAGMA foreign_keys=OFF;--> statement-breakpoint
CREATE TABLE `__new_campaign_logs` (
`id` integer,
`from` integer,
`timestamp` integer NOT NULL,
`type` text,
`details` text NOT NULL,
PRIMARY KEY(`id`, `from`, `timestamp`),
FOREIGN KEY (`id`) REFERENCES `campaign`(`id`) ON UPDATE cascade ON DELETE cascade,
FOREIGN KEY (`from`) REFERENCES `campaign_characters`(`character`) ON UPDATE cascade ON DELETE cascade
);
--> statement-breakpoint
INSERT INTO `__new_campaign_logs`("id", "from", "timestamp", "type", "details") SELECT "id", "from", "timestamp", "type", "details" FROM `campaign_logs`;--> statement-breakpoint
DROP TABLE `campaign_logs`;--> statement-breakpoint
ALTER TABLE `__new_campaign_logs` RENAME TO `campaign_logs`;--> statement-breakpoint
PRAGMA foreign_keys=ON;

View File

@ -0,0 +1,894 @@
{
"version": "6",
"dialect": "sqlite",
"id": "4dcce951-0911-424a-b0b1-2db5b2f466f9",
"prevId": "42b1cd62-a77f-4fd3-b271-c44a66a56316",
"tables": {
"campaign_characters": {
"name": "campaign_characters",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_characters_id_campaign_id_fk": {
"name": "campaign_characters_id_campaign_id_fk",
"tableFrom": "campaign_characters",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_characters_character_character_id_fk": {
"name": "campaign_characters_character_character_id_fk",
"tableFrom": "campaign_characters",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_characters_id_character_pk": {
"columns": [
"id",
"character"
],
"name": "campaign_characters_id_character_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign_members": {
"name": "campaign_members",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"user": {
"name": "user",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"rights": {
"name": "rights",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_members_id_campaign_id_fk": {
"name": "campaign_members_id_campaign_id_fk",
"tableFrom": "campaign_members",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_members_user_users_id_fk": {
"name": "campaign_members_user_users_id_fk",
"tableFrom": "campaign_members",
"tableTo": "users",
"columnsFrom": [
"user"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_members_id_user_pk": {
"columns": [
"id",
"user"
],
"name": "campaign_members_id_user_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign": {
"name": "campaign",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"joinby": {
"name": "joinby",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'invite'"
}
},
"indexes": {},
"foreignKeys": {
"campaign_owner_users_id_fk": {
"name": "campaign_owner_users_id_fk",
"tableFrom": "campaign",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_abilities": {
"name": "character_abilities",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"ability": {
"name": "ability",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"value": {
"name": "value",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"max": {
"name": "max",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {},
"foreignKeys": {
"character_abilities_character_character_id_fk": {
"name": "character_abilities_character_character_id_fk",
"tableFrom": "character_abilities",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_abilities_character_ability_pk": {
"columns": [
"character",
"ability"
],
"name": "character_abilities_character_ability_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_choices": {
"name": "character_choices",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"id": {
"name": "id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_choices_character_character_id_fk": {
"name": "character_choices_character_character_id_fk",
"tableFrom": "character_choices",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_choices_character_id_choice_pk": {
"columns": [
"character",
"id",
"choice"
],
"name": "character_choices_character_id_choice_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_leveling": {
"name": "character_leveling",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_leveling_character_character_id_fk": {
"name": "character_leveling_character_character_id_fk",
"tableFrom": "character_leveling",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_leveling_character_level_pk": {
"columns": [
"character",
"level"
],
"name": "character_leveling_character_level_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character": {
"name": "character",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"people": {
"name": "people",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 1
},
"variables": {
"name": "variables",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'{\"health\": 0,\"mana\": 0,\"spells\": [],\"items\": [],\"exhaustion\": 0,\"sickness\": [],\"poisons\": []}'"
},
"aspect": {
"name": "aspect",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"public_notes": {
"name": "public_notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"private_notes": {
"name": "private_notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"visibility": {
"name": "visibility",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'private'"
},
"thumbnail": {
"name": "thumbnail",
"type": "blob",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_owner_users_id_fk": {
"name": "character_owner_users_id_fk",
"tableFrom": "character",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_training": {
"name": "character_training",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"stat": {
"name": "stat",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_training_character_character_id_fk": {
"name": "character_training_character_character_id_fk",
"tableFrom": "character_training",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_training_character_stat_level_pk": {
"columns": [
"character",
"stat",
"level"
],
"name": "character_training_character_stat_level_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"email_validation": {
"name": "email_validation",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"project_content": {
"name": "project_content",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"content": {
"name": "content",
"type": "blob",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"project_files": {
"name": "project_files",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"path": {
"name": "path",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"navigable": {
"name": "navigable",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"private": {
"name": "private",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"project_files_path_unique": {
"name": "project_files_path_unique",
"columns": [
"path"
],
"isUnique": true
}
},
"foreignKeys": {
"project_files_owner_users_id_fk": {
"name": "project_files_owner_users_id_fk",
"tableFrom": "project_files",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"user_permissions": {
"name": "user_permissions",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"permission": {
"name": "permission",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"user_permissions_id_users_id_fk": {
"name": "user_permissions_id_users_id_fk",
"tableFrom": "user_permissions",
"tableTo": "users",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"user_permissions_id_permission_pk": {
"columns": [
"id",
"permission"
],
"name": "user_permissions_id_permission_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"user_sessions": {
"name": "user_sessions",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"user_sessions_user_id_users_id_fk": {
"name": "user_sessions_user_id_users_id_fk",
"tableFrom": "user_sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"user_sessions_id_user_id_pk": {
"columns": [
"id",
"user_id"
],
"name": "user_sessions_id_user_id_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users_data": {
"name": "users_data",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"signin": {
"name": "signin",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"lastTimestamp": {
"name": "lastTimestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"users_data_id_users_id_fk": {
"name": "users_data_id_users_id_fk",
"tableFrom": "users_data",
"tableTo": "users",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"hash": {
"name": "hash",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"state": {
"name": "state",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {
"users_username_unique": {
"name": "users_username_unique",
"columns": [
"username"
],
"isUnique": true
},
"users_email_unique": {
"name": "users_email_unique",
"columns": [
"email"
],
"isUnique": true
},
"users_hash_unique": {
"name": "users_hash_unique",
"columns": [
"hash"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -0,0 +1,893 @@
{
"version": "6",
"dialect": "sqlite",
"id": "606c7320-97b9-4a5b-87ad-2f83093d6b55",
"prevId": "4dcce951-0911-424a-b0b1-2db5b2f466f9",
"tables": {
"campaign_characters": {
"name": "campaign_characters",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_characters_id_campaign_id_fk": {
"name": "campaign_characters_id_campaign_id_fk",
"tableFrom": "campaign_characters",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_characters_character_character_id_fk": {
"name": "campaign_characters_character_character_id_fk",
"tableFrom": "campaign_characters",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_characters_id_character_pk": {
"columns": [
"id",
"character"
],
"name": "campaign_characters_id_character_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign_members": {
"name": "campaign_members",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"user": {
"name": "user",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"rights": {
"name": "rights",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_members_id_campaign_id_fk": {
"name": "campaign_members_id_campaign_id_fk",
"tableFrom": "campaign_members",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_members_user_users_id_fk": {
"name": "campaign_members_user_users_id_fk",
"tableFrom": "campaign_members",
"tableTo": "users",
"columnsFrom": [
"user"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_members_id_user_pk": {
"columns": [
"id",
"user"
],
"name": "campaign_members_id_user_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign": {
"name": "campaign",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"link": {
"name": "link",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_owner_users_id_fk": {
"name": "campaign_owner_users_id_fk",
"tableFrom": "campaign",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_abilities": {
"name": "character_abilities",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"ability": {
"name": "ability",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"value": {
"name": "value",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"max": {
"name": "max",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {},
"foreignKeys": {
"character_abilities_character_character_id_fk": {
"name": "character_abilities_character_character_id_fk",
"tableFrom": "character_abilities",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_abilities_character_ability_pk": {
"columns": [
"character",
"ability"
],
"name": "character_abilities_character_ability_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_choices": {
"name": "character_choices",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"id": {
"name": "id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_choices_character_character_id_fk": {
"name": "character_choices_character_character_id_fk",
"tableFrom": "character_choices",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_choices_character_id_choice_pk": {
"columns": [
"character",
"id",
"choice"
],
"name": "character_choices_character_id_choice_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_leveling": {
"name": "character_leveling",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_leveling_character_character_id_fk": {
"name": "character_leveling_character_character_id_fk",
"tableFrom": "character_leveling",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_leveling_character_level_pk": {
"columns": [
"character",
"level"
],
"name": "character_leveling_character_level_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character": {
"name": "character",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"people": {
"name": "people",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 1
},
"variables": {
"name": "variables",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'{\"health\": 0,\"mana\": 0,\"spells\": [],\"items\": [],\"exhaustion\": 0,\"sickness\": [],\"poisons\": []}'"
},
"aspect": {
"name": "aspect",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"public_notes": {
"name": "public_notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"private_notes": {
"name": "private_notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"visibility": {
"name": "visibility",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'private'"
},
"thumbnail": {
"name": "thumbnail",
"type": "blob",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_owner_users_id_fk": {
"name": "character_owner_users_id_fk",
"tableFrom": "character",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_training": {
"name": "character_training",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"stat": {
"name": "stat",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_training_character_character_id_fk": {
"name": "character_training_character_character_id_fk",
"tableFrom": "character_training",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_training_character_stat_level_pk": {
"columns": [
"character",
"stat",
"level"
],
"name": "character_training_character_stat_level_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"email_validation": {
"name": "email_validation",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"project_content": {
"name": "project_content",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"content": {
"name": "content",
"type": "blob",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"project_files": {
"name": "project_files",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"path": {
"name": "path",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"navigable": {
"name": "navigable",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"private": {
"name": "private",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"project_files_path_unique": {
"name": "project_files_path_unique",
"columns": [
"path"
],
"isUnique": true
}
},
"foreignKeys": {
"project_files_owner_users_id_fk": {
"name": "project_files_owner_users_id_fk",
"tableFrom": "project_files",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"user_permissions": {
"name": "user_permissions",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"permission": {
"name": "permission",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"user_permissions_id_users_id_fk": {
"name": "user_permissions_id_users_id_fk",
"tableFrom": "user_permissions",
"tableTo": "users",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"user_permissions_id_permission_pk": {
"columns": [
"id",
"permission"
],
"name": "user_permissions_id_permission_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"user_sessions": {
"name": "user_sessions",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"user_sessions_user_id_users_id_fk": {
"name": "user_sessions_user_id_users_id_fk",
"tableFrom": "user_sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"user_sessions_id_user_id_pk": {
"columns": [
"id",
"user_id"
],
"name": "user_sessions_id_user_id_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users_data": {
"name": "users_data",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"signin": {
"name": "signin",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"lastTimestamp": {
"name": "lastTimestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"users_data_id_users_id_fk": {
"name": "users_data_id_users_id_fk",
"tableFrom": "users_data",
"tableTo": "users",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"hash": {
"name": "hash",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"state": {
"name": "state",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {
"users_username_unique": {
"name": "users_username_unique",
"columns": [
"username"
],
"isUnique": true
},
"users_email_unique": {
"name": "users_email_unique",
"columns": [
"email"
],
"isUnique": true
},
"users_hash_unique": {
"name": "users_hash_unique",
"columns": [
"hash"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -0,0 +1,998 @@
{
"version": "6",
"dialect": "sqlite",
"id": "1b8ca1cb-ac1d-481e-a8d9-6d3fdffd7e3e",
"prevId": "606c7320-97b9-4a5b-87ad-2f83093d6b55",
"tables": {
"campaign_characters": {
"name": "campaign_characters",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_characters_id_campaign_id_fk": {
"name": "campaign_characters_id_campaign_id_fk",
"tableFrom": "campaign_characters",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_characters_character_character_id_fk": {
"name": "campaign_characters_character_character_id_fk",
"tableFrom": "campaign_characters",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_characters_id_character_pk": {
"columns": [
"id",
"character"
],
"name": "campaign_characters_id_character_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign_logs": {
"name": "campaign_logs",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"from": {
"name": "from",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"details": {
"name": "details",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_logs_id_campaign_id_fk": {
"name": "campaign_logs_id_campaign_id_fk",
"tableFrom": "campaign_logs",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_logs_from_campaign_characters_id_fk": {
"name": "campaign_logs_from_campaign_characters_id_fk",
"tableFrom": "campaign_logs",
"tableTo": "campaign_characters",
"columnsFrom": [
"from"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_logs_id_from_timestamp_pk": {
"columns": [
"id",
"from",
"timestamp"
],
"name": "campaign_logs_id_from_timestamp_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign_members": {
"name": "campaign_members",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"user": {
"name": "user",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"rights": {
"name": "rights",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_members_id_campaign_id_fk": {
"name": "campaign_members_id_campaign_id_fk",
"tableFrom": "campaign_members",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_members_user_users_id_fk": {
"name": "campaign_members_user_users_id_fk",
"tableFrom": "campaign_members",
"tableTo": "users",
"columnsFrom": [
"user"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_members_id_user_pk": {
"columns": [
"id",
"user"
],
"name": "campaign_members_id_user_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign": {
"name": "campaign",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"link": {
"name": "link",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"status": {
"name": "status",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'PREPARING'"
},
"inventory": {
"name": "inventory",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'[]'"
},
"money": {
"name": "money",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": 0
}
},
"indexes": {},
"foreignKeys": {
"campaign_owner_users_id_fk": {
"name": "campaign_owner_users_id_fk",
"tableFrom": "campaign",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_abilities": {
"name": "character_abilities",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"ability": {
"name": "ability",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"value": {
"name": "value",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"max": {
"name": "max",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {},
"foreignKeys": {
"character_abilities_character_character_id_fk": {
"name": "character_abilities_character_character_id_fk",
"tableFrom": "character_abilities",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_abilities_character_ability_pk": {
"columns": [
"character",
"ability"
],
"name": "character_abilities_character_ability_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_choices": {
"name": "character_choices",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"id": {
"name": "id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_choices_character_character_id_fk": {
"name": "character_choices_character_character_id_fk",
"tableFrom": "character_choices",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_choices_character_id_choice_pk": {
"columns": [
"character",
"id",
"choice"
],
"name": "character_choices_character_id_choice_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_leveling": {
"name": "character_leveling",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_leveling_character_character_id_fk": {
"name": "character_leveling_character_character_id_fk",
"tableFrom": "character_leveling",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_leveling_character_level_pk": {
"columns": [
"character",
"level"
],
"name": "character_leveling_character_level_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character": {
"name": "character",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"people": {
"name": "people",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 1
},
"variables": {
"name": "variables",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'{\"health\": 0,\"mana\": 0,\"spells\": [],\"items\": [],\"exhaustion\": 0,\"sickness\": [],\"poisons\": []}'"
},
"aspect": {
"name": "aspect",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"public_notes": {
"name": "public_notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"private_notes": {
"name": "private_notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"visibility": {
"name": "visibility",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'private'"
},
"thumbnail": {
"name": "thumbnail",
"type": "blob",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_owner_users_id_fk": {
"name": "character_owner_users_id_fk",
"tableFrom": "character",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_training": {
"name": "character_training",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"stat": {
"name": "stat",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_training_character_character_id_fk": {
"name": "character_training_character_character_id_fk",
"tableFrom": "character_training",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_training_character_stat_level_pk": {
"columns": [
"character",
"stat",
"level"
],
"name": "character_training_character_stat_level_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"email_validation": {
"name": "email_validation",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"project_content": {
"name": "project_content",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"content": {
"name": "content",
"type": "blob",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"project_files": {
"name": "project_files",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"path": {
"name": "path",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"navigable": {
"name": "navigable",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"private": {
"name": "private",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"project_files_path_unique": {
"name": "project_files_path_unique",
"columns": [
"path"
],
"isUnique": true
}
},
"foreignKeys": {
"project_files_owner_users_id_fk": {
"name": "project_files_owner_users_id_fk",
"tableFrom": "project_files",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"user_permissions": {
"name": "user_permissions",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"permission": {
"name": "permission",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"user_permissions_id_users_id_fk": {
"name": "user_permissions_id_users_id_fk",
"tableFrom": "user_permissions",
"tableTo": "users",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"user_permissions_id_permission_pk": {
"columns": [
"id",
"permission"
],
"name": "user_permissions_id_permission_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"user_sessions": {
"name": "user_sessions",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"user_sessions_user_id_users_id_fk": {
"name": "user_sessions_user_id_users_id_fk",
"tableFrom": "user_sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"user_sessions_id_user_id_pk": {
"columns": [
"id",
"user_id"
],
"name": "user_sessions_id_user_id_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users_data": {
"name": "users_data",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"signin": {
"name": "signin",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"lastTimestamp": {
"name": "lastTimestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"users_data_id_users_id_fk": {
"name": "users_data_id_users_id_fk",
"tableFrom": "users_data",
"tableTo": "users",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"hash": {
"name": "hash",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"state": {
"name": "state",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {
"users_username_unique": {
"name": "users_username_unique",
"columns": [
"username"
],
"isUnique": true
},
"users_email_unique": {
"name": "users_email_unique",
"columns": [
"email"
],
"isUnique": true
},
"users_hash_unique": {
"name": "users_hash_unique",
"columns": [
"hash"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -0,0 +1,998 @@
{
"version": "6",
"dialect": "sqlite",
"id": "98e63b73-701b-4ae1-8472-2ee706e064ff",
"prevId": "1b8ca1cb-ac1d-481e-a8d9-6d3fdffd7e3e",
"tables": {
"campaign_characters": {
"name": "campaign_characters",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_characters_id_campaign_id_fk": {
"name": "campaign_characters_id_campaign_id_fk",
"tableFrom": "campaign_characters",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_characters_character_character_id_fk": {
"name": "campaign_characters_character_character_id_fk",
"tableFrom": "campaign_characters",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_characters_id_character_pk": {
"columns": [
"id",
"character"
],
"name": "campaign_characters_id_character_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign_logs": {
"name": "campaign_logs",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"from": {
"name": "from",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"details": {
"name": "details",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_logs_id_campaign_id_fk": {
"name": "campaign_logs_id_campaign_id_fk",
"tableFrom": "campaign_logs",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_logs_from_campaign_characters_character_fk": {
"name": "campaign_logs_from_campaign_characters_character_fk",
"tableFrom": "campaign_logs",
"tableTo": "campaign_characters",
"columnsFrom": [
"from"
],
"columnsTo": [
"character"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_logs_id_from_timestamp_pk": {
"columns": [
"id",
"from",
"timestamp"
],
"name": "campaign_logs_id_from_timestamp_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign_members": {
"name": "campaign_members",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"user": {
"name": "user",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"rights": {
"name": "rights",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"campaign_members_id_campaign_id_fk": {
"name": "campaign_members_id_campaign_id_fk",
"tableFrom": "campaign_members",
"tableTo": "campaign",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
},
"campaign_members_user_users_id_fk": {
"name": "campaign_members_user_users_id_fk",
"tableFrom": "campaign_members",
"tableTo": "users",
"columnsFrom": [
"user"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"campaign_members_id_user_pk": {
"columns": [
"id",
"user"
],
"name": "campaign_members_id_user_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"campaign": {
"name": "campaign",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"link": {
"name": "link",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"status": {
"name": "status",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'PREPARING'"
},
"inventory": {
"name": "inventory",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'[]'"
},
"money": {
"name": "money",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": 0
}
},
"indexes": {},
"foreignKeys": {
"campaign_owner_users_id_fk": {
"name": "campaign_owner_users_id_fk",
"tableFrom": "campaign",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_abilities": {
"name": "character_abilities",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"ability": {
"name": "ability",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"value": {
"name": "value",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"max": {
"name": "max",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {},
"foreignKeys": {
"character_abilities_character_character_id_fk": {
"name": "character_abilities_character_character_id_fk",
"tableFrom": "character_abilities",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_abilities_character_ability_pk": {
"columns": [
"character",
"ability"
],
"name": "character_abilities_character_ability_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_choices": {
"name": "character_choices",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"id": {
"name": "id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_choices_character_character_id_fk": {
"name": "character_choices_character_character_id_fk",
"tableFrom": "character_choices",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_choices_character_id_choice_pk": {
"columns": [
"character",
"id",
"choice"
],
"name": "character_choices_character_id_choice_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_leveling": {
"name": "character_leveling",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_leveling_character_character_id_fk": {
"name": "character_leveling_character_character_id_fk",
"tableFrom": "character_leveling",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_leveling_character_level_pk": {
"columns": [
"character",
"level"
],
"name": "character_leveling_character_level_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character": {
"name": "character",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"people": {
"name": "people",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 1
},
"variables": {
"name": "variables",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'{\"health\": 0,\"mana\": 0,\"spells\": [],\"items\": [],\"exhaustion\": 0,\"sickness\": [],\"poisons\": []}'"
},
"aspect": {
"name": "aspect",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"public_notes": {
"name": "public_notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"private_notes": {
"name": "private_notes",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"visibility": {
"name": "visibility",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'private'"
},
"thumbnail": {
"name": "thumbnail",
"type": "blob",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_owner_users_id_fk": {
"name": "character_owner_users_id_fk",
"tableFrom": "character",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"character_training": {
"name": "character_training",
"columns": {
"character": {
"name": "character",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"stat": {
"name": "stat",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"level": {
"name": "level",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"choice": {
"name": "choice",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"character_training_character_character_id_fk": {
"name": "character_training_character_character_id_fk",
"tableFrom": "character_training",
"tableTo": "character",
"columnsFrom": [
"character"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"character_training_character_stat_level_pk": {
"columns": [
"character",
"stat",
"level"
],
"name": "character_training_character_stat_level_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"email_validation": {
"name": "email_validation",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"project_content": {
"name": "project_content",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"content": {
"name": "content",
"type": "blob",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"project_files": {
"name": "project_files",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"path": {
"name": "path",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"owner": {
"name": "owner",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"navigable": {
"name": "navigable",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"private": {
"name": "private",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"order": {
"name": "order",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"project_files_path_unique": {
"name": "project_files_path_unique",
"columns": [
"path"
],
"isUnique": true
}
},
"foreignKeys": {
"project_files_owner_users_id_fk": {
"name": "project_files_owner_users_id_fk",
"tableFrom": "project_files",
"tableTo": "users",
"columnsFrom": [
"owner"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"user_permissions": {
"name": "user_permissions",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"permission": {
"name": "permission",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"user_permissions_id_users_id_fk": {
"name": "user_permissions_id_users_id_fk",
"tableFrom": "user_permissions",
"tableTo": "users",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"user_permissions_id_permission_pk": {
"columns": [
"id",
"permission"
],
"name": "user_permissions_id_permission_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"user_sessions": {
"name": "user_sessions",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"timestamp": {
"name": "timestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"user_sessions_user_id_users_id_fk": {
"name": "user_sessions_user_id_users_id_fk",
"tableFrom": "user_sessions",
"tableTo": "users",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {
"user_sessions_id_user_id_pk": {
"columns": [
"id",
"user_id"
],
"name": "user_sessions_id_user_id_pk"
}
},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users_data": {
"name": "users_data",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"signin": {
"name": "signin",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"lastTimestamp": {
"name": "lastTimestamp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"users_data_id_users_id_fk": {
"name": "users_data_id_users_id_fk",
"tableFrom": "users_data",
"tableTo": "users",
"columnsFrom": [
"id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "cascade"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"hash": {
"name": "hash",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"state": {
"name": "state",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {
"users_username_unique": {
"name": "users_username_unique",
"columns": [
"username"
],
"isUnique": true
},
"users_email_unique": {
"name": "users_email_unique",
"columns": [
"email"
],
"isUnique": true
},
"users_hash_unique": {
"name": "users_hash_unique",
"columns": [
"hash"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View File

@ -134,6 +134,34 @@
"when": 1761829250157,
"tag": "0018_friendly_deadpool",
"breakpoints": true
},
{
"idx": 19,
"version": "6",
"when": 1762260955744,
"tag": "0019_flaky_steel_serpent",
"breakpoints": true
},
{
"idx": 20,
"version": "6",
"when": 1762261125335,
"tag": "0020_daily_echo",
"breakpoints": true
},
{
"idx": 21,
"version": "6",
"when": 1762947637242,
"tag": "0021_familiar_dagger",
"breakpoints": true
},
{
"idx": 22,
"version": "6",
"when": 1762948869537,
"tag": "0022_warm_bushwacker",
"breakpoints": true
}
]
}

View File

@ -130,6 +130,7 @@ export default defineNuxtConfig({
preset: 'bun',
experimental: {
tasks: true,
websocket: true,
},
watchOptions: {
usePolling: true,

View File

@ -1 +1,3 @@
<template></template>
<template>
</template>

View File

@ -1 +1,79 @@
<template></template>
<script setup lang="ts">
import { Icon } from '@iconify/vue/dist/iconify.js';
import { unifySlug } from '~/shared/general.util';
definePageMeta({
guestsGoesTo: '/user/login',
});
const id = unifySlug(useRoute().params.id ?? '');
const { user, loggedIn } = useUserSession();
const { data: campaign, error, status } = await useFetch(`/api/campaign/${id}`);
const copied = ref(false);
const stopCopy = useDebounceFn(() => copied.value = false, 5000);
const websocket = useWebSocket(`wss://localhost:3000/ws/campaign/${id}`, { heartbeat: true });
async function copyLink()
{
await navigator.clipboard.writeText(`https://d-any.com/campaign/join/${ encodeURIComponent(campaign.value!.link) }`);
copied.value = true;
stopCopy();
}
</script>
<template>
<div v-if="status === 'pending'" class="flex flex-col items-center justify-center w-full h-full">
<Loading />
</div>
<div class="flex flex-col w-full h-full items-center gap-4" v-else-if="campaign && status === 'success'">
<div class="flex flex-row gap-8 items-center">
<div class="flex flex-row gap-8 items-baseline">
<span class="text-2xl font-semibold">{{ campaign.name }}</span>
<span class="italic">MJ: {{ campaign.owner.username }}</span>
</div>
<div class="border border-light-35 dark:border-dark-35 p-1 flex flex-row items-center gap-2">
<pre class="ps-1 w-[400px] truncate">https://d-any.com/campaign/join/{{ encodeURIComponent(campaign.link) }}</pre>
<div class="cursor-pointer border border-light-35 dark:border-dark-35 hover:bg-light-30 dark:hover:bg-dark-30 hover:border-light-50 dark:hover:border-dark-50 p-1" @click="copyLink">
<Icon v-if="!copied" icon="radix-icons:clipboard" class="w-4 h-4"/>
<Icon v-else icon="radix-icons:check" class="w-4 h-4"/>
</div>
</div>
<div class="cursor-pointer border border-light-35 dark:border-dark-35 hover:bg-light-30 dark:hover:bg-dark-30 hover:border-light-50 dark:hover:border-dark-50 p-1" @click="() => websocket.open()">{{ websocket.status }}</div>
</div>
<div class="flex flex-row gap-4 flex-1">
<div class="flex flex-col">
<div class="flex flex-row items-center gap-4"><span class="text-lg font-bold tracking-tight">Joueurs</span><span class="border-b border-dashed border-light-35 dark:border-dark-35 w-full"></span></div>
<div class="flex flex-col divide-y divide-light-25 dark:divide-dark-25 w-64">
<template v-if="campaign.members.length > 0">
<div v-for="player of campaign.members" class="flex flex-col py-1 my-1">
<div class="flex flex-row items-center justify-between">
<span>{{ player.member?.username }}</span>
<Tooltip message="Absent" side="right" :delay="0"><span class="rounded-full w-3 h-3 block border-light-50 dark:border-dark-50 border-2 border-dashed"></span></Tooltip>
</div>
<template v-if="player.characters.length > 0">
<div class="flex flex-col border-l-2 border-light-35 dark:border-dark-35 ps-2">
<div class="flex flex-row items-center justify-between" v-for="character of player.characters">
<span>{{ character.character.name }}</span>
</div>
</div>
</template>
<template v-else>
<span class="text-sm italic text-light-70 dark:text-dark-70">Sans personnage</span>
</template>
</div>
</template>
<template v-else>
<span class="text-sm italic py-2 text-center">Invitez des joueurs via le lien d'invitation</span>
</template>
</div>
</div>
<div class="border-l border-light-35 dark:border-dark-35"></div>
<div class="flex flex-col divide-y divide-light-25 dark:divide-dark-25 w-[800px]">
</div>
</div>
</div>
<div class="flex flex-col" v-else-if="status === 'error'">
</div>
</template>

View File

@ -1,28 +1,60 @@
<script setup lang="ts">
import { Toaster } from '~/shared/components.util';
definePageMeta({
guestsGoesTo: '/user/login',
});
const { user, loggedIn } = useUserSession();
const { data: campaigns, error, status } = await useFetch(`/api/campaign`);
const archives = computed(() => campaigns.value?.filter(e => e.status === 'ARCHIVED'));
const valids = computed(() => campaigns.value?.filter(e => e.status !== 'ARCHIVED'));
function leaveCampaign(id: number)
async function leaveCampaign(id: number)
{
try
{
await useRequestFetch()(`/api/campaign/${id}/leave`, { method: 'POST', });
campaigns.value = campaigns.value?.filter(e => e.id !== id);
}
catch(e)
{
Toaster.add({ duration: 10000, content: (e as Error).message ?? e, title: 'Une erreur est survenue.', type: 'error', timer: true, });
}
}
async function removeCampaign(id: number)
{
try
{
await useRequestFetch()(`/api/campaign/${id}`, { method: 'DELETE', });
campaigns.value = campaigns.value?.filter(e => e.id !== id);
}
catch(e)
{
Toaster.add({ duration: 10000, content: (e as Error).message ?? e, title: 'Une erreur est survenue.', type: 'error', timer: true, });
}
}
function create()
{
useRequestFetch()('/api/campaign', {
method: 'POST',
body: { id: 'new', name: 'Test', description: '', joinby: 'link' },
}).then(() => Toaster.add({ duration: 8000, content: 'Campagne créée', type: 'info' })).catch((e) => Toaster.add({ duration: 8000, title: 'Une erreur est survenue', content: e, type: 'error' }))
}
</script>
<template>
<Head>
<Title>d[any] - Mes personnages</Title>
<Title>d[any] - Mes campagnes</Title>
</Head>
<div class="flex flex-col">
<div v-if="status === 'pending'" class="flex flex-1 justify-center align-center">
<Loading size="large" />
</div>
<template v-else-if="status === 'success' && loggedIn && user">
<div v-if="campaigns && campaigns.length > 0" class="grid p-6 2xl:grid-cols-3 lg:grid-cols-2 grid-cols-1 gap-4 w-full">
<div class="flex flex-col w-[360px] border border-light-35 dark:border-dark-35" v-for="campaign of campaigns">
<div v-if="campaigns && campaigns.length > 0" class="flex flex-col gap-4">
<div v-if="valids && valids.length > 0" class="grid p-6 2xl:grid-cols-3 lg:grid-cols-2 grid-cols-1 gap-4 w-full">
<div class="flex flex-col w-[360px] border border-light-35 dark:border-dark-35" v-for="campaign of valids">
<NuxtLink :to="{ name: 'campaign-id', params: { id: campaign.id } }" class="group bg-light-10 dark:bg-dark-10 border-b border-light-35 dark:border-dark-35 p-2 flex flex-col gap-2">
<div class="flex flex-row gap-8 ps-4 items-center">
<div class="flex flex-1 flex-col gap-2 justify-center">
@ -36,33 +68,99 @@ function leaveCampaign(id: number)
</div>
</NuxtLink>
<div class="flex justify-around items-center py-2 px-4 gap-4">
<NuxtLink :to="{ name: 'character-id-edit', params: { id: campaign.id } }" class="text-sm font-bold cursor-pointer hover:text-accent-blue">Editer</NuxtLink>
<span class="w-px h-full bg-light-50 dark:bg-dark-50"></span>
<AlertDialogRoot v-if="user.id !== campaign.owner.id">
<AlertDialogRoot>
<AlertDialogTrigger>
<span class="text-sm font-bold text-light-red dark:text-dark-red">Quitter</span>
<span class="text-sm font-bold text-light-red dark:text-dark-red">{{ user.id !== campaign.owner.id ? 'Quitter' : 'Supprimer' }}</span>
</AlertDialogTrigger>
<AlertDialogPortal>
<AlertDialogOverlay class="bg-light-0 dark:bg-dark-0 opacity-70 fixed inset-0 z-40" />
<AlertDialogContent
class="data-[state=open]:animate-contentShow fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[800px] translate-x-[-50%] translate-y-[-50%] bg-light-10 dark:bg-dark-10 border border-light-30 dark:border-dark-30 p-6 z-50 text-light-100 dark:text-dark-100">
<AlertDialogTitle class="text-3xl font-light relative -top-2">Vous vous appretez à quitter "{{ campaign.name }}". Etes vous sûr ?</AlertDialogTitle>
<AlertDialogTitle class="text-3xl font-light relative -top-2">Vous vous appretez à {{ user.id !== campaign.owner.id ? 'quitter' : 'supprimer' }} "{{ campaign.name }}". Etes vous sûr ?</AlertDialogTitle>
<div class="flex flex-1 justify-end gap-4">
<AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel>
<AlertDialogAction asChild><Button @click="() => leaveCampaign(campaign.id)" class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Oui</Button></AlertDialogAction>
<AlertDialogAction asChild><Button @click="() => user?.id !== campaign.owner.id ? leaveCampaign(campaign.id) : removeCampaign(campaign.id)" class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Oui</Button></AlertDialogAction>
</div>
</AlertDialogContent>
</AlertDialogPortal>
</AlertDialogRoot>
</div>
</div>
<div class="flex flex-col w-[360px] border border-light-35 dark:border-dark-35" v-for="campaign of campaigns.filter(e => e.status === 'ARCHIVED')">
<NuxtLink :to="{ name: 'campaign-id', params: { id: campaign.id } }" class="group bg-light-10 dark:bg-dark-10 border-b border-light-35 dark:border-dark-35 p-2 flex flex-col gap-2">
<div class="flex flex-row gap-8 ps-4 items-center">
<div class="flex flex-1 flex-col gap-2 justify-center">
<span class="text-lg font-bold group-hover:text-accent-blue">{{ campaign.name }}</span>
<span class="border-b w-full border-light-50 dark:border-dark-50"></span>
<div class="flex flex-row flex-1 items-stretch gap-4">
<span class="text-sm">{{ campaign.members.length }} joueur{{ campaign.members.length === 1 ? '' : 's' }}</span>
</div>
</div>
<div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100 dark:bg-dark-100 !bg-opacity-10"></div>
</div>
</NuxtLink>
</div>
</div>
<div v-if="archives && archives.length > 0" class="flex flex-row w-full gap-8 justify-center items-center"><span class="border-t border-light-35 dark:border-dark-35 flex-1"></span><span class="text-lg font-semibold">Archives</span><span class="border-t border-light-35 dark:border-dark-35 flex-1"></span></div>
<div v-if="archives && archives.length > 0" class="grid p-6 2xl:grid-cols-3 lg:grid-cols-2 grid-cols-1 gap-4 w-full">
<div class="flex flex-col w-[360px] border border-light-35 dark:border-dark-35" v-for="campaign of archives">
<NuxtLink :to="{ name: 'campaign-id', params: { id: campaign.id } }" class="group bg-light-10 dark:bg-dark-10 border-b border-light-35 dark:border-dark-35 p-2 flex flex-col gap-2">
<div class="flex flex-row gap-8 ps-4 items-center">
<div class="flex flex-1 flex-col gap-2 justify-center">
<span class="text-lg font-bold group-hover:text-accent-blue">{{ campaign.name }}</span>
<span class="border-b w-full border-light-50 dark:border-dark-50"></span>
<div class="flex flex-row flex-1 items-stretch gap-4">
<span class="text-sm">{{ campaign.members.length }} joueur{{ campaign.members.length === 1 ? '' : 's' }}</span>
</div>
</div>
<div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100 dark:bg-dark-100 !bg-opacity-10"></div>
</div>
</NuxtLink>
<div class="flex justify-around items-center py-2 px-4 gap-4">
<AlertDialogRoot>
<AlertDialogTrigger>
<span class="text-sm font-bold text-light-red dark:text-dark-red">{{ user.id !== campaign.owner.id ? 'Quitter' : 'Supprimer' }}</span>
</AlertDialogTrigger>
<AlertDialogPortal>
<AlertDialogOverlay class="bg-light-0 dark:bg-dark-0 opacity-70 fixed inset-0 z-40" />
<AlertDialogContent
class="data-[state=open]:animate-contentShow fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[800px] translate-x-[-50%] translate-y-[-50%] bg-light-10 dark:bg-dark-10 border border-light-30 dark:border-dark-30 p-6 z-50 text-light-100 dark:text-dark-100">
<AlertDialogTitle class="text-3xl font-light relative -top-2">Vous vous appretez à {{ user.id !== campaign.owner.id ? 'quitter' : 'supprimer' }} "{{ campaign.name }}". Etes vous sûr ?</AlertDialogTitle>
<div class="flex flex-1 justify-end gap-4">
<AlertDialogCancel asChild><Button>Non</Button></AlertDialogCancel>
<AlertDialogAction asChild><Button @click="() => user?.id !== campaign.owner.id ? leaveCampaign(campaign.id) : removeCampaign(campaign.id)" class="border-light-red dark:border-dark-red hover:border-light-red dark:hover:border-dark-red hover:bg-light-redBack dark:hover:bg-dark-redBack text-light-red dark:text-dark-red focus:shadow-light-red dark:focus:shadow-dark-red">Oui</Button></AlertDialogAction>
</div>
</AlertDialogContent>
</AlertDialogPortal>
</AlertDialogRoot>
</div>
</div>
<div class="flex flex-col w-[360px] border border-light-35 dark:border-dark-35" v-for="campaign of campaigns.filter(e => e.status === 'ARCHIVED')">
<NuxtLink :to="{ name: 'campaign-id', params: { id: campaign.id } }" class="group bg-light-10 dark:bg-dark-10 border-b border-light-35 dark:border-dark-35 p-2 flex flex-col gap-2">
<div class="flex flex-row gap-8 ps-4 items-center">
<div class="flex flex-1 flex-col gap-2 justify-center">
<span class="text-lg font-bold group-hover:text-accent-blue">{{ campaign.name }}</span>
<span class="border-b w-full border-light-50 dark:border-dark-50"></span>
<div class="flex flex-row flex-1 items-stretch gap-4">
<span class="text-sm">{{ campaign.members.length }} joueur{{ campaign.members.length === 1 ? '' : 's' }}</span>
</div>
</div>
<div class="rounded-full w-[96px] h-[96px] border border-light-50 dark:border-dark-50 bg-light-100 dark:bg-dark-100 !bg-opacity-10"></div>
</div>
</NuxtLink>
</div>
</div>
</div>
<div v-else class="flex flex-col gap-2 items-center flex-1">
<span class="text-lg font-bold">Vous n'avez pas encore rejoint de campagne</span>
<NuxtLink class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow]
<div class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow]
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
hover:bg-light-25 dark:hover:bg-dark-25 hover:border-light-50 dark:hover:border-dark-50
focus:bg-light-30 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" :to="{ name: 'campaign-id-edit', params: { id: 'new' } }">Créer ma campagne</NuxtLink>
focus:bg-light-30 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" @click="create">Créer ma campagne</div>
<!-- <NuxtLink class="inline-flex justify-center items-center outline-none leading-none transition-[box-shadow]
text-light-100 dark:text-dark-100 bg-light-20 dark:bg-dark-20 border border-light-40 dark:border-dark-40
hover:bg-light-25 dark:hover:bg-dark-25 hover:border-light-50 dark:hover:border-dark-50
focus:bg-light-30 dark:focus:bg-dark-30 focus:border-light-50 dark:focus:border-dark-50 focus:shadow-raw focus:shadow-light-50 dark:focus:shadow-dark-50 py-2 px-4" :to="{ name: 'campaign-id-edit', params: { id: 'new' } }">Créer ma campagne</NuxtLink> -->
</div>
</template>
<div v-else>

View File

@ -6,17 +6,15 @@ export default defineEventHandler(async (e) => {
const session = await getUserSession(e);
if(!session || !session.user)
{
setResponseStatus(e, 401);
return;
}
if(!session || !session.user) return setResponseStatus(e, 401);
return db.query.campaignTable.findMany({
const data = db.query.campaignTable.findMany({
with: {
members: { with: { member: { columns: { username: true, id: true } } }, columns: { } },
characters: { where: ({ id }) => eq(id, session.user!.id), columns: { character: true } },
owner: { columns: { username: true, id: true } }
owner: { columns: { username: true, id: true } },
},
}).sync();
return data.filter(e => e.owner.id === session.user!.id || e.members.find(e => e.member?.id === session.user!.id));
});

View File

@ -1,7 +1,9 @@
import { eq } from 'drizzle-orm';
import { z } from 'zod/v4';
import useDatabase from '~/composables/useDatabase';
import { campaignMembersTable, campaignTable } from '~/db/schema';
import { CampaignValidation } from '~/shared/campaign.util';
import { cryptURI } from '~/shared/general.util';
export default defineEventHandler(async (e) => {
const body = await readValidatedBody(e, CampaignValidation.extend({ id: z.unknown(), }).safeParse);
@ -26,9 +28,11 @@ export default defineEventHandler(async (e) => {
name: body.data.name,
description: body.data.description,
owner: session.user!.id,
joinby: body.data.joinby,
link: '',
}).returning({ id: campaignTable.id }).get().id;
tx.update(campaignTable).set({ link: cryptURI('campaign', id) }).where(eq(campaignTable.id, id)).run();
tx.insert(campaignMembersTable).values({ id, rights: 'dm', user: session.user!.id }).run();
return id;

View File

@ -20,6 +20,7 @@ export default defineEventHandler(async (e) => {
}
catch(_e)
{
console.error(_e);
return setResponseStatus(e, 500);
}
});

View File

@ -1,4 +1,4 @@
import { eq } from 'drizzle-orm';
import { eq, not, sql } from 'drizzle-orm';
import useDatabase from '~/composables/useDatabase';
export default defineEventHandler(async (e) => {
@ -19,14 +19,18 @@ export default defineEventHandler(async (e) => {
const db = useDatabase();
const data = db.query.campaignTable.findFirst({
with: {
members: { with: { member: { columns: { username: true, id: true } } }, columns: { } },
characters: { columns: { character: true } },
owner: { columns: { username: true, id: true } }
members: { with: { member: { columns: { username: true, id: true } } }, columns: { }, where: ({ rights }) => not(eq(rights, 'dm')), extras: { 'characters': sql`NULL`.as('characters') } },
characters: { with: { character: { columns: { id: true, name: true, owner: true } } } },
owner: { columns: { username: true, id: true } },
},
where: ({ id: _id }) => eq(_id, parseInt(id, 10)),
}).sync();
if(data && (data.owner === session.user.id || data.members.find(e => e.member?.id === session.user!.id))) return data;
if(data && (data.owner.id === session.user.id || data.members.find(e => e.member?.id === session.user!.id)))
{
data.members.forEach(e => e.characters = data.characters.filter(_e => _e.character?.owner === e.member?.id))
return data;
}
else if(!data) return setResponseStatus(e, 404);
else return setResponseStatus(e, 403);
});

View File

@ -40,7 +40,6 @@ export default defineEventHandler(async (e) => {
tx.update(campaignTable).set({
name: body.data.name,
description: body.data.description,
joinby: body.data.joinby,
}).where(eq(campaignTable.id, id)).run();
});
}

View File

@ -1,37 +0,0 @@
import { and, eq, sql } from "drizzle-orm";
import { campaignMembersTable, campaignTable } from "~/db/schema";
export default defineEventHandler(async (e) => {
const _id = getRouterParam(e, "id");
if(!_id)
{
setResponseStatus(e, 400);
return;
}
const id = parseInt(_id, 10);
const session = await getUserSession(e);
if(!session || !session.user)
{
setResponseStatus(e, 401);
return;
}
const db = useDatabase();
try
{
const campaign = db.select({ owner: campaignTable.owner }).from(campaignTable).where(and(eq(campaignTable.id, sql.placeholder('id')))).get({ id: id });
if(campaign && campaign.owner === session.user.id) return setResponseStatus(e, 403);
const members = db.select({ id: campaignMembersTable.user }).from(campaignMembersTable).where(and(eq(campaignMembersTable.id, sql.placeholder('id')), eq(campaignMembersTable.user, sql.placeholder('user')))).get({ id: id, user: session.user.id });
if(members) return setResponseStatus(e, 403);
db.insert(campaignMembersTable).values({ id, rights: 'player', user: session.user!.id });
return setResponseStatus(e, 200);
}
catch(_e)
{
return setResponseStatus(e, 500);
}
});

View File

@ -1,4 +1,5 @@
import { and, eq, sql } from "drizzle-orm";
import useDatabase from "~/composables/useDatabase";
import { campaignMembersTable, campaignTable } from "~/db/schema";
export default defineEventHandler(async (e) => {

View File

@ -1,25 +0,0 @@
import { defineEventHandler, setResponseHeader } from 'h3';
export default defineEventHandler(async (event) => {
const acceptEncoding = event.headers.get('accept-encoding') || '';
if (!acceptEncoding.includes('zstd')) return;
const _end = event.node.res.end;
//@ts-expect-error
event.node.res.end = async (body: any, ...args: any[]) => {
const buffer = typeof body === "string" ? new TextEncoder().encode(body) : body;
if(buffer)
{
setResponseHeader(event, "Content-Encoding", "zstd");
setResponseHeader(event, "Vary", "Accept-Encoding");
//@ts-expect-error
_end.call(event.node.res, await Bun.zstdCompress(buffer), ...args);
}
else
{
//@ts-expect-error
_end.call(event.node.res, body, ...args);
}
};
});

View File

@ -0,0 +1,37 @@
import { and, eq, sql } from "drizzle-orm";
import useDatabase from "~/composables/useDatabase";
import { campaignMembersTable, campaignTable } from "~/db/schema";
import { decryptURI } from "~/shared/general.util";
export default defineEventHandler(async (e) => {
const link = getRouterParam(e, "link");
if(!link) return setResponseStatus(e, 400);
const id = decryptURI(decodeURIComponent(link), 'campaign');
if(!id) return sendRedirect(e, `/campaign`);
const session = await getUserSession(e);
if(!session || !session.user) return sendRedirect(e, `/user/login`);
const db = useDatabase();
try
{
const campaign = db.select({ owner: campaignTable.owner }).from(campaignTable).where(and(eq(campaignTable.id, sql.placeholder('id')), eq(campaignTable.link, sql.placeholder('link')))).get({ id, link: decodeURIComponent(link) });
if(!campaign) return setResponseStatus(e, 404);
if(campaign && campaign.owner === session.user.id) return sendRedirect(e, `/campaign/${id}`);
const members = db.select({ id: campaignMembersTable.user }).from(campaignMembersTable).where(and(eq(campaignMembersTable.id, sql.placeholder('id')), eq(campaignMembersTable.user, sql.placeholder('user')))).get({ id: id, user: session.user.id });
if(members) return sendRedirect(e, `/campaign/${id}`);
db.insert(campaignMembersTable).values({ id, rights: 'player', user: session.user!.id }).run();
return sendRedirect(e, `/campaign/${id}`);
}
catch(_e)
{
return sendRedirect(e, `/campaign`);
}
})

View File

@ -0,0 +1,22 @@
export default defineWebSocketHandler({
message(peer, message) {
},
open(peer) {
const id = new URL(peer.request.url).pathname.split('/').slice(-1)[0];
if(!id) return peer.close();
peer.subscribe(`campaigns/${id}`);
peer.publish(`campaigns/${id}`, true);
},
close(peer, details) {
const id = new URL(peer.request.url).pathname.split('/').slice(-1)[0];
if(!id) return peer.close();
peer.publish(`campaigns/${id}`, false);
peer.unsubscribe(`campaigns/${id}`);
},
error(peer, error) {
console.error(error);
}
})

View File

@ -3,6 +3,13 @@ import { z } from "zod/v4";
export const CampaignValidation = z.object({
id: z.number(),
name: z.string().nonempty(),
description: z.string(),
joinby: z.enum([ 'link', 'invite' ]),
description: z.string()
});
class CampaignSheet
{
constructor()
{
}
}

View File

@ -44,6 +44,7 @@ export const defaultCharacter: Character = {
exhaustion: 0,
sickness: [],
poisons: [],
money: 0,
},
owner: -1,
@ -563,6 +564,8 @@ export class CharacterBuilder extends CharacterCompiler
{
document.title = `d[any] - Edition de nouveau personnage`;
this.id = 'new';
this._character.id = -1;
this.render();
this.display(0);
}
@ -628,14 +631,21 @@ export class CharacterBuilder extends CharacterCompiler
{
if(this.id === 'new' || this.id === '-1')
{
//@ts-ignore
this.id = this._character.id = this._result.id = await useRequestFetch()(`/api/character`, {
const result = await useRequestFetch()(`/api/character`, {
method: 'post',
body: this._character,
onResponseError: (e) => {
Toaster.add({ title: 'Erreur d\'enregistrement', content: e.response.status === 401 ? "Vous n'êtes pas autorisé à effectué cette opération" : e.response.statusText, type: 'error', closeable: true, duration: 25000, timer: true });
this.id = 'new';
}
});
if(result !== undefined)
{
this._character.id = this._result.id = result as number;
this.id = result.toString();
}
Toaster.add({ content: 'Personnage créé', type: 'success', duration: 25000, timer: true });
useRouter().replace({ name: 'character-id-edit', params: { id: this.id } })
if(leave) useRouter().push({ name: 'character-id', params: { id: this.id } });

View File

@ -139,6 +139,7 @@ export class Content
try
{
Content._overview = parse<Record<string, Omit<LocalContent, 'content'>>>(overview);
Content._reverseMapping = Object.values(Content._overview).reduce((p, v) => { p[v.path] = v.id; return p; }, {} as Record<string, string>);
await Content.pull();
}
catch(e)
@ -245,16 +246,14 @@ export class Content
if(force || !_overview || new Date(_overview.timestamp) < new Date(file.timestamp))
{
Content._overview[file.id] = file;
Content._reverseMapping[file.path] = file.id;
Content.queue.queue(() => {
return useRequestFetch()(`/api/file/content/${file.id}`, { cache: 'no-cache' }).then(async (content: string | undefined | null) => {
if(content)
{
if(file.type !== 'folder')
{
Content.queue.queue(() => Content.write(file.id, content, { create: true }));
//Content.queue.queue(() => Content.write('storage/' + file.path + (file.type === 'canvas' ? '.canvas' : '.md'), Content.toString({ ...file, content: Content.fromString(file, content) }), { create: true }));
}
}
else
Content._overview[file.id]!.error = true;

View File

@ -69,3 +69,35 @@ export function lerp(x: number, a: number, b: number): number
{
return (1-x)*a+x*b;
}
// The value position is randomized
// The metadata separator is randomized as a letter (to avoid collision with numbers)
// The URI is (| == picked separator) |v_length|first part of the hash + seed + second part of the hash|v_pos|seed as hex.
// Every number are converted to string as hexadecimal values
export function cryptURI(key: string, value: number, seed?: number): string
{
const _seed = seed ?? Date.now();
const hash = Bun.hash(key + value.toString(), _seed).toString(16);
const pos = Math.floor(Math.random() * hash.length);
const separator = String.fromCharCode(Math.floor(Math.random() * 26 + 96));
return Bun.zstdCompressSync(separator + value.toString(16).length + separator + hash.substring(0, pos) + value + hash.substring(pos) + separator + pos + separator + _seed.toString(16), { level: 1 }).toString('base64');
}
export function decryptURI(uri: string, key: string): number | undefined
{
const decoder = new TextDecoder();
const _uri = decoder.decode(Bun.zstdDecompressSync(Buffer.from(uri, 'base64')));
const separator = _uri.charAt(0);
const length = parseInt(_uri.substring(1, _uri.indexOf(separator, 1)), 10);
const seed = parseInt(_uri.substring(_uri.lastIndexOf(separator) + 1), 16);
const pos = parseInt(_uri.substring(_uri.lastIndexOf(separator, _uri.length - seed.toString(16).length - 2) + 1, _uri.lastIndexOf(separator)), 10);
const _hash = _uri.substring(2 + length.toString(10).length, _uri.length - (2 + seed.toString(16).length + pos.toString(10).length));
const value = _hash.substring(pos, pos + length);
const hash = _hash.substring(0, pos) + _hash.substring(pos + length);
if(Bun.hash(key + value, seed).toString(16) === hash)
return parseInt(value, 10);
else
return undefined;
}