WIP: feat: Create new db app #32
@ -9,7 +9,8 @@ This is a minimal [Blitz.js](https://github.com/blitz-js/blitz) app.
|
||||
Run your app in the development mode.
|
||||
|
||||
```
|
||||
blitz dev
|
||||
yarn blitz p generate
|
||||
yarn blitz dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
13
db/app/api/greetingsQueue.ts
Normal file
13
db/app/api/greetingsQueue.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Queue } from "quirrel/blitz"
|
||||
|
||||
export interface Greetings {
|
||||
to: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export default Queue(
|
||||
"api/greetingsQueue", // the path of this API route
|
||||
async ({ to, message }: Greetings) => {
|
||||
console.log(`Greetings, ${to}! Thy ears shall hear: "${message}"`)
|
||||
}
|
||||
)
|
34
db/app/api/items/refresh.ts
Normal file
34
db/app/api/items/refresh.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { CronJob } from "quirrel/blitz"
|
||||
import { PrismaClient, Item } from '@prisma/client';
|
||||
import { RawItemData } from '../../core/dto/rawData/RawItemData';
|
||||
import { propsToClassKey } from "@mui/styles";
|
||||
|
||||
export default CronJob(
|
||||
"api/items/refresh", // the path of this API route
|
||||
"@daily", // cron schedule (see https://crontab.guru)
|
||||
async () => {
|
||||
const itemsResponse = await fetch('https://dev.sp-tarkov.com/SPT-AKI/Server/raw/branch/development/project/assets/database/templates/items.json');
|
||||
if (itemsResponse.status >= 300) {
|
||||
throw Error(`Could not retrieve items from Gitea: code ${itemsResponse.status}`)
|
||||
}
|
||||
const items: { [id: string]: RawItemData } = await itemsResponse.json();
|
||||
const prisma: PrismaClient = new PrismaClient();
|
||||
|
||||
const promises: any[] = []
|
||||
|
||||
Object.entries(items).forEach(
|
||||
async ([key, value]) => {
|
||||
const {props, ...data} = RawItemData.fromRawData(value).toItemData();
|
||||
promises.push(await prisma.item.upsert({
|
||||
create: {props, ...data},
|
||||
update: data,
|
||||
where: {
|
||||
internalId: data.internalId
|
||||
},
|
||||
}))
|
||||
}
|
||||
);
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
)
|
53
db/app/core/components/Form.tsx
Normal file
53
db/app/core/components/Form.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import { ReactNode, PropsWithoutRef } from "react"
|
||||
import { Form as FinalForm, FormProps as FinalFormProps } from "react-final-form"
|
||||
import { z } from "zod"
|
||||
import { validateZodSchema } from "blitz"
|
||||
export { FORM_ERROR } from "final-form"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
submitText?: string
|
||||
schema?: S
|
||||
onSubmit: FinalFormProps<z.infer<S>>["onSubmit"]
|
||||
initialValues?: FinalFormProps<z.infer<S>>["initialValues"]
|
||||
}
|
||||
|
||||
export function Form<S extends z.ZodType<any, any>>({
|
||||
children,
|
||||
submitText,
|
||||
schema,
|
||||
initialValues,
|
||||
onSubmit,
|
||||
...props
|
||||
}: FormProps<S>) {
|
||||
return (
|
||||
<FinalForm
|
||||
initialValues={initialValues}
|
||||
validate={validateZodSchema(schema)}
|
||||
onSubmit={onSubmit}
|
||||
render={({ handleSubmit, submitting, submitError }) => (
|
||||
<form onSubmit={handleSubmit} className="form" {...props}>
|
||||
{/* Form fields supplied as children are rendered here */}
|
||||
{children}
|
||||
|
||||
{submitError && (
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{submitError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{submitText && (
|
||||
<button type="submit" disabled={submitting}>
|
||||
{submitText}
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Form
|
@ -3,4 +3,57 @@ export interface ItemData {
|
||||
_name: string
|
||||
_parent?: string
|
||||
_type?: string
|
||||
_props: {
|
||||
Name: string
|
||||
ShortName: string
|
||||
Description: string
|
||||
Weight: number
|
||||
BackgroundColor: string
|
||||
Width: number
|
||||
Height: number
|
||||
StackMaxSize: number
|
||||
ItemSound: string
|
||||
Prefab: {
|
||||
path: string
|
||||
rcid: string
|
||||
}
|
||||
UsePrefab: {
|
||||
path: string
|
||||
rcid: string
|
||||
}
|
||||
StackObjectsCount: number
|
||||
NotShownInSlot: boolean
|
||||
ExaminedByDefault: boolean
|
||||
ExamineTime: number
|
||||
IsUndiscardable: boolean
|
||||
IsUnsaleable: boolean
|
||||
IsUnbuyable: boolean
|
||||
IsUngivable: boolean
|
||||
IsLockedafterEquip: boolean
|
||||
QuestItem: boolean
|
||||
LootExperience: number
|
||||
ExamineExperience: number
|
||||
HideEntrails: boolean
|
||||
RepairCost: number
|
||||
RepairSpeed: number
|
||||
ExtraSizeLeft: number
|
||||
ExtraSizeRight: number
|
||||
ExtraSizeUp: number
|
||||
ExtraSizeDown: number
|
||||
ExtraSizeForceAdd: boolean
|
||||
MergesWithChildren: boolean
|
||||
CanSellOnRagfair: boolean
|
||||
CanRequireOnRagfair: boolean
|
||||
ConflictingItems: string[]
|
||||
Unlootable: boolean
|
||||
UnlootableFromSlot: string
|
||||
UnlootableFromSide: string[]
|
||||
AnimationVariantsNumber: number
|
||||
DiscardingBlock: boolean
|
||||
RagFairCommissionModifier: number
|
||||
IsAlwaysAvailableForInsurance: boolean
|
||||
MaxResource: number
|
||||
Resource: number
|
||||
}
|
||||
_proto?: string
|
||||
}
|
29
db/app/core/dto/rawData/RawItemData.ts
Normal file
29
db/app/core/dto/rawData/RawItemData.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { RawItemProps } from './RawItemProps';
|
||||
|
||||
export class RawItemData {
|
||||
_id: string;
|
||||
_name: string;
|
||||
_parent: string;
|
||||
_type: string;
|
||||
_props: RawItemProps;
|
||||
_proto?: string | undefined;
|
||||
|
||||
static fromRawData(data: any): RawItemData {
|
||||
const rawData = new RawItemData();
|
||||
Object.assign(rawData, data)
|
||||
rawData._props = RawItemProps.fromRawData(data['_props'])
|
||||
|
||||
return rawData;
|
||||
}
|
||||
|
||||
toItemData(): Prisma.XOR<Prisma.ItemCreateInput, Prisma.ItemUncheckedCreateInput> {
|
||||
return {
|
||||
internalId: this._id,
|
||||
name: this._name,
|
||||
type: this._type,
|
||||
props: this._props.toItemPropsData(),
|
||||
proto: this._proto
|
||||
}
|
||||
}
|
||||
}
|
27
db/app/core/dto/rawData/RawItemPrefab.ts
Normal file
27
db/app/core/dto/rawData/RawItemPrefab.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Prisma } from "@prisma/client"
|
||||
|
||||
export class RawItemPrefab {
|
||||
id: number
|
||||
path: string
|
||||
rcid: string
|
||||
|
||||
static fromRawData(data: any): RawItemPrefab {
|
||||
const rawData = new RawItemPrefab();
|
||||
Object.assign(rawData, data)
|
||||
return rawData
|
||||
}
|
||||
|
||||
toItemPrefabData(): Prisma.ItemPrefabCreateNestedOneWithoutItemPropInput {
|
||||
return {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: this.id
|
||||
},
|
||||
create: {
|
||||
path: this.path,
|
||||
rcid: this.path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
77
db/app/core/dto/rawData/RawItemProps.ts
Normal file
77
db/app/core/dto/rawData/RawItemProps.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { RawItemPrefab } from './RawItemPrefab';
|
||||
import { RawItemUsePrefab } from './RawItemUsePrefab';
|
||||
|
||||
export class RawItemProps {
|
||||
id: number
|
||||
Name: string
|
||||
ShortName: string
|
||||
Description: string
|
||||
Weight: number
|
||||
BackgroundColor: string
|
||||
Width: number
|
||||
Height: number
|
||||
StackMaxSize: number
|
||||
ItemSound: string
|
||||
Prefab: RawItemPrefab
|
||||
UsePrefab: RawItemUsePrefab
|
||||
StackObjectsCount: number
|
||||
NotShownInSlot: boolean
|
||||
ExaminedByDefault: boolean
|
||||
ExamineTime: number
|
||||
IsUndiscardable: boolean
|
||||
IsUnsaleable: boolean
|
||||
IsUnbuyable: boolean
|
||||
IsUngivable: boolean
|
||||
IsLockedafterEquip: boolean
|
||||
QuestItem: boolean
|
||||
LootExperience: number
|
||||
ExamineExperience: number
|
||||
HideEntrails: boolean
|
||||
RepairCost: number
|
||||
RepairSpeed: number
|
||||
ExtraSizeLeft: number
|
||||
ExtraSizeRight: number
|
||||
ExtraSizeUp: number
|
||||
ExtraSizeDown: number
|
||||
ExtraSizeForceAdd: boolean
|
||||
MergesWithChildren: boolean
|
||||
CanSellOnRagfair: boolean
|
||||
CanRequireOnRagfair: boolean
|
||||
ConflictingItems: string[]
|
||||
Unlootable: boolean
|
||||
UnlootableFromSlot: string
|
||||
UnlootableFromSide: string[]
|
||||
AnimationVariantsNumber: number
|
||||
DiscardingBlock: boolean
|
||||
RagFairCommissionModifier: number
|
||||
IsAlwaysAvailableForInsurance: boolean
|
||||
MaxResource: number
|
||||
Resource: number
|
||||
Backgroundcolor: string
|
||||
IsLockedAfterEquip: boolean
|
||||
|
||||
static fromRawData(data: any): RawItemProps {
|
||||
const rawData = new RawItemProps();
|
||||
Object.assign(rawData, data)
|
||||
rawData.Prefab = RawItemPrefab.fromRawData(data['Prefab'])
|
||||
rawData.UsePrefab = RawItemUsePrefab.fromRawData(data['UsePrefab'])
|
||||
return rawData
|
||||
}
|
||||
|
||||
toItemPropsData(): Prisma.ItemPropCreateNestedOneWithoutItemInput {
|
||||
const {id, ...data} = this;
|
||||
return {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id
|
||||
},
|
||||
create: {
|
||||
...data,
|
||||
Prefab: this.Prefab.toItemPrefabData(),
|
||||
UsePrefab: this.UsePrefab.toItemUsePrefabData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
db/app/core/dto/rawData/RawItemUsePrefab.ts
Normal file
26
db/app/core/dto/rawData/RawItemUsePrefab.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Prisma } from '@prisma/client';
|
||||
export class RawItemUsePrefab {
|
||||
id: number
|
||||
path: string
|
||||
rcid: string
|
||||
|
||||
static fromRawData(data: any): RawItemUsePrefab {
|
||||
const rawData = new RawItemUsePrefab();
|
||||
Object.assign(rawData, data)
|
||||
return rawData
|
||||
}
|
||||
|
||||
toItemUsePrefabData(): Prisma.ItemUsePrefabCreateNestedOneWithoutItemPropInput {
|
||||
return {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: this.id
|
||||
},
|
||||
create: {
|
||||
path: this.path,
|
||||
rcid: this.rcid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,3 +31,5 @@ export const Layout = (props: PropsWithChildren<Props>) => {
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Layout ;
|
12
db/app/item-prefabs/components/ItemPrefabForm.tsx
Normal file
12
db/app/item-prefabs/components/ItemPrefabForm.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { Form, FormProps } from "app/core/components/Form"
|
||||
import { LabeledTextField } from "app/core/components/LabeledTextField"
|
||||
import { z } from "zod"
|
||||
export { FORM_ERROR } from "app/core/components/Form"
|
||||
|
||||
export function ItemPrefabForm<S extends z.ZodType<any, any>>(props: FormProps<S>) {
|
||||
return (
|
||||
<Form<S> {...props}>
|
||||
<LabeledTextField name="name" label="Name" placeholder="Name" />
|
||||
</Form>
|
||||
)
|
||||
}
|
18
db/app/item-prefabs/mutations/createItemPrefab.ts
Normal file
18
db/app/item-prefabs/mutations/createItemPrefab.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const CreateItemPrefab = z.object({
|
||||
name: z.string(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(CreateItemPrefab),
|
||||
resolver.authorize(),
|
||||
async (input) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemPrefab = await db.itemPrefab.create({ data: input })
|
||||
|
||||
return itemPrefab
|
||||
}
|
||||
)
|
18
db/app/item-prefabs/mutations/deleteItemPrefab.ts
Normal file
18
db/app/item-prefabs/mutations/deleteItemPrefab.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const DeleteItemPrefab = z.object({
|
||||
id: z.number(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(DeleteItemPrefab),
|
||||
resolver.authorize(),
|
||||
async ({ id }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemPrefab = await db.itemPrefab.deleteMany({ where: { id } })
|
||||
|
||||
return itemPrefab
|
||||
}
|
||||
)
|
19
db/app/item-prefabs/mutations/updateItemPrefab.ts
Normal file
19
db/app/item-prefabs/mutations/updateItemPrefab.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const UpdateItemPrefab = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(UpdateItemPrefab),
|
||||
resolver.authorize(),
|
||||
async ({ id, ...data }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemPrefab = await db.itemPrefab.update({ where: { id }, data })
|
||||
|
||||
return itemPrefab
|
||||
}
|
||||
)
|
17
db/app/item-prefabs/queries/getItemPrefab.ts
Normal file
17
db/app/item-prefabs/queries/getItemPrefab.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { resolver, NotFoundError } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const GetItemPrefab = z.object({
|
||||
// This accepts type of undefined, but is required at runtime
|
||||
id: z.number().optional().refine(Boolean, "Required"),
|
||||
})
|
||||
|
||||
export default resolver.pipe(resolver.zod(GetItemPrefab), resolver.authorize(), async ({ id }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemPrefab = await db.itemPrefab.findFirst({ where: { id } })
|
||||
|
||||
if (!itemPrefab) throw new NotFoundError()
|
||||
|
||||
return itemPrefab
|
||||
})
|
30
db/app/item-prefabs/queries/getItemPrefabs.ts
Normal file
30
db/app/item-prefabs/queries/getItemPrefabs.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { paginate, resolver } from "blitz"
|
||||
import db, { Prisma } from "db"
|
||||
|
||||
interface GetItemPrefabsInput
|
||||
extends Pick<Prisma.ItemPrefabFindManyArgs, "where" | "orderBy" | "skip" | "take"> {}
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.authorize(),
|
||||
async ({ where, orderBy, skip = 0, take = 100 }: GetItemPrefabsInput) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const {
|
||||
items: itemPrefabs,
|
||||
hasMore,
|
||||
nextPage,
|
||||
count,
|
||||
} = await paginate({
|
||||
skip,
|
||||
take,
|
||||
count: () => db.itemPrefab.count({ where }),
|
||||
query: (paginateArgs) => db.itemPrefab.findMany({ ...paginateArgs, where, orderBy }),
|
||||
})
|
||||
|
||||
return {
|
||||
itemPrefabs,
|
||||
nextPage,
|
||||
hasMore,
|
||||
count,
|
||||
}
|
||||
}
|
||||
)
|
12
db/app/item-props/components/ItemPropForm.tsx
Normal file
12
db/app/item-props/components/ItemPropForm.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { Form, FormProps } from "app/core/components/Form"
|
||||
import { LabeledTextField } from "app/core/components/LabeledTextField"
|
||||
import { z } from "zod"
|
||||
export { FORM_ERROR } from "app/core/components/Form"
|
||||
|
||||
export function ItemPropForm<S extends z.ZodType<any, any>>(props: FormProps<S>) {
|
||||
return (
|
||||
<Form<S> {...props}>
|
||||
<LabeledTextField name="name" label="Name" placeholder="Name" />
|
||||
</Form>
|
||||
)
|
||||
}
|
14
db/app/item-props/mutations/createItemProp.ts
Normal file
14
db/app/item-props/mutations/createItemProp.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const CreateItemProp = z.object({
|
||||
name: z.string(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(resolver.zod(CreateItemProp), resolver.authorize(), async (input) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemProp = await db.itemProp.create({ data: input })
|
||||
|
||||
return itemProp
|
||||
})
|
14
db/app/item-props/mutations/deleteItemProp.ts
Normal file
14
db/app/item-props/mutations/deleteItemProp.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const DeleteItemProp = z.object({
|
||||
id: z.number(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(resolver.zod(DeleteItemProp), resolver.authorize(), async ({ id }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemProp = await db.itemProp.deleteMany({ where: { id } })
|
||||
|
||||
return itemProp
|
||||
})
|
19
db/app/item-props/mutations/updateItemProp.ts
Normal file
19
db/app/item-props/mutations/updateItemProp.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const UpdateItemProp = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(UpdateItemProp),
|
||||
resolver.authorize(),
|
||||
async ({ id, ...data }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemProp = await db.itemProp.update({ where: { id }, data })
|
||||
|
||||
return itemProp
|
||||
}
|
||||
)
|
17
db/app/item-props/queries/getItemProp.ts
Normal file
17
db/app/item-props/queries/getItemProp.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { resolver, NotFoundError } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const GetItemProp = z.object({
|
||||
// This accepts type of undefined, but is required at runtime
|
||||
id: z.number().optional().refine(Boolean, "Required"),
|
||||
})
|
||||
|
||||
export default resolver.pipe(resolver.zod(GetItemProp), resolver.authorize(), async ({ id }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemProp = await db.itemProp.findFirst({ where: { id } })
|
||||
|
||||
if (!itemProp) throw new NotFoundError()
|
||||
|
||||
return itemProp
|
||||
})
|
30
db/app/item-props/queries/getItemProps.ts
Normal file
30
db/app/item-props/queries/getItemProps.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { paginate, resolver } from "blitz"
|
||||
import db, { Prisma } from "db"
|
||||
|
||||
interface GetItemPropsInput
|
||||
extends Pick<Prisma.ItemPropFindManyArgs, "where" | "orderBy" | "skip" | "take"> {}
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.authorize(),
|
||||
async ({ where, orderBy, skip = 0, take = 100 }: GetItemPropsInput) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const {
|
||||
items: itemProps,
|
||||
hasMore,
|
||||
nextPage,
|
||||
count,
|
||||
} = await paginate({
|
||||
skip,
|
||||
take,
|
||||
count: () => db.itemProp.count({ where }),
|
||||
query: (paginateArgs) => db.itemProp.findMany({ ...paginateArgs, where, orderBy }),
|
||||
})
|
||||
|
||||
return {
|
||||
itemProps,
|
||||
nextPage,
|
||||
hasMore,
|
||||
count,
|
||||
}
|
||||
}
|
||||
)
|
12
db/app/item-use-prefabs/components/ItemUsePrefabForm.tsx
Normal file
12
db/app/item-use-prefabs/components/ItemUsePrefabForm.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { Form, FormProps } from "app/core/components/Form"
|
||||
import { LabeledTextField } from "app/core/components/LabeledTextField"
|
||||
import { z } from "zod"
|
||||
export { FORM_ERROR } from "app/core/components/Form"
|
||||
|
||||
export function ItemUsePrefabForm<S extends z.ZodType<any, any>>(props: FormProps<S>) {
|
||||
return (
|
||||
<Form<S> {...props}>
|
||||
<LabeledTextField name="name" label="Name" placeholder="Name" />
|
||||
</Form>
|
||||
)
|
||||
}
|
18
db/app/item-use-prefabs/mutations/createItemUsePrefab.ts
Normal file
18
db/app/item-use-prefabs/mutations/createItemUsePrefab.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const CreateItemUsePrefab = z.object({
|
||||
name: z.string(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(CreateItemUsePrefab),
|
||||
resolver.authorize(),
|
||||
async (input) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemUsePrefab = await db.itemUsePrefab.create({ data: input })
|
||||
|
||||
return itemUsePrefab
|
||||
}
|
||||
)
|
18
db/app/item-use-prefabs/mutations/deleteItemUsePrefab.ts
Normal file
18
db/app/item-use-prefabs/mutations/deleteItemUsePrefab.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const DeleteItemUsePrefab = z.object({
|
||||
id: z.number(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(DeleteItemUsePrefab),
|
||||
resolver.authorize(),
|
||||
async ({ id }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemUsePrefab = await db.itemUsePrefab.deleteMany({ where: { id } })
|
||||
|
||||
return itemUsePrefab
|
||||
}
|
||||
)
|
19
db/app/item-use-prefabs/mutations/updateItemUsePrefab.ts
Normal file
19
db/app/item-use-prefabs/mutations/updateItemUsePrefab.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const UpdateItemUsePrefab = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(UpdateItemUsePrefab),
|
||||
resolver.authorize(),
|
||||
async ({ id, ...data }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemUsePrefab = await db.itemUsePrefab.update({ where: { id }, data })
|
||||
|
||||
return itemUsePrefab
|
||||
}
|
||||
)
|
21
db/app/item-use-prefabs/queries/getItemUsePrefab.ts
Normal file
21
db/app/item-use-prefabs/queries/getItemUsePrefab.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { resolver, NotFoundError } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const GetItemUsePrefab = z.object({
|
||||
// This accepts type of undefined, but is required at runtime
|
||||
id: z.number().optional().refine(Boolean, "Required"),
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(GetItemUsePrefab),
|
||||
resolver.authorize(),
|
||||
async ({ id }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const itemUsePrefab = await db.itemUsePrefab.findFirst({ where: { id } })
|
||||
|
||||
if (!itemUsePrefab) throw new NotFoundError()
|
||||
|
||||
return itemUsePrefab
|
||||
}
|
||||
)
|
30
db/app/item-use-prefabs/queries/getItemUsePrefabs.ts
Normal file
30
db/app/item-use-prefabs/queries/getItemUsePrefabs.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { paginate, resolver } from "blitz"
|
||||
import db, { Prisma } from "db"
|
||||
|
||||
interface GetItemUsePrefabsInput
|
||||
extends Pick<Prisma.ItemUsePrefabFindManyArgs, "where" | "orderBy" | "skip" | "take"> {}
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.authorize(),
|
||||
async ({ where, orderBy, skip = 0, take = 100 }: GetItemUsePrefabsInput) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const {
|
||||
items: itemUsePrefabs,
|
||||
hasMore,
|
||||
nextPage,
|
||||
count,
|
||||
} = await paginate({
|
||||
skip,
|
||||
take,
|
||||
count: () => db.itemUsePrefab.count({ where }),
|
||||
query: (paginateArgs) => db.itemUsePrefab.findMany({ ...paginateArgs, where, orderBy }),
|
||||
})
|
||||
|
||||
return {
|
||||
itemUsePrefabs,
|
||||
nextPage,
|
||||
hasMore,
|
||||
count,
|
||||
}
|
||||
}
|
||||
)
|
12
db/app/items/components/ItemForm.tsx
Normal file
12
db/app/items/components/ItemForm.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { Form, FormProps } from "app/core/components/Form"
|
||||
import { LabeledTextField } from "app/core/components/LabeledTextField"
|
||||
import { z } from "zod"
|
||||
export { FORM_ERROR } from "app/core/components/Form"
|
||||
|
||||
export function ItemForm<S extends z.ZodType<any, any>>(props: FormProps<S>) {
|
||||
return (
|
||||
<Form<S> {...props}>
|
||||
<LabeledTextField name="name" label="Name" placeholder="Name" />
|
||||
</Form>
|
||||
)
|
||||
}
|
16
db/app/items/mutations/createItem.ts
Normal file
16
db/app/items/mutations/createItem.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const CreateItem = z.object({
|
||||
id: z.number().optional().refine(Boolean, "Required"),
|
||||
name: z.string().optional().refine(String, "Required"),
|
||||
type: z.string().optional().refine(String, "Required"),
|
||||
})
|
||||
|
||||
export default resolver.pipe(resolver.zod(CreateItem), resolver.authorize(), async (input) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const item = await db.item.create({ data: input })
|
||||
|
||||
return item
|
||||
})
|
14
db/app/items/mutations/deleteItem.ts
Normal file
14
db/app/items/mutations/deleteItem.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const DeleteItem = z.object({
|
||||
id: z.number(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(resolver.zod(DeleteItem), resolver.authorize(), async ({ id }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const item = await db.item.deleteMany({ where: { id } })
|
||||
|
||||
return item
|
||||
})
|
19
db/app/items/mutations/updateItem.ts
Normal file
19
db/app/items/mutations/updateItem.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { resolver } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const UpdateItem = z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
})
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.zod(UpdateItem),
|
||||
resolver.authorize(),
|
||||
async ({ id, ...data }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const item = await db.item.update({ where: { id }, data })
|
||||
|
||||
return item
|
||||
}
|
||||
)
|
17
db/app/items/queries/getItem.ts
Normal file
17
db/app/items/queries/getItem.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { resolver, NotFoundError } from "blitz"
|
||||
import db from "db"
|
||||
import { z } from "zod"
|
||||
|
||||
const GetItem = z.object({
|
||||
// This accepts type of undefined, but is required at runtime
|
||||
id: z.number().optional().refine(Boolean, "Required")
|
||||
})
|
||||
|
||||
export default resolver.pipe(resolver.zod(GetItem), resolver.authorize(), async ({ id }) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const item = await db.item.findFirst({ where: { id } })
|
||||
|
||||
if (!item) throw new NotFoundError()
|
||||
|
||||
return item
|
||||
})
|
30
db/app/items/queries/getItems.ts
Normal file
30
db/app/items/queries/getItems.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { paginate, resolver } from "blitz"
|
||||
import db, { Prisma } from "db"
|
||||
|
||||
interface GetItemsInput
|
||||
extends Pick<Prisma.ItemFindManyArgs, "where" | "orderBy" | "skip" | "take"> {}
|
||||
|
||||
export default resolver.pipe(
|
||||
resolver.authorize(),
|
||||
async ({ where, orderBy, skip = 0, take = 100 }: GetItemsInput) => {
|
||||
// TODO: in multi-tenant app, you must add validation to ensure correct tenant
|
||||
const {
|
||||
items: items,
|
||||
hasMore,
|
||||
nextPage,
|
||||
count,
|
||||
} = await paginate({
|
||||
skip,
|
||||
take,
|
||||
count: () => db.item.count({ where }),
|
||||
query: (paginateArgs) => db.item.findMany({ ...paginateArgs, where, orderBy }),
|
||||
})
|
||||
|
||||
return {
|
||||
items,
|
||||
nextPage,
|
||||
hasMore,
|
||||
count,
|
||||
}
|
||||
}
|
||||
)
|
8
db/app/mutations/enqueueGreeting.ts
Normal file
8
db/app/mutations/enqueueGreeting.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import greetingsQueue from "app/api/greetingsQueue"
|
||||
|
||||
export default async function enqueueGreeting() {
|
||||
await greetingsQueue.enqueue({
|
||||
to: "Sandy Cheeks",
|
||||
message: "Howdy!",
|
||||
})
|
||||
}
|
7
db/db/index.ts
Normal file
7
db/db/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { enhancePrisma } from "blitz"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
const EnhancedPrisma = enhancePrisma(PrismaClient)
|
||||
|
||||
export * from "@prisma/client"
|
||||
export default new EnhancedPrisma()
|
@ -0,0 +1,41 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Item" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"itemId" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "Item_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ItemProp" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"shortName" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL,
|
||||
"weight" INTEGER NOT NULL,
|
||||
"backgroundcolor" TEXT NOT NULL,
|
||||
"width" INTEGER NOT NULL,
|
||||
"height" INTEGER NOT NULL,
|
||||
"stackMaxSize" INTEGER NOT NULL,
|
||||
"itemSound" TEXT NOT NULL,
|
||||
"stackObjectsCount" INTEGER NOT NULL,
|
||||
"notShownInSlot" BOOLEAN NOT NULL,
|
||||
"examinedByDefault" BOOLEAN NOT NULL,
|
||||
"examineTime" INTEGER NOT NULL,
|
||||
"isUndiscardable" BOOLEAN NOT NULL,
|
||||
"isUnsaleable" BOOLEAN NOT NULL,
|
||||
"isUnbuyable" BOOLEAN NOT NULL,
|
||||
"isUngivable" BOOLEAN NOT NULL,
|
||||
"isLockedAfterEquip" BOOLEAN NOT NULL,
|
||||
|
||||
CONSTRAINT "ItemProp_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Item" ADD CONSTRAINT "Item_itemId_fkey" FOREIGN KEY ("itemId") REFERENCES "Item"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
|
@ -0,0 +1,10 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "ItemPrefab" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"path" TEXT NOT NULL,
|
||||
"rcid" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "ItemPrefab_pkey" PRIMARY KEY ("id")
|
||||
);
|
@ -0,0 +1,10 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "ItemUsePrefab" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"path" TEXT NOT NULL,
|
||||
"rcid" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "ItemUsePrefab_pkey" PRIMARY KEY ("id")
|
||||
);
|
@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "ItemProp" ADD COLUMN "conflictingItems" TEXT[],
|
||||
ADD COLUMN "unlootableFromSide" TEXT[];
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `itemPrefabId` to the `ItemProp` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `itemProp` to the `ItemProp` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `itemUsePrefabId` to the `ItemProp` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "ItemProp" ADD COLUMN "itemPrefabId" INTEGER NOT NULL,
|
||||
ADD COLUMN "itemProp" TEXT NOT NULL,
|
||||
ADD COLUMN "itemUsePrefabId" INTEGER NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ItemProp" ADD CONSTRAINT "ItemProp_itemPrefabId_fkey" FOREIGN KEY ("itemPrefabId") REFERENCES "ItemPrefab"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ItemProp" ADD CONSTRAINT "ItemProp_itemUsePrefabId_fkey" FOREIGN KEY ("itemUsePrefabId") REFERENCES "ItemUsePrefab"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
11
db/db/migrations/20220409024413_add_item_prop/migration.sql
Normal file
11
db/db/migrations/20220409024413_add_item_prop/migration.sql
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `itemPropId` to the `Item` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Item" ADD COLUMN "itemPropId" INTEGER NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Item" ADD CONSTRAINT "Item_itemPropId_fkey" FOREIGN KEY ("itemPropId") REFERENCES "ItemProp"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
@ -0,0 +1,8 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `internalId` to the `Item` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Item" ADD COLUMN "internalId" TEXT NOT NULL;
|
@ -0,0 +1,8 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- A unique constraint covering the columns `[internalId]` on the table `Item` will be added. If there are existing duplicate values, this will fail.
|
||||
|
||||
*/
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Item_internalId_key" ON "Item"("internalId");
|
3
db/db/migrations/migration_lock.toml
Normal file
3
db/db/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
79
db/db/schema.prisma
Normal file
79
db/db/schema.prisma
Normal file
@ -0,0 +1,79 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
model Item {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
name String
|
||||
type String
|
||||
|
||||
parentItem Item? @relation("ItemToItem", fields: [parent], references: [internalId], onDelete: NoAction, onUpdate: NoAction)
|
||||
Item Item[] @relation("ItemToItem")
|
||||
parent String
|
||||
internalId String @unique
|
||||
|
||||
props ItemProp @relation(fields: [itemPropId], references: [id])
|
||||
itemPropId Int
|
||||
proto String?
|
||||
}
|
||||
|
||||
model ItemProp {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
Name String
|
||||
ShortName String
|
||||
Description String
|
||||
Weight Int
|
||||
Backgroundcolor String
|
||||
Width Int
|
||||
Height Int
|
||||
StackMaxSize Int
|
||||
ItemSound String
|
||||
StackObjectsCount Int
|
||||
NotShownInSlot Boolean
|
||||
ExaminedByDefault Boolean
|
||||
ExamineTime Int
|
||||
IsUndiscardable Boolean
|
||||
IsUnsaleable Boolean
|
||||
IsUnbuyable Boolean
|
||||
IsUngivable Boolean
|
||||
IsLockedAfterEquip Boolean
|
||||
ConflictingItems String[]
|
||||
UnlootableFromSide String[]
|
||||
Prefab ItemPrefab @relation(fields: [ItemPrefabId], references: [id])
|
||||
UsePrefab ItemUsePrefab @relation(fields: [ItemUsePrefabId], references: [id])
|
||||
ItemPrefabId Int
|
||||
ItemUsePrefabId Int
|
||||
Item Item[]
|
||||
}
|
||||
|
||||
model ItemPrefab {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
path String
|
||||
rcid String
|
||||
ItemProp ItemProp[]
|
||||
}
|
||||
|
||||
model ItemUsePrefab {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
path String
|
||||
rcid String
|
||||
ItemProp ItemProp[]
|
||||
}
|
15
db/db/seeds.ts
Normal file
15
db/db/seeds.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// import db from "./index"
|
||||
|
||||
/*
|
||||
* This seed function is executed when you run `blitz db seed`.
|
||||
*
|
||||
* Probably you want to use a library like https://chancejs.com
|
||||
* to easily generate realistic data.
|
||||
*/
|
||||
const seed = async () => {
|
||||
// for (let i = 0; i < 5; i++) {
|
||||
// await db.project.create({ data: { name: "Project " + i } })
|
||||
// }
|
||||
}
|
||||
|
||||
export default seed
|
@ -3,12 +3,14 @@
|
||||
"version": "1.0.0",
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
"dev": "blitz dev",
|
||||
"dev": "concurrently --raw \"blitz dev\" 'quirrel'",
|
||||
"build": "blitz build",
|
||||
"start": "blitz start",
|
||||
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch"
|
||||
"test:watch": "jest --watch",
|
||||
"studio": "blitz prisma studio",
|
||||
"blitz": "blitz"
|
||||
},
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
@ -19,26 +21,38 @@
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"prisma": {
|
||||
"schema": "db/schema.prisma"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "11.x",
|
||||
"@emotion/styled": "11.x",
|
||||
"@mui/icons-material": "5.2.4",
|
||||
"@mui/material": "5.x",
|
||||
"@mui/styles": "5.x",
|
||||
"@prisma/client": "3.12.0",
|
||||
"blitz": "0.44.4",
|
||||
"final-form": "4.20.6",
|
||||
"next-themes": "0.0.15",
|
||||
"quirrel": "1.x",
|
||||
"react": "18.0.0-beta-149b420f6-20211119",
|
||||
"react-dom": "18.0.0-alpha-5ca4b0433-20211020",
|
||||
"react-final-form": "6.5.9",
|
||||
"react-json-view": "1.21.3",
|
||||
"zustand": "3.6.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "17.0.37",
|
||||
"concurrently": "6.x",
|
||||
"eslint": "7.32.0",
|
||||
"lint-staged": "11.3.0-beta.2",
|
||||
"prettier": "2.5.1",
|
||||
"pretty-quick": "3.1.2",
|
||||
"prisma": "3.12.0",
|
||||
"typescript": "~4.5"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
}
|
||||
|
1761
db/yarn.lock
1761
db/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user