diff --git a/.gitignore b/.gitignore index c62c698..4a7f73a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ .nitro .cache dist -content # Node dependencies node_modules @@ -23,5 +22,3 @@ logs .env .env.* !.env.example - -db.sqlite-* \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index ae0ea2a..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "type": "chrome", - "request": "launch", - "name": "client: chrome", - "url": "http://localhost:3000", - "webRoot": "${workspaceFolder}" - }, - { - "type": "node", - "request": "launch", - "name": "server: nuxt", - "outputCapture": "std", - "program": "${workspaceFolder}/node_modules/nuxi/bin/nuxi.mjs", - "args": [ - "dev" - ], - } - ], - "compounds": [ - { - "name": "fullstack: nuxt", - "configurations": [ - "server: nuxt", - "client: chrome" - ] - } - ] -} \ No newline at end of file diff --git a/app.vue b/app.vue index b7594fa..6db39f9 100644 --- a/app.vue +++ b/app.vue @@ -1,49 +1,12 @@ - - - - diff --git a/assets/third-party.md b/assets/third-party.md deleted file mode 100644 index 5d089f7..0000000 --- a/assets/third-party.md +++ /dev/null @@ -1,7 +0,0 @@ -### Librairies, framework et outils libres utilisés: -- [Vuejs](https://vuejs.org/) - MIT License - Copyright (c) 2018-present, Yuxi (Evan) You and Vue contributors -- [vue-router](https://router.vuejs.org/) - MIT License - Copyright (c) 2019-present Eduardo San Martin Morote -- [Nuxt](https://nuxt.com/) - MIT License - Copyright (c) 2016-present - Nuxt Team -- [nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils) - MIT License - Copyright (c) 2023 Sébastien Chopin - -Le logo a été créé grace aux icones de [Game Icons](https://game-icons.net). \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index d37f33e..586211d 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/EditableMarkdown.vue b/components/EditableMarkdown.vue deleted file mode 100644 index 787d1b2..0000000 --- a/components/EditableMarkdown.vue +++ /dev/null @@ -1,130 +0,0 @@ - - - - - \ No newline at end of file diff --git a/components/Toast.vue b/components/Toast.vue new file mode 100644 index 0000000..66f7a6b --- /dev/null +++ b/components/Toast.vue @@ -0,0 +1,39 @@ + + + + + \ No newline at end of file diff --git a/components/Tooltip.vue b/components/Tooltip.vue new file mode 100644 index 0000000..2fe972d --- /dev/null +++ b/components/Tooltip.vue @@ -0,0 +1,83 @@ + + + + + \ No newline at end of file diff --git a/components/canvas/CanvasEdge.vue b/components/canvas/CanvasEdge.vue deleted file mode 100644 index 32ec580..0000000 --- a/components/canvas/CanvasEdge.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - \ No newline at end of file diff --git a/components/canvas/CanvasNode.vue b/components/canvas/CanvasNode.vue deleted file mode 100644 index 796f62d..0000000 --- a/components/canvas/CanvasNode.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - - - \ No newline at end of file diff --git a/components/canvas/CanvasRenderer.vue b/components/canvas/CanvasRenderer.vue deleted file mode 100644 index 0f19190..0000000 --- a/components/canvas/CanvasRenderer.vue +++ /dev/null @@ -1,244 +0,0 @@ - - - \ No newline at end of file diff --git a/components/explorer/CommentSide.vue b/components/explorer/CommentSide.vue deleted file mode 100644 index 06fdd4b..0000000 --- a/components/explorer/CommentSide.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - \ No newline at end of file diff --git a/components/explorer/ExplorerNavigation.vue b/components/explorer/ExplorerNavigation.vue deleted file mode 100644 index 92aacf6..0000000 --- a/components/explorer/ExplorerNavigation.vue +++ /dev/null @@ -1,20 +0,0 @@ - - - \ No newline at end of file diff --git a/components/explorer/NavigationLink.vue b/components/explorer/NavigationLink.vue deleted file mode 100644 index 16f9a3d..0000000 --- a/components/explorer/NavigationLink.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - \ No newline at end of file diff --git a/components/explorer/PreviewContent.vue b/components/explorer/PreviewContent.vue deleted file mode 100644 index d57be5d..0000000 --- a/components/explorer/PreviewContent.vue +++ /dev/null @@ -1,65 +0,0 @@ - - - \ No newline at end of file diff --git a/components/prose/ProseA.vue b/components/prose/ProseA.vue deleted file mode 100644 index cdf9c39..0000000 --- a/components/prose/ProseA.vue +++ /dev/null @@ -1,50 +0,0 @@ - - - \ No newline at end of file diff --git a/components/prose/ProseBlockquote.vue b/components/prose/ProseBlockquote.vue deleted file mode 100644 index 7cfa02d..0000000 --- a/components/prose/ProseBlockquote.vue +++ /dev/null @@ -1,182 +0,0 @@ - - - - - \ No newline at end of file diff --git a/components/prose/ProseCode.vue b/components/prose/ProseCode.vue deleted file mode 100644 index b15c934..0000000 --- a/components/prose/ProseCode.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/components/prose/ProseEm.vue b/components/prose/ProseEm.vue deleted file mode 100644 index 7f4ae1f..0000000 --- a/components/prose/ProseEm.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/ProseH1.vue b/components/prose/ProseH1.vue deleted file mode 100644 index c210949..0000000 --- a/components/prose/ProseH1.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/components/prose/ProseH2.vue b/components/prose/ProseH2.vue deleted file mode 100644 index 2e5b56c..0000000 --- a/components/prose/ProseH2.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/prose/ProseH3.vue b/components/prose/ProseH3.vue deleted file mode 100644 index 779d877..0000000 --- a/components/prose/ProseH3.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/prose/ProseH4.vue b/components/prose/ProseH4.vue deleted file mode 100644 index d6338c5..0000000 --- a/components/prose/ProseH4.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/components/prose/ProseH5.vue b/components/prose/ProseH5.vue deleted file mode 100644 index b85ab77..0000000 --- a/components/prose/ProseH5.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/prose/ProseH6.vue b/components/prose/ProseH6.vue deleted file mode 100644 index 9e63455..0000000 --- a/components/prose/ProseH6.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/prose/ProseHr.vue b/components/prose/ProseHr.vue deleted file mode 100644 index 5f70ba4..0000000 --- a/components/prose/ProseHr.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/components/prose/ProseImg.vue b/components/prose/ProseImg.vue deleted file mode 100644 index 8244bb1..0000000 --- a/components/prose/ProseImg.vue +++ /dev/null @@ -1,42 +0,0 @@ - - - diff --git a/components/prose/ProseLi.vue b/components/prose/ProseLi.vue deleted file mode 100644 index 3b3a838..0000000 --- a/components/prose/ProseLi.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/components/prose/ProseOl.vue b/components/prose/ProseOl.vue deleted file mode 100644 index 7d3a886..0000000 --- a/components/prose/ProseOl.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/ProseP.vue b/components/prose/ProseP.vue deleted file mode 100644 index a5d480d..0000000 --- a/components/prose/ProseP.vue +++ /dev/null @@ -1,12 +0,0 @@ - - - \ No newline at end of file diff --git a/components/prose/ProsePre.vue b/components/prose/ProsePre.vue deleted file mode 100644 index ab89465..0000000 --- a/components/prose/ProsePre.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - - - diff --git a/components/prose/ProseScript.vue b/components/prose/ProseScript.vue deleted file mode 100644 index e95f58a..0000000 --- a/components/prose/ProseScript.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/components/prose/ProseStrong.vue b/components/prose/ProseStrong.vue deleted file mode 100644 index 994fae3..0000000 --- a/components/prose/ProseStrong.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/ProseTable.vue b/components/prose/ProseTable.vue deleted file mode 100644 index a8ca108..0000000 --- a/components/prose/ProseTable.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/ProseTag.vue b/components/prose/ProseTag.vue deleted file mode 100644 index 0bdd589..0000000 --- a/components/prose/ProseTag.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - \ No newline at end of file diff --git a/components/prose/ProseTbody.vue b/components/prose/ProseTbody.vue deleted file mode 100644 index 09d6191..0000000 --- a/components/prose/ProseTbody.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/ProseTd.vue b/components/prose/ProseTd.vue deleted file mode 100644 index 57d94fc..0000000 --- a/components/prose/ProseTd.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/ProseTh.vue b/components/prose/ProseTh.vue deleted file mode 100644 index fd3a967..0000000 --- a/components/prose/ProseTh.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/ProseThead.vue b/components/prose/ProseThead.vue deleted file mode 100644 index 95dbdb7..0000000 --- a/components/prose/ProseThead.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/ProseTr.vue b/components/prose/ProseTr.vue deleted file mode 100644 index 926f7c1..0000000 --- a/components/prose/ProseTr.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/ProseUl.vue b/components/prose/ProseUl.vue deleted file mode 100644 index 5c12ce9..0000000 --- a/components/prose/ProseUl.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/components/prose/live/LiveA.vue b/components/prose/live/LiveA.vue deleted file mode 100644 index 9463c60..0000000 --- a/components/prose/live/LiveA.vue +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/components/prose/live/LiveBlockquote.vue b/components/prose/live/LiveBlockquote.vue deleted file mode 100644 index 7cfa02d..0000000 --- a/components/prose/live/LiveBlockquote.vue +++ /dev/null @@ -1,182 +0,0 @@ - - - - - \ No newline at end of file diff --git a/components/prose/live/LiveH1.vue b/components/prose/live/LiveH1.vue deleted file mode 100644 index 00aa20e..0000000 --- a/components/prose/live/LiveH1.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/components/prose/live/LiveH2.vue b/components/prose/live/LiveH2.vue deleted file mode 100644 index 45ec119..0000000 --- a/components/prose/live/LiveH2.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/prose/live/LiveH3.vue b/components/prose/live/LiveH3.vue deleted file mode 100644 index 779d877..0000000 --- a/components/prose/live/LiveH3.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/prose/live/LiveH4.vue b/components/prose/live/LiveH4.vue deleted file mode 100644 index d6338c5..0000000 --- a/components/prose/live/LiveH4.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/components/prose/live/LiveH5.vue b/components/prose/live/LiveH5.vue deleted file mode 100644 index b85ab77..0000000 --- a/components/prose/live/LiveH5.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/prose/live/LiveH6.vue b/components/prose/live/LiveH6.vue deleted file mode 100644 index 9e63455..0000000 --- a/components/prose/live/LiveH6.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/components/prose/live/LiveTag.vue b/components/prose/live/LiveTag.vue deleted file mode 100644 index d022531..0000000 --- a/components/prose/live/LiveTag.vue +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/components/standard/Accordion.vue b/components/standard/Accordion.vue deleted file mode 100644 index 43dd040..0000000 --- a/components/standard/Accordion.vue +++ /dev/null @@ -1,19 +0,0 @@ - - \ No newline at end of file diff --git a/components/standard/Dialog.vue b/components/standard/Dialog.vue deleted file mode 100644 index e9d88dc..0000000 --- a/components/standard/Dialog.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/Highlight.vue b/components/standard/Highlight.vue deleted file mode 100644 index 5ca62eb..0000000 --- a/components/standard/Highlight.vue +++ /dev/null @@ -1,27 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/HoverPopup.vue b/components/standard/HoverPopup.vue deleted file mode 100644 index fb19620..0000000 --- a/components/standard/HoverPopup.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/Icon.vue b/components/standard/Icon.vue deleted file mode 100644 index 2a03a02..0000000 --- a/components/standard/Icon.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/Input.vue b/components/standard/Input.vue deleted file mode 100644 index ef6b51b..0000000 --- a/components/standard/Input.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/InputField.vue b/components/standard/InputField.vue deleted file mode 100644 index f69830a..0000000 --- a/components/standard/InputField.vue +++ /dev/null @@ -1,21 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/Markdown.vue b/components/standard/Markdown.vue deleted file mode 100644 index 8e31498..0000000 --- a/components/standard/Markdown.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/MarkdownRenderer.vue b/components/standard/MarkdownRenderer.vue deleted file mode 100644 index 0a0cf14..0000000 --- a/components/standard/MarkdownRenderer.vue +++ /dev/null @@ -1,111 +0,0 @@ - \ No newline at end of file diff --git a/components/standard/NavBar.vue b/components/standard/NavBar.vue deleted file mode 100644 index a60836b..0000000 --- a/components/standard/NavBar.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/Picture.vue b/components/standard/Picture.vue deleted file mode 100644 index fcec929..0000000 --- a/components/standard/Picture.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/SearchView.vue b/components/standard/SearchView.vue deleted file mode 100644 index 4f48dc2..0000000 --- a/components/standard/SearchView.vue +++ /dev/null @@ -1,91 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/ThemeIcon.vue b/components/standard/ThemeIcon.vue deleted file mode 100644 index 0cb69a3..0000000 --- a/components/standard/ThemeIcon.vue +++ /dev/null @@ -1,16 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/ThemeSwitch.client.vue b/components/standard/ThemeSwitch.client.vue deleted file mode 100644 index 564cec6..0000000 --- a/components/standard/ThemeSwitch.client.vue +++ /dev/null @@ -1,39 +0,0 @@ - - - \ No newline at end of file diff --git a/components/standard/Upload.vue b/components/standard/Upload.vue deleted file mode 100644 index dc3491e..0000000 --- a/components/standard/Upload.vue +++ /dev/null @@ -1,27 +0,0 @@ - - - \ No newline at end of file diff --git a/README.md b/components/todo/Accordeon.vue similarity index 100% rename from README.md rename to components/todo/Accordeon.vue diff --git a/server/api/project/[projectId]/access.post.ts b/components/todo/Checkbox.vue similarity index 100% rename from server/api/project/[projectId]/access.post.ts rename to components/todo/Checkbox.vue diff --git a/server/api/project/[projectId]/comment.post.ts b/components/todo/Collapsible.vue similarity index 100% rename from server/api/project/[projectId]/comment.post.ts rename to components/todo/Collapsible.vue diff --git a/server/api/project/[projectId]/file.post.ts b/components/todo/Combobox.vue similarity index 100% rename from server/api/project/[projectId]/file.post.ts rename to components/todo/Combobox.vue diff --git a/components/todo/ContextMenu.vue b/components/todo/ContextMenu.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/Dialog.vue b/components/todo/Dialog.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/HoverCard.vue b/components/todo/HoverCard.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/Icon.vue b/components/todo/Icon.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/Menu.vue b/components/todo/Menu.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/NumberPicker.vue b/components/todo/NumberPicker.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/Pagination.vue b/components/todo/Pagination.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/PinPicker.vue b/components/todo/PinPicker.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/Progress.vue b/components/todo/Progress.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/RadioInput.vue b/components/todo/RadioInput.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/SelectInput.vue b/components/todo/SelectInput.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/SliderInput.vue b/components/todo/SliderInput.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/Switch.vue b/components/todo/Switch.vue new file mode 100644 index 0000000..e69de29 diff --git a/components/todo/TextInput.vue b/components/todo/TextInput.vue new file mode 100644 index 0000000..e69de29 diff --git a/composables/useDatabase.ts b/composables/useDatabase.ts index 8ab2990..e5486a3 100644 --- a/composables/useDatabase.ts +++ b/composables/useDatabase.ts @@ -1,21 +1,13 @@ +import ".dotenv/config"; import { Database } from "bun:sqlite"; +import { drizzle } from "drizzle-orm/bun-sqlite"; -let instance: Database | undefined; - -export default function useDatabase(): Database { - if(instance === undefined) - instance = getDatabase(); - - return instance; -} - -function getDatabase(): Database +export default function useDatabase() { - const { dbFile } = useRuntimeConfig(); + const sqlite = new Database(process.env.DB_FILE); + const db = drizzle({ client: sqlite }); - const db = new Database(dbFile); - - db.exec("PRAGMA journal_mode = WAL;"); + db.run("PRAGMA journal_mode = WAL;"); return db; } \ No newline at end of file diff --git a/composables/useMarkdown.ts b/composables/useMarkdown.ts deleted file mode 100644 index 2b5b06d..0000000 --- a/composables/useMarkdown.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { unified, type Processor } from "unified"; -import type { Root } from 'hast'; -import RemarkParse from "remark-parse"; - -import RemarkRehype from 'remark-rehype'; -import RemarkOfm from 'remark-ofm'; -import RemarkBreaks from 'remark-breaks' -import RemarkGfm from 'remark-gfm'; -import RemarkFrontmatter from 'remark-frontmatter'; -import RehypeRaw from 'rehype-raw'; - -export default function useMarkdown(): (md: string) => Root -{ - let processor: Processor; - - const parse = (markdown: string) => { - if (!processor) - { - processor = unified().use([RemarkParse, RemarkGfm, RemarkOfm, RemarkBreaks, RemarkFrontmatter]); - processor.use(RemarkRehype, { allowDangerousHtml: true }); - processor.use(RehypeRaw); - } - - const processed = processor.runSync(processor.parse(markdown)) as Root; - return processed; - } - - return parse; -} \ No newline at end of file diff --git a/composables/useProject.ts b/composables/useProject.ts deleted file mode 100644 index 88bbfa4..0000000 --- a/composables/useProject.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { Project } from "~/types/api"; - -export default function useProject() -{ - const project = useCookie('project'); - - const id = useState("projectId", () => parseInt(project.value ?? '0')); - const name = useState("projectName", undefined); - const owner = useState("projectOwner", undefined); - const home = useState("projectHomepage", () => null); - const summary = useState("projectSummary", () => null); - - return { - id, name, owner, home, summary, get, set - }; -} - -async function get(): Promise { - const id = useState("projectId"); - - if (!id.value) - return false; - - try { - const result = await $fetch(`/api/project/${id.value}`) as Project; - - const name = useState("projectName"); - const owner = useState("projectOwner"); - const home = useState("projectHomepage"); - const summary = useState("projectSummary"); - - name.value = result.name; - owner.value = result.owner; - home.value = result.home; - summary.value = result.summary; - - return true; - } catch(e) { - return false; - } -} -async function set(id: number): Promise { - const _id = useState("projectId"); - - _id.value = id; - const project = useCookie('project'); - project.value = id.toString(); - - return await get(); -} \ No newline at end of file diff --git a/composables/useUserSession.ts b/composables/useUserSession.ts deleted file mode 100644 index d225abb..0000000 --- a/composables/useUserSession.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { UserSession, UserSessionComposable } from '~/types/auth' - -const useSessionState = () => useState('nuxt-session', () => ({})) -const useAuthReadyState = () => useState('nuxt-auth-ready', () => false) - -/** - * Composable to get back the user session and utils around it. - * @see https://github.com/atinux/nuxt-auth-utils - */ -export function useUserSession(): UserSessionComposable { - const sessionState = useSessionState() - const authReadyState = useAuthReadyState() - return { - ready: computed(() => authReadyState.value), - loggedIn: computed(() => Boolean(sessionState.value.user)), - user: computed(() => sessionState.value.user || null), - session: sessionState, - fetch, - clear, - } -} - -async function fetch() { - const authReadyState = useAuthReadyState() - useSessionState().value = await useRequestFetch()('/api/auth/session', { - headers: { - Accept: 'text/json', - }, - retry: false, - }).catch(() => ({})) - if (!authReadyState.value) { - authReadyState.value = true - } -} - -async function clear() { - await $fetch('/api/auth/session', { method: 'DELETE' }) - useSessionState().value = {} - useRouter().go(0); -} \ No newline at end of file diff --git a/db.sqlite b/db.sqlite index 84a1d69..a0daa29 100644 Binary files a/db.sqlite and b/db.sqlite differ diff --git a/db/schema.ts b/db/schema.ts new file mode 100644 index 0000000..1fd87c8 --- /dev/null +++ b/db/schema.ts @@ -0,0 +1,33 @@ +import { int, text, sqliteTable, type SQLiteTableExtraConfig, primaryKey, blob } from 'drizzle-orm/sqlite-core'; + +export const usersTable = sqliteTable("users", { + id: int().primaryKey({ autoIncrement: true }), + username: text().notNull().unique(), + email: text().notNull().unique(), + hash: text().notNull().unique(), + state: int().default(0), +}); + +export const usersDataTable = sqliteTable("users_data", { + id: int().primaryKey().references(() => usersTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }), +}); + +export const userSessionsTable = sqliteTable("user_sessions", { + id: int().notNull(), + user_id: int().notNull().references(() => usersTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }), + timestamp: int({ mode: 'timestamp' }).notNull().$defaultFn(() => new Date()), +}, (table): SQLiteTableExtraConfig => { + return { + pk: primaryKey({ columns: [ table.id, table.user_id ] }), + } +}); + +export const explorerContentTable = sqliteTable("explorer_content", { + path: text().primaryKey(), + owner: int().notNull().references(() => usersTable.id, { onDelete: 'cascade', onUpdate: 'cascade' }), + title: text().notNull(), + type: text({ enum: ['file', 'folder', 'markdown', 'canvas'] }).notNull(), + content: blob({ mode: 'buffer' }), + navigable: int({ mode: 'boolean' }).default(true), + private: int({ mode: 'boolean' }).default(false), +}); \ No newline at end of file diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..b19c6f8 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,11 @@ +import 'dotenv/config'; +import { defineConfig } from 'drizzle-kit'; + +export default defineConfig({ + out: './drizzle', + schema: './db/schema.ts', + dialect: 'sqlite', + dbCredentials: { + url: process.env.DB_FILE!, + }, +}); \ No newline at end of file diff --git a/drizzle/0000_youthful_ma_gnuci.sql b/drizzle/0000_youthful_ma_gnuci.sql new file mode 100644 index 0000000..86df767 --- /dev/null +++ b/drizzle/0000_youthful_ma_gnuci.sql @@ -0,0 +1,35 @@ +CREATE TABLE `explorer_content` ( + `path` text PRIMARY KEY NOT NULL, + `owner` integer NOT NULL, + `title` text NOT NULL, + `type` text NOT NULL, + `content` blob, + `navigable` integer DEFAULT true, + `private` integer DEFAULT false, + FOREIGN KEY (`owner`) REFERENCES `users`(`id`) ON UPDATE cascade ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `user_sessions` ( + `id` integer NOT NULL, + `user_id` integer NOT NULL, + `timestamp` integer NOT NULL, + PRIMARY KEY(`id`, `user_id`), + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE cascade ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `users_data` ( + `id` integer PRIMARY KEY NOT NULL, + FOREIGN KEY (`id`) REFERENCES `users`(`id`) ON UPDATE cascade ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `users` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `username` text NOT NULL, + `email` text NOT NULL, + `hash` text NOT NULL, + `state` integer DEFAULT 0 +); +--> statement-breakpoint +CREATE UNIQUE INDEX `users_username_unique` ON `users` (`username`);--> statement-breakpoint +CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint +CREATE UNIQUE INDEX `users_hash_unique` ON `users` (`hash`); \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..351290c --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,245 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "ddf5d5b3-bf1e-4d8d-89cb-230f8e90137a", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "explorer_content": { + "name": "explorer_content", + "columns": { + "path": { + "name": "path", + "type": "text", + "primaryKey": true, + "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 + }, + "content": { + "name": "content", + "type": "blob", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "navigable": { + "name": "navigable", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": true + }, + "private": { + "name": "private", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "explorer_content_owner_users_id_fk": { + "name": "explorer_content_owner_users_id_fk", + "tableFrom": "explorer_content", + "tableTo": "users", + "columnsFrom": [ + "owner" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "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 + } + }, + "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": false, + "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": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..1da4d4d --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1730124775172, + "tag": "0000_youthful_ma_gnuci", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/layouts/default.vue b/layouts/default.vue index f68da08..fd6f1b0 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -1,9 +1,3 @@ \ No newline at end of file diff --git a/layouts/explorer.vue b/layouts/explorer.vue deleted file mode 100644 index 4dd0107..0000000 --- a/layouts/explorer.vue +++ /dev/null @@ -1,11 +0,0 @@ - \ No newline at end of file diff --git a/middleware/auth.global.ts b/middleware/auth.global.ts deleted file mode 100644 index b302d00..0000000 --- a/middleware/auth.global.ts +++ /dev/null @@ -1,22 +0,0 @@ -export default defineNuxtRouteMiddleware(async (to, from) => { - const { loggedIn, ready, fetch } = useUserSession(); - const meta = to.meta; - - if(!ready) - await fetch(); - - if(!!meta.guestsGoesTo && !loggedIn.value) - { - return navigateTo(meta.guestsGoesTo); - } - else if(meta.requireAuth && !loggedIn.value) - { - return abortNavigation(); - } - else if(!!meta.usersGoesTo && loggedIn.value) - { - return navigateTo(meta.usersGoesTo); - } - - return; -}); \ No newline at end of file diff --git a/modules/database.sync/module.ts b/modules/database.sync/module.ts deleted file mode 100644 index 69e726e..0000000 --- a/modules/database.sync/module.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { defineNuxtModule } from '@nuxt/kit' -import { Database } from "bun:sqlite"; - -interface Schema -{ - type: string; - name: string; - tbl_name: string; - sql: string; -} -interface Structure -{ - cid: number; - name: string; - type: string; - [key: string]: number | string | null; -} - -export default defineNuxtModule({ - setup (options, nuxt) { - nuxt.hook('build:done', () => { - console.log("Building database"); - - const template = new Database(nuxt.options.runtimeConfig.templateFile); - const schema = template.query(`SELECT * FROM sqlite_schema WHERE type = 'table'`).all() as Schema[]; - const structure = template.query(`SELECT * FROM pragma_table_info(?1)`); - - const db = new Database(nuxt.options.runtimeConfig.dbFile); - db.exec(`PRAGMA foreign_keys = OFF; PRAGMA defer_foreign_keys = OFF;`); - const oldSchema = db.query(`SELECT * FROM sqlite_schema WHERE type = 'table'`).all() as Schema[]; - const oldStructure = db.query(`SELECT * FROM pragma_table_info(?1)`); - - (db.transaction((tables: Schema[], oldTables: Schema[]) => { - for(const table of tables) - { - const oldIdx = oldTables.findIndex(e => e.name === table.name); - - if(table.name === 'sqlite_sequence') - { - oldTables.splice(oldIdx, 1); - continue; - } - - const columns = structure.all(table.name) as Structure[]; - const oldColumns = oldStructure.all(table.name) as Structure[]; - - if(oldIdx !== -1) - { - oldTables.splice(oldIdx, 1); - - const filteredColumns = oldColumns.filter(e => columns.find(f => e.name === f.name)).map(e => `"${e.name}"`).join(', ') - - db.exec(table.sql.replace(`CREATE TABLE "${table.name}"`, `CREATE TABLE "${table.name}_new"`)); - db.exec(`INSERT INTO ${table.name}_new (${filteredColumns}) SELECT ${filteredColumns} FROM ${table.name}`); - db.exec(`DROP TABLE ${table.name}`); - db.exec(`ALTER TABLE ${table.name}_new RENAME TO ${table.name}`); - } - else - { - db.exec(table.sql); - } - } - for(const table of oldTables) - { - db.exec(`DROP TABLE ${table.name}`); - } - })).immediate(schema, oldSchema); - - db.exec(`PRAGMA foreign_keys = ON; PRAGMA foreign_keys = ON;`); - - db.close(); - template.close(); - }); - } -}) \ No newline at end of file diff --git a/nuxt.config.ts b/nuxt.config.ts index b87b201..3f988f0 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -1,21 +1,13 @@ // https://nuxt.com/docs/api/configuration/nuxt-config -import DatabaseSync from './modules/database.sync/module'; - export default defineNuxtConfig({ + compatibilityDate: '2024-04-03', modules: [ - "@nuxtjs/color-mode", - "nuxt-security", - "@nuxtjs/tailwindcss", - "@vueuse/nuxt", - DatabaseSync + '@nuxtjs/color-mode', + 'nuxt-security', + '@nuxtjs/tailwindcss', + '@vueuse/nuxt', + 'radix-vue/nuxt', ], - runtimeConfig: { - dbFile: 'db.sqlite', - templateFile: 'template.sqlite', - session: { - password: '699c46bd-9aaa-4364-ad01-510ee4fe7013' - } - }, tailwindcss: { viewer: false, config: { @@ -23,7 +15,7 @@ export default defineNuxtConfig({ extend: { boxShadow: { raw: '0 0 0 4px var(--tw-shadow-color)' - } + }, }, colors: { transparent: 'transparent', @@ -83,21 +75,13 @@ export default defineNuxtConfig({ app: { pageTransition: false, layoutTransition: false - }, + }/*, components: [ { path: '~/components', pathPrefix: false, }, - ], - nitro: { - experimental: { - tasks: true, - }, - scheduledTasks: { - '0 */1 * * *': ['sync'] - } - }, + ]*/, security: { rateLimiter: false, headers: { @@ -106,5 +90,4 @@ export default defineNuxtConfig({ } } }, - compatibilityDate: '2024-07-25', }) \ No newline at end of file diff --git a/package.json b/package.json index b53cd4b..57d832d 100644 --- a/package.json +++ b/package.json @@ -1,28 +1,22 @@ { - "devDependencies": { - "@nuxtjs/color-mode": "^3.4.4", - "@nuxtjs/tailwindcss": "^6.12.1", - "@types/bun": "^1.1.8", - "@types/diff": "^5.2.2", - "@vueuse/gesture": "^2.0.0", - "@vueuse/nuxt": "^11.0.3", - "hast-util-to-html": "^9.0.2", - "nuxt": "^3.13.1", - "nuxt-security": "^2.0.0-rc.9", - "remark-breaks": "^4.0.0", - "remark-ofm": "link:remark-ofm", - "vue": "^3.5.3", - "vue-router": "^4.4.3", - "zod": "^3.23.8" - }, + "name": "d-any", + "private": true, + "type": "module", "dependencies": { - "diff": "^5.2.0", - "lodash.capitalize": "^4.2.1", - "rehype-raw": "^7.0.0", - "remark-frontmatter": "^5.0.0", - "remark-gfm": "^4.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.0", - "unified": "^11.0.5" + "@nuxtjs/color-mode": "^3.5.2", + "@nuxtjs/tailwindcss": "^6.12.2", + "@vueuse/nuxt": "^11.1.0", + "drizzle-orm": "^0.35.3", + "nuxt": "^3.13.2", + "nuxt-security": "^2.0.0", + "radix-vue": "^1.9.8", + "vue": "latest", + "vue-router": "latest" + }, + "devDependencies": { + "@types/bun": "^1.1.12", + "better-sqlite3": "^11.5.0", + "bun-types": "^1.1.33", + "drizzle-kit": "^0.26.2" } -} \ No newline at end of file +} diff --git a/pages/[...slug].vue b/pages/[...slug].vue index 09c7c43..414146a 100644 --- a/pages/[...slug].vue +++ b/pages/[...slug].vue @@ -1,9 +1,3 @@ \ No newline at end of file diff --git a/pages/admin/index.vue b/pages/admin/index.vue new file mode 100644 index 0000000..681216c --- /dev/null +++ b/pages/admin/index.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/pages/editing.vue b/pages/editing.vue deleted file mode 100644 index ce39228..0000000 --- a/pages/editing.vue +++ /dev/null @@ -1,223 +0,0 @@ - - - - - \ No newline at end of file diff --git a/pages/explore/[...path].vue b/pages/explore/[...path].vue new file mode 100644 index 0000000..559e875 --- /dev/null +++ b/pages/explore/[...path].vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/pages/explore/index.vue b/pages/explore/index.vue new file mode 100644 index 0000000..4065277 --- /dev/null +++ b/pages/explore/index.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/pages/explorer/[projectId]/[...slug].vue b/pages/explorer/[projectId]/[...slug].vue deleted file mode 100644 index 1892439..0000000 --- a/pages/explorer/[projectId]/[...slug].vue +++ /dev/null @@ -1,33 +0,0 @@ - - - \ No newline at end of file diff --git a/pages/explorer/index.vue b/pages/explorer/index.vue deleted file mode 100644 index ae5cec4..0000000 --- a/pages/explorer/index.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - \ No newline at end of file diff --git a/pages/index.vue b/pages/index.vue index ed32d0e..56f72b9 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -1,9 +1,18 @@ + + \ No newline at end of file diff --git a/pages/legal.vue b/pages/legal.vue new file mode 100644 index 0000000..27e0f69 --- /dev/null +++ b/pages/legal.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/pages/third-party.vue b/pages/third-party.vue deleted file mode 100644 index 49bfd81..0000000 --- a/pages/third-party.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/pages/user/[id].vue b/pages/user/[id].vue new file mode 100644 index 0000000..6e8c5dc --- /dev/null +++ b/pages/user/[id].vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/pages/user/edit.vue b/pages/user/edit.vue new file mode 100644 index 0000000..050d0b6 --- /dev/null +++ b/pages/user/edit.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/pages/user/login.vue b/pages/user/login.vue index f596a00..ccdba11 100644 --- a/pages/user/login.vue +++ b/pages/user/login.vue @@ -1,97 +1,3 @@ - - \ No newline at end of file diff --git a/pages/user/profile.vue b/pages/user/profile.vue deleted file mode 100644 index e0bbff4..0000000 --- a/pages/user/profile.vue +++ /dev/null @@ -1,74 +0,0 @@ - - - \ No newline at end of file diff --git a/pages/user/register.vue b/pages/user/register.vue index bed9fcd..ee39853 100644 --- a/pages/user/register.vue +++ b/pages/user/register.vue @@ -1,124 +1,3 @@ - - \ No newline at end of file diff --git a/pages/users/[id].vue b/pages/users/[id].vue deleted file mode 100644 index afb0e55..0000000 --- a/pages/users/[id].vue +++ /dev/null @@ -1,31 +0,0 @@ - - - \ No newline at end of file diff --git a/plugins/session.client.ts b/plugins/session.client.ts deleted file mode 100644 index ce704e1..0000000 --- a/plugins/session.client.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default defineNuxtPlugin(async (nuxtApp) => { - if (!nuxtApp.payload.serverRendered || Boolean(nuxtApp.payload.prerenderedAt) || Boolean(nuxtApp.payload.isCached)) { - nuxtApp.hook('app:mounted', async () => { - await useUserSession().fetch() - }) - } -}) \ No newline at end of file diff --git a/plugins/session.server.ts b/plugins/session.server.ts deleted file mode 100644 index 0571894..0000000 --- a/plugins/session.server.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default defineNuxtPlugin({ - name: 'session-fetch-plugin', - enforce: 'pre', - async setup(nuxtApp) { - // Flag if request is cached - nuxtApp.payload.isCached = Boolean(useRequestEvent()?.context.cache) - if (nuxtApp.payload.serverRendered && !nuxtApp.payload.prerenderedAt && !nuxtApp.payload.isCached) { - await useUserSession().fetch() - } - }, -}) \ No newline at end of file diff --git a/plugins/vueuse.gesture.ts b/plugins/vueuse.gesture.ts deleted file mode 100644 index 07d3788..0000000 --- a/plugins/vueuse.gesture.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { GesturePlugin } from '@vueuse/gesture' - -export default defineNuxtPlugin((nuxtApp) => { - nuxtApp.vueApp.use(GesturePlugin) -}) \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico index f48a0f4..18993ad 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/icons/close.svg b/public/icons/close.svg deleted file mode 100644 index ae612fc..0000000 --- a/public/icons/close.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/public/icons/link-canvas.svg b/public/icons/link-canvas.svg deleted file mode 100644 index 3e4b07f..0000000 --- a/public/icons/link-canvas.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/link-file.svg b/public/icons/link-file.svg deleted file mode 100644 index ca7eed8..0000000 --- a/public/icons/link-file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/link-folder.svg b/public/icons/link-folder.svg deleted file mode 100644 index b2ebb2a..0000000 --- a/public/icons/link-folder.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/logo.dark.svg b/public/icons/logo.dark.svg deleted file mode 100644 index a677d37..0000000 --- a/public/icons/logo.dark.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/public/icons/logo.light.svg b/public/icons/logo.light.svg deleted file mode 100644 index cacf91f..0000000 --- a/public/icons/logo.light.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/public/icons/user-login.svg b/public/icons/user-login.svg deleted file mode 100644 index c102c08..0000000 --- a/public/icons/user-login.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/user-unknown.svg b/public/icons/user-unknown.svg deleted file mode 100644 index 46a8af7..0000000 --- a/public/icons/user-unknown.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1 @@ + diff --git a/schemas/login.ts b/schemas/login.ts deleted file mode 100644 index c1db54c..0000000 --- a/schemas/login.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { z } from "zod"; - -export const schema = z.object({ - usernameOrEmail: z.string({ required_error: "Nom d'utilisateur ou email obligatoire" }), - password: z.string({ required_error: "Mot de passe obligatoire" }), -}); - -export type Login = z.infer; \ No newline at end of file diff --git a/schemas/registration.ts b/schemas/registration.ts deleted file mode 100644 index b9bba67..0000000 --- a/schemas/registration.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { z } from "zod"; - -function securePassword(password: string, ctx: z.RefinementCtx): void { - const lowercase = password.toLowerCase(); - const uppercase = password.toUpperCase(); - - if(lowercase === password) - { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "Votre mot de passe doit contenir au moins une majuscule", - }); - } - if(uppercase === password) - { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "Votre mot de passe doit contenir au moins une minuscule", - }); - } - if(!/[0-9]/.test(password)) - { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "Votre mot de passe doit contenir au moins un chiffre", - }); - } - if(!" !\"#$%&'()*+,-./:;<=>?@[]^_`{|}~".split("").some(e => password.includes(e))) - { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "Votre mot de passe doit contenir au moins un symbole", - }); - } -} - -export const schema = z.object({ - username: z.string({ required_error: "Nom d'utilisateur obligatoire" }).min(3, "Votre nom d'utilisateur doit contenir au moins 3 caractères").max(32, "Votre nom d'utilisateur doit contenir au plus 32 caractères").superRefine((user, ctx) => { - const test = z.string().email().safeParse(user); - if(test.success) - { - ctx.addIssue({ - code: z.ZodIssueCode.invalid_string, - validation: 'email', - message: "Votre nom d'utilisateur ne peut pas être une addresse mail", - }); - } - }), - email: z.string({ required_error: "Email obligatoire" }).email("Adresse mail invalide"), - password: z.string({ required_error: "Mot de passe obligatoire" }).min(8, "Votre mot de passe doit contenir au moins 8 caractères").max(128, "Votre mot de passe doit contenir au moins 8 caractères").superRefine(securePassword), - data: z.object({ - - }).partial().nullish(), -}); - -export type Registration = z.infer; \ No newline at end of file diff --git a/server/api/auth/login.post.ts b/server/api/auth/login.post.ts deleted file mode 100644 index 38df8d6..0000000 --- a/server/api/auth/login.post.ts +++ /dev/null @@ -1,89 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; -import { schema } from '~/schemas/login'; -import { User, UserExtendedData, UserRawData, UserSession, UserSessionRequired } from '~/types/auth'; -import type { Database } from "bun:sqlite"; -import { ZodError } from 'zod'; -import { checkSession, logSession } from '~/server/utils/user'; - -interface SuccessHandler -{ - success: true; - session: UserSession; -} -interface ErrorHandler -{ - success: false; - error: Error | ZodError<{ - usernameOrEmail: string; - password: string; - }>; -} -type Return = SuccessHandler | ErrorHandler; - -export default defineEventHandler(async (e): Promise => { - try - { - const session = await getUserSession(e); - const db = useDatabase(); - - const checkedSession = await checkSession(e, session); - - if(checkedSession !== undefined) - return checkedSession; - - const body = await readValidatedBody(e, schema.safeParse); - - if (!body.success) - { - await clearUserSession(e); - - setResponseStatus(e, 406); - return { success: false, error: body.error }; - } - - const hash = await Bun.password.hash(body.data.password); - const checkID = db.query(`SELECT id, hash FROM users WHERE (username = ?1 or email = ?1)`); - const id = checkID.get(body.data.usernameOrEmail) as { id: number, hash: string }; - - if(!id || !id.id || !id.hash) - { - await clearUserSession(e); - - setResponseStatus(e, 401); - return { success: false, error: new ZodError([{ code: 'custom', path: ['username'], message: 'Identifiant inconnu' }]) }; - } - - const valid = await Bun.password.verify(body.data.password, id.hash); - - if(!valid) - { - await clearUserSession(e); - - setResponseStatus(e, 401); - return { success: false, error: new ZodError([{ code: 'custom', path: ['password'], message: 'Mot de passe incorrect' }]) }; - } - - const data = await logSession(e, await setUserSession(e, { user: getData(db, id.id) }) as UserSessionRequired); - - setResponseStatus(e, 201); - return { success: true, session: data }; - } - catch(err: any) - { - await clearUserSession(e); - - console.error(err); - return { success: false, error: err as Error }; - } -}); - -function getData(db: Database, id: number): User -{ - const userQuery = db.query(`SELECT id, username, email, state FROM users WHERE id = ?1`); - const user = userQuery.get(id) as UserRawData; - - const userDataQuery = db.query(`SELECT * FROM users_data WHERE user_id = ?1`); - const userData = userDataQuery.get(id) as UserExtendedData; - - return { ...user, ...userData }; -} \ No newline at end of file diff --git a/server/api/auth/register.post.ts b/server/api/auth/register.post.ts deleted file mode 100644 index c173807..0000000 --- a/server/api/auth/register.post.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { ZodError, ZodIssue } from 'zod'; -import useDatabase from '~/composables/useDatabase'; -import { schema } from '~/schemas/registration'; -import { checkSession, logSession } from '~/server/utils/user'; -import { UserSession, UserSessionRequired } from '~/types/auth'; - -interface SuccessHandler -{ - success: true; - session: UserSession; -} -interface ErrorHandler -{ - success: false; - error: Error | ZodError<{ - username: string; - email: string; - password: string; - }>; -} -type Return = SuccessHandler | ErrorHandler; - -export default defineEventHandler(async (e): Promise => { - try - { - const session = await getUserSession(e); - const db = useDatabase(); - - const checkedSession = await checkSession(e, session); - - if(checkedSession !== undefined) - return checkedSession; - - const body = await readValidatedBody(e, schema.safeParse); - - if (!body.success) - { - await clearUserSession(e); - - setResponseStatus(e, 406); - return { success: false, error: body.error }; - } - - const usernameQuery = db.query(`SELECT COUNT(*) as count FROM users WHERE username = ?1`); - const checkUsername = usernameQuery.get(body.data.username) as any; - - const emailQuery = db.query(`SELECT COUNT(*) as count FROM users WHERE email = ?1`); - const checkEmail = emailQuery.get(body.data.email) as any; - - const errors: ZodIssue[] = []; - if(checkUsername.count !== 0) - errors.push({ code: 'custom', path: ['username'], message: "Ce nom d'utilisateur est déjà utilisé" }); - if(checkEmail.count !== 0) - errors.push({ code: 'custom', path: ['email'], message: "Cette adresse mail est déjà utilisée" }); - - if(errors.length > 0) - { - setResponseStatus(e, 406); - return { success: false, error: new ZodError(errors) }; - } - else - { - 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)`); - registration.run(body.data.username, body.data.email, hash); - - const userIdQuery = db.query(`SELECT id FROM users WHERE username = ?1`); - const id = (userIdQuery.get(body.data.username) as any).id; - - const registeringData = db.query(`INSERT INTO users_data(user_id, signin_timestamp) VALUES(?1, ?2)`); - 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); - - setResponseStatus(e, 201); - return { success: true, session }; - } - } - catch(err: any) - { - await clearUserSession(e); - - console.error(err); - return { success: false, error: err as Error }; - } -}); \ No newline at end of file diff --git a/server/api/auth/session.delete.ts b/server/api/auth/session.delete.ts deleted file mode 100644 index a21b435..0000000 --- a/server/api/auth/session.delete.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { eventHandler } from 'h3'; -import { clearUserSession } from '~/server/utils/session'; - -export default eventHandler(async (event) => { - await clearUserSession(event); - - return { loggedOut: true }; -}) \ No newline at end of file diff --git a/server/api/auth/session.get.ts b/server/api/auth/session.get.ts deleted file mode 100644 index a7245a9..0000000 --- a/server/api/auth/session.get.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { eventHandler } from 'h3' -import { getUserSession, sessionHooks } from '~/server/utils/session' -import type { UserSessionRequired } from '~/types/auth' - -export default eventHandler(async (event) => { - const session = await getUserSession(event) - - if (session.user) { - await sessionHooks.callHookParallel('fetch', session as UserSessionRequired, event) - } - - return session -}) \ No newline at end of file diff --git a/server/api/project.get.ts b/server/api/project.get.ts deleted file mode 100644 index 1d63086..0000000 --- a/server/api/project.get.ts +++ /dev/null @@ -1,31 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; -import { ProjectSearch } from '~/types/api'; - -export default defineEventHandler(async (e) => { - const query = getQuery(e); - - const where = ["f.type != $type"]; - const criteria: Record = { $type: "Folder" }; - - if(query && query.owner !== undefined) - { - where.push("owner = $owner"); - criteria["$owner"] = query.owner; - } - if(query && query.name !== undefined) - { - where.push("name = $name"); - criteria["$name"] = query.name; - } - - const db = useDatabase(); - - const content = db.query(`SELECT p.*, u.username, COUNT(f.path) as pages FROM explorer_projects p LEFT JOIN users u ON p.owner = u.id LEFT JOIN explorer_files f ON f.project = p.id WHERE ${where.join(" AND ")} GROUP BY p.id`).all(criteria) as ProjectSearch[]; - - if(content.length > 0) - { - return content; - } - - setResponseStatus(e, 404); -}); \ No newline at end of file diff --git a/server/api/project/[projectId].get.ts b/server/api/project/[projectId].get.ts deleted file mode 100644 index 35af39f..0000000 --- a/server/api/project/[projectId].get.ts +++ /dev/null @@ -1,20 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; - -export default defineEventHandler(async (e) => { - const project = getRouterParam(e, "projectId"); - - const where = ["id = $id"]; - const criteria: Record = { $id: project }; - - if (!!project) { - const db = useDatabase(); - - const content = db.query(`SELECT * FROM explorer_projects WHERE ${where.join(" and ")}`).get(criteria) as Project; - - if (content) { - return content; - } - } - - setResponseStatus(e, 404); -}); \ No newline at end of file diff --git a/server/api/project/[projectId].patch.ts b/server/api/project/[projectId].patch.ts deleted file mode 100644 index 82694f5..0000000 --- a/server/api/project/[projectId].patch.ts +++ /dev/null @@ -1,20 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; - -export default defineEventHandler(async (e) => { - const project = getRouterParam(e, "projectId"); - - const where = ["project = $project"]; - const criteria: Record = { $project: project }; - - if (!!project) { - const db = useDatabase(); - - const content = db.query(`SELECT * FROM explorer_projects WHERE ${where.join(" and ")}`).all(criteria) as Project[]; - - if (content.length > 0) { - return content; - } - } - - setResponseStatus(e, 404); -}); \ No newline at end of file diff --git a/server/api/project/[projectId].post.ts b/server/api/project/[projectId].post.ts deleted file mode 100644 index 82694f5..0000000 --- a/server/api/project/[projectId].post.ts +++ /dev/null @@ -1,20 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; - -export default defineEventHandler(async (e) => { - const project = getRouterParam(e, "projectId"); - - const where = ["project = $project"]; - const criteria: Record = { $project: project }; - - if (!!project) { - const db = useDatabase(); - - const content = db.query(`SELECT * FROM explorer_projects WHERE ${where.join(" and ")}`).all(criteria) as Project[]; - - if (content.length > 0) { - return content; - } - } - - setResponseStatus(e, 404); -}); \ No newline at end of file diff --git a/server/api/project/[projectId]/file.get.ts b/server/api/project/[projectId]/file.get.ts deleted file mode 100644 index 26db9fb..0000000 --- a/server/api/project/[projectId]/file.get.ts +++ /dev/null @@ -1,54 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; -import type { File } from '~/types/api'; - -export default defineCachedEventHandler(async (e) => { - const project = getRouterParam(e, "projectId"); - const query = getQuery(e); - - if(!project) - { - setResponseStatus(e, 404); - return; - } - - const where = ["project = $project"]; - const criteria: Record = { $project: project }; - - if(query && query.path !== undefined) - { - where.push("path = $path"); - criteria["$path"] = query.path; - } - if(query && query.title !== undefined) - { - where.push("title = $title"); - criteria["$title"] = query.title; - } - if(query && query.type !== undefined) - { - where.push("type = $type"); - criteria["$type"] = query.type; - } - if (query && query.search !== undefined) - { - where.push("path LIKE $search"); - criteria["$search"] = query.search; - } - - if(where.length > 1) - { - const db = useDatabase(); - - const content = db.query(`SELECT * FROM explorer_files WHERE ${where.join(" and ")}`).all(criteria) as File[]; - - if(content.length > 0) - { - return content; - } - } - - setResponseStatus(e, 404); -}, { - maxAge: 60*60*24, - getKey: (e) => `${getRouterParam(e, "projectId")}-${JSON.stringify(getQuery(e))}` - }); \ No newline at end of file diff --git a/server/api/project/[projectId]/file/[path].get.ts b/server/api/project/[projectId]/file/[path].get.ts deleted file mode 100644 index a03e65f..0000000 --- a/server/api/project/[projectId]/file/[path].get.ts +++ /dev/null @@ -1,41 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; -import type { CommentedFile, CommentSearch, File } from '~/types/api'; - -export default defineCachedEventHandler(async (e) => { - const project = getRouterParam(e, "projectId"); - const path = decodeURIComponent(getRouterParam(e, "path") ?? ''); - - if(!project) - { - setResponseStatus(e, 404); - return; - } - if(!path) - { - setResponseStatus(e, 404); - return; - } - - const where = ["project = $project", "path = $path"]; - const criteria: Record = { $project: project, $path: path }; - - if(where.length > 1) - { - const db = useDatabase(); - - const content = db.query(`SELECT * FROM explorer_files WHERE ${where.join(" and ")}`).get(criteria) as File; - - if(content !== undefined) - { - const comments = db.query(`SELECT comment.*, user.username FROM explorer_comments comment LEFT JOIN users user ON comment.user_id = user.id WHERE ${where.join(" and ")}`).all(criteria) as CommentSearch[]; - - return { ...content, comments } as CommentedFile; - } - } - - setResponseStatus(e, 404); - return; -}, { - maxAge: 60*60*24, - getKey: (e) => `file-${getRouterParam(e, "projectId")}-${getRouterParam(e, "path")}` - }); \ No newline at end of file diff --git a/server/api/project/[projectId]/navigation.get.ts b/server/api/project/[projectId]/navigation.get.ts deleted file mode 100644 index 0de8f58..0000000 --- a/server/api/project/[projectId]/navigation.get.ts +++ /dev/null @@ -1,72 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; -import { Navigation } from '~/types/api'; - -type NavigatioNExtension = Navigation & { owner: number, navigable: boolean }; - -export default defineEventHandler(async (e) => { - const project = getRouterParam(e, "projectId"); - const { user } = await getUserSession(e); - - if(!project) - { - setResponseStatus(e, 404); - return; - } - - const db = useDatabase(); - - const content = db.query(`SELECT "path", "title", "type", "order", "private", "navigable", "owner" FROM explorer_files WHERE project = ?1`).all(project!).sort((a: any, b: any) => a.path.length - b.path.length) as NavigatioNExtension[]; - - if(content.length > 0) - { - const navigation: Navigation[] = []; - - for(const idx in content) - { - const item = content[idx]; - if(!!item.private && (user?.id ?? -1) !== item.owner || !item.navigable) - { - delete content[idx]; - continue; - } - - const parent = item.path.includes('/') ? item.path.substring(0, item.path.lastIndexOf('/')) : undefined; - - if(parent && !content.find(e => e && e.path === parent)) - { - delete content[idx]; - continue; - } - } - for(const item of content.filter(e => !!e)) - { - addChild(navigation, item); - } - - return navigation; - } - - setResponseStatus(e, 404); -}); - -function addChild(arr: Navigation[], e: Navigation): void -{ - const parent = arr.find(f => e.path.startsWith(f.path)); - - if(parent) - { - if(!parent.children) - parent.children = []; - - addChild(parent.children, e); - } - else - { - arr.push({ title: e.title, path: e.path, type: e.type, order: e.order, private: e.private }); - arr.sort((a, b) => { - if(a.order && b.order) - return a.order - b.order; - return a.title.localeCompare(b.title); - }); - } -} \ No newline at end of file diff --git a/server/api/project/[projectId]/tags/[tag].get.ts b/server/api/project/[projectId]/tags/[tag].get.ts deleted file mode 100644 index 4b9f359..0000000 --- a/server/api/project/[projectId]/tags/[tag].get.ts +++ /dev/null @@ -1,42 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; -import type { Tag } from '~/types/api'; - -export default defineCachedEventHandler(async (e) => { - try - { - const project = getRouterParam(e, "projectId"); - const tag = decodeURIComponent(getRouterParam(e, "tag") ?? ''); - - if(!project) - { - setResponseStatus(e, 404); - return; - } - if(!tag) - { - setResponseStatus(e, 404); - return; - } - - const where = ["project = $project", "tag = $tag"]; - const criteria: Record = { $project: project, $tag: tag }; - - const db = useDatabase(); - const content = db.query(`SELECT * FROM explorer_tags WHERE ${where.join(" and ")}`).get(criteria) as Tag; - - if(content !== undefined) - { - return content; - } - - setResponseStatus(e, 404); - } - catch(err) - { - console.error(err); - setResponseStatus(e, 500); - } -}, { - maxAge: 60*60*24, - getKey: (e) => `tag-${getRouterParam(e, "projectId")}-${getRouterParam(e, "tag")}` - }); \ No newline at end of file diff --git a/server/api/search.get.ts b/server/api/search.get.ts deleted file mode 100644 index 265b3c1..0000000 --- a/server/api/search.get.ts +++ /dev/null @@ -1,21 +0,0 @@ -import useDatabase from '~/composables/useDatabase'; - -export default defineEventHandler(async (e) => { - const query = getQuery(e); - - if (query.search) { - const db = useDatabase(); - - const projects = db.query(`SELECT p.*, u.username, COUNT(f.path) as pages FROM explorer_projects p LEFT JOIN users u ON p.owner = u.id LEFT JOIN explorer_files f ON f.project = p.id WHERE name LIKE ?1 AND f.type != "Folder" GROUP BY p.id`).all(query.search) as ProjectSearch[]; - const files = db.query(`SELECT f.*, u.username, count(c.path) as comments FROM explorer_files f LEFT JOIN users u ON f.owner = u.id LEFT JOIN explorer_comments c ON c.project = f.project AND c.path = f.path WHERE title LIKE ?1 AND private = 0 AND type != "Folder" GROUP BY f.project, f.path`).all(query.search) as FileSearch[]; - const users = db.query(`SELECT id, username FROM users WHERE username LIKE ?1`).all(query.search) as UserSearch[]; - - return { - projects, - files, - users - } as Search; - } - - setResponseStatus(e, 404); -}); \ No newline at end of file diff --git a/server/api/users/[id].get.ts b/server/api/users/[id].get.ts deleted file mode 100644 index 1e65580..0000000 --- a/server/api/users/[id].get.ts +++ /dev/null @@ -1,16 +0,0 @@ -import useDatabase from "~/composables/useDatabase"; -import type { User } 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 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; -}); \ No newline at end of file diff --git a/server/api/users/[id]/comments.get.ts b/server/api/users/[id]/comments.get.ts deleted file mode 100644 index dd2956b..0000000 --- a/server/api/users/[id]/comments.get.ts +++ /dev/null @@ -1,16 +0,0 @@ -import useDatabase from "~/composables/useDatabase"; -import type { CommentSearch } 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_comments WHERE user_id = ?1`).all(id) as CommentSearch[]; -}); \ No newline at end of file diff --git a/server/api/users/[id]/projects.get.ts b/server/api/users/[id]/projects.get.ts deleted file mode 100644 index 74b3665..0000000 --- a/server/api/users/[id]/projects.get.ts +++ /dev/null @@ -1,16 +0,0 @@ -import useDatabase from "~/composables/useDatabase"; -import type { ProjectSearch } 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 p.*, count(f.path) as pages FROM explorer_projects p LEFT JOIN explorer_files f ON p.id = f.project WHERE p.owner = ?1`).all(id) as Omit[]; -}); \ No newline at end of file diff --git a/server/plugins/session.ts b/server/plugins/session.ts deleted file mode 100644 index 782a87d..0000000 --- a/server/plugins/session.ts +++ /dev/null @@ -1,38 +0,0 @@ -import useDatabase from "~/composables/useDatabase"; - -const monthAsMs = 60 * 60 * 24 * 30; - -export default defineNitroPlugin(() => { - const db = useDatabase(); - - sessionHooks.hook('fetch', async (session, event) => { - const query = db.prepare('SELECT last_refresh FROM user_sessions WHERE id = ?1 AND user_id = ?2'); - const result = query.get(session.id, session.user.id) as { last_refresh: number }; - - if(!result) - { - clearUserSession(event); - throw createError({ statusCode: 401, message: 'Unauthorized' }); - } - else if(result && result.last_refresh && result.last_refresh < Date.now() - monthAsMs) - { - clearUserSession(event); - throw createError({ statusCode: 401, message: 'Session has expired' }); - } - else - { - db.prepare('UPDATE user_sessions SET last_refresh = ?1 WHERE id = ?2 AND user_id = ?3').run(Date.now(), session.id, session.user.id); - } - }); - sessionHooks.hook('clear', async (session, event) => { - if(session.id && session.user) - { - try - { - const query = db.prepare('DELETE FROM user_sessions WHERE id = ?1 AND user_id = ?2'); - query.run(session.id, session.user.id); - } - catch(e) { } - } - }); -}); \ No newline at end of file diff --git a/server/tasks/sync.ts b/server/tasks/sync.ts deleted file mode 100644 index 4a05a28..0000000 --- a/server/tasks/sync.ts +++ /dev/null @@ -1,165 +0,0 @@ -import useDatabase from "~/composables/useDatabase"; -import { extname, basename } from 'node:path'; -import type { File, FileType, Tag } from '~/types/api'; -import { CanvasColor, CanvasContent } from "~/types/canvas"; -import useMarkdown from "~/composables/useMarkdown"; - -const typeMapping: Record = { - ".md": "Markdown", - ".canvas": "Canvas" -}; - -export default defineTask({ - meta: { - name: 'sync', - description: 'Synchronise the project 1 with Obsidian', - }, - async run(event) { - try { - const tree = await $fetch('https://git.peaceultime.com/api/v1/repos/peaceultime/system-aspect/git/trees/master', { - method: 'get', - headers: { - accept: 'application/json', - }, - params: { - recursive: true, - per_page: 1000, - } - }) as any; - - const files: File[] = await Promise.all(tree.tree.filter((e: any) => !e.path.startsWith(".")).map(async (e: any) => { - if(e.type === 'tree') - { - const title = basename(e.path); - const order = /(\d+)\. ?(.+)/gsmi.exec(title); - const path = (e.path as string).split('/').map(f => { const check = /(\d+)\. ?(.+)/gsmi.exec(f); return check && check[2] ? check[2] : f }).join('/'); - return { - path: path.toLowerCase().replaceAll(" ", "-").normalize("NFD").replace(/[\u0300-\u036f]/g, ""), - order: order && order[1] ? order[1] : 50, - title: order && order[2] ? order[2] : title, - type: 'Folder', - content: null - } - } - - const extension = extname(e.path); - const title = basename(e.path, extension); - const order = /(\d+)\. ?(.+)/gsmi.exec(title); - const path = (e.path as string).split('/').map(f => { const check = /(\d+)\. ?(.+)/gsmi.exec(f); return check && check[2] ? check[2] : f }).join('/'); - const content = (await $fetch(`https://git.peaceultime.com/api/v1/repos/peaceultime/system-aspect/raw/${encodeURIComponent(e.path)}`)); - - return { - path: (extension === '.md' ? path.replace(extension, '') : path).toLowerCase().replaceAll(" ", "-").normalize("NFD").replace(/[\u0300-\u036f]/g, ""), - order: order && order[1] ? order[1] : 50, - title: order && order[2] ? order[2] : title, - type: (typeMapping[extension] ?? 'File'), - content: reshapeContent(content as string, typeMapping[extension] ?? 'File') - } - })); - - let tags: Tag[] = []; - const tagFile = files.find(e => e.path === "tags"); - - if(tagFile) - { - const parsed = useMarkdown()(tagFile.content); - const titles = parsed.children.filter(e => e.type === 'element' && e.tagName.match(/h\d/)); - for(let i = 0; i < titles.length; i++) - { - const start = titles[i].position?.start.offset ?? 0; - const end = titles.length === i + 1 ? tagFile.content.length : titles[i + 1].position.start.offset - 1; - tags.push({ tag: titles[i].properties.id, description: tagFile.content.substring(titles[i].position?.end.offset + 1, end) }); - } - } - - const db = useDatabase(); - - const oldFiles = db.prepare(`SELECT * FROM explorer_files WHERE project = ?1`).all('1') as File[]; - const removeFiles = db.prepare(`DELETE FROM explorer_files WHERE project = ?1 AND path = ?2`); - db.transaction((data: File[]) => data.forEach(e => removeFiles.run('1', e.path)))(oldFiles.filter(e => !files.find(f => f.path = e.path))); - removeFiles.finalize(); - - const oldTags = db.prepare(`SELECT * FROM explorer_tags WHERE project = ?1`).all('1') as Tag[]; - const removeTags = db.prepare(`DELETE FROM explorer_tags WHERE project = ?1 AND tag = ?2`); - db.transaction((data: Tag[]) => data.forEach(e => removeTags.run('1', e.tag)))(oldTags.filter(e => !tags.find(f => f.tag = e.tag))); - removeTags.finalize(); - - const insertFiles = db.prepare(`INSERT INTO explorer_files("project", "path", "owner", "title", "order", "type", "content") VALUES (1, $path, 1, $title, $order, $type, $content)`); - const updateFiles = db.prepare(`UPDATE explorer_files SET content = $content WHERE project = 1 AND path = $path`); - db.transaction((content) => { - for (const item of content) { - let order = item.order; - - if (typeof order === 'string') - order = parseInt(item.order, 10); - - if (isNaN(order)) - order = 999; - - if(oldFiles.find(e => item.path === e.path)) - updateFiles.run({ $path: item.path, $content: item.content }); - else - insertFiles.run({ $path: item.path, $title: item.title, $type: item.type, $content: item.content, $order: order }); - } - })(files); - - insertFiles.finalize(); - updateFiles.finalize(); - - const insertTags = db.prepare(`INSERT INTO explorer_tags("project", "tag", "description") VALUES (1, $tag, $description)`); - const updateTags = db.prepare(`UPDATE explorer_tags SET description = $description WHERE project = 1 AND tag = $tag`); - db.transaction((content) => { - for (const item of content) { - if (oldTags.find(e => item.tag === e.tag)) - updateTags.run({ $tag: item.tag, $description: item.description }); - else - insertTags.run({ $tag: item.tag, $description: item.description }); - } - })(tags); - - insertTags.finalize(); - updateTags.finalize(); - - useStorage('cache').clear(); - - return { result: true }; - } - catch(e) - { - return { result: false }; - } - }, -}) - -function reshapeContent(content: string, type: FileType): string | null -{ - switch(type) - { - case "Markdown": - case "File": - return content; - case "Canvas": - const data = JSON.parse(content) as CanvasContent; - data.edges.forEach(e => e.color = typeof e.color === 'string' ? getColor(e.color) : undefined); - data.nodes.forEach(e => e.color = typeof e.color === 'string' ? getColor(e.color) : undefined); - return JSON.stringify(data); - default: - case 'Folder': - return null; - } -} -function getColor(color: string): CanvasColor -{ - const colors: Record = { - '1': 'red', - '2': 'orange', - '3': 'yellow', - '4': 'green', - '5': 'cyan', - '6': 'purple', - }; - if(colors.hasOwnProperty(color)) - return { class: colors[color] }; - else - return { hex: color }; -} \ No newline at end of file diff --git a/server/utils/session.ts b/server/utils/session.ts deleted file mode 100644 index 6136a72..0000000 --- a/server/utils/session.ts +++ /dev/null @@ -1,110 +0,0 @@ -import type { H3Event, SessionConfig } from 'h3' -import { useSession, createError } from 'h3' -import { defu } from 'defu' -import { createHooks } from 'hookable' -import { useRuntimeConfig } from '#imports' -import type { UserSession, UserSessionRequired } from '~/types/auth' - -export interface SessionHooks { - /** - * Called when fetching the session from the API - * - Add extra properties to the session - * - Throw an error if the session could not be verified (with a database for example) - */ - fetch: (session: UserSessionRequired, event: H3Event) => void | Promise - /** - * Called before clearing the session - */ - clear: (session: UserSession, event: H3Event) => void | Promise -} - -export const sessionHooks = createHooks() - -/** - * Get the user session from the current request - * @param event The Request (h3) event - * @returns The user session - */ -export async function getUserSession(event: H3Event) { - const session = await _useSession(event); - - if(!session.data || !session.data.id) - { - await session.update(defu({ id: session.id }, session.data)); - } - - return session.data; -} -/** - * Set a user session - * @param event The Request (h3) event - * @param data User session data, please only store public information since it can be decoded with API calls - * @see https://github.com/atinux/nuxt-auth-utils - */ -export async function setUserSession(event: H3Event, data: UserSession) { - const session = await _useSession(event) - - await session.update(defu(data, session.data)) - - return session.data -} - -/** - * Replace a user session - * @param event The Request (h3) event - * @param data User session data, please only store public information since it can be decoded with API calls - */ -export async function replaceUserSession(event: H3Event, data: UserSession) { - const session = await _useSession(event) - - await session.clear() - await session.update(data) - - return session.data -} - -/** - * Clear the user session and removing the session cookie - * @param event The Request (h3) event - * @returns true if the session was cleared - */ -export async function clearUserSession(event: H3Event) { - const session = await _useSession(event) - - await sessionHooks.callHookParallel('clear', session.data, event) - await session.clear() - - return true -} - -/** - * Require a user session, throw a 401 error if the user is not logged in - * @param event - * @param opts Options to customize the error message and status code - * @param opts.statusCode The status code to use for the error (defaults to 401) - * @param opts.message The message to use for the error (defaults to Unauthorized) - * @see https://github.com/atinux/nuxt-auth-utils - */ -export async function requireUserSession(event: H3Event, opts: { statusCode?: number, message?: string } = {}): Promise { - const userSession = await getUserSession(event) - - if (!userSession.user) { - throw createError({ - statusCode: opts.statusCode || 401, - message: opts.message || 'Unauthorized', - }) - } - - return userSession as UserSessionRequired -} - -let sessionConfig: SessionConfig - -function _useSession(event: H3Event) { - if (!sessionConfig) { - const runtimeConfig = useRuntimeConfig(event) - - sessionConfig = runtimeConfig.session; - } - return useSession(event, sessionConfig) -} \ No newline at end of file diff --git a/server/utils/user.ts b/server/utils/user.ts deleted file mode 100644 index 21b4579..0000000 --- a/server/utils/user.ts +++ /dev/null @@ -1,35 +0,0 @@ -import useDatabase from "~/composables/useDatabase"; -import { Return } from "~/types/api"; -import type { UserSession, UserSessionRequired } from "~/types/auth"; - -export async function checkSession(e: H3Event, session: UserSession): Promise -{ - const db = useDatabase(); - - if(session.id && session.user?.id) - { - const checkSession = db.query("SELECT user_id FROM user_sessions WHERE id = ?1"); - const sessionId = checkSession.get(session.id) as any; - - if(sessionId && sessionId.user_id === session.user?.id) - { - return { success: true, session }; - } - else - { - await clearUserSession(e); - - setResponseStatus(e, 406); - return { success: false, error: new Error('Vous êtes déjà connecté') }; - } - } -} -export async function logSession(e: H3Event, session: UserSessionRequired): Promise -{ - const db = useDatabase(); - - const loggingIn = db.query(`INSERT INTO user_sessions(id, user_id, ip, agent, last_refresh) VALUES(?1, ?2, ?3, ?4, ?5)`); - loggingIn.get(session.id, session.user.id, getRequestIP(e) ?? null, getRequestHeader(e, 'User-Agent') ?? null, Date.now()); - - return session; -} \ No newline at end of file diff --git a/template.sqlite b/template.sqlite deleted file mode 100644 index 0ae37d2..0000000 Binary files a/template.sqlite and /dev/null differ diff --git a/types/api.d.ts b/types/api.d.ts deleted file mode 100644 index e79d033..0000000 --- a/types/api.d.ts +++ /dev/null @@ -1,87 +0,0 @@ -export interface SuccessHandler -{ - success: true; - session: UserSession; -} -export interface ErrorHandler -{ - success: false; - error: Error | ZodError; -} -export type Return = SuccessHandler | ErrorHandler; - -export interface Project { - id: number; - name: string; - owner: number; - home: string; - summary: string; -} -export interface Navigation { - title: string; - path: string; - type: string; - order: number; - private: boolean; - children?: Navigation[]; -} -export type FileMetadata = Record; -export type FileType = 'Markdown' | 'Canvas' | 'File' | 'Folder'; -export interface File { - project: number; - path: string; - owner: number; - title: string; - order: number; - type: FileType; - content: string; - navigable: boolean; - private: boolean; - metadata: FileMetadata; -} -export interface Comment { - project: number; - path: number; - user_id: number; - sequence: number; - position: number; - length: number; - content: string; -} -export interface User { - id: number; - username: string; -} -export interface Tag { - tag: string; - project: number; - description: string; -} - - -export type ProjectSearch = Project & -{ - pages: number; - username: string; -} -export type FileSearch = Omit & -{ - comments: number; - username: string; -} -export type CommentSearch = Comment & -{ - username: string; -} -export type UserSearch = User & -{ -} -export type CommentedFile = File & -{ - comments: CommentSearch[]; -} -export interface Search { - projects: ProjectSearch[]; - files: FileSearch[]; - users: UserSearch[]; -} \ No newline at end of file diff --git a/types/auth.d.ts b/types/auth.d.ts deleted file mode 100644 index 0010eb9..0000000 --- a/types/auth.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { ComputedRef, Ref } from 'vue' - -import 'vue-router'; -declare module 'vue-router' -{ - interface RouteMeta - { - requiresAuth?: boolean; - guestsGoesTo?: string; - usersGoesTo?: string; - } -} - -import 'nuxt'; -declare module 'nuxt' -{ - interface RuntimeConfig - { - session: SessionConfig; - } -} - -export interface UserRawData { - id: number; - username: string; - email: string; - state: number; -} - -export interface UserExtendedData { - signin_timestamp: number; -} - -export type User = UserRawData & UserExtendedData; - -export interface UserSession { - user?: User; - id?: string; -} - -export interface UserSessionRequired extends UserSession { - user: User; - id: string; -} - -export interface UserSessionComposable { - /** - * Computed indicating if the auth session is ready - */ - ready: ComputedRef - /** - * Computed indicating if the user is logged in. - */ - loggedIn: ComputedRef - /** - * The user object if logged in, null otherwise. - */ - user: ComputedRef - /** - * The session object. - */ - session: Ref - /** - * Fetch the user session from the server. - */ - fetch: () => Promise - /** - * Clear the user session and remove the session cookie. - */ - clear: () => Promise -} \ No newline at end of file diff --git a/types/canvas.d.ts b/types/canvas.d.ts deleted file mode 100644 index a126928..0000000 --- a/types/canvas.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -export interface CanvasContent { - nodes: CanvasNode[]; - edges: CanvasEdge[]; - groups: CanvasGroup[]; -} -export type CanvasColor = { - class?: string; -} & { - hex?: string; -} -export interface CanvasNode { - type: 'group' | 'text'; - id: string; - x: number; - y: number; - width: number; - height: number; - color?: CanvasColor; - label?: string; - text?: any; -}; -export interface CanvasEdge { - id: string; - fromNode: string; - fromSide: 'bottom' | 'top' | 'left' | 'right'; - toNode: string; - toSide: 'bottom' | 'top' | 'left' | 'right'; - color?: CanvasColor; - label?: string; -}; -export interface CanvasGroup { - name: string; - nodes: string[]; -} \ No newline at end of file diff --git a/utils/utils.ts b/utils/utils.ts deleted file mode 100644 index d7a0a6e..0000000 --- a/utils/utils.ts +++ /dev/null @@ -1,42 +0,0 @@ -export function unifySlug(slug: string | string[]): string -{ - return (Array.isArray(slug) ? slug.join('/') : slug); -} -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(); -} -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; -} -export function clamp(x: number, min: number, max: number): number { - if (x > max) - return max; - if (x < min) - return min; - return x; -} \ No newline at end of file