mirror of
https://github.com/Lifeforge-app/lifeforge.git
synced 2026-06-28 14:55:45 +00:00
25w29
Former-commit-id: e3dd3b1fa9510cbaa4ff21deda9310f86583e99c [formerly d71f00314d37442771ad59cfd33de230df9ff2d6] [formerly d236ed1a6d2ef949f36387e84bcd50425d24e266 [formerly 89aec5e5c3e321e53e8cec93effa25c6cd44039e]] Former-commit-id: f0ab09ec99e37dbefa18d8b873149044ecac5ee7 [formerly 211e34db4b22b193e2da9a5a7af70458249ed8e2] Former-commit-id: 82c9b5a2300566822f787af1f9eed1ddafdf1696
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
import clsx from 'clsx'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { IAchievementEntry } from '../interfaces/achievements_interfaces'
|
||||
import { AchievementsSchemas } from 'shared'
|
||||
|
||||
function DifficultySelector({
|
||||
selectedDifficulty,
|
||||
setSelectedDifficulty
|
||||
}: {
|
||||
selectedDifficulty: IAchievementEntry['difficulty']
|
||||
selectedDifficulty: AchievementsSchemas.IEntry['difficulty']
|
||||
setSelectedDifficulty: React.Dispatch<
|
||||
React.SetStateAction<IAchievementEntry['difficulty']>
|
||||
React.SetStateAction<AchievementsSchemas.IEntry['difficulty']>
|
||||
>
|
||||
}) {
|
||||
const { t } = useTranslation('apps.achievements')
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import clsx from 'clsx'
|
||||
import { useCallback } from 'react'
|
||||
import { AchievementsSchemas, ISchemaWithPB } from 'shared'
|
||||
|
||||
import { DeleteConfirmationModal, HamburgerMenu, MenuItem } from '@lifeforge/ui'
|
||||
import { useModalStore } from '@lifeforge/ui'
|
||||
|
||||
import { type IAchievementEntry } from '../interfaces/achievements_interfaces'
|
||||
import ModifyAchievementModal from './ModifyAchievementModal'
|
||||
|
||||
function EntryItem({ entry }: { entry: IAchievementEntry }) {
|
||||
function EntryItem({
|
||||
entry
|
||||
}: {
|
||||
entry: ISchemaWithPB<AchievementsSchemas.IEntry>
|
||||
}) {
|
||||
const open = useModalStore(state => state.open)
|
||||
|
||||
const handleDeleteEntry = useCallback(() => {
|
||||
@@ -16,7 +20,7 @@ function EntryItem({ entry }: { entry: IAchievementEntry }) {
|
||||
apiEndpoint: 'achievements/entries',
|
||||
data: entry,
|
||||
itemName: 'achievement',
|
||||
nameKey: 'title',
|
||||
nameKey: 'title' as const,
|
||||
queryKey: ['achievements/entries', entry.difficulty]
|
||||
})
|
||||
}, [entry])
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AchievementsSchemas, ISchemaWithPB } from 'shared'
|
||||
import COLOR from 'tailwindcss/colors'
|
||||
|
||||
import { FormModal } from '@lifeforge/ui'
|
||||
import { type IFieldProps } from '@lifeforge/ui'
|
||||
|
||||
import {
|
||||
type IAchievementEntry,
|
||||
IAchievementEntryFormState
|
||||
} from '../interfaces/achievements_interfaces'
|
||||
|
||||
const difficulties = [
|
||||
['easy', 'green'],
|
||||
['medium', 'yellow'],
|
||||
@@ -23,19 +19,19 @@ function ModifyAchievementModal({
|
||||
}: {
|
||||
data: {
|
||||
type: 'create' | 'update' | null
|
||||
existedData: IAchievementEntry | null
|
||||
currentDifficulty: IAchievementEntry['difficulty']
|
||||
existedData: ISchemaWithPB<AchievementsSchemas.IEntry> | null
|
||||
currentDifficulty: AchievementsSchemas.IEntry['difficulty']
|
||||
}
|
||||
onClose: () => void
|
||||
}) {
|
||||
const { t } = useTranslation('apps.achievements')
|
||||
const [formState, setFormState] = useState<IAchievementEntryFormState>({
|
||||
const [formState, setFormState] = useState<AchievementsSchemas.IEntry>({
|
||||
title: '',
|
||||
thoughts: '',
|
||||
difficulty: 'easy'
|
||||
})
|
||||
|
||||
const FIELDS: IFieldProps<IAchievementEntryFormState>[] = [
|
||||
const FIELDS: IFieldProps<AchievementsSchemas.IEntry>[] = [
|
||||
{
|
||||
id: 'title',
|
||||
required: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { IAchievementsEntry, SchemaWithPB } from 'shared'
|
||||
import { AchievementsSchemas, ISchemaWithPB } from 'shared'
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -22,8 +22,8 @@ function Achievements() {
|
||||
const { t } = useTranslation('apps.achievements')
|
||||
const open = useModalStore(state => state.open)
|
||||
const [selectedDifficulty, setSelectedDifficulty] =
|
||||
useState<IAchievementsEntry['difficulty']>('impossible')
|
||||
const entriesQuery = useAPIQuery<SchemaWithPB<IAchievementsEntry>[]>(
|
||||
useState<AchievementsSchemas.IEntry['difficulty']>('impossible')
|
||||
const entriesQuery = useAPIQuery<ISchemaWithPB<AchievementsSchemas.IEntry>[]>(
|
||||
`achievements/entries/${selectedDifficulty}`,
|
||||
['achievements/entries', selectedDifficulty]
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Link } from 'react-router'
|
||||
import { BlogSchemas } from 'shared'
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -11,11 +12,9 @@ import {
|
||||
|
||||
import useAPIQuery from '@hooks/useAPIQuery'
|
||||
|
||||
import { IBlogEntry } from './interfaces/blog_interfaces'
|
||||
|
||||
function Blog() {
|
||||
const { t } = useTranslation('apps.blog')
|
||||
const entriesQuery = useAPIQuery<IBlogEntry[]>('blog/entries', [
|
||||
const entriesQuery = useAPIQuery<BlogSchemas.IEntry[]>('blog/entries', [
|
||||
'blog',
|
||||
'entries'
|
||||
])
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
import { RecordModel } from 'pocketbase'
|
||||
|
||||
interface IBlogEntry extends RecordModel {
|
||||
title: string
|
||||
content: string
|
||||
media: string[]
|
||||
excerpt: string
|
||||
visibility: 'public' | 'private' | 'unlisted'
|
||||
featured_image: string
|
||||
labels: string[]
|
||||
category: string
|
||||
}
|
||||
|
||||
interface IBlogEntryFormState {
|
||||
title: string
|
||||
excerpt: string
|
||||
@@ -21,4 +8,4 @@ interface IBlogEntryFormState {
|
||||
labels: string[]
|
||||
}
|
||||
|
||||
export type { IBlogEntry, IBlogEntryFormState }
|
||||
export type { IBlogEntryFormState }
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
"build:shared": "cd shared && bun run build",
|
||||
"build:server": "cd server && bun run build",
|
||||
"build": "bun run build:shared && bun run build:server && bun run build:client",
|
||||
"types:client": "cd client && bun run tsc --noEmit",
|
||||
"types:shared": "cd shared && bun run tsc --noEmit",
|
||||
"types:server": "cd server && bun run tsc --noEmit",
|
||||
"types": "bun run types:shared && bun run types:server && bun run types:client",
|
||||
"lint:client": "cd client && bun run lint",
|
||||
"postinstall": "bun run build:shared && bun run build:server",
|
||||
"schema:generate": "bun run scripts/generateSchema.ts"
|
||||
},
|
||||
|
||||
@@ -3,19 +3,18 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { BlogSchemas } from "shared";
|
||||
import z from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { BlogEntrySchema } from "../schema";
|
||||
|
||||
const blogEntriesRouter = express.Router();
|
||||
|
||||
const getAllEntries = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all blog entries")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(BlogEntrySchema)),
|
||||
response: z.array(WithPBSchema(BlogSchemas.EntrySchema)),
|
||||
})
|
||||
.callback(({ pb }) => pb.collection("blog__entries").getFullList());
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: blog
|
||||
* Generated at: 2025-07-09T12:50:41.283Z
|
||||
* Contains: blog__entries, blog__categories
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const BlogEntrySchema = z.object({
|
||||
content: z.string(),
|
||||
title: z.string(),
|
||||
media: z.array(z.string()),
|
||||
excerpt: z.string(),
|
||||
visibility: z.enum(["private", "public", "unlisted", ""]),
|
||||
featured_image: z.string(),
|
||||
labels: z.any(),
|
||||
category: z.string(),
|
||||
});
|
||||
|
||||
const BlogCategorySchema = z.object({
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
icon: z.string(),
|
||||
});
|
||||
|
||||
type IBlogEntry = z.infer<typeof BlogEntrySchema>;
|
||||
type IBlogCategory = z.infer<typeof BlogCategorySchema>;
|
||||
|
||||
export { BlogEntrySchema, BlogCategorySchema };
|
||||
|
||||
export type { IBlogEntry, IBlogCategory };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
// Add your custom schemas here. They will not be overwritten by this script.
|
||||
@@ -1,8 +1,9 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { BlogSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IBlogEntry } from "../schema";
|
||||
|
||||
export const getAllEntries = (pb: PocketBase): Promise<WithPB<IBlogEntry>[]> =>
|
||||
pb.collection("blog__entries").getFullList<WithPB<IBlogEntry>>();
|
||||
export const getAllEntries = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<BlogSchemas.IEntry>[]> =>
|
||||
pb.collection("blog__entries").getFullList<WithPB<BlogSchemas.IEntry>>();
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { BooksLibraryCollectionSchema } from "../schema";
|
||||
import * as CollectionsService from "../services/collections.service";
|
||||
|
||||
const booksLibraryCollectionsRouter = express.Router();
|
||||
@@ -16,7 +16,9 @@ const getAllCollections = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all collections for the books library")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(BooksLibraryCollectionSchema)),
|
||||
response: z.array(
|
||||
WithPBSchema(BooksLibrarySchemas.CollectionAggregatedSchema),
|
||||
),
|
||||
})
|
||||
.callback(({ pb }) => CollectionsService.getAllCollections(pb));
|
||||
|
||||
@@ -24,8 +26,8 @@ const createCollection = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new collection for the books library")
|
||||
.schema({
|
||||
body: BooksLibraryCollectionSchema,
|
||||
response: WithPBSchema(BooksLibraryCollectionSchema),
|
||||
body: BooksLibrarySchemas.CollectionSchema,
|
||||
response: WithPBSchema(BooksLibrarySchemas.CollectionSchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(({ pb, body }) => CollectionsService.createCollection(pb, body));
|
||||
@@ -37,8 +39,8 @@ const updateCollection = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: BooksLibraryCollectionSchema,
|
||||
response: WithPBSchema(BooksLibraryCollectionSchema),
|
||||
body: BooksLibrarySchemas.CollectionSchema,
|
||||
response: WithPBSchema(BooksLibrarySchemas.CollectionAggregatedSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "books_library__collections",
|
||||
|
||||
@@ -5,11 +5,11 @@ import {
|
||||
} from "@functions/forgeController";
|
||||
import { getAPIKey } from "@functions/getAPIKey";
|
||||
import express from "express";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { BooksLibraryEntrySchema } from "../schema";
|
||||
import * as EntriesService from "../services/entries.service";
|
||||
|
||||
const booksLibraryEntriesRouter = express.Router();
|
||||
@@ -18,7 +18,7 @@ const getAllEntries = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all entries in the books library")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(BooksLibraryEntrySchema)),
|
||||
response: z.array(WithPBSchema(BooksLibrarySchemas.EntrySchema)),
|
||||
})
|
||||
.callback(({ pb }) => EntriesService.getAllEntries(pb));
|
||||
|
||||
@@ -29,7 +29,7 @@ const updateEntry = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: BooksLibraryEntrySchema.pick({
|
||||
body: BooksLibrarySchemas.EntrySchema.pick({
|
||||
title: true,
|
||||
authors: true,
|
||||
collection: true,
|
||||
@@ -44,7 +44,7 @@ const updateEntry = forgeController
|
||||
return isNaN(year) ? 0 : year;
|
||||
}),
|
||||
}),
|
||||
response: WithPBSchema(BooksLibraryEntrySchema),
|
||||
response: WithPBSchema(BooksLibrarySchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "books_library__entries",
|
||||
@@ -64,7 +64,7 @@ const toggleFavouriteStatus = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(BooksLibraryEntrySchema),
|
||||
response: WithPBSchema(BooksLibrarySchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "books_library__entries",
|
||||
@@ -80,7 +80,7 @@ const toggleReadStatus = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(BooksLibraryEntrySchema),
|
||||
response: WithPBSchema(BooksLibrarySchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "books_library__entries",
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { BooksLibraryFileTypeSchema } from "../schema";
|
||||
import * as FileTypesService from "../services/fileTypes.service";
|
||||
|
||||
const booksLibraryFileTypesRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const getAllFileTypes = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all file types for the books library")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(BooksLibraryFileTypeSchema)),
|
||||
response: z.array(WithPBSchema(BooksLibrarySchemas.FileTypeSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await FileTypesService.getAllFileTypes(pb));
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { BooksLibraryLanguageSchema } from "../schema";
|
||||
import * as LanguagesService from "../services/languages.service";
|
||||
|
||||
const booksLibraryLanguagesRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const getAllLanguages = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all languages for the books library")
|
||||
.schema({
|
||||
response: z.array(BooksLibraryLanguageSchema),
|
||||
response: z.array(BooksLibrarySchemas.LanguageSchema),
|
||||
})
|
||||
.callback(({ pb }) => LanguagesService.getAllLanguages(pb));
|
||||
|
||||
@@ -24,8 +24,8 @@ const createLanguage = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new language for the books library")
|
||||
.schema({
|
||||
body: BooksLibraryLanguageSchema,
|
||||
response: WithPBSchema(BooksLibraryLanguageSchema),
|
||||
body: BooksLibrarySchemas.LanguageSchema,
|
||||
response: WithPBSchema(BooksLibrarySchemas.LanguageSchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(
|
||||
@@ -39,8 +39,8 @@ const updateLanguage = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: BooksLibraryLanguageSchema,
|
||||
response: WithPBSchema(BooksLibraryLanguageSchema),
|
||||
body: BooksLibrarySchemas.LanguageSchema,
|
||||
response: WithPBSchema(BooksLibrarySchemas.LanguageSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "books_library__languages",
|
||||
|
||||
@@ -3,12 +3,9 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import {
|
||||
BooksLibraryEntrySchema,
|
||||
BooksLibraryLibgenSearchResultSchema,
|
||||
} from "../schema";
|
||||
import * as libgenService from "../services/libgen.service";
|
||||
|
||||
const booksLibraryLibgenRouter = express.Router();
|
||||
@@ -30,7 +27,7 @@ const searchBooks = forgeController
|
||||
req: z.string(),
|
||||
page: z.string(),
|
||||
}),
|
||||
response: BooksLibraryLibgenSearchResultSchema,
|
||||
response: BooksLibrarySchemas.BooksLibraryLibgenSearchResultSchema,
|
||||
})
|
||||
.callback(async ({ query }) => await libgenService.searchBooks(query));
|
||||
|
||||
@@ -55,7 +52,7 @@ const getLocalLibraryData = forgeController
|
||||
provider: z.string(),
|
||||
md5: z.string(),
|
||||
}),
|
||||
response: BooksLibraryEntrySchema.omit({
|
||||
response: BooksLibrarySchemas.EntrySchema.omit({
|
||||
collection: true,
|
||||
file: true,
|
||||
is_favourite: true,
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: booksLibrary
|
||||
* Generated at: 2025-07-09T12:50:41.285Z
|
||||
* Contains: books_library__collections, books_library__languages, books_library__entries, books_library__file_types, books_library__file_types_aggregated, books_library__languages_aggregated, books_library__collections_aggregated
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const BooksLibraryCollectionSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
});
|
||||
|
||||
const BooksLibraryLanguageSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
});
|
||||
|
||||
const BooksLibraryEntrySchema = z.object({
|
||||
title: z.string(),
|
||||
authors: z.string(),
|
||||
md5: z.string(),
|
||||
year_published: z.number(),
|
||||
publisher: z.string(),
|
||||
languages: z.array(z.string()),
|
||||
collection: z.string(),
|
||||
extension: z.string(),
|
||||
edition: z.string(),
|
||||
size: z.number(),
|
||||
isbn: z.string(),
|
||||
file: z.string(),
|
||||
thumbnail: z.string(),
|
||||
is_favourite: z.boolean(),
|
||||
is_read: z.boolean(),
|
||||
time_finished: z.string(),
|
||||
});
|
||||
|
||||
const BooksLibraryFileTypeSchema = z.object({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const BooksLibraryFileTypeAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
const BooksLibraryLanguageAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
const BooksLibraryCollectionAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
type IBooksLibraryCollection = z.infer<typeof BooksLibraryCollectionSchema>;
|
||||
type IBooksLibraryLanguage = z.infer<typeof BooksLibraryLanguageSchema>;
|
||||
type IBooksLibraryEntry = z.infer<typeof BooksLibraryEntrySchema>;
|
||||
type IBooksLibraryFileType = z.infer<typeof BooksLibraryFileTypeSchema>;
|
||||
type IBooksLibraryFileTypeAggregated = z.infer<
|
||||
typeof BooksLibraryFileTypeAggregatedSchema
|
||||
>;
|
||||
type IBooksLibraryLanguageAggregated = z.infer<
|
||||
typeof BooksLibraryLanguageAggregatedSchema
|
||||
>;
|
||||
type IBooksLibraryCollectionAggregated = z.infer<
|
||||
typeof BooksLibraryCollectionAggregatedSchema
|
||||
>;
|
||||
|
||||
export {
|
||||
BooksLibraryCollectionSchema,
|
||||
BooksLibraryLanguageSchema,
|
||||
BooksLibraryEntrySchema,
|
||||
BooksLibraryFileTypeSchema,
|
||||
BooksLibraryFileTypeAggregatedSchema,
|
||||
BooksLibraryLanguageAggregatedSchema,
|
||||
BooksLibraryCollectionAggregatedSchema,
|
||||
};
|
||||
|
||||
export type {
|
||||
IBooksLibraryCollection,
|
||||
IBooksLibraryLanguage,
|
||||
IBooksLibraryEntry,
|
||||
IBooksLibraryFileType,
|
||||
IBooksLibraryFileTypeAggregated,
|
||||
IBooksLibraryLanguageAggregated,
|
||||
IBooksLibraryCollectionAggregated,
|
||||
};
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
const BooksLibraryLibgenSearchResultSchema = z.object({
|
||||
provider: z.string(),
|
||||
query: z.string(),
|
||||
resultsCount: z.string(),
|
||||
data: z.record(z.string(), z.any()),
|
||||
page: z.number(),
|
||||
});
|
||||
|
||||
type IBooksLibraryLibgenSearchResult = z.infer<
|
||||
typeof BooksLibraryLibgenSearchResultSchema
|
||||
>;
|
||||
|
||||
export { BooksLibraryLibgenSearchResultSchema };
|
||||
|
||||
export type { IBooksLibraryLibgenSearchResult };
|
||||
@@ -1,34 +1,41 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IBooksLibraryCollection } from "../schema";
|
||||
|
||||
export const getAllCollections = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<IBooksLibraryCollection>[]> =>
|
||||
export const getAllCollections = (pb: PocketBase) =>
|
||||
pb
|
||||
.collection("books_library__collections_aggregated")
|
||||
.getFullList<WithPB<IBooksLibraryCollection>>({
|
||||
.getFullList<WithPB<BooksLibrarySchemas.ICollectionAggregated>>({
|
||||
sort: "name",
|
||||
});
|
||||
|
||||
export const createCollection = (
|
||||
export const createCollection = async (
|
||||
pb: PocketBase,
|
||||
data: Omit<IBooksLibraryCollection, "amount">,
|
||||
): Promise<WithPB<IBooksLibraryCollection>> =>
|
||||
pb
|
||||
data: Omit<BooksLibrarySchemas.ICollection, "amount">,
|
||||
) => {
|
||||
const collection = await pb
|
||||
.collection("books_library__collections")
|
||||
.create<WithPB<IBooksLibraryCollection>>(data);
|
||||
.create<WithPB<BooksLibrarySchemas.ICollection>>(data);
|
||||
|
||||
export const updateCollection = (
|
||||
return pb
|
||||
.collection("books_library__collections_aggregated")
|
||||
.getOne<WithPB<BooksLibrarySchemas.ICollectionAggregated>>(collection.id);
|
||||
};
|
||||
|
||||
export const updateCollection = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: Omit<IBooksLibraryCollection, "amount">,
|
||||
): Promise<WithPB<IBooksLibraryCollection>> =>
|
||||
pb
|
||||
data: Omit<BooksLibrarySchemas.ICollectionAggregated, "amount">,
|
||||
) => {
|
||||
const collection = await pb
|
||||
.collection("books_library__collections")
|
||||
.update<WithPB<IBooksLibraryCollection>>(id, data);
|
||||
.update<WithPB<BooksLibrarySchemas.ICollection>>(id, data);
|
||||
|
||||
return pb
|
||||
.collection("books_library__collections_aggregated")
|
||||
.getOne<WithPB<BooksLibrarySchemas.ICollectionAggregated>>(collection.id);
|
||||
};
|
||||
|
||||
export const deleteCollection = async (pb: PocketBase, id: string) => {
|
||||
await pb.collection("books_library__collections").delete(id);
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import ClientError from "@functions/ClientError";
|
||||
import mailer from "nodemailer";
|
||||
import Pocketbase from "pocketbase";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IBooksLibraryEntry } from "../schema";
|
||||
|
||||
export const getAllEntries = (
|
||||
pb: Pocketbase,
|
||||
): Promise<WithPB<IBooksLibraryEntry>[]> =>
|
||||
): Promise<WithPB<BooksLibrarySchemas.IEntry>[]> =>
|
||||
pb
|
||||
.collection("books_library__entries")
|
||||
.getFullList<WithPB<IBooksLibraryEntry>>({
|
||||
.getFullList<WithPB<BooksLibrarySchemas.IEntry>>({
|
||||
sort: "-is_favourite,-created",
|
||||
});
|
||||
|
||||
@@ -19,7 +18,7 @@ export const updateEntry = (
|
||||
pb: Pocketbase,
|
||||
id: string,
|
||||
data: Pick<
|
||||
IBooksLibraryEntry,
|
||||
BooksLibrarySchemas.IEntry,
|
||||
| "title"
|
||||
| "authors"
|
||||
| "collection"
|
||||
@@ -29,22 +28,22 @@ export const updateEntry = (
|
||||
| "publisher"
|
||||
| "year_published"
|
||||
>,
|
||||
): Promise<WithPB<IBooksLibraryEntry>> =>
|
||||
): Promise<WithPB<BooksLibrarySchemas.IEntry>> =>
|
||||
pb
|
||||
.collection("books_library__entries")
|
||||
.update<WithPB<IBooksLibraryEntry>>(id, data);
|
||||
.update<WithPB<BooksLibrarySchemas.IEntry>>(id, data);
|
||||
|
||||
export const toggleFavouriteStatus = async (
|
||||
pb: Pocketbase,
|
||||
id: string,
|
||||
): Promise<WithPB<IBooksLibraryEntry>> => {
|
||||
): Promise<WithPB<BooksLibrarySchemas.IEntry>> => {
|
||||
const book = await pb
|
||||
.collection("books_library__entries")
|
||||
.getOne<WithPB<IBooksLibraryEntry>>(id);
|
||||
.getOne<WithPB<BooksLibrarySchemas.IEntry>>(id);
|
||||
|
||||
return await pb
|
||||
.collection("books_library__entries")
|
||||
.update<WithPB<IBooksLibraryEntry>>(id, {
|
||||
.update<WithPB<BooksLibrarySchemas.IEntry>>(id, {
|
||||
is_favourite: !book.is_favourite,
|
||||
});
|
||||
};
|
||||
@@ -52,14 +51,14 @@ export const toggleFavouriteStatus = async (
|
||||
export const toggleReadStatus = async (
|
||||
pb: Pocketbase,
|
||||
id: string,
|
||||
): Promise<WithPB<IBooksLibraryEntry>> => {
|
||||
): Promise<WithPB<BooksLibrarySchemas.IEntry>> => {
|
||||
const book = await pb
|
||||
.collection("books_library__entries")
|
||||
.getOne<WithPB<IBooksLibraryEntry>>(id);
|
||||
.getOne<WithPB<BooksLibrarySchemas.IEntry>>(id);
|
||||
|
||||
return await pb
|
||||
.collection("books_library__entries")
|
||||
.update<WithPB<IBooksLibraryEntry>>(id, {
|
||||
.update<WithPB<BooksLibrarySchemas.IEntry>>(id, {
|
||||
is_read: !book.is_read,
|
||||
time_finished: !book.is_read ? new Date().toISOString() : "",
|
||||
});
|
||||
@@ -86,7 +85,7 @@ export const sendToKindle = async (
|
||||
|
||||
const entry = await pb
|
||||
.collection("books_library__entries")
|
||||
.getOne<IBooksLibraryEntry>(id);
|
||||
.getOne<BooksLibrarySchemas.IEntry>(id);
|
||||
|
||||
const fileLink = pb.files.getURL(entry, entry.file);
|
||||
const content = await fetch(fileLink).then((res) => res.arrayBuffer());
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IBooksLibraryFileType } from "../schema";
|
||||
|
||||
export const getAllFileTypes = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<IBooksLibraryFileType>[]> =>
|
||||
): Promise<WithPB<BooksLibrarySchemas.IFileType>[]> =>
|
||||
pb
|
||||
.collection("books_library__file_types_aggregated")
|
||||
.getFullList<WithPB<IBooksLibraryFileType>>({
|
||||
.getFullList<WithPB<BooksLibrarySchemas.IFileType>>({
|
||||
sort: "name",
|
||||
});
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IBooksLibraryLanguage } from "../schema";
|
||||
|
||||
export const getAllLanguages = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<IBooksLibraryLanguage>[]> =>
|
||||
): Promise<WithPB<BooksLibrarySchemas.ILanguage>[]> =>
|
||||
pb
|
||||
.collection("books_library__languages_aggregated")
|
||||
.getFullList<WithPB<IBooksLibraryLanguage>>();
|
||||
.getFullList<WithPB<BooksLibrarySchemas.ILanguage>>();
|
||||
|
||||
export const createLanguage = (
|
||||
pb: PocketBase,
|
||||
languageData: { name: string; icon: string },
|
||||
): Promise<WithPB<IBooksLibraryLanguage>> =>
|
||||
): Promise<WithPB<BooksLibrarySchemas.ILanguage>> =>
|
||||
pb
|
||||
.collection("books_library__languages")
|
||||
.create<WithPB<IBooksLibraryLanguage>>(languageData);
|
||||
.create<WithPB<BooksLibrarySchemas.ILanguage>>(languageData);
|
||||
|
||||
export const updateLanguage = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
languageData: { name: string; icon: string },
|
||||
): Promise<WithPB<IBooksLibraryLanguage>> =>
|
||||
): Promise<WithPB<BooksLibrarySchemas.ILanguage>> =>
|
||||
pb
|
||||
.collection("books_library__languages")
|
||||
.update<WithPB<IBooksLibraryLanguage>>(id, languageData);
|
||||
.update<WithPB<BooksLibrarySchemas.ILanguage>>(id, languageData);
|
||||
|
||||
export const deleteLanguage = async (pb: PocketBase, id: string) => {
|
||||
await pb.collection("books_library__languages").delete(id);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
import { IBooksLibraryLibgenSearchResult } from "../../../schema";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
|
||||
export const searchBooks = async (queries: {
|
||||
provider: string;
|
||||
req: string;
|
||||
page: string;
|
||||
}): Promise<IBooksLibraryLibgenSearchResult> => {
|
||||
}): Promise<BooksLibrarySchemas.IBooksLibraryLibgenSearchResult> => {
|
||||
const target = new URL(
|
||||
queries.provider === "libgen.is"
|
||||
? "http://libgen.is/search.php?lg_topic=libgen&open=0&view=detailed&res=25&column=def&phrase=0&sort=year&sortmode=DESC"
|
||||
@@ -19,7 +18,7 @@ export const searchBooks = async (queries: {
|
||||
const data = await fetch(target.href).then((res) => res.text());
|
||||
const dom = new JSDOM(data);
|
||||
const document = dom.window.document;
|
||||
let final: IBooksLibraryLibgenSearchResult["data"] = [];
|
||||
let final: BooksLibrarySchemas.IBooksLibraryLibgenSearchResult["data"] = [];
|
||||
let resultsCount = "";
|
||||
|
||||
if (queries.provider === "libgen.is") {
|
||||
@@ -49,8 +48,8 @@ export const searchBooks = async (queries: {
|
||||
function parseLibgenIS(
|
||||
document: Document,
|
||||
): [
|
||||
IBooksLibraryLibgenSearchResult["data"],
|
||||
IBooksLibraryLibgenSearchResult["resultsCount"],
|
||||
BooksLibrarySchemas.IBooksLibraryLibgenSearchResult["data"],
|
||||
BooksLibrarySchemas.IBooksLibraryLibgenSearchResult["resultsCount"],
|
||||
] {
|
||||
const table = Array.from(
|
||||
document.querySelectorAll('body > table[rules="cols"]'),
|
||||
@@ -98,8 +97,8 @@ function parseLibgenMirror(
|
||||
provider: string,
|
||||
document: Document,
|
||||
): [
|
||||
IBooksLibraryLibgenSearchResult["data"],
|
||||
IBooksLibraryLibgenSearchResult["resultsCount"],
|
||||
BooksLibrarySchemas.IBooksLibraryLibgenSearchResult["data"],
|
||||
BooksLibrarySchemas.IBooksLibraryLibgenSearchResult["resultsCount"],
|
||||
] {
|
||||
return [
|
||||
Array.from(document.querySelectorAll("#tablelibgen tbody tr")).map((e) => ({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { spawn } from "child_process";
|
||||
import fs from "fs";
|
||||
import Pocketbase from "pocketbase";
|
||||
import { BooksLibrarySchemas } from "shared";
|
||||
import { Server } from "socket.io";
|
||||
|
||||
import {
|
||||
@@ -8,14 +9,12 @@ import {
|
||||
updateTaskInPool,
|
||||
} from "@middlewares/taskPoolMiddleware";
|
||||
|
||||
import { IBooksLibraryEntry } from "../../../schema";
|
||||
|
||||
export const addToLibrary = async (
|
||||
io: Server,
|
||||
pb: Pocketbase,
|
||||
md5: string,
|
||||
metadata: Omit<
|
||||
IBooksLibraryEntry,
|
||||
BooksLibrarySchemas.IEntry,
|
||||
"thumbnail" | "file" | "is_favourite" | "is_read" | "time_finished"
|
||||
> & {
|
||||
thumbnail: string;
|
||||
@@ -140,7 +139,7 @@ const processDownloadedFiles = async (
|
||||
pb: Pocketbase,
|
||||
md5: string,
|
||||
metadata: Omit<
|
||||
IBooksLibraryEntry,
|
||||
BooksLibrarySchemas.IEntry,
|
||||
"thumbnail" | "file" | "is_favourite" | "is_read" | "time_finished"
|
||||
> & {
|
||||
thumbnail: string | File;
|
||||
|
||||
@@ -4,11 +4,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { CalendarSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { CalendarCalendarSchema } from "../schema";
|
||||
import * as CalendarsService from "../services/calendars.service";
|
||||
|
||||
const calendarCalendarsRouter = express.Router();
|
||||
@@ -17,7 +17,7 @@ const getAllCalendars = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all calendars")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(CalendarCalendarSchema)),
|
||||
response: z.array(WithPBSchema(CalendarSchemas.CalendarSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await CalendarsService.getAllCalendars(pb));
|
||||
|
||||
@@ -28,7 +28,7 @@ const getCalendarById = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(CalendarCalendarSchema),
|
||||
response: WithPBSchema(CalendarSchemas.CalendarSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "calendar__calendars",
|
||||
@@ -42,8 +42,8 @@ const createCalendar = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new calendar")
|
||||
.schema({
|
||||
body: CalendarCalendarSchema,
|
||||
response: WithPBSchema(CalendarCalendarSchema),
|
||||
body: CalendarSchemas.CalendarSchema,
|
||||
response: WithPBSchema(CalendarSchemas.CalendarSchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(async ({ pb, body }) => {
|
||||
@@ -66,8 +66,8 @@ const updateCalendar = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: CalendarCalendarSchema,
|
||||
response: WithPBSchema(CalendarCalendarSchema),
|
||||
body: CalendarSchemas.CalendarSchema,
|
||||
response: WithPBSchema(CalendarSchemas.CalendarSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "calendar__calendars",
|
||||
|
||||
@@ -4,11 +4,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { CalendarSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { CalendarCategorySchema } from "../schema";
|
||||
import * as CategoriesService from "../services/categories.service";
|
||||
|
||||
const calendarCategoriesRouter = express.Router();
|
||||
@@ -17,7 +17,7 @@ const getAllCategories = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all calendar categories")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(CalendarCategorySchema)),
|
||||
response: z.array(WithPBSchema(CalendarSchemas.CategorySchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await CategoriesService.getAllCategories(pb));
|
||||
|
||||
@@ -28,7 +28,7 @@ const getCategoryById = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(CalendarCategorySchema),
|
||||
response: WithPBSchema(CalendarSchemas.CategorySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "calendar__categories",
|
||||
@@ -42,8 +42,8 @@ const createCategory = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new calendar category")
|
||||
.schema({
|
||||
body: CalendarCategorySchema,
|
||||
response: WithPBSchema(CalendarCategorySchema),
|
||||
body: CalendarSchemas.CategorySchema,
|
||||
response: WithPBSchema(CalendarSchemas.CategorySchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(async ({ pb, body }) => {
|
||||
@@ -70,8 +70,8 @@ const updateCategory = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: CalendarCategorySchema,
|
||||
response: WithPBSchema(CalendarCategorySchema),
|
||||
body: CalendarSchemas.CategorySchema,
|
||||
response: WithPBSchema(CalendarSchemas.CategorySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "calendar__categories",
|
||||
|
||||
@@ -4,13 +4,13 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { CalendarSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { singleUploadMiddleware } from "@middlewares/uploadMiddleware";
|
||||
|
||||
import { CalendarEventSchema } from "../schema";
|
||||
import * as EventsService from "../services/events.service";
|
||||
|
||||
const calendarEventsRouter = express.Router();
|
||||
@@ -23,7 +23,7 @@ const getEventsByDateRange = forgeController
|
||||
start: z.string(),
|
||||
end: z.string(),
|
||||
}),
|
||||
response: z.array(WithPBSchema(CalendarEventSchema.partial())),
|
||||
response: z.array(WithPBSchema(CalendarSchemas.EventSchema.partial())),
|
||||
})
|
||||
.callback(
|
||||
async ({ pb, query: { start, end } }) =>
|
||||
@@ -34,7 +34,7 @@ const getEventsToday = forgeController
|
||||
.route("GET /today")
|
||||
.description("Get today's events")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(CalendarEventSchema.partial())),
|
||||
response: z.array(WithPBSchema(CalendarSchemas.EventSchema.partial())),
|
||||
})
|
||||
.callback(async ({ pb }) => await EventsService.getTodayEvents(pb));
|
||||
|
||||
@@ -45,7 +45,7 @@ const getEventById = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(CalendarEventSchema),
|
||||
response: WithPBSchema(CalendarSchemas.EventSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "calendar__events",
|
||||
@@ -58,7 +58,7 @@ const createEvent = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new event")
|
||||
.schema({
|
||||
body: CalendarEventSchema.omit({
|
||||
body: CalendarSchemas.EventSchema.omit({
|
||||
exceptions: true,
|
||||
}).extend({
|
||||
location: z
|
||||
@@ -72,7 +72,7 @@ const createEvent = forgeController
|
||||
])
|
||||
.optional(),
|
||||
}),
|
||||
response: WithPBSchema(CalendarEventSchema),
|
||||
response: WithPBSchema(CalendarSchemas.EventSchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(async ({ pb, body }) => {
|
||||
@@ -88,7 +88,7 @@ const scanImage = forgeController
|
||||
.description("Scan an image to extract event data")
|
||||
.middlewares(singleUploadMiddleware)
|
||||
.schema({
|
||||
response: CalendarEventSchema.partial(),
|
||||
response: CalendarSchemas.EventSchema.partial(),
|
||||
})
|
||||
.callback(async ({ pb, req }) => {
|
||||
const { file } = req;
|
||||
@@ -133,10 +133,10 @@ const updateEvent = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: CalendarEventSchema.partial().omit({
|
||||
body: CalendarSchemas.EventSchema.partial().omit({
|
||||
exceptions: true,
|
||||
}),
|
||||
response: WithPBSchema(CalendarEventSchema),
|
||||
response: WithPBSchema(CalendarSchemas.EventSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "calendar__events",
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: calendar
|
||||
* Generated at: 2025-07-09T12:50:41.282Z
|
||||
* Contains: calendar__events, calendar__categories, calendar__categories_aggregated, calendar__calendars
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const CalendarEventSchema = z.object({
|
||||
start: z.string(),
|
||||
end: z.string(),
|
||||
title: z.string(),
|
||||
category: z.string(),
|
||||
calendar: z.string(),
|
||||
location: z.string(),
|
||||
reference_link: z.string(),
|
||||
description: z.string(),
|
||||
is_striktethrough: z.boolean(),
|
||||
is_recurring: z.boolean(),
|
||||
use_google_map: z.boolean(),
|
||||
type: z.enum(["single", "recurring", ""]),
|
||||
recurring_rrule: z.string(),
|
||||
recurring_duration_unit: z.string(),
|
||||
recurring_duration_amount: z.number(),
|
||||
exceptions: z.any(),
|
||||
});
|
||||
|
||||
const CalendarCategorySchema = z.object({
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
icon: z.string(),
|
||||
});
|
||||
|
||||
const CalendarCategoryAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
const CalendarCalendarSchema = z.object({
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
});
|
||||
|
||||
type ICalendarEvent = z.infer<typeof CalendarEventSchema>;
|
||||
type ICalendarCategory = z.infer<typeof CalendarCategorySchema>;
|
||||
type ICalendarCategoryAggregated = z.infer<
|
||||
typeof CalendarCategoryAggregatedSchema
|
||||
>;
|
||||
type ICalendarCalendar = z.infer<typeof CalendarCalendarSchema>;
|
||||
|
||||
export {
|
||||
CalendarEventSchema,
|
||||
CalendarCategorySchema,
|
||||
CalendarCategoryAggregatedSchema,
|
||||
CalendarCalendarSchema,
|
||||
};
|
||||
|
||||
export type {
|
||||
ICalendarEvent,
|
||||
ICalendarCategory,
|
||||
ICalendarCategoryAggregated,
|
||||
ICalendarCalendar,
|
||||
};
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
// Add your custom schemas here. They will not be overwritten by this script.
|
||||
@@ -1,38 +1,41 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { CalendarSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { ICalendarCalendar } from "../schema";
|
||||
|
||||
export const getAllCalendars = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<ICalendarCalendar>[]> =>
|
||||
pb.collection("calendar__calendars").getFullList<WithPB<ICalendarCalendar>>({
|
||||
sort: "+name",
|
||||
});
|
||||
): Promise<WithPB<CalendarSchemas.ICalendar>[]> =>
|
||||
pb
|
||||
.collection("calendar__calendars")
|
||||
.getFullList<WithPB<CalendarSchemas.ICalendar>>({
|
||||
sort: "+name",
|
||||
});
|
||||
|
||||
export const getCalendarById = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<ICalendarCalendar>> =>
|
||||
pb.collection("calendar__calendars").getOne<WithPB<ICalendarCalendar>>(id);
|
||||
): Promise<WithPB<CalendarSchemas.ICalendar>> =>
|
||||
pb
|
||||
.collection("calendar__calendars")
|
||||
.getOne<WithPB<CalendarSchemas.ICalendar>>(id);
|
||||
|
||||
export const createCalendar = (
|
||||
pb: PocketBase,
|
||||
calendarData: ICalendarCalendar,
|
||||
): Promise<WithPB<ICalendarCalendar>> =>
|
||||
calendarData: CalendarSchemas.ICalendar,
|
||||
): Promise<WithPB<CalendarSchemas.ICalendar>> =>
|
||||
pb
|
||||
.collection("calendar__calendars")
|
||||
.create<WithPB<ICalendarCalendar>>(calendarData);
|
||||
.create<WithPB<CalendarSchemas.ICalendar>>(calendarData);
|
||||
|
||||
export const updateCalendar = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
calendarData: ICalendarCalendar,
|
||||
): Promise<WithPB<ICalendarCalendar>> =>
|
||||
calendarData: CalendarSchemas.ICalendar,
|
||||
): Promise<WithPB<CalendarSchemas.ICalendar>> =>
|
||||
pb
|
||||
.collection("calendar__calendars")
|
||||
.update<WithPB<ICalendarCalendar>>(id, calendarData);
|
||||
.update<WithPB<CalendarSchemas.ICalendar>>(id, calendarData);
|
||||
|
||||
export const deleteCalendar = async (pb: PocketBase, id: string) => {
|
||||
await pb.collection("calendar__calendars").delete(id);
|
||||
|
||||
@@ -1,43 +1,44 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { CalendarSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { ICalendarCategory } from "../schema";
|
||||
|
||||
export const getAllCategories = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<ICalendarCategory>[]> =>
|
||||
): Promise<WithPB<CalendarSchemas.ICategory>[]> =>
|
||||
pb
|
||||
.collection("calendar__categories_aggregated")
|
||||
.getFullList<WithPB<ICalendarCategory>>({
|
||||
.getFullList<WithPB<CalendarSchemas.ICategory>>({
|
||||
sort: "+name",
|
||||
});
|
||||
|
||||
export const createCategory = async (
|
||||
pb: PocketBase,
|
||||
categoryData: Omit<ICalendarCategory, "amount">,
|
||||
): Promise<WithPB<ICalendarCategory>> => {
|
||||
categoryData: Omit<CalendarSchemas.ICategory, "amount">,
|
||||
): Promise<WithPB<CalendarSchemas.ICategory>> => {
|
||||
const createdEntry = await pb
|
||||
.collection("calendar__categories")
|
||||
.create<WithPB<Omit<ICalendarCategory, "amount">>>(categoryData);
|
||||
.create<WithPB<Omit<CalendarSchemas.ICategory, "amount">>>(categoryData);
|
||||
|
||||
return await pb
|
||||
.collection("calendar__categories_aggregated")
|
||||
.getOne<WithPB<ICalendarCategory>>(createdEntry.id);
|
||||
.getOne<WithPB<CalendarSchemas.ICategory>>(createdEntry.id);
|
||||
};
|
||||
|
||||
export const updateCategory = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
categoryData: Omit<ICalendarCategory, "amount">,
|
||||
): Promise<WithPB<ICalendarCategory>> => {
|
||||
categoryData: Omit<CalendarSchemas.ICategory, "amount">,
|
||||
): Promise<WithPB<CalendarSchemas.ICategory>> => {
|
||||
const updatedEntry = await pb
|
||||
.collection("calendar__categories")
|
||||
.update<WithPB<Omit<ICalendarCategory, "amount">>>(id, categoryData);
|
||||
.update<
|
||||
WithPB<Omit<CalendarSchemas.ICategory, "amount">>
|
||||
>(id, categoryData);
|
||||
|
||||
return await pb
|
||||
.collection("calendar__categories_aggregated")
|
||||
.getOne<WithPB<ICalendarCategory>>(updatedEntry.id);
|
||||
.getOne<WithPB<CalendarSchemas.ICategory>>(updatedEntry.id);
|
||||
};
|
||||
|
||||
export const deleteCategory = async (pb: PocketBase, id: string) => {
|
||||
@@ -47,5 +48,7 @@ export const deleteCategory = async (pb: PocketBase, id: string) => {
|
||||
export const getCategoryById = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<ICalendarCategory>> =>
|
||||
pb.collection("calendar__categories").getOne<WithPB<ICalendarCategory>>(id);
|
||||
): Promise<WithPB<CalendarSchemas.ICategory>> =>
|
||||
pb
|
||||
.collection("calendar__categories")
|
||||
.getOne<WithPB<CalendarSchemas.ICategory>>(id);
|
||||
|
||||
@@ -3,19 +3,16 @@ import fs from "fs";
|
||||
import moment from "moment";
|
||||
import PocketBase from "pocketbase";
|
||||
import rrule from "rrule";
|
||||
import { CalendarSchemas, MoviesSchemas, TodoListSchemas } from "shared";
|
||||
import { z } from "zod";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IMoviesEntry } from "../../movies/schema";
|
||||
import { ITodoListEntry } from "../../todoList/schema";
|
||||
import { ICalendarCategory, ICalendarEvent } from "../schema";
|
||||
|
||||
export const getEventsByDateRange = async (
|
||||
pb: PocketBase,
|
||||
startDate: string,
|
||||
endDate: string,
|
||||
): Promise<WithPB<Partial<ICalendarEvent>>[]> => {
|
||||
): Promise<WithPB<Partial<CalendarSchemas.IEvent>>[]> => {
|
||||
const start = moment(startDate).startOf("day").toISOString();
|
||||
const end = moment(endDate).endOf("day").toISOString();
|
||||
|
||||
@@ -23,7 +20,7 @@ export const getEventsByDateRange = async (
|
||||
|
||||
const singleCalendarEvents = await pb
|
||||
.collection("calendar__events")
|
||||
.getFullList<WithPB<ICalendarEvent>>({
|
||||
.getFullList<WithPB<CalendarSchemas.IEvent>>({
|
||||
filter: `(start >= '${start}' || end >= '${start}') && (start <= '${end}' || end <= '${end}') && type="single"`,
|
||||
});
|
||||
|
||||
@@ -31,7 +28,7 @@ export const getEventsByDateRange = async (
|
||||
|
||||
const recurringCalendarEvents = await pb
|
||||
.collection("calendar__events")
|
||||
.getFullList<WithPB<ICalendarEvent>>({
|
||||
.getFullList<WithPB<CalendarSchemas.IEvent>>({
|
||||
filter: "type='recurring'",
|
||||
});
|
||||
|
||||
@@ -85,7 +82,7 @@ export const getEventsByDateRange = async (
|
||||
const todoEntries = (
|
||||
await pb
|
||||
.collection("todo_list__entries")
|
||||
.getFullList<WithPB<ITodoListEntry>>({
|
||||
.getFullList<WithPB<TodoListSchemas.IEntry>>({
|
||||
filter: `due_date >= '${start}' && due_date <= '${end}'`,
|
||||
})
|
||||
.catch(() => [])
|
||||
@@ -99,7 +96,7 @@ export const getEventsByDateRange = async (
|
||||
description: entry.notes,
|
||||
reference_link: `/todo-list?entry=${entry.id}`,
|
||||
is_strikethrough: entry.done,
|
||||
} as Partial<WithPB<ICalendarEvent>>;
|
||||
} as Partial<WithPB<CalendarSchemas.IEvent>>;
|
||||
});
|
||||
|
||||
allEvents.push(...todoEntries);
|
||||
@@ -107,7 +104,7 @@ export const getEventsByDateRange = async (
|
||||
const movieEntries = (
|
||||
await pb
|
||||
.collection("movies__entries")
|
||||
.getFullList<WithPB<IMoviesEntry>>({
|
||||
.getFullList<WithPB<MoviesSchemas.IEntry>>({
|
||||
filter: `theatre_showtime >= '${start}' && theatre_showtime <= '${end}'`,
|
||||
})
|
||||
.catch(() => [])
|
||||
@@ -132,7 +129,7 @@ ${entry.theatre_number}
|
||||
${entry.theatre_seat}
|
||||
`,
|
||||
reference_link: `/movies?show-ticket=${entry.id}`,
|
||||
} as WithPB<ICalendarEvent>;
|
||||
} as WithPB<CalendarSchemas.IEvent>;
|
||||
});
|
||||
|
||||
allEvents.push(...movieEntries);
|
||||
@@ -142,7 +139,7 @@ ${entry.theatre_seat}
|
||||
|
||||
export const getTodayEvents = async (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<Partial<ICalendarEvent>>[]> => {
|
||||
): Promise<WithPB<Partial<CalendarSchemas.IEvent>>[]> => {
|
||||
const day = moment().format("YYYY-MM-DD");
|
||||
|
||||
const events = await getEventsByDateRange(pb, day, day);
|
||||
@@ -153,12 +150,12 @@ export const getTodayEvents = async (
|
||||
export const createEvent = async (
|
||||
pb: PocketBase,
|
||||
eventData: Omit<
|
||||
ICalendarEvent,
|
||||
CalendarSchemas.IEvent,
|
||||
"is_strikethrough" | "exceptions" | "location"
|
||||
> & {
|
||||
location?: string | { displayName: { text: string } };
|
||||
},
|
||||
): Promise<WithPB<ICalendarEvent>> => {
|
||||
): Promise<WithPB<CalendarSchemas.IEvent>> => {
|
||||
if (eventData.type === "recurring") {
|
||||
eventData.end = "";
|
||||
} else {
|
||||
@@ -173,16 +170,16 @@ export const createEvent = async (
|
||||
|
||||
return await pb
|
||||
.collection("calendar__events")
|
||||
.create<WithPB<ICalendarEvent>>(eventData);
|
||||
.create<WithPB<CalendarSchemas.IEvent>>(eventData);
|
||||
};
|
||||
|
||||
export const scanImage = async (
|
||||
pb: PocketBase,
|
||||
filePath: string,
|
||||
): Promise<Partial<ICalendarEvent> | null> => {
|
||||
): Promise<Partial<CalendarSchemas.IEvent> | null> => {
|
||||
const categories = await pb
|
||||
.collection("calendar__categories")
|
||||
.getFullList<WithPB<ICalendarCategory>>();
|
||||
.getFullList<WithPB<CalendarSchemas.ICategory>>();
|
||||
|
||||
const categoryList = categories.map((category) => category.name);
|
||||
|
||||
@@ -245,21 +242,24 @@ export const scanImage = async (
|
||||
return null;
|
||||
}
|
||||
|
||||
(response as Partial<ICalendarEvent>).category = categories.find(
|
||||
(response as Partial<CalendarSchemas.IEvent>).category = categories.find(
|
||||
(category) => category.name === response.category,
|
||||
)?.id;
|
||||
|
||||
return response as Partial<ICalendarEvent>;
|
||||
return response as Partial<CalendarSchemas.IEvent>;
|
||||
};
|
||||
|
||||
export const updateEvent = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
eventData: Omit<Partial<ICalendarEvent>, "is_strikethrough" | "exceptions">,
|
||||
): Promise<WithPB<ICalendarEvent>> =>
|
||||
eventData: Omit<
|
||||
Partial<CalendarSchemas.IEvent>,
|
||||
"is_strikethrough" | "exceptions"
|
||||
>,
|
||||
): Promise<WithPB<CalendarSchemas.IEvent>> =>
|
||||
pb
|
||||
.collection("calendar__events")
|
||||
.update<WithPB<ICalendarEvent>>(id, eventData);
|
||||
.update<WithPB<CalendarSchemas.IEvent>>(id, eventData);
|
||||
|
||||
export const deleteEvent = async (pb: PocketBase, id: string) => {
|
||||
await pb.collection("calendar__events").delete(id);
|
||||
@@ -268,8 +268,8 @@ export const deleteEvent = async (pb: PocketBase, id: string) => {
|
||||
export const getEventById = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<ICalendarEvent>> =>
|
||||
pb.collection("calendar__events").getOne<WithPB<ICalendarEvent>>(id);
|
||||
): Promise<WithPB<CalendarSchemas.IEvent>> =>
|
||||
pb.collection("calendar__events").getOne<WithPB<CalendarSchemas.IEvent>>(id);
|
||||
|
||||
export const addException = async (
|
||||
pb: PocketBase,
|
||||
@@ -278,7 +278,7 @@ export const addException = async (
|
||||
): Promise<boolean> => {
|
||||
const event = await pb
|
||||
.collection("calendar__events")
|
||||
.getOne<WithPB<ICalendarEvent>>(id);
|
||||
.getOne<WithPB<CalendarSchemas.IEvent>>(id);
|
||||
|
||||
const exceptions = event.exceptions || [];
|
||||
|
||||
@@ -290,7 +290,7 @@ export const addException = async (
|
||||
|
||||
await pb
|
||||
.collection("calendar__events")
|
||||
.update<WithPB<ICalendarEvent>>(id, { exceptions });
|
||||
.update<WithPB<CalendarSchemas.IEvent>>(id, { exceptions });
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -4,15 +4,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { CodeTimeSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import {
|
||||
CodeTimeActivitiesSchema,
|
||||
CodeTimeDailyEntrySchema,
|
||||
CodeTimeStatisticsSchema,
|
||||
} from "../schema";
|
||||
import * as CodeTimeService from "../services/codeTime.service";
|
||||
|
||||
const codeTimeRouter = express.Router();
|
||||
@@ -29,7 +25,7 @@ const getActivities = forgeController
|
||||
val ? parseInt(val, 10) : new Date().getFullYear(),
|
||||
),
|
||||
}),
|
||||
response: CodeTimeActivitiesSchema,
|
||||
response: CodeTimeSchemas.CodeTimeActivitiesSchema,
|
||||
})
|
||||
.callback(
|
||||
async ({ pb, query: { year } }) =>
|
||||
@@ -40,7 +36,7 @@ const getStatistics = forgeController
|
||||
.route("GET /statistics")
|
||||
.description("Get code time statistics")
|
||||
.schema({
|
||||
response: CodeTimeStatisticsSchema,
|
||||
response: CodeTimeSchemas.CodeTimeStatisticsSchema,
|
||||
})
|
||||
.callback(async ({ pb }) => await CodeTimeService.getStatistics(pb));
|
||||
|
||||
@@ -51,7 +47,7 @@ const getLastXDays = forgeController
|
||||
query: z.object({
|
||||
days: z.string().transform((val) => parseInt(val, 10)),
|
||||
}),
|
||||
response: z.array(WithPBSchema(CodeTimeDailyEntrySchema)),
|
||||
response: z.array(WithPBSchema(CodeTimeSchemas.DailyEntrySchema)),
|
||||
})
|
||||
.callback(async ({ pb, query: { days } }) => {
|
||||
if (days > 30) {
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: codeTime
|
||||
* Generated at: 2025-07-09T12:50:41.285Z
|
||||
* Contains: code_time__projects, code_time__languages, code_time__daily_entries
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const CodeTimeProjectSchema = z.object({
|
||||
name: z.string(),
|
||||
duration: z.number(),
|
||||
});
|
||||
|
||||
const CodeTimeLanguageSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
duration: z.number(),
|
||||
});
|
||||
|
||||
const CodeTimeDailyEntrySchema = z.object({
|
||||
date: z.string(),
|
||||
relative_files: z.any(),
|
||||
projects: z.any(),
|
||||
total_minutes: z.number(),
|
||||
last_timestamp: z.number(),
|
||||
languages: z.any(),
|
||||
});
|
||||
|
||||
type ICodeTimeProject = z.infer<typeof CodeTimeProjectSchema>;
|
||||
type ICodeTimeLanguage = z.infer<typeof CodeTimeLanguageSchema>;
|
||||
type ICodeTimeDailyEntry = z.infer<typeof CodeTimeDailyEntrySchema>;
|
||||
|
||||
export {
|
||||
CodeTimeProjectSchema,
|
||||
CodeTimeLanguageSchema,
|
||||
CodeTimeDailyEntrySchema,
|
||||
};
|
||||
|
||||
export type { ICodeTimeProject, ICodeTimeLanguage, ICodeTimeDailyEntry };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
const CodeTimeActivitiesSchema = z.object({
|
||||
data: z.array(
|
||||
z.object({
|
||||
date: z.string(),
|
||||
count: z.number(),
|
||||
level: z.number(),
|
||||
}),
|
||||
),
|
||||
firstYear: z.number(),
|
||||
});
|
||||
|
||||
const CodeTimeStatisticsSchema = z.object({
|
||||
"Most time spent": z.number(),
|
||||
"Total time spent": z.number(),
|
||||
"Average time spent": z.number(),
|
||||
"Longest streak": z.number(),
|
||||
"Current streak": z.number(),
|
||||
});
|
||||
|
||||
type ICodeTimeActivities = z.infer<typeof CodeTimeActivitiesSchema>;
|
||||
type ICodeTimeStatistics = z.infer<typeof CodeTimeStatisticsSchema>;
|
||||
|
||||
export { CodeTimeActivitiesSchema, CodeTimeStatisticsSchema };
|
||||
|
||||
export type { ICodeTimeActivities, ICodeTimeStatistics };
|
||||
@@ -1,15 +1,10 @@
|
||||
import moment from "moment";
|
||||
import PocketBase from "pocketbase";
|
||||
import puppeteer from "puppeteer-core";
|
||||
import { CodeTimeSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import {
|
||||
ICodeTimeActivities,
|
||||
ICodeTimeDailyEntry,
|
||||
ICodeTimeStatistics,
|
||||
} from "../schema";
|
||||
|
||||
export const addDays = (date: Date, days: number): Date => {
|
||||
const newDate = new Date(date.valueOf());
|
||||
newDate.setDate(newDate.getDate() + days);
|
||||
@@ -29,12 +24,12 @@ export const getDates = (startDate: Date, stopDate: Date): Date[] => {
|
||||
export const getActivities = async (
|
||||
pb: PocketBase,
|
||||
year?: number,
|
||||
): Promise<ICodeTimeActivities> => {
|
||||
): Promise<CodeTimeSchemas.ICodeTimeActivities> => {
|
||||
const yearValue = Number(year) || new Date().getFullYear();
|
||||
|
||||
const data = await pb
|
||||
.collection("code_time__daily_entries")
|
||||
.getFullList<WithPB<ICodeTimeDailyEntry>>({
|
||||
.getFullList<WithPB<CodeTimeSchemas.IDailyEntry>>({
|
||||
filter: `date >= "${yearValue}-01-01 00:00:00.000Z" && date <= "${yearValue}-12-31 23:59:59.999Z"`,
|
||||
});
|
||||
|
||||
@@ -86,7 +81,7 @@ export const getActivities = async (
|
||||
|
||||
const firstRecordEver = await pb
|
||||
.collection("code_time__daily_entries")
|
||||
.getList<WithPB<ICodeTimeDailyEntry>>(1, 1, {
|
||||
.getList<WithPB<CodeTimeSchemas.IDailyEntry>>(1, 1, {
|
||||
sort: "+date",
|
||||
});
|
||||
|
||||
@@ -98,7 +93,7 @@ export const getActivities = async (
|
||||
|
||||
export const getStatistics = async (
|
||||
pb: PocketBase,
|
||||
): Promise<ICodeTimeStatistics> => {
|
||||
): Promise<CodeTimeSchemas.ICodeTimeStatistics> => {
|
||||
const everything = await pb
|
||||
.collection("code_time__daily_entries")
|
||||
.getFullList({
|
||||
@@ -198,12 +193,12 @@ export const getStatistics = async (
|
||||
export const getLastXDays = async (
|
||||
pb: PocketBase,
|
||||
days: number,
|
||||
): Promise<WithPB<ICodeTimeDailyEntry>[]> => {
|
||||
): Promise<WithPB<CodeTimeSchemas.IDailyEntry>[]> => {
|
||||
const lastXDays = moment().subtract(days, "days").format("YYYY-MM-DD");
|
||||
|
||||
const data = await pb
|
||||
.collection("code_time__daily_entries")
|
||||
.getFullList<WithPB<ICodeTimeDailyEntry>>({
|
||||
.getFullList<WithPB<CodeTimeSchemas.IDailyEntry>>({
|
||||
filter: `date >= "${lastXDays} 00:00:00.000Z"`,
|
||||
});
|
||||
|
||||
@@ -294,7 +289,7 @@ export const getEachDay = async (
|
||||
|
||||
const data = await pb
|
||||
.collection("code_time__daily_entries")
|
||||
.getFullList<WithPB<ICodeTimeDailyEntry>>({
|
||||
.getFullList<WithPB<CodeTimeSchemas.IDailyEntry>>({
|
||||
filter: `date >= "${firstDay} 00:00:00.000Z" && date <= "${lastDay} 23:59:59.999Z"`,
|
||||
});
|
||||
|
||||
@@ -319,7 +314,7 @@ export const getUserMinutes = async (
|
||||
|
||||
const items = await pb
|
||||
.collection("code_time__daily_entries")
|
||||
.getFullList<WithPB<ICodeTimeDailyEntry>>({
|
||||
.getFullList<WithPB<CodeTimeSchemas.IDailyEntry>>({
|
||||
filter: `date >= "${minTime}"`,
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { GuitarTabsSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import {
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
|
||||
import { uploadMiddleware } from "@middlewares/uploadMiddleware";
|
||||
|
||||
import { GuitarTabsEntrySchema, GuitarTabsSidebarDataSchema } from "../schema";
|
||||
import * as entriesService from "../services/entries.service";
|
||||
|
||||
const guitarTabsEntriesRouter = express.Router();
|
||||
@@ -22,7 +22,7 @@ const getSidebarData = forgeController
|
||||
.route("GET /sidebar-data")
|
||||
.description("Get sidebar data for guitar tabs")
|
||||
.schema({
|
||||
response: GuitarTabsSidebarDataSchema,
|
||||
response: GuitarTabsSchemas.GuitarTabsSidebarDataSchema,
|
||||
})
|
||||
.callback(async ({ pb }) => await entriesService.getSidebarData(pb));
|
||||
|
||||
@@ -47,7 +47,7 @@ const getEntries = forgeController
|
||||
.optional()
|
||||
.default("newest"),
|
||||
}),
|
||||
response: PBListResultSchema(WithPBSchema(GuitarTabsEntrySchema)),
|
||||
response: PBListResultSchema(WithPBSchema(GuitarTabsSchemas.EntrySchema)),
|
||||
})
|
||||
.callback(
|
||||
async ({ pb, query }) => await entriesService.getEntries(pb, query),
|
||||
@@ -57,7 +57,7 @@ const getRandomEntry = forgeController
|
||||
.route("GET /random")
|
||||
.description("Get a random guitar tab entry")
|
||||
.schema({
|
||||
response: WithPBSchema(GuitarTabsEntrySchema),
|
||||
response: WithPBSchema(GuitarTabsSchemas.EntrySchema),
|
||||
})
|
||||
.callback(async ({ pb }) => await entriesService.getRandomEntry(pb));
|
||||
|
||||
@@ -90,12 +90,12 @@ const updateEntry = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: GuitarTabsEntrySchema.pick({
|
||||
body: GuitarTabsSchemas.EntrySchema.pick({
|
||||
name: true,
|
||||
author: true,
|
||||
type: true,
|
||||
}),
|
||||
response: WithPBSchema(GuitarTabsEntrySchema),
|
||||
response: WithPBSchema(GuitarTabsSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "guitar_tabs__entries",
|
||||
@@ -129,7 +129,7 @@ const toggleFavorite = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(GuitarTabsEntrySchema),
|
||||
response: WithPBSchema(GuitarTabsSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "guitar_tabs__entries",
|
||||
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { GuitarTabsSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { GuitarTabsGuitarWorldEntrySchema } from "../schema";
|
||||
import * as guitarWorldService from "../services/guitarWorld.service";
|
||||
|
||||
const guitarTabsGuitarWorldRouter = express.Router();
|
||||
@@ -19,7 +19,7 @@ const getTabsList = forgeController
|
||||
page: z.number().optional().default(1),
|
||||
}),
|
||||
response: z.object({
|
||||
data: z.array(GuitarTabsGuitarWorldEntrySchema),
|
||||
data: z.array(GuitarTabsSchemas.GuitarTabsGuitarWorldEntrySchema),
|
||||
totalItems: z.number(),
|
||||
perPage: z.number(),
|
||||
}),
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: guitarTabs
|
||||
* Generated at: 2025-07-09T12:50:41.283Z
|
||||
* Contains: guitar_tabs__entries, guitar_tabs__authors_aggregated
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const GuitarTabsEntrySchema = z.object({
|
||||
name: z.string(),
|
||||
type: z.enum(["fingerstyle", "singalong", ""]),
|
||||
pageCount: z.string(),
|
||||
thumbnail: z.string(),
|
||||
author: z.string(),
|
||||
pdf: z.string(),
|
||||
audio: z.string(),
|
||||
musescore: z.string(),
|
||||
isFavourite: z.boolean(),
|
||||
});
|
||||
|
||||
const GuitarTabsAuthorAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
type IGuitarTabsEntry = z.infer<typeof GuitarTabsEntrySchema>;
|
||||
type IGuitarTabsAuthorAggregated = z.infer<
|
||||
typeof GuitarTabsAuthorAggregatedSchema
|
||||
>;
|
||||
|
||||
export { GuitarTabsEntrySchema, GuitarTabsAuthorAggregatedSchema };
|
||||
|
||||
export type { IGuitarTabsEntry, IGuitarTabsAuthorAggregated };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
const GuitarTabsSidebarDataSchema = z.object({
|
||||
total: z.number(),
|
||||
favourites: z.number(),
|
||||
categories: z.object({
|
||||
fingerstyle: z.number(),
|
||||
singalong: z.number(),
|
||||
uncategorized: z.number(),
|
||||
}),
|
||||
authors: z.record(z.string(), z.number()),
|
||||
});
|
||||
|
||||
const GuitarTabsGuitarWorldEntrySchema = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
subtitle: z.string(),
|
||||
category: z.string(),
|
||||
mainArtist: z.string(),
|
||||
uploader: z.string(),
|
||||
audioUrl: z.string(),
|
||||
});
|
||||
|
||||
type IGuitarTabsSidebarData = z.infer<typeof GuitarTabsSidebarDataSchema>;
|
||||
type IGuitarTabsGuitarWorldEntry = z.infer<
|
||||
typeof GuitarTabsGuitarWorldEntrySchema
|
||||
>;
|
||||
|
||||
export { GuitarTabsSidebarDataSchema, GuitarTabsGuitarWorldEntrySchema };
|
||||
|
||||
export type { IGuitarTabsSidebarData, IGuitarTabsGuitarWorldEntry };
|
||||
@@ -3,6 +3,7 @@ import fs from "fs";
|
||||
import pdfPageCounter from "pdf-page-counter";
|
||||
import pdfThumbnail from "pdf-thumbnail";
|
||||
import PocketBase, { ListResult } from "pocketbase";
|
||||
import { GuitarTabsSchemas } from "shared";
|
||||
import { Server } from "socket.io";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
@@ -13,33 +14,27 @@ import {
|
||||
updateTaskInPool,
|
||||
} from "@middlewares/taskPoolMiddleware";
|
||||
|
||||
import {
|
||||
IGuitarTabsAuthorAggregated,
|
||||
IGuitarTabsEntry,
|
||||
IGuitarTabsSidebarData,
|
||||
} from "../schema";
|
||||
|
||||
let left = 0;
|
||||
|
||||
export const getRandomEntry = async (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<IGuitarTabsEntry>> => {
|
||||
): Promise<WithPB<GuitarTabsSchemas.IEntry>> => {
|
||||
const allScores = await pb
|
||||
.collection("guitar_tabs__entries")
|
||||
.getFullList<WithPB<IGuitarTabsEntry>>();
|
||||
.getFullList<WithPB<GuitarTabsSchemas.IEntry>>();
|
||||
|
||||
return allScores[Math.floor(Math.random() * allScores.length)];
|
||||
};
|
||||
|
||||
export const getSidebarData = async (
|
||||
pb: PocketBase,
|
||||
): Promise<IGuitarTabsSidebarData> => {
|
||||
): Promise<GuitarTabsSchemas.IGuitarTabsSidebarData> => {
|
||||
const allScores = await pb
|
||||
.collection("guitar_tabs__entries")
|
||||
.getFullList<WithPB<IGuitarTabsEntry>>();
|
||||
.getFullList<WithPB<GuitarTabsSchemas.IEntry>>();
|
||||
const allAuthors = await pb
|
||||
.collection("guitar_tabs__authors_aggregated")
|
||||
.getFullList<WithPB<IGuitarTabsAuthorAggregated>>();
|
||||
.getFullList<WithPB<GuitarTabsSchemas.IAuthorAggregated>>();
|
||||
|
||||
return {
|
||||
total: allScores.length,
|
||||
@@ -53,7 +48,7 @@ export const getSidebarData = async (
|
||||
authors: Object.fromEntries(
|
||||
allAuthors.map((author) => [author.name, author.amount]),
|
||||
),
|
||||
} satisfies IGuitarTabsSidebarData;
|
||||
} satisfies GuitarTabsSchemas.IGuitarTabsSidebarData;
|
||||
};
|
||||
|
||||
export const getEntries = (
|
||||
@@ -73,10 +68,10 @@ export const getEntries = (
|
||||
starred: boolean;
|
||||
sort: "name" | "author" | "newest" | "oldest";
|
||||
},
|
||||
): Promise<ListResult<WithPB<IGuitarTabsEntry>>> => {
|
||||
): Promise<ListResult<WithPB<GuitarTabsSchemas.IEntry>>> => {
|
||||
return pb
|
||||
.collection("guitar_tabs__entries")
|
||||
.getList<WithPB<IGuitarTabsEntry>>(page, 20, {
|
||||
.getList<WithPB<GuitarTabsSchemas.IEntry>>(page, 20, {
|
||||
filter: `(name~"${query}" || author~"${query}")
|
||||
${category ? `&& type="${category === "uncategorized" ? "" : category}"` : ""}
|
||||
${author ? `&& ${author === "[na]" ? "author = ''" : `author~"${author}"`}` : ""}
|
||||
@@ -240,7 +235,7 @@ const processFiles = async (
|
||||
|
||||
await pb
|
||||
.collection("guitar_tabs__entries")
|
||||
.create<WithPB<IGuitarTabsEntry>>(
|
||||
.create<WithPB<GuitarTabsSchemas.IEntry>>(
|
||||
{
|
||||
name,
|
||||
thumbnail: new File([thumbnailBuffer], `${decodedName}.jpeg`),
|
||||
@@ -322,13 +317,19 @@ const processFiles = async (
|
||||
export const updateEntry = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
{ name, author, type }: Pick<IGuitarTabsEntry, "name" | "author" | "type">,
|
||||
): Promise<WithPB<IGuitarTabsEntry>> =>
|
||||
pb.collection("guitar_tabs__entries").update<WithPB<IGuitarTabsEntry>>(id, {
|
||||
{
|
||||
name,
|
||||
author,
|
||||
type,
|
||||
});
|
||||
}: Pick<GuitarTabsSchemas.IEntry, "name" | "author" | "type">,
|
||||
): Promise<WithPB<GuitarTabsSchemas.IEntry>> =>
|
||||
pb
|
||||
.collection("guitar_tabs__entries")
|
||||
.update<WithPB<GuitarTabsSchemas.IEntry>>(id, {
|
||||
name,
|
||||
author,
|
||||
type,
|
||||
});
|
||||
|
||||
export const deleteEntry = async (pb: PocketBase, id: string) => {
|
||||
await pb.collection("guitar_tabs__entries").delete(id);
|
||||
@@ -337,14 +338,14 @@ export const deleteEntry = async (pb: PocketBase, id: string) => {
|
||||
export const toggleFavorite = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<IGuitarTabsEntry>> => {
|
||||
): Promise<WithPB<GuitarTabsSchemas.IEntry>> => {
|
||||
const entry = await pb
|
||||
.collection("guitar_tabs__entries")
|
||||
.getOne<WithPB<IGuitarTabsEntry>>(id);
|
||||
.getOne<WithPB<GuitarTabsSchemas.IEntry>>(id);
|
||||
|
||||
return await pb
|
||||
.collection("guitar_tabs__entries")
|
||||
.update<WithPB<IGuitarTabsEntry>>(id, {
|
||||
.update<WithPB<GuitarTabsSchemas.IEntry>>(id, {
|
||||
isFavourite: !entry.isFavourite,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import fs from "fs";
|
||||
import { JSDOM } from "jsdom";
|
||||
import PDFDocument from "pdfkit";
|
||||
import PocketBase from "pocketbase";
|
||||
import { GuitarTabsSchemas } from "shared";
|
||||
import sharp from "sharp";
|
||||
import { Server } from "socket.io";
|
||||
|
||||
@@ -12,13 +13,11 @@ import {
|
||||
updateTaskInPool,
|
||||
} from "@middlewares/taskPoolMiddleware";
|
||||
|
||||
import { IGuitarTabsEntry, IGuitarTabsGuitarWorldEntry } from "../schema";
|
||||
|
||||
export const getTabsList = async (
|
||||
cookie: string,
|
||||
page: number,
|
||||
): Promise<{
|
||||
data: IGuitarTabsGuitarWorldEntry[];
|
||||
data: GuitarTabsSchemas.IGuitarTabsGuitarWorldEntry[];
|
||||
totalItems: number;
|
||||
perPage: number;
|
||||
}> => {
|
||||
@@ -163,7 +162,7 @@ export const downloadTab = async (
|
||||
|
||||
const newEntry = await pb
|
||||
.collection("guitar_tabs__entries")
|
||||
.create<WithPB<IGuitarTabsEntry>>({
|
||||
.create<WithPB<GuitarTabsSchemas.IEntry>>({
|
||||
name,
|
||||
author: mainArtist,
|
||||
pageCount: images.length,
|
||||
|
||||
@@ -4,13 +4,13 @@ import {
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import fs from "fs";
|
||||
import { IdeaBoxSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { singleUploadMiddlewareOfKey } from "@middlewares/uploadMiddleware";
|
||||
|
||||
import { IdeaBoxContainerSchema } from "../schema";
|
||||
import * as containersService from "../services/containers.service";
|
||||
|
||||
const ideaBoxContainersRouter = express.Router();
|
||||
@@ -33,7 +33,7 @@ const getContainers = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all containers")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(IdeaBoxContainerSchema)),
|
||||
response: z.array(WithPBSchema(IdeaBoxSchemas.ContainerSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await containersService.getContainers(pb));
|
||||
|
||||
@@ -41,8 +41,8 @@ const createContainer = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new container")
|
||||
.schema({
|
||||
body: IdeaBoxContainerSchema,
|
||||
response: WithPBSchema(IdeaBoxContainerSchema),
|
||||
body: IdeaBoxSchemas.ContainerSchema,
|
||||
response: WithPBSchema(IdeaBoxSchemas.ContainerSchema),
|
||||
})
|
||||
.middlewares(singleUploadMiddlewareOfKey("cover"))
|
||||
.callback(async ({ pb, body: { name, color, icon, cover }, req }) => {
|
||||
@@ -94,7 +94,7 @@ const updateContainer = forgeController
|
||||
icon: z.string(),
|
||||
cover: z.string().optional(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxContainerSchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.ContainerSchema),
|
||||
})
|
||||
.middlewares(singleUploadMiddlewareOfKey("cover"))
|
||||
.existenceCheck("params", {
|
||||
|
||||
@@ -4,11 +4,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { IdeaBoxSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IdeaBoxFolderSchema } from "../schema";
|
||||
import * as foldersService from "../services/folders.service";
|
||||
|
||||
const ideaBoxFoldersRouter = express.Router();
|
||||
@@ -21,7 +21,7 @@ const getFolders = forgeController
|
||||
container: z.string(),
|
||||
"0": z.string(),
|
||||
}),
|
||||
response: z.array(WithPBSchema(IdeaBoxFolderSchema)),
|
||||
response: z.array(WithPBSchema(IdeaBoxSchemas.FolderSchema)),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
container: "idea_box__containers",
|
||||
@@ -53,7 +53,7 @@ const createFolder = forgeController
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxFolderSchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.FolderSchema),
|
||||
})
|
||||
.existenceCheck("body", {
|
||||
container: "idea_box__containers",
|
||||
@@ -73,7 +73,7 @@ const updateFolder = forgeController
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxFolderSchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.FolderSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "idea_box__folders",
|
||||
@@ -93,7 +93,7 @@ const moveFolder = forgeController
|
||||
query: z.object({
|
||||
target: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxFolderSchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.FolderSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "idea_box__folders",
|
||||
@@ -113,7 +113,7 @@ const removeFromFolder = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxFolderSchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.FolderSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "idea_box__folders",
|
||||
|
||||
@@ -5,11 +5,11 @@ import {
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import multer from "multer";
|
||||
import { IdeaBoxSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IIdeaBoxEntry, IdeaBoxEntrySchema } from "../schema";
|
||||
import * as ideasService from "../services/ideas.service";
|
||||
|
||||
const ideaBoxIdeasRouter = express.Router();
|
||||
@@ -28,7 +28,7 @@ const getIdeas = forgeController
|
||||
.optional()
|
||||
.transform((val) => val === "true"),
|
||||
}),
|
||||
response: z.array(WithPBSchema(IdeaBoxEntrySchema)),
|
||||
response: z.array(WithPBSchema(IdeaBoxSchemas.EntrySchema)),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
container: "idea_box__containers",
|
||||
@@ -58,7 +58,7 @@ const createIdea = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new idea")
|
||||
.schema({
|
||||
body: IdeaBoxEntrySchema.pick({
|
||||
body: IdeaBoxSchemas.EntrySchema.pick({
|
||||
type: true,
|
||||
container: true,
|
||||
folder: true,
|
||||
@@ -69,7 +69,7 @@ const createIdea = forgeController
|
||||
imageLink: z.string().optional(),
|
||||
tags: z.string().transform((val) => JSON.parse(val)),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxEntrySchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.EntrySchema),
|
||||
})
|
||||
.middlewares(multer().single("image") as any)
|
||||
.existenceCheck("body", {
|
||||
@@ -83,7 +83,7 @@ const createIdea = forgeController
|
||||
}) => {
|
||||
const { file } = req;
|
||||
|
||||
let data: Omit<IIdeaBoxEntry, "image" | "archived" | "pinned"> & {
|
||||
let data: Omit<IdeaBoxSchemas.IEntry, "image" | "archived" | "pinned"> & {
|
||||
image?: File;
|
||||
} = {
|
||||
title: "",
|
||||
@@ -141,7 +141,7 @@ const updateIdea = forgeController
|
||||
type: z.enum(["text", "link", "image"]),
|
||||
tags: z.array(z.string()).optional(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxEntrySchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "idea_box__entries",
|
||||
@@ -196,7 +196,7 @@ const pinIdea = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxEntrySchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "idea_box__entries",
|
||||
@@ -213,7 +213,7 @@ const archiveIdea = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxEntrySchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "idea_box__entries",
|
||||
@@ -233,7 +233,7 @@ const moveIdea = forgeController
|
||||
query: z.object({
|
||||
target: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxEntrySchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "idea_box__entries",
|
||||
@@ -253,7 +253,7 @@ const removeFromFolder = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxEntrySchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "idea_box__entries",
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { IdeaBoxSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IdeaBoxTagSchema } from "../schema";
|
||||
import * as tagsService from "../services/tags.service";
|
||||
|
||||
const ideaBoxTagsRouter = express.Router();
|
||||
@@ -19,7 +19,7 @@ const getTags = forgeController
|
||||
params: z.object({
|
||||
container: z.string(),
|
||||
}),
|
||||
response: z.array(WithPBSchema(IdeaBoxTagSchema)),
|
||||
response: z.array(WithPBSchema(IdeaBoxSchemas.TagSchema)),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
container: "idea_box__containers",
|
||||
@@ -33,11 +33,11 @@ const createTag = forgeController
|
||||
.route("POST /:container")
|
||||
.description("Create a new tag")
|
||||
.schema({
|
||||
body: IdeaBoxTagSchema,
|
||||
body: IdeaBoxSchemas.TagSchema,
|
||||
params: z.object({
|
||||
container: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxTagSchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.TagSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
container: "idea_box__containers",
|
||||
@@ -52,11 +52,11 @@ const updateTag = forgeController
|
||||
.route("PATCH /:id")
|
||||
.description("Update a tag")
|
||||
.schema({
|
||||
body: IdeaBoxTagSchema,
|
||||
body: IdeaBoxSchemas.TagSchema,
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(IdeaBoxTagSchema),
|
||||
response: WithPBSchema(IdeaBoxSchemas.TagSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "idea_box__tags",
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: ideaBox
|
||||
* Generated at: 2025-07-09T12:50:41.284Z
|
||||
* Contains: idea_box__containers, idea_box__entries, idea_box__folders, idea_box__tags, idea_box__tags_aggregated, idea_box__containers_aggregated
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const IdeaBoxContainerSchema = z.object({
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
name: z.string(),
|
||||
cover: z.string(),
|
||||
});
|
||||
|
||||
const IdeaBoxEntrySchema = z.object({
|
||||
type: z.enum(["text", "image", "link"]),
|
||||
image: z.string(),
|
||||
title: z.string(),
|
||||
content: z.string(),
|
||||
container: z.string(),
|
||||
folder: z.string(),
|
||||
pinned: z.boolean(),
|
||||
archived: z.boolean(),
|
||||
tags: z.any(),
|
||||
});
|
||||
|
||||
const IdeaBoxFolderSchema = z.object({
|
||||
container: z.string(),
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
icon: z.string(),
|
||||
parent: z.string(),
|
||||
});
|
||||
|
||||
const IdeaBoxTagSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
container: z.string(),
|
||||
});
|
||||
|
||||
const IdeaBoxTagAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
icon: z.string(),
|
||||
container: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
const IdeaBoxContainerAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
icon: z.string(),
|
||||
cover: z.string(),
|
||||
text_count: z.number(),
|
||||
link_count: z.number(),
|
||||
image_count: z.number(),
|
||||
});
|
||||
|
||||
type IIdeaBoxContainer = z.infer<typeof IdeaBoxContainerSchema>;
|
||||
type IIdeaBoxEntry = z.infer<typeof IdeaBoxEntrySchema>;
|
||||
type IIdeaBoxFolder = z.infer<typeof IdeaBoxFolderSchema>;
|
||||
type IIdeaBoxTag = z.infer<typeof IdeaBoxTagSchema>;
|
||||
type IIdeaBoxTagAggregated = z.infer<typeof IdeaBoxTagAggregatedSchema>;
|
||||
type IIdeaBoxContainerAggregated = z.infer<
|
||||
typeof IdeaBoxContainerAggregatedSchema
|
||||
>;
|
||||
|
||||
export {
|
||||
IdeaBoxContainerSchema,
|
||||
IdeaBoxEntrySchema,
|
||||
IdeaBoxFolderSchema,
|
||||
IdeaBoxTagSchema,
|
||||
IdeaBoxTagAggregatedSchema,
|
||||
IdeaBoxContainerAggregatedSchema,
|
||||
};
|
||||
|
||||
export type {
|
||||
IIdeaBoxContainer,
|
||||
IIdeaBoxEntry,
|
||||
IIdeaBoxFolder,
|
||||
IIdeaBoxTag,
|
||||
IIdeaBoxTagAggregated,
|
||||
IIdeaBoxContainerAggregated,
|
||||
};
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
// Add your custom schemas here. They will not be overwritten by this script.
|
||||
@@ -1,9 +1,8 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { IdeaBoxSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IIdeaBoxContainer } from "../schema";
|
||||
|
||||
export const checkContainerExists = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
@@ -15,11 +14,11 @@ export const checkContainerExists = async (
|
||||
|
||||
export const getContainers = async (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<IIdeaBoxContainer>[]> =>
|
||||
): Promise<WithPB<IdeaBoxSchemas.IContainer>[]> =>
|
||||
(
|
||||
await pb.collection("idea_box__containers_aggregated").getFullList<
|
||||
WithPB<
|
||||
IIdeaBoxContainer & {
|
||||
IdeaBoxSchemas.IContainer & {
|
||||
text_count: number;
|
||||
link_count: number;
|
||||
image_count: number;
|
||||
@@ -43,8 +42,11 @@ export const createContainer = async (
|
||||
color: string,
|
||||
icon: string,
|
||||
coverFile?: File,
|
||||
): Promise<WithPB<IIdeaBoxContainer>> => {
|
||||
const containerData: Pick<IIdeaBoxContainer, "name" | "color" | "icon"> & {
|
||||
): Promise<WithPB<IdeaBoxSchemas.IContainer>> => {
|
||||
const containerData: Pick<
|
||||
IdeaBoxSchemas.IContainer,
|
||||
"name" | "color" | "icon"
|
||||
> & {
|
||||
cover?: File | string;
|
||||
} = {
|
||||
name,
|
||||
@@ -60,7 +62,7 @@ export const createContainer = async (
|
||||
|
||||
return await pb
|
||||
.collection("idea_box__containers")
|
||||
.create<WithPB<IIdeaBoxContainer>>(containerData);
|
||||
.create<WithPB<IdeaBoxSchemas.IContainer>>(containerData);
|
||||
};
|
||||
|
||||
export const updateContainer = async (
|
||||
@@ -70,8 +72,11 @@ export const updateContainer = async (
|
||||
color: string,
|
||||
icon: string,
|
||||
coverFile?: File | "keep",
|
||||
): Promise<WithPB<IIdeaBoxContainer>> => {
|
||||
const containerData: Pick<IIdeaBoxContainer, "name" | "color" | "icon"> & {
|
||||
): Promise<WithPB<IdeaBoxSchemas.IContainer>> => {
|
||||
const containerData: Pick<
|
||||
IdeaBoxSchemas.IContainer,
|
||||
"name" | "color" | "icon"
|
||||
> & {
|
||||
cover?: File | string;
|
||||
} = {
|
||||
name,
|
||||
@@ -85,7 +90,7 @@ export const updateContainer = async (
|
||||
|
||||
return await pb
|
||||
.collection("idea_box__containers")
|
||||
.update<WithPB<IIdeaBoxContainer>>(id, containerData);
|
||||
.update<WithPB<IdeaBoxSchemas.IContainer>>(id, containerData);
|
||||
};
|
||||
|
||||
export const deleteContainer = async (pb: PocketBase, id: string) => {
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { IdeaBoxSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IIdeaBoxFolder } from "../schema";
|
||||
|
||||
export const getFolders = (
|
||||
pb: PocketBase,
|
||||
container: string,
|
||||
lastFolder: string,
|
||||
): Promise<WithPB<IIdeaBoxFolder>[]> =>
|
||||
pb.collection("idea_box__folders").getFullList<WithPB<IIdeaBoxFolder>>({
|
||||
filter: `container = "${container}" && parent = "${lastFolder}"`,
|
||||
sort: "name",
|
||||
});
|
||||
): Promise<WithPB<IdeaBoxSchemas.IFolder>[]> =>
|
||||
pb
|
||||
.collection("idea_box__folders")
|
||||
.getFullList<WithPB<IdeaBoxSchemas.IFolder>>({
|
||||
filter: `container = "${container}" && parent = "${lastFolder}"`,
|
||||
sort: "name",
|
||||
});
|
||||
|
||||
export const createFolder = async (
|
||||
pb: PocketBase,
|
||||
@@ -29,8 +30,8 @@ export const createFolder = async (
|
||||
icon: string;
|
||||
color: string;
|
||||
},
|
||||
): Promise<WithPB<IIdeaBoxFolder>> =>
|
||||
pb.collection("idea_box__folders").create<WithPB<IIdeaBoxFolder>>({
|
||||
): Promise<WithPB<IdeaBoxSchemas.IFolder>> =>
|
||||
pb.collection("idea_box__folders").create<WithPB<IdeaBoxSchemas.IFolder>>({
|
||||
name,
|
||||
container,
|
||||
parent,
|
||||
@@ -42,29 +43,35 @@ export const updateFolder = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
{ name, icon, color }: { name: string; icon: string; color: string },
|
||||
): Promise<WithPB<IIdeaBoxFolder>> =>
|
||||
pb.collection("idea_box__folders").update<WithPB<IIdeaBoxFolder>>(id, {
|
||||
name,
|
||||
icon,
|
||||
color,
|
||||
});
|
||||
): Promise<WithPB<IdeaBoxSchemas.IFolder>> =>
|
||||
pb
|
||||
.collection("idea_box__folders")
|
||||
.update<WithPB<IdeaBoxSchemas.IFolder>>(id, {
|
||||
name,
|
||||
icon,
|
||||
color,
|
||||
});
|
||||
|
||||
export const moveFolder = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
target: string,
|
||||
): Promise<WithPB<IIdeaBoxFolder>> =>
|
||||
pb.collection("idea_box__folders").update<WithPB<IIdeaBoxFolder>>(id, {
|
||||
parent: target,
|
||||
});
|
||||
): Promise<WithPB<IdeaBoxSchemas.IFolder>> =>
|
||||
pb
|
||||
.collection("idea_box__folders")
|
||||
.update<WithPB<IdeaBoxSchemas.IFolder>>(id, {
|
||||
parent: target,
|
||||
});
|
||||
|
||||
export const removeFromFolder = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<IIdeaBoxFolder>> =>
|
||||
pb.collection("idea_box__folders").update<WithPB<IIdeaBoxFolder>>(id, {
|
||||
parent: "",
|
||||
});
|
||||
): Promise<WithPB<IdeaBoxSchemas.IFolder>> =>
|
||||
pb
|
||||
.collection("idea_box__folders")
|
||||
.update<WithPB<IdeaBoxSchemas.IFolder>>(id, {
|
||||
parent: "",
|
||||
});
|
||||
|
||||
export const deleteFolder = async (pb: PocketBase, id: string) => {
|
||||
await pb.collection("idea_box__folders").delete(id);
|
||||
@@ -84,7 +91,7 @@ export const validateFolderPath = async (
|
||||
try {
|
||||
const folderEntry = await pb
|
||||
.collection("idea_box__folders")
|
||||
.getOne<WithPB<IIdeaBoxFolder>>(folder);
|
||||
.getOne<WithPB<IdeaBoxSchemas.IFolder>>(folder);
|
||||
|
||||
if (
|
||||
folderEntry.parent !== lastFolder ||
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { IdeaBoxSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IIdeaBoxEntry, IIdeaBoxFolder } from "../schema";
|
||||
|
||||
export const getIdeas = (
|
||||
pb: PocketBase,
|
||||
container: string,
|
||||
folder: string,
|
||||
archived: boolean,
|
||||
): Promise<WithPB<IIdeaBoxEntry>[]> =>
|
||||
pb.collection("idea_box__entries").getFullList<WithPB<IIdeaBoxEntry>>({
|
||||
filter: `container = "${container}" && archived = ${archived} ${
|
||||
folder ? `&& folder = "${folder}"` : "&& folder=''"
|
||||
}`,
|
||||
sort: "-pinned,-created",
|
||||
});
|
||||
): Promise<WithPB<IdeaBoxSchemas.IEntry>[]> =>
|
||||
pb
|
||||
.collection("idea_box__entries")
|
||||
.getFullList<WithPB<IdeaBoxSchemas.IEntry>>({
|
||||
filter: `container = "${container}" && archived = ${archived} ${
|
||||
folder ? `&& folder = "${folder}"` : "&& folder=''"
|
||||
}`,
|
||||
sort: "-pinned,-created",
|
||||
});
|
||||
|
||||
export const validateFolderPath = async (
|
||||
pb: PocketBase,
|
||||
@@ -32,7 +33,7 @@ export const validateFolderPath = async (
|
||||
try {
|
||||
const folderEntry = await pb
|
||||
.collection("idea_box__folders")
|
||||
.getOne<IIdeaBoxFolder>(folder);
|
||||
.getOne<IdeaBoxSchemas.IFolder>(folder);
|
||||
|
||||
if (
|
||||
folderEntry.parent !== lastFolder ||
|
||||
@@ -54,18 +55,22 @@ export const validateFolderPath = async (
|
||||
|
||||
export const createIdea = (
|
||||
pb: PocketBase,
|
||||
data: Omit<IIdeaBoxEntry, "image" | "pinned" | "archived"> & {
|
||||
data: Omit<IdeaBoxSchemas.IEntry, "image" | "pinned" | "archived"> & {
|
||||
image?: File;
|
||||
},
|
||||
): Promise<WithPB<IIdeaBoxEntry>> =>
|
||||
pb.collection("idea_box__entries").create<WithPB<IIdeaBoxEntry>>(data);
|
||||
): Promise<WithPB<IdeaBoxSchemas.IEntry>> =>
|
||||
pb
|
||||
.collection("idea_box__entries")
|
||||
.create<WithPB<IdeaBoxSchemas.IEntry>>(data);
|
||||
|
||||
export const updateIdea = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: Partial<IIdeaBoxEntry>,
|
||||
): Promise<WithPB<IIdeaBoxEntry>> =>
|
||||
pb.collection("idea_box__entries").update<WithPB<IIdeaBoxEntry>>(id, data);
|
||||
data: Partial<IdeaBoxSchemas.IEntry>,
|
||||
): Promise<WithPB<IdeaBoxSchemas.IEntry>> =>
|
||||
pb
|
||||
.collection("idea_box__entries")
|
||||
.update<WithPB<IdeaBoxSchemas.IEntry>>(id, data);
|
||||
|
||||
export const deleteIdea = async (pb: PocketBase, id: string) => {
|
||||
await pb.collection("idea_box__entries").delete(id);
|
||||
@@ -74,11 +79,11 @@ export const deleteIdea = async (pb: PocketBase, id: string) => {
|
||||
export const updatePinStatus = async (pb: PocketBase, id: string) => {
|
||||
const idea = await pb
|
||||
.collection("idea_box__entries")
|
||||
.getOne<WithPB<IIdeaBoxEntry>>(id);
|
||||
.getOne<WithPB<IdeaBoxSchemas.IEntry>>(id);
|
||||
|
||||
const entry = await pb
|
||||
.collection("idea_box__entries")
|
||||
.update<WithPB<IIdeaBoxEntry>>(id, {
|
||||
.update<WithPB<IdeaBoxSchemas.IEntry>>(id, {
|
||||
pinned: !idea.pinned,
|
||||
});
|
||||
|
||||
@@ -88,11 +93,11 @@ export const updatePinStatus = async (pb: PocketBase, id: string) => {
|
||||
export const updateArchiveStatus = async (pb: PocketBase, id: string) => {
|
||||
const idea = await pb
|
||||
.collection("idea_box__entries")
|
||||
.getOne<WithPB<IIdeaBoxEntry>>(id);
|
||||
.getOne<WithPB<IdeaBoxSchemas.IEntry>>(id);
|
||||
|
||||
const entry = await pb
|
||||
.collection("idea_box__entries")
|
||||
.update<WithPB<IIdeaBoxEntry>>(id, {
|
||||
.update<WithPB<IdeaBoxSchemas.IEntry>>(id, {
|
||||
archived: !idea.archived,
|
||||
pinned: false,
|
||||
});
|
||||
@@ -104,15 +109,15 @@ export const moveIdea = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
target: string,
|
||||
): Promise<WithPB<IIdeaBoxEntry>> =>
|
||||
pb.collection("idea_box__entries").update<WithPB<IIdeaBoxEntry>>(id, {
|
||||
): Promise<WithPB<IdeaBoxSchemas.IEntry>> =>
|
||||
pb.collection("idea_box__entries").update<WithPB<IdeaBoxSchemas.IEntry>>(id, {
|
||||
folder: target,
|
||||
});
|
||||
|
||||
export const removeFromFolder = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<IIdeaBoxEntry>> =>
|
||||
pb.collection("idea_box__entries").update<WithPB<IIdeaBoxEntry>>(id, {
|
||||
): Promise<WithPB<IdeaBoxSchemas.IEntry>> =>
|
||||
pb.collection("idea_box__entries").update<WithPB<IdeaBoxSchemas.IEntry>>(id, {
|
||||
folder: "",
|
||||
});
|
||||
|
||||
@@ -4,11 +4,10 @@ import { clientError } from "@functions/response";
|
||||
import { Request, Response } from "express";
|
||||
import ogs from "open-graph-scraper";
|
||||
import PocketBase from "pocketbase";
|
||||
import { IdeaBoxSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IIdeaBoxContainer, IIdeaBoxEntry, IIdeaBoxFolder } from "../schema";
|
||||
|
||||
const OGCache = new Map<string, any>();
|
||||
|
||||
export const getPath = async (
|
||||
@@ -18,8 +17,8 @@ export const getPath = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
): Promise<{
|
||||
container: WithPB<IIdeaBoxContainer>;
|
||||
path: WithPB<IIdeaBoxFolder>[];
|
||||
container: WithPB<IdeaBoxSchemas.IContainer>;
|
||||
path: WithPB<IdeaBoxSchemas.IFolder>[];
|
||||
} | null> => {
|
||||
const containerExists = await checkExistence(
|
||||
req,
|
||||
@@ -32,14 +31,14 @@ export const getPath = async (
|
||||
|
||||
const containerEntry = await pb
|
||||
.collection("idea_box__containers")
|
||||
.getOne<WithPB<IIdeaBoxContainer>>(container);
|
||||
.getOne<WithPB<IdeaBoxSchemas.IContainer>>(container);
|
||||
|
||||
containerEntry.cover = pb.files
|
||||
.getURL(containerEntry, containerEntry.cover)
|
||||
.replace(`${pb.baseURL}/api/files`, "");
|
||||
|
||||
let lastFolder = "";
|
||||
const fullPath: WithPB<IIdeaBoxFolder>[] = [];
|
||||
const fullPath: WithPB<IdeaBoxSchemas.IFolder>[] = [];
|
||||
|
||||
for (const folder of path) {
|
||||
if (!(await checkExistence(req, res, "idea_box__folders", folder))) {
|
||||
@@ -48,7 +47,7 @@ export const getPath = async (
|
||||
|
||||
const folderEntry = await pb
|
||||
.collection("idea_box__folders")
|
||||
.getOne<WithPB<IIdeaBoxFolder>>(folder);
|
||||
.getOne<WithPB<IdeaBoxSchemas.IFolder>>(folder);
|
||||
|
||||
if (
|
||||
folderEntry.parent !== lastFolder ||
|
||||
@@ -98,7 +97,7 @@ export const checkValid = async (
|
||||
|
||||
const folderEntry = await pb
|
||||
.collection("idea_box__folders")
|
||||
.getOne<IIdeaBoxFolder>(folder);
|
||||
.getOne<IdeaBoxSchemas.IFolder>(folder);
|
||||
|
||||
if (
|
||||
folderEntry.parent !== lastFolder ||
|
||||
@@ -120,7 +119,7 @@ export const getOgData = async (
|
||||
): Promise<any | null> => {
|
||||
const data = await pb
|
||||
.collection("idea_box__entries")
|
||||
.getOne<IIdeaBoxEntry>(id);
|
||||
.getOne<IdeaBoxSchemas.IEntry>(id);
|
||||
|
||||
if (data.type !== "link") {
|
||||
throw new ClientError(
|
||||
@@ -159,26 +158,26 @@ async function recursivelySearchFolder(
|
||||
parents: string,
|
||||
pb: PocketBase,
|
||||
): Promise<
|
||||
(Omit<WithPB<IIdeaBoxEntry>, "folder"> & {
|
||||
folder: WithPB<IIdeaBoxFolder>;
|
||||
(Omit<WithPB<IdeaBoxSchemas.IEntry>, "folder"> & {
|
||||
folder: WithPB<IdeaBoxSchemas.IFolder>;
|
||||
expand?: {
|
||||
folder: WithPB<IIdeaBoxFolder>;
|
||||
folder: WithPB<IdeaBoxSchemas.IFolder>;
|
||||
};
|
||||
fullPath: string;
|
||||
})[]
|
||||
> {
|
||||
const folderInsideFolder = await pb
|
||||
.collection("idea_box__folders")
|
||||
.getFullList<WithPB<IIdeaBoxFolder>>({
|
||||
.getFullList<WithPB<IdeaBoxSchemas.IFolder>>({
|
||||
filter: `parent = "${folderId}"`,
|
||||
});
|
||||
|
||||
const allResults = (
|
||||
await pb.collection("idea_box__entries").getFullList<
|
||||
Omit<WithPB<IIdeaBoxEntry>, "folder"> & {
|
||||
folder: WithPB<IIdeaBoxFolder>;
|
||||
Omit<WithPB<IdeaBoxSchemas.IEntry>, "folder"> & {
|
||||
folder: WithPB<IdeaBoxSchemas.IFolder>;
|
||||
expand?: {
|
||||
folder: WithPB<IIdeaBoxFolder>;
|
||||
folder: WithPB<IdeaBoxSchemas.IFolder>;
|
||||
};
|
||||
}
|
||||
>({
|
||||
@@ -225,10 +224,10 @@ export const search = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
): Promise<
|
||||
| (Omit<WithPB<IIdeaBoxEntry>, "folder"> & {
|
||||
folder: WithPB<IIdeaBoxFolder>;
|
||||
| (Omit<WithPB<IdeaBoxSchemas.IEntry>, "folder"> & {
|
||||
folder: WithPB<IdeaBoxSchemas.IFolder>;
|
||||
expand?: {
|
||||
folder: WithPB<IIdeaBoxFolder>;
|
||||
folder: WithPB<IdeaBoxSchemas.IFolder>;
|
||||
};
|
||||
fullPath: string;
|
||||
})[]
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { IdeaBoxSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IIdeaBoxTag } from "../schema";
|
||||
|
||||
export const getTags = (
|
||||
pb: PocketBase,
|
||||
container: string,
|
||||
): Promise<WithPB<IIdeaBoxTag>[]> =>
|
||||
pb.collection("idea_box__tags_aggregated").getFullList<WithPB<IIdeaBoxTag>>({
|
||||
filter: `container = "${container}"`,
|
||||
});
|
||||
): Promise<WithPB<IdeaBoxSchemas.ITag>[]> =>
|
||||
pb
|
||||
.collection("idea_box__tags_aggregated")
|
||||
.getFullList<WithPB<IdeaBoxSchemas.ITag>>({
|
||||
filter: `container = "${container}"`,
|
||||
});
|
||||
|
||||
export const createTag = (
|
||||
pb: PocketBase,
|
||||
@@ -25,7 +26,7 @@ export const createTag = (
|
||||
color: string;
|
||||
},
|
||||
) =>
|
||||
pb.collection("idea_box__tags").create<WithPB<IIdeaBoxTag>>({
|
||||
pb.collection("idea_box__tags").create<WithPB<IdeaBoxSchemas.ITag>>({
|
||||
name,
|
||||
icon,
|
||||
color,
|
||||
@@ -44,7 +45,7 @@ export const updateTag = (
|
||||
icon: string;
|
||||
color: string;
|
||||
},
|
||||
): Promise<WithPB<IIdeaBoxTag>> =>
|
||||
): Promise<WithPB<IdeaBoxSchemas.ITag>> =>
|
||||
pb.collection("idea_box__tags").update(id, {
|
||||
name,
|
||||
icon,
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { MomentVaultSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import {
|
||||
@@ -13,7 +14,6 @@ import {
|
||||
|
||||
import { uploadMiddleware } from "@middlewares/uploadMiddleware";
|
||||
|
||||
import { MomentVaultEntrySchema } from "../schema";
|
||||
import * as EntriesServices from "../services/entries.service";
|
||||
|
||||
const momentVaultEntriesRouter = express.Router();
|
||||
@@ -28,7 +28,7 @@ const getEntries = forgeController
|
||||
.optional()
|
||||
.transform((val) => parseInt(val ?? "1", 10) || 1),
|
||||
}),
|
||||
response: PBListResultSchema(WithPBSchema(MomentVaultEntrySchema)),
|
||||
response: PBListResultSchema(WithPBSchema(MomentVaultSchemas.EntrySchema)),
|
||||
})
|
||||
.callback(
|
||||
async ({ pb, query }) =>
|
||||
@@ -44,7 +44,7 @@ const createEntry = forgeController
|
||||
content: z.string().optional(),
|
||||
transcription: z.string().optional(),
|
||||
}),
|
||||
response: WithPBSchema(MomentVaultEntrySchema),
|
||||
response: WithPBSchema(MomentVaultSchemas.EntrySchema),
|
||||
})
|
||||
.middlewares(uploadMiddleware)
|
||||
.callback(async ({ pb, body: { type, content, transcription }, req }) => {
|
||||
@@ -101,7 +101,7 @@ const updateEntry = forgeController
|
||||
body: z.object({
|
||||
content: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(MomentVaultEntrySchema),
|
||||
response: WithPBSchema(MomentVaultSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "moment_vault__entries",
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: momentVault
|
||||
* Generated at: 2025-07-09T12:50:41.283Z
|
||||
* Contains: moment_vault__entries
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const MomentVaultEntrySchema = z.object({
|
||||
type: z.enum(["text", "audio", "video", "photos", ""]),
|
||||
file: z.array(z.string()),
|
||||
content: z.string(),
|
||||
transcription: z.string(),
|
||||
});
|
||||
|
||||
type IMomentVaultEntry = z.infer<typeof MomentVaultEntrySchema>;
|
||||
|
||||
export { MomentVaultEntrySchema };
|
||||
|
||||
export type { IMomentVaultEntry };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
// Add your custom schemas here. They will not be overwritten by this script.
|
||||
@@ -1,28 +1,28 @@
|
||||
import fs from "fs";
|
||||
import { ListResult } from "pocketbase";
|
||||
import Client from "pocketbase";
|
||||
import { MomentVaultSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IMomentVaultEntry } from "../schema";
|
||||
import { convertToMp3 } from "../utils/convertToMP3";
|
||||
|
||||
export async function getEntryById(
|
||||
pb: Client,
|
||||
id: string,
|
||||
): Promise<IMomentVaultEntry> {
|
||||
): Promise<MomentVaultSchemas.IEntry> {
|
||||
return await pb
|
||||
.collection("moment_vault__entries")
|
||||
.getOne<IMomentVaultEntry>(id);
|
||||
.getOne<MomentVaultSchemas.IEntry>(id);
|
||||
}
|
||||
|
||||
export const getAllEntries = async (
|
||||
pb: Client,
|
||||
page: number = 1,
|
||||
): Promise<ListResult<WithPB<IMomentVaultEntry>>> => {
|
||||
): Promise<ListResult<WithPB<MomentVaultSchemas.IEntry>>> => {
|
||||
const entries = await pb
|
||||
.collection("moment_vault__entries")
|
||||
.getList<WithPB<IMomentVaultEntry>>(page, 10, {
|
||||
.getList<WithPB<MomentVaultSchemas.IEntry>>(page, 10, {
|
||||
sort: "-created",
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ export const createAudioEntry = async (
|
||||
file: Express.Multer.File;
|
||||
transcription?: string;
|
||||
},
|
||||
): Promise<WithPB<IMomentVaultEntry>> => {
|
||||
): Promise<WithPB<MomentVaultSchemas.IEntry>> => {
|
||||
if (file.mimetype !== "audio/mp3") {
|
||||
file.path = await convertToMp3(file.path);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export const createAudioEntry = async (
|
||||
|
||||
const entry = await pb
|
||||
.collection("moment_vault__entries")
|
||||
.create<WithPB<IMomentVaultEntry>>({
|
||||
.create<WithPB<MomentVaultSchemas.IEntry>>({
|
||||
type: "audio",
|
||||
file: new File([fileBuffer], file.path.split("/").pop() || "audio.mp3"),
|
||||
transcription,
|
||||
@@ -75,10 +75,10 @@ export const createAudioEntry = async (
|
||||
export const createTextEntry = async (
|
||||
pb: Client,
|
||||
content: string,
|
||||
): Promise<WithPB<IMomentVaultEntry>> => {
|
||||
): Promise<WithPB<MomentVaultSchemas.IEntry>> => {
|
||||
return await pb
|
||||
.collection("moment_vault__entries")
|
||||
.create<WithPB<IMomentVaultEntry>>({
|
||||
.create<WithPB<MomentVaultSchemas.IEntry>>({
|
||||
type: "text",
|
||||
content,
|
||||
});
|
||||
@@ -87,7 +87,7 @@ export const createTextEntry = async (
|
||||
export const createPhotosEntry = async (
|
||||
pb: Client,
|
||||
files: Express.Multer.File[],
|
||||
): Promise<WithPB<IMomentVaultEntry>> => {
|
||||
): Promise<WithPB<MomentVaultSchemas.IEntry>> => {
|
||||
const allImages = files.map((file) => {
|
||||
const fileBuffer = fs.readFileSync(file.path);
|
||||
return new File([fileBuffer], file.path.split("/").pop() || "photo.jpg");
|
||||
@@ -95,7 +95,7 @@ export const createPhotosEntry = async (
|
||||
|
||||
const entry = await pb
|
||||
.collection("moment_vault__entries")
|
||||
.create<WithPB<IMomentVaultEntry>>({
|
||||
.create<WithPB<MomentVaultSchemas.IEntry>>({
|
||||
type: "photos",
|
||||
file: allImages,
|
||||
});
|
||||
@@ -117,10 +117,10 @@ export const updateEntry = async (
|
||||
pb: Client,
|
||||
id: string,
|
||||
content: string,
|
||||
): Promise<WithPB<IMomentVaultEntry>> =>
|
||||
): Promise<WithPB<MomentVaultSchemas.IEntry>> =>
|
||||
await pb
|
||||
.collection("moment_vault__entries")
|
||||
.update<WithPB<IMomentVaultEntry>>(id, {
|
||||
.update<WithPB<MomentVaultSchemas.IEntry>>(id, {
|
||||
content,
|
||||
});
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import fs from "fs";
|
||||
import Groq from "groq-sdk";
|
||||
import PocketBase from "pocketbase";
|
||||
import request from "request";
|
||||
|
||||
import { IMomentVaultEntry } from "../schema";
|
||||
import { MomentVaultSchemas } from "shared";
|
||||
|
||||
const getTranscription = async (
|
||||
filePath: string,
|
||||
@@ -35,7 +34,7 @@ export const transcribeExisted = async (
|
||||
|
||||
const entry = await pb
|
||||
.collection("moment_vault__entries")
|
||||
.getOne<IMomentVaultEntry>(id);
|
||||
.getOne<MomentVaultSchemas.IEntry>(id);
|
||||
|
||||
if (!entry.file) {
|
||||
throw new ClientError("No audio file found in entry");
|
||||
@@ -61,9 +60,11 @@ export const transcribeExisted = async (
|
||||
throw new Error("Transcription failed");
|
||||
}
|
||||
|
||||
await pb.collection("moment_vault__entries").update<IMomentVaultEntry>(id, {
|
||||
transcription: response,
|
||||
});
|
||||
await pb
|
||||
.collection("moment_vault__entries")
|
||||
.update<MomentVaultSchemas.IEntry>(id, {
|
||||
transcription: response,
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (err) {
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { MoviesSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { MoviesEntrySchema } from "../schema";
|
||||
import * as entriesService from "../services/entries.service";
|
||||
|
||||
const moviesEntriesRouter = express.Router();
|
||||
@@ -24,7 +24,7 @@ const getAllEntries = forgeController
|
||||
.transform((val) => (val === "true" ? true : false)),
|
||||
}),
|
||||
response: z.object({
|
||||
entries: z.array(WithPBSchema(MoviesEntrySchema)),
|
||||
entries: z.array(WithPBSchema(MoviesSchemas.EntrySchema)),
|
||||
total: z.number(),
|
||||
}),
|
||||
})
|
||||
@@ -39,7 +39,7 @@ const createEntryFromTMDB = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(MoviesEntrySchema),
|
||||
response: WithPBSchema(MoviesSchemas.EntrySchema),
|
||||
})
|
||||
.callback(({ pb, params: { id } }) =>
|
||||
entriesService.createEntryFromTMDB(pb, id),
|
||||
@@ -68,7 +68,7 @@ const toggleWatchStatus = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(MoviesEntrySchema),
|
||||
response: WithPBSchema(MoviesSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "movies__entries",
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { MoviesSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { MoviesEntrySchema } from "../schema";
|
||||
import * as TicketService from "../services/ticket.service";
|
||||
|
||||
const moviesTicketRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const updateTicket = forgeController
|
||||
.route("POST /")
|
||||
.description("Update ticket information for a movie entry")
|
||||
.schema({
|
||||
body: MoviesEntrySchema.pick({
|
||||
body: MoviesSchemas.EntrySchema.pick({
|
||||
ticket_number: true,
|
||||
theatre_number: true,
|
||||
theatre_seat: true,
|
||||
@@ -34,7 +34,7 @@ const updateTicket = forgeController
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
response: WithPBSchema(MoviesEntrySchema),
|
||||
response: WithPBSchema(MoviesSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("body", {
|
||||
entry_id: "movies__entries",
|
||||
@@ -48,7 +48,7 @@ const updateTicketPatch = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: MoviesEntrySchema.pick({
|
||||
body: MoviesSchemas.EntrySchema.pick({
|
||||
ticket_number: true,
|
||||
theatre_number: true,
|
||||
theatre_seat: true,
|
||||
@@ -65,7 +65,7 @@ const updateTicketPatch = forgeController
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
response: WithPBSchema(MoviesEntrySchema),
|
||||
response: WithPBSchema(MoviesSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "movies__entries",
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: movies
|
||||
* Generated at: 2025-07-09T12:50:41.284Z
|
||||
* Contains: movies__entries
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const MoviesEntrySchema = z.object({
|
||||
tmdb_id: z.number(),
|
||||
title: z.string(),
|
||||
original_title: z.string(),
|
||||
poster: z.string(),
|
||||
genres: z.any(),
|
||||
duration: z.number(),
|
||||
overview: z.string(),
|
||||
countries: z.any(),
|
||||
language: z.string(),
|
||||
release_date: z.string(),
|
||||
watch_date: z.string(),
|
||||
ticket_number: z.string(),
|
||||
theatre_seat: z.string(),
|
||||
theatre_showtime: z.string(),
|
||||
theatre_location: z.string(),
|
||||
theatre_number: z.string(),
|
||||
is_watched: z.boolean(),
|
||||
});
|
||||
|
||||
type IMoviesEntry = z.infer<typeof MoviesEntrySchema>;
|
||||
|
||||
export { MoviesEntrySchema };
|
||||
|
||||
export type { IMoviesEntry };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
// Add your custom schemas here. They will not be overwritten by this script.
|
||||
@@ -1,25 +1,26 @@
|
||||
import { getAPIKey } from "@functions/getAPIKey";
|
||||
import PocketBase from "pocketbase";
|
||||
import { MoviesSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IMoviesEntry } from "../schema";
|
||||
|
||||
export const getAllEntries = async (
|
||||
pb: PocketBase,
|
||||
watched: boolean,
|
||||
): Promise<{
|
||||
entries: WithPB<IMoviesEntry>[];
|
||||
entries: WithPB<MoviesSchemas.IEntry>[];
|
||||
total: number;
|
||||
}> => {
|
||||
const entries = await pb
|
||||
.collection("movies__entries")
|
||||
.getFullList<WithPB<IMoviesEntry>>({
|
||||
.getFullList<WithPB<MoviesSchemas.IEntry>>({
|
||||
filter: `is_watched = ${watched}`,
|
||||
});
|
||||
|
||||
const total = (
|
||||
await pb.collection("movies__entries").getList<WithPB<IMoviesEntry>>(1, 1)
|
||||
await pb
|
||||
.collection("movies__entries")
|
||||
.getList<WithPB<MoviesSchemas.IEntry>>(1, 1)
|
||||
).totalItems;
|
||||
|
||||
return {
|
||||
@@ -44,7 +45,7 @@ export const getAllEntries = async (
|
||||
export const createEntryFromTMDB = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<IMoviesEntry>> => {
|
||||
): Promise<WithPB<MoviesSchemas.IEntry>> => {
|
||||
const apiKey = await getAPIKey("tmdb", pb);
|
||||
if (!apiKey) {
|
||||
throw new Error("API key not found");
|
||||
@@ -52,7 +53,7 @@ export const createEntryFromTMDB = async (
|
||||
|
||||
const existedData = await pb
|
||||
.collection("movies__entries")
|
||||
.getFirstListItem<WithPB<IMoviesEntry>>(`tmdb_id = ${id}`)
|
||||
.getFirstListItem<WithPB<MoviesSchemas.IEntry>>(`tmdb_id = ${id}`)
|
||||
.catch(() => null);
|
||||
|
||||
if (existedData) {
|
||||
@@ -84,7 +85,7 @@ export const createEntryFromTMDB = async (
|
||||
|
||||
return await pb
|
||||
.collection("movies__entries")
|
||||
.create<WithPB<IMoviesEntry>>(entryData);
|
||||
.create<WithPB<MoviesSchemas.IEntry>>(entryData);
|
||||
};
|
||||
|
||||
export const deleteEntry = async (
|
||||
@@ -97,14 +98,14 @@ export const deleteEntry = async (
|
||||
export const toggleWatchStatus = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<IMoviesEntry>> => {
|
||||
): Promise<WithPB<MoviesSchemas.IEntry>> => {
|
||||
const entry = await pb
|
||||
.collection("movies__entries")
|
||||
.getOne<WithPB<IMoviesEntry>>(id);
|
||||
.getOne<WithPB<MoviesSchemas.IEntry>>(id);
|
||||
|
||||
const updatedEntry = await pb
|
||||
.collection("movies__entries")
|
||||
.update<WithPB<IMoviesEntry>>(id, {
|
||||
.update<WithPB<MoviesSchemas.IEntry>>(id, {
|
||||
is_watched: !entry.is_watched,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { MoviesSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IMoviesEntry } from "../schema";
|
||||
|
||||
export const updateTicket = (
|
||||
pb: PocketBase,
|
||||
ticketData: Pick<
|
||||
IMoviesEntry,
|
||||
MoviesSchemas.IEntry,
|
||||
"ticket_number" | "theatre_number" | "theatre_seat" | "theatre_showtime"
|
||||
> & {
|
||||
entry_id: string;
|
||||
@@ -22,24 +21,26 @@ export const updateTicket = (
|
||||
};
|
||||
};
|
||||
},
|
||||
): Promise<WithPB<IMoviesEntry>> => {
|
||||
): Promise<WithPB<MoviesSchemas.IEntry>> => {
|
||||
(ticketData as any).theatre_location =
|
||||
ticketData.theatre_location.displayName.text;
|
||||
|
||||
return pb
|
||||
.collection("movies__entries")
|
||||
.update<WithPB<IMoviesEntry>>(ticketData.entry_id, ticketData);
|
||||
.update<WithPB<MoviesSchemas.IEntry>>(ticketData.entry_id, ticketData);
|
||||
};
|
||||
|
||||
export const clearTicket = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<void> => {
|
||||
await pb.collection("movies__entries").update<WithPB<IMoviesEntry>>(id, {
|
||||
ticket_number: "",
|
||||
theatre_location: "",
|
||||
theatre_number: "",
|
||||
theatre_seat: "",
|
||||
theatre_showtime: "",
|
||||
});
|
||||
await pb
|
||||
.collection("movies__entries")
|
||||
.update<WithPB<MoviesSchemas.IEntry>>(id, {
|
||||
ticket_number: "",
|
||||
theatre_location: "",
|
||||
theatre_number: "",
|
||||
theatre_seat: "",
|
||||
theatre_showtime: "",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { getAPIKey } from "@functions/getAPIKey";
|
||||
import Pocketbase from "pocketbase";
|
||||
import { MoviesSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IMoviesEntry } from "../schema";
|
||||
|
||||
export const searchMovies = async (pb: Pocketbase, q: string, page: number) => {
|
||||
const apiKey = await getAPIKey("tmdb", pb);
|
||||
|
||||
@@ -24,7 +23,7 @@ export const searchMovies = async (pb: Pocketbase, q: string, page: number) => {
|
||||
|
||||
const allIds = await pb
|
||||
.collection("movies__entries")
|
||||
.getFullList<WithPB<IMoviesEntry>>({
|
||||
.getFullList<WithPB<MoviesSchemas.IEntry>>({
|
||||
filter: response.results
|
||||
.map((entry: { id: number }) => `tmdb_id = ${entry.id}`)
|
||||
.join(" || "),
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { MusicSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { MusicEntrySchema } from "../schema";
|
||||
import * as EntriesService from "../services/entries.service";
|
||||
|
||||
const musicEntriesRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const getAllEntries = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all music entries")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(MusicEntrySchema)),
|
||||
response: z.array(WithPBSchema(MusicSchemas.EntrySchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await EntriesService.getAllEntries(pb));
|
||||
|
||||
@@ -27,8 +27,8 @@ const updateEntry = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: MusicEntrySchema.pick({ name: true, author: true }),
|
||||
response: WithPBSchema(MusicEntrySchema),
|
||||
body: MusicSchemas.EntrySchema.pick({ name: true, author: true }),
|
||||
response: WithPBSchema(MusicSchemas.EntrySchema),
|
||||
})
|
||||
.callback(
|
||||
async ({ pb, params: { id }, body }) =>
|
||||
@@ -59,7 +59,7 @@ const toggleFavorite = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(MusicEntrySchema),
|
||||
response: WithPBSchema(MusicSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "music__entries",
|
||||
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { MusicSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { YoutubeDataSchema } from "../schema";
|
||||
import * as YoutubeService from "../services/youtube.service";
|
||||
|
||||
const musicYoutubeRouter = express.Router();
|
||||
@@ -17,7 +17,7 @@ const getVideoInfo = forgeController
|
||||
params: z.object({
|
||||
id: z.string().regex(/^[a-zA-Z0-9_-]{11}$/, "Invalid YouTube video ID"),
|
||||
}),
|
||||
response: YoutubeDataSchema,
|
||||
response: MusicSchemas.YoutubeDataSchema,
|
||||
})
|
||||
.callback(
|
||||
async ({ params: { id } }) => await YoutubeService.getVideoInfo(id),
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: music
|
||||
* Generated at: 2025-07-09T12:50:41.281Z
|
||||
* Contains: music__entries
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const MusicEntrySchema = z.object({
|
||||
name: z.string(),
|
||||
duration: z.string(),
|
||||
author: z.string(),
|
||||
file: z.string(),
|
||||
is_favourite: z.boolean(),
|
||||
});
|
||||
|
||||
type IMusicEntry = z.infer<typeof MusicEntrySchema>;
|
||||
|
||||
export { MusicEntrySchema };
|
||||
|
||||
export type { IMusicEntry };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
const YoutubeDataSchema = z.object({
|
||||
title: z.string(),
|
||||
uploadDate: z.string(),
|
||||
uploader: z.string(),
|
||||
uploaderUrl: z.string().optional(),
|
||||
duration: z.string(),
|
||||
viewCount: z.number(),
|
||||
likeCount: z.number(),
|
||||
thumbnail: z.string(),
|
||||
});
|
||||
|
||||
type IYoutubeData = z.infer<typeof YoutubeDataSchema>;
|
||||
|
||||
export { YoutubeDataSchema };
|
||||
|
||||
export type { IYoutubeData };
|
||||
@@ -1,9 +1,8 @@
|
||||
import Pocketbase from "pocketbase";
|
||||
import { MusicSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IMusicEntry } from "../schema";
|
||||
|
||||
let importProgress: "in_progress" | "completed" | "failed" | "empty" = "empty";
|
||||
|
||||
export const getImportProgress = ():
|
||||
@@ -22,17 +21,21 @@ export const setImportProgress = (
|
||||
|
||||
export const getAllEntries = async (
|
||||
pb: Pocketbase,
|
||||
): Promise<WithPB<IMusicEntry>[]> =>
|
||||
await pb.collection("music__entries").getFullList<WithPB<IMusicEntry>>({
|
||||
sort: "-is_favourite, name",
|
||||
});
|
||||
): Promise<WithPB<MusicSchemas.IEntry>[]> =>
|
||||
await pb
|
||||
.collection("music__entries")
|
||||
.getFullList<WithPB<MusicSchemas.IEntry>>({
|
||||
sort: "-is_favourite, name",
|
||||
});
|
||||
|
||||
export const updateEntry = async (
|
||||
pb: Pocketbase,
|
||||
id: string,
|
||||
data: { name: string; author: string },
|
||||
): Promise<WithPB<IMusicEntry>> =>
|
||||
await pb.collection("music__entries").update<WithPB<IMusicEntry>>(id, data);
|
||||
): Promise<WithPB<MusicSchemas.IEntry>> =>
|
||||
await pb
|
||||
.collection("music__entries")
|
||||
.update<WithPB<MusicSchemas.IEntry>>(id, data);
|
||||
|
||||
export const deleteEntry = async (pb: Pocketbase, id: string) => {
|
||||
await pb.collection("music__entries").delete(id);
|
||||
@@ -41,7 +44,7 @@ export const deleteEntry = async (pb: Pocketbase, id: string) => {
|
||||
export const toggleFavorite = async (
|
||||
pb: Pocketbase,
|
||||
id: string,
|
||||
): Promise<WithPB<IMusicEntry>> => {
|
||||
): Promise<WithPB<MusicSchemas.IEntry>> => {
|
||||
const entry = await pb.collection("music__entries").getOne(id);
|
||||
return await pb.collection("music__entries").update(id, {
|
||||
is_favourite: !entry.is_favourite,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { exec } from "child_process";
|
||||
import { readFileSync, readdirSync, unlinkSync } from "fs";
|
||||
import Pocketbase from "pocketbase";
|
||||
import { MusicSchemas } from "shared";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
import { IYoutubeData } from "../schema";
|
||||
|
||||
let downloadStatus: "empty" | "in_progress" | "completed" | "failed" = "empty";
|
||||
|
||||
export const getDownloadStatus = () => ({
|
||||
@@ -17,7 +16,9 @@ export const setDownloadStatus = (
|
||||
downloadStatus = status;
|
||||
};
|
||||
|
||||
export const getVideoInfo = (videoId: string): Promise<IYoutubeData> => {
|
||||
export const getVideoInfo = (
|
||||
videoId: string,
|
||||
): Promise<MusicSchemas.IYoutubeData> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(
|
||||
`${process.cwd()}/src/core/bin/yt-dlp --skip-download --print "title,upload_date,uploader,duration,view_count,like_count,thumbnail" "https://www.youtube.com/watch?v=${videoId}"`,
|
||||
@@ -37,7 +38,7 @@ export const getVideoInfo = (videoId: string): Promise<IYoutubeData> => {
|
||||
thumbnail,
|
||||
] = stdout.split("\n");
|
||||
|
||||
const response: IYoutubeData = {
|
||||
const response: MusicSchemas.IYoutubeData = {
|
||||
title,
|
||||
uploadDate,
|
||||
uploader,
|
||||
|
||||
@@ -3,12 +3,12 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { PasswordsSchemas } from "shared";
|
||||
import { v4 } from "uuid";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { PasswordsEntrySchema } from "../schema";
|
||||
import * as EntriesService from "../services/entries.service";
|
||||
|
||||
const passwordsEntriesRouter = express.Router();
|
||||
@@ -31,7 +31,7 @@ const getAllEntries = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all password entries")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(PasswordsEntrySchema)),
|
||||
response: z.array(WithPBSchema(PasswordsSchemas.EntrySchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await EntriesService.getAllEntries(pb));
|
||||
|
||||
@@ -39,12 +39,12 @@ const createEntry = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new password entry")
|
||||
.schema({
|
||||
body: PasswordsEntrySchema.omit({
|
||||
body: PasswordsSchemas.EntrySchema.omit({
|
||||
pinned: true,
|
||||
}).extend({
|
||||
master: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(PasswordsEntrySchema),
|
||||
response: WithPBSchema(PasswordsSchemas.EntrySchema),
|
||||
})
|
||||
.callback(
|
||||
async ({ pb, body }) =>
|
||||
@@ -59,12 +59,12 @@ const updateEntry = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: PasswordsEntrySchema.omit({
|
||||
body: PasswordsSchemas.EntrySchema.omit({
|
||||
pinned: true,
|
||||
}).extend({
|
||||
master: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(PasswordsEntrySchema),
|
||||
response: WithPBSchema(PasswordsSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "passwords__entries",
|
||||
@@ -118,7 +118,7 @@ const togglePin = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(PasswordsEntrySchema),
|
||||
response: WithPBSchema(PasswordsSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "passwords__entries",
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: passwords
|
||||
* Generated at: 2025-07-09T12:50:41.283Z
|
||||
* Contains: passwords__entries
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const PasswordsEntrySchema = z.object({
|
||||
name: z.string(),
|
||||
website: z.string(),
|
||||
username: z.string(),
|
||||
password: z.string(),
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
pinned: z.boolean(),
|
||||
});
|
||||
|
||||
type IPasswordsEntry = z.infer<typeof PasswordsEntrySchema>;
|
||||
|
||||
export { PasswordsEntrySchema };
|
||||
|
||||
export type { IPasswordsEntry };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
// Add your custom schemas here. They will not be overwritten by this script.
|
||||
@@ -1,17 +1,17 @@
|
||||
import { decrypt, decrypt2, encrypt, encrypt2 } from "@functions/encryption";
|
||||
import PocketBase from "pocketbase";
|
||||
import { PasswordsSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IPasswordsEntry } from "../schema";
|
||||
import { getDecryptedMaster } from "./master.service";
|
||||
|
||||
export const getAllEntries = async (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<IPasswordsEntry>[]> =>
|
||||
): Promise<WithPB<PasswordsSchemas.IEntry>[]> =>
|
||||
await pb
|
||||
.collection("passwords__entries")
|
||||
.getFullList<WithPB<IPasswordsEntry>>({
|
||||
.getFullList<WithPB<PasswordsSchemas.IEntry>>({
|
||||
sort: "-pinned, name",
|
||||
});
|
||||
|
||||
@@ -25,9 +25,9 @@ export const createEntry = async (
|
||||
username,
|
||||
password,
|
||||
master,
|
||||
}: Omit<IPasswordsEntry, "decrypted" | "pinned"> & { master: string },
|
||||
}: Omit<PasswordsSchemas.IEntry, "decrypted" | "pinned"> & { master: string },
|
||||
challenge: string,
|
||||
): Promise<WithPB<IPasswordsEntry>> => {
|
||||
): Promise<WithPB<PasswordsSchemas.IEntry>> => {
|
||||
const decryptedMaster = await getDecryptedMaster(pb, master, challenge);
|
||||
|
||||
const decryptedPassword = decrypt2(password, challenge);
|
||||
@@ -38,7 +38,7 @@ export const createEntry = async (
|
||||
|
||||
const entry = await pb
|
||||
.collection("passwords__entries")
|
||||
.create<WithPB<IPasswordsEntry>>({
|
||||
.create<WithPB<PasswordsSchemas.IEntry>>({
|
||||
name,
|
||||
icon,
|
||||
color,
|
||||
@@ -61,9 +61,9 @@ export const updateEntry = async (
|
||||
username,
|
||||
password,
|
||||
master,
|
||||
}: Omit<IPasswordsEntry, "decrypted" | "pinned"> & { master: string },
|
||||
}: Omit<PasswordsSchemas.IEntry, "decrypted" | "pinned"> & { master: string },
|
||||
challenge: string,
|
||||
): Promise<WithPB<IPasswordsEntry>> => {
|
||||
): Promise<WithPB<PasswordsSchemas.IEntry>> => {
|
||||
const decryptedMaster = await getDecryptedMaster(pb, master, challenge);
|
||||
|
||||
const decryptedPassword = decrypt2(password, challenge);
|
||||
@@ -74,7 +74,7 @@ export const updateEntry = async (
|
||||
|
||||
const entry = await pb
|
||||
.collection("passwords__entries")
|
||||
.update<WithPB<IPasswordsEntry>>(id, {
|
||||
.update<WithPB<PasswordsSchemas.IEntry>>(id, {
|
||||
name,
|
||||
icon,
|
||||
color,
|
||||
@@ -94,7 +94,7 @@ export const decryptEntry = async (
|
||||
): Promise<string> => {
|
||||
const decryptedMaster = await getDecryptedMaster(pb, master, challenge);
|
||||
|
||||
const password: IPasswordsEntry = await pb
|
||||
const password: PasswordsSchemas.IEntry = await pb
|
||||
.collection("passwords__entries")
|
||||
.getOne(id);
|
||||
|
||||
@@ -113,14 +113,14 @@ export const deleteEntry = async (pb: PocketBase, id: string) => {
|
||||
export const togglePin = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<IPasswordsEntry>> => {
|
||||
): Promise<WithPB<PasswordsSchemas.IEntry>> => {
|
||||
const entry = await pb
|
||||
.collection("passwords__entries")
|
||||
.getOne<WithPB<IPasswordsEntry>>(id);
|
||||
.getOne<WithPB<PasswordsSchemas.IEntry>>(id);
|
||||
|
||||
return await pb
|
||||
.collection("passwords__entries")
|
||||
.update<WithPB<IPasswordsEntry>>(id, {
|
||||
.update<WithPB<PasswordsSchemas.IEntry>>(id, {
|
||||
pinned: !entry.pinned,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { RailwayMapSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { RailwayMapLineSchema, RailwayMapStationSchema } from "../schema";
|
||||
import * as RailwayMapServices from "../services/railwayMap.service";
|
||||
|
||||
const railwayMapRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const getLines = forgeController
|
||||
.route("GET /lines")
|
||||
.description("Get all railway lines")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(RailwayMapLineSchema)),
|
||||
response: z.array(WithPBSchema(RailwayMapSchemas.LineSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await RailwayMapServices.getLines(pb));
|
||||
|
||||
@@ -24,7 +24,7 @@ const getStations = forgeController
|
||||
.route("GET /stations")
|
||||
.description("Get all railway stations")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(RailwayMapStationSchema)),
|
||||
response: z.array(WithPBSchema(RailwayMapSchemas.StationSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => RailwayMapServices.getStations(pb));
|
||||
|
||||
@@ -36,7 +36,7 @@ const getShortestPath = forgeController
|
||||
start: z.string(),
|
||||
end: z.string(),
|
||||
}),
|
||||
response: z.array(WithPBSchema(RailwayMapStationSchema)),
|
||||
response: z.array(WithPBSchema(RailwayMapSchemas.StationSchema)),
|
||||
})
|
||||
.callback(
|
||||
async ({ pb, query: { start, end } }) =>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: railwayMap
|
||||
* Generated at: 2025-07-09T12:50:41.284Z
|
||||
* Contains: railway_map__lines, railway_map__stations
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const RailwayMapLineSchema = z.object({
|
||||
country: z.string(),
|
||||
type: z.string(),
|
||||
code: z.string(),
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
ways: z.any(),
|
||||
map_paths: z.any(),
|
||||
});
|
||||
|
||||
const RailwayMapStationSchema = z.object({
|
||||
name: z.string(),
|
||||
desc: z.string(),
|
||||
lines: z.array(z.string()),
|
||||
codes: z.any(),
|
||||
coords: z.any(),
|
||||
map_data: z.any(),
|
||||
type: z.string(),
|
||||
distances: z.any(),
|
||||
map_image: z.string(),
|
||||
});
|
||||
|
||||
type IRailwayMapLine = z.infer<typeof RailwayMapLineSchema>;
|
||||
type IRailwayMapStation = z.infer<typeof RailwayMapStationSchema>;
|
||||
|
||||
export { RailwayMapLineSchema, RailwayMapStationSchema };
|
||||
|
||||
export type { IRailwayMapLine, IRailwayMapStation };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
// Add your custom schemas here. They will not be overwritten by this script.
|
||||
@@ -1,29 +1,33 @@
|
||||
import ClientError from "@functions/ClientError";
|
||||
import PocketBase from "pocketbase";
|
||||
import { RailwayMapSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IRailwayMapLine, IRailwayMapStation } from "../schema";
|
||||
import dijkstraWithTransfers from "../utils/pathFinding";
|
||||
|
||||
export const getLines = (pb: PocketBase): Promise<WithPB<IRailwayMapLine>[]> =>
|
||||
pb.collection("railway_map__lines").getFullList<WithPB<IRailwayMapLine>>();
|
||||
export const getLines = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<RailwayMapSchemas.ILine>[]> =>
|
||||
pb
|
||||
.collection("railway_map__lines")
|
||||
.getFullList<WithPB<RailwayMapSchemas.ILine>>();
|
||||
|
||||
export const getStations = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<IRailwayMapStation>[]> =>
|
||||
): Promise<WithPB<RailwayMapSchemas.IStation>[]> =>
|
||||
pb
|
||||
.collection("railway_map__stations")
|
||||
.getFullList<WithPB<IRailwayMapStation>>();
|
||||
.getFullList<WithPB<RailwayMapSchemas.IStation>>();
|
||||
|
||||
export const getShortestPath = async (
|
||||
pb: PocketBase,
|
||||
start: string,
|
||||
end: string,
|
||||
): Promise<WithPB<IRailwayMapStation>[]> => {
|
||||
): Promise<WithPB<RailwayMapSchemas.IStation>[]> => {
|
||||
const allStations = await pb
|
||||
.collection("railway_map__stations")
|
||||
.getFullList<WithPB<IRailwayMapStation>>();
|
||||
.getFullList<WithPB<RailwayMapSchemas.IStation>>();
|
||||
|
||||
if (
|
||||
![start, end].every((station) => allStations.some((s) => s.id === station))
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { TodoListSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { TodoListEntrySchema, TodoListStatusCounterSchema } from "../schema";
|
||||
import * as entriesService from "../services/entries.service";
|
||||
|
||||
const todoListEntriesRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const getStatusCounter = forgeController
|
||||
.route("GET /utils/status-counter")
|
||||
.description("Get status counter for todo entries")
|
||||
.schema({
|
||||
response: TodoListStatusCounterSchema,
|
||||
response: TodoListSchemas.TodoListStatusCounterSchema,
|
||||
})
|
||||
.callback(async ({ pb }) => await entriesService.getStatusCounter(pb));
|
||||
|
||||
@@ -27,7 +27,7 @@ const getEntryById = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(TodoListEntrySchema),
|
||||
response: WithPBSchema(TodoListSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "todo_list__entries",
|
||||
@@ -47,7 +47,7 @@ const getAllEntries = forgeController
|
||||
tag: z.string().optional(),
|
||||
query: z.string().optional(),
|
||||
}),
|
||||
response: z.array(WithPBSchema(TodoListEntrySchema)),
|
||||
response: z.array(WithPBSchema(TodoListSchemas.EntrySchema)),
|
||||
})
|
||||
.existenceCheck("query", {
|
||||
tag: "[todo_list_tags]",
|
||||
@@ -63,11 +63,11 @@ const createEntry = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new todo entry")
|
||||
.schema({
|
||||
body: TodoListEntrySchema.omit({
|
||||
body: TodoListSchemas.EntrySchema.omit({
|
||||
completed_at: true,
|
||||
done: true,
|
||||
}),
|
||||
response: WithPBSchema(TodoListEntrySchema),
|
||||
response: WithPBSchema(TodoListSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("body", {
|
||||
list: "[todo_list_lists]",
|
||||
@@ -84,11 +84,11 @@ const updateEntry = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: TodoListEntrySchema.omit({
|
||||
body: TodoListSchemas.EntrySchema.omit({
|
||||
completed_at: true,
|
||||
done: true,
|
||||
}),
|
||||
response: WithPBSchema(TodoListEntrySchema),
|
||||
response: WithPBSchema(TodoListSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "todo_list__entries",
|
||||
@@ -127,7 +127,7 @@ const toggleEntry = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(TodoListEntrySchema),
|
||||
response: WithPBSchema(TodoListSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "todo_list__entries",
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { TodoListSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { TodoListListSchema } from "../schema";
|
||||
import * as listsService from "../services/lists.service";
|
||||
|
||||
const todoListListsRouter = express.Router();
|
||||
@@ -17,7 +17,7 @@ const getAllLists = forgeController
|
||||
.description("Get all todo lists")
|
||||
.schema({
|
||||
response: z.array(
|
||||
WithPBSchema(TodoListListSchema.extend({ amount: z.number() })),
|
||||
WithPBSchema(TodoListSchemas.ListSchema.extend({ amount: z.number() })),
|
||||
),
|
||||
})
|
||||
.callback(({ pb }) => listsService.getAllLists(pb));
|
||||
@@ -26,8 +26,14 @@ const createList = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new todo list")
|
||||
.schema({
|
||||
body: TodoListListSchema.pick({ name: true, icon: true, color: true }),
|
||||
response: WithPBSchema(TodoListListSchema.extend({ amount: z.number() })),
|
||||
body: TodoListSchemas.ListSchema.pick({
|
||||
name: true,
|
||||
icon: true,
|
||||
color: true,
|
||||
}),
|
||||
response: WithPBSchema(
|
||||
TodoListSchemas.ListSchema.extend({ amount: z.number() }),
|
||||
),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(({ pb, body }) => listsService.createList(pb, body));
|
||||
@@ -39,8 +45,14 @@ const updateList = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: TodoListListSchema.pick({ name: true, icon: true, color: true }),
|
||||
response: WithPBSchema(TodoListListSchema.extend({ amount: z.number() })),
|
||||
body: TodoListSchemas.ListSchema.pick({
|
||||
name: true,
|
||||
icon: true,
|
||||
color: true,
|
||||
}),
|
||||
response: WithPBSchema(
|
||||
TodoListSchemas.ListSchema.extend({ amount: z.number() }),
|
||||
),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "todo_list__lists",
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { TodoListSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { TodoListPrioritySchema } from "../schema";
|
||||
import * as prioritiesService from "../services/priorities.service";
|
||||
|
||||
const todoListPrioritiesRouter = express.Router();
|
||||
@@ -17,7 +17,9 @@ const getAllPriorities = forgeController
|
||||
.description("Get all todo priorities")
|
||||
.schema({
|
||||
response: z.array(
|
||||
WithPBSchema(TodoListPrioritySchema.extend({ amount: z.number() })),
|
||||
WithPBSchema(
|
||||
TodoListSchemas.PrioritySchema.extend({ amount: z.number() }),
|
||||
),
|
||||
),
|
||||
})
|
||||
.callback(({ pb }) => prioritiesService.getAllPriorities(pb));
|
||||
@@ -26,9 +28,9 @@ const createPriority = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new todo priority")
|
||||
.schema({
|
||||
body: TodoListPrioritySchema,
|
||||
body: TodoListSchemas.PrioritySchema,
|
||||
response: WithPBSchema(
|
||||
TodoListPrioritySchema.extend({ amount: z.number() }),
|
||||
TodoListSchemas.PrioritySchema.extend({ amount: z.number() }),
|
||||
),
|
||||
})
|
||||
.statusCode(201)
|
||||
@@ -41,9 +43,9 @@ const updatePriority = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: TodoListPrioritySchema.pick({ name: true, color: true }),
|
||||
body: TodoListSchemas.PrioritySchema.pick({ name: true, color: true }),
|
||||
response: WithPBSchema(
|
||||
TodoListPrioritySchema.extend({ amount: z.number() }),
|
||||
TodoListSchemas.PrioritySchema.extend({ amount: z.number() }),
|
||||
),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { TodoListSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { TodoListTagSchema } from "../schema";
|
||||
import * as tagsService from "../services/tags.service";
|
||||
|
||||
const todoListTagsRouter = express.Router();
|
||||
@@ -17,7 +17,7 @@ const getAllTags = forgeController
|
||||
.description("Get all todo tags")
|
||||
.schema({
|
||||
response: z.array(
|
||||
WithPBSchema(TodoListTagSchema.extend({ amount: z.number() })),
|
||||
WithPBSchema(TodoListSchemas.TagSchema.extend({ amount: z.number() })),
|
||||
),
|
||||
})
|
||||
.callback(({ pb }) => tagsService.getAllTags(pb));
|
||||
@@ -26,8 +26,10 @@ const createTag = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new todo tag")
|
||||
.schema({
|
||||
body: TodoListTagSchema,
|
||||
response: WithPBSchema(TodoListTagSchema.extend({ amount: z.number() })),
|
||||
body: TodoListSchemas.TagSchema,
|
||||
response: WithPBSchema(
|
||||
TodoListSchemas.TagSchema.extend({ amount: z.number() }),
|
||||
),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(({ pb, body }) => tagsService.createTag(pb, body));
|
||||
@@ -39,8 +41,10 @@ const updateTag = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: TodoListTagSchema,
|
||||
response: WithPBSchema(TodoListTagSchema.extend({ amount: z.number() })),
|
||||
body: TodoListSchemas.TagSchema,
|
||||
response: WithPBSchema(
|
||||
TodoListSchemas.TagSchema.extend({ amount: z.number() }),
|
||||
),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "todo_list__tags",
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: todoList
|
||||
* Generated at: 2025-07-09T12:50:41.283Z
|
||||
* Contains: todo_list__lists, todo_list__tags, todo_list__entries, todo_list__priorities, todo_list__lists_aggregated, todo_list__tags_aggregated, todo_list__priorities_aggregated
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const TodoListListSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
});
|
||||
|
||||
const TodoListTagSchema = z.object({
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const TodoListEntrySchema = z.object({
|
||||
summary: z.string(),
|
||||
notes: z.string(),
|
||||
due_date: z.string(),
|
||||
due_date_has_time: z.boolean(),
|
||||
list: z.string(),
|
||||
tags: z.array(z.string()),
|
||||
priority: z.string(),
|
||||
done: z.boolean(),
|
||||
completed_at: z.string(),
|
||||
});
|
||||
|
||||
const TodoListPrioritySchema = z.object({
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
const TodoListListAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
icon: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
const TodoListTagAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
const TodoListPriorityAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
type ITodoListList = z.infer<typeof TodoListListSchema>;
|
||||
type ITodoListTag = z.infer<typeof TodoListTagSchema>;
|
||||
type ITodoListEntry = z.infer<typeof TodoListEntrySchema>;
|
||||
type ITodoListPriority = z.infer<typeof TodoListPrioritySchema>;
|
||||
type ITodoListListAggregated = z.infer<typeof TodoListListAggregatedSchema>;
|
||||
type ITodoListTagAggregated = z.infer<typeof TodoListTagAggregatedSchema>;
|
||||
type ITodoListPriorityAggregated = z.infer<
|
||||
typeof TodoListPriorityAggregatedSchema
|
||||
>;
|
||||
|
||||
export {
|
||||
TodoListListSchema,
|
||||
TodoListTagSchema,
|
||||
TodoListEntrySchema,
|
||||
TodoListPrioritySchema,
|
||||
TodoListListAggregatedSchema,
|
||||
TodoListTagAggregatedSchema,
|
||||
TodoListPriorityAggregatedSchema,
|
||||
};
|
||||
|
||||
export type {
|
||||
ITodoListList,
|
||||
ITodoListTag,
|
||||
ITodoListEntry,
|
||||
ITodoListPriority,
|
||||
ITodoListListAggregated,
|
||||
ITodoListTagAggregated,
|
||||
ITodoListPriorityAggregated,
|
||||
};
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
const TodoListStatusCounterSchema = z.object({
|
||||
all: z.number(),
|
||||
today: z.number(),
|
||||
scheduled: z.number(),
|
||||
overdue: z.number(),
|
||||
completed: z.number(),
|
||||
});
|
||||
|
||||
type ITodoListStatusCounter = z.infer<typeof TodoListStatusCounterSchema>;
|
||||
|
||||
export { TodoListStatusCounterSchema };
|
||||
|
||||
export type { ITodoListStatusCounter };
|
||||
@@ -1,15 +1,16 @@
|
||||
import moment from "moment";
|
||||
import PocketBase from "pocketbase";
|
||||
import { TodoListSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { ITodoListEntry, ITodoListStatusCounter } from "../schema";
|
||||
|
||||
export const getEntryById = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<ITodoListEntry>> =>
|
||||
pb.collection("todo_list__entries").getOne<WithPB<ITodoListEntry>>(id);
|
||||
): Promise<WithPB<TodoListSchemas.IEntry>> =>
|
||||
pb
|
||||
.collection("todo_list__entries")
|
||||
.getOne<WithPB<TodoListSchemas.IEntry>>(id);
|
||||
|
||||
export const getAllEntries = async (
|
||||
pb: PocketBase,
|
||||
@@ -17,7 +18,7 @@ export const getAllEntries = async (
|
||||
tag?: string,
|
||||
list?: string,
|
||||
priority?: string,
|
||||
): Promise<WithPB<ITodoListEntry>[]> => {
|
||||
): Promise<WithPB<TodoListSchemas.IEntry>[]> => {
|
||||
const filters = {
|
||||
all: "done = false",
|
||||
today: `done = false && due_date >= "${moment()
|
||||
@@ -45,7 +46,7 @@ export const getAllEntries = async (
|
||||
|
||||
return await pb
|
||||
.collection("todo_list__entries")
|
||||
.getFullList<WithPB<ITodoListEntry>>({
|
||||
.getFullList<WithPB<TodoListSchemas.IEntry>>({
|
||||
filter: finalFilter,
|
||||
sort: "-created",
|
||||
});
|
||||
@@ -53,7 +54,7 @@ export const getAllEntries = async (
|
||||
|
||||
export const getStatusCounter = async (
|
||||
pb: PocketBase,
|
||||
): Promise<ITodoListStatusCounter> => {
|
||||
): Promise<TodoListSchemas.ITodoListStatusCounter> => {
|
||||
const filters = {
|
||||
all: "done = false",
|
||||
today: `done = false && due_date >= "${moment()
|
||||
@@ -73,7 +74,7 @@ export const getStatusCounter = async (
|
||||
completed: "done = true",
|
||||
};
|
||||
|
||||
const counters: ITodoListStatusCounter = {
|
||||
const counters: TodoListSchemas.ITodoListStatusCounter = {
|
||||
all: 0,
|
||||
today: 0,
|
||||
scheduled: 0,
|
||||
@@ -96,10 +97,13 @@ export const getStatusCounter = async (
|
||||
|
||||
export const createEntry = async (
|
||||
pb: PocketBase,
|
||||
data: Omit<ITodoListEntry, "completed_at" | "done" | "due_date_has_time"> & {
|
||||
data: Omit<
|
||||
TodoListSchemas.IEntry,
|
||||
"completed_at" | "done" | "due_date_has_time"
|
||||
> & {
|
||||
due_date_has_time?: boolean;
|
||||
},
|
||||
): Promise<WithPB<ITodoListEntry>> => {
|
||||
): Promise<WithPB<TodoListSchemas.IEntry>> => {
|
||||
if (data.due_date && !data.due_date_has_time) {
|
||||
data.due_date = moment(data.due_date).endOf("day").toISOString();
|
||||
}
|
||||
@@ -108,16 +112,19 @@ export const createEntry = async (
|
||||
|
||||
return await pb
|
||||
.collection("todo_list__entries")
|
||||
.create<WithPB<ITodoListEntry>>(data);
|
||||
.create<WithPB<TodoListSchemas.IEntry>>(data);
|
||||
};
|
||||
|
||||
export const updateEntry = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: Omit<ITodoListEntry, "completed_at" | "done" | "due_date_has_time"> & {
|
||||
data: Omit<
|
||||
TodoListSchemas.IEntry,
|
||||
"completed_at" | "done" | "due_date_has_time"
|
||||
> & {
|
||||
due_date_has_time?: boolean;
|
||||
},
|
||||
): Promise<WithPB<ITodoListEntry>> => {
|
||||
): Promise<WithPB<TodoListSchemas.IEntry>> => {
|
||||
if (data.due_date && !data.due_date_has_time) {
|
||||
data.due_date = moment(data.due_date).endOf("day").toISOString();
|
||||
}
|
||||
@@ -137,14 +144,14 @@ export const deleteEntry = async (
|
||||
export const toggleEntry = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<ITodoListEntry>> => {
|
||||
): Promise<WithPB<TodoListSchemas.IEntry>> => {
|
||||
const entry = await pb
|
||||
.collection("todo_list__entries")
|
||||
.getOne<WithPB<ITodoListEntry>>(id);
|
||||
.getOne<WithPB<TodoListSchemas.IEntry>>(id);
|
||||
|
||||
return await pb
|
||||
.collection("todo_list__entries")
|
||||
.update<WithPB<ITodoListEntry>>(id, {
|
||||
.update<WithPB<TodoListSchemas.IEntry>>(id, {
|
||||
done: !entry.done,
|
||||
completed_at: entry.done
|
||||
? null
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { TodoListSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { ITodoListList } from "../schema";
|
||||
|
||||
export const getAllLists = (
|
||||
pb: PocketBase,
|
||||
): Promise<
|
||||
WithPB<
|
||||
ITodoListList & {
|
||||
TodoListSchemas.IList & {
|
||||
amount: number;
|
||||
}
|
||||
>[]
|
||||
> =>
|
||||
pb.collection("todo_list__lists_aggregated").getFullList<
|
||||
WithPB<ITodoListList> & {
|
||||
WithPB<TodoListSchemas.IList> & {
|
||||
amount: number;
|
||||
}
|
||||
>();
|
||||
|
||||
export const createList = async (
|
||||
pb: PocketBase,
|
||||
data: ITodoListList,
|
||||
data: TodoListSchemas.IList,
|
||||
): Promise<
|
||||
WithPB<
|
||||
ITodoListList & {
|
||||
TodoListSchemas.IList & {
|
||||
amount: number;
|
||||
}
|
||||
>
|
||||
> => {
|
||||
const created = await pb
|
||||
.collection("todo_list__lists")
|
||||
.create<WithPB<ITodoListList>>(data);
|
||||
.create<WithPB<TodoListSchemas.IList>>(data);
|
||||
|
||||
return pb.collection("todo_list__lists_aggregated").getOne<
|
||||
WithPB<ITodoListList> & {
|
||||
WithPB<TodoListSchemas.IList> & {
|
||||
amount: number;
|
||||
}
|
||||
>(created.id);
|
||||
@@ -43,21 +42,21 @@ export const createList = async (
|
||||
export const updateList = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: ITodoListList,
|
||||
data: TodoListSchemas.IList,
|
||||
): Promise<
|
||||
WithPB<
|
||||
ITodoListList & {
|
||||
TodoListSchemas.IList & {
|
||||
amount: number;
|
||||
}
|
||||
>
|
||||
> => {
|
||||
const updated = await pb
|
||||
.collection("todo_list__lists")
|
||||
.update<WithPB<ITodoListList>>(id, data);
|
||||
.update<WithPB<TodoListSchemas.IList>>(id, data);
|
||||
|
||||
return pb.collection("todo_list__lists_aggregated").getOne<
|
||||
WithPB<
|
||||
ITodoListList & {
|
||||
TodoListSchemas.IList & {
|
||||
amount: number;
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { TodoListSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { ITodoListPriority } from "../schema";
|
||||
|
||||
export const getAllPriorities = (
|
||||
pb: PocketBase,
|
||||
): Promise<
|
||||
WithPB<
|
||||
ITodoListPriority & {
|
||||
TodoListSchemas.IPriority & {
|
||||
amount: number;
|
||||
}
|
||||
>[]
|
||||
> =>
|
||||
pb.collection("todo_list__priorities_aggregated").getFullList<
|
||||
WithPB<
|
||||
ITodoListPriority & {
|
||||
TodoListSchemas.IPriority & {
|
||||
amount: number;
|
||||
}
|
||||
>
|
||||
@@ -23,41 +22,41 @@ export const getAllPriorities = (
|
||||
|
||||
export const createPriority = async (
|
||||
pb: PocketBase,
|
||||
data: Omit<ITodoListPriority, "amount">,
|
||||
data: Omit<TodoListSchemas.IPriority, "amount">,
|
||||
): Promise<
|
||||
WithPB<
|
||||
ITodoListPriority & {
|
||||
TodoListSchemas.IPriority & {
|
||||
amount: number;
|
||||
}
|
||||
>
|
||||
> => {
|
||||
const created = await pb
|
||||
.collection("todo_list__priorities")
|
||||
.create<WithPB<ITodoListPriority>>(data);
|
||||
.create<WithPB<TodoListSchemas.IPriority>>(data);
|
||||
|
||||
return pb
|
||||
.collection("todo_list__priorities_aggregated")
|
||||
.getOne<WithPB<ITodoListPriority & { amount: number }>>(created.id);
|
||||
.getOne<WithPB<TodoListSchemas.IPriority & { amount: number }>>(created.id);
|
||||
};
|
||||
|
||||
export const updatePriority = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: Omit<ITodoListPriority, "amount">,
|
||||
data: Omit<TodoListSchemas.IPriority, "amount">,
|
||||
): Promise<
|
||||
WithPB<
|
||||
ITodoListPriority & {
|
||||
TodoListSchemas.IPriority & {
|
||||
amount: number;
|
||||
}
|
||||
>
|
||||
> => {
|
||||
const updated = await pb
|
||||
.collection("todo_list__priorities")
|
||||
.update<WithPB<ITodoListPriority>>(id, data);
|
||||
.update<WithPB<TodoListSchemas.IPriority>>(id, data);
|
||||
|
||||
return pb
|
||||
.collection("todo_list__priorities_aggregated")
|
||||
.getOne<WithPB<ITodoListPriority & { amount: number }>>(updated.id);
|
||||
.getOne<WithPB<TodoListSchemas.IPriority & { amount: number }>>(updated.id);
|
||||
};
|
||||
|
||||
export const deletePriority = async (
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { TodoListSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { ITodoListTag } from "../schema";
|
||||
|
||||
export const getAllTags = (
|
||||
pb: PocketBase,
|
||||
): Promise<
|
||||
WithPB<
|
||||
ITodoListTag & {
|
||||
TodoListSchemas.ITag & {
|
||||
amount: number;
|
||||
}
|
||||
>[]
|
||||
> =>
|
||||
pb.collection("todo_list__tags_aggregated").getFullList<
|
||||
WithPB<
|
||||
ITodoListTag & {
|
||||
TodoListSchemas.ITag & {
|
||||
amount: number;
|
||||
}
|
||||
>
|
||||
@@ -23,41 +22,41 @@ export const getAllTags = (
|
||||
|
||||
export const createTag = async (
|
||||
pb: PocketBase,
|
||||
data: ITodoListTag,
|
||||
data: TodoListSchemas.ITag,
|
||||
): Promise<
|
||||
WithPB<
|
||||
ITodoListTag & {
|
||||
TodoListSchemas.ITag & {
|
||||
amount: number;
|
||||
}
|
||||
>
|
||||
> => {
|
||||
const created = await pb
|
||||
.collection("todo_list__tags")
|
||||
.create<WithPB<ITodoListTag>>(data);
|
||||
.create<WithPB<TodoListSchemas.ITag>>(data);
|
||||
|
||||
return pb
|
||||
.collection("todo_list__tags_aggregated")
|
||||
.getOne<WithPB<ITodoListTag & { amount: number }>>(created.id);
|
||||
.getOne<WithPB<TodoListSchemas.ITag & { amount: number }>>(created.id);
|
||||
};
|
||||
|
||||
export const updateTag = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: ITodoListTag,
|
||||
data: TodoListSchemas.ITag,
|
||||
): Promise<
|
||||
WithPB<
|
||||
ITodoListTag & {
|
||||
TodoListSchemas.ITag & {
|
||||
amount: number;
|
||||
}
|
||||
>
|
||||
> => {
|
||||
const updated = await pb
|
||||
.collection("todo_list__tags")
|
||||
.update<WithPB<ITodoListTag>>(id, data);
|
||||
.update<WithPB<TodoListSchemas.ITag>>(id, data);
|
||||
|
||||
return pb
|
||||
.collection("todo_list__tags_aggregated")
|
||||
.getOne<WithPB<ITodoListTag & { amount: number }>>(updated.id);
|
||||
.getOne<WithPB<TodoListSchemas.ITag & { amount: number }>>(updated.id);
|
||||
};
|
||||
|
||||
export const deleteTag = async (pb: PocketBase, id: string): Promise<void> => {
|
||||
|
||||
@@ -4,16 +4,13 @@ import {
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import fs from "fs";
|
||||
import { VirtualWardrobeSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { fieldsUploadMiddleware } from "@middlewares/uploadMiddleware";
|
||||
|
||||
import {
|
||||
VirtualWardrobeEntrySchema,
|
||||
VirtualWardrobeSidebarDataSchema,
|
||||
} from "../schema";
|
||||
import * as entriesService from "../services/entries.service";
|
||||
import * as visionService from "../services/vision.service";
|
||||
|
||||
@@ -23,7 +20,7 @@ const getSidebarData = forgeController
|
||||
.route("GET /sidebar-data")
|
||||
.description("Get sidebar data for virtual wardrobe")
|
||||
.schema({
|
||||
response: VirtualWardrobeSidebarDataSchema,
|
||||
response: VirtualWardrobeSchemas.VirtualWardrobeSidebarDataSchema,
|
||||
})
|
||||
.callback(async ({ pb }) => await entriesService.getSidebarData(pb));
|
||||
|
||||
@@ -43,7 +40,7 @@ const getEntries = forgeController
|
||||
.transform((val) => val === "true"),
|
||||
q: z.string().optional(),
|
||||
}),
|
||||
response: z.array(WithPBSchema(VirtualWardrobeEntrySchema)),
|
||||
response: z.array(WithPBSchema(VirtualWardrobeSchemas.EntrySchema)),
|
||||
})
|
||||
.callback(
|
||||
async ({ pb, query }) => await entriesService.getEntries(pb, query),
|
||||
@@ -63,7 +60,7 @@ const createEntry = forgeController
|
||||
price: z.string().transform((val) => parseFloat(val)),
|
||||
notes: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(VirtualWardrobeEntrySchema),
|
||||
response: WithPBSchema(VirtualWardrobeSchemas.EntrySchema),
|
||||
})
|
||||
.middlewares(
|
||||
fieldsUploadMiddleware.bind({
|
||||
@@ -120,7 +117,7 @@ const updateEntry = forgeController
|
||||
price: z.number().optional(),
|
||||
notes: z.string().optional(),
|
||||
}),
|
||||
response: WithPBSchema(VirtualWardrobeEntrySchema),
|
||||
response: WithPBSchema(VirtualWardrobeSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "virtual_wardrobe__entries",
|
||||
@@ -154,7 +151,7 @@ const toggleFavourite = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(VirtualWardrobeEntrySchema),
|
||||
response: WithPBSchema(VirtualWardrobeSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "virtual_wardrobe__entries",
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { VirtualWardrobeSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { VirtualWardrobeEntrySchema } from "../schema";
|
||||
import * as sessionService from "../services/session.service";
|
||||
|
||||
const virtualWardrobeSessionRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const getCart = forgeController
|
||||
.route("GET /cart")
|
||||
.description("Get session cart items")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(VirtualWardrobeEntrySchema)),
|
||||
response: z.array(WithPBSchema(VirtualWardrobeSchemas.EntrySchema)),
|
||||
})
|
||||
.callback(async () => sessionService.getSessionCart());
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: virtualWardrobe
|
||||
* Generated at: 2025-07-09T12:50:41.279Z
|
||||
* Contains: virtual_wardrobe__entries, virtual_wardrobe__histories
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const VirtualWardrobeEntrySchema = z.object({
|
||||
name: z.string(),
|
||||
category: z.string(),
|
||||
subcategory: z.string(),
|
||||
colors: z.any(),
|
||||
size: z.string(),
|
||||
brand: z.string(),
|
||||
front_image: z.string(),
|
||||
back_image: z.string(),
|
||||
last_worn: z.string(),
|
||||
times_worn: z.number(),
|
||||
purchase_date: z.string(),
|
||||
price: z.number(),
|
||||
notes: z.string(),
|
||||
is_favourite: z.boolean(),
|
||||
});
|
||||
|
||||
const VirtualWardrobeHistorySchema = z.object({
|
||||
entries: z.array(z.string()),
|
||||
notes: z.string(),
|
||||
});
|
||||
|
||||
type IVirtualWardrobeEntry = z.infer<typeof VirtualWardrobeEntrySchema>;
|
||||
type IVirtualWardrobeHistory = z.infer<typeof VirtualWardrobeHistorySchema>;
|
||||
|
||||
export { VirtualWardrobeEntrySchema, VirtualWardrobeHistorySchema };
|
||||
|
||||
export type { IVirtualWardrobeEntry, IVirtualWardrobeHistory };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
const VirtualWardrobeSidebarDataSchema = z.object({
|
||||
total: z.number(),
|
||||
favourites: z.number(),
|
||||
categories: z.record(z.string(), z.number()),
|
||||
subcategories: z.record(z.string(), z.number()),
|
||||
brands: z.record(z.string(), z.number()),
|
||||
sizes: z.record(z.string(), z.number()),
|
||||
colors: z.record(z.string(), z.number()),
|
||||
});
|
||||
|
||||
type IVirtualWardrobeSidebarData = z.infer<
|
||||
typeof VirtualWardrobeSidebarDataSchema
|
||||
>;
|
||||
|
||||
export { VirtualWardrobeSidebarDataSchema };
|
||||
|
||||
export type { IVirtualWardrobeSidebarData };
|
||||
@@ -1,15 +1,14 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { VirtualWardrobeSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IVirtualWardrobeEntry, IVirtualWardrobeSidebarData } from "../schema";
|
||||
|
||||
export const getSidebarData = async (
|
||||
pb: PocketBase,
|
||||
): Promise<IVirtualWardrobeSidebarData> => {
|
||||
): Promise<VirtualWardrobeSchemas.IVirtualWardrobeSidebarData> => {
|
||||
const allEntries = await pb
|
||||
.collection("virtual_wardrobe__entries")
|
||||
.getFullList<IVirtualWardrobeEntry>();
|
||||
.getFullList<WithPB<VirtualWardrobeSchemas.IEntry>>();
|
||||
|
||||
const categories = allEntries.reduce(
|
||||
(acc, curr) => {
|
||||
@@ -90,7 +89,7 @@ export const getEntries = async (
|
||||
favourite?: boolean;
|
||||
q?: string;
|
||||
},
|
||||
): Promise<WithPB<IVirtualWardrobeEntry>[]> => {
|
||||
): Promise<WithPB<VirtualWardrobeSchemas.IEntry>[]> => {
|
||||
const filterArray = [
|
||||
filters.category && `category = '${filters.category}'`,
|
||||
filters.subcategory && `subcategory = '${filters.subcategory}'`,
|
||||
@@ -104,7 +103,7 @@ export const getEntries = async (
|
||||
|
||||
const entries = await pb
|
||||
.collection("virtual_wardrobe__entries")
|
||||
.getFullList<WithPB<IVirtualWardrobeEntry>>({
|
||||
.getFullList<WithPB<VirtualWardrobeSchemas.IEntry>>({
|
||||
filter: filterArray.join(" && "),
|
||||
sort: "-created",
|
||||
});
|
||||
@@ -130,10 +129,10 @@ export const createEntry = async (
|
||||
front_image: File;
|
||||
back_image: File;
|
||||
},
|
||||
): Promise<WithPB<IVirtualWardrobeEntry>> => {
|
||||
): Promise<WithPB<VirtualWardrobeSchemas.IEntry>> => {
|
||||
const newEntry = await pb
|
||||
.collection("virtual_wardrobe__entries")
|
||||
.create<WithPB<IVirtualWardrobeEntry>>(data);
|
||||
.create<WithPB<VirtualWardrobeSchemas.IEntry>>(data);
|
||||
|
||||
return {
|
||||
...newEntry,
|
||||
@@ -149,11 +148,11 @@ export const createEntry = async (
|
||||
export const updateEntry = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: Partial<IVirtualWardrobeEntry>,
|
||||
): Promise<WithPB<IVirtualWardrobeEntry>> => {
|
||||
data: Partial<VirtualWardrobeSchemas.IEntry>,
|
||||
): Promise<WithPB<VirtualWardrobeSchemas.IEntry>> => {
|
||||
const updatedEntry = await pb
|
||||
.collection("virtual_wardrobe__entries")
|
||||
.update<WithPB<IVirtualWardrobeEntry>>(id, data);
|
||||
.update<WithPB<VirtualWardrobeSchemas.IEntry>>(id, data);
|
||||
|
||||
return {
|
||||
...updatedEntry,
|
||||
@@ -176,10 +175,10 @@ export const deleteEntry = async (
|
||||
export const toggleFavourite = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<IVirtualWardrobeEntry>> => {
|
||||
): Promise<WithPB<VirtualWardrobeSchemas.IEntry>> => {
|
||||
const entry = await pb
|
||||
.collection("virtual_wardrobe__entries")
|
||||
.getOne<WithPB<IVirtualWardrobeEntry>>(id);
|
||||
.getOne<WithPB<VirtualWardrobeSchemas.IEntry>>(id);
|
||||
|
||||
return await updateEntry(pb, id, {
|
||||
is_favourite: !entry.is_favourite,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { VirtualWardrobeSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IVirtualWardrobeEntry } from "../schema";
|
||||
const sessionCart = new Set<WithPB<VirtualWardrobeSchemas.IEntry>>();
|
||||
|
||||
const sessionCart = new Set<WithPB<IVirtualWardrobeEntry>>();
|
||||
|
||||
export const getSessionCart = (): WithPB<IVirtualWardrobeEntry>[] => {
|
||||
export const getSessionCart = (): WithPB<VirtualWardrobeSchemas.IEntry>[] => {
|
||||
return Array.from(sessionCart);
|
||||
};
|
||||
|
||||
@@ -20,7 +19,7 @@ export const addToCart = async (
|
||||
|
||||
const item = await pb
|
||||
.collection("virtual_wardrobe__entries")
|
||||
.getOne<WithPB<IVirtualWardrobeEntry>>(entryId);
|
||||
.getOne<WithPB<VirtualWardrobeSchemas.IEntry>>(entryId);
|
||||
|
||||
const processedItem = {
|
||||
...item,
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { WalletSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { WalletAssetSchema } from "../schema";
|
||||
import * as AssetsService from "../services/assets.service";
|
||||
|
||||
const walletAssetsRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const getAllAssets = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all wallet assets")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(WalletAssetSchema)),
|
||||
response: z.array(WithPBSchema(WalletSchemas.AssetAggregatedSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await AssetsService.getAllAssets(pb));
|
||||
|
||||
@@ -24,7 +24,7 @@ const createAsset = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new wallet asset")
|
||||
.schema({
|
||||
body: WalletAssetSchema.pick({
|
||||
body: WalletSchemas.AssetSchema.pick({
|
||||
name: true,
|
||||
icon: true,
|
||||
starting_balance: true,
|
||||
@@ -34,7 +34,7 @@ const createAsset = forgeController
|
||||
return isNaN(balance) ? 0 : balance;
|
||||
}),
|
||||
}),
|
||||
response: WithPBSchema(WalletAssetSchema),
|
||||
response: WithPBSchema(WalletSchemas.AssetSchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(async ({ pb, body }) => await AssetsService.createAsset(pb, body));
|
||||
@@ -63,7 +63,7 @@ const updateAsset = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: WalletAssetSchema.pick({
|
||||
body: WalletSchemas.AssetSchema.pick({
|
||||
name: true,
|
||||
icon: true,
|
||||
starting_balance: true,
|
||||
@@ -73,7 +73,7 @@ const updateAsset = forgeController
|
||||
return isNaN(balance) ? 0 : balance;
|
||||
}),
|
||||
}),
|
||||
response: WithPBSchema(WalletAssetSchema),
|
||||
response: WithPBSchema(WalletSchemas.AssetSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "wallet__assets",
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { WalletSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { WalletCategorySchema } from "../schema";
|
||||
import * as CategoriesService from "../services/categories.service";
|
||||
|
||||
const walletCategoriesRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const getAllCategories = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all wallet categories")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(WalletCategorySchema)),
|
||||
response: z.array(WithPBSchema(WalletSchemas.CategoryAggregatedSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await CategoriesService.getAllCategories(pb));
|
||||
|
||||
@@ -24,8 +24,8 @@ const createCategory = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new wallet category")
|
||||
.schema({
|
||||
body: WalletCategorySchema,
|
||||
response: WithPBSchema(WalletCategorySchema),
|
||||
body: WalletSchemas.CategorySchema,
|
||||
response: WithPBSchema(WalletSchemas.CategorySchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(
|
||||
@@ -39,8 +39,8 @@ const updateCategory = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: WalletCategorySchema,
|
||||
response: WithPBSchema(WalletCategorySchema),
|
||||
body: WalletSchemas.CategorySchema,
|
||||
response: WithPBSchema(WalletSchemas.CategorySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "wallet__categories",
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { WalletSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { WalletLedgerSchema } from "../schema";
|
||||
import * as LedgersService from "../services/ledgers.service";
|
||||
|
||||
const walletLedgersRouter = express.Router();
|
||||
@@ -16,7 +16,7 @@ const getAllLedgers = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all wallet ledgers")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(WalletLedgerSchema)),
|
||||
response: z.array(WithPBSchema(WalletSchemas.LedgerSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await LedgersService.getAllLedgers(pb));
|
||||
|
||||
@@ -24,8 +24,8 @@ const createLedger = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new wallet ledger")
|
||||
.schema({
|
||||
body: WalletLedgerSchema,
|
||||
response: WithPBSchema(WalletLedgerSchema),
|
||||
body: WalletSchemas.LedgerSchema,
|
||||
response: WithPBSchema(WalletSchemas.LedgerSchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(
|
||||
@@ -39,8 +39,8 @@ const updateLedger = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: WalletLedgerSchema,
|
||||
response: WithPBSchema(WalletLedgerSchema),
|
||||
body: WalletSchemas.LedgerSchema,
|
||||
response: WithPBSchema(WalletSchemas.LedgerSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "wallet__ledgers",
|
||||
|
||||
@@ -3,16 +3,13 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { WalletSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { singleUploadMiddleware } from "@middlewares/uploadMiddleware";
|
||||
|
||||
import {
|
||||
WalletReceiptScanResultSchema,
|
||||
WalletTransactionSchema,
|
||||
} from "../schema";
|
||||
import * as TransactionsService from "../services/transactions.service";
|
||||
|
||||
const walletTransactionsRouter = express.Router();
|
||||
@@ -21,7 +18,7 @@ const getAllTransactions = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all wallet transactions")
|
||||
.schema({
|
||||
response: z.array(WithPBSchema(WalletTransactionSchema)),
|
||||
response: z.array(WithPBSchema(WalletSchemas.TransactionSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await TransactionsService.getAllTransactions(pb));
|
||||
|
||||
@@ -59,7 +56,7 @@ const createTransaction = forgeController
|
||||
fromAsset: z.string().optional(),
|
||||
toAsset: z.string().optional(),
|
||||
}),
|
||||
response: z.array(WithPBSchema(WalletTransactionSchema)),
|
||||
response: z.array(WithPBSchema(WalletSchemas.TransactionSchema)),
|
||||
})
|
||||
.middlewares(singleUploadMiddleware)
|
||||
.existenceCheck("body", {
|
||||
@@ -115,7 +112,7 @@ const updateTransaction = forgeController
|
||||
.default("false")
|
||||
.transform((val) => val === "true"),
|
||||
}),
|
||||
response: WithPBSchema(WalletTransactionSchema),
|
||||
response: WithPBSchema(WalletSchemas.TransactionSchema),
|
||||
})
|
||||
.middlewares(singleUploadMiddleware)
|
||||
.existenceCheck("params", {
|
||||
@@ -158,7 +155,7 @@ const scanReceipt = forgeController
|
||||
.route("POST /scan-receipt")
|
||||
.description("Scan receipt to extract transaction data")
|
||||
.schema({
|
||||
response: WalletReceiptScanResultSchema,
|
||||
response: WalletSchemas.WalletReceiptScanResultSchema,
|
||||
})
|
||||
.middlewares(singleUploadMiddleware)
|
||||
.callback(async ({ pb, req }) => {
|
||||
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { WalletSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WalletIncomeExpensesSummarySchema } from "../schema";
|
||||
import * as UtilsService from "../services/utils.service";
|
||||
|
||||
const walletUtilsRouter = express.Router();
|
||||
@@ -32,7 +32,7 @@ const getIncomeExpensesSummary = forgeController
|
||||
year: z.string(),
|
||||
month: z.string(),
|
||||
}),
|
||||
response: WalletIncomeExpensesSummarySchema,
|
||||
response: WalletSchemas.WalletIncomeExpensesSummarySchema,
|
||||
})
|
||||
.callback(
|
||||
async ({ pb, query: { year, month } }) =>
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: wallet
|
||||
* Generated at: 2025-07-09T12:50:41.284Z
|
||||
* Contains: wallet__assets, wallet__ledgers, wallet__categories, wallet__transactions, wallet__categories_aggregated, wallet__assets_aggregated, wallet__ledgers_aggregated, wallet__transaction_types_aggregated
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const WalletAssetSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
starting_balance: z.number(),
|
||||
});
|
||||
|
||||
const WalletLedgerSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
});
|
||||
|
||||
const WalletCategorySchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
type: z.enum(["income", "expenses", ""]),
|
||||
});
|
||||
|
||||
const WalletTransactionSchema = z.object({
|
||||
type: z.enum(["income", "expenses", "transfer", ""]),
|
||||
side: z.enum(["debit", "credit", ""]),
|
||||
particulars: z.string(),
|
||||
amount: z.number(),
|
||||
date: z.string(),
|
||||
location_name: z.string(),
|
||||
location_coords: z.object({ lat: z.number(), lon: z.number() }),
|
||||
category: z.string(),
|
||||
asset: z.string(),
|
||||
ledger: z.string(),
|
||||
receipt: z.string(),
|
||||
});
|
||||
|
||||
const WalletCategoryAggregatedSchema = z.object({
|
||||
type: z.enum(["income", "expenses", ""]),
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
color: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
const WalletAssetAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
icon: z.string(),
|
||||
starting_balance: z.number(),
|
||||
amount: z.number(),
|
||||
balance: z.any(),
|
||||
});
|
||||
|
||||
const WalletLedgerAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
color: z.string(),
|
||||
icon: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
const WalletTransactionTypeAggregatedSchema = z.object({
|
||||
name: z.enum(["income", "expenses", "transfer", ""]),
|
||||
amount: z.number(),
|
||||
accumulate: z.any(),
|
||||
});
|
||||
|
||||
type IWalletAsset = z.infer<typeof WalletAssetSchema>;
|
||||
type IWalletLedger = z.infer<typeof WalletLedgerSchema>;
|
||||
type IWalletCategory = z.infer<typeof WalletCategorySchema>;
|
||||
type IWalletTransaction = z.infer<typeof WalletTransactionSchema>;
|
||||
type IWalletCategoryAggregated = z.infer<typeof WalletCategoryAggregatedSchema>;
|
||||
type IWalletAssetAggregated = z.infer<typeof WalletAssetAggregatedSchema>;
|
||||
type IWalletLedgerAggregated = z.infer<typeof WalletLedgerAggregatedSchema>;
|
||||
type IWalletTransactionTypeAggregated = z.infer<
|
||||
typeof WalletTransactionTypeAggregatedSchema
|
||||
>;
|
||||
|
||||
export {
|
||||
WalletAssetSchema,
|
||||
WalletLedgerSchema,
|
||||
WalletCategorySchema,
|
||||
WalletTransactionSchema,
|
||||
WalletCategoryAggregatedSchema,
|
||||
WalletAssetAggregatedSchema,
|
||||
WalletLedgerAggregatedSchema,
|
||||
WalletTransactionTypeAggregatedSchema,
|
||||
};
|
||||
|
||||
export type {
|
||||
IWalletAsset,
|
||||
IWalletLedger,
|
||||
IWalletCategory,
|
||||
IWalletTransaction,
|
||||
IWalletCategoryAggregated,
|
||||
IWalletAssetAggregated,
|
||||
IWalletLedgerAggregated,
|
||||
IWalletTransactionTypeAggregated,
|
||||
};
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
const WalletIncomeExpensesSummarySchema = z.object({
|
||||
totalIncome: z.number(),
|
||||
totalExpenses: z.number(),
|
||||
monthlyIncome: z.number(),
|
||||
monthlyExpenses: z.number(),
|
||||
});
|
||||
|
||||
const WalletReceiptScanResultSchema = z.object({
|
||||
date: z.string(),
|
||||
particulars: z.string(),
|
||||
type: z.string(),
|
||||
amount: z.number(),
|
||||
});
|
||||
|
||||
type IWalletIncomeExpensesSummary = z.infer<
|
||||
typeof WalletIncomeExpensesSummarySchema
|
||||
>;
|
||||
type IWalletReceiptScanResult = z.infer<typeof WalletReceiptScanResultSchema>;
|
||||
|
||||
export { WalletIncomeExpensesSummarySchema, WalletReceiptScanResultSchema };
|
||||
export type { IWalletIncomeExpensesSummary, IWalletReceiptScanResult };
|
||||
@@ -1,15 +1,18 @@
|
||||
import Moment from "moment";
|
||||
import MomentRange from "moment-range";
|
||||
import PocketBase from "pocketbase";
|
||||
import { WalletSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IWalletAsset, IWalletTransaction } from "../schema";
|
||||
|
||||
export const getAllAssets = (pb: PocketBase): Promise<WithPB<IWalletAsset>[]> =>
|
||||
pb.collection("wallet__assets_aggregated").getFullList<WithPB<IWalletAsset>>({
|
||||
sort: "name",
|
||||
});
|
||||
export const getAllAssets = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<WalletSchemas.IAssetAggregated>[]> =>
|
||||
pb
|
||||
.collection("wallet__assets_aggregated")
|
||||
.getFullList<WithPB<WalletSchemas.IAssetAggregated>>({
|
||||
sort: "name",
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
const moment = MomentRange.extendMoment(Moment);
|
||||
@@ -20,13 +23,15 @@ export const getAssetAccumulatedBalance = async (
|
||||
): Promise<Record<string, number>> => {
|
||||
const asset = await pb
|
||||
.collection("wallet__assets")
|
||||
.getOne<Pick<WithPB<IWalletAsset>, "starting_balance">>(assetId, {
|
||||
.getOne<Pick<WithPB<WalletSchemas.IAsset>, "starting_balance">>(assetId, {
|
||||
fields: "starting_balance",
|
||||
});
|
||||
|
||||
const allTransactions = await pb
|
||||
.collection("wallet__transactions")
|
||||
.getFullList<Pick<WithPB<IWalletTransaction>, "amount" | "date" | "side">>({
|
||||
.getFullList<
|
||||
Pick<WithPB<WalletSchemas.ITransaction>, "amount" | "date" | "side">
|
||||
>({
|
||||
filter: `asset = '${assetId}'`,
|
||||
sort: "-date",
|
||||
fields: "amount,date,side",
|
||||
@@ -63,16 +68,18 @@ export const getAssetAccumulatedBalance = async (
|
||||
|
||||
export const createAsset = (
|
||||
pb: PocketBase,
|
||||
data: Pick<IWalletAsset, "name" | "icon" | "starting_balance">,
|
||||
): Promise<WithPB<IWalletAsset>> =>
|
||||
pb.collection("wallet__assets").create<WithPB<IWalletAsset>>(data);
|
||||
data: Pick<WalletSchemas.IAsset, "name" | "icon" | "starting_balance">,
|
||||
): Promise<WithPB<WalletSchemas.IAsset>> =>
|
||||
pb.collection("wallet__assets").create<WithPB<WalletSchemas.IAsset>>(data);
|
||||
|
||||
export const updateAsset = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: Pick<IWalletAsset, "name" | "icon" | "starting_balance">,
|
||||
): Promise<WithPB<IWalletAsset>> =>
|
||||
pb.collection("wallet__assets").update<WithPB<IWalletAsset>>(id, data);
|
||||
data: Pick<WalletSchemas.IAsset, "name" | "icon" | "starting_balance">,
|
||||
): Promise<WithPB<WalletSchemas.IAsset>> =>
|
||||
pb
|
||||
.collection("wallet__assets")
|
||||
.update<WithPB<WalletSchemas.IAsset>>(id, data);
|
||||
|
||||
export const deleteAsset = async (
|
||||
pb: PocketBase,
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { WalletSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IWalletCategory } from "../schema";
|
||||
|
||||
export const getAllCategories = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<IWalletCategory>[]> =>
|
||||
): Promise<WithPB<WalletSchemas.ICategoryAggregated>[]> =>
|
||||
pb
|
||||
.collection("wallet__categories_aggregated")
|
||||
.getFullList<WithPB<IWalletCategory>>({
|
||||
.getFullList<WithPB<WalletSchemas.ICategoryAggregated>>({
|
||||
sort: "name",
|
||||
});
|
||||
|
||||
export const createCategory = (
|
||||
pb: PocketBase,
|
||||
data: Omit<IWalletCategory, "amount">,
|
||||
): Promise<WithPB<IWalletCategory>> =>
|
||||
pb.collection("wallet__categories").create<WithPB<IWalletCategory>>(data);
|
||||
data: Omit<WalletSchemas.ICategory, "amount">,
|
||||
): Promise<WithPB<WalletSchemas.ICategory>> =>
|
||||
pb
|
||||
.collection("wallet__categories")
|
||||
.create<WithPB<WalletSchemas.ICategory>>(data);
|
||||
|
||||
export const updateCategory = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: Omit<IWalletCategory, "amount">,
|
||||
): Promise<WithPB<IWalletCategory>> =>
|
||||
pb.collection("wallet__categories").update<WithPB<IWalletCategory>>(id, data);
|
||||
data: Omit<WalletSchemas.ICategory, "amount">,
|
||||
): Promise<WithPB<WalletSchemas.ICategory>> =>
|
||||
pb
|
||||
.collection("wallet__categories")
|
||||
.update<WithPB<WalletSchemas.ICategory>>(id, data);
|
||||
|
||||
export const deleteCategory = async (
|
||||
pb: PocketBase,
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { WalletSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IWalletLedger } from "../schema";
|
||||
|
||||
export const getAllLedgers = (
|
||||
pb: PocketBase,
|
||||
): Promise<WithPB<IWalletLedger>[]> =>
|
||||
): Promise<WithPB<WalletSchemas.ILedger>[]> =>
|
||||
pb
|
||||
.collection("wallet__ledgers_aggregated")
|
||||
.getFullList<WithPB<IWalletLedger>>({
|
||||
.getFullList<WithPB<WalletSchemas.ILedger>>({
|
||||
sort: "name",
|
||||
});
|
||||
|
||||
export const createLedger = (
|
||||
pb: PocketBase,
|
||||
data: Omit<IWalletLedger, "amount">,
|
||||
): Promise<WithPB<IWalletLedger>> =>
|
||||
pb.collection("wallet__ledgers").create<WithPB<IWalletLedger>>(data);
|
||||
data: Omit<WalletSchemas.ILedger, "amount">,
|
||||
): Promise<WithPB<WalletSchemas.ILedger>> =>
|
||||
pb.collection("wallet__ledgers").create<WithPB<WalletSchemas.ILedger>>(data);
|
||||
|
||||
export const updateLedger = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: Omit<IWalletLedger, "amount">,
|
||||
): Promise<WithPB<IWalletLedger>> =>
|
||||
pb.collection("wallet__ledgers").update<WithPB<IWalletLedger>>(id, data);
|
||||
data: Omit<WalletSchemas.ILedger, "amount">,
|
||||
): Promise<WithPB<WalletSchemas.ILedger>> =>
|
||||
pb
|
||||
.collection("wallet__ledgers")
|
||||
.update<WithPB<WalletSchemas.ILedger>>(id, data);
|
||||
|
||||
export const deleteLedger = async (
|
||||
pb: PocketBase,
|
||||
|
||||
@@ -3,12 +3,11 @@ import parseOCR from "@functions/parseOCR";
|
||||
import fs from "fs";
|
||||
import { fromPath } from "pdf2pic";
|
||||
import Pocketbase from "pocketbase";
|
||||
import { WalletSchemas } from "shared";
|
||||
import { z } from "zod";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IWalletReceiptScanResult, IWalletTransaction } from "../schema";
|
||||
|
||||
function convertPDFToImage(path: string): Promise<File | undefined> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
@@ -53,16 +52,19 @@ function convertPDFToImage(path: string): Promise<File | undefined> {
|
||||
|
||||
export const getAllTransactions = (
|
||||
pb: Pocketbase,
|
||||
): Promise<WithPB<IWalletTransaction>[]> =>
|
||||
): Promise<WithPB<WalletSchemas.ITransaction>[]> =>
|
||||
pb
|
||||
.collection("wallet__transactions")
|
||||
.getFullList<WithPB<IWalletTransaction>>({
|
||||
.getFullList<WithPB<WalletSchemas.ITransaction>>({
|
||||
sort: "-date,-created",
|
||||
});
|
||||
|
||||
export const createTransaction = async (
|
||||
pb: Pocketbase,
|
||||
data: Pick<IWalletTransaction, "particulars" | "date" | "amount" | "type"> & {
|
||||
data: Pick<
|
||||
WalletSchemas.ITransaction,
|
||||
"particulars" | "date" | "amount" | "type"
|
||||
> & {
|
||||
asset?: string;
|
||||
ledger?: string;
|
||||
category?: string;
|
||||
@@ -75,7 +77,7 @@ export const createTransaction = async (
|
||||
};
|
||||
},
|
||||
file: Express.Multer.File | undefined,
|
||||
): Promise<WithPB<IWalletTransaction>[]> => {
|
||||
): Promise<WithPB<WalletSchemas.ITransaction>[]> => {
|
||||
async function processFile(): Promise<
|
||||
[Express.Multer.File | File | undefined, string | undefined]
|
||||
> {
|
||||
@@ -110,10 +112,10 @@ export const createTransaction = async (
|
||||
}
|
||||
|
||||
async function createIncomeOrExpensesTransactions(): Promise<
|
||||
WithPB<IWalletTransaction>[]
|
||||
WithPB<WalletSchemas.ITransaction>[]
|
||||
> {
|
||||
const newData: Omit<
|
||||
IWalletTransaction,
|
||||
WalletSchemas.ITransaction,
|
||||
"receipt" | "fromAsset" | "toAsset"
|
||||
> & {
|
||||
receipt: File | string;
|
||||
@@ -133,19 +135,19 @@ export const createTransaction = async (
|
||||
|
||||
const transaction = await pb
|
||||
.collection("wallet__transactions")
|
||||
.create<WithPB<IWalletTransaction>>(newData);
|
||||
.create<WithPB<WalletSchemas.ITransaction>>(newData);
|
||||
|
||||
return [transaction];
|
||||
}
|
||||
|
||||
async function createTransferTransactions(): Promise<
|
||||
WithPB<IWalletTransaction>[]
|
||||
WithPB<WalletSchemas.ITransaction>[]
|
||||
> {
|
||||
const _from = await pb.collection("wallet__assets").getOne(fromAsset!);
|
||||
const _to = await pb.collection("wallet__assets").getOne(toAsset!);
|
||||
|
||||
const baseTransferData: Omit<
|
||||
IWalletTransaction,
|
||||
WalletSchemas.ITransaction,
|
||||
| "receipt"
|
||||
| "category"
|
||||
| "ledger"
|
||||
@@ -169,14 +171,14 @@ export const createTransaction = async (
|
||||
baseTransferData.asset = toAsset!;
|
||||
const debit = await pb
|
||||
.collection("wallet__transactions")
|
||||
.create<WithPB<IWalletTransaction>>(baseTransferData);
|
||||
.create<WithPB<WalletSchemas.ITransaction>>(baseTransferData);
|
||||
|
||||
baseTransferData.particulars = `Transfer to ${_to.name}`;
|
||||
baseTransferData.side = "credit";
|
||||
baseTransferData.asset = fromAsset!;
|
||||
const credit = await pb
|
||||
.collection("wallet__transactions")
|
||||
.create<WithPB<IWalletTransaction>>(baseTransferData);
|
||||
.create<WithPB<WalletSchemas.ITransaction>>(baseTransferData);
|
||||
|
||||
return [debit, credit];
|
||||
}
|
||||
@@ -195,7 +197,7 @@ export const createTransaction = async (
|
||||
|
||||
let [targetFile, fileName] = await processFile();
|
||||
|
||||
let created: WithPB<IWalletTransaction>[] = [];
|
||||
let created: WithPB<WalletSchemas.ITransaction>[] = [];
|
||||
|
||||
switch (type) {
|
||||
case "income":
|
||||
@@ -217,7 +219,10 @@ export const createTransaction = async (
|
||||
export const updateTransaction = async (
|
||||
pb: Pocketbase,
|
||||
id: string,
|
||||
data: Pick<IWalletTransaction, "particulars" | "date" | "amount" | "type"> & {
|
||||
data: Pick<
|
||||
WalletSchemas.ITransaction,
|
||||
"particulars" | "date" | "amount" | "type"
|
||||
> & {
|
||||
asset?: string;
|
||||
ledger?: string;
|
||||
category?: string;
|
||||
@@ -229,7 +234,7 @@ export const updateTransaction = async (
|
||||
},
|
||||
file: Express.Multer.File | undefined,
|
||||
toRemoveReceipt: boolean,
|
||||
): Promise<WithPB<IWalletTransaction>> => {
|
||||
): Promise<WithPB<WalletSchemas.ITransaction>> => {
|
||||
async function processFile(): Promise<
|
||||
[Express.Multer.File | File | undefined, string | undefined]
|
||||
> {
|
||||
@@ -281,7 +286,7 @@ export const updateTransaction = async (
|
||||
|
||||
const foundTransaction = await pb
|
||||
.collection("wallet__transactions")
|
||||
.getOne<WithPB<IWalletTransaction>>(id);
|
||||
.getOne<WithPB<WalletSchemas.ITransaction>>(id);
|
||||
|
||||
const [targetFile, fileName] = await processFile();
|
||||
|
||||
@@ -292,7 +297,7 @@ export const updateTransaction = async (
|
||||
}
|
||||
|
||||
const updatedData: Omit<
|
||||
IWalletTransaction,
|
||||
WalletSchemas.ITransaction,
|
||||
"receipt" | "fromAsset" | "toAsset"
|
||||
> & {
|
||||
receipt: File | string;
|
||||
@@ -312,7 +317,7 @@ export const updateTransaction = async (
|
||||
|
||||
const transaction = await pb
|
||||
.collection("wallet__transactions")
|
||||
.update<WithPB<IWalletTransaction>>(id, updatedData);
|
||||
.update<WithPB<WalletSchemas.ITransaction>>(id, updatedData);
|
||||
|
||||
if (file && fs.existsSync(file.path)) {
|
||||
fs.unlinkSync(file.path);
|
||||
@@ -331,8 +336,8 @@ export const deleteTransaction = async (
|
||||
export const scanReceipt = async (
|
||||
pb: Pocketbase,
|
||||
file: Express.Multer.File,
|
||||
): Promise<IWalletReceiptScanResult> => {
|
||||
async function getTransactionDetails(): Promise<IWalletReceiptScanResult> {
|
||||
): Promise<WalletSchemas.IWalletReceiptScanResult> => {
|
||||
async function getTransactionDetails(): Promise<WalletSchemas.IWalletReceiptScanResult> {
|
||||
const TransactionDetails = z.object({
|
||||
date: z.string(),
|
||||
particulars: z.string(),
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import moment from "moment";
|
||||
import Pocketbase from "pocketbase";
|
||||
import { WalletSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import {
|
||||
IWalletCategory,
|
||||
IWalletIncomeExpensesSummary,
|
||||
IWalletTransaction,
|
||||
IWalletTransactionTypeAggregated,
|
||||
} from "../schema";
|
||||
|
||||
export const getTypesCount = async (
|
||||
pb: Pocketbase,
|
||||
): Promise<{
|
||||
@@ -20,7 +14,7 @@ export const getTypesCount = async (
|
||||
}> => {
|
||||
const types = await pb
|
||||
.collection("wallet__transaction_types_aggregated")
|
||||
.getFullList<WithPB<IWalletTransactionTypeAggregated>>();
|
||||
.getFullList<WithPB<WalletSchemas.ITransactionTypeAggregated>>();
|
||||
|
||||
const typesCount: {
|
||||
[key: string]: {
|
||||
@@ -43,7 +37,7 @@ export const getIncomeExpensesSummary = async (
|
||||
pb: Pocketbase,
|
||||
year: string,
|
||||
month: string,
|
||||
): Promise<IWalletIncomeExpensesSummary> => {
|
||||
): Promise<WalletSchemas.IWalletIncomeExpensesSummary> => {
|
||||
const start = moment(`${year}-${month}-01`)
|
||||
.startOf("month")
|
||||
.format("YYYY-MM-DD");
|
||||
@@ -51,7 +45,7 @@ export const getIncomeExpensesSummary = async (
|
||||
|
||||
const transactions = await pb
|
||||
.collection("wallet__transactions")
|
||||
.getFullList<WithPB<IWalletTransaction>>({
|
||||
.getFullList<WithPB<WalletSchemas.ITransaction>>({
|
||||
filter: "type = 'income' || type = 'expenses'",
|
||||
sort: "-date,-created",
|
||||
});
|
||||
@@ -130,8 +124,8 @@ export const getExpensesBreakdown = async (
|
||||
.format("YYYY-MM-DD");
|
||||
|
||||
const expenses = await pb.collection("wallet__transactions").getFullList<
|
||||
WithPB<IWalletTransaction> & {
|
||||
expand?: { category: WithPB<IWalletCategory> };
|
||||
WithPB<WalletSchemas.ITransaction> & {
|
||||
expand?: { category: WithPB<WalletSchemas.ICategory> };
|
||||
}
|
||||
>({
|
||||
filter: `date >= '${startDate}' && date <= '${endDate}' && type = 'expenses'`,
|
||||
|
||||
@@ -4,13 +4,13 @@ import {
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import fs from "fs";
|
||||
import { WishlistSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { singleUploadMiddleware } from "@middlewares/uploadMiddleware";
|
||||
|
||||
import { WishlistEntrySchema } from "../schema";
|
||||
import * as entriesService from "../services/entries.service";
|
||||
|
||||
const wishlistEntriesRouter = express.Router();
|
||||
@@ -36,7 +36,7 @@ const getEntriesByListId = forgeController
|
||||
.optional()
|
||||
.transform((val) => val === "true"),
|
||||
}),
|
||||
response: z.array(WithPBSchema(WishlistEntrySchema)),
|
||||
response: z.array(WithPBSchema(WishlistSchemas.EntrySchema)),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "wishlist__lists",
|
||||
@@ -72,7 +72,7 @@ const createEntry = forgeController
|
||||
list: z.string(),
|
||||
image: z.any().optional(),
|
||||
}),
|
||||
response: WithPBSchema(WishlistEntrySchema),
|
||||
response: WithPBSchema(WishlistSchemas.EntrySchema),
|
||||
})
|
||||
.middlewares(singleUploadMiddleware)
|
||||
.existenceCheck("body", {
|
||||
@@ -118,7 +118,7 @@ const updateEntry = forgeController
|
||||
imageRemoved: z.string().optional(),
|
||||
}),
|
||||
response: z.union([
|
||||
WithPBSchema(WishlistEntrySchema),
|
||||
WithPBSchema(WishlistSchemas.EntrySchema),
|
||||
z.literal("removed"),
|
||||
]),
|
||||
})
|
||||
@@ -170,7 +170,7 @@ const updateEntryBoughtStatus = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(WishlistEntrySchema),
|
||||
response: WithPBSchema(WishlistSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "wishlist__entries",
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { WishlistSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { WishlistListSchema } from "../schema";
|
||||
import * as listsService from "../services/lists.service";
|
||||
|
||||
const wishlistListsRouter = express.Router();
|
||||
@@ -19,7 +19,7 @@ const getList = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(WishlistListSchema),
|
||||
response: WithPBSchema(WishlistSchemas.ListAggregatedSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "wishlist__lists",
|
||||
@@ -46,16 +46,7 @@ const getAllLists = forgeController
|
||||
.route("GET /")
|
||||
.description("Get all wishlists with statistics")
|
||||
.schema({
|
||||
response: z.array(
|
||||
WithPBSchema(
|
||||
WishlistListSchema.extend({
|
||||
total_count: z.number(),
|
||||
bought_count: z.number(),
|
||||
total_amount: z.number(),
|
||||
bought_amount: z.number(),
|
||||
}),
|
||||
),
|
||||
),
|
||||
response: z.array(WithPBSchema(WishlistSchemas.ListAggregatedSchema)),
|
||||
})
|
||||
.callback(async ({ pb }) => await listsService.getAllLists(pb));
|
||||
|
||||
@@ -63,8 +54,8 @@ const createList = forgeController
|
||||
.route("POST /")
|
||||
.description("Create a new wishlist")
|
||||
.schema({
|
||||
body: WishlistListSchema,
|
||||
response: WithPBSchema(WishlistListSchema),
|
||||
body: WishlistSchemas.ListSchema,
|
||||
response: WithPBSchema(WishlistSchemas.ListSchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(async ({ pb, body }) => await listsService.createList(pb, body));
|
||||
@@ -76,8 +67,8 @@ const updateList = forgeController
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
body: WishlistListSchema,
|
||||
response: WithPBSchema(WishlistListSchema),
|
||||
body: WishlistSchemas.ListSchema,
|
||||
response: WithPBSchema(WishlistSchemas.ListSchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "wishlist__lists",
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: wishlist
|
||||
* Generated at: 2025-07-09T12:50:41.285Z
|
||||
* Contains: wishlist__lists, wishlist__entries, wishlist__lists_aggregated
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const WishlistListSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
color: z.string(),
|
||||
icon: z.string(),
|
||||
});
|
||||
|
||||
const WishlistEntrySchema = z.object({
|
||||
name: z.string(),
|
||||
url: z.string(),
|
||||
price: z.number(),
|
||||
image: z.string(),
|
||||
list: z.string(),
|
||||
bought: z.boolean(),
|
||||
bought_at: z.string(),
|
||||
});
|
||||
|
||||
const WishlistListAggregatedSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
color: z.string(),
|
||||
icon: z.string(),
|
||||
total_count: z.number(),
|
||||
total_amount: z.any(),
|
||||
bought_count: z.number(),
|
||||
bought_amount: z.any(),
|
||||
});
|
||||
|
||||
type IWishlistList = z.infer<typeof WishlistListSchema>;
|
||||
type IWishlistEntry = z.infer<typeof WishlistEntrySchema>;
|
||||
type IWishlistListAggregated = z.infer<typeof WishlistListAggregatedSchema>;
|
||||
|
||||
export {
|
||||
WishlistListSchema,
|
||||
WishlistEntrySchema,
|
||||
WishlistListAggregatedSchema,
|
||||
};
|
||||
|
||||
export type { IWishlistList, IWishlistEntry, IWishlistListAggregated };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
// Add your custom schemas here. They will not be overwritten by this script.
|
||||
@@ -1,9 +1,9 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { WishlistSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import scrapeProviders from "../helpers/scrapers/index";
|
||||
import { IWishlistEntry } from "../schema";
|
||||
|
||||
export const getCollectionId = (pb: PocketBase): string =>
|
||||
pb.collection("wishlist__entries").collectionIdOrName;
|
||||
@@ -12,12 +12,14 @@ export const getEntriesByListId = (
|
||||
pb: PocketBase,
|
||||
listId: string,
|
||||
bought?: boolean,
|
||||
): Promise<WithPB<IWishlistEntry>[]> =>
|
||||
pb.collection("wishlist__entries").getFullList<WithPB<IWishlistEntry>>({
|
||||
filter: `list = "${listId}" ${
|
||||
typeof bought !== "undefined" ? `&& bought = ${bought}` : ""
|
||||
}`,
|
||||
});
|
||||
): Promise<WithPB<WishlistSchemas.IEntry>[]> =>
|
||||
pb
|
||||
.collection("wishlist__entries")
|
||||
.getFullList<WithPB<WishlistSchemas.IEntry>>({
|
||||
filter: `list = "${listId}" ${
|
||||
typeof bought !== "undefined" ? `&& bought = ${bought}` : ""
|
||||
}`,
|
||||
});
|
||||
|
||||
export const scrapeExternal = async (
|
||||
pb: PocketBase,
|
||||
@@ -41,11 +43,11 @@ export const scrapeExternal = async (
|
||||
|
||||
export const createEntry = async (
|
||||
pb: PocketBase,
|
||||
data: Omit<IWishlistEntry, "image" | "bought_at"> & { image?: File },
|
||||
): Promise<WithPB<IWishlistEntry>> => {
|
||||
data: Omit<WishlistSchemas.IEntry, "image" | "bought_at"> & { image?: File },
|
||||
): Promise<WithPB<WishlistSchemas.IEntry>> => {
|
||||
const entry = await pb
|
||||
.collection("wishlist__entries")
|
||||
.create<WithPB<IWishlistEntry>>(data);
|
||||
.create<WithPB<WishlistSchemas.IEntry>>(data);
|
||||
|
||||
return entry;
|
||||
};
|
||||
@@ -60,10 +62,10 @@ export const updateEntry = async (
|
||||
price: number;
|
||||
image?: File | null;
|
||||
},
|
||||
): Promise<WithPB<IWishlistEntry>> => {
|
||||
): Promise<WithPB<WishlistSchemas.IEntry>> => {
|
||||
const entry = await pb
|
||||
.collection("wishlist__entries")
|
||||
.update<WithPB<IWishlistEntry>>(id, data);
|
||||
.update<WithPB<WishlistSchemas.IEntry>>(id, data);
|
||||
|
||||
return entry;
|
||||
};
|
||||
@@ -71,23 +73,23 @@ export const updateEntry = async (
|
||||
export const getEntry = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<IWishlistEntry>> => {
|
||||
): Promise<WithPB<WishlistSchemas.IEntry>> => {
|
||||
return await pb
|
||||
.collection("wishlist__entries")
|
||||
.getOne<WithPB<IWishlistEntry>>(id);
|
||||
.getOne<WithPB<WishlistSchemas.IEntry>>(id);
|
||||
};
|
||||
|
||||
export const updateEntryBoughtStatus = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<WithPB<IWishlistEntry>> => {
|
||||
): Promise<WithPB<WishlistSchemas.IEntry>> => {
|
||||
const oldEntry = await pb
|
||||
.collection("wishlist__entries")
|
||||
.getOne<WithPB<IWishlistEntry>>(id);
|
||||
.getOne<WithPB<WishlistSchemas.IEntry>>(id);
|
||||
|
||||
const entry = await pb
|
||||
.collection("wishlist__entries")
|
||||
.update<WithPB<IWishlistEntry>>(id, {
|
||||
.update<WithPB<WishlistSchemas.IEntry>>(id, {
|
||||
bought: !oldEntry.bought,
|
||||
bought_at: oldEntry.bought ? null : new Date().toISOString(),
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import PocketBase from "pocketbase";
|
||||
import { WishlistSchemas } from "shared";
|
||||
|
||||
import { WithPB } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { IWishlistList } from "../schema";
|
||||
|
||||
export const checkListExists = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
@@ -19,64 +18,34 @@ export const checkListExists = async (
|
||||
export const getList = (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
): Promise<
|
||||
WithPB<
|
||||
IWishlistList & {
|
||||
total_count: number;
|
||||
bought_count: number;
|
||||
total_amount: number;
|
||||
bought_amount: number;
|
||||
}
|
||||
>
|
||||
> =>
|
||||
pb.collection("wishlist__lists_aggregated").getOne<
|
||||
WithPB<
|
||||
IWishlistList & {
|
||||
total_count: number;
|
||||
bought_count: number;
|
||||
total_amount: number;
|
||||
bought_amount: number;
|
||||
}
|
||||
>
|
||||
>(id);
|
||||
): Promise<WithPB<WishlistSchemas.IListAggregated>> =>
|
||||
pb
|
||||
.collection("wishlist__lists_aggregated")
|
||||
.getOne<WithPB<WishlistSchemas.IListAggregated>>(id);
|
||||
|
||||
export const getAllLists = (
|
||||
pb: PocketBase,
|
||||
): Promise<
|
||||
WithPB<
|
||||
IWishlistList & {
|
||||
total_count: number;
|
||||
bought_count: number;
|
||||
total_amount: number;
|
||||
bought_amount: number;
|
||||
}
|
||||
>[]
|
||||
> =>
|
||||
pb.collection("wishlist__lists_aggregated").getFullList<
|
||||
WithPB<
|
||||
IWishlistList & {
|
||||
total_count: number;
|
||||
bought_count: number;
|
||||
total_amount: number;
|
||||
bought_amount: number;
|
||||
}
|
||||
>
|
||||
>({
|
||||
sort: "name",
|
||||
});
|
||||
): Promise<WithPB<WishlistSchemas.IListAggregated>[]> =>
|
||||
pb
|
||||
.collection("wishlist__lists_aggregated")
|
||||
.getFullList<WithPB<WishlistSchemas.IListAggregated>>({
|
||||
sort: "name",
|
||||
});
|
||||
|
||||
export const createList = (
|
||||
pb: PocketBase,
|
||||
data: IWishlistList,
|
||||
): Promise<WithPB<IWishlistList>> =>
|
||||
pb.collection("wishlist__lists").create<WithPB<IWishlistList>>(data);
|
||||
data: WishlistSchemas.IList,
|
||||
): Promise<WithPB<WishlistSchemas.IList>> =>
|
||||
pb.collection("wishlist__lists").create<WithPB<WishlistSchemas.IList>>(data);
|
||||
|
||||
export const updateList = async (
|
||||
pb: PocketBase,
|
||||
id: string,
|
||||
data: IWishlistList,
|
||||
): Promise<WithPB<IWishlistList>> =>
|
||||
pb.collection("wishlist__lists").update<WithPB<IWishlistList>>(id, data);
|
||||
data: WishlistSchemas.IList,
|
||||
): Promise<WithPB<WishlistSchemas.IList>> =>
|
||||
pb
|
||||
.collection("wishlist__lists")
|
||||
.update<WithPB<WishlistSchemas.IList>>(id, data);
|
||||
|
||||
export const deleteList = async (pb: PocketBase, id: string): Promise<void> => {
|
||||
await pb.collection("wishlist__lists").delete(id);
|
||||
|
||||
@@ -4,11 +4,11 @@ import {
|
||||
forgeController,
|
||||
} from "@functions/forgeController";
|
||||
import express from "express";
|
||||
import { ApiKeysSchemas } from "shared";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { WithPBSchema } from "@typescript/pocketbase_interfaces";
|
||||
|
||||
import { ApiKeysEntrySchema } from "../schema";
|
||||
import { challenge } from "../services/auth.service";
|
||||
import getDecryptedMaster, * as entriesService from "../services/entries.service";
|
||||
|
||||
@@ -21,7 +21,7 @@ const getAllEntries = forgeController
|
||||
query: z.object({
|
||||
master: z.string(),
|
||||
}),
|
||||
response: z.array(WithPBSchema(ApiKeysEntrySchema)),
|
||||
response: z.array(WithPBSchema(ApiKeysSchemas.EntrySchema)),
|
||||
})
|
||||
.callback(async ({ pb, query: { master } }) => {
|
||||
await getDecryptedMaster(pb, decodeURIComponent(master));
|
||||
@@ -71,7 +71,7 @@ const createEntry = forgeController
|
||||
body: z.object({
|
||||
data: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(ApiKeysEntrySchema),
|
||||
response: WithPBSchema(ApiKeysSchemas.EntrySchema),
|
||||
})
|
||||
.statusCode(201)
|
||||
.callback(async ({ pb, body: { data } }) => {
|
||||
@@ -100,7 +100,7 @@ const updateEntry = forgeController
|
||||
body: z.object({
|
||||
data: z.string(),
|
||||
}),
|
||||
response: WithPBSchema(ApiKeysEntrySchema),
|
||||
response: WithPBSchema(ApiKeysSchemas.EntrySchema),
|
||||
})
|
||||
.existenceCheck("params", {
|
||||
id: "api_keys__entries",
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* This file is auto-generated. DO NOT EDIT IT MANUALLY.
|
||||
* If you want to add custom schemas, you will find a dedicated space at the end of this file.
|
||||
* Generated for module: apiKeys
|
||||
* Generated at: 2025-07-09T12:50:41.286Z
|
||||
* Contains: api_keys__entries
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
const ApiKeysEntrySchema = z.object({
|
||||
keyId: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
icon: z.string(),
|
||||
key: z.string(),
|
||||
});
|
||||
|
||||
type IApiKeysEntry = z.infer<typeof ApiKeysEntrySchema>;
|
||||
|
||||
export { ApiKeysEntrySchema };
|
||||
|
||||
export type { IApiKeysEntry };
|
||||
|
||||
// -------------------- CUSTOM SCHEMAS --------------------
|
||||
|
||||
// Add your custom schemas here. They will not be overwritten by this script.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user