Compare commits
3 commits
f3d43c71f7
...
3e75fb6ae2
| Author | SHA1 | Date | |
|---|---|---|---|
| 3e75fb6ae2 | |||
| 9dd7d2e04f | |||
| 3be83f3503 |
14 changed files with 519 additions and 164 deletions
|
|
@ -36,6 +36,39 @@ export type DateTimeFilter<$PrismaModel = never> = {
|
||||||
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StringFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[]
|
||||||
|
notIn?: string[]
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StringNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | null
|
||||||
|
notIn?: string[] | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SortOrderInput = {
|
||||||
|
sort: Prisma.SortOrder
|
||||||
|
nulls?: Prisma.NullsOrder
|
||||||
|
}
|
||||||
|
|
||||||
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
export type IntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[]
|
in?: number[]
|
||||||
|
|
@ -66,6 +99,40 @@ export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[]
|
||||||
|
notIn?: string[]
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | null
|
||||||
|
notIn?: string[] | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedIntFilter<$PrismaModel = never> = {
|
export type NestedIntFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[]
|
in?: number[]
|
||||||
|
|
@ -88,6 +155,34 @@ export type NestedDateTimeFilter<$PrismaModel = never> = {
|
||||||
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NestedStringFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[]
|
||||||
|
notIn?: string[]
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | null
|
||||||
|
notIn?: string[] | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[]
|
in?: number[]
|
||||||
|
|
@ -129,4 +224,49 @@ export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[]
|
||||||
|
notIn?: string[]
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | null
|
||||||
|
notIn?: string[] | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedIntNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: number[] | null
|
||||||
|
notIn?: number[] | null
|
||||||
|
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ const config: runtime.GetPrismaClientConfig = {
|
||||||
"clientVersion": "7.4.2",
|
"clientVersion": "7.4.2",
|
||||||
"engineVersion": "94a226be1cf2967af2541cca5529f0f7ba866919",
|
"engineVersion": "94a226be1cf2967af2541cca5529f0f7ba866919",
|
||||||
"activeProvider": "sqlite",
|
"activeProvider": "sqlite",
|
||||||
"inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n}\n\nmodel PhotoPost {\n id Int @id @default(autoincrement())\n createdAt DateTime @default(now())\n}\n",
|
"inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"sqlite\"\n}\n\nmodel PhotoPost {\n id Int @id @default(autoincrement())\n createdAt DateTime @default(now())\n fileName String @unique\n title String\n description String?\n}\n",
|
||||||
"runtimeDataModel": {
|
"runtimeDataModel": {
|
||||||
"models": {},
|
"models": {},
|
||||||
"enums": {},
|
"enums": {},
|
||||||
|
|
@ -32,10 +32,10 @@ const config: runtime.GetPrismaClientConfig = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.runtimeDataModel = JSON.parse("{\"models\":{\"PhotoPost\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
config.runtimeDataModel = JSON.parse("{\"models\":{\"PhotoPost\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"fileName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"title\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"description\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
||||||
config.parameterizationSchema = {
|
config.parameterizationSchema = {
|
||||||
strings: JSON.parse("[\"where\",\"PhotoPost.findUnique\",\"PhotoPost.findUniqueOrThrow\",\"orderBy\",\"cursor\",\"PhotoPost.findFirst\",\"PhotoPost.findFirstOrThrow\",\"PhotoPost.findMany\",\"data\",\"PhotoPost.createOne\",\"PhotoPost.createMany\",\"PhotoPost.createManyAndReturn\",\"PhotoPost.updateOne\",\"PhotoPost.updateMany\",\"PhotoPost.updateManyAndReturn\",\"create\",\"update\",\"PhotoPost.upsertOne\",\"PhotoPost.deleteOne\",\"PhotoPost.deleteMany\",\"having\",\"_count\",\"_avg\",\"_sum\",\"_min\",\"_max\",\"PhotoPost.groupBy\",\"PhotoPost.aggregate\",\"AND\",\"OR\",\"NOT\",\"id\",\"createdAt\",\"equals\",\"in\",\"notIn\",\"lt\",\"lte\",\"gt\",\"gte\",\"not\",\"set\",\"increment\",\"decrement\",\"multiply\",\"divide\"]"),
|
strings: JSON.parse("[\"where\",\"PhotoPost.findUnique\",\"PhotoPost.findUniqueOrThrow\",\"orderBy\",\"cursor\",\"PhotoPost.findFirst\",\"PhotoPost.findFirstOrThrow\",\"PhotoPost.findMany\",\"data\",\"PhotoPost.createOne\",\"PhotoPost.createMany\",\"PhotoPost.createManyAndReturn\",\"PhotoPost.updateOne\",\"PhotoPost.updateMany\",\"PhotoPost.updateManyAndReturn\",\"create\",\"update\",\"PhotoPost.upsertOne\",\"PhotoPost.deleteOne\",\"PhotoPost.deleteMany\",\"having\",\"_count\",\"_avg\",\"_sum\",\"_min\",\"_max\",\"PhotoPost.groupBy\",\"PhotoPost.aggregate\",\"AND\",\"OR\",\"NOT\",\"id\",\"createdAt\",\"fileName\",\"title\",\"description\",\"equals\",\"in\",\"notIn\",\"lt\",\"lte\",\"gt\",\"gte\",\"contains\",\"startsWith\",\"endsWith\",\"not\",\"set\",\"increment\",\"decrement\",\"multiply\",\"divide\"]"),
|
||||||
graph: "KwsQBRwAACIAMB0AAAQAEB4AACIAMB8CAAAAASBAACQAIQEAAAABACABAAAAAQAgBRwAACIAMB0AAAQAEB4AACIAMB8CACMAISBAACQAIQADAAAABAAgAwAABQAwBAAAAQAgAwAAAAQAIAMAAAUAMAQAAAEAIAMAAAAEACADAAAFADAEAAABACACHwIAAAABIEAAAAABAQgAAAkAIAIfAgAAAAEgQAAAAAEBCAAACwAwAQgAAAsAMAIfAgArACEgQAAqACECAAAAAQAgCAAADgAgAh8CACsAISBAACoAIQIAAAAEACAIAAAQACACAAAABAAgCAAAEAAgAwAAAAEAIA8AAAkAIBAAAA4AIAEAAAABACABAAAABAAgBRUAACUAIBYAACYAIBcAACkAIBgAACgAIBkAACcAIAUcAAAaADAdAAAXABAeAAAaADAfAgAbACEgQAAcACEDAAAABAAgAwAAFgAwFAAAFwAgAwAAAAQAIAMAAAUAMAQAAAEAIAUcAAAaADAdAAAXABAeAAAaADAfAgAbACEgQAAcACENFQAAHgAgFgAAIQAgFwAAHgAgGAAAHgAgGQAAHgAgIQIAAAABIgIAAAAEIwIAAAAEJAIAAAABJQIAAAABJgIAAAABJwIAAAABKAIAIAAhCxUAAB4AIBgAAB8AIBkAAB8AICFAAAAAASJAAAAABCNAAAAABCRAAAAAASVAAAAAASZAAAAAASdAAAAAAShAAB0AIQsVAAAeACAYAAAfACAZAAAfACAhQAAAAAEiQAAAAAQjQAAAAAQkQAAAAAElQAAAAAEmQAAAAAEnQAAAAAEoQAAdACEIIQIAAAABIgIAAAAEIwIAAAAEJAIAAAABJQIAAAABJgIAAAABJwIAAAABKAIAHgAhCCFAAAAAASJAAAAABCNAAAAABCRAAAAAASVAAAAAASZAAAAAASdAAAAAAShAAB8AIQ0VAAAeACAWAAAhACAXAAAeACAYAAAeACAZAAAeACAhAgAAAAEiAgAAAAQjAgAAAAQkAgAAAAElAgAAAAEmAgAAAAEnAgAAAAEoAgAgACEIIQgAAAABIggAAAAEIwgAAAAEJAgAAAABJQgAAAABJggAAAABJwgAAAABKAgAIQAhBRwAACIAMB0AAAQAEB4AACIAMB8CACMAISBAACQAIQghAgAAAAEiAgAAAAQjAgAAAAQkAgAAAAElAgAAAAEmAgAAAAEnAgAAAAEoAgAeACEIIUAAAAABIkAAAAAEI0AAAAAEJEAAAAABJUAAAAABJkAAAAABJ0AAAAABKEAAHwAhAAAAAAABKUAAAAABBSkCAAAAASoCAAAAASsCAAAAASwCAAAAAS0CAAAAAQAAAAAFFQAGFgAHFwAIGAAJGQAKAAAAAAAFFQAGFgAHFwAIGAAJGQAKAQIBAgMBBQYBBgcBBwgBCQoBCgwCCw0DDA8BDRECDhIEERMBEhQBExUCGhgFGxkL"
|
graph: "NwsQCBwAACkAMB0AAAQAEB4AACkAMB8CAAAAASBAACsAISEBAAAAASIBACwAISMBAC0AIQEAAAABACABAAAAAQAgCBwAACkAMB0AAAQAEB4AACkAMB8CACoAISBAACsAISEBACwAISIBACwAISMBAC0AIQEjAAAuACADAAAABAAgAwAABQAwBAAAAQAgAwAAAAQAIAMAAAUAMAQAAAEAIAMAAAAEACADAAAFADAEAAABACAFHwIAAAABIEAAAAABIQEAAAABIgEAAAABIwEAAAABAQgAAAkAIAUfAgAAAAEgQAAAAAEhAQAAAAEiAQAAAAEjAQAAAAEBCAAACwAwAQgAAAsAMAUfAgA3ACEgQAA0ACEhAQA1ACEiAQA1ACEjAQA2ACECAAAAAQAgCAAADgAgBR8CADcAISBAADQAISEBADUAISIBADUAISMBADYAIQIAAAAEACAIAAAQACACAAAABAAgCAAAEAAgAwAAAAEAIA8AAAkAIBAAAA4AIAEAAAABACABAAAABAAgBhUAAC8AIBYAADAAIBcAADMAIBgAADIAIBkAADEAICMAAC4AIAgcAAAaADAdAAAXABAeAAAaADAfAgAbACEgQAAcACEhAQAdACEiAQAdACEjAQAeACEDAAAABAAgAwAAFgAwFAAAFwAgAwAAAAQAIAMAAAUAMAQAAAEAIAgcAAAaADAdAAAXABAeAAAaADAfAgAbACEgQAAcACEhAQAdACEiAQAdACEjAQAeACENFQAAIwAgFgAAKAAgFwAAIwAgGAAAIwAgGQAAIwAgJAIAAAABJQIAAAAEJgIAAAAEJwIAAAABKAIAAAABKQIAAAABKgIAAAABLgIAJwAhCxUAACMAIBgAACYAIBkAACYAICRAAAAAASVAAAAABCZAAAAABCdAAAAAAShAAAAAASlAAAAAASpAAAAAAS5AACUAIQ4VAAAjACAYAAAkACAZAAAkACAkAQAAAAElAQAAAAQmAQAAAAQnAQAAAAEoAQAAAAEpAQAAAAEqAQAAAAErAQAAAAEsAQAAAAEtAQAAAAEuAQAiACEOFQAAIAAgGAAAIQAgGQAAIQAgJAEAAAABJQEAAAAFJgEAAAAFJwEAAAABKAEAAAABKQEAAAABKgEAAAABKwEAAAABLAEAAAABLQEAAAABLgEAHwAhDhUAACAAIBgAACEAIBkAACEAICQBAAAAASUBAAAABSYBAAAABScBAAAAASgBAAAAASkBAAAAASoBAAAAASsBAAAAASwBAAAAAS0BAAAAAS4BAB8AIQgkAgAAAAElAgAAAAUmAgAAAAUnAgAAAAEoAgAAAAEpAgAAAAEqAgAAAAEuAgAgACELJAEAAAABJQEAAAAFJgEAAAAFJwEAAAABKAEAAAABKQEAAAABKgEAAAABKwEAAAABLAEAAAABLQEAAAABLgEAIQAhDhUAACMAIBgAACQAIBkAACQAICQBAAAAASUBAAAABCYBAAAABCcBAAAAASgBAAAAASkBAAAAASoBAAAAASsBAAAAASwBAAAAAS0BAAAAAS4BACIAIQgkAgAAAAElAgAAAAQmAgAAAAQnAgAAAAEoAgAAAAEpAgAAAAEqAgAAAAEuAgAjACELJAEAAAABJQEAAAAEJgEAAAAEJwEAAAABKAEAAAABKQEAAAABKgEAAAABKwEAAAABLAEAAAABLQEAAAABLgEAJAAhCxUAACMAIBgAACYAIBkAACYAICRAAAAAASVAAAAABCZAAAAABCdAAAAAAShAAAAAASlAAAAAASpAAAAAAS5AACUAIQgkQAAAAAElQAAAAAQmQAAAAAQnQAAAAAEoQAAAAAEpQAAAAAEqQAAAAAEuQAAmACENFQAAIwAgFgAAKAAgFwAAIwAgGAAAIwAgGQAAIwAgJAIAAAABJQIAAAAEJgIAAAAEJwIAAAABKAIAAAABKQIAAAABKgIAAAABLgIAJwAhCCQIAAAAASUIAAAABCYIAAAABCcIAAAAASgIAAAAASkIAAAAASoIAAAAAS4IACgAIQgcAAApADAdAAAEABAeAAApADAfAgAqACEgQAArACEhAQAsACEiAQAsACEjAQAtACEIJAIAAAABJQIAAAAEJgIAAAAEJwIAAAABKAIAAAABKQIAAAABKgIAAAABLgIAIwAhCCRAAAAAASVAAAAABCZAAAAABCdAAAAAAShAAAAAASlAAAAAASpAAAAAAS5AACYAIQskAQAAAAElAQAAAAQmAQAAAAQnAQAAAAEoAQAAAAEpAQAAAAEqAQAAAAErAQAAAAEsAQAAAAEtAQAAAAEuAQAkACELJAEAAAABJQEAAAAFJgEAAAAFJwEAAAABKAEAAAABKQEAAAABKgEAAAABKwEAAAABLAEAAAABLQEAAAABLgEAIQAhAAAAAAAAAS9AAAAAAQEvAQAAAAEBLwEAAAABBS8CAAAAATACAAAAATECAAAAATICAAAAATMCAAAAAQAAAAAFFQAGFgAHFwAIGAAJGQAKAAAAAAAFFQAGFgAHFwAIGAAJGQAKAQIBAgMBBQYBBgcBBwgBCQoBCgwCCw0DDA8BDRECDhIEERMBEhQBExUCGhgFGxkL"
|
||||||
}
|
}
|
||||||
|
|
||||||
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
|
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
|
||||||
|
|
|
||||||
|
|
@ -516,7 +516,10 @@ export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof
|
||||||
|
|
||||||
export const PhotoPostScalarFieldEnum = {
|
export const PhotoPostScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt',
|
||||||
|
fileName: 'fileName',
|
||||||
|
title: 'title',
|
||||||
|
description: 'description'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type PhotoPostScalarFieldEnum = (typeof PhotoPostScalarFieldEnum)[keyof typeof PhotoPostScalarFieldEnum]
|
export type PhotoPostScalarFieldEnum = (typeof PhotoPostScalarFieldEnum)[keyof typeof PhotoPostScalarFieldEnum]
|
||||||
|
|
@ -530,6 +533,14 @@ export const SortOrder = {
|
||||||
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
|
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Field references
|
* Field references
|
||||||
|
|
@ -550,6 +561,13 @@ export type DateTimeFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'String'
|
||||||
|
*/
|
||||||
|
export type StringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'String'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'Float'
|
* Reference to a field of type 'Float'
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,10 @@ export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof
|
||||||
|
|
||||||
export const PhotoPostScalarFieldEnum = {
|
export const PhotoPostScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
createdAt: 'createdAt'
|
createdAt: 'createdAt',
|
||||||
|
fileName: 'fileName',
|
||||||
|
title: 'title',
|
||||||
|
description: 'description'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type PhotoPostScalarFieldEnum = (typeof PhotoPostScalarFieldEnum)[keyof typeof PhotoPostScalarFieldEnum]
|
export type PhotoPostScalarFieldEnum = (typeof PhotoPostScalarFieldEnum)[keyof typeof PhotoPostScalarFieldEnum]
|
||||||
|
|
@ -82,3 +85,11 @@ export const SortOrder = {
|
||||||
|
|
||||||
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
|
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,16 +37,25 @@ export type PhotoPostSumAggregateOutputType = {
|
||||||
export type PhotoPostMinAggregateOutputType = {
|
export type PhotoPostMinAggregateOutputType = {
|
||||||
id: number | null
|
id: number | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
|
fileName: string | null
|
||||||
|
title: string | null
|
||||||
|
description: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostMaxAggregateOutputType = {
|
export type PhotoPostMaxAggregateOutputType = {
|
||||||
id: number | null
|
id: number | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
|
fileName: string | null
|
||||||
|
title: string | null
|
||||||
|
description: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostCountAggregateOutputType = {
|
export type PhotoPostCountAggregateOutputType = {
|
||||||
id: number
|
id: number
|
||||||
createdAt: number
|
createdAt: number
|
||||||
|
fileName: number
|
||||||
|
title: number
|
||||||
|
description: number
|
||||||
_all: number
|
_all: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,16 +71,25 @@ export type PhotoPostSumAggregateInputType = {
|
||||||
export type PhotoPostMinAggregateInputType = {
|
export type PhotoPostMinAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
|
fileName?: true
|
||||||
|
title?: true
|
||||||
|
description?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostMaxAggregateInputType = {
|
export type PhotoPostMaxAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
|
fileName?: true
|
||||||
|
title?: true
|
||||||
|
description?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostCountAggregateInputType = {
|
export type PhotoPostCountAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
|
fileName?: true
|
||||||
|
title?: true
|
||||||
|
description?: true
|
||||||
_all?: true
|
_all?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,6 +182,9 @@ export type PhotoPostGroupByArgs<ExtArgs extends runtime.Types.Extensions.Intern
|
||||||
export type PhotoPostGroupByOutputType = {
|
export type PhotoPostGroupByOutputType = {
|
||||||
id: number
|
id: number
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
fileName: string
|
||||||
|
title: string
|
||||||
|
description: string | null
|
||||||
_count: PhotoPostCountAggregateOutputType | null
|
_count: PhotoPostCountAggregateOutputType | null
|
||||||
_avg: PhotoPostAvgAggregateOutputType | null
|
_avg: PhotoPostAvgAggregateOutputType | null
|
||||||
_sum: PhotoPostSumAggregateOutputType | null
|
_sum: PhotoPostSumAggregateOutputType | null
|
||||||
|
|
@ -192,24 +213,36 @@ export type PhotoPostWhereInput = {
|
||||||
NOT?: Prisma.PhotoPostWhereInput | Prisma.PhotoPostWhereInput[]
|
NOT?: Prisma.PhotoPostWhereInput | Prisma.PhotoPostWhereInput[]
|
||||||
id?: Prisma.IntFilter<"PhotoPost"> | number
|
id?: Prisma.IntFilter<"PhotoPost"> | number
|
||||||
createdAt?: Prisma.DateTimeFilter<"PhotoPost"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"PhotoPost"> | Date | string
|
||||||
|
fileName?: Prisma.StringFilter<"PhotoPost"> | string
|
||||||
|
title?: Prisma.StringFilter<"PhotoPost"> | string
|
||||||
|
description?: Prisma.StringNullableFilter<"PhotoPost"> | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostOrderByWithRelationInput = {
|
export type PhotoPostOrderByWithRelationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
fileName?: Prisma.SortOrder
|
||||||
|
title?: Prisma.SortOrder
|
||||||
|
description?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostWhereUniqueInput = Prisma.AtLeast<{
|
export type PhotoPostWhereUniqueInput = Prisma.AtLeast<{
|
||||||
id?: number
|
id?: number
|
||||||
|
fileName?: string
|
||||||
AND?: Prisma.PhotoPostWhereInput | Prisma.PhotoPostWhereInput[]
|
AND?: Prisma.PhotoPostWhereInput | Prisma.PhotoPostWhereInput[]
|
||||||
OR?: Prisma.PhotoPostWhereInput[]
|
OR?: Prisma.PhotoPostWhereInput[]
|
||||||
NOT?: Prisma.PhotoPostWhereInput | Prisma.PhotoPostWhereInput[]
|
NOT?: Prisma.PhotoPostWhereInput | Prisma.PhotoPostWhereInput[]
|
||||||
createdAt?: Prisma.DateTimeFilter<"PhotoPost"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"PhotoPost"> | Date | string
|
||||||
}, "id">
|
title?: Prisma.StringFilter<"PhotoPost"> | string
|
||||||
|
description?: Prisma.StringNullableFilter<"PhotoPost"> | string | null
|
||||||
|
}, "id" | "fileName">
|
||||||
|
|
||||||
export type PhotoPostOrderByWithAggregationInput = {
|
export type PhotoPostOrderByWithAggregationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
fileName?: Prisma.SortOrder
|
||||||
|
title?: Prisma.SortOrder
|
||||||
|
description?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
_count?: Prisma.PhotoPostCountOrderByAggregateInput
|
_count?: Prisma.PhotoPostCountOrderByAggregateInput
|
||||||
_avg?: Prisma.PhotoPostAvgOrderByAggregateInput
|
_avg?: Prisma.PhotoPostAvgOrderByAggregateInput
|
||||||
_max?: Prisma.PhotoPostMaxOrderByAggregateInput
|
_max?: Prisma.PhotoPostMaxOrderByAggregateInput
|
||||||
|
|
@ -223,43 +256,70 @@ export type PhotoPostScalarWhereWithAggregatesInput = {
|
||||||
NOT?: Prisma.PhotoPostScalarWhereWithAggregatesInput | Prisma.PhotoPostScalarWhereWithAggregatesInput[]
|
NOT?: Prisma.PhotoPostScalarWhereWithAggregatesInput | Prisma.PhotoPostScalarWhereWithAggregatesInput[]
|
||||||
id?: Prisma.IntWithAggregatesFilter<"PhotoPost"> | number
|
id?: Prisma.IntWithAggregatesFilter<"PhotoPost"> | number
|
||||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"PhotoPost"> | Date | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"PhotoPost"> | Date | string
|
||||||
|
fileName?: Prisma.StringWithAggregatesFilter<"PhotoPost"> | string
|
||||||
|
title?: Prisma.StringWithAggregatesFilter<"PhotoPost"> | string
|
||||||
|
description?: Prisma.StringNullableWithAggregatesFilter<"PhotoPost"> | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostCreateInput = {
|
export type PhotoPostCreateInput = {
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
fileName: string
|
||||||
|
title: string
|
||||||
|
description?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostUncheckedCreateInput = {
|
export type PhotoPostUncheckedCreateInput = {
|
||||||
id?: number
|
id?: number
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
fileName: string
|
||||||
|
title: string
|
||||||
|
description?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostUpdateInput = {
|
export type PhotoPostUpdateInput = {
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
fileName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
title?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostUncheckedUpdateInput = {
|
export type PhotoPostUncheckedUpdateInput = {
|
||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
id?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
fileName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
title?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostCreateManyInput = {
|
export type PhotoPostCreateManyInput = {
|
||||||
id?: number
|
id?: number
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
|
fileName: string
|
||||||
|
title: string
|
||||||
|
description?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostUpdateManyMutationInput = {
|
export type PhotoPostUpdateManyMutationInput = {
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
fileName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
title?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostUncheckedUpdateManyInput = {
|
export type PhotoPostUncheckedUpdateManyInput = {
|
||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
id?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
fileName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
title?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
description?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostCountOrderByAggregateInput = {
|
export type PhotoPostCountOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
fileName?: Prisma.SortOrder
|
||||||
|
title?: Prisma.SortOrder
|
||||||
|
description?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostAvgOrderByAggregateInput = {
|
export type PhotoPostAvgOrderByAggregateInput = {
|
||||||
|
|
@ -269,11 +329,17 @@ export type PhotoPostAvgOrderByAggregateInput = {
|
||||||
export type PhotoPostMaxOrderByAggregateInput = {
|
export type PhotoPostMaxOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
fileName?: Prisma.SortOrder
|
||||||
|
title?: Prisma.SortOrder
|
||||||
|
description?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostMinOrderByAggregateInput = {
|
export type PhotoPostMinOrderByAggregateInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
|
fileName?: Prisma.SortOrder
|
||||||
|
title?: Prisma.SortOrder
|
||||||
|
description?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostSumOrderByAggregateInput = {
|
export type PhotoPostSumOrderByAggregateInput = {
|
||||||
|
|
@ -284,6 +350,14 @@ export type DateTimeFieldUpdateOperationsInput = {
|
||||||
set?: Date | string
|
set?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StringFieldUpdateOperationsInput = {
|
||||||
|
set?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NullableStringFieldUpdateOperationsInput = {
|
||||||
|
set?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
export type IntFieldUpdateOperationsInput = {
|
export type IntFieldUpdateOperationsInput = {
|
||||||
set?: number
|
set?: number
|
||||||
increment?: number
|
increment?: number
|
||||||
|
|
@ -297,24 +371,36 @@ export type IntFieldUpdateOperationsInput = {
|
||||||
export type PhotoPostSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type PhotoPostSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
|
fileName?: boolean
|
||||||
|
title?: boolean
|
||||||
|
description?: boolean
|
||||||
}, ExtArgs["result"]["photoPost"]>
|
}, ExtArgs["result"]["photoPost"]>
|
||||||
|
|
||||||
export type PhotoPostSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type PhotoPostSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
|
fileName?: boolean
|
||||||
|
title?: boolean
|
||||||
|
description?: boolean
|
||||||
}, ExtArgs["result"]["photoPost"]>
|
}, ExtArgs["result"]["photoPost"]>
|
||||||
|
|
||||||
export type PhotoPostSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type PhotoPostSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
|
fileName?: boolean
|
||||||
|
title?: boolean
|
||||||
|
description?: boolean
|
||||||
}, ExtArgs["result"]["photoPost"]>
|
}, ExtArgs["result"]["photoPost"]>
|
||||||
|
|
||||||
export type PhotoPostSelectScalar = {
|
export type PhotoPostSelectScalar = {
|
||||||
id?: boolean
|
id?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
|
fileName?: boolean
|
||||||
|
title?: boolean
|
||||||
|
description?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PhotoPostOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "createdAt", ExtArgs["result"]["photoPost"]>
|
export type PhotoPostOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "createdAt" | "fileName" | "title" | "description", ExtArgs["result"]["photoPost"]>
|
||||||
|
|
||||||
export type $PhotoPostPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type $PhotoPostPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
name: "PhotoPost"
|
name: "PhotoPost"
|
||||||
|
|
@ -322,6 +408,9 @@ export type $PhotoPostPayload<ExtArgs extends runtime.Types.Extensions.InternalA
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
id: number
|
id: number
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
fileName: string
|
||||||
|
title: string
|
||||||
|
description: string | null
|
||||||
}, ExtArgs["result"]["photoPost"]>
|
}, ExtArgs["result"]["photoPost"]>
|
||||||
composites: {}
|
composites: {}
|
||||||
}
|
}
|
||||||
|
|
@ -747,6 +836,9 @@ export interface Prisma__PhotoPostClient<T, Null = never, ExtArgs extends runtim
|
||||||
export interface PhotoPostFieldRefs {
|
export interface PhotoPostFieldRefs {
|
||||||
readonly id: Prisma.FieldRef<"PhotoPost", 'Int'>
|
readonly id: Prisma.FieldRef<"PhotoPost", 'Int'>
|
||||||
readonly createdAt: Prisma.FieldRef<"PhotoPost", 'DateTime'>
|
readonly createdAt: Prisma.FieldRef<"PhotoPost", 'DateTime'>
|
||||||
|
readonly fileName: Prisma.FieldRef<"PhotoPost", 'String'>
|
||||||
|
readonly title: Prisma.FieldRef<"PhotoPost", 'String'>
|
||||||
|
readonly description: Prisma.FieldRef<"PhotoPost", 'String'>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -941,7 +1033,7 @@ export type PhotoPostCreateArgs<ExtArgs extends runtime.Types.Extensions.Interna
|
||||||
/**
|
/**
|
||||||
* The data needed to create a PhotoPost.
|
* The data needed to create a PhotoPost.
|
||||||
*/
|
*/
|
||||||
data?: Prisma.XOR<Prisma.PhotoPostCreateInput, Prisma.PhotoPostUncheckedCreateInput>
|
data: Prisma.XOR<Prisma.PhotoPostCreateInput, Prisma.PhotoPostUncheckedCreateInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `description` to the `PhotoPost` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `filePath` to the `PhotoPost` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `title` to the `PhotoPost` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_PhotoPost" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"filePath" TEXT NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"description" TEXT NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_PhotoPost" ("createdAt", "id") SELECT "createdAt", "id" FROM "PhotoPost";
|
||||||
|
DROP TABLE "PhotoPost";
|
||||||
|
ALTER TABLE "new_PhotoPost" RENAME TO "PhotoPost";
|
||||||
|
CREATE UNIQUE INDEX "PhotoPost_filePath_key" ON "PhotoPost"("filePath");
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `filePath` on the `PhotoPost` table. All the data in the column will be lost.
|
||||||
|
- Added the required column `fileName` to the `PhotoPost` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA defer_foreign_keys=ON;
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_PhotoPost" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"fileName" TEXT NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"description" TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- The filename field is the filepath, split by '/', and it's the last part,
|
||||||
|
-- so we need to modify the filePath column to fileName
|
||||||
|
ALTER TABLE "PhotoPost" ADD COLUMN "fileName" TEXT NOT NULL DEFAULT '';
|
||||||
|
UPDATE "PhotoPost" SET "fileName" = SUBSTR("filePath", INSTR("filePath", '/') + 1);
|
||||||
|
|
||||||
|
INSERT INTO "new_PhotoPost" ("createdAt", "description", "fileName", "id", "title") SELECT "createdAt", "description", "fileName", "id", "title" FROM "PhotoPost";
|
||||||
|
|
||||||
|
DROP TABLE "PhotoPost";
|
||||||
|
ALTER TABLE "new_PhotoPost" RENAME TO "PhotoPost";
|
||||||
|
CREATE UNIQUE INDEX "PhotoPost_fileName_key" ON "PhotoPost"("fileName");
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
|
PRAGMA defer_foreign_keys=OFF;
|
||||||
|
|
@ -10,4 +10,8 @@ datasource db {
|
||||||
model PhotoPost {
|
model PhotoPost {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
fileName String @unique
|
||||||
|
title String
|
||||||
|
description String?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,120 +5,124 @@ title: >-
|
||||||
date: 2026-02-20T17:26:06.111Z
|
date: 2026-02-20T17:26:06.111Z
|
||||||
slug: 2026-02-20-immich-object-storage-upgrade
|
slug: 2026-02-20-immich-object-storage-upgrade
|
||||||
author: Thomas Wilson-Cook
|
author: Thomas Wilson-Cook
|
||||||
|
tag:
|
||||||
|
- technical
|
||||||
|
- self-hosting
|
||||||
|
- immich
|
||||||
|
|
||||||
---
|
---
|
||||||
In my 2026 effort to write more about my personal computing setup, I want to document the switchover from Volume Mount SSD to Object Storage for my personal photo storage.
|
In my 2026 effort to write more about my personal computing setup, I want to document the switchover from Volume Mount SSD to Object Storage for my personal photo storage.
|
||||||
|
|
||||||
I have been using [Immich](https://immich.app) as a self-hosted Apple/Google photos alternative for several months now. I grew concerned that images I took with a device I owned could (quite easily) be taken away from me.
|
I have been using [Immich](https://immich.app) as a self-hosted Apple/Google photos alternative for several months now. I grew concerned that images I took with a device I owned could (quite easily) be taken away from me.
|
||||||
|
|
||||||
I have long used Immich for photo sharing with my (Android-using) spouse, largely for building a shared album of our cat but also for its great collaborative tools for friends/family ("everyone add all your photos/videos from the holiday here").
|
I have long used Immich for photo sharing with my (Android-using) spouse, largely for building a shared album of our cat but also for its great collaborative tools for friends/family ("everyone add all your photos/videos from the holiday here").
|
||||||
|
|
||||||
I run a lot of my personal software on a Virtual Private Server (a VPS), a small linux "computer" in a data centre somewhere, and [Coolify](https://coolify.io) as my platform. I'm running a couple of web services and databases from it, and it's working great. I'm paying ~£15/mo for my server, and the relevant space.
|
I run a lot of my personal software on a Virtual Private Server (a VPS), a small linux "computer" in a data centre somewhere, and [Coolify](https://coolify.io) as my platform. I'm running a couple of web services and databases from it, and it's working great. I'm paying ~£15/mo for my server, and the relevant space.
|
||||||
|
|
||||||
Over time I've watched my storage use increase. Especially as a hobbyist and [freelance](https://www.wilsoncookphotography.com) photographer, a couple of gigabytes can fill up pretty fast.
|
Over time I've watched my storage use increase. Especially as a hobbyist and [freelance](https://www.wilsoncookphotography.com) photographer, a couple of gigabytes can fill up pretty fast.
|
||||||
|
|
||||||
I was previously using an SSD volume mounted to my VPS (because it was the easiest option to expand the storage with my host). However, as I put more media onto the server, I wanted a way to increase capacity without the (steep) linear price increase.
|
I was previously using an SSD volume mounted to my VPS (because it was the easiest option to expand the storage with my host). However, as I put more media onto the server, I wanted a way to increase capacity without the (steep) linear price increase.
|
||||||
|
|
||||||
Over a morning I was able to migrate from a mounted SSD volume on my VPS to an S3 compatible object storage. The result is cheaper storage (per gb/month), and a pragmatically higher limit.
|
Over a morning I was able to migrate from a mounted SSD volume on my VPS to an S3 compatible object storage. The result is cheaper storage (per gb/month), and a pragmatically higher limit.
|
||||||
|
|
||||||
## 1: Create your Object Storage
|
## 1: Create your Object Storage
|
||||||
|
|
||||||
Simpleton that I am, I did this through click-ops. I went to my cloud provider and created a new bucket on their S3-compatible storage.
|
Simpleton that I am, I did this through click-ops. I went to my cloud provider and created a new bucket on their S3-compatible storage.
|
||||||
|
|
||||||
I created the access credentials so that my VPS could access it.
|
I created the access credentials so that my VPS could access it.
|
||||||
|
|
||||||
If you want to access your Object Storage from elsewhere (like your personal workstation, or through another service) - create another set of credentials for them.
|
If you want to access your Object Storage from elsewhere (like your personal workstation, or through another service) - create another set of credentials for them.
|
||||||
|
|
||||||
## 2: Connect through rclone
|
## 2: Connect through rclone
|
||||||
|
|
||||||
For simplicity, we want our VPS to treat the storage bucket as if it's just another directory on the file system. Luckily, there's an open source tool for that: [rclone](https://github.com/rclone/rclone), a CLI for connecting to cloud-storage providers (including S3 compatible storage).
|
For simplicity, we want our VPS to treat the storage bucket as if it's just another directory on the file system. Luckily, there's an open source tool for that: [rclone](https://github.com/rclone/rclone), a CLI for connecting to cloud-storage providers (including S3 compatible storage).
|
||||||
|
|
||||||
After installing it (my VPS runs the Ubuntu distro, so `sudo apt install rclone`). You can set it up with the command line:
|
After installing it (my VPS runs the Ubuntu distro, so `sudo apt install rclone`). You can set it up with the command line:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ rclone config
|
$ rclone config
|
||||||
```
|
```
|
||||||
|
|
||||||
Which will take you through a nice interactive installer. Or you can just update the config:
|
Which will take you through a nice interactive installer. Or you can just update the config:
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
# ~/.config/rclone/rclone.conf
|
# ~/.config/rclone/rclone.conf
|
||||||
[s3]
|
[s3]
|
||||||
type = s3
|
type = s3
|
||||||
provider = Other
|
provider = Other
|
||||||
access_key_id = $ACCESS_KEY
|
access_key_id = $ACCESS_KEY
|
||||||
secret_access_key = $SECRET_KEY
|
secret_access_key = $SECRET_KEY
|
||||||
endpoint = $REGION_NAME.some-provider.com
|
endpoint = $REGION_NAME.some-provider.com
|
||||||
acl = private
|
acl = private
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace all those `$VARIABLE_NAMES` with your own values.
|
Replace all those `$VARIABLE_NAMES` with your own values.
|
||||||
|
|
||||||
**Important**: note how your bucket name isn't there? You'll need to provide your bucket name specifically when running rclone from the terminal. In the examples below, I'll be using `my-bucket-name`. This is the (globally unique) name you gave your S3-compatible bucket when you created it in step 1.
|
**Important**: note how your bucket name isn't there? You'll need to provide your bucket name specifically when running rclone from the terminal. In the examples below, I'll be using `my-bucket-name`. This is the (globally unique) name you gave your S3-compatible bucket when you created it in step 1.
|
||||||
|
|
||||||
As a proof of life, I created a "directory" (S3 doesn't have "real" directories so to speak) in my bucket at `/immich` (more click-ops in my cloud provider's web UI), and then:
|
As a proof of life, I created a "directory" (S3 doesn't have "real" directories so to speak) in my bucket at `/immich` (more click-ops in my cloud provider's web UI), and then:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ rclone lsd s3:my-bucket-name
|
$ rclone lsd s3:my-bucket-name
|
||||||
```
|
```
|
||||||
|
|
||||||
...and expected to see the directory (of size 0) listed out.
|
...and expected to see the directory (of size 0) listed out.
|
||||||
|
|
||||||
|
|
||||||
## 3: Clone your data
|
## 3: Clone your data
|
||||||
|
|
||||||
Because I have an in-flight Immich instance, I need to make an exact copy of my immich data within my Object Store bucket:
|
Because I have an in-flight Immich instance, I need to make an exact copy of my immich data within my Object Store bucket:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ rclone sync $CURRENT_IMMICH_LOCATION s3:my-bucket-name/immich -p
|
$ rclone sync $CURRENT_IMMICH_LOCATION s3:my-bucket-name/immich -p
|
||||||
```
|
```
|
||||||
|
|
||||||
- `-p` is the Progress flag, shows what's uploading and how far through you are. I had several GB of data, so it was useful for knowing it hadn't got stuck.
|
- `-p` is the Progress flag, shows what's uploading and how far through you are. I had several GB of data, so it was useful for knowing it hadn't got stuck.
|
||||||
|
|
||||||
## 4: Mount your volume
|
## 4: Mount your volume
|
||||||
|
|
||||||
I wanted to create a clean mount to the `/immich` directory in the root of my bucket. I'm trying to put these commands in my `/usr/local/bin` directory, instead of just hitting `UP` enough times in my terminal until I find the right thing.
|
I wanted to create a clean mount to the `/immich` directory in the root of my bucket. I'm trying to put these commands in my `/usr/local/bin` directory, instead of just hitting `UP` enough times in my terminal until I find the right thing.
|
||||||
|
|
||||||
Here's a script for mounting:
|
Here's a script for mounting:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# /usr/local/bin/mount-s3-immich
|
# /usr/local/bin/mount-s3-immich
|
||||||
rclone mount hetzner:my-bucket-name/immich /mnt/s3-immich \
|
rclone mount hetzner:my-bucket-name/immich /mnt/s3-immich \
|
||||||
--allow-other \
|
--allow-other \
|
||||||
--vfs-cache-mode writes \
|
--vfs-cache-mode writes \
|
||||||
--daemon
|
--daemon
|
||||||
```
|
```
|
||||||
|
|
||||||
* `--allow-other`: Allows other users on the VPS to access the mount (like our containerised Immich service).
|
* `--allow-other`: Allows other users on the VPS to access the mount (like our containerised Immich service).
|
||||||
* `--vfs-cache-mode` Immich's write patterns may not be atomic, and this creates a buffer for those writes before they're uploaded.
|
* `--vfs-cache-mode` Immich's write patterns may not be atomic, and this creates a buffer for those writes before they're uploaded.
|
||||||
* `--daemon` runs the process as a daemon (in the background). If you're a tmux wiz, you can use that but I'm not.
|
* `--daemon` runs the process as a daemon (in the background). If you're a tmux wiz, you can use that but I'm not.
|
||||||
|
|
||||||
You'll need to make sure that `user_allow_other` is enabled in your `/etc/fuse.conf`. Because this will be a Fuse mount, the `--allow-other` flag for rclone needs to be cleared by the filesystem itself.
|
You'll need to make sure that `user_allow_other` is enabled in your `/etc/fuse.conf`. Because this will be a Fuse mount, the `--allow-other` flag for rclone needs to be cleared by the filesystem itself.
|
||||||
|
|
||||||
And then for unmounting:
|
And then for unmounting:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# /usr/local/bin/unmount-s3-immich
|
# /usr/local/bin/unmount-s3-immich
|
||||||
fusermount -u /mnt/s3-immich
|
fusermount -u /mnt/s3-immich
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5: Update your Immich docker-compose
|
## 5: Update your Immich docker-compose
|
||||||
|
|
||||||
Over to your Coolify dashboard now (I'm assuming you have an Immich service running there).
|
Over to your Coolify dashboard now (I'm assuming you have an Immich service running there).
|
||||||
|
|
||||||
You need to update the Volume on your Docker Compose to use your new mount as the source of data. That means mapping that new mounted volume to `/usr/src/app/upload`:
|
You need to update the Volume on your Docker Compose to use your new mount as the source of data. That means mapping that new mounted volume to `/usr/src/app/upload`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# Immich Service Dockerfile (e.g. in Coolify)
|
# Immich Service Dockerfile (e.g. in Coolify)
|
||||||
services:
|
services:
|
||||||
immich:
|
immich:
|
||||||
image: 'ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}'
|
image: 'ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}'
|
||||||
volumes:
|
volumes:
|
||||||
- '/mnt/s3-immich:/usr/src/app/upload'
|
- '/mnt/s3-immich:/usr/src/app/upload'
|
||||||
#...rest of docker-compose.yaml
|
#...rest of docker-compose.yaml
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 6: 🎉
|
## 6: 🎉
|
||||||
|
|
||||||
Y'all good! You're now storing photos at a _very_ cheap £/GB _and_ you have a little more ownership.
|
Y'all good! You're now storing photos at a _very_ cheap £/GB _and_ you have a little more ownership.
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,31 @@ title: 📖 Started reading "The Other Pandemic"
|
||||||
date: 2026-02-27T07:36:44.006Z
|
date: 2026-02-27T07:36:44.006Z
|
||||||
slug: 2026-02-27--started-reading-the-other-pandemic
|
slug: 2026-02-27--started-reading-the-other-pandemic
|
||||||
author: Thomas Wilson-Cook
|
author: Thomas Wilson-Cook
|
||||||
|
tags:
|
||||||
|
- book-review
|
||||||
|
- reading
|
||||||
|
|
||||||
---
|
---
|
||||||
I picked up James Ball's *The Other Pandemic: How QAnon Contaminated the World* ([bookshop.org](https://uk.bookshop.org/p/books/the-other-pandemic-how-qanon-contaminated-the-world-james-ball/7618064?ean=9781526642516&next=t)) from the library. Ball appeared on an episode of the podcast *Oh God What Now?* ([Apple Podcasts](https://podcasts.apple.com/gb/podcast/withstanding-the-farage-barrage-can-we-reform-proof-the-uk/id1245265763?i=1000750517915)) and the book sounded interesting. I've just crossed the one hundred page mark.
|
I picked up James Ball's *The Other Pandemic: How QAnon Contaminated the World* ([bookshop.org](https://uk.bookshop.org/p/books/the-other-pandemic-how-qanon-contaminated-the-world-james-ball/7618064?ean=9781526642516&next=t)) from the library. Ball appeared on an episode of the podcast *Oh God What Now?* ([Apple Podcasts](https://podcasts.apple.com/gb/podcast/withstanding-the-farage-barrage-can-we-reform-proof-the-uk/id1245265763?i=1000750517915)) and the book sounded interesting. I've just crossed the one hundred page mark.
|
||||||
|
|
||||||
Ball makes the useful (if tired) comparison between the spread of conspiratorial beliefs, like QAnon, and a virus[^1]. You could make the same comparison about other bits of being a human: music, language, or stories are all transmitted and evolve like viruses. Beautiful, sure, but sentimental. What do I *do* with this?
|
Ball makes the useful (if tired) comparison between the spread of conspiratorial beliefs, like QAnon, and a virus[^1]. You could make the same comparison about other bits of being a human: music, language, or stories are all transmitted and evolve like viruses. Beautiful, sure, but sentimental. What do I *do* with this?
|
||||||
|
|
||||||
Fortunately, Ball can think at least one step ahead of some writers and journalists I've read. They tell us *how* it's like a virus, and it's actually pretty interesting.
|
Fortunately, Ball can think at least one step ahead of some writers and journalists I've read. They tell us *how* it's like a virus, and it's actually pretty interesting.
|
||||||
|
|
||||||
Ball reminds us of survivorship bias ([wikipedia](https://en.wikipedia.org/wiki/Survivorship_bias)) and natural reservoir ([wikipedia](https://en.wikipedia.org/wiki/Natural_reservoir)) of conspiratorial beliefs. In nature, a natural reservoir might be a population of animals (cows, bats, mice) where a pathogen can live and be transmitted before being passed on to another population.
|
Ball reminds us of survivorship bias ([wikipedia](https://en.wikipedia.org/wiki/Survivorship_bias)) and natural reservoir ([wikipedia](https://en.wikipedia.org/wiki/Natural_reservoir)) of conspiratorial beliefs. In nature, a natural reservoir might be a population of animals (cows, bats, mice) where a pathogen can live and be transmitted before being passed on to another population.
|
||||||
|
|
||||||
In the context of conspiratorial beliefs, there are countless forums, group chats, and social media accounts constantly trying new ideas and framings. Naturally, the ideas that break containment from one place to another will be the most attention grabbing: the most outrageous, or maybe even the most plausible (to a certain population, if not the general public).
|
In the context of conspiratorial beliefs, there are countless forums, group chats, and social media accounts constantly trying new ideas and framings. Naturally, the ideas that break containment from one place to another will be the most attention grabbing: the most outrageous, or maybe even the most plausible (to a certain population, if not the general public).
|
||||||
|
|
||||||
Although countless people (earnest believers, state actors, and now generative artificial intelligence) are posting a new conspiracy every second of every day - the vast, vast majority of them are lost to time. Shame.
|
Although countless people (earnest believers, state actors, and now generative artificial intelligence) are posting a new conspiracy every second of every day - the vast, vast majority of them are lost to time. Shame.
|
||||||
|
|
||||||
It's no accident that QAnon started on 4chan, says Ball. A site with a set number of posts visible at any one time. To hang around, a post would have to have more engagement than its peers. The users of 4chan were already a self-selecting group of a quite libertarian-leaning, pseudonymous internet image board, initially with an anonymous founder. It's an environment that could allow a lot of different ideas to appear and then spread.
|
It's no accident that QAnon started on 4chan, says Ball. A site with a set number of posts visible at any one time. To hang around, a post would have to have more engagement than its peers. The users of 4chan were already a self-selecting group of a quite libertarian-leaning, pseudonymous internet image board, initially with an anonymous founder. It's an environment that could allow a lot of different ideas to appear and then spread.
|
||||||
|
|
||||||
The book suffers a little from the same condition as Naomi Klein's *Doppleganger*: the author details an overwhelming, negative phenomenon (bad for both individuals, communities, and societies) and then just stands back with you and is like "wow, seems bad".
|
The book suffers a little from the same condition as Naomi Klein's *Doppleganger*: the author details an overwhelming, negative phenomenon (bad for both individuals, communities, and societies) and then just stands back with you and is like "wow, seems bad".
|
||||||
|
|
||||||
But also, it's not a book about de-radicalisation or de-programming. It's a book about the spread of QAnon beliefs.
|
But also, it's not a book about de-radicalisation or de-programming. It's a book about the spread of QAnon beliefs.
|
||||||
|
|
||||||
Who knows, maybe Ball will wrap it up nicely in the next 120 pages. So far, I'm enjoying it.
|
Who knows, maybe Ball will wrap it up nicely in the next 120 pages. So far, I'm enjoying it.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[^1]: The comparison seems well considered in this case, but it reminds me a little of how the human mind is always compared to the most modern technology of the day. There was a time when the human brain was complex like a water mill, or like a wax tablet. ([the *Guardian*](https://www.theguardian.com/science/2014/jul/26/photography-supercomputers-see-ourselves-in-our-inventions-brain-neuroscience))
|
[^1]: The comparison seems well considered in this case, but it reminds me a little of how the human mind is always compared to the most modern technology of the day. There was a time when the human brain was complex like a water mill, or like a wax tablet. ([the *Guardian*](https://www.theguardian.com/science/2014/jul/26/photography-supercomputers-see-ourselves-in-our-inventions-brain-neuroscience))
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,25 @@ title: '📖 Book Review: The Evening and the Morning'
|
||||||
date: 2026-02-28T09:35:28.579Z
|
date: 2026-02-28T09:35:28.579Z
|
||||||
slug: 2026-02-28-book-review-the-evening-and-the-morning
|
slug: 2026-02-28-book-review-the-evening-and-the-morning
|
||||||
author: Thomas Wilson-Cook
|
author: Thomas Wilson-Cook
|
||||||
|
tags:
|
||||||
|
- book-review
|
||||||
|
- reading
|
||||||
|
|
||||||
---
|
---
|
||||||
This audiobook kept me company in the garden as I got through a handful of long-running chores. At twenty-four hours in length I'm happy to say that the garden is at best half-finished!
|
This audiobook kept me company in the garden as I got through a handful of long-running chores. At twenty-four hours in length I'm happy to say that the garden is at best half-finished!
|
||||||
|
|
||||||
About a decade ago, I picked up Follett's first three *Kingbridge* books[^1] for a similar reason: I was spending more time outdoors on my bike, and I used to love having audiobooks to keep me company.
|
About a decade ago, I picked up Follett's first three *Kingbridge* books[^1] for a similar reason: I was spending more time outdoors on my bike, and I used to love having audiobooks to keep me company.
|
||||||
|
|
||||||
What I have come to love most about Follett's writing is his focus on character narrative. Especially as they run over many hundreds of pages. At time I think he is prone to archetypes or simplification, but I'll leave it to the reader to consider how well any work of art has captured the internal experience of many people.
|
What I have come to love most about Follett's writing is his focus on character narrative. Especially as they run over many hundreds of pages. At time I think he is prone to archetypes or simplification, but I'll leave it to the reader to consider how well any work of art has captured the internal experience of many people.
|
||||||
|
|
||||||
Although I take the historical accuracy with a pinch of salt, especially when it comes to the spiritual or religious lives of people at the time, Follett's writing about a time where we have infamously indecisive historical evidence. I don't think this book would be much improved by higher maternal and juvenile mortality, or the comparatively much higher risk of food- and water-borne illness. I think the good of humanising people that feel so distant to us does far more good than could be done by painting flawless historical accuracy.
|
Although I take the historical accuracy with a pinch of salt, especially when it comes to the spiritual or religious lives of people at the time, Follett's writing about a time where we have infamously indecisive historical evidence. I don't think this book would be much improved by higher maternal and juvenile mortality, or the comparatively much higher risk of food- and water-borne illness. I think the good of humanising people that feel so distant to us does far more good than could be done by painting flawless historical accuracy.
|
||||||
|
|
||||||
The book is pulled along by a cast of characters, neatly split into those that you like and those that you shouldn't. He crafts villains and villainy well enough that I feel my blood boil when they get away with things, and sing when their past misdeeds finally catch up with them. And although I praise him for making the characters feel understandable, I don't think he does much to help you sympathise with the villains. To that extent, the writing can feel a little cartoonish, or more like a caricature than a studied portrait.
|
The book is pulled along by a cast of characters, neatly split into those that you like and those that you shouldn't. He crafts villains and villainy well enough that I feel my blood boil when they get away with things, and sing when their past misdeeds finally catch up with them. And although I praise him for making the characters feel understandable, I don't think he does much to help you sympathise with the villains. To that extent, the writing can feel a little cartoonish, or more like a caricature than a studied portrait.
|
||||||
|
|
||||||
The moral narrative of the books seems to be that honest hard work and natural skill will eventually improve things, and that it's possible to improve a community as well as an individual. That being kind and empathetic to people will give you a better social credit than being threatening. I think that's a good lesson we should be teaching people, but I also think it has its limits. But so does everything.
|
The moral narrative of the books seems to be that honest hard work and natural skill will eventually improve things, and that it's possible to improve a community as well as an individual. That being kind and empathetic to people will give you a better social credit than being threatening. I think that's a good lesson we should be teaching people, but I also think it has its limits. But so does everything.
|
||||||
|
|
||||||
I have no doubt that, in several years, I will dig this audiobook back out so it can accompany as I lay fences, level some earth, or re-decorate some part of my house.
|
I have no doubt that, in several years, I will dig this audiobook back out so it can accompany as I lay fences, level some earth, or re-decorate some part of my house.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[^1]: I initially thought of these as a trilogy. But reviewing their release dates as 1989, 2007, and 2017, I'm sure Follett's experience is a little more nuanced than "a trilogy".
|
[^1]: I initially thought of these as a trilogy. But reviewing their release dates as 1989, 2007, and 2017, I'm sure Follett's experience is a little more nuanced than "a trilogy".
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ slug: 2026-03-13--book-review-the-other-pandemic
|
||||||
author: Thomas Wilson-Cook
|
author: Thomas Wilson-Cook
|
||||||
tags:
|
tags:
|
||||||
- book-review
|
- book-review
|
||||||
|
- reading
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
23
src/content/blog/2026-03-15-started-reading-fundamentally.md
Normal file
23
src/content/blog/2026-03-15-started-reading-fundamentally.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
title: 📖 Started reading "Fundamentally" by Nussaibah Younis
|
||||||
|
date: 2026-03-15T17:27:13.661Z
|
||||||
|
slug: 2026-03-15-started-reading-fundamentally
|
||||||
|
author: Thomas Wilson-Cook
|
||||||
|
tags:
|
||||||
|
- journal
|
||||||
|
- reading
|
||||||
|
|
||||||
|
---
|
||||||
|
I picked up the 2025 Women's Prize shortlister "Fundamentally" by Nussaibah Younis. This was a Christmas gift from a family member, after I remember putting it down on my list because it ended up on a few end of year lists.
|
||||||
|
|
||||||
|
I remember being intrigued by the author. That she'd written this piece of fiction based heavily on her own experience in the humanitarian sector, working on deradicalisation at the UN.
|
||||||
|
|
||||||
|
At their worst, books like this can turn into strange power fantasies, or self-mythologies that strip away a lot of the nuance. It's how we can end up with the Manic Pixie Dream Girl ([wikipedia](https://en.wikipedia.org/wiki/Manic_Pixie_Dream_Girl)) or Mary Sue ([wikipedia](https://en.wikipedia.org/wiki/Mary_Sue))[^1].
|
||||||
|
|
||||||
|
I like Younis' interview in the Womens Prize ([link](https://womensprize.com/in-conversation-with-nussaibah-younis/)) because it seems like she might have gone the reverse direction, where the novel came after (or was part of) deconstructing that self-aggrandising narrative:
|
||||||
|
|
||||||
|
> I studied Arabic, read a lot of books, got a PhD. Then I went to Iraq. The trouble is, having an Iraqi father did not make me Iraqi, and having a PhD did not make me competent. I was just another idiot foreigner; well-meaning but totally out of my depth. I spent ten years as an NGO worker, trying to ‘save Iraq.’ I lived an insane life, pursuing hair-brained schemes to ‘build peace.’ My naivety shone like a spotlight as I played out my brown saviour fantasies, all the while being mocked and extorted by locals. Before long, I was embroiled in the farcical humanitarian sector. I’d started out by asking: how can I help? But soon I was asking: how can I maximise my budgets? How can I curry favour with corrupt Iraqi ministers? And how can I screw over my rivals? As a workplace, humanitarianism is as aggressive as investment banking, except that lives hang in the balance
|
||||||
|
|
||||||
|
Like I said, I'm only really about an hour in but as I hurtle towards page 100 (the point at which I give myself complete freedom to did-not-finish a book), I can easily foresee making my way through this one.
|
||||||
|
|
||||||
|
[^1]: While one can gender the Mary Sue to a "Gary Stu" or "Marty Stu" , it's interesting that the Manic Pixie Dream Girl archetype relies a bit more on gender dynamics. I certainly don't think we see any Manic Pixie Dream Boys, and personally I wouldn't care to.
|
||||||
|
|
@ -33,8 +33,10 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
<main class="thomaswilson-container blog">
|
<main class="thomaswilson-container blog">
|
||||||
<header class="blog__header">
|
<header class="blog__header">
|
||||||
|
<a href="/blog" class="weblog-link">Return to blog</a>
|
||||||
<h1 class="title post-title">{post.title}</h1>
|
<h1 class="title post-title">{post.title}</h1>
|
||||||
<p class="post-author">
|
<p class="post-author">
|
||||||
{#if post.author}
|
{#if post.author}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue