mirror of
https://github.com/usatiuk/photos.git
synced 2025-10-27 23:17:49 +01:00
more type safety with zod
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,6 @@
|
||||
.DS_Store
|
||||
.idea/
|
||||
node_modules/
|
||||
/node_modules
|
||||
build/
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
39
backend/package-lock.json
generated
39
backend/package-lock.json
generated
@@ -15,7 +15,6 @@
|
||||
"class-validator": "^0.14.0",
|
||||
"exifreader": "^4.13.0",
|
||||
"hasha": "^5.2.2",
|
||||
"io-ts": "^2.2.20",
|
||||
"jsonwebtoken": "^9.0.1",
|
||||
"koa": "^2.14.2",
|
||||
"koa-body": "^5.0.0",
|
||||
@@ -2951,12 +2950,6 @@
|
||||
"url": "https://ko-fi.com/tunnckoCore/commissions"
|
||||
}
|
||||
},
|
||||
"node_modules/fp-ts": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.1.tgz",
|
||||
"integrity": "sha512-by7U5W8dkIzcvDofUcO42yl9JbnHTEDBrzu3pt5fKT+Z4Oy85I21K80EYJYdjQGC2qum4Vo55Ag57iiIK4FYuA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
@@ -3602,14 +3595,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/io-ts": {
|
||||
"version": "2.2.20",
|
||||
"resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.2.20.tgz",
|
||||
"integrity": "sha512-Rq2BsYmtwS5vVttie4rqrOCIfHCS9TgpRLFpKQCM1wZBBRY9nWVGmEvm2FnDbSE2un1UE39DvFpTR5UL47YDcA==",
|
||||
"peerDependencies": {
|
||||
"fp-ts": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
|
||||
@@ -4514,9 +4499,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -9572,12 +9557,6 @@
|
||||
"qs": "^6.11.0"
|
||||
}
|
||||
},
|
||||
"fp-ts": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.1.tgz",
|
||||
"integrity": "sha512-by7U5W8dkIzcvDofUcO42yl9JbnHTEDBrzu3pt5fKT+Z4Oy85I21K80EYJYdjQGC2qum4Vo55Ag57iiIK4FYuA==",
|
||||
"peer": true
|
||||
},
|
||||
"fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
@@ -10027,12 +10006,6 @@
|
||||
"side-channel": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"io-ts": {
|
||||
"version": "2.2.20",
|
||||
"resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.2.20.tgz",
|
||||
"integrity": "sha512-Rq2BsYmtwS5vVttie4rqrOCIfHCS9TgpRLFpKQCM1wZBBRY9nWVGmEvm2FnDbSE2un1UE39DvFpTR5UL47YDcA==",
|
||||
"requires": {}
|
||||
},
|
||||
"is-array-buffer": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
|
||||
@@ -10694,9 +10667,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
"class-validator": "^0.14.0",
|
||||
"exifreader": "^4.13.0",
|
||||
"hasha": "^5.2.2",
|
||||
"io-ts": "^2.2.20",
|
||||
"jsonwebtoken": "^9.0.1",
|
||||
"koa": "^2.14.2",
|
||||
"koa-body": "^5.0.0",
|
||||
@@ -48,12 +47,12 @@
|
||||
"@types/hasha": "^3.0.1",
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/koa": "^2.13.7",
|
||||
"@types/koa__cors": "^4.0.0",
|
||||
"@types/koa__router": "^12.0.0",
|
||||
"@types/koa-logger": "^3.1.2",
|
||||
"@types/koa-send": "^4.1.3",
|
||||
"@types/koa-sslify": "^4.0.3",
|
||||
"@types/koa-static": "^4.0.2",
|
||||
"@types/koa__cors": "^4.0.0",
|
||||
"@types/koa__router": "^12.0.0",
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/mysql": "^2.15.21",
|
||||
@@ -85,4 +84,4 @@
|
||||
"pre-commit": "npm run lint-all && npm run prettier-check"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,15 @@ import { config, EnvType } from "~config";
|
||||
import { userRouter } from "~routes/users";
|
||||
import { devRouter } from "~routes/dev";
|
||||
import { photosRouter } from "~routes/photos";
|
||||
import { TUserJWT } from "~shared/types";
|
||||
|
||||
export const app = new Koa();
|
||||
export interface IAppState extends Koa.DefaultState {
|
||||
user?: TUserJWT;
|
||||
}
|
||||
|
||||
export interface IAppContext extends Koa.DefaultContext {}
|
||||
|
||||
export const app = new Koa<IAppState, IAppContext>();
|
||||
|
||||
const tmpPath = path.join(config.dataDir, "tmp");
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as path from "path";
|
||||
import * as fs from "fs/promises";
|
||||
import * as mime from "mime-types";
|
||||
import * as jwt from "jsonwebtoken";
|
||||
import { IPhotoReqJSON, IPhotoJSON } from "~/shared/types";
|
||||
import { TPhotoReqJSON, TPhotoJSON } from "~/shared/types";
|
||||
|
||||
import {
|
||||
BaseEntity,
|
||||
@@ -212,7 +212,7 @@ export class Photo extends BaseEntity {
|
||||
}
|
||||
}
|
||||
|
||||
public async toJSON(): Promise<IPhotoJSON> {
|
||||
public async toJSON(): Promise<TPhotoJSON> {
|
||||
if (!isNumber(this.user.id)) {
|
||||
throw new Error("User not loaded");
|
||||
}
|
||||
@@ -232,7 +232,7 @@ export class Photo extends BaseEntity {
|
||||
};
|
||||
}
|
||||
|
||||
public async toReqJSON(): Promise<IPhotoReqJSON> {
|
||||
public async toReqJSON(): Promise<TPhotoReqJSON> {
|
||||
const token = await this.getJWTToken();
|
||||
return { ...(await this.toJSON()), accessToken: token };
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as bcrypt from "bcrypt";
|
||||
import * as jwt from "jsonwebtoken";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs/promises";
|
||||
import { IUserJSON, IUserAuthJSON } from "~/shared/types";
|
||||
import { TUserJSON, TUserAuthJSON } from "~/shared/types";
|
||||
|
||||
import {
|
||||
AfterInsert,
|
||||
@@ -83,12 +83,12 @@ export class User extends BaseEntity {
|
||||
return validateOrReject(this);
|
||||
}
|
||||
|
||||
public toJSON(): IUserJSON {
|
||||
public toJSON(): TUserJSON {
|
||||
const { id, username, isAdmin } = this;
|
||||
return { id, username, isAdmin };
|
||||
}
|
||||
|
||||
public toAuthJSON(): IUserAuthJSON {
|
||||
public toAuthJSON(): TUserAuthJSON {
|
||||
const json = this.toJSON();
|
||||
return { ...json, jwt: this.toJWT() };
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import * as Router from "@koa/router";
|
||||
import { Photo } from "~entity/Photo";
|
||||
import { User } from "~entity/User";
|
||||
import { IAppContext, IAppState } from "~app";
|
||||
|
||||
export const devRouter = new Router();
|
||||
export const devRouter = new Router<IAppState, IAppContext>();
|
||||
|
||||
devRouter.get("/dev/clean", async (ctx) => {
|
||||
type ContextType = Parameters<Parameters<(typeof devRouter)["post"]>["2"]>["0"];
|
||||
|
||||
devRouter.get("/dev/clean", async (ctx: ContextType) => {
|
||||
await Photo.remove(await Photo.find());
|
||||
await User.remove(await User.find());
|
||||
ctx.body = { success: true };
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import * as Router from "@koa/router";
|
||||
import { Photo } from "~entity/Photo";
|
||||
import {
|
||||
IPhotoReqJSON,
|
||||
IPhotosNewRespBody,
|
||||
IPhotosNewPostBody,
|
||||
IPhotoByIDDeleteRespBody,
|
||||
IPhotosUploadRespBody,
|
||||
IPhotosListRespBody,
|
||||
IPhotosByIDGetRespBody,
|
||||
IPhotosDeleteRespBody,
|
||||
IPhotosDeleteBody,
|
||||
IPhotosGetShowTokenByID,
|
||||
IPhotoShowToken,
|
||||
IAPIResponse,
|
||||
IPhotosListPagination,
|
||||
TPhotoReqJSON,
|
||||
TPhotosNewRespBody,
|
||||
TPhotoByIDDeleteRespBody,
|
||||
TPhotosUploadRespBody,
|
||||
TPhotosListRespBody,
|
||||
TPhotosByIDGetRespBody,
|
||||
TPhotosDeleteRespBody,
|
||||
PhotosListPagination,
|
||||
PhotosNewPostBody,
|
||||
PhotoJSON,
|
||||
TPhotosGetShowTokenByIDRespBody,
|
||||
PhotosDeleteBody,
|
||||
} from "~/shared/types";
|
||||
import send = require("koa-send");
|
||||
import { getHash, getSize } from "~util";
|
||||
@@ -21,24 +20,25 @@ import * as jwt from "jsonwebtoken";
|
||||
import { config } from "~config";
|
||||
import { ValidationError } from "class-validator";
|
||||
import { In } from "typeorm";
|
||||
import { IAppContext, IAppState } from "~app";
|
||||
|
||||
export const photosRouter = new Router();
|
||||
export const photosRouter = new Router<IAppState, IAppContext>();
|
||||
|
||||
photosRouter.post("/photos/new", async (ctx) => {
|
||||
// Typescript requires explicit type annotations for CFA......
|
||||
type ContextType = Parameters<
|
||||
Parameters<(typeof photosRouter)["post"]>["2"]
|
||||
>["0"];
|
||||
|
||||
photosRouter.post("/photos/new", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
|
||||
const { user } = ctx.state;
|
||||
const body = ctx.request.body as IPhotosNewPostBody;
|
||||
const body = PhotosNewPostBody.parse(ctx.request.body);
|
||||
const { hash, size, format } = body;
|
||||
|
||||
if (!(hash && size && format)) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const photo = new Photo(user, hash, size, format);
|
||||
const photo = Photo.create({ user, hash, size, format });
|
||||
|
||||
try {
|
||||
await photo.save();
|
||||
@@ -47,13 +47,12 @@ photosRouter.post("/photos/new", async (ctx) => {
|
||||
const photo = await Photo.findOne({ hash, size, user });
|
||||
if (!photo) {
|
||||
ctx.throw(404);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.body = {
|
||||
error: false,
|
||||
data: await photo.toReqJSON(),
|
||||
} as IPhotosNewRespBody;
|
||||
} as TPhotosNewRespBody;
|
||||
return;
|
||||
}
|
||||
if (
|
||||
@@ -61,7 +60,6 @@ photosRouter.post("/photos/new", async (ctx) => {
|
||||
(Array.isArray(e) && e.some((e) => e instanceof ValidationError))
|
||||
) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
console.log(e);
|
||||
ctx.throw(500);
|
||||
@@ -70,10 +68,10 @@ photosRouter.post("/photos/new", async (ctx) => {
|
||||
ctx.body = {
|
||||
error: false,
|
||||
data: await photo.toReqJSON(),
|
||||
} as IPhotosNewRespBody;
|
||||
} as TPhotosNewRespBody;
|
||||
});
|
||||
|
||||
photosRouter.post("/photos/upload/:id", async (ctx) => {
|
||||
photosRouter.post("/photos/upload/:id", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
@@ -84,32 +82,28 @@ photosRouter.post("/photos/upload/:id", async (ctx) => {
|
||||
|
||||
if (!id) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const { user } = ctx.state;
|
||||
const photo = await Photo.findOne({ id: parseInt(id), user });
|
||||
if (!photo) {
|
||||
ctx.throw(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx.request.files || Object.keys(ctx.request.files).length === 0) {
|
||||
ctx.throw(400, "No file");
|
||||
return;
|
||||
}
|
||||
|
||||
if (photo.uploaded) {
|
||||
ctx.throw(400, "Already uploaded");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.request.files) {
|
||||
const files = ctx.request.files;
|
||||
if (Object.keys(files).length > 1) {
|
||||
ctx.throw(400, "Too many files");
|
||||
return;
|
||||
}
|
||||
|
||||
const file = Object.values(files)[0];
|
||||
if (Array.isArray(file)) {
|
||||
throw "more than one file uploaded";
|
||||
@@ -120,7 +114,6 @@ photosRouter.post("/photos/upload/:id", async (ctx) => {
|
||||
|
||||
if (photoHash !== photo.hash || photoSize !== photo.size) {
|
||||
ctx.throw(400, "Wrong photo");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -134,14 +127,14 @@ photosRouter.post("/photos/upload/:id", async (ctx) => {
|
||||
ctx.body = {
|
||||
error: false,
|
||||
data: await photo.toReqJSON(),
|
||||
} as IPhotosUploadRespBody;
|
||||
} as TPhotosUploadRespBody;
|
||||
});
|
||||
|
||||
/**
|
||||
export interface IPhotosByIDPatchBody {
|
||||
export interface TPhotosByIDPatchBody {
|
||||
}
|
||||
export type IPhotosByIDPatchRespBody = IAPIResponse<IPhotoReqJSON>;
|
||||
photosRouter.patch("/photos/byID/:id", async (ctx) => {
|
||||
export type TPhotosByIDPatchRespBody = IAPIResponse<TPhotoReqJSON>;
|
||||
photosRouter.patch("/photos/byID/:id", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
return;
|
||||
@@ -180,7 +173,7 @@ photosRouter.patch("/photos/byID/:id", async (ctx) => {
|
||||
});
|
||||
*/
|
||||
|
||||
photosRouter.get("/photos/list", async (ctx) => {
|
||||
photosRouter.get("/photos/list", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
@@ -200,8 +193,8 @@ photosRouter.get("/photos/list", async (ctx) => {
|
||||
skip = parseInt(skip);
|
||||
}
|
||||
|
||||
if (!num || num > IPhotosListPagination) {
|
||||
num = IPhotosListPagination;
|
||||
if (!num || num > PhotosListPagination) {
|
||||
num = PhotosListPagination;
|
||||
}
|
||||
|
||||
const photos = await Photo.find({
|
||||
@@ -211,17 +204,17 @@ photosRouter.get("/photos/list", async (ctx) => {
|
||||
order: { shotAt: "DESC" },
|
||||
});
|
||||
|
||||
const photosList: IPhotoReqJSON[] = await Promise.all(
|
||||
const photosList: TPhotoReqJSON[] = await Promise.all(
|
||||
photos.map(async (photo) => await photo.toReqJSON()),
|
||||
);
|
||||
|
||||
ctx.body = {
|
||||
error: false,
|
||||
data: photosList,
|
||||
} as IPhotosListRespBody;
|
||||
} as TPhotosListRespBody;
|
||||
});
|
||||
|
||||
photosRouter.get("/photos/byID/:id", async (ctx) => {
|
||||
photosRouter.get("/photos/byID/:id", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
@@ -232,7 +225,6 @@ photosRouter.get("/photos/byID/:id", async (ctx) => {
|
||||
|
||||
if (!id) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const { user } = ctx.state;
|
||||
@@ -241,16 +233,15 @@ photosRouter.get("/photos/byID/:id", async (ctx) => {
|
||||
|
||||
if (!photo) {
|
||||
ctx.throw(404);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.body = {
|
||||
error: false,
|
||||
data: await photo.toReqJSON(),
|
||||
} as IPhotosByIDGetRespBody;
|
||||
} as TPhotosByIDGetRespBody;
|
||||
});
|
||||
|
||||
photosRouter.get("/photos/showByID/:id/:token", async (ctx) => {
|
||||
photosRouter.get("/photos/showByID/:id/:token", async (ctx: ContextType) => {
|
||||
const { id, token } = ctx.params as {
|
||||
id: string | undefined;
|
||||
token: string | undefined;
|
||||
@@ -258,7 +249,6 @@ photosRouter.get("/photos/showByID/:id/:token", async (ctx) => {
|
||||
|
||||
if (!(id && token)) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -267,7 +257,7 @@ photosRouter.get("/photos/showByID/:id/:token", async (ctx) => {
|
||||
ctx.throw(401);
|
||||
}
|
||||
|
||||
const photoReqJSON = jwt.decode(token) as IPhotoReqJSON;
|
||||
const photoReqJSON = PhotoJSON.parse(jwt.decode(token));
|
||||
const { user } = photoReqJSON;
|
||||
|
||||
const photo = await Photo.findOne({
|
||||
@@ -277,7 +267,6 @@ photosRouter.get("/photos/showByID/:id/:token", async (ctx) => {
|
||||
|
||||
if (!photo) {
|
||||
ctx.throw(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -292,7 +281,7 @@ photosRouter.get("/photos/showByID/:id/:token", async (ctx) => {
|
||||
await send(ctx, await photo.getReadyPath("original"));
|
||||
});
|
||||
|
||||
photosRouter.get("/photos/showByID/:id", async (ctx) => {
|
||||
photosRouter.get("/photos/showByID/:id", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
@@ -303,7 +292,6 @@ photosRouter.get("/photos/showByID/:id", async (ctx) => {
|
||||
|
||||
if (!id) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const { user } = ctx.state;
|
||||
@@ -312,7 +300,6 @@ photosRouter.get("/photos/showByID/:id", async (ctx) => {
|
||||
|
||||
if (!photo) {
|
||||
ctx.throw(404);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -327,7 +314,7 @@ photosRouter.get("/photos/showByID/:id", async (ctx) => {
|
||||
await send(ctx, await photo.getReadyPath("original"));
|
||||
});
|
||||
|
||||
photosRouter.get("/photos/getShowByIDToken/:id", async (ctx) => {
|
||||
photosRouter.get("/photos/getShowByIDToken/:id", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
@@ -338,7 +325,6 @@ photosRouter.get("/photos/getShowByIDToken/:id", async (ctx) => {
|
||||
|
||||
if (!id) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const { user } = ctx.state;
|
||||
@@ -346,15 +332,14 @@ photosRouter.get("/photos/getShowByIDToken/:id", async (ctx) => {
|
||||
const photo = await Photo.findOne({ id: parseInt(id), user });
|
||||
if (!photo) {
|
||||
ctx.throw(404);
|
||||
return;
|
||||
}
|
||||
|
||||
const token = await photo.getJWTToken();
|
||||
|
||||
ctx.body = { error: false, data: token } as IPhotosGetShowTokenByID;
|
||||
ctx.body = { error: false, data: token } as TPhotosGetShowTokenByIDRespBody;
|
||||
});
|
||||
|
||||
photosRouter.delete("/photos/byID/:id", async (ctx) => {
|
||||
photosRouter.delete("/photos/byID/:id", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
@@ -365,7 +350,6 @@ photosRouter.delete("/photos/byID/:id", async (ctx) => {
|
||||
|
||||
if (!id) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const { user } = ctx.state;
|
||||
@@ -374,7 +358,6 @@ photosRouter.delete("/photos/byID/:id", async (ctx) => {
|
||||
|
||||
if (!photo) {
|
||||
ctx.throw(404);
|
||||
return;
|
||||
}
|
||||
|
||||
await photo.remove();
|
||||
@@ -382,22 +365,17 @@ photosRouter.delete("/photos/byID/:id", async (ctx) => {
|
||||
ctx.body = {
|
||||
error: false,
|
||||
data: true,
|
||||
} as IPhotoByIDDeleteRespBody;
|
||||
} as TPhotoByIDDeleteRespBody;
|
||||
});
|
||||
|
||||
photosRouter.post("/photos/delete", async (ctx) => {
|
||||
photosRouter.post("/photos/delete", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
|
||||
const body = ctx.request.body as IPhotosDeleteBody;
|
||||
const body = PhotosDeleteBody.parse(ctx.request.body);
|
||||
const { photos } = body;
|
||||
|
||||
if (!photos || !Array.isArray(photos) || photos.length == 0) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const { user } = ctx.state;
|
||||
try {
|
||||
await Photo.delete({
|
||||
@@ -408,11 +386,11 @@ photosRouter.post("/photos/delete", async (ctx) => {
|
||||
ctx.body = {
|
||||
error: false,
|
||||
data: true,
|
||||
} as IPhotosDeleteRespBody;
|
||||
} as TPhotosDeleteRespBody;
|
||||
} catch (e) {
|
||||
ctx.body = {
|
||||
data: null,
|
||||
error: "Internal server error",
|
||||
} as IPhotosDeleteRespBody;
|
||||
} as TPhotosDeleteRespBody;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,70 +2,65 @@ import * as Router from "@koa/router";
|
||||
import { getConfigValue, ConfigKey } from "~entity/Config";
|
||||
import { User } from "~entity/User";
|
||||
import {
|
||||
IUserJWT,
|
||||
IUserGetRespBody,
|
||||
IUserEditRespBody,
|
||||
IUserSignupBody,
|
||||
IUserSignupRespBody,
|
||||
IUserLoginRespBody,
|
||||
IUserEditBody,
|
||||
IUserLoginBody,
|
||||
TUserJWT,
|
||||
TUserGetRespBody,
|
||||
TUserEditRespBody,
|
||||
TUserSignupBody,
|
||||
TUserSignupRespBody,
|
||||
TUserLoginRespBody,
|
||||
TUserEditBody,
|
||||
TUserLoginBody,
|
||||
UserLoginBody,
|
||||
UserSignupBody,
|
||||
UserEditBody,
|
||||
} from "~/shared/types";
|
||||
import { IAppContext, IAppState } from "~app";
|
||||
|
||||
export const userRouter = new Router();
|
||||
export const userRouter = new Router<IAppState, IAppContext>();
|
||||
|
||||
userRouter.get("/users/user", async (ctx) => {
|
||||
type ContextType = Parameters<
|
||||
Parameters<(typeof userRouter)["post"]>["2"]
|
||||
>["0"];
|
||||
|
||||
userRouter.get("/users/user", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
|
||||
const jwt = ctx.state.user as IUserJWT;
|
||||
|
||||
const jwt = ctx.state.user;
|
||||
const user = await User.findOne(jwt.id);
|
||||
|
||||
if (!user) {
|
||||
ctx.throw(401);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.body = { error: false, data: user.toAuthJSON() } as IUserGetRespBody;
|
||||
ctx.body = { error: false, data: user.toAuthJSON() } as TUserGetRespBody;
|
||||
});
|
||||
|
||||
userRouter.post("/users/login", async (ctx) => {
|
||||
userRouter.post("/users/login", async (ctx: ContextType) => {
|
||||
const request = ctx.request;
|
||||
|
||||
if (!request.body) {
|
||||
ctx.throw(400);
|
||||
}
|
||||
const { username, password } = request.body as IUserLoginBody;
|
||||
|
||||
if (!(username && password)) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
const { username, password } = UserLoginBody.parse(request.body);
|
||||
|
||||
const user = await User.findOne({ username });
|
||||
if (!user || !(await user.verifyPassword(password))) {
|
||||
ctx.throw(404, "User not found");
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.body = { error: false, data: user.toAuthJSON() } as IUserLoginRespBody;
|
||||
ctx.body = { error: false, data: user.toAuthJSON() } as TUserLoginRespBody;
|
||||
});
|
||||
|
||||
userRouter.post("/users/signup", async (ctx) => {
|
||||
userRouter.post("/users/signup", async (ctx: ContextType) => {
|
||||
const request = ctx.request;
|
||||
|
||||
if (!request.body) {
|
||||
ctx.throw(400);
|
||||
}
|
||||
|
||||
const { username, password, email } = request.body as IUserSignupBody;
|
||||
|
||||
if (!(username && password && email)) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
const { username, password, email } = UserSignupBody.parse(request.body);
|
||||
|
||||
const user = new User(username, email);
|
||||
const users = await User.find();
|
||||
@@ -91,33 +86,30 @@ userRouter.post("/users/signup", async (ctx) => {
|
||||
ctx.throw(500);
|
||||
}
|
||||
|
||||
ctx.body = { error: false, data: user.toAuthJSON() } as IUserSignupRespBody;
|
||||
ctx.body = { error: false, data: user.toAuthJSON() } as TUserSignupRespBody;
|
||||
});
|
||||
|
||||
userRouter.post("/users/edit", async (ctx) => {
|
||||
userRouter.post("/users/edit", async (ctx: ContextType) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
|
||||
const jwt = ctx.state.user as IUserJWT;
|
||||
const jwt = ctx.state.user;
|
||||
const user = await User.findOne(jwt.id);
|
||||
const request = ctx.request;
|
||||
|
||||
if (!user) {
|
||||
ctx.throw(401);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!request.body) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
const { password } = request.body as IUserEditBody;
|
||||
const { password } = UserEditBody.parse(request.body);
|
||||
|
||||
if (!password) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
await user.setPassword(password);
|
||||
@@ -129,5 +121,5 @@ userRouter.post("/users/edit", async (ctx) => {
|
||||
ctx.throw(500);
|
||||
}
|
||||
|
||||
ctx.body = { error: false, data: user.toAuthJSON() } as IUserEditRespBody;
|
||||
ctx.body = { error: false, data: user.toAuthJSON() } as TUserEditRespBody;
|
||||
});
|
||||
|
||||
@@ -4,10 +4,11 @@ import * as request from "supertest";
|
||||
import { getConnection } from "typeorm";
|
||||
import { app } from "~app";
|
||||
import { Photo } from "~entity/Photo";
|
||||
import { IPhotoReqJSON ,
|
||||
IPhotosDeleteBody,
|
||||
IPhotosListRespBody,
|
||||
IPhotosNewPostBody,
|
||||
import {
|
||||
TPhotoReqJSON,
|
||||
TPhotosDeleteBody,
|
||||
TPhotosListRespBody,
|
||||
TPhotosNewPostBody,
|
||||
} from "~shared/types";
|
||||
import * as fs from "fs/promises";
|
||||
import { constants as fsConstants } from "fs";
|
||||
@@ -60,7 +61,7 @@ describe("photos", function () {
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photo = response.body.data as IPhotoReqJSON;
|
||||
const photo = response.body.data as TPhotoReqJSON;
|
||||
|
||||
const usedPhoto = await seed.dogPhoto.toReqJSON();
|
||||
|
||||
@@ -85,9 +86,7 @@ describe("photos", function () {
|
||||
Authorization: `Bearer ${seed.user2.toJWT()}`,
|
||||
})
|
||||
.expect(200);
|
||||
expect(parseInt(response.header["content-length"])).to.equal(
|
||||
dogFileSize,
|
||||
);
|
||||
expect(parseInt(response.get("content-length"))).to.equal(dogFileSize);
|
||||
});
|
||||
|
||||
it("should delete a photo after file has been deleted", async function () {
|
||||
@@ -97,12 +96,10 @@ describe("photos", function () {
|
||||
Authorization: `Bearer ${seed.user2.toJWT()}`,
|
||||
})
|
||||
.expect(200);
|
||||
expect(parseInt(response.header["content-length"])).to.equal(
|
||||
dogFileSize,
|
||||
);
|
||||
expect(parseInt(response.get("content-length"))).to.equal(dogFileSize);
|
||||
|
||||
await fs.unlink(await seed.dogPhoto.getReadyPath("original"));
|
||||
const response2 = await request(callback)
|
||||
await request(callback)
|
||||
.get(`/photos/showByID/${seed.dogPhoto.id}`)
|
||||
.set({
|
||||
Authorization: `Bearer ${seed.user2.toJWT()}`,
|
||||
@@ -122,13 +119,13 @@ describe("photos", function () {
|
||||
const dogSmallThumbSize = (
|
||||
await fs.stat(seed.dogPhoto.getThumbPath("512"))
|
||||
).size;
|
||||
expect(parseInt(response.header["content-length"])).to.equal(
|
||||
expect(parseInt(response.get("content-length"))).to.equal(
|
||||
dogSmallThumbSize,
|
||||
);
|
||||
|
||||
await fs.unlink(await seed.dogPhoto.getReadyPath("512"));
|
||||
await fs.unlink(await seed.dogPhoto.getReadyPath("original"));
|
||||
const response2 = await request(callback)
|
||||
await request(callback)
|
||||
.get(`/photos/showByID/${seed.dogPhoto.id}?size=512`)
|
||||
.set({
|
||||
Authorization: `Bearer ${seed.user2.toJWT()}`,
|
||||
@@ -146,7 +143,7 @@ describe("photos", function () {
|
||||
Authorization: `Bearer ${seed.user2.toJWT()}`,
|
||||
})
|
||||
.expect(200);
|
||||
expect(parseInt(response.header["content-length"])).to.be.lessThan(
|
||||
expect(parseInt(response.get("content-length"))).to.be.lessThan(
|
||||
dogFileSize,
|
||||
);
|
||||
});
|
||||
@@ -161,12 +158,12 @@ describe("photos", function () {
|
||||
const dogSmallThumbSize = (
|
||||
await fs.stat(seed.dogPhoto.getThumbPath("512"))
|
||||
).size;
|
||||
expect(parseInt(response.header["content-length"])).to.equal(
|
||||
expect(parseInt(response.get("content-length"))).to.equal(
|
||||
dogSmallThumbSize,
|
||||
);
|
||||
|
||||
await fs.unlink(seed.dogPhoto.getThumbPath("512"));
|
||||
const response2 = await request(callback)
|
||||
await request(callback)
|
||||
.get(`/photos/showByID/${seed.dogPhoto.id}?size=512`)
|
||||
.set({
|
||||
Authorization: `Bearer ${seed.user2.toJWT()}`,
|
||||
@@ -175,7 +172,7 @@ describe("photos", function () {
|
||||
const dogSmallThumbSize2 = (
|
||||
await fs.stat(seed.dogPhoto.getThumbPath("512"))
|
||||
).size;
|
||||
expect(parseInt(response.header["content-length"])).to.equal(
|
||||
expect(parseInt(response.get("content-length"))).to.equal(
|
||||
dogSmallThumbSize2,
|
||||
);
|
||||
});
|
||||
@@ -188,7 +185,7 @@ describe("photos", function () {
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const listRespBody = listResp.body as IPhotosListRespBody;
|
||||
const listRespBody = listResp.body as TPhotosListRespBody;
|
||||
|
||||
if (listRespBody.error !== false) {
|
||||
expect(listResp.body.error).to.be.false;
|
||||
@@ -201,7 +198,7 @@ describe("photos", function () {
|
||||
const listAnyResp = await request(callback)
|
||||
.get(`/photos/showByID/${photos[0].id}/${photos[0].accessToken}`)
|
||||
.expect(200);
|
||||
expect(parseInt(listAnyResp.header["content-length"])).to.be.oneOf([
|
||||
expect(parseInt(listAnyResp.get("content-length"))).to.be.oneOf([
|
||||
dogFileSize,
|
||||
catFileSize,
|
||||
]);
|
||||
@@ -219,9 +216,7 @@ describe("photos", function () {
|
||||
const response = await request(callback)
|
||||
.get(`/photos/showByID/${seed.dogPhoto.id}/${token}`)
|
||||
.expect(200);
|
||||
expect(parseInt(response.header["content-length"])).to.equal(
|
||||
dogFileSize,
|
||||
);
|
||||
expect(parseInt(response.get("content-length"))).to.equal(dogFileSize);
|
||||
|
||||
const tokenSelfSigned = jwt.sign(
|
||||
await seed.dogPhoto.toReqJSON(),
|
||||
@@ -234,7 +229,7 @@ describe("photos", function () {
|
||||
const responseSS = await request(callback)
|
||||
.get(`/photos/showByID/${seed.dogPhoto.id}/${tokenSelfSigned}`)
|
||||
.expect(200);
|
||||
expect(parseInt(responseSS.header["content-length"])).to.equal(
|
||||
expect(parseInt(responseSS.get("content-length"))).to.equal(
|
||||
dogFileSize,
|
||||
);
|
||||
});
|
||||
@@ -248,7 +243,7 @@ describe("photos", function () {
|
||||
},
|
||||
);
|
||||
|
||||
const response = await request(callback)
|
||||
await request(callback)
|
||||
.get(`/photos/showByID/${seed.dogPhoto.id}/${token}`)
|
||||
.expect(401);
|
||||
});
|
||||
@@ -275,17 +270,17 @@ describe("photos", function () {
|
||||
hash: dogHash,
|
||||
size: dogSize,
|
||||
format: dogFormat,
|
||||
} as IPhotosNewPostBody)
|
||||
} as TPhotosNewPostBody)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photo = response.body.data as IPhotoReqJSON;
|
||||
const photo = response.body.data as TPhotoReqJSON;
|
||||
|
||||
expect(photo.hash).to.be.equal(dogHash);
|
||||
const dbPhoto = await Photo.findOneOrFail({
|
||||
id: photo.id,
|
||||
user: seed.user1.id as any,
|
||||
user: { id: seed.user1.id },
|
||||
});
|
||||
expect(dbPhoto.hash).to.be.equal(dogHash);
|
||||
|
||||
@@ -302,7 +297,7 @@ describe("photos", function () {
|
||||
|
||||
const dbPhotoUpl = await Photo.findOneOrFail({
|
||||
id: photo.id,
|
||||
user: seed.user1.id as any,
|
||||
user: { id: seed.user1.id },
|
||||
});
|
||||
expect(dbPhotoUpl.hash).to.be.equal(dogHash);
|
||||
expect(await dbPhotoUpl.origFileExists()).to.be.equal(true);
|
||||
@@ -317,9 +312,7 @@ describe("photos", function () {
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(parseInt(showResp.header["content-length"])).to.equal(
|
||||
dogFileSize,
|
||||
);
|
||||
expect(parseInt(showResp.get("content-length"))).to.equal(dogFileSize);
|
||||
});
|
||||
|
||||
it("should create, upload and show a png file", async function () {
|
||||
@@ -333,17 +326,17 @@ describe("photos", function () {
|
||||
hash: pngHash,
|
||||
size: pngSize,
|
||||
format: pngFormat,
|
||||
} as IPhotosNewPostBody)
|
||||
} as TPhotosNewPostBody)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photo = response.body.data as IPhotoReqJSON;
|
||||
const photo = response.body.data as TPhotoReqJSON;
|
||||
|
||||
expect(photo.hash).to.be.equal(pngHash);
|
||||
const dbPhoto = await Photo.findOneOrFail({
|
||||
id: photo.id,
|
||||
user: seed.user1.id as any,
|
||||
user: { id: seed.user1.id },
|
||||
});
|
||||
expect(dbPhoto.hash).to.be.equal(pngHash);
|
||||
|
||||
@@ -360,7 +353,7 @@ describe("photos", function () {
|
||||
|
||||
const dbPhotoUpl = await Photo.findOneOrFail({
|
||||
id: photo.id,
|
||||
user: seed.user1.id as any,
|
||||
user: { id: seed.user1.id },
|
||||
});
|
||||
expect(dbPhotoUpl.hash).to.be.equal(pngHash);
|
||||
expect(dbPhotoUpl.format).to.be.equal(pngFormat);
|
||||
@@ -377,9 +370,7 @@ describe("photos", function () {
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(parseInt(showResp.header["content-length"])).to.equal(
|
||||
pngFileSize,
|
||||
);
|
||||
expect(parseInt(showResp.get("content-length"))).to.equal(pngFileSize);
|
||||
});
|
||||
|
||||
it("should not create a photo twice", async function () {
|
||||
@@ -393,12 +384,12 @@ describe("photos", function () {
|
||||
hash: dogHash,
|
||||
size: dogSize,
|
||||
format: dogFormat,
|
||||
} as IPhotosNewPostBody)
|
||||
} as TPhotosNewPostBody)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const response2 = await request(callback)
|
||||
await request(callback)
|
||||
.post("/photos/new")
|
||||
.set({
|
||||
Authorization: `Bearer ${seed.user1.toJWT()}`,
|
||||
@@ -408,7 +399,7 @@ describe("photos", function () {
|
||||
hash: dogHash,
|
||||
size: dogSize,
|
||||
format: dogFormat,
|
||||
} as IPhotosNewPostBody)
|
||||
} as TPhotosNewPostBody)
|
||||
.expect(200);
|
||||
|
||||
const dbPhoto = await Photo.find({
|
||||
@@ -430,17 +421,17 @@ describe("photos", function () {
|
||||
hash: dogHash,
|
||||
size: dogSize,
|
||||
format: dogFormat,
|
||||
} as IPhotosNewPostBody)
|
||||
} as TPhotosNewPostBody)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photo = response.body.data as IPhotoReqJSON;
|
||||
const photo = response.body.data as TPhotoReqJSON;
|
||||
|
||||
expect(photo.hash).to.be.equal(dogHash);
|
||||
const dbPhoto = await Photo.findOneOrFail({
|
||||
id: photo.id,
|
||||
user: seed.user1.id as any,
|
||||
user: { id: seed.user1.id },
|
||||
});
|
||||
expect(dbPhoto.hash).to.be.equal(dogHash);
|
||||
|
||||
@@ -473,9 +464,7 @@ describe("photos", function () {
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(parseInt(showResp.header["content-length"])).to.equal(
|
||||
dogFileSize,
|
||||
);
|
||||
expect(parseInt(showResp.get("content-length"))).to.equal(dogFileSize);
|
||||
});
|
||||
|
||||
it("should not upload a wrong photo", async function () {
|
||||
@@ -489,17 +478,17 @@ describe("photos", function () {
|
||||
hash: dogHash,
|
||||
size: dogSize,
|
||||
format: dogFormat,
|
||||
} as IPhotosNewPostBody)
|
||||
} as TPhotosNewPostBody)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photo = response.body.data as IPhotoReqJSON;
|
||||
const photo = response.body.data as TPhotoReqJSON;
|
||||
|
||||
expect(photo.hash).to.be.equal(dogHash);
|
||||
const dbPhoto = await Photo.findOneOrFail({
|
||||
id: photo.id,
|
||||
user: seed.user1.id as any,
|
||||
user: { id: seed.user1.id },
|
||||
});
|
||||
expect(dbPhoto.hash).to.be.equal(dogHash);
|
||||
|
||||
@@ -516,7 +505,7 @@ describe("photos", function () {
|
||||
|
||||
expect(await dbPhoto.origFileExists()).to.be.equal(false);
|
||||
|
||||
const showResp = await request(callback)
|
||||
await request(callback)
|
||||
.get(`/photos/showByID/${photo.id}`)
|
||||
.set({
|
||||
Authorization: `Bearer ${seed.user1.toJWT()}`,
|
||||
@@ -535,17 +524,17 @@ describe("photos", function () {
|
||||
hash: dogHash,
|
||||
size: dogSize,
|
||||
format: dogFormat,
|
||||
} as IPhotosNewPostBody)
|
||||
} as TPhotosNewPostBody)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photo = response.body.data as IPhotoReqJSON;
|
||||
const photo = response.body.data as TPhotoReqJSON;
|
||||
|
||||
expect(photo.hash).to.be.equal(dogHash);
|
||||
const dbPhoto = await Photo.findOneOrFail({
|
||||
id: photo.id,
|
||||
user: seed.user1.id as any,
|
||||
user: { id: seed.user1.id },
|
||||
});
|
||||
expect(dbPhoto.hash).to.be.equal(dogHash);
|
||||
expect(await dbPhoto.origFileExists()).to.be.equal(false);
|
||||
@@ -573,17 +562,17 @@ describe("photos", function () {
|
||||
hash: dogHash,
|
||||
size: dogSize,
|
||||
format: dogFormat,
|
||||
} as IPhotosNewPostBody)
|
||||
} as TPhotosNewPostBody)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photo = response.body.data as IPhotoReqJSON;
|
||||
const photo = response.body.data as TPhotoReqJSON;
|
||||
|
||||
expect(photo.hash).to.be.equal(dogHash);
|
||||
const dbPhoto = await Photo.findOneOrFail({
|
||||
id: photo.id,
|
||||
user: seed.user1.id as any,
|
||||
user: { id: seed.user1.id },
|
||||
});
|
||||
expect(dbPhoto.hash).to.be.equal(dogHash);
|
||||
expect(await dbPhoto.origFileExists()).to.be.equal(false);
|
||||
@@ -608,7 +597,7 @@ describe("photos", function () {
|
||||
});
|
||||
|
||||
it("should not create a photo with weird properties", async function () {
|
||||
const response = await request(callback)
|
||||
await request(callback)
|
||||
.post("/photos/new")
|
||||
.set({
|
||||
Authorization: `Bearer ${seed.user1.toJWT()}`,
|
||||
@@ -618,7 +607,7 @@ describe("photos", function () {
|
||||
hash: "../test",
|
||||
size: "33333",
|
||||
format: dogFormat,
|
||||
} as IPhotosNewPostBody)
|
||||
} as TPhotosNewPostBody)
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
@@ -635,13 +624,13 @@ describe("photos", function () {
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photo = response.body.data as IPhotoReqJSON;
|
||||
const photo = response.body.data as TPhotoReqJSON;
|
||||
|
||||
expect(photo.name).to.be.equal("Test1");
|
||||
|
||||
const dbPhoto = await Photo.findOne({
|
||||
id: seed.dogPhoto.id,
|
||||
user: seed.user1.id as any,
|
||||
user: {id:seed.user1.id} ,
|
||||
});
|
||||
|
||||
expect(dbPhoto.name).to.be.equal("Test1");
|
||||
@@ -662,7 +651,7 @@ describe("photos", function () {
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photos = response.body.data as IPhotoReqJSON[];
|
||||
const photos = response.body.data as TPhotoReqJSON[];
|
||||
const userPhotos = [
|
||||
await seed.dogPhoto.toReqJSON(),
|
||||
await seed.catPhoto.toReqJSON(),
|
||||
@@ -685,7 +674,7 @@ describe("photos", function () {
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
const photo = response.body.data as IPhotoReqJSON;
|
||||
const photo = response.body.data as TPhotoReqJSON;
|
||||
|
||||
const usedPhoto = seed.catPhoto.toReqJSON();
|
||||
|
||||
@@ -704,7 +693,7 @@ describe("photos", function () {
|
||||
})
|
||||
.send({
|
||||
photos: [await seed.dogPhoto.toReqJSON()],
|
||||
} as IPhotosDeleteBody)
|
||||
} as TPhotosDeleteBody)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
@@ -736,7 +725,7 @@ describe("photos", function () {
|
||||
await seed.dogPhoto.toReqJSON(),
|
||||
await seed.catPhoto.toReqJSON(),
|
||||
],
|
||||
} as IPhotosDeleteBody)
|
||||
} as TPhotosDeleteBody)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.error).to.be.false;
|
||||
|
||||
@@ -5,13 +5,13 @@ import { getConnection } from "typeorm";
|
||||
import { app } from "~app";
|
||||
import { User } from "~entity/User";
|
||||
import {
|
||||
IUserEditBody,
|
||||
IUserEditRespBody,
|
||||
IUserGetRespBody,
|
||||
IUserLoginBody,
|
||||
IUserLoginRespBody,
|
||||
IUserSignupBody,
|
||||
IUserSignupRespBody,
|
||||
TUserEditBody,
|
||||
TUserEditRespBody,
|
||||
TUserGetRespBody,
|
||||
TUserLoginBody,
|
||||
TUserLoginRespBody,
|
||||
TUserSignupBody,
|
||||
TUserSignupRespBody,
|
||||
} from "~shared/types";
|
||||
|
||||
import { allowSignups, ISeed, seedDB } from "./util";
|
||||
@@ -43,13 +43,13 @@ describe("users", function () {
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200);
|
||||
|
||||
const body = response.body as IUserGetRespBody;
|
||||
const body = response.body as TUserGetRespBody;
|
||||
|
||||
if (body.error !== false) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { jwt: _, ...user } = body.data;
|
||||
|
||||
expect(user).to.deep.equal(seed.user1.toJSON());
|
||||
@@ -59,17 +59,17 @@ describe("users", function () {
|
||||
const response = await request(callback)
|
||||
.post("/users/login")
|
||||
.set({ "Content-Type": "application/json" })
|
||||
.send({ username: "User1", password: "User1" } as IUserLoginBody)
|
||||
.send({ username: "User1", password: "User1" } as TUserLoginBody)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200);
|
||||
|
||||
const body = response.body as IUserLoginRespBody;
|
||||
const body = response.body as TUserLoginRespBody;
|
||||
|
||||
if (body.error !== false) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { jwt: _, ...user } = response.body.data;
|
||||
expect(user).to.deep.equal(seed.user1.toJSON());
|
||||
});
|
||||
@@ -78,10 +78,10 @@ describe("users", function () {
|
||||
const response = await request(callback)
|
||||
.post("/users/login")
|
||||
.set({ "Content-Type": "application/json" })
|
||||
.send({ username: "User1", password: "asdf" } as IUserLoginBody)
|
||||
.send({ username: "User1", password: "asdf" } as TUserLoginBody)
|
||||
.expect(404);
|
||||
|
||||
const body = response.body as IUserLoginRespBody;
|
||||
const body = response.body as TUserLoginRespBody;
|
||||
expect(body.error).to.be.equal("User not found");
|
||||
expect(body.data).to.be.false;
|
||||
});
|
||||
@@ -96,17 +96,17 @@ describe("users", function () {
|
||||
username: "NUser1",
|
||||
password: "NUser1",
|
||||
email: "nuser1@users.com",
|
||||
} as IUserSignupBody)
|
||||
} as TUserSignupBody)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200);
|
||||
|
||||
const body = response.body as IUserSignupRespBody;
|
||||
const body = response.body as TUserSignupRespBody;
|
||||
|
||||
if (body.error !== false) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { jwt: _, ...user } = body.data;
|
||||
const newUser = await User.findOneOrFail({ username: "NUser1" });
|
||||
expect(user).to.deep.equal(newUser.toJSON());
|
||||
@@ -120,11 +120,11 @@ describe("users", function () {
|
||||
username: "NUser1",
|
||||
password: "NUser1",
|
||||
email: "nuser1@users.com",
|
||||
} as IUserSignupBody)
|
||||
} as TUserSignupBody)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(400);
|
||||
|
||||
const body = response.body as IUserSignupRespBody;
|
||||
const body = response.body as TUserSignupRespBody;
|
||||
|
||||
expect(body.error).to.be.equal("Signups not allowed");
|
||||
expect(body.data).to.be.false;
|
||||
@@ -140,17 +140,17 @@ describe("users", function () {
|
||||
username: "NUser1",
|
||||
password: "NUser1",
|
||||
email: "nuser1@users.com",
|
||||
} as IUserSignupBody)
|
||||
} as TUserSignupBody)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200);
|
||||
|
||||
const body = response.body as IUserSignupRespBody;
|
||||
const body = response.body as TUserSignupRespBody;
|
||||
|
||||
if (body.error !== false) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { jwt: _, ...user } = body.data;
|
||||
const newUser = await User.findOneOrFail({ username: "NUser1" });
|
||||
expect(user).to.deep.equal(newUser.toJSON());
|
||||
@@ -163,11 +163,11 @@ describe("users", function () {
|
||||
username: "NUser2",
|
||||
password: "NUser2",
|
||||
email: "nuser2@users.com",
|
||||
} as IUserSignupBody)
|
||||
} as TUserSignupBody)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(400);
|
||||
|
||||
const body2 = response2.body as IUserSignupRespBody;
|
||||
const body2 = response2.body as TUserSignupRespBody;
|
||||
|
||||
expect(body2.error).to.be.equal("Signups not allowed");
|
||||
expect(body2.data).to.be.false;
|
||||
@@ -184,17 +184,17 @@ describe("users", function () {
|
||||
username: "NUser1",
|
||||
password: "NUser1",
|
||||
email: "nuser1@users.com",
|
||||
} as IUserSignupBody)
|
||||
} as TUserSignupBody)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200);
|
||||
|
||||
const body = response.body as IUserSignupRespBody;
|
||||
const body = response.body as TUserSignupRespBody;
|
||||
|
||||
if (body.error !== false) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { jwt: jwt1, ...user } = body.data;
|
||||
const newUser = await User.findOneOrFail({ username: "NUser1" });
|
||||
expect(user).to.deep.equal(newUser.toJSON());
|
||||
@@ -207,17 +207,17 @@ describe("users", function () {
|
||||
username: "NUser2",
|
||||
password: "NUser2",
|
||||
email: "nuser2@users.com",
|
||||
} as IUserSignupBody)
|
||||
} as TUserSignupBody)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200);
|
||||
|
||||
const body2 = response2.body as IUserSignupRespBody;
|
||||
const body2 = response2.body as TUserSignupRespBody;
|
||||
|
||||
if (body2.error !== false) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { jwt: jwt2, ...user2 } = body2.data;
|
||||
const newUser2 = await User.findOneOrFail({ username: "NUser2" });
|
||||
expect(user2).to.deep.equal(newUser2.toJSON());
|
||||
@@ -234,10 +234,10 @@ describe("users", function () {
|
||||
username: "User1",
|
||||
password: "NUser1",
|
||||
email: "user1@users.com",
|
||||
} as IUserSignupBody)
|
||||
} as TUserSignupBody)
|
||||
.expect(400);
|
||||
|
||||
const body = response.body as IUserSignupRespBody;
|
||||
const body = response.body as TUserSignupRespBody;
|
||||
|
||||
expect(body.error).to.be.equal("User already exists");
|
||||
expect(body.data).to.be.false;
|
||||
@@ -252,15 +252,14 @@ describe("users", function () {
|
||||
})
|
||||
.send({
|
||||
password: "User1NewPass",
|
||||
} as IUserEditBody)
|
||||
} as TUserEditBody)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200);
|
||||
|
||||
const body = response.body as IUserEditRespBody;
|
||||
const body = response.body as TUserEditRespBody;
|
||||
|
||||
if (body.error !== false) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const loginResponse = await request(callback)
|
||||
@@ -269,27 +268,27 @@ describe("users", function () {
|
||||
.send({
|
||||
username: "User1",
|
||||
password: "User1NewPass",
|
||||
} as IUserLoginBody)
|
||||
} as TUserLoginBody)
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200);
|
||||
|
||||
const loginBody = loginResponse.body as IUserLoginRespBody;
|
||||
const loginBody = loginResponse.body as TUserLoginRespBody;
|
||||
|
||||
if (loginBody.error !== false) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { jwt: _, ...user } = loginBody.data;
|
||||
expect(user).to.deep.equal(seed.user1.toJSON());
|
||||
|
||||
const badLoginResponse = await request(callback)
|
||||
.post("/users/login")
|
||||
.set({ "Content-Type": "application/json" })
|
||||
.send({ username: "User1", password: "User1" } as IUserLoginBody)
|
||||
.send({ username: "User1", password: "User1" } as TUserLoginBody)
|
||||
.expect(404);
|
||||
|
||||
const badLoginBody = badLoginResponse.body as IUserLoginRespBody;
|
||||
const badLoginBody = badLoginResponse.body as TUserLoginRespBody;
|
||||
|
||||
expect(badLoginBody.error).to.be.equal("User not found");
|
||||
expect(badLoginBody.data).to.be.false;
|
||||
|
||||
@@ -24,8 +24,5 @@
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"./tests/**/*.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"frontend"
|
||||
]
|
||||
}
|
||||
99
frontend/package-lock.json
generated
99
frontend/package-lock.json
generated
@@ -26,7 +26,6 @@
|
||||
"eslint-plugin-react": "^7.33.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"flush-promises": "^1.0.2",
|
||||
"io-ts": "^2.2.20",
|
||||
"jest": "^29.6.2",
|
||||
"jest-environment-jsdom": "^29.6.2",
|
||||
"jest-junit": "^14.0.1",
|
||||
@@ -140,9 +139,9 @@
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
|
||||
},
|
||||
"node_modules/@babel/core/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -180,9 +179,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -5963,9 +5962,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-jsx-a11y/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -6372,12 +6371,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fp-ts": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.1.tgz",
|
||||
"integrity": "sha512-by7U5W8dkIzcvDofUcO42yl9JbnHTEDBrzu3pt5fKT+Z4Oy85I21K80EYJYdjQGC2qum4Vo55Ag57iiIK4FYuA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@@ -6962,14 +6955,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/io-ts": {
|
||||
"version": "2.2.20",
|
||||
"resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.2.20.tgz",
|
||||
"integrity": "sha512-Rq2BsYmtwS5vVttie4rqrOCIfHCS9TgpRLFpKQCM1wZBBRY9nWVGmEvm2FnDbSE2un1UE39DvFpTR5UL47YDcA==",
|
||||
"peerDependencies": {
|
||||
"fp-ts": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||
@@ -7387,9 +7372,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/istanbul-lib-instrument/node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@@ -10820,9 +10805,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tough-cookie": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
|
||||
"integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"dependencies": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
@@ -11351,9 +11336,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -11540,9 +11525,9 @@
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11570,9 +11555,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -15526,9 +15511,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -15810,12 +15795,6 @@
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"fp-ts": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.1.tgz",
|
||||
"integrity": "sha512-by7U5W8dkIzcvDofUcO42yl9JbnHTEDBrzu3pt5fKT+Z4Oy85I21K80EYJYdjQGC2qum4Vo55Ag57iiIK4FYuA==",
|
||||
"peer": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@@ -16204,12 +16183,6 @@
|
||||
"side-channel": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"io-ts": {
|
||||
"version": "2.2.20",
|
||||
"resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.2.20.tgz",
|
||||
"integrity": "sha512-Rq2BsYmtwS5vVttie4rqrOCIfHCS9TgpRLFpKQCM1wZBBRY9nWVGmEvm2FnDbSE2un1UE39DvFpTR5UL47YDcA==",
|
||||
"requires": {}
|
||||
},
|
||||
"is-arguments": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||
@@ -16479,9 +16452,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -18916,9 +18889,9 @@
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
|
||||
"integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
|
||||
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
|
||||
"requires": {
|
||||
"psl": "^1.1.33",
|
||||
"punycode": "^2.1.1",
|
||||
@@ -19292,9 +19265,9 @@
|
||||
}
|
||||
},
|
||||
"word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
"eslint-plugin-react": "^7.33.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"flush-promises": "^1.0.2",
|
||||
"io-ts": "^2.2.20",
|
||||
"jest": "^29.6.2",
|
||||
"jest-environment-jsdom": "^29.6.2",
|
||||
"jest-junit": "^14.0.1",
|
||||
|
||||
@@ -16,7 +16,7 @@ export function AccountComponent(props: IAccountComponentProps) {
|
||||
return (
|
||||
<Card className="AuthForm" elevation={2}>
|
||||
<form
|
||||
onSubmit={(e: React.FormEvent<any>) => {
|
||||
onSubmit={(e: React.FormEvent<never>) => {
|
||||
e.preventDefault();
|
||||
if (pass.trim()) {
|
||||
props.changePass(pass);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Position, Toaster } from "@blueprintjs/core";
|
||||
import { IPhotoReqJSON } from "./shared/types";
|
||||
import { TPhotoReqJSON } from "./shared/types";
|
||||
|
||||
export const AppToaster = Toaster.create({
|
||||
className: "recipe-toaster",
|
||||
@@ -43,7 +43,7 @@ export function showPhotoCreateFailToast(f: File, e: string): void {
|
||||
}
|
||||
|
||||
export function showPhotoUploadJSONFailToast(
|
||||
p: IPhotoReqJSON | number,
|
||||
p: TPhotoReqJSON | number,
|
||||
e: string,
|
||||
): void {
|
||||
const photoMsg = typeof p === "number" ? p : p.hash;
|
||||
|
||||
@@ -23,7 +23,9 @@ export class AuthScreenComponent extends React.PureComponent<IAuthScreenProps> {
|
||||
}
|
||||
public render() {
|
||||
const { location } = this.props.history;
|
||||
const { from } = (this.props.location.state as any) || { from: "/" };
|
||||
const { from } = (this.props.location.state as { from: string }) || {
|
||||
from: "/",
|
||||
};
|
||||
const { loggedIn } = this.props;
|
||||
return loggedIn ? (
|
||||
<Redirect to={from} />
|
||||
@@ -46,7 +48,7 @@ export class AuthScreenComponent extends React.PureComponent<IAuthScreenProps> {
|
||||
transform: "translate3d(400px,0,0)",
|
||||
}}
|
||||
>
|
||||
{(_location: any) => (style: any) => (
|
||||
{(_location) => (style) => (
|
||||
<animated.div style={style}>
|
||||
<Switch location={_location}>
|
||||
<Route path="/login" component={Login} />
|
||||
|
||||
@@ -36,7 +36,7 @@ export class LoginComponent extends React.PureComponent<
|
||||
this.props.history.push("/signup");
|
||||
}
|
||||
|
||||
public submit(e: React.FormEvent<any>) {
|
||||
public submit<T extends React.FormEvent>(e: T) {
|
||||
e.preventDefault();
|
||||
const { username, password } = this.state;
|
||||
if (!this.props.inProgress) {
|
||||
|
||||
@@ -37,7 +37,7 @@ export class SignupComponent extends React.PureComponent<
|
||||
this.props.history.push("/login");
|
||||
}
|
||||
|
||||
public submit(e: React.FormEvent<any>) {
|
||||
public submit<T extends React.FormEvent>(e: T) {
|
||||
e.preventDefault();
|
||||
const { username, password, email } = this.state;
|
||||
if (!this.props.inProgress) {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { connect } from "react-redux";
|
||||
import { Route, RouteComponentProps, Switch, withRouter } from "react-router";
|
||||
import { animated, config, Transition } from "react-spring/renderprops";
|
||||
import { Dispatch } from "redux";
|
||||
import { IUserJSON } from "../shared/types";
|
||||
import { TUserJSON } from "../shared/types";
|
||||
import { Account } from "../Account/Account";
|
||||
import { Overview } from "../Photos/Overview";
|
||||
import { toggleDarkMode } from "../redux/localSettings/actions";
|
||||
@@ -24,7 +24,7 @@ import { PhotoRoute } from "../Photos/PhotoRoute";
|
||||
import { UploadStatus } from "./UploadStatus";
|
||||
|
||||
export interface IHomeProps extends RouteComponentProps {
|
||||
user: IUserJSON | null;
|
||||
user: TUserJSON | null;
|
||||
|
||||
darkMode: boolean;
|
||||
|
||||
@@ -90,7 +90,7 @@ export class HomeComponent extends React.PureComponent<IHomeProps> {
|
||||
transform: "translate3d(400px,0,0)",
|
||||
}}
|
||||
>
|
||||
{(_location: any) => (style: any) => (
|
||||
{(_location) => (style) => (
|
||||
<animated.div
|
||||
style={style}
|
||||
className="viewComponent"
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
photosDeleteStart,
|
||||
photosLoadStart,
|
||||
} from "../redux/photos/actions";
|
||||
import { IPhotoReqJSON } from "~/src/shared/types";
|
||||
import { TPhotoReqJSON } from "~/src/shared/types";
|
||||
import { PhotoCard } from "./PhotoCard";
|
||||
import {
|
||||
Alignment,
|
||||
@@ -26,7 +26,7 @@ import { Photo } from "./Photo";
|
||||
import { showDeletionToast } from "~/src/AppToaster";
|
||||
|
||||
export interface IOverviewComponentProps {
|
||||
photos: IPhotoReqJSON[];
|
||||
photos: TPhotoReqJSON[];
|
||||
triedLoading: boolean;
|
||||
allPhotosLoaded: boolean;
|
||||
overviewFetching: boolean;
|
||||
@@ -35,8 +35,8 @@ export interface IOverviewComponentProps {
|
||||
darkMode: boolean;
|
||||
|
||||
fetchPhotos: () => void;
|
||||
startDeletePhotos: (photos: IPhotoReqJSON[]) => void;
|
||||
cancelDelete: (photos: IPhotoReqJSON[]) => void;
|
||||
startDeletePhotos: (photos: TPhotoReqJSON[]) => void;
|
||||
cancelDelete: (photos: TPhotoReqJSON[]) => void;
|
||||
}
|
||||
|
||||
const PhotoCardM = React.memo(PhotoCard);
|
||||
@@ -80,7 +80,7 @@ export const OverviewComponent: React.FunctionComponent<
|
||||
(
|
||||
acc: Record<
|
||||
string,
|
||||
Record<string, Record<string, IPhotoReqJSON[]>>
|
||||
Record<string, Record<string, TPhotoReqJSON[]>>
|
||||
>,
|
||||
photo,
|
||||
) => {
|
||||
@@ -108,7 +108,7 @@ export const OverviewComponent: React.FunctionComponent<
|
||||
const els = Object.keys(dates[year]).reduce(
|
||||
(accMonths: JSX.Element[], month): JSX.Element[] => {
|
||||
const photos = Object.values(dates[year][month]).reduce(
|
||||
(accDays: IPhotoReqJSON[], day) => {
|
||||
(accDays: TPhotoReqJSON[], day) => {
|
||||
return [...day, ...accDays];
|
||||
},
|
||||
[],
|
||||
@@ -264,9 +264,9 @@ function mapStateToProps(state: IAppState) {
|
||||
function mapDispatchToProps(dispatch: Dispatch) {
|
||||
return {
|
||||
fetchPhotos: () => dispatch(photosLoadStart()),
|
||||
startDeletePhotos: (photos: IPhotoReqJSON[]) =>
|
||||
startDeletePhotos: (photos: TPhotoReqJSON[]) =>
|
||||
dispatch(photosDeleteStart(photos)),
|
||||
cancelDelete: (photos: IPhotoReqJSON[]) =>
|
||||
cancelDelete: (photos: TPhotoReqJSON[]) =>
|
||||
dispatch(photosDeleteCancel(photos)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,24 +2,31 @@ import "./Photo.scss";
|
||||
import * as React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Dispatch } from "redux";
|
||||
import { IPhotoReqJSON } from "~/src/shared/types";
|
||||
import { TPhotoReqJSON } from "~/src/shared/types";
|
||||
import { LoadingStub } from "../LoadingStub";
|
||||
import { getPhotoImgPath, getPhotoThumbPath } from "../redux/api/photos";
|
||||
import { photoLoadStart } from "../redux/photos/actions";
|
||||
import { IPhotoState } from "../redux/photos/reducer";
|
||||
import { TPhotoState } from "../redux/photos/reducer";
|
||||
import { IAppState } from "../redux/reducers";
|
||||
import { LargeSize, PreviewSize } from "./helper";
|
||||
|
||||
export interface IPhotoComponentProps {
|
||||
id: number;
|
||||
photo: IPhotoReqJSON | undefined;
|
||||
photoState: IPhotoState | undefined;
|
||||
type StateProps = {
|
||||
photo: TPhotoReqJSON | undefined;
|
||||
photoState: TPhotoState | undefined;
|
||||
};
|
||||
|
||||
type DispatchProps = {
|
||||
fetchPhoto: (id: number) => void;
|
||||
close: () => void;
|
||||
}
|
||||
};
|
||||
|
||||
export const PhotoComponent: React.FunctionComponent<IPhotoComponentProps> = (
|
||||
type OwnProps = {
|
||||
id: number;
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
export type TPhotoComponentProps = StateProps & DispatchProps & OwnProps;
|
||||
|
||||
export const PhotoComponent: React.FunctionComponent<TPhotoComponentProps> = (
|
||||
props,
|
||||
) => {
|
||||
const [smallPreviewFetching, setSmallPreviewFetching] =
|
||||
@@ -114,7 +121,7 @@ export const PhotoComponent: React.FunctionComponent<IPhotoComponentProps> = (
|
||||
);
|
||||
};
|
||||
|
||||
function mapStateToProps(state: IAppState, props: IPhotoComponentProps) {
|
||||
function mapStateToProps(state: IAppState, props: OwnProps) {
|
||||
const { id } = props;
|
||||
return {
|
||||
photo: state.photos?.photos?.find((p) => p.id === id),
|
||||
@@ -126,8 +133,7 @@ function mapDispatchToProps(dispatch: Dispatch) {
|
||||
return { fetchPhoto: (id: number) => dispatch(photoLoadStart(id)) };
|
||||
}
|
||||
|
||||
// Because https://github.com/DefinitelyTyped/DefinitelyTyped/issues/16990
|
||||
export const Photo = connect(
|
||||
export const Photo = connect<StateProps, DispatchProps, OwnProps, IAppState>(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
)(PhotoComponent) as any;
|
||||
)(PhotoComponent);
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Spinner,
|
||||
} from "@blueprintjs/core";
|
||||
import * as React from "react";
|
||||
import { IPhotoReqJSON } from "~/src/shared/types";
|
||||
import { TPhotoReqJSON } from "~/src/shared/types";
|
||||
import { getPhotoThumbPath } from "../redux/api/photos";
|
||||
import { showDeletionToast } from "../AppToaster";
|
||||
import { Dispatch } from "redux";
|
||||
@@ -17,30 +17,30 @@ import { connect } from "react-redux";
|
||||
import { RouteComponentProps, withRouter } from "react-router";
|
||||
import { PreviewSize } from "./helper";
|
||||
|
||||
export interface IPhotoCardComponentProps extends RouteComponentProps {
|
||||
photo: IPhotoReqJSON;
|
||||
export interface TPhotoCardComponentProps extends RouteComponentProps {
|
||||
photo: TPhotoReqJSON;
|
||||
selected: boolean;
|
||||
id: string;
|
||||
|
||||
deletePhoto: (photos: IPhotoReqJSON[]) => void;
|
||||
cancelDelete: (photos: IPhotoReqJSON[]) => void;
|
||||
deletePhoto: (photos: TPhotoReqJSON[]) => void;
|
||||
cancelDelete: (photos: TPhotoReqJSON[]) => void;
|
||||
onClick: (e: React.MouseEvent<HTMLElement>) => void;
|
||||
}
|
||||
|
||||
export interface IPhotoCardComponentState {
|
||||
export interface TPhotoCardComponentState {
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
const defaultPhotoCardState: IPhotoCardComponentState = {
|
||||
const defaultPhotoCardState: TPhotoCardComponentState = {
|
||||
loaded: false,
|
||||
};
|
||||
|
||||
@ContextMenuTarget
|
||||
export class PhotoCardComponent extends React.PureComponent<
|
||||
IPhotoCardComponentProps,
|
||||
IPhotoCardComponentState
|
||||
TPhotoCardComponentProps,
|
||||
TPhotoCardComponentState
|
||||
> {
|
||||
constructor(props: IPhotoCardComponentProps) {
|
||||
constructor(props: TPhotoCardComponentProps) {
|
||||
super(props);
|
||||
|
||||
this.handleDelete = this.handleDelete.bind(this);
|
||||
@@ -121,9 +121,9 @@ export class PhotoCardComponent extends React.PureComponent<
|
||||
|
||||
function mapDispatchToProps(dispatch: Dispatch) {
|
||||
return {
|
||||
deletePhoto: (photos: IPhotoReqJSON[]) =>
|
||||
deletePhoto: (photos: TPhotoReqJSON[]) =>
|
||||
dispatch(photosDeleteStart(photos)),
|
||||
cancelDelete: (photos: IPhotoReqJSON[]) =>
|
||||
cancelDelete: (photos: TPhotoReqJSON[]) =>
|
||||
dispatch(photosDeleteCancel(photos)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@ function getId(props: RouteComponentProps) {
|
||||
return parseInt((props.match?.params as { id: string }).id);
|
||||
}
|
||||
|
||||
export const PhotoRouteComponent: React.FunctionComponent<RouteComponentProps> = (
|
||||
props: RouteComponentProps,
|
||||
) => {
|
||||
export const PhotoRouteComponent: React.FunctionComponent<
|
||||
RouteComponentProps
|
||||
> = (props: RouteComponentProps) => {
|
||||
const id = getId(props);
|
||||
|
||||
return <Photo id={id} />;
|
||||
return <Photo id={id} close={() => {}} />;
|
||||
};
|
||||
|
||||
export const PhotoRoute = withRouter(PhotoRouteComponent);
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { IUserLoginRespBody, IUserSignupRespBody } from "~/src/shared/types";
|
||||
import {
|
||||
TUserLoginRespBody,
|
||||
TUserSignupRespBody,
|
||||
UserLoginRespBody,
|
||||
UserSignupRespBody,
|
||||
} from "~/src/shared/types";
|
||||
import { fetchJSON } from "../utils";
|
||||
|
||||
export async function login(
|
||||
username: string,
|
||||
password: string,
|
||||
): Promise<IUserLoginRespBody> {
|
||||
return fetchJSON("/users/login", "POST", {
|
||||
): Promise<TUserLoginRespBody> {
|
||||
return fetchJSON("/users/login", "POST", UserLoginRespBody, {
|
||||
username,
|
||||
password,
|
||||
});
|
||||
@@ -15,8 +20,8 @@ export async function signup(
|
||||
username: string,
|
||||
password: string,
|
||||
email: string,
|
||||
): Promise<IUserSignupRespBody> {
|
||||
return fetchJSON("/users/signup", "POST", {
|
||||
): Promise<TUserSignupRespBody> {
|
||||
return fetchJSON("/users/signup", "POST", UserSignupRespBody, {
|
||||
username,
|
||||
password,
|
||||
email,
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
import { IPhotoReqJSON } from "~/src/shared/types";
|
||||
import {
|
||||
IPhotosByIDGetRespBody,
|
||||
IPhotosDeleteRespBody,
|
||||
IPhotosListRespBody,
|
||||
IPhotosNewRespBody,
|
||||
IPhotosUploadRespBody,
|
||||
PhotosByIDGetRespBody,
|
||||
PhotosDeleteRespBody,
|
||||
PhotosListRespBody,
|
||||
PhotosNewRespBody,
|
||||
PhotosUploadRespBody,
|
||||
TPhotoReqJSON,
|
||||
} from "~/src/shared/types";
|
||||
import {
|
||||
TPhotosByIDGetRespBody,
|
||||
TPhotosDeleteRespBody,
|
||||
TPhotosListRespBody,
|
||||
TPhotosNewRespBody,
|
||||
TPhotosUploadRespBody,
|
||||
} from "~/src/shared/types";
|
||||
import { apiRoot } from "~src/env";
|
||||
import { fetchJSONAuth } from "./utils";
|
||||
|
||||
export function getPhotoImgPath(photo: IPhotoReqJSON): string {
|
||||
export function getPhotoImgPath(photo: TPhotoReqJSON): string {
|
||||
return `${apiRoot}/photos/showByID/${photo.id}/${photo.accessToken}`;
|
||||
}
|
||||
|
||||
export function getPhotoThumbPath(photo: IPhotoReqJSON, size: number): string {
|
||||
export function getPhotoThumbPath(photo: TPhotoReqJSON, size: number): string {
|
||||
return `${apiRoot}/photos/showByID/${photo.id}/${
|
||||
photo.accessToken
|
||||
}?size=${size.toString()}`;
|
||||
@@ -22,35 +29,50 @@ export function getPhotoThumbPath(photo: IPhotoReqJSON, size: number): string {
|
||||
export async function fetchPhotosList(
|
||||
skip: number,
|
||||
num: number,
|
||||
): Promise<IPhotosListRespBody> {
|
||||
): Promise<TPhotosListRespBody> {
|
||||
const params = new URLSearchParams({
|
||||
skip: skip.toString(),
|
||||
num: num.toString(),
|
||||
});
|
||||
return fetchJSONAuth(`/photos/list?${params.toString()}`, "GET");
|
||||
return fetchJSONAuth(
|
||||
`/photos/list?${params.toString()}`,
|
||||
"GET",
|
||||
PhotosListRespBody,
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchPhoto(id: number): Promise<IPhotosByIDGetRespBody> {
|
||||
return fetchJSONAuth(`/photos/byID/${id}`, "GET");
|
||||
export async function fetchPhoto(id: number): Promise<TPhotosByIDGetRespBody> {
|
||||
return fetchJSONAuth(`/photos/byID/${id}`, "GET", PhotosByIDGetRespBody);
|
||||
}
|
||||
|
||||
export async function createPhoto(
|
||||
hash: string,
|
||||
size: string,
|
||||
format: string,
|
||||
): Promise<IPhotosNewRespBody> {
|
||||
return fetchJSONAuth("/photos/new", "POST", { hash, size, format });
|
||||
): Promise<TPhotosNewRespBody> {
|
||||
return fetchJSONAuth("/photos/new", "POST", PhotosNewRespBody, {
|
||||
hash,
|
||||
size,
|
||||
format,
|
||||
});
|
||||
}
|
||||
|
||||
export async function uploadPhoto(
|
||||
file: File,
|
||||
id: number,
|
||||
): Promise<IPhotosUploadRespBody> {
|
||||
return fetchJSONAuth(`/photos/upload/${id}`, "POST", file);
|
||||
): Promise<TPhotosUploadRespBody> {
|
||||
return fetchJSONAuth(
|
||||
`/photos/upload/${id}`,
|
||||
"POST",
|
||||
PhotosUploadRespBody,
|
||||
file,
|
||||
);
|
||||
}
|
||||
|
||||
export async function deletePhotos(
|
||||
photos: IPhotoReqJSON[],
|
||||
): Promise<IPhotosDeleteRespBody> {
|
||||
return fetchJSONAuth(`/photos/delete`, "POST", { photos });
|
||||
photos: TPhotoReqJSON[],
|
||||
): Promise<TPhotosDeleteRespBody> {
|
||||
return fetchJSONAuth(`/photos/delete`, "POST", PhotosDeleteRespBody, {
|
||||
photos,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { fetchJSONAuth } from "../utils";
|
||||
import { IUserEditRespBody, IUserGetRespBody } from "~/src/shared/types";
|
||||
import {
|
||||
TUserEditRespBody,
|
||||
TUserGetRespBody,
|
||||
UserEditRespBody,
|
||||
UserGetRespBody,
|
||||
} from "~/src/shared/types";
|
||||
|
||||
export async function fetchUser(): Promise<IUserGetRespBody> {
|
||||
return fetchJSONAuth(
|
||||
"/users/user",
|
||||
"GET",
|
||||
) as unknown as Promise<IUserGetRespBody>;
|
||||
export async function fetchUser(): Promise<TUserGetRespBody> {
|
||||
return fetchJSONAuth("/users/user", "GET", UserGetRespBody);
|
||||
}
|
||||
|
||||
export async function changeUserPassword(
|
||||
newPassword: string,
|
||||
): Promise<IUserEditRespBody> {
|
||||
return fetchJSONAuth("/users/edit", "POST", {
|
||||
): Promise<TUserEditRespBody> {
|
||||
return fetchJSONAuth("/users/edit", "POST", UserEditRespBody, {
|
||||
password: newPassword,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { apiRoot } from "~src/env";
|
||||
import { IAPIResponse } from "~/src/shared/types";
|
||||
|
||||
let token: string | null;
|
||||
|
||||
@@ -15,49 +14,44 @@ export function deleteToken(): void {
|
||||
token = null;
|
||||
}
|
||||
|
||||
export async function fetchJSON<T>(
|
||||
export async function fetchJSON<T, P extends { parse: (string) => T }>(
|
||||
path: string,
|
||||
method: string,
|
||||
parser: P,
|
||||
body?: string | Record<string, unknown> | File,
|
||||
headers?: Record<string, string>,
|
||||
): Promise<IAPIResponse<T>> {
|
||||
if (typeof body === "object" && !(body instanceof File)) {
|
||||
body = JSON.stringify(body);
|
||||
headers = {
|
||||
...headers,
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
}
|
||||
// TODO: io-ts or something like that
|
||||
if (body instanceof File) {
|
||||
const formData = new FormData();
|
||||
formData.append("photo", body);
|
||||
const response = await fetch(apiRoot + path, {
|
||||
method,
|
||||
headers,
|
||||
body: formData,
|
||||
});
|
||||
const json = (await response.json()) as Record<string, unknown>;
|
||||
return json as unknown as IAPIResponse<T>;
|
||||
}
|
||||
): Promise<T> {
|
||||
const reqBody = () =>
|
||||
body instanceof File
|
||||
? (() => {
|
||||
const fd = new FormData();
|
||||
fd.append("photo", body);
|
||||
return fd;
|
||||
})()
|
||||
: JSON.stringify(body);
|
||||
|
||||
const reqHeaders = () =>
|
||||
body instanceof File
|
||||
? headers
|
||||
: { ...headers, "Content-Type": "application/json" };
|
||||
|
||||
const response = await fetch(apiRoot + path, {
|
||||
method,
|
||||
body,
|
||||
headers,
|
||||
headers: reqHeaders(),
|
||||
body: reqBody(),
|
||||
});
|
||||
const json = (await response.json()) as Record<string, unknown>;
|
||||
return json as unknown as IAPIResponse<T>;
|
||||
return parser.parse(await response.json());
|
||||
}
|
||||
|
||||
export async function fetchJSONAuth<T>(
|
||||
export async function fetchJSONAuth<T, P extends { parse: (string) => T }>(
|
||||
path: string,
|
||||
method: string,
|
||||
parser: P,
|
||||
body?: string | Record<string, unknown> | File,
|
||||
headers?: Record<string, unknown>,
|
||||
): Promise<IAPIResponse<T>> {
|
||||
): Promise<T> {
|
||||
if (token) {
|
||||
return fetchJSON(path, method, body, {
|
||||
return fetchJSON(path, method, parser, body, {
|
||||
...headers,
|
||||
Authorization: `Bearer ${token}`,
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Action } from "redux";
|
||||
import { IUserAuthJSON } from "~/src/shared/types";
|
||||
import { TUserAuthJSON } from "~/src/shared/types";
|
||||
|
||||
export enum AuthTypes {
|
||||
AUTH_START = "AUTH_START",
|
||||
@@ -28,7 +28,7 @@ export interface ISignupStartAction extends Action {
|
||||
|
||||
export interface IAuthSuccessAction extends Action {
|
||||
type: AuthTypes.AUTH_SUCCESS;
|
||||
payload: IUserAuthJSON;
|
||||
payload: TUserAuthJSON;
|
||||
}
|
||||
|
||||
export interface IAuthFailureAction extends Action {
|
||||
@@ -64,7 +64,7 @@ export function signupStart(
|
||||
};
|
||||
}
|
||||
|
||||
export function authSuccess(user: IUserAuthJSON): IAuthSuccessAction {
|
||||
export function authSuccess(user: TUserAuthJSON): IAuthSuccessAction {
|
||||
return { type: AuthTypes.AUTH_SUCCESS, payload: user };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Action } from "redux";
|
||||
import { IPhotoReqJSON } from "~src/shared/types";
|
||||
import { TPhotoReqJSON } from "~src/shared/types";
|
||||
import {
|
||||
showPhotoCreateFailToast,
|
||||
showPhotoUploadFileFailToast,
|
||||
@@ -29,242 +29,242 @@ export enum PhotoTypes {
|
||||
PHOTOS_DELETE_CANCEL = "PHOTOS_DELETE_CANCEL",
|
||||
}
|
||||
|
||||
export interface IPhotosLoadStartAction extends Action {
|
||||
export interface TPhotosLoadStartAction extends Action {
|
||||
type: PhotoTypes.PHOTOS_LOAD_START;
|
||||
}
|
||||
|
||||
export interface IPhotosLoadSuccessAction extends Action {
|
||||
export interface TPhotosLoadSuccessAction extends Action {
|
||||
type: PhotoTypes.PHOTOS_LOAD_SUCCESS;
|
||||
photos: IPhotoReqJSON[];
|
||||
photos: TPhotoReqJSON[];
|
||||
}
|
||||
|
||||
export interface IPhotosLoadFailAction extends Action {
|
||||
export interface TPhotosLoadFailAction extends Action {
|
||||
type: PhotoTypes.PHOTOS_LOAD_FAIL;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface IPhotoLoadStartAction extends Action {
|
||||
export interface TPhotoLoadStartAction extends Action {
|
||||
type: PhotoTypes.PHOTO_LOAD_START;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface IPhotoLoadSuccessAction extends Action {
|
||||
export interface TPhotoLoadSuccessAction extends Action {
|
||||
type: PhotoTypes.PHOTO_LOAD_SUCCESS;
|
||||
photo: IPhotoReqJSON;
|
||||
photo: TPhotoReqJSON;
|
||||
}
|
||||
|
||||
export interface IPhotoLoadFailAction extends Action {
|
||||
export interface TPhotoLoadFailAction extends Action {
|
||||
type: PhotoTypes.PHOTO_LOAD_FAIL;
|
||||
id: number;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface IPhotosUploadStartAction extends Action {
|
||||
export interface TPhotosUploadStartAction extends Action {
|
||||
type: PhotoTypes.PHOTOS_UPLOAD_START;
|
||||
files: FileList;
|
||||
}
|
||||
|
||||
export interface IPhotoCreateQueue extends Action {
|
||||
export interface TPhotoCreateQueue extends Action {
|
||||
type: PhotoTypes.PHOTO_CREATE_QUEUE;
|
||||
file: File;
|
||||
}
|
||||
|
||||
export interface IPhotoUploadQueue extends Action {
|
||||
export interface TPhotoUploadQueue extends Action {
|
||||
type: PhotoTypes.PHOTO_UPLOAD_QUEUE;
|
||||
file: File;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface IPhotoCreateStart extends Action {
|
||||
export interface TPhotoCreateStart extends Action {
|
||||
type: PhotoTypes.PHOTO_CREATE_START;
|
||||
file: File;
|
||||
}
|
||||
|
||||
export interface IPhotoUploadStart extends Action {
|
||||
export interface TPhotoUploadStart extends Action {
|
||||
type: PhotoTypes.PHOTO_UPLOAD_START;
|
||||
file: File;
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface IPhotoUploadSuccessAction extends Action {
|
||||
export interface TPhotoUploadSuccessAction extends Action {
|
||||
type: PhotoTypes.PHOTO_UPLOAD_SUCCESS;
|
||||
photo: IPhotoReqJSON;
|
||||
photo: TPhotoReqJSON;
|
||||
}
|
||||
|
||||
export interface IPhotoUploadFailAction extends Action {
|
||||
export interface TPhotoUploadFailAction extends Action {
|
||||
type: PhotoTypes.PHOTO_UPLOAD_FAIL;
|
||||
photo: IPhotoReqJSON | number;
|
||||
photo: TPhotoReqJSON | number;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface IPhotoCreateSuccessAction extends Action {
|
||||
export interface TPhotoCreateSuccessAction extends Action {
|
||||
type: PhotoTypes.PHOTO_CREATE_SUCCESS;
|
||||
photo: IPhotoReqJSON;
|
||||
photo: TPhotoReqJSON;
|
||||
file: File;
|
||||
}
|
||||
|
||||
export interface IPhotoCreateFailAction extends Action {
|
||||
export interface TPhotoCreateFailAction extends Action {
|
||||
type: PhotoTypes.PHOTO_CREATE_FAIL;
|
||||
file: File;
|
||||
error: string;
|
||||
}
|
||||
|
||||
export interface IPhotosDeleteStartAction extends Action {
|
||||
export interface TPhotosDeleteStartAction extends Action {
|
||||
type: PhotoTypes.PHOTOS_DELETE_START;
|
||||
photos: IPhotoReqJSON[];
|
||||
photos: TPhotoReqJSON[];
|
||||
}
|
||||
|
||||
export interface IPhotosDeleteSuccessAction extends Action {
|
||||
export interface TPhotosDeleteSuccessAction extends Action {
|
||||
type: PhotoTypes.PHOTOS_DELETE_SUCCESS;
|
||||
photos: IPhotoReqJSON[];
|
||||
photos: TPhotoReqJSON[];
|
||||
}
|
||||
|
||||
export interface IPhotosDeleteFailAction extends Action {
|
||||
export interface TPhotosDeleteFailAction extends Action {
|
||||
type: PhotoTypes.PHOTOS_DELETE_FAIL;
|
||||
photos: IPhotoReqJSON[];
|
||||
photos: TPhotoReqJSON[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface IPhotosDeleteCancelAction extends Action {
|
||||
export interface TPhotosDeleteCancelAction extends Action {
|
||||
type: PhotoTypes.PHOTOS_DELETE_CANCEL;
|
||||
photos: IPhotoReqJSON[];
|
||||
photos: TPhotoReqJSON[];
|
||||
}
|
||||
|
||||
export interface IPhotosStartFetchingSpinner extends Action {
|
||||
export interface TPhotosStartFetchingSpinner extends Action {
|
||||
type: PhotoTypes.PHOTOS_START_FETCHING_SPINNER;
|
||||
}
|
||||
|
||||
export function photoCreateQueue(file: File): IPhotoCreateQueue {
|
||||
export function photoCreateQueue(file: File): TPhotoCreateQueue {
|
||||
return { type: PhotoTypes.PHOTO_CREATE_QUEUE, file };
|
||||
}
|
||||
|
||||
export function photoUploadQueue(file: File, id: number): IPhotoUploadQueue {
|
||||
export function photoUploadQueue(file: File, id: number): TPhotoUploadQueue {
|
||||
return { type: PhotoTypes.PHOTO_UPLOAD_QUEUE, file, id };
|
||||
}
|
||||
|
||||
export function photoCreateStart(file: File): IPhotoCreateStart {
|
||||
export function photoCreateStart(file: File): TPhotoCreateStart {
|
||||
return { type: PhotoTypes.PHOTO_CREATE_START, file };
|
||||
}
|
||||
|
||||
export function photoUploadStart(file: File, id: number): IPhotoUploadStart {
|
||||
export function photoUploadStart(file: File, id: number): TPhotoUploadStart {
|
||||
return { type: PhotoTypes.PHOTO_UPLOAD_START, file, id };
|
||||
}
|
||||
|
||||
export function photosLoadStart(): IPhotosLoadStartAction {
|
||||
export function photosLoadStart(): TPhotosLoadStartAction {
|
||||
return { type: PhotoTypes.PHOTOS_LOAD_START };
|
||||
}
|
||||
|
||||
export function photoLoadStart(id: number): IPhotoLoadStartAction {
|
||||
export function photoLoadStart(id: number): TPhotoLoadStartAction {
|
||||
return { type: PhotoTypes.PHOTO_LOAD_START, id };
|
||||
}
|
||||
|
||||
export function photosUploadStart(files: FileList): IPhotosUploadStartAction {
|
||||
export function photosUploadStart(files: FileList): TPhotosUploadStartAction {
|
||||
return { type: PhotoTypes.PHOTOS_UPLOAD_START, files };
|
||||
}
|
||||
|
||||
export function photoUploadSuccess(
|
||||
photo: IPhotoReqJSON,
|
||||
): IPhotoUploadSuccessAction {
|
||||
photo: TPhotoReqJSON,
|
||||
): TPhotoUploadSuccessAction {
|
||||
return { type: PhotoTypes.PHOTO_UPLOAD_SUCCESS, photo };
|
||||
}
|
||||
|
||||
export function photoUploadFail(
|
||||
photo: IPhotoReqJSON | number,
|
||||
photo: TPhotoReqJSON | number,
|
||||
error: string,
|
||||
): IPhotoUploadFailAction {
|
||||
): TPhotoUploadFailAction {
|
||||
showPhotoUploadJSONFailToast(photo, error);
|
||||
return { type: PhotoTypes.PHOTO_UPLOAD_FAIL, photo, error };
|
||||
}
|
||||
|
||||
export function photoUploadFailWithFile(
|
||||
photo: IPhotoReqJSON | number,
|
||||
photo: TPhotoReqJSON | number,
|
||||
file: File,
|
||||
error: string,
|
||||
): IPhotoUploadFailAction {
|
||||
): TPhotoUploadFailAction {
|
||||
showPhotoUploadFileFailToast(file, error);
|
||||
return { type: PhotoTypes.PHOTO_UPLOAD_FAIL, photo, error };
|
||||
}
|
||||
|
||||
export function photoCreateSuccess(
|
||||
photo: IPhotoReqJSON,
|
||||
photo: TPhotoReqJSON,
|
||||
file: File,
|
||||
): IPhotoCreateSuccessAction {
|
||||
): TPhotoCreateSuccessAction {
|
||||
return { type: PhotoTypes.PHOTO_CREATE_SUCCESS, photo, file };
|
||||
}
|
||||
|
||||
export function photoCreateFail(
|
||||
file: File,
|
||||
error: string,
|
||||
): IPhotoCreateFailAction {
|
||||
): TPhotoCreateFailAction {
|
||||
showPhotoCreateFailToast(file, error);
|
||||
return { type: PhotoTypes.PHOTO_CREATE_FAIL, file, error };
|
||||
}
|
||||
|
||||
export function photosLoadSuccess(
|
||||
photos: IPhotoReqJSON[],
|
||||
): IPhotosLoadSuccessAction {
|
||||
photos: TPhotoReqJSON[],
|
||||
): TPhotosLoadSuccessAction {
|
||||
return { type: PhotoTypes.PHOTOS_LOAD_SUCCESS, photos };
|
||||
}
|
||||
|
||||
export function photosLoadFail(error: string): IPhotosLoadFailAction {
|
||||
export function photosLoadFail(error: string): TPhotosLoadFailAction {
|
||||
return { type: PhotoTypes.PHOTOS_LOAD_FAIL, error };
|
||||
}
|
||||
|
||||
export function photoLoadSuccess(
|
||||
photo: IPhotoReqJSON,
|
||||
): IPhotoLoadSuccessAction {
|
||||
photo: TPhotoReqJSON,
|
||||
): TPhotoLoadSuccessAction {
|
||||
return { type: PhotoTypes.PHOTO_LOAD_SUCCESS, photo };
|
||||
}
|
||||
|
||||
export function photoLoadFail(id: number, error: string): IPhotoLoadFailAction {
|
||||
export function photoLoadFail(id: number, error: string): TPhotoLoadFailAction {
|
||||
return { type: PhotoTypes.PHOTO_LOAD_FAIL, id, error };
|
||||
}
|
||||
|
||||
export function photosDeleteStart(
|
||||
photos: IPhotoReqJSON[],
|
||||
): IPhotosDeleteStartAction {
|
||||
photos: TPhotoReqJSON[],
|
||||
): TPhotosDeleteStartAction {
|
||||
return { type: PhotoTypes.PHOTOS_DELETE_START, photos };
|
||||
}
|
||||
export function photosDeleteSuccess(
|
||||
photos: IPhotoReqJSON[],
|
||||
): IPhotosDeleteSuccessAction {
|
||||
photos: TPhotoReqJSON[],
|
||||
): TPhotosDeleteSuccessAction {
|
||||
return { type: PhotoTypes.PHOTOS_DELETE_SUCCESS, photos };
|
||||
}
|
||||
export function photosDeleteFail(
|
||||
photos: IPhotoReqJSON[],
|
||||
photos: TPhotoReqJSON[],
|
||||
error?: string,
|
||||
): IPhotosDeleteFailAction {
|
||||
): TPhotosDeleteFailAction {
|
||||
return { type: PhotoTypes.PHOTOS_DELETE_FAIL, photos, error };
|
||||
}
|
||||
|
||||
export function photosDeleteCancel(
|
||||
photos: IPhotoReqJSON[],
|
||||
): IPhotosDeleteCancelAction {
|
||||
photos: TPhotoReqJSON[],
|
||||
): TPhotosDeleteCancelAction {
|
||||
return { type: PhotoTypes.PHOTOS_DELETE_CANCEL, photos };
|
||||
}
|
||||
|
||||
export function photosStartFetchingSpinner(): IPhotosStartFetchingSpinner {
|
||||
export function photosStartFetchingSpinner(): TPhotosStartFetchingSpinner {
|
||||
return { type: PhotoTypes.PHOTOS_START_FETCHING_SPINNER };
|
||||
}
|
||||
|
||||
export type PhotoAction =
|
||||
| IPhotosLoadStartAction
|
||||
| IPhotosLoadFailAction
|
||||
| IPhotosLoadSuccessAction
|
||||
| IPhotosStartFetchingSpinner
|
||||
| IPhotosUploadStartAction
|
||||
| IPhotoCreateFailAction
|
||||
| IPhotoCreateSuccessAction
|
||||
| IPhotoUploadFailAction
|
||||
| IPhotoUploadSuccessAction
|
||||
| IPhotosDeleteFailAction
|
||||
| IPhotosDeleteStartAction
|
||||
| IPhotosDeleteSuccessAction
|
||||
| IPhotosDeleteCancelAction
|
||||
| IPhotoLoadFailAction
|
||||
| IPhotoLoadStartAction
|
||||
| IPhotoLoadSuccessAction
|
||||
| IPhotoUploadQueue
|
||||
| IPhotoCreateQueue
|
||||
| IPhotoCreateStart
|
||||
| IPhotoUploadStart;
|
||||
| TPhotosLoadStartAction
|
||||
| TPhotosLoadFailAction
|
||||
| TPhotosLoadSuccessAction
|
||||
| TPhotosStartFetchingSpinner
|
||||
| TPhotosUploadStartAction
|
||||
| TPhotoCreateFailAction
|
||||
| TPhotoCreateSuccessAction
|
||||
| TPhotoUploadFailAction
|
||||
| TPhotoUploadSuccessAction
|
||||
| TPhotosDeleteFailAction
|
||||
| TPhotosDeleteStartAction
|
||||
| TPhotosDeleteSuccessAction
|
||||
| TPhotosDeleteCancelAction
|
||||
| TPhotoLoadFailAction
|
||||
| TPhotoLoadStartAction
|
||||
| TPhotoLoadSuccessAction
|
||||
| TPhotoUploadQueue
|
||||
| TPhotoCreateQueue
|
||||
| TPhotoCreateStart
|
||||
| TPhotoUploadStart;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { Reducer } from "redux";
|
||||
import { IPhotoReqJSON } from "~/src/shared/types";
|
||||
import { TPhotoReqJSON } from "~/src/shared/types";
|
||||
import { UserAction, UserTypes } from "~src/redux/user/actions";
|
||||
import { PhotoAction, PhotoTypes } from "./actions";
|
||||
|
||||
export interface IPhotoState {
|
||||
export interface TPhotoState {
|
||||
fetching: boolean;
|
||||
fetchingError: string | null;
|
||||
}
|
||||
|
||||
export interface IPhotosState {
|
||||
photos: IPhotoReqJSON[];
|
||||
export interface TPhotosState {
|
||||
photos: TPhotoReqJSON[];
|
||||
|
||||
photoStates: Record<number, IPhotoState>;
|
||||
photoStates: Record<number, TPhotoState>;
|
||||
|
||||
overviewFetching: boolean;
|
||||
allPhotosLoaded: boolean;
|
||||
@@ -24,10 +24,10 @@ export interface IPhotosState {
|
||||
photoUploadQueue: Record<number, File>;
|
||||
photosUploading: number;
|
||||
|
||||
deleteCache: Record<number, IPhotoReqJSON>;
|
||||
deleteCache: Record<number, TPhotoReqJSON>;
|
||||
}
|
||||
|
||||
const defaultPhotosState: IPhotosState = {
|
||||
const defaultPhotosState: TPhotosState = {
|
||||
photos: [],
|
||||
allPhotosLoaded: false,
|
||||
overviewFetching: false,
|
||||
@@ -45,12 +45,12 @@ const defaultPhotosState: IPhotosState = {
|
||||
deleteCache: {},
|
||||
};
|
||||
|
||||
export function sortPhotos(photos: IPhotoReqJSON[]): IPhotoReqJSON[] {
|
||||
export function sortPhotos(photos: TPhotoReqJSON[]): TPhotoReqJSON[] {
|
||||
return [...photos].sort((a, b) => b.shotAt - a.shotAt);
|
||||
}
|
||||
|
||||
export const photosReducer: Reducer<IPhotosState, PhotoAction> = (
|
||||
state: IPhotosState = defaultPhotosState,
|
||||
export const photosReducer: Reducer<TPhotosState, PhotoAction> = (
|
||||
state: TPhotosState = defaultPhotosState,
|
||||
action: PhotoAction | UserAction,
|
||||
) => {
|
||||
switch (action.type) {
|
||||
@@ -230,7 +230,7 @@ export const photosReducer: Reducer<IPhotosState, PhotoAction> = (
|
||||
case PhotoTypes.PHOTOS_DELETE_FAIL:
|
||||
case PhotoTypes.PHOTOS_DELETE_CANCEL: {
|
||||
const delCache = { ...state.deleteCache };
|
||||
let photos: IPhotoReqJSON[] = [...state.photos];
|
||||
let photos: TPhotoReqJSON[] = [...state.photos];
|
||||
for (const photo of action.photos) {
|
||||
if (delCache[photo.id]) {
|
||||
photos = sortPhotos([...photos, delCache[photo.id]]);
|
||||
|
||||
@@ -18,9 +18,9 @@ import {
|
||||
uploadPhoto,
|
||||
} from "~src/redux/api/photos";
|
||||
import {
|
||||
IPhotosDeleteStartAction,
|
||||
IPhotoLoadStartAction,
|
||||
IPhotosUploadStartAction,
|
||||
TPhotosDeleteStartAction,
|
||||
TPhotoLoadStartAction,
|
||||
TPhotosUploadStartAction,
|
||||
photoCreateFail,
|
||||
photoCreateQueue,
|
||||
photoCreateStart,
|
||||
@@ -38,7 +38,7 @@ import {
|
||||
photoUploadStart,
|
||||
photoUploadSuccess,
|
||||
} from "./actions";
|
||||
import { IPhotosNewRespBody, IPhotosListPagination } from "~src/shared/types";
|
||||
import { TPhotosNewRespBody, PhotosListPagination } from "~src/shared/types";
|
||||
|
||||
// Thanks, https://dev.to/qortex/compute-md5-checksum-for-a-file-in-typescript-59a4
|
||||
function computeChecksumMd5(file: File): Promise<string> {
|
||||
@@ -112,7 +112,7 @@ function* photosLoad() {
|
||||
|
||||
const skip = state.photos.photos ? state.photos.photos.length : 0;
|
||||
const { response, timeout } = yield race({
|
||||
response: call(fetchPhotosList, skip, IPhotosListPagination),
|
||||
response: call(fetchPhotosList, skip, PhotosListPagination),
|
||||
timeout: delay(10000),
|
||||
});
|
||||
|
||||
@@ -133,7 +133,7 @@ function* photosLoad() {
|
||||
}
|
||||
}
|
||||
|
||||
function* photoLoad(action: IPhotoLoadStartAction) {
|
||||
function* photoLoad(action: TPhotoLoadStartAction) {
|
||||
try {
|
||||
//const spinner = yield fork(startSpinner);
|
||||
|
||||
@@ -185,7 +185,7 @@ function* photoCreate() {
|
||||
return;
|
||||
}
|
||||
if (response.data || response.error === "Photo already exists") {
|
||||
const photo = (response as IPhotosNewRespBody).data;
|
||||
const photo = (response as TPhotosNewRespBody).data;
|
||||
yield put(photoCreateSuccess(photo, f));
|
||||
yield put(photoUploadQueue(f, photo.id));
|
||||
} else {
|
||||
@@ -233,14 +233,14 @@ function* photoUpload() {
|
||||
}
|
||||
}
|
||||
|
||||
function* photosUpload(action: IPhotosUploadStartAction) {
|
||||
function* photosUpload(action: TPhotosUploadStartAction) {
|
||||
const files = Array.from(action.files);
|
||||
for (const file of files) {
|
||||
yield put(photoCreateQueue(file));
|
||||
}
|
||||
}
|
||||
|
||||
function* photosDelete(action: IPhotosDeleteStartAction) {
|
||||
function* photosDelete(action: TPhotosDeleteStartAction) {
|
||||
try {
|
||||
const { cancelled } = yield race({
|
||||
timeout: delay(3000),
|
||||
|
||||
@@ -7,14 +7,14 @@ import {
|
||||
ILocalSettingsState,
|
||||
localSettingsReducer,
|
||||
} from "./localSettings/reducer";
|
||||
import { IPhotosState, photosReducer } from "./photos/reducer";
|
||||
import { IUserState, userReducer } from "./user/reducer";
|
||||
import { TPhotosState, photosReducer } from "./photos/reducer";
|
||||
import { TUserState, userReducer } from "./user/reducer";
|
||||
|
||||
export interface IAppState {
|
||||
auth: IAuthState & PersistPartial;
|
||||
user: IUserState;
|
||||
user: TUserState;
|
||||
localSettings: ILocalSettingsState & PersistPartial;
|
||||
photos: IPhotosState;
|
||||
photos: TPhotosState;
|
||||
}
|
||||
|
||||
const authPersistConfig = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Action } from "redux";
|
||||
import { IUserAuthJSON } from "~src/shared/types";
|
||||
import { TUserAuthJSON } from "~src/shared/types";
|
||||
import {
|
||||
showPasswordNotSavedToast,
|
||||
showPasswordSavedToast,
|
||||
@@ -15,20 +15,20 @@ export enum UserTypes {
|
||||
USER_PASS_CHANGE_FAIL = "USER_PASS_CHANGE_FAIL",
|
||||
}
|
||||
|
||||
export interface IUserGetAction extends Action {
|
||||
export interface TUserGetAction extends Action {
|
||||
type: UserTypes.USER_GET;
|
||||
}
|
||||
|
||||
export interface IUserLogoutAction extends Action {
|
||||
export interface TUserLogoutAction extends Action {
|
||||
type: UserTypes.USER_LOGOUT;
|
||||
}
|
||||
|
||||
export interface IUserGetSuccessAction extends Action {
|
||||
export interface TUserGetSuccessAction extends Action {
|
||||
type: UserTypes.USER_GET_SUCCESS;
|
||||
payload: IUserAuthJSON;
|
||||
payload: TUserAuthJSON;
|
||||
}
|
||||
|
||||
export interface IUserGetFailAction extends Action {
|
||||
export interface TUserGetFailAction extends Action {
|
||||
type: UserTypes.USER_GET_FAIL;
|
||||
payload: {
|
||||
error: string;
|
||||
@@ -36,17 +36,17 @@ export interface IUserGetFailAction extends Action {
|
||||
};
|
||||
}
|
||||
|
||||
export interface IUserPassChangeAction extends Action {
|
||||
export interface TUserPassChangeAction extends Action {
|
||||
type: UserTypes.USER_PASS_CHANGE;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface IUserPassChangeSuccessAction extends Action {
|
||||
export interface TUserPassChangeSuccessAction extends Action {
|
||||
type: UserTypes.USER_PASS_CHANGE_SUCCESS;
|
||||
payload: IUserAuthJSON;
|
||||
payload: TUserAuthJSON;
|
||||
}
|
||||
|
||||
export interface IUserPassChangeFailAction extends Action {
|
||||
export interface TUserPassChangeFailAction extends Action {
|
||||
type: UserTypes.USER_PASS_CHANGE_FAIL;
|
||||
payload: {
|
||||
error: string;
|
||||
@@ -54,32 +54,32 @@ export interface IUserPassChangeFailAction extends Action {
|
||||
};
|
||||
}
|
||||
|
||||
export function getUser(): IUserGetAction {
|
||||
export function getUser(): TUserGetAction {
|
||||
return { type: UserTypes.USER_GET };
|
||||
}
|
||||
|
||||
export function logoutUser(): IUserLogoutAction {
|
||||
export function logoutUser(): TUserLogoutAction {
|
||||
return { type: UserTypes.USER_LOGOUT };
|
||||
}
|
||||
|
||||
export function getUserSuccess(user: IUserAuthJSON): IUserGetSuccessAction {
|
||||
export function getUserSuccess(user: TUserAuthJSON): TUserGetSuccessAction {
|
||||
return { type: UserTypes.USER_GET_SUCCESS, payload: user };
|
||||
}
|
||||
|
||||
export function getUserFail(
|
||||
error: string,
|
||||
logout: boolean,
|
||||
): IUserGetFailAction {
|
||||
): TUserGetFailAction {
|
||||
return { type: UserTypes.USER_GET_FAIL, payload: { error, logout } };
|
||||
}
|
||||
|
||||
export function userPassChange(password: string): IUserPassChangeAction {
|
||||
export function userPassChange(password: string): TUserPassChangeAction {
|
||||
return { type: UserTypes.USER_PASS_CHANGE, password };
|
||||
}
|
||||
|
||||
export function userPassChangeSuccess(
|
||||
user: IUserAuthJSON,
|
||||
): IUserPassChangeSuccessAction {
|
||||
user: TUserAuthJSON,
|
||||
): TUserPassChangeSuccessAction {
|
||||
showPasswordSavedToast();
|
||||
return { type: UserTypes.USER_PASS_CHANGE_SUCCESS, payload: user };
|
||||
}
|
||||
@@ -87,7 +87,7 @@ export function userPassChangeSuccess(
|
||||
export function userPassChangeFail(
|
||||
error: string,
|
||||
logout: boolean,
|
||||
): IUserPassChangeFailAction {
|
||||
): TUserPassChangeFailAction {
|
||||
showPasswordNotSavedToast(error);
|
||||
return {
|
||||
type: UserTypes.USER_PASS_CHANGE_FAIL,
|
||||
@@ -96,10 +96,10 @@ export function userPassChangeFail(
|
||||
}
|
||||
|
||||
export type UserAction =
|
||||
| IUserGetAction
|
||||
| IUserGetSuccessAction
|
||||
| IUserGetFailAction
|
||||
| IUserLogoutAction
|
||||
| IUserPassChangeAction
|
||||
| IUserPassChangeFailAction
|
||||
| IUserPassChangeSuccessAction;
|
||||
| TUserGetAction
|
||||
| TUserGetSuccessAction
|
||||
| TUserGetFailAction
|
||||
| TUserLogoutAction
|
||||
| TUserPassChangeAction
|
||||
| TUserPassChangeFailAction
|
||||
| TUserPassChangeSuccessAction;
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Reducer } from "react";
|
||||
import { IUserJSON } from "~/src/shared/types";
|
||||
import { TUserJSON } from "~/src/shared/types";
|
||||
import { AuthAction, AuthTypes } from "~src/redux/auth/actions";
|
||||
import { UserAction, UserTypes } from "./actions";
|
||||
|
||||
export interface IUserState {
|
||||
user: IUserJSON | null;
|
||||
export interface TUserState {
|
||||
user: TUserJSON | null;
|
||||
}
|
||||
|
||||
const defaultUserState: IUserState = {
|
||||
const defaultUserState: TUserState = {
|
||||
user: null,
|
||||
};
|
||||
|
||||
export const userReducer: Reducer<IUserState, AuthAction> = (
|
||||
state: IUserState = defaultUserState,
|
||||
export const userReducer: Reducer<TUserState, AuthAction> = (
|
||||
state: TUserState = defaultUserState,
|
||||
action: AuthAction | UserAction,
|
||||
): IUserState => {
|
||||
): TUserState => {
|
||||
switch (action.type) {
|
||||
case AuthTypes.AUTH_SUCCESS:
|
||||
case UserTypes.USER_GET_SUCCESS:
|
||||
|
||||
@@ -4,7 +4,7 @@ import { changeUserPassword, fetchUser } from "~src/redux/api/user";
|
||||
import {
|
||||
getUserFail,
|
||||
getUserSuccess,
|
||||
IUserPassChangeAction,
|
||||
TUserPassChangeAction,
|
||||
userPassChangeFail,
|
||||
userPassChangeSuccess,
|
||||
UserTypes,
|
||||
@@ -32,7 +32,7 @@ function* getUser() {
|
||||
}
|
||||
}
|
||||
|
||||
function* userPassChange(action: IUserPassChangeAction) {
|
||||
function* userPassChange(action: TUserPassChangeAction) {
|
||||
try {
|
||||
const { response, timeout } = yield race({
|
||||
response: call(changeUserPassword, action.password),
|
||||
|
||||
15
shared/node_modules/.package-lock.json
generated
vendored
Normal file
15
shared/node_modules/.package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "photos-shared",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/zod": {
|
||||
"version": "3.21.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
|
||||
"integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
shared/node_modules/zod/LICENSE
generated
vendored
Normal file
21
shared/node_modules/zod/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Colin McDonnell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
2739
shared/node_modules/zod/README.md
generated
vendored
Normal file
2739
shared/node_modules/zod/README.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
shared/node_modules/zod/index.d.ts
generated
vendored
Normal file
2
shared/node_modules/zod/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./lib";
|
||||
export as namespace Zod;
|
||||
163
shared/node_modules/zod/lib/ZodError.d.ts
generated
vendored
Normal file
163
shared/node_modules/zod/lib/ZodError.d.ts
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
import type { TypeOf, ZodType } from ".";
|
||||
import { Primitive } from "./helpers/typeAliases";
|
||||
import { util, ZodParsedType } from "./helpers/util";
|
||||
declare type allKeys<T> = T extends any ? keyof T : never;
|
||||
export declare type inferFlattenedErrors<T extends ZodType<any, any, any>, U = string> = typeToFlattenedError<TypeOf<T>, U>;
|
||||
export declare type typeToFlattenedError<T, U = string> = {
|
||||
formErrors: U[];
|
||||
fieldErrors: {
|
||||
[P in allKeys<T>]?: U[];
|
||||
};
|
||||
};
|
||||
export declare const ZodIssueCode: {
|
||||
invalid_type: "invalid_type";
|
||||
invalid_literal: "invalid_literal";
|
||||
custom: "custom";
|
||||
invalid_union: "invalid_union";
|
||||
invalid_union_discriminator: "invalid_union_discriminator";
|
||||
invalid_enum_value: "invalid_enum_value";
|
||||
unrecognized_keys: "unrecognized_keys";
|
||||
invalid_arguments: "invalid_arguments";
|
||||
invalid_return_type: "invalid_return_type";
|
||||
invalid_date: "invalid_date";
|
||||
invalid_string: "invalid_string";
|
||||
too_small: "too_small";
|
||||
too_big: "too_big";
|
||||
invalid_intersection_types: "invalid_intersection_types";
|
||||
not_multiple_of: "not_multiple_of";
|
||||
not_finite: "not_finite";
|
||||
};
|
||||
export declare type ZodIssueCode = keyof typeof ZodIssueCode;
|
||||
export declare type ZodIssueBase = {
|
||||
path: (string | number)[];
|
||||
message?: string;
|
||||
};
|
||||
export interface ZodInvalidTypeIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.invalid_type;
|
||||
expected: ZodParsedType;
|
||||
received: ZodParsedType;
|
||||
}
|
||||
export interface ZodInvalidLiteralIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.invalid_literal;
|
||||
expected: unknown;
|
||||
received: unknown;
|
||||
}
|
||||
export interface ZodUnrecognizedKeysIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.unrecognized_keys;
|
||||
keys: string[];
|
||||
}
|
||||
export interface ZodInvalidUnionIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.invalid_union;
|
||||
unionErrors: ZodError[];
|
||||
}
|
||||
export interface ZodInvalidUnionDiscriminatorIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.invalid_union_discriminator;
|
||||
options: Primitive[];
|
||||
}
|
||||
export interface ZodInvalidEnumValueIssue extends ZodIssueBase {
|
||||
received: string | number;
|
||||
code: typeof ZodIssueCode.invalid_enum_value;
|
||||
options: (string | number)[];
|
||||
}
|
||||
export interface ZodInvalidArgumentsIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.invalid_arguments;
|
||||
argumentsError: ZodError;
|
||||
}
|
||||
export interface ZodInvalidReturnTypeIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.invalid_return_type;
|
||||
returnTypeError: ZodError;
|
||||
}
|
||||
export interface ZodInvalidDateIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.invalid_date;
|
||||
}
|
||||
export declare type StringValidation = "email" | "url" | "emoji" | "uuid" | "regex" | "cuid" | "cuid2" | "ulid" | "datetime" | "ip" | {
|
||||
includes: string;
|
||||
position?: number;
|
||||
} | {
|
||||
startsWith: string;
|
||||
} | {
|
||||
endsWith: string;
|
||||
};
|
||||
export interface ZodInvalidStringIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.invalid_string;
|
||||
validation: StringValidation;
|
||||
}
|
||||
export interface ZodTooSmallIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.too_small;
|
||||
minimum: number | bigint;
|
||||
inclusive: boolean;
|
||||
exact?: boolean;
|
||||
type: "array" | "string" | "number" | "set" | "date" | "bigint";
|
||||
}
|
||||
export interface ZodTooBigIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.too_big;
|
||||
maximum: number | bigint;
|
||||
inclusive: boolean;
|
||||
exact?: boolean;
|
||||
type: "array" | "string" | "number" | "set" | "date" | "bigint";
|
||||
}
|
||||
export interface ZodInvalidIntersectionTypesIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.invalid_intersection_types;
|
||||
}
|
||||
export interface ZodNotMultipleOfIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.not_multiple_of;
|
||||
multipleOf: number | bigint;
|
||||
}
|
||||
export interface ZodNotFiniteIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.not_finite;
|
||||
}
|
||||
export interface ZodCustomIssue extends ZodIssueBase {
|
||||
code: typeof ZodIssueCode.custom;
|
||||
params?: {
|
||||
[k: string]: any;
|
||||
};
|
||||
}
|
||||
export declare type DenormalizedError = {
|
||||
[k: string]: DenormalizedError | string[];
|
||||
};
|
||||
export declare type ZodIssueOptionalMessage = ZodInvalidTypeIssue | ZodInvalidLiteralIssue | ZodUnrecognizedKeysIssue | ZodInvalidUnionIssue | ZodInvalidUnionDiscriminatorIssue | ZodInvalidEnumValueIssue | ZodInvalidArgumentsIssue | ZodInvalidReturnTypeIssue | ZodInvalidDateIssue | ZodInvalidStringIssue | ZodTooSmallIssue | ZodTooBigIssue | ZodInvalidIntersectionTypesIssue | ZodNotMultipleOfIssue | ZodNotFiniteIssue | ZodCustomIssue;
|
||||
export declare type ZodIssue = ZodIssueOptionalMessage & {
|
||||
fatal?: boolean;
|
||||
message: string;
|
||||
};
|
||||
export declare const quotelessJson: (obj: any) => string;
|
||||
declare type recursiveZodFormattedError<T> = T extends [any, ...any[]] ? {
|
||||
[K in keyof T]?: ZodFormattedError<T[K]>;
|
||||
} : T extends any[] ? {
|
||||
[k: number]: ZodFormattedError<T[number]>;
|
||||
} : T extends object ? {
|
||||
[K in keyof T]?: ZodFormattedError<T[K]>;
|
||||
} : unknown;
|
||||
export declare type ZodFormattedError<T, U = string> = {
|
||||
_errors: U[];
|
||||
} & recursiveZodFormattedError<NonNullable<T>>;
|
||||
export declare type inferFormattedError<T extends ZodType<any, any, any>, U = string> = ZodFormattedError<TypeOf<T>, U>;
|
||||
export declare class ZodError<T = any> extends Error {
|
||||
issues: ZodIssue[];
|
||||
get errors(): ZodIssue[];
|
||||
constructor(issues: ZodIssue[]);
|
||||
format(): ZodFormattedError<T>;
|
||||
format<U>(mapper: (issue: ZodIssue) => U): ZodFormattedError<T, U>;
|
||||
static create: (issues: ZodIssue[]) => ZodError<any>;
|
||||
toString(): string;
|
||||
get message(): string;
|
||||
get isEmpty(): boolean;
|
||||
addIssue: (sub: ZodIssue) => void;
|
||||
addIssues: (subs?: ZodIssue[]) => void;
|
||||
flatten(): typeToFlattenedError<T>;
|
||||
flatten<U>(mapper?: (issue: ZodIssue) => U): typeToFlattenedError<T, U>;
|
||||
get formErrors(): typeToFlattenedError<T, string>;
|
||||
}
|
||||
declare type stripPath<T extends object> = T extends any ? util.OmitKeys<T, "path"> : never;
|
||||
export declare type IssueData = stripPath<ZodIssueOptionalMessage> & {
|
||||
path?: (string | number)[];
|
||||
fatal?: boolean;
|
||||
};
|
||||
export declare type ErrorMapCtx = {
|
||||
defaultError: string;
|
||||
data: any;
|
||||
};
|
||||
export declare type ZodErrorMap = (issue: ZodIssueOptionalMessage, _ctx: ErrorMapCtx) => {
|
||||
message: string;
|
||||
};
|
||||
export {};
|
||||
124
shared/node_modules/zod/lib/ZodError.js
generated
vendored
Normal file
124
shared/node_modules/zod/lib/ZodError.js
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ZodError = exports.quotelessJson = exports.ZodIssueCode = void 0;
|
||||
const util_1 = require("./helpers/util");
|
||||
exports.ZodIssueCode = util_1.util.arrayToEnum([
|
||||
"invalid_type",
|
||||
"invalid_literal",
|
||||
"custom",
|
||||
"invalid_union",
|
||||
"invalid_union_discriminator",
|
||||
"invalid_enum_value",
|
||||
"unrecognized_keys",
|
||||
"invalid_arguments",
|
||||
"invalid_return_type",
|
||||
"invalid_date",
|
||||
"invalid_string",
|
||||
"too_small",
|
||||
"too_big",
|
||||
"invalid_intersection_types",
|
||||
"not_multiple_of",
|
||||
"not_finite",
|
||||
]);
|
||||
const quotelessJson = (obj) => {
|
||||
const json = JSON.stringify(obj, null, 2);
|
||||
return json.replace(/"([^"]+)":/g, "$1:");
|
||||
};
|
||||
exports.quotelessJson = quotelessJson;
|
||||
class ZodError extends Error {
|
||||
constructor(issues) {
|
||||
super();
|
||||
this.issues = [];
|
||||
this.addIssue = (sub) => {
|
||||
this.issues = [...this.issues, sub];
|
||||
};
|
||||
this.addIssues = (subs = []) => {
|
||||
this.issues = [...this.issues, ...subs];
|
||||
};
|
||||
const actualProto = new.target.prototype;
|
||||
if (Object.setPrototypeOf) {
|
||||
Object.setPrototypeOf(this, actualProto);
|
||||
}
|
||||
else {
|
||||
this.__proto__ = actualProto;
|
||||
}
|
||||
this.name = "ZodError";
|
||||
this.issues = issues;
|
||||
}
|
||||
get errors() {
|
||||
return this.issues;
|
||||
}
|
||||
format(_mapper) {
|
||||
const mapper = _mapper ||
|
||||
function (issue) {
|
||||
return issue.message;
|
||||
};
|
||||
const fieldErrors = { _errors: [] };
|
||||
const processError = (error) => {
|
||||
for (const issue of error.issues) {
|
||||
if (issue.code === "invalid_union") {
|
||||
issue.unionErrors.map(processError);
|
||||
}
|
||||
else if (issue.code === "invalid_return_type") {
|
||||
processError(issue.returnTypeError);
|
||||
}
|
||||
else if (issue.code === "invalid_arguments") {
|
||||
processError(issue.argumentsError);
|
||||
}
|
||||
else if (issue.path.length === 0) {
|
||||
fieldErrors._errors.push(mapper(issue));
|
||||
}
|
||||
else {
|
||||
let curr = fieldErrors;
|
||||
let i = 0;
|
||||
while (i < issue.path.length) {
|
||||
const el = issue.path[i];
|
||||
const terminal = i === issue.path.length - 1;
|
||||
if (!terminal) {
|
||||
curr[el] = curr[el] || { _errors: [] };
|
||||
}
|
||||
else {
|
||||
curr[el] = curr[el] || { _errors: [] };
|
||||
curr[el]._errors.push(mapper(issue));
|
||||
}
|
||||
curr = curr[el];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
processError(this);
|
||||
return fieldErrors;
|
||||
}
|
||||
toString() {
|
||||
return this.message;
|
||||
}
|
||||
get message() {
|
||||
return JSON.stringify(this.issues, util_1.util.jsonStringifyReplacer, 2);
|
||||
}
|
||||
get isEmpty() {
|
||||
return this.issues.length === 0;
|
||||
}
|
||||
flatten(mapper = (issue) => issue.message) {
|
||||
const fieldErrors = {};
|
||||
const formErrors = [];
|
||||
for (const sub of this.issues) {
|
||||
if (sub.path.length > 0) {
|
||||
fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || [];
|
||||
fieldErrors[sub.path[0]].push(mapper(sub));
|
||||
}
|
||||
else {
|
||||
formErrors.push(mapper(sub));
|
||||
}
|
||||
}
|
||||
return { formErrors, fieldErrors };
|
||||
}
|
||||
get formErrors() {
|
||||
return this.flatten();
|
||||
}
|
||||
}
|
||||
exports.ZodError = ZodError;
|
||||
ZodError.create = (issues) => {
|
||||
const error = new ZodError(issues);
|
||||
return error;
|
||||
};
|
||||
17
shared/node_modules/zod/lib/__tests__/Mocker.d.ts
generated
vendored
Normal file
17
shared/node_modules/zod/lib/__tests__/Mocker.d.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
export declare class Mocker {
|
||||
pick: (...args: any[]) => any;
|
||||
get string(): string;
|
||||
get number(): number;
|
||||
get bigint(): bigint;
|
||||
get boolean(): boolean;
|
||||
get date(): Date;
|
||||
get symbol(): symbol;
|
||||
get null(): null;
|
||||
get undefined(): undefined;
|
||||
get stringOptional(): any;
|
||||
get stringNullable(): any;
|
||||
get numberOptional(): any;
|
||||
get numberNullable(): any;
|
||||
get booleanOptional(): any;
|
||||
get booleanNullable(): any;
|
||||
}
|
||||
57
shared/node_modules/zod/lib/__tests__/Mocker.js
generated
vendored
Normal file
57
shared/node_modules/zod/lib/__tests__/Mocker.js
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Mocker = void 0;
|
||||
function getRandomInt(max) {
|
||||
return Math.floor(Math.random() * Math.floor(max));
|
||||
}
|
||||
const testSymbol = Symbol("test");
|
||||
class Mocker {
|
||||
constructor() {
|
||||
this.pick = (...args) => {
|
||||
return args[getRandomInt(args.length)];
|
||||
};
|
||||
}
|
||||
get string() {
|
||||
return Math.random().toString(36).substring(7);
|
||||
}
|
||||
get number() {
|
||||
return Math.random() * 100;
|
||||
}
|
||||
get bigint() {
|
||||
return BigInt(Math.floor(Math.random() * 10000));
|
||||
}
|
||||
get boolean() {
|
||||
return Math.random() < 0.5;
|
||||
}
|
||||
get date() {
|
||||
return new Date(Math.floor(Date.now() * Math.random()));
|
||||
}
|
||||
get symbol() {
|
||||
return testSymbol;
|
||||
}
|
||||
get null() {
|
||||
return null;
|
||||
}
|
||||
get undefined() {
|
||||
return undefined;
|
||||
}
|
||||
get stringOptional() {
|
||||
return this.pick(this.string, this.undefined);
|
||||
}
|
||||
get stringNullable() {
|
||||
return this.pick(this.string, this.null);
|
||||
}
|
||||
get numberOptional() {
|
||||
return this.pick(this.number, this.undefined);
|
||||
}
|
||||
get numberNullable() {
|
||||
return this.pick(this.number, this.null);
|
||||
}
|
||||
get booleanOptional() {
|
||||
return this.pick(this.boolean, this.undefined);
|
||||
}
|
||||
get booleanNullable() {
|
||||
return this.pick(this.boolean, this.null);
|
||||
}
|
||||
}
|
||||
exports.Mocker = Mocker;
|
||||
5
shared/node_modules/zod/lib/benchmarks/discriminatedUnion.d.ts
generated
vendored
Normal file
5
shared/node_modules/zod/lib/benchmarks/discriminatedUnion.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import Benchmark from "benchmark";
|
||||
declare const _default: {
|
||||
suites: Benchmark.Suite[];
|
||||
};
|
||||
export default _default;
|
||||
79
shared/node_modules/zod/lib/benchmarks/discriminatedUnion.js
generated
vendored
Normal file
79
shared/node_modules/zod/lib/benchmarks/discriminatedUnion.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const benchmark_1 = __importDefault(require("benchmark"));
|
||||
const index_1 = require("../index");
|
||||
const doubleSuite = new benchmark_1.default.Suite("z.discriminatedUnion: double");
|
||||
const manySuite = new benchmark_1.default.Suite("z.discriminatedUnion: many");
|
||||
const aSchema = index_1.z.object({
|
||||
type: index_1.z.literal("a"),
|
||||
});
|
||||
const objA = {
|
||||
type: "a",
|
||||
};
|
||||
const bSchema = index_1.z.object({
|
||||
type: index_1.z.literal("b"),
|
||||
});
|
||||
const objB = {
|
||||
type: "b",
|
||||
};
|
||||
const cSchema = index_1.z.object({
|
||||
type: index_1.z.literal("c"),
|
||||
});
|
||||
const objC = {
|
||||
type: "c",
|
||||
};
|
||||
const dSchema = index_1.z.object({
|
||||
type: index_1.z.literal("d"),
|
||||
});
|
||||
const double = index_1.z.discriminatedUnion("type", [aSchema, bSchema]);
|
||||
const many = index_1.z.discriminatedUnion("type", [aSchema, bSchema, cSchema, dSchema]);
|
||||
doubleSuite
|
||||
.add("valid: a", () => {
|
||||
double.parse(objA);
|
||||
})
|
||||
.add("valid: b", () => {
|
||||
double.parse(objB);
|
||||
})
|
||||
.add("invalid: null", () => {
|
||||
try {
|
||||
double.parse(null);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.add("invalid: wrong shape", () => {
|
||||
try {
|
||||
double.parse(objC);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`${doubleSuite.name}: ${e.target}`);
|
||||
});
|
||||
manySuite
|
||||
.add("valid: a", () => {
|
||||
many.parse(objA);
|
||||
})
|
||||
.add("valid: c", () => {
|
||||
many.parse(objC);
|
||||
})
|
||||
.add("invalid: null", () => {
|
||||
try {
|
||||
many.parse(null);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.add("invalid: wrong shape", () => {
|
||||
try {
|
||||
many.parse({ type: "unknown" });
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`${manySuite.name}: ${e.target}`);
|
||||
});
|
||||
exports.default = {
|
||||
suites: [doubleSuite, manySuite],
|
||||
};
|
||||
1
shared/node_modules/zod/lib/benchmarks/index.d.ts
generated
vendored
Normal file
1
shared/node_modules/zod/lib/benchmarks/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
46
shared/node_modules/zod/lib/benchmarks/index.js
generated
vendored
Normal file
46
shared/node_modules/zod/lib/benchmarks/index.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const discriminatedUnion_1 = __importDefault(require("./discriminatedUnion"));
|
||||
const object_1 = __importDefault(require("./object"));
|
||||
const primitives_1 = __importDefault(require("./primitives"));
|
||||
const realworld_1 = __importDefault(require("./realworld"));
|
||||
const string_1 = __importDefault(require("./string"));
|
||||
const union_1 = __importDefault(require("./union"));
|
||||
const argv = process.argv.slice(2);
|
||||
let suites = [];
|
||||
if (!argv.length) {
|
||||
suites = [
|
||||
...realworld_1.default.suites,
|
||||
...primitives_1.default.suites,
|
||||
...string_1.default.suites,
|
||||
...object_1.default.suites,
|
||||
...union_1.default.suites,
|
||||
...discriminatedUnion_1.default.suites,
|
||||
];
|
||||
}
|
||||
else {
|
||||
if (argv.includes("--realworld")) {
|
||||
suites.push(...realworld_1.default.suites);
|
||||
}
|
||||
if (argv.includes("--primitives")) {
|
||||
suites.push(...primitives_1.default.suites);
|
||||
}
|
||||
if (argv.includes("--string")) {
|
||||
suites.push(...string_1.default.suites);
|
||||
}
|
||||
if (argv.includes("--object")) {
|
||||
suites.push(...object_1.default.suites);
|
||||
}
|
||||
if (argv.includes("--union")) {
|
||||
suites.push(...union_1.default.suites);
|
||||
}
|
||||
if (argv.includes("--discriminatedUnion")) {
|
||||
suites.push(...discriminatedUnion_1.default.suites);
|
||||
}
|
||||
}
|
||||
for (const suite of suites) {
|
||||
suite.run();
|
||||
}
|
||||
5
shared/node_modules/zod/lib/benchmarks/object.d.ts
generated
vendored
Normal file
5
shared/node_modules/zod/lib/benchmarks/object.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import Benchmark from "benchmark";
|
||||
declare const _default: {
|
||||
suites: Benchmark.Suite[];
|
||||
};
|
||||
export default _default;
|
||||
70
shared/node_modules/zod/lib/benchmarks/object.js
generated
vendored
Normal file
70
shared/node_modules/zod/lib/benchmarks/object.js
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const benchmark_1 = __importDefault(require("benchmark"));
|
||||
const index_1 = require("../index");
|
||||
const emptySuite = new benchmark_1.default.Suite("z.object: empty");
|
||||
const shortSuite = new benchmark_1.default.Suite("z.object: short");
|
||||
const longSuite = new benchmark_1.default.Suite("z.object: long");
|
||||
const empty = index_1.z.object({});
|
||||
const short = index_1.z.object({
|
||||
string: index_1.z.string(),
|
||||
});
|
||||
const long = index_1.z.object({
|
||||
string: index_1.z.string(),
|
||||
number: index_1.z.number(),
|
||||
boolean: index_1.z.boolean(),
|
||||
});
|
||||
emptySuite
|
||||
.add("valid", () => {
|
||||
empty.parse({});
|
||||
})
|
||||
.add("valid: extra keys", () => {
|
||||
empty.parse({ string: "string" });
|
||||
})
|
||||
.add("invalid: null", () => {
|
||||
try {
|
||||
empty.parse(null);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`${emptySuite.name}: ${e.target}`);
|
||||
});
|
||||
shortSuite
|
||||
.add("valid", () => {
|
||||
short.parse({ string: "string" });
|
||||
})
|
||||
.add("valid: extra keys", () => {
|
||||
short.parse({ string: "string", number: 42 });
|
||||
})
|
||||
.add("invalid: null", () => {
|
||||
try {
|
||||
short.parse(null);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`${shortSuite.name}: ${e.target}`);
|
||||
});
|
||||
longSuite
|
||||
.add("valid", () => {
|
||||
long.parse({ string: "string", number: 42, boolean: true });
|
||||
})
|
||||
.add("valid: extra keys", () => {
|
||||
long.parse({ string: "string", number: 42, boolean: true, list: [] });
|
||||
})
|
||||
.add("invalid: null", () => {
|
||||
try {
|
||||
long.parse(null);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`${longSuite.name}: ${e.target}`);
|
||||
});
|
||||
exports.default = {
|
||||
suites: [emptySuite, shortSuite, longSuite],
|
||||
};
|
||||
5
shared/node_modules/zod/lib/benchmarks/primitives.d.ts
generated
vendored
Normal file
5
shared/node_modules/zod/lib/benchmarks/primitives.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import Benchmark from "benchmark";
|
||||
declare const _default: {
|
||||
suites: Benchmark.Suite[];
|
||||
};
|
||||
export default _default;
|
||||
136
shared/node_modules/zod/lib/benchmarks/primitives.js
generated
vendored
Normal file
136
shared/node_modules/zod/lib/benchmarks/primitives.js
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const benchmark_1 = __importDefault(require("benchmark"));
|
||||
const Mocker_1 = require("../__tests__/Mocker");
|
||||
const index_1 = require("../index");
|
||||
const val = new Mocker_1.Mocker();
|
||||
const enumSuite = new benchmark_1.default.Suite("z.enum");
|
||||
const enumSchema = index_1.z.enum(["a", "b", "c"]);
|
||||
enumSuite
|
||||
.add("valid", () => {
|
||||
enumSchema.parse("a");
|
||||
})
|
||||
.add("invalid", () => {
|
||||
try {
|
||||
enumSchema.parse("x");
|
||||
}
|
||||
catch (e) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`z.enum: ${e.target}`);
|
||||
});
|
||||
const undefinedSuite = new benchmark_1.default.Suite("z.undefined");
|
||||
const undefinedSchema = index_1.z.undefined();
|
||||
undefinedSuite
|
||||
.add("valid", () => {
|
||||
undefinedSchema.parse(undefined);
|
||||
})
|
||||
.add("invalid", () => {
|
||||
try {
|
||||
undefinedSchema.parse(1);
|
||||
}
|
||||
catch (e) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`z.undefined: ${e.target}`);
|
||||
});
|
||||
const literalSuite = new benchmark_1.default.Suite("z.literal");
|
||||
const short = "short";
|
||||
const bad = "bad";
|
||||
const literalSchema = index_1.z.literal("short");
|
||||
literalSuite
|
||||
.add("valid", () => {
|
||||
literalSchema.parse(short);
|
||||
})
|
||||
.add("invalid", () => {
|
||||
try {
|
||||
literalSchema.parse(bad);
|
||||
}
|
||||
catch (e) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`z.literal: ${e.target}`);
|
||||
});
|
||||
const numberSuite = new benchmark_1.default.Suite("z.number");
|
||||
const numberSchema = index_1.z.number().int();
|
||||
numberSuite
|
||||
.add("valid", () => {
|
||||
numberSchema.parse(1);
|
||||
})
|
||||
.add("invalid type", () => {
|
||||
try {
|
||||
numberSchema.parse("bad");
|
||||
}
|
||||
catch (e) { }
|
||||
})
|
||||
.add("invalid number", () => {
|
||||
try {
|
||||
numberSchema.parse(0.5);
|
||||
}
|
||||
catch (e) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`z.number: ${e.target}`);
|
||||
});
|
||||
const dateSuite = new benchmark_1.default.Suite("z.date");
|
||||
const plainDate = index_1.z.date();
|
||||
const minMaxDate = index_1.z
|
||||
.date()
|
||||
.min(new Date("2021-01-01"))
|
||||
.max(new Date("2030-01-01"));
|
||||
dateSuite
|
||||
.add("valid", () => {
|
||||
plainDate.parse(new Date());
|
||||
})
|
||||
.add("invalid", () => {
|
||||
try {
|
||||
plainDate.parse(1);
|
||||
}
|
||||
catch (e) { }
|
||||
})
|
||||
.add("valid min and max", () => {
|
||||
minMaxDate.parse(new Date("2023-01-01"));
|
||||
})
|
||||
.add("invalid min", () => {
|
||||
try {
|
||||
minMaxDate.parse(new Date("2019-01-01"));
|
||||
}
|
||||
catch (e) { }
|
||||
})
|
||||
.add("invalid max", () => {
|
||||
try {
|
||||
minMaxDate.parse(new Date("2031-01-01"));
|
||||
}
|
||||
catch (e) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`z.date: ${e.target}`);
|
||||
});
|
||||
const symbolSuite = new benchmark_1.default.Suite("z.symbol");
|
||||
const symbolSchema = index_1.z.symbol();
|
||||
symbolSuite
|
||||
.add("valid", () => {
|
||||
symbolSchema.parse(val.symbol);
|
||||
})
|
||||
.add("invalid", () => {
|
||||
try {
|
||||
symbolSchema.parse(1);
|
||||
}
|
||||
catch (e) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`z.symbol: ${e.target}`);
|
||||
});
|
||||
exports.default = {
|
||||
suites: [
|
||||
enumSuite,
|
||||
undefinedSuite,
|
||||
literalSuite,
|
||||
numberSuite,
|
||||
dateSuite,
|
||||
symbolSuite,
|
||||
],
|
||||
};
|
||||
5
shared/node_modules/zod/lib/benchmarks/realworld.d.ts
generated
vendored
Normal file
5
shared/node_modules/zod/lib/benchmarks/realworld.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import Benchmark from "benchmark";
|
||||
declare const _default: {
|
||||
suites: Benchmark.Suite[];
|
||||
};
|
||||
export default _default;
|
||||
56
shared/node_modules/zod/lib/benchmarks/realworld.js
generated
vendored
Normal file
56
shared/node_modules/zod/lib/benchmarks/realworld.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const benchmark_1 = __importDefault(require("benchmark"));
|
||||
const index_1 = require("../index");
|
||||
const shortSuite = new benchmark_1.default.Suite("realworld");
|
||||
const People = index_1.z.array(index_1.z.object({
|
||||
type: index_1.z.literal("person"),
|
||||
hair: index_1.z.enum(["blue", "brown"]),
|
||||
active: index_1.z.boolean(),
|
||||
name: index_1.z.string(),
|
||||
age: index_1.z.number().int(),
|
||||
hobbies: index_1.z.array(index_1.z.string()),
|
||||
address: index_1.z.object({
|
||||
street: index_1.z.string(),
|
||||
zip: index_1.z.string(),
|
||||
country: index_1.z.string(),
|
||||
}),
|
||||
}));
|
||||
let i = 0;
|
||||
function num() {
|
||||
return ++i;
|
||||
}
|
||||
function str() {
|
||||
return (++i % 100).toString(16);
|
||||
}
|
||||
function array(fn) {
|
||||
return Array.from({ length: ++i % 10 }, () => fn());
|
||||
}
|
||||
const people = Array.from({ length: 100 }, () => {
|
||||
return {
|
||||
type: "person",
|
||||
hair: i % 2 ? "blue" : "brown",
|
||||
active: !!(i % 2),
|
||||
name: str(),
|
||||
age: num(),
|
||||
hobbies: array(str),
|
||||
address: {
|
||||
street: str(),
|
||||
zip: str(),
|
||||
country: str(),
|
||||
},
|
||||
};
|
||||
});
|
||||
shortSuite
|
||||
.add("valid", () => {
|
||||
People.parse(people);
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`${shortSuite.name}: ${e.target}`);
|
||||
});
|
||||
exports.default = {
|
||||
suites: [shortSuite],
|
||||
};
|
||||
5
shared/node_modules/zod/lib/benchmarks/string.d.ts
generated
vendored
Normal file
5
shared/node_modules/zod/lib/benchmarks/string.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import Benchmark from "benchmark";
|
||||
declare const _default: {
|
||||
suites: Benchmark.Suite[];
|
||||
};
|
||||
export default _default;
|
||||
55
shared/node_modules/zod/lib/benchmarks/string.js
generated
vendored
Normal file
55
shared/node_modules/zod/lib/benchmarks/string.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const benchmark_1 = __importDefault(require("benchmark"));
|
||||
const index_1 = require("../index");
|
||||
const SUITE_NAME = "z.string";
|
||||
const suite = new benchmark_1.default.Suite(SUITE_NAME);
|
||||
const empty = "";
|
||||
const short = "short";
|
||||
const long = "long".repeat(256);
|
||||
const manual = (str) => {
|
||||
if (typeof str !== "string") {
|
||||
throw new Error("Not a string");
|
||||
}
|
||||
return str;
|
||||
};
|
||||
const stringSchema = index_1.z.string();
|
||||
const optionalStringSchema = index_1.z.string().optional();
|
||||
const optionalNullableStringSchema = index_1.z.string().optional().nullable();
|
||||
suite
|
||||
.add("empty string", () => {
|
||||
stringSchema.parse(empty);
|
||||
})
|
||||
.add("short string", () => {
|
||||
stringSchema.parse(short);
|
||||
})
|
||||
.add("long string", () => {
|
||||
stringSchema.parse(long);
|
||||
})
|
||||
.add("optional string", () => {
|
||||
optionalStringSchema.parse(long);
|
||||
})
|
||||
.add("nullable string", () => {
|
||||
optionalNullableStringSchema.parse(long);
|
||||
})
|
||||
.add("nullable (null) string", () => {
|
||||
optionalNullableStringSchema.parse(null);
|
||||
})
|
||||
.add("invalid: null", () => {
|
||||
try {
|
||||
stringSchema.parse(null);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.add("manual parser: long", () => {
|
||||
manual(long);
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`${SUITE_NAME}: ${e.target}`);
|
||||
});
|
||||
exports.default = {
|
||||
suites: [suite],
|
||||
};
|
||||
5
shared/node_modules/zod/lib/benchmarks/union.d.ts
generated
vendored
Normal file
5
shared/node_modules/zod/lib/benchmarks/union.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import Benchmark from "benchmark";
|
||||
declare const _default: {
|
||||
suites: Benchmark.Suite[];
|
||||
};
|
||||
export default _default;
|
||||
79
shared/node_modules/zod/lib/benchmarks/union.js
generated
vendored
Normal file
79
shared/node_modules/zod/lib/benchmarks/union.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const benchmark_1 = __importDefault(require("benchmark"));
|
||||
const index_1 = require("../index");
|
||||
const doubleSuite = new benchmark_1.default.Suite("z.union: double");
|
||||
const manySuite = new benchmark_1.default.Suite("z.union: many");
|
||||
const aSchema = index_1.z.object({
|
||||
type: index_1.z.literal("a"),
|
||||
});
|
||||
const objA = {
|
||||
type: "a",
|
||||
};
|
||||
const bSchema = index_1.z.object({
|
||||
type: index_1.z.literal("b"),
|
||||
});
|
||||
const objB = {
|
||||
type: "b",
|
||||
};
|
||||
const cSchema = index_1.z.object({
|
||||
type: index_1.z.literal("c"),
|
||||
});
|
||||
const objC = {
|
||||
type: "c",
|
||||
};
|
||||
const dSchema = index_1.z.object({
|
||||
type: index_1.z.literal("d"),
|
||||
});
|
||||
const double = index_1.z.union([aSchema, bSchema]);
|
||||
const many = index_1.z.union([aSchema, bSchema, cSchema, dSchema]);
|
||||
doubleSuite
|
||||
.add("valid: a", () => {
|
||||
double.parse(objA);
|
||||
})
|
||||
.add("valid: b", () => {
|
||||
double.parse(objB);
|
||||
})
|
||||
.add("invalid: null", () => {
|
||||
try {
|
||||
double.parse(null);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.add("invalid: wrong shape", () => {
|
||||
try {
|
||||
double.parse(objC);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`${doubleSuite.name}: ${e.target}`);
|
||||
});
|
||||
manySuite
|
||||
.add("valid: a", () => {
|
||||
many.parse(objA);
|
||||
})
|
||||
.add("valid: c", () => {
|
||||
many.parse(objC);
|
||||
})
|
||||
.add("invalid: null", () => {
|
||||
try {
|
||||
many.parse(null);
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.add("invalid: wrong shape", () => {
|
||||
try {
|
||||
many.parse({ type: "unknown" });
|
||||
}
|
||||
catch (err) { }
|
||||
})
|
||||
.on("cycle", (e) => {
|
||||
console.log(`${manySuite.name}: ${e.target}`);
|
||||
});
|
||||
exports.default = {
|
||||
suites: [doubleSuite, manySuite],
|
||||
};
|
||||
5
shared/node_modules/zod/lib/errors.d.ts
generated
vendored
Normal file
5
shared/node_modules/zod/lib/errors.d.ts
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import defaultErrorMap from "./locales/en";
|
||||
import type { ZodErrorMap } from "./ZodError";
|
||||
export { defaultErrorMap };
|
||||
export declare function setErrorMap(map: ZodErrorMap): void;
|
||||
export declare function getErrorMap(): ZodErrorMap;
|
||||
17
shared/node_modules/zod/lib/errors.js
generated
vendored
Normal file
17
shared/node_modules/zod/lib/errors.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getErrorMap = exports.setErrorMap = exports.defaultErrorMap = void 0;
|
||||
const en_1 = __importDefault(require("./locales/en"));
|
||||
exports.defaultErrorMap = en_1.default;
|
||||
let overrideErrorMap = en_1.default;
|
||||
function setErrorMap(map) {
|
||||
overrideErrorMap = map;
|
||||
}
|
||||
exports.setErrorMap = setErrorMap;
|
||||
function getErrorMap() {
|
||||
return overrideErrorMap;
|
||||
}
|
||||
exports.getErrorMap = getErrorMap;
|
||||
6
shared/node_modules/zod/lib/external.d.ts
generated
vendored
Normal file
6
shared/node_modules/zod/lib/external.d.ts
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
export * from "./errors";
|
||||
export * from "./helpers/parseUtil";
|
||||
export * from "./helpers/typeAliases";
|
||||
export * from "./helpers/util";
|
||||
export * from "./types";
|
||||
export * from "./ZodError";
|
||||
18
shared/node_modules/zod/lib/external.js
generated
vendored
Normal file
18
shared/node_modules/zod/lib/external.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./errors"), exports);
|
||||
__exportStar(require("./helpers/parseUtil"), exports);
|
||||
__exportStar(require("./helpers/typeAliases"), exports);
|
||||
__exportStar(require("./helpers/util"), exports);
|
||||
__exportStar(require("./types"), exports);
|
||||
__exportStar(require("./ZodError"), exports);
|
||||
8
shared/node_modules/zod/lib/helpers/enumUtil.d.ts
generated
vendored
Normal file
8
shared/node_modules/zod/lib/helpers/enumUtil.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export declare namespace enumUtil {
|
||||
type UnionToIntersectionFn<T> = (T extends unknown ? (k: () => T) => void : never) extends (k: infer Intersection) => void ? Intersection : never;
|
||||
type GetUnionLast<T> = UnionToIntersectionFn<T> extends () => infer Last ? Last : never;
|
||||
type UnionToTuple<T, Tuple extends unknown[] = []> = [T] extends [never] ? Tuple : UnionToTuple<Exclude<T, GetUnionLast<T>>, [GetUnionLast<T>, ...Tuple]>;
|
||||
type CastToStringTuple<T> = T extends [string, ...string[]] ? T : never;
|
||||
export type UnionToTupleString<T> = CastToStringTuple<UnionToTuple<T>>;
|
||||
export {};
|
||||
}
|
||||
2
shared/node_modules/zod/lib/helpers/enumUtil.js
generated
vendored
Normal file
2
shared/node_modules/zod/lib/helpers/enumUtil.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
9
shared/node_modules/zod/lib/helpers/errorUtil.d.ts
generated
vendored
Normal file
9
shared/node_modules/zod/lib/helpers/errorUtil.d.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
export declare namespace errorUtil {
|
||||
type ErrMessage = string | {
|
||||
message?: string;
|
||||
};
|
||||
const errToObj: (message?: ErrMessage | undefined) => {
|
||||
message?: string | undefined;
|
||||
};
|
||||
const toString: (message?: ErrMessage | undefined) => string | undefined;
|
||||
}
|
||||
8
shared/node_modules/zod/lib/helpers/errorUtil.js
generated
vendored
Normal file
8
shared/node_modules/zod/lib/helpers/errorUtil.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.errorUtil = void 0;
|
||||
var errorUtil;
|
||||
(function (errorUtil) {
|
||||
errorUtil.errToObj = (message) => typeof message === "string" ? { message } : message || {};
|
||||
errorUtil.toString = (message) => typeof message === "string" ? message : message === null || message === void 0 ? void 0 : message.message;
|
||||
})(errorUtil = exports.errorUtil || (exports.errorUtil = {}));
|
||||
78
shared/node_modules/zod/lib/helpers/parseUtil.d.ts
generated
vendored
Normal file
78
shared/node_modules/zod/lib/helpers/parseUtil.d.ts
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { IssueData, ZodErrorMap, ZodIssue } from "../ZodError";
|
||||
import type { ZodParsedType } from "./util";
|
||||
export declare const makeIssue: (params: {
|
||||
data: any;
|
||||
path: (string | number)[];
|
||||
errorMaps: ZodErrorMap[];
|
||||
issueData: IssueData;
|
||||
}) => ZodIssue;
|
||||
export declare type ParseParams = {
|
||||
path: (string | number)[];
|
||||
errorMap: ZodErrorMap;
|
||||
async: boolean;
|
||||
};
|
||||
export declare type ParsePathComponent = string | number;
|
||||
export declare type ParsePath = ParsePathComponent[];
|
||||
export declare const EMPTY_PATH: ParsePath;
|
||||
export interface ParseContext {
|
||||
readonly common: {
|
||||
readonly issues: ZodIssue[];
|
||||
readonly contextualErrorMap?: ZodErrorMap;
|
||||
readonly async: boolean;
|
||||
};
|
||||
readonly path: ParsePath;
|
||||
readonly schemaErrorMap?: ZodErrorMap;
|
||||
readonly parent: ParseContext | null;
|
||||
readonly data: any;
|
||||
readonly parsedType: ZodParsedType;
|
||||
}
|
||||
export declare type ParseInput = {
|
||||
data: any;
|
||||
path: (string | number)[];
|
||||
parent: ParseContext;
|
||||
};
|
||||
export declare function addIssueToContext(ctx: ParseContext, issueData: IssueData): void;
|
||||
export declare type ObjectPair = {
|
||||
key: SyncParseReturnType<any>;
|
||||
value: SyncParseReturnType<any>;
|
||||
};
|
||||
export declare class ParseStatus {
|
||||
value: "aborted" | "dirty" | "valid";
|
||||
dirty(): void;
|
||||
abort(): void;
|
||||
static mergeArray(status: ParseStatus, results: SyncParseReturnType<any>[]): SyncParseReturnType;
|
||||
static mergeObjectAsync(status: ParseStatus, pairs: {
|
||||
key: ParseReturnType<any>;
|
||||
value: ParseReturnType<any>;
|
||||
}[]): Promise<SyncParseReturnType<any>>;
|
||||
static mergeObjectSync(status: ParseStatus, pairs: {
|
||||
key: SyncParseReturnType<any>;
|
||||
value: SyncParseReturnType<any>;
|
||||
alwaysSet?: boolean;
|
||||
}[]): SyncParseReturnType;
|
||||
}
|
||||
export interface ParseResult {
|
||||
status: "aborted" | "dirty" | "valid";
|
||||
data: any;
|
||||
}
|
||||
export declare type INVALID = {
|
||||
status: "aborted";
|
||||
};
|
||||
export declare const INVALID: INVALID;
|
||||
export declare type DIRTY<T> = {
|
||||
status: "dirty";
|
||||
value: T;
|
||||
};
|
||||
export declare const DIRTY: <T>(value: T) => DIRTY<T>;
|
||||
export declare type OK<T> = {
|
||||
status: "valid";
|
||||
value: T;
|
||||
};
|
||||
export declare const OK: <T>(value: T) => OK<T>;
|
||||
export declare type SyncParseReturnType<T = any> = OK<T> | DIRTY<T> | INVALID;
|
||||
export declare type AsyncParseReturnType<T> = Promise<SyncParseReturnType<T>>;
|
||||
export declare type ParseReturnType<T> = SyncParseReturnType<T> | AsyncParseReturnType<T>;
|
||||
export declare const isAborted: (x: ParseReturnType<any>) => x is INVALID;
|
||||
export declare const isDirty: <T>(x: ParseReturnType<T>) => x is OK<T> | DIRTY<T>;
|
||||
export declare const isValid: <T>(x: ParseReturnType<T>) => x is OK<T> | DIRTY<T>;
|
||||
export declare const isAsync: <T>(x: ParseReturnType<T>) => x is AsyncParseReturnType<T>;
|
||||
114
shared/node_modules/zod/lib/helpers/parseUtil.js
generated
vendored
Normal file
114
shared/node_modules/zod/lib/helpers/parseUtil.js
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isAsync = exports.isValid = exports.isDirty = exports.isAborted = exports.OK = exports.DIRTY = exports.INVALID = exports.ParseStatus = exports.addIssueToContext = exports.EMPTY_PATH = exports.makeIssue = void 0;
|
||||
const errors_1 = require("../errors");
|
||||
const en_1 = __importDefault(require("../locales/en"));
|
||||
const makeIssue = (params) => {
|
||||
const { data, path, errorMaps, issueData } = params;
|
||||
const fullPath = [...path, ...(issueData.path || [])];
|
||||
const fullIssue = {
|
||||
...issueData,
|
||||
path: fullPath,
|
||||
};
|
||||
let errorMessage = "";
|
||||
const maps = errorMaps
|
||||
.filter((m) => !!m)
|
||||
.slice()
|
||||
.reverse();
|
||||
for (const map of maps) {
|
||||
errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message;
|
||||
}
|
||||
return {
|
||||
...issueData,
|
||||
path: fullPath,
|
||||
message: issueData.message || errorMessage,
|
||||
};
|
||||
};
|
||||
exports.makeIssue = makeIssue;
|
||||
exports.EMPTY_PATH = [];
|
||||
function addIssueToContext(ctx, issueData) {
|
||||
const issue = (0, exports.makeIssue)({
|
||||
issueData: issueData,
|
||||
data: ctx.data,
|
||||
path: ctx.path,
|
||||
errorMaps: [
|
||||
ctx.common.contextualErrorMap,
|
||||
ctx.schemaErrorMap,
|
||||
(0, errors_1.getErrorMap)(),
|
||||
en_1.default,
|
||||
].filter((x) => !!x),
|
||||
});
|
||||
ctx.common.issues.push(issue);
|
||||
}
|
||||
exports.addIssueToContext = addIssueToContext;
|
||||
class ParseStatus {
|
||||
constructor() {
|
||||
this.value = "valid";
|
||||
}
|
||||
dirty() {
|
||||
if (this.value === "valid")
|
||||
this.value = "dirty";
|
||||
}
|
||||
abort() {
|
||||
if (this.value !== "aborted")
|
||||
this.value = "aborted";
|
||||
}
|
||||
static mergeArray(status, results) {
|
||||
const arrayValue = [];
|
||||
for (const s of results) {
|
||||
if (s.status === "aborted")
|
||||
return exports.INVALID;
|
||||
if (s.status === "dirty")
|
||||
status.dirty();
|
||||
arrayValue.push(s.value);
|
||||
}
|
||||
return { status: status.value, value: arrayValue };
|
||||
}
|
||||
static async mergeObjectAsync(status, pairs) {
|
||||
const syncPairs = [];
|
||||
for (const pair of pairs) {
|
||||
syncPairs.push({
|
||||
key: await pair.key,
|
||||
value: await pair.value,
|
||||
});
|
||||
}
|
||||
return ParseStatus.mergeObjectSync(status, syncPairs);
|
||||
}
|
||||
static mergeObjectSync(status, pairs) {
|
||||
const finalObject = {};
|
||||
for (const pair of pairs) {
|
||||
const { key, value } = pair;
|
||||
if (key.status === "aborted")
|
||||
return exports.INVALID;
|
||||
if (value.status === "aborted")
|
||||
return exports.INVALID;
|
||||
if (key.status === "dirty")
|
||||
status.dirty();
|
||||
if (value.status === "dirty")
|
||||
status.dirty();
|
||||
if (typeof value.value !== "undefined" || pair.alwaysSet) {
|
||||
finalObject[key.value] = value.value;
|
||||
}
|
||||
}
|
||||
return { status: status.value, value: finalObject };
|
||||
}
|
||||
}
|
||||
exports.ParseStatus = ParseStatus;
|
||||
exports.INVALID = Object.freeze({
|
||||
status: "aborted",
|
||||
});
|
||||
const DIRTY = (value) => ({ status: "dirty", value });
|
||||
exports.DIRTY = DIRTY;
|
||||
const OK = (value) => ({ status: "valid", value });
|
||||
exports.OK = OK;
|
||||
const isAborted = (x) => x.status === "aborted";
|
||||
exports.isAborted = isAborted;
|
||||
const isDirty = (x) => x.status === "dirty";
|
||||
exports.isDirty = isDirty;
|
||||
const isValid = (x) => x.status === "valid";
|
||||
exports.isValid = isValid;
|
||||
const isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
|
||||
exports.isAsync = isAsync;
|
||||
8
shared/node_modules/zod/lib/helpers/partialUtil.d.ts
generated
vendored
Normal file
8
shared/node_modules/zod/lib/helpers/partialUtil.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { ZodArray, ZodNullable, ZodObject, ZodOptional, ZodRawShape, ZodTuple, ZodTupleItems, ZodTypeAny } from "../index";
|
||||
export declare namespace partialUtil {
|
||||
type DeepPartial<T extends ZodTypeAny> = T extends ZodObject<ZodRawShape> ? ZodObject<{
|
||||
[k in keyof T["shape"]]: ZodOptional<DeepPartial<T["shape"][k]>>;
|
||||
}, T["_def"]["unknownKeys"], T["_def"]["catchall"]> : T extends ZodArray<infer Type, infer Card> ? ZodArray<DeepPartial<Type>, Card> : T extends ZodOptional<infer Type> ? ZodOptional<DeepPartial<Type>> : T extends ZodNullable<infer Type> ? ZodNullable<DeepPartial<Type>> : T extends ZodTuple<infer Items> ? {
|
||||
[k in keyof Items]: Items[k] extends ZodTypeAny ? DeepPartial<Items[k]> : never;
|
||||
} extends infer PI ? PI extends ZodTupleItems ? ZodTuple<PI> : never : never : T;
|
||||
}
|
||||
2
shared/node_modules/zod/lib/helpers/partialUtil.js
generated
vendored
Normal file
2
shared/node_modules/zod/lib/helpers/partialUtil.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
2
shared/node_modules/zod/lib/helpers/typeAliases.d.ts
generated
vendored
Normal file
2
shared/node_modules/zod/lib/helpers/typeAliases.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export declare type Primitive = string | number | symbol | bigint | boolean | null | undefined;
|
||||
export declare type Scalars = Primitive | Primitive[];
|
||||
2
shared/node_modules/zod/lib/helpers/typeAliases.js
generated
vendored
Normal file
2
shared/node_modules/zod/lib/helpers/typeAliases.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
68
shared/node_modules/zod/lib/helpers/util.d.ts
generated
vendored
Normal file
68
shared/node_modules/zod/lib/helpers/util.d.ts
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
export declare namespace util {
|
||||
type AssertEqual<T, U> = (<V>() => V extends T ? 1 : 2) extends <V>() => V extends U ? 1 : 2 ? true : false;
|
||||
export type isAny<T> = 0 extends 1 & T ? true : false;
|
||||
export const assertEqual: <A, B>(val: AssertEqual<A, B>) => AssertEqual<A, B>;
|
||||
export function assertIs<T>(_arg: T): void;
|
||||
export function assertNever(_x: never): never;
|
||||
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||
export type OmitKeys<T, K extends string> = Pick<T, Exclude<keyof T, K>>;
|
||||
export type MakePartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
export const arrayToEnum: <T extends string, U extends [T, ...T[]]>(items: U) => { [k in U[number]]: k; };
|
||||
export const getValidEnumValues: (obj: any) => any[];
|
||||
export const objectValues: (obj: any) => any[];
|
||||
export const objectKeys: ObjectConstructor["keys"];
|
||||
export const find: <T>(arr: T[], checker: (arg: T) => any) => T | undefined;
|
||||
export type identity<T> = objectUtil.identity<T>;
|
||||
export type flatten<T> = objectUtil.flatten<T>;
|
||||
export type noUndefined<T> = T extends undefined ? never : T;
|
||||
export const isInteger: NumberConstructor["isInteger"];
|
||||
export function joinValues<T extends any[]>(array: T, separator?: string): string;
|
||||
export const jsonStringifyReplacer: (_: string, value: any) => any;
|
||||
export {};
|
||||
}
|
||||
export declare namespace objectUtil {
|
||||
export type MergeShapes<U, V> = {
|
||||
[k in Exclude<keyof U, keyof V>]: U[k];
|
||||
} & V;
|
||||
type requiredKeys<T extends object> = {
|
||||
[k in keyof T]: undefined extends T[k] ? never : k;
|
||||
}[keyof T];
|
||||
export type addQuestionMarks<T extends object, R extends keyof T = requiredKeys<T>> = Pick<Required<T>, R> & Partial<T>;
|
||||
export type identity<T> = T;
|
||||
export type flatten<T> = identity<{
|
||||
[k in keyof T]: T[k];
|
||||
}>;
|
||||
export type noNeverKeys<T> = {
|
||||
[k in keyof T]: [T[k]] extends [never] ? never : k;
|
||||
}[keyof T];
|
||||
export type noNever<T> = identity<{
|
||||
[k in noNeverKeys<T>]: k extends keyof T ? T[k] : never;
|
||||
}>;
|
||||
export const mergeShapes: <U, T>(first: U, second: T) => T & U;
|
||||
export type extendShape<A, B> = flatten<Omit<A, keyof B> & B>;
|
||||
export {};
|
||||
}
|
||||
export declare const ZodParsedType: {
|
||||
function: "function";
|
||||
number: "number";
|
||||
string: "string";
|
||||
nan: "nan";
|
||||
integer: "integer";
|
||||
float: "float";
|
||||
boolean: "boolean";
|
||||
date: "date";
|
||||
bigint: "bigint";
|
||||
symbol: "symbol";
|
||||
undefined: "undefined";
|
||||
null: "null";
|
||||
array: "array";
|
||||
object: "object";
|
||||
unknown: "unknown";
|
||||
promise: "promise";
|
||||
void: "void";
|
||||
never: "never";
|
||||
map: "map";
|
||||
set: "set";
|
||||
};
|
||||
export declare type ZodParsedType = keyof typeof ZodParsedType;
|
||||
export declare const getParsedType: (data: any) => ZodParsedType;
|
||||
142
shared/node_modules/zod/lib/helpers/util.js
generated
vendored
Normal file
142
shared/node_modules/zod/lib/helpers/util.js
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getParsedType = exports.ZodParsedType = exports.objectUtil = exports.util = void 0;
|
||||
var util;
|
||||
(function (util) {
|
||||
util.assertEqual = (val) => val;
|
||||
function assertIs(_arg) { }
|
||||
util.assertIs = assertIs;
|
||||
function assertNever(_x) {
|
||||
throw new Error();
|
||||
}
|
||||
util.assertNever = assertNever;
|
||||
util.arrayToEnum = (items) => {
|
||||
const obj = {};
|
||||
for (const item of items) {
|
||||
obj[item] = item;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
util.getValidEnumValues = (obj) => {
|
||||
const validKeys = util.objectKeys(obj).filter((k) => typeof obj[obj[k]] !== "number");
|
||||
const filtered = {};
|
||||
for (const k of validKeys) {
|
||||
filtered[k] = obj[k];
|
||||
}
|
||||
return util.objectValues(filtered);
|
||||
};
|
||||
util.objectValues = (obj) => {
|
||||
return util.objectKeys(obj).map(function (e) {
|
||||
return obj[e];
|
||||
});
|
||||
};
|
||||
util.objectKeys = typeof Object.keys === "function"
|
||||
? (obj) => Object.keys(obj)
|
||||
: (object) => {
|
||||
const keys = [];
|
||||
for (const key in object) {
|
||||
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
util.find = (arr, checker) => {
|
||||
for (const item of arr) {
|
||||
if (checker(item))
|
||||
return item;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
util.isInteger = typeof Number.isInteger === "function"
|
||||
? (val) => Number.isInteger(val)
|
||||
: (val) => typeof val === "number" && isFinite(val) && Math.floor(val) === val;
|
||||
function joinValues(array, separator = " | ") {
|
||||
return array
|
||||
.map((val) => (typeof val === "string" ? `'${val}'` : val))
|
||||
.join(separator);
|
||||
}
|
||||
util.joinValues = joinValues;
|
||||
util.jsonStringifyReplacer = (_, value) => {
|
||||
if (typeof value === "bigint") {
|
||||
return value.toString();
|
||||
}
|
||||
return value;
|
||||
};
|
||||
})(util = exports.util || (exports.util = {}));
|
||||
var objectUtil;
|
||||
(function (objectUtil) {
|
||||
objectUtil.mergeShapes = (first, second) => {
|
||||
return {
|
||||
...first,
|
||||
...second,
|
||||
};
|
||||
};
|
||||
})(objectUtil = exports.objectUtil || (exports.objectUtil = {}));
|
||||
exports.ZodParsedType = util.arrayToEnum([
|
||||
"string",
|
||||
"nan",
|
||||
"number",
|
||||
"integer",
|
||||
"float",
|
||||
"boolean",
|
||||
"date",
|
||||
"bigint",
|
||||
"symbol",
|
||||
"function",
|
||||
"undefined",
|
||||
"null",
|
||||
"array",
|
||||
"object",
|
||||
"unknown",
|
||||
"promise",
|
||||
"void",
|
||||
"never",
|
||||
"map",
|
||||
"set",
|
||||
]);
|
||||
const getParsedType = (data) => {
|
||||
const t = typeof data;
|
||||
switch (t) {
|
||||
case "undefined":
|
||||
return exports.ZodParsedType.undefined;
|
||||
case "string":
|
||||
return exports.ZodParsedType.string;
|
||||
case "number":
|
||||
return isNaN(data) ? exports.ZodParsedType.nan : exports.ZodParsedType.number;
|
||||
case "boolean":
|
||||
return exports.ZodParsedType.boolean;
|
||||
case "function":
|
||||
return exports.ZodParsedType.function;
|
||||
case "bigint":
|
||||
return exports.ZodParsedType.bigint;
|
||||
case "symbol":
|
||||
return exports.ZodParsedType.symbol;
|
||||
case "object":
|
||||
if (Array.isArray(data)) {
|
||||
return exports.ZodParsedType.array;
|
||||
}
|
||||
if (data === null) {
|
||||
return exports.ZodParsedType.null;
|
||||
}
|
||||
if (data.then &&
|
||||
typeof data.then === "function" &&
|
||||
data.catch &&
|
||||
typeof data.catch === "function") {
|
||||
return exports.ZodParsedType.promise;
|
||||
}
|
||||
if (typeof Map !== "undefined" && data instanceof Map) {
|
||||
return exports.ZodParsedType.map;
|
||||
}
|
||||
if (typeof Set !== "undefined" && data instanceof Set) {
|
||||
return exports.ZodParsedType.set;
|
||||
}
|
||||
if (typeof Date !== "undefined" && data instanceof Date) {
|
||||
return exports.ZodParsedType.date;
|
||||
}
|
||||
return exports.ZodParsedType.object;
|
||||
default:
|
||||
return exports.ZodParsedType.unknown;
|
||||
}
|
||||
};
|
||||
exports.getParsedType = getParsedType;
|
||||
4
shared/node_modules/zod/lib/index.d.ts
generated
vendored
Normal file
4
shared/node_modules/zod/lib/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import * as z from "./external";
|
||||
export * from "./external";
|
||||
export { z };
|
||||
export default z;
|
||||
29
shared/node_modules/zod/lib/index.js
generated
vendored
Normal file
29
shared/node_modules/zod/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.z = void 0;
|
||||
const z = __importStar(require("./external"));
|
||||
exports.z = z;
|
||||
__exportStar(require("./external"), exports);
|
||||
exports.default = z;
|
||||
3953
shared/node_modules/zod/lib/index.mjs
generated
vendored
Normal file
3953
shared/node_modules/zod/lib/index.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4066
shared/node_modules/zod/lib/index.umd.js
generated
vendored
Normal file
4066
shared/node_modules/zod/lib/index.umd.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3
shared/node_modules/zod/lib/locales/en.d.ts
generated
vendored
Normal file
3
shared/node_modules/zod/lib/locales/en.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
import { ZodErrorMap } from "../ZodError";
|
||||
declare const errorMap: ZodErrorMap;
|
||||
export default errorMap;
|
||||
129
shared/node_modules/zod/lib/locales/en.js
generated
vendored
Normal file
129
shared/node_modules/zod/lib/locales/en.js
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const util_1 = require("../helpers/util");
|
||||
const ZodError_1 = require("../ZodError");
|
||||
const errorMap = (issue, _ctx) => {
|
||||
let message;
|
||||
switch (issue.code) {
|
||||
case ZodError_1.ZodIssueCode.invalid_type:
|
||||
if (issue.received === util_1.ZodParsedType.undefined) {
|
||||
message = "Required";
|
||||
}
|
||||
else {
|
||||
message = `Expected ${issue.expected}, received ${issue.received}`;
|
||||
}
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.invalid_literal:
|
||||
message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util_1.util.jsonStringifyReplacer)}`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.unrecognized_keys:
|
||||
message = `Unrecognized key(s) in object: ${util_1.util.joinValues(issue.keys, ", ")}`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.invalid_union:
|
||||
message = `Invalid input`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.invalid_union_discriminator:
|
||||
message = `Invalid discriminator value. Expected ${util_1.util.joinValues(issue.options)}`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.invalid_enum_value:
|
||||
message = `Invalid enum value. Expected ${util_1.util.joinValues(issue.options)}, received '${issue.received}'`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.invalid_arguments:
|
||||
message = `Invalid function arguments`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.invalid_return_type:
|
||||
message = `Invalid function return type`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.invalid_date:
|
||||
message = `Invalid date`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.invalid_string:
|
||||
if (typeof issue.validation === "object") {
|
||||
if ("includes" in issue.validation) {
|
||||
message = `Invalid input: must include "${issue.validation.includes}"`;
|
||||
if (typeof issue.validation.position === "number") {
|
||||
message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`;
|
||||
}
|
||||
}
|
||||
else if ("startsWith" in issue.validation) {
|
||||
message = `Invalid input: must start with "${issue.validation.startsWith}"`;
|
||||
}
|
||||
else if ("endsWith" in issue.validation) {
|
||||
message = `Invalid input: must end with "${issue.validation.endsWith}"`;
|
||||
}
|
||||
else {
|
||||
util_1.util.assertNever(issue.validation);
|
||||
}
|
||||
}
|
||||
else if (issue.validation !== "regex") {
|
||||
message = `Invalid ${issue.validation}`;
|
||||
}
|
||||
else {
|
||||
message = "Invalid";
|
||||
}
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.too_small:
|
||||
if (issue.type === "array")
|
||||
message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`;
|
||||
else if (issue.type === "string")
|
||||
message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`;
|
||||
else if (issue.type === "number")
|
||||
message = `Number must be ${issue.exact
|
||||
? `exactly equal to `
|
||||
: issue.inclusive
|
||||
? `greater than or equal to `
|
||||
: `greater than `}${issue.minimum}`;
|
||||
else if (issue.type === "date")
|
||||
message = `Date must be ${issue.exact
|
||||
? `exactly equal to `
|
||||
: issue.inclusive
|
||||
? `greater than or equal to `
|
||||
: `greater than `}${new Date(Number(issue.minimum))}`;
|
||||
else
|
||||
message = "Invalid input";
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.too_big:
|
||||
if (issue.type === "array")
|
||||
message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`;
|
||||
else if (issue.type === "string")
|
||||
message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`;
|
||||
else if (issue.type === "number")
|
||||
message = `Number must be ${issue.exact
|
||||
? `exactly`
|
||||
: issue.inclusive
|
||||
? `less than or equal to`
|
||||
: `less than`} ${issue.maximum}`;
|
||||
else if (issue.type === "bigint")
|
||||
message = `BigInt must be ${issue.exact
|
||||
? `exactly`
|
||||
: issue.inclusive
|
||||
? `less than or equal to`
|
||||
: `less than`} ${issue.maximum}`;
|
||||
else if (issue.type === "date")
|
||||
message = `Date must be ${issue.exact
|
||||
? `exactly`
|
||||
: issue.inclusive
|
||||
? `smaller than or equal to`
|
||||
: `smaller than`} ${new Date(Number(issue.maximum))}`;
|
||||
else
|
||||
message = "Invalid input";
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.custom:
|
||||
message = `Invalid input`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.invalid_intersection_types:
|
||||
message = `Intersection results could not be merged`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.not_multiple_of:
|
||||
message = `Number must be a multiple of ${issue.multipleOf}`;
|
||||
break;
|
||||
case ZodError_1.ZodIssueCode.not_finite:
|
||||
message = "Number must be finite";
|
||||
break;
|
||||
default:
|
||||
message = _ctx.defaultError;
|
||||
util_1.util.assertNever(issue);
|
||||
}
|
||||
return { message };
|
||||
};
|
||||
exports.default = errorMap;
|
||||
1012
shared/node_modules/zod/lib/types.d.ts
generated
vendored
Normal file
1012
shared/node_modules/zod/lib/types.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3252
shared/node_modules/zod/lib/types.js
generated
vendored
Normal file
3252
shared/node_modules/zod/lib/types.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
105
shared/node_modules/zod/package.json
generated
vendored
Normal file
105
shared/node_modules/zod/package.json
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"name": "zod",
|
||||
"version": "3.21.4",
|
||||
"author": "Colin McDonnell <colin@colinhacks.com>",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/colinhacks/zod"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"module": "./lib/index.mjs",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^8.2.0",
|
||||
"@types/benchmark": "^2.1.0",
|
||||
"@types/jest": "^29.2.2",
|
||||
"@types/node": "14",
|
||||
"@typescript-eslint/eslint-plugin": "^5.15.0",
|
||||
"@typescript-eslint/parser": "^5.15.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"dependency-cruiser": "^9.19.0",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-ban": "^1.6.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"husky": "^7.0.4",
|
||||
"jest": "^29.3.1",
|
||||
"lint-staged": "^12.3.7",
|
||||
"nodemon": "^2.0.15",
|
||||
"prettier": "^2.6.0",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"rollup": "^2.70.1",
|
||||
"ts-jest": "^29.0.3",
|
||||
"ts-morph": "^14.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"tslib": "^2.3.1",
|
||||
"tsx": "^3.8.0",
|
||||
"typescript": "~4.5.0"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./lib/index.js",
|
||||
"import": "./lib/index.mjs",
|
||||
"types": "./index.d.ts"
|
||||
},
|
||||
"./package.json": "./package.json",
|
||||
"./locales/*": "./lib/locales/*"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/colinhacks/zod/issues"
|
||||
},
|
||||
"description": "TypeScript-first schema declaration and validation library with static type inference",
|
||||
"files": [
|
||||
"/lib",
|
||||
"/index.d.ts"
|
||||
],
|
||||
"funding": "https://github.com/sponsors/colinhacks",
|
||||
"homepage": "https://zod.dev",
|
||||
"keywords": [
|
||||
"typescript",
|
||||
"schema",
|
||||
"validation",
|
||||
"type",
|
||||
"inference"
|
||||
],
|
||||
"license": "MIT",
|
||||
"lint-staged": {
|
||||
"src/*.ts": [
|
||||
"eslint --cache --fix",
|
||||
"prettier --ignore-unknown --write"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"prettier:check": "prettier --check src/**/*.ts deno/lib/**/*.ts --no-error-on-unmatched-pattern",
|
||||
"prettier:fix": "prettier --write src/**/*.ts deno/lib/**/*.ts --ignore-unknown --no-error-on-unmatched-pattern",
|
||||
"lint:check": "eslint --cache --ext .ts ./src",
|
||||
"lint:fix": "eslint --cache --fix --ext .ts ./src",
|
||||
"check": "yarn lint:check && yarn prettier:check",
|
||||
"fix": "yarn lint:fix && yarn prettier:fix",
|
||||
"clean": "rm -rf lib/* deno/lib/*",
|
||||
"build": "yarn run clean && npm run build:cjs && npm run build:esm && npm run build:deno",
|
||||
"build:deno": "node ./deno/build.mjs && cp ./README.md ./deno/lib",
|
||||
"build:esm": "rollup --config rollup.config.js",
|
||||
"build:cjs": "tsc -p tsconfig.cjs.json",
|
||||
"build:types": "tsc -p tsconfig.types.json",
|
||||
"build:test": "tsc -p tsconfig.test.json",
|
||||
"rollup": "rollup --config rollup.config.js",
|
||||
"test:watch": "jest --watch",
|
||||
"test": "jest --coverage",
|
||||
"test:deno": "cd deno && deno test",
|
||||
"prepublishOnly": "npm run test && npm run build && npm run build:deno",
|
||||
"play": "nodemon -e ts -w . -x tsx playground.ts",
|
||||
"depcruise": "depcruise -c .dependency-cruiser.js src",
|
||||
"benchmark": "tsx src/benchmarks/index.ts",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"support": {
|
||||
"backing": {
|
||||
"npm-funding": true
|
||||
}
|
||||
},
|
||||
"types": "./index.d.ts",
|
||||
"dependencies": {}
|
||||
}
|
||||
28
shared/package-lock.json
generated
Normal file
28
shared/package-lock.json
generated
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "photos-shared",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "photos-shared",
|
||||
"dependencies": {
|
||||
"zod": "^3.21.4"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.21.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
|
||||
"integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": {
|
||||
"version": "3.21.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
|
||||
"integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
6
shared/package.json
Normal file
6
shared/package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "photos-shared",
|
||||
"dependencies": {
|
||||
"zod": "^3.21.4"
|
||||
}
|
||||
}
|
||||
171
shared/types.ts
171
shared/types.ts
@@ -1,85 +1,124 @@
|
||||
interface IAPIErrorResponse {
|
||||
data: null;
|
||||
error: string;
|
||||
import { z } from "zod";
|
||||
|
||||
export const APIErrorResponse = z.object({
|
||||
data: z.null(),
|
||||
error: z.string(),
|
||||
});
|
||||
export type TAPIErrorResponse = z.infer<typeof APIErrorResponse>;
|
||||
|
||||
function CreateAPISuccessResponse<T extends z.ZodTypeAny>(obj: T) {
|
||||
return z.object({
|
||||
error: z.literal(false),
|
||||
data: obj,
|
||||
});
|
||||
}
|
||||
|
||||
interface IAPISuccessResponse<T> {
|
||||
error: false;
|
||||
data: T;
|
||||
function CreateAPIResponse<T extends z.ZodTypeAny>(obj: T) {
|
||||
return z.union([APIErrorResponse, CreateAPISuccessResponse(obj)]);
|
||||
}
|
||||
|
||||
export type IAPIResponse<T> = IAPIErrorResponse | IAPISuccessResponse<T>;
|
||||
export const PhotoJSON = z.object({
|
||||
id: z.number(),
|
||||
user: z.number(),
|
||||
hash: z.string(),
|
||||
size: z.string(),
|
||||
format: z.string(),
|
||||
createdAt: z.number(),
|
||||
editedAt: z.number(),
|
||||
shotAt: z.number(),
|
||||
uploaded: z.boolean(),
|
||||
});
|
||||
export type TPhotoJSON = z.infer<typeof PhotoJSON>;
|
||||
|
||||
export interface IPhotoJSON {
|
||||
id: number;
|
||||
user: number;
|
||||
hash: string;
|
||||
size: string;
|
||||
format: string;
|
||||
createdAt: number;
|
||||
editedAt: number;
|
||||
shotAt: number;
|
||||
uploaded: boolean;
|
||||
}
|
||||
export const PhotoReqJSON = PhotoJSON.extend({
|
||||
accessToken: z.string(),
|
||||
});
|
||||
export type TPhotoReqJSON = z.infer<typeof PhotoReqJSON>;
|
||||
|
||||
export interface IPhotoReqJSON extends IPhotoJSON {
|
||||
accessToken: string;
|
||||
}
|
||||
export const PhotoShowToken = z.string();
|
||||
export type TPhotoShowToken = z.infer<typeof PhotoShowToken>;
|
||||
|
||||
export type IPhotoShowToken = string;
|
||||
export type IPhotosGetShowTokenByID = IAPIResponse<IPhotoShowToken>;
|
||||
export const PhotosGetShowTokenByIDRespBody = CreateAPIResponse(PhotoShowToken);
|
||||
export type TPhotosGetShowTokenByIDRespBody = z.infer<
|
||||
typeof PhotosGetShowTokenByIDRespBody
|
||||
>;
|
||||
|
||||
export interface IPhotosNewPostBody {
|
||||
hash: string | undefined;
|
||||
size: string | undefined;
|
||||
format: string | undefined;
|
||||
}
|
||||
export const PhotosNewPostBody = z.object({
|
||||
hash: z.string(),
|
||||
size: z.string(),
|
||||
format: z.string(),
|
||||
});
|
||||
export type TPhotosNewPostBody = z.infer<typeof PhotosNewPostBody>;
|
||||
|
||||
export interface IPhotosDeleteBody {
|
||||
photos: IPhotoReqJSON[];
|
||||
}
|
||||
export const PhotosDeleteBody = z.object({
|
||||
photos: z.array(PhotoReqJSON),
|
||||
});
|
||||
export type TPhotosDeleteBody = z.infer<typeof PhotosDeleteBody>;
|
||||
|
||||
export const IPhotosListPagination = 50;
|
||||
export const PhotosListPagination = 50;
|
||||
|
||||
export type IPhotosNewRespBody = IAPIResponse<IPhotoReqJSON>;
|
||||
export type IPhotosUploadRespBody = IAPIResponse<IPhotoReqJSON>;
|
||||
export type IPhotosListRespBody = IAPIResponse<IPhotoReqJSON[]>;
|
||||
export type IPhotosByIDGetRespBody = IAPIResponse<IPhotoReqJSON>;
|
||||
export type IPhotoByIDDeleteRespBody = IAPIResponse<boolean>;
|
||||
export type IPhotosDeleteRespBody = IAPIResponse<boolean>;
|
||||
export const PhotosNewRespBody = CreateAPIResponse(PhotoReqJSON);
|
||||
export type TPhotosNewRespBody = z.infer<typeof PhotosNewRespBody>;
|
||||
|
||||
export interface IUserJSON {
|
||||
id: number;
|
||||
username: string;
|
||||
isAdmin: boolean;
|
||||
}
|
||||
export const PhotosUploadRespBody = CreateAPIResponse(PhotoReqJSON);
|
||||
export type TPhotosUploadRespBody = z.infer<typeof PhotosUploadRespBody>;
|
||||
|
||||
export interface IUserJWT extends IUserJSON {
|
||||
ext: number;
|
||||
iat: number;
|
||||
}
|
||||
export const PhotosListRespBody = CreateAPIResponse(z.array(PhotoReqJSON));
|
||||
export type TPhotosListRespBody = z.infer<typeof PhotosListRespBody>;
|
||||
|
||||
export interface IUserAuthJSON extends IUserJSON {
|
||||
jwt: string;
|
||||
}
|
||||
export const PhotosByIDGetRespBody = CreateAPIResponse(PhotoReqJSON);
|
||||
export type TPhotosByIDGetRespBody = z.infer<typeof PhotosByIDGetRespBody>;
|
||||
|
||||
export interface IUserSignupBody {
|
||||
username: string | undefined;
|
||||
password: string | undefined;
|
||||
email: string | undefined;
|
||||
}
|
||||
export const PhotoByIDDeleteRespBody = CreateAPIResponse(z.boolean());
|
||||
export type TPhotoByIDDeleteRespBody = z.infer<typeof PhotoByIDDeleteRespBody>;
|
||||
|
||||
export type IUserSignupRespBody = IAPIResponse<IUserAuthJSON>;
|
||||
export const PhotosDeleteRespBody = CreateAPIResponse(z.boolean());
|
||||
export type TPhotosDeleteRespBody = z.infer<typeof PhotosDeleteRespBody>;
|
||||
|
||||
export type IUserGetRespBody = IAPIResponse<IUserAuthJSON>;
|
||||
export type IUserLoginRespBody = IAPIResponse<IUserAuthJSON>;
|
||||
export const UserJSON = z.object({
|
||||
id: z.number(),
|
||||
username: z.string(),
|
||||
isAdmin: z.boolean(),
|
||||
});
|
||||
export type TUserJSON = z.infer<typeof UserJSON>;
|
||||
|
||||
export interface IUserEditBody {
|
||||
password: string | undefined;
|
||||
}
|
||||
export const UserJWT = UserJSON.extend({
|
||||
ext: z.number(),
|
||||
iat: z.number(),
|
||||
});
|
||||
export type TUserJWT = z.infer<typeof UserJWT>;
|
||||
|
||||
export type IUserEditRespBody = IAPIResponse<IUserAuthJSON>;
|
||||
export interface IUserLoginBody {
|
||||
username: string | undefined;
|
||||
password: string | undefined;
|
||||
}
|
||||
export const UserAuthJSON = UserJSON.extend({
|
||||
jwt: z.string(),
|
||||
});
|
||||
export type TUserAuthJSON = z.infer<typeof UserAuthJSON>;
|
||||
|
||||
export const UserSignupBody = z.object({
|
||||
username: z.string(),
|
||||
password: z.string(),
|
||||
email: z.string(),
|
||||
});
|
||||
export type TUserSignupBody = z.infer<typeof UserSignupBody>;
|
||||
|
||||
export const UserSignupRespBody = CreateAPIResponse(UserAuthJSON);
|
||||
export type TUserSignupRespBody = z.infer<typeof UserSignupRespBody>;
|
||||
|
||||
export const UserGetRespBody = CreateAPIResponse(UserAuthJSON);
|
||||
export type TUserGetRespBody = z.infer<typeof UserGetRespBody>;
|
||||
|
||||
export const UserLoginRespBody = CreateAPIResponse(UserAuthJSON);
|
||||
export type TUserLoginRespBody = z.infer<typeof UserLoginRespBody>;
|
||||
|
||||
export const UserEditBody = z.object({
|
||||
password: z.optional(z.string()),
|
||||
});
|
||||
export type TUserEditBody = z.infer<typeof UserEditBody>;
|
||||
|
||||
export const UserEditRespBody = CreateAPIResponse(UserAuthJSON);
|
||||
export type TUserEditRespBody = z.infer<typeof UserEditRespBody>;
|
||||
|
||||
export const UserLoginBody = z.object({
|
||||
username: z.string(),
|
||||
password: z.string(),
|
||||
});
|
||||
export type TUserLoginBody = z.infer<typeof UserLoginBody>;
|
||||
|
||||
Reference in New Issue
Block a user