Add insertion and deletion of tables for db sync, and user view
This commit is contained in:
parent
fa1a13d411
commit
e904f28b3b
|
|
@ -76,7 +76,7 @@ async function debounced()
|
||||||
<div class="cursor-pointer hover:bg-light-25 dark:hover:bg-dark-25 px-4 py-1 " v-for="result of results.users" :key="result.id"
|
<div class="cursor-pointer hover:bg-light-25 dark:hover:bg-dark-25 px-4 py-1 " v-for="result of results.users" :key="result.id"
|
||||||
@mouseenter="(e) => (e.target as HTMLElement).classList.add('is-selected')"
|
@mouseenter="(e) => (e.target as HTMLElement).classList.add('is-selected')"
|
||||||
@mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')"
|
@mouseleave="(e) => (e.target as HTMLElement).classList.remove('is-selected')"
|
||||||
@mousedown.prevent="navigateTo(`/user/${result.id}`); input = ''; emit('navigate');">
|
@mousedown.prevent="navigateTo(`/users/${result.id}`); input = ''; emit('navigate');">
|
||||||
<div class="">
|
<div class="">
|
||||||
<Highlight class="text-lg" :text="result.username" :matched="input" />
|
<Highlight class="text-lg" :text="result.username" :matched="input" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -26,22 +26,41 @@ export default defineNuxtModule({
|
||||||
|
|
||||||
const db = new Database(nuxt.options.runtimeConfig.dbFile);
|
const db = new Database(nuxt.options.runtimeConfig.dbFile);
|
||||||
db.exec(`PRAGMA foreign_keys = 0`);
|
db.exec(`PRAGMA foreign_keys = 0`);
|
||||||
|
const oldSchema = db.query(`SELECT * FROM sqlite_schema WHERE type = 'table'`).all() as Schema[];
|
||||||
const structure = db.query(`SELECT * FROM pragma_table_info(?1)`);
|
const structure = db.query(`SELECT * FROM pragma_table_info(?1)`);
|
||||||
|
|
||||||
(db.transaction((tables: Schema[]) => {
|
(db.transaction((tables: Schema[], oldTables: Schema[]) => {
|
||||||
for(const table of tables)
|
for(const table of tables)
|
||||||
{
|
{
|
||||||
|
const oldIdx = oldTables.findIndex(e => e.name === table.name);
|
||||||
|
|
||||||
if(table.name === 'sqlite_sequence')
|
if(table.name === 'sqlite_sequence')
|
||||||
|
{
|
||||||
|
oldTables.splice(oldIdx, 1);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const columns = structure.all(table.name) as Structure[];
|
const columns = structure.all(table.name) as Structure[];
|
||||||
|
|
||||||
|
if(oldIdx !== -1)
|
||||||
|
{
|
||||||
|
oldTables.splice(oldIdx, 1);
|
||||||
|
|
||||||
db.exec(`ALTER TABLE ${table.name} RENAME TO ${table.name}_old`);
|
db.exec(`ALTER TABLE ${table.name} RENAME TO ${table.name}_old`);
|
||||||
db.exec(table.sql);
|
db.exec(table.sql);
|
||||||
db.exec(`INSERT INTO ${table.name} (${columns.map(e => `"${e.name}"`).join(', ')}) SELECT * FROM ${table.name}_old`);
|
db.exec(`INSERT INTO ${table.name} (${columns.map(e => `"${e.name}"`).join(', ')}) SELECT * FROM ${table.name}_old`);
|
||||||
db.exec(`DROP TABLE ${table.name}_old`);
|
db.exec(`DROP TABLE ${table.name}_old`);
|
||||||
}
|
}
|
||||||
})).immediate(schema);
|
else
|
||||||
|
{
|
||||||
|
db.exec(table.sql);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(const table of oldTables)
|
||||||
|
{
|
||||||
|
db.exec(`DROP TABLE ${table.name}`);
|
||||||
|
}
|
||||||
|
})).immediate(schema, oldSchema);
|
||||||
|
|
||||||
db.exec(`PRAGMA foreign_keys = 1`);
|
db.exec(`PRAGMA foreign_keys = 1`);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,8 @@
|
||||||
<Head>
|
<Head>
|
||||||
<Title>Inconnu</Title>
|
<Title>Inconnu</Title>
|
||||||
</Head>
|
</Head>
|
||||||
<div class="site-body-center-column">
|
<div class="h-100 w-100 flex flex-1 flex-col justify-center items-center">
|
||||||
<div class="render-container">
|
<div class="text-3xl font-extralight tracking-wide text-light-60 dark:text-dark-60">Introuvable</div>
|
||||||
<div class="not-found-container">
|
<div class="text-lg text-light-60 dark:text-dark-60">Cette page n'existe pas</div>
|
||||||
<div class="not-found-image"></div>
|
|
||||||
<div class="not-found-title">Introuvable</div>
|
|
||||||
<div class="not-found-description">Cette page n'existe pas</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -1,6 +1,31 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
const route = useRoute();
|
||||||
|
const { data: user } = useFetch(`/api/users/${route.params.id}`);
|
||||||
|
const { data: projects } = useFetch(`/api/users/${route.params.id}/projects`);
|
||||||
|
const { data: comments } = useFetch(`/api/users/${route.params.id}/comments`);
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Head>
|
<Head>
|
||||||
<Title>Inconnu</Title>
|
<Title>Inconnu</Title>
|
||||||
</Head>
|
</Head>
|
||||||
<div>TODO :)</div>
|
<div v-if="user" class="border border-light-35 dark:border-dark-35 p-4 flex">
|
||||||
|
<div>
|
||||||
|
<picture :width=128 :height=128 class="flex" >
|
||||||
|
<source :src="`/users/${user?.id}/normal.jpg`" :width=128 :height=128 />
|
||||||
|
<Icon :icon="`users/unknown`" :width=128 :height=128 ></Icon>
|
||||||
|
</picture>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<span class="text-xl font-semibold">{{ user?.username }}</span>
|
||||||
|
<span>Inscrit depuis le {{ format(new Date(user.signin_timestamp), 'dd/MM/yyyy') }}</span>
|
||||||
|
<br/>
|
||||||
|
<span>A créé {{ projects?.length ?? 0 }} projet(s)</span>
|
||||||
|
<span>A créé {{ comments?.length ?? 0 }} commentaire(s)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="h-100 w-100 flex flex-1 flex-col justify-center items-center">
|
||||||
|
<div class="text-3xl font-extralight tracking-wide text-light-60 dark:text-dark-60">Introuvable</div>
|
||||||
|
<div class="text-lg text-light-60 dark:text-dark-60">Cette page n'existe pas</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -62,13 +62,13 @@ export default defineEventHandler(async (e): Promise<Return> => {
|
||||||
{
|
{
|
||||||
const hash = await Bun.password.hash(body.data.password);
|
const hash = await Bun.password.hash(body.data.password);
|
||||||
const registration = db.query(`INSERT INTO users(username, email, hash, state) VALUES(?1, ?2, ?3, 0)`);
|
const registration = db.query(`INSERT INTO users(username, email, hash, state) VALUES(?1, ?2, ?3, 0)`);
|
||||||
registration.get(body.data.username, body.data.email, hash) as any;
|
registration.run(body.data.username, body.data.email, hash);
|
||||||
|
|
||||||
const userIdQuery = db.query(`SELECT id FROM users WHERE username = ?1`);
|
const userIdQuery = db.query(`SELECT id FROM users WHERE username = ?1`);
|
||||||
const id = (userIdQuery.get(body.data.username) as any).id;
|
const id = (userIdQuery.get(body.data.username) as any).id;
|
||||||
|
|
||||||
const registeringData = db.query(`INSERT INTO users_data(user_id) VALUES(?1)`);
|
const registeringData = db.query(`INSERT INTO users_data(user_id, signin_timestamp) VALUES(?1, ?2)`);
|
||||||
registeringData.get(id);
|
registeringData.run(id, Date.now());
|
||||||
|
|
||||||
logSession(e, await setUserSession(e, { user: { id: id, username: body.data.username, email: body.data.email, state: 0 } }) as UserSessionRequired);
|
logSession(e, await setUserSession(e, { user: { id: id, username: body.data.username, email: body.data.email, state: 0 } }) as UserSessionRequired);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,5 @@ export default defineEventHandler((e) => {
|
||||||
|
|
||||||
const db = useDatabase();
|
const db = useDatabase();
|
||||||
|
|
||||||
return db.query(`SELECT id, usernamme, email, state FROM users WHERE id = ?1`).get(id) as User;
|
return db.query(`SELECT id, username, email, state, d.* FROM users u LEFT JOIN users_data d ON u.id = d.user_id WHERE u.id = ?1`).get(id) as User;
|
||||||
});
|
});
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import useDatabase from "~/composables/useDatabase";
|
||||||
|
import type { Comment } from "~/types/auth";
|
||||||
|
|
||||||
|
export default defineEventHandler((e) => {
|
||||||
|
const id = getRouterParam(e, 'id');
|
||||||
|
|
||||||
|
if(!id)
|
||||||
|
{
|
||||||
|
setResponseStatus(e, 400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = useDatabase();
|
||||||
|
|
||||||
|
return db.query(`SELECT * FROM explorer_comments WHERE user_id = ?1`).all(id) as Comment[];
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import useDatabase from "~/composables/useDatabase";
|
||||||
|
import type { Project } from "~/types/api";
|
||||||
|
|
||||||
|
export default defineEventHandler((e) => {
|
||||||
|
const id = getRouterParam(e, 'id');
|
||||||
|
|
||||||
|
if(!id)
|
||||||
|
{
|
||||||
|
setResponseStatus(e, 400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = useDatabase();
|
||||||
|
|
||||||
|
return db.query(`SELECT * FROM explorer_projects WHERE owner = ?1`).all(id) as Project[];
|
||||||
|
});
|
||||||
|
|
@ -11,10 +11,12 @@ export default defineNitroPlugin(() => {
|
||||||
|
|
||||||
if(!result)
|
if(!result)
|
||||||
{
|
{
|
||||||
|
clearUserSession(event);
|
||||||
throw createError({ statusCode: 401, message: 'Unauthorized' });
|
throw createError({ statusCode: 401, message: 'Unauthorized' });
|
||||||
}
|
}
|
||||||
else if(result && result.lastRefresh && result.lastRefresh < Date.now() - monthAsMs)
|
else if(result && result.lastRefresh && result.lastRefresh < Date.now() - monthAsMs)
|
||||||
{
|
{
|
||||||
|
clearUserSession(event);
|
||||||
throw createError({ statusCode: 401, message: 'Session has expired' });
|
throw createError({ statusCode: 401, message: 'Session has expired' });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -24,9 +26,13 @@ export default defineNitroPlugin(() => {
|
||||||
});
|
});
|
||||||
sessionHooks.hook('clear', async (session, event) => {
|
sessionHooks.hook('clear', async (session, event) => {
|
||||||
if(session.id && session.user)
|
if(session.id && session.user)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
const query = db.prepare('DELETE FROM user_sessions WHERE id = ?1 AND user_id = ?2');
|
const query = db.prepare('DELETE FROM user_sessions WHERE id = ?1 AND user_id = ?2');
|
||||||
query.run(session.id, session.user.id);
|
query.run(session.id, session.user.id);
|
||||||
}
|
}
|
||||||
|
catch(e) { }
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
BIN
template.sqlite
BIN
template.sqlite
Binary file not shown.
|
|
@ -28,7 +28,7 @@ export interface UserRawData {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserExtendedData {
|
export interface UserExtendedData {
|
||||||
|
signin_timestamp: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type User = UserRawData & UserExtendedData;
|
export type User = UserRawData & UserExtendedData;
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,30 @@ export function parseId(id: string | undefined): string |undefined
|
||||||
{
|
{
|
||||||
return id?.normalize('NFD')?.replace(/[\u0300-\u036f]/g, '')?.replace(/^\d\. */g, '')?.replace(/\s/g, "-")?.replace(/%/g, "-percent")?.replace(/\?/g, "-q")?.toLowerCase();
|
return id?.normalize('NFD')?.replace(/[\u0300-\u036f]/g, '')?.replace(/^\d\. */g, '')?.replace(/\s/g, "-")?.replace(/%/g, "-percent")?.replace(/\?/g, "-q")?.toLowerCase();
|
||||||
}
|
}
|
||||||
|
export function padLeft(text: string, pad: string, length: number): string
|
||||||
|
{
|
||||||
|
return text.concat(pad.repeat(length - text.length));
|
||||||
|
}
|
||||||
|
export function padRight(text: string, pad: string, length: number): string
|
||||||
|
{
|
||||||
|
return pad.repeat(length - text.length).concat(text);
|
||||||
|
}
|
||||||
|
export function format(date: Date, template: string): string
|
||||||
|
{
|
||||||
|
const transforms = {
|
||||||
|
"yyyy": (date: Date) => date.getUTCFullYear().toString(),
|
||||||
|
"MM": (date: Date) => padRight((date.getUTCMonth() + 1).toString(), '0', 2),
|
||||||
|
"dd": (date: Date) => padRight(date.getUTCDate().toString(), '0', 2),
|
||||||
|
"mm": (date: Date) => padRight(date.getFullYear().toString(), '0', 2),
|
||||||
|
"HH": (date: Date) => padRight(date.getFullYear().toString(), '0', 2),
|
||||||
|
"ss": (date: Date) => padRight(date.getFullYear().toString(), '0', 2),
|
||||||
|
};
|
||||||
|
const keys = Object.keys(transforms);
|
||||||
|
|
||||||
|
for(const key of keys)
|
||||||
|
{
|
||||||
|
template = template.replaceAll(key, () => transforms[key](date));
|
||||||
|
}
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue