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:
melvinchia3636
2025-07-17 21:23:51 +08:00
parent 3f5a91f475
commit 644cf9b8ea
113 changed files with 1087 additions and 1979 deletions

View File

@@ -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')

View File

@@ -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])

View File

@@ -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,

View File

@@ -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]
)

View File

@@ -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'
])

View File

@@ -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 }

View File

@@ -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"
},

View File

@@ -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());

View File

@@ -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.

View File

@@ -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>>();

View File

@@ -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",

View File

@@ -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",

View File

@@ -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));

View File

@@ -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",

View File

@@ -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,

View File

@@ -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 };

View File

@@ -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);

View File

@@ -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());

View File

@@ -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",
});

View File

@@ -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);

View File

@@ -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) => ({

View File

@@ -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;

View 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",

View 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 { 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",

View File

@@ -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",

View File

@@ -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.

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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) {

View File

@@ -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 };

View File

@@ -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}"`,
});

View File

@@ -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",

View File

@@ -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(),
}),

View File

@@ -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 };

View File

@@ -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,
});
};

View File

@@ -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,

View File

@@ -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", {

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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.

View File

@@ -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) => {

View File

@@ -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 ||

View File

@@ -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: "",
});

View File

@@ -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;
})[]

View File

@@ -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,

View File

@@ -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",

View File

@@ -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.

View File

@@ -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,
});

View File

@@ -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) {

View File

@@ -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",

View File

@@ -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",

View File

@@ -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.

View File

@@ -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,
});

View File

@@ -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: "",
});
};

View File

@@ -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(" || "),

View File

@@ -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",

View File

@@ -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),

View File

@@ -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 };

View File

@@ -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,

View File

@@ -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,

View File

@@ -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",

View File

@@ -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.

View File

@@ -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,
});
};

View File

@@ -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 } }) =>

View File

@@ -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.

View File

@@ -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))

View File

@@ -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",

View File

@@ -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",

View File

@@ -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", {

View File

@@ -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",

View File

@@ -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 };

View File

@@ -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

View File

@@ -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;
}
>

View File

@@ -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 (

View File

@@ -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> => {

View File

@@ -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",

View File

@@ -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());

View File

@@ -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 };

View File

@@ -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,

View File

@@ -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,

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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 }) => {

View File

@@ -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 } }) =>

View File

@@ -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 };

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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(),

View File

@@ -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'`,

View File

@@ -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",

View File

@@ -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",

View File

@@ -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.

View File

@@ -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(),
});

View File

@@ -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);

View File

@@ -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",

View File

@@ -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