Fix everything needed for correct build

This commit is contained in:
rubikscraft 2022-08-28 16:31:20 +02:00
parent d0dea59a3a
commit 62a4c91ba7
No known key found for this signature in database
GPG Key ID: 1463EBE9200A5CD4
53 changed files with 490 additions and 152 deletions

View File

@ -1,8 +1,22 @@
node_modules
**/node_modules
dist
**/dist
Dockerfile
docker-compose.yml
support
backend/dist
frontend/dist
shared/dist
**/.angular
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
.pnp.*
yarn-error.log
temp
.vscode
.git

View File

@ -77,9 +77,9 @@ services:
PICSUR_DB_HOST: picsur_postgres
# PICSUR_DB_PORT: 5432
# PICSUR_DB_USER: picsur
# PICSUR_DB_USERNAME: picsur
# PICSUR_DB_PASSWORD: picsur
# PICSUR_DB_NAME: picsur
# PICSUR_DB_DATABASE: picsur
# PICSUR_ADMIN_PASSWORD: picsur

View File

@ -6,7 +6,6 @@
"spec": false
},
"compilerOptions": {
"tsConfigPath": "tsconfig.build.json"
},
"exec": "pog"
"tsConfigPath": "tsconfig.json"
}
}

View File

@ -14,6 +14,8 @@
"start:dev": "yarn clean && nest start --watch --exec \"node --es-module-specifier-resolution=node\"",
"start:debug": "nest start --debug --watch --exec \"node --es-module-specifier-resolution=node\"",
"start:prod": "node --es-module-specifier-resolution=node dist/main",
"typeorm": "typeorm-ts-node-esm",
"migrate": "yarn typeorm migration:generate -d ./src/datasource.ts",
"format": "prettier --write \"src/**/*.ts\"",
"clean": "rimraf dist",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",

View File

@ -1,11 +1,10 @@
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { ServeStaticModule } from '@nestjs/serve-static';
import { TypeOrmModule } from '@nestjs/typeorm';
import cors from 'cors';
import { IncomingMessage, ServerResponse } from 'http';
import { EarlyConfigModule } from './config/early/early-config.module';
import { ServeStaticConfigService } from './config/early/serve-static.config.service';
import { TypeOrmConfigService } from './config/early/type-orm.config.service';
import { DatabaseModule } from './database/database.module';
import { PicsurLoggerModule } from './logger/logger.module';
import { AuthManagerModule } from './managers/auth/auth.module';
import { DemoManagerModule } from './managers/demo/demo.module';
@ -37,15 +36,12 @@ const imageCorsOverride = (
@Module({
imports: [
TypeOrmModule.forRootAsync({
useExisting: TypeOrmConfigService,
imports: [EarlyConfigModule],
}),
PicsurLoggerModule,
ServeStaticModule.forRootAsync({
useExisting: ServeStaticConfigService,
imports: [EarlyConfigModule],
}),
PicsurLoggerModule,
DatabaseModule,
AuthManagerModule,
DemoManagerModule,
PicsurRoutesModule,

View File

@ -1,8 +1,8 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EImageDerivativeBackend } from '../../models/entities/image-derivative.entity';
import { EImageFileBackend } from '../../models/entities/image-file.entity';
import { EImageBackend } from '../../models/entities/image.entity';
import { EImageDerivativeBackend } from '../../database/entities/image-derivative.entity';
import { EImageFileBackend } from '../../database/entities/image-file.entity';
import { EImageBackend } from '../../database/entities/image.entity';
import { ImageDBService } from './image-db.service';
import { ImageFileDBService } from './image-file-db.service';

View File

@ -3,9 +3,9 @@ import { InjectRepository } from '@nestjs/typeorm';
import { AsyncFailable, Fail, FT } from 'picsur-shared/dist/types';
import { FindResult } from 'picsur-shared/dist/types/find-result';
import { In, Repository } from 'typeorm';
import { EImageDerivativeBackend } from '../../models/entities/image-derivative.entity';
import { EImageFileBackend } from '../../models/entities/image-file.entity';
import { EImageBackend } from '../../models/entities/image.entity';
import { EImageDerivativeBackend } from '../../database/entities/image-derivative.entity';
import { EImageFileBackend } from '../../database/entities/image-file.entity';
import { EImageBackend } from '../../database/entities/image.entity';
@Injectable()
export class ImageDBService {

View File

@ -3,8 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm';
import { ImageEntryVariant } from 'picsur-shared/dist/dto/image-entry-variant.enum';
import { AsyncFailable, Fail, FT } from 'picsur-shared/dist/types';
import { LessThan, Repository } from 'typeorm';
import { EImageDerivativeBackend } from '../../models/entities/image-derivative.entity';
import { EImageFileBackend } from '../../models/entities/image-file.entity';
import { EImageDerivativeBackend } from '../../database/entities/image-derivative.entity';
import { EImageFileBackend } from '../../database/entities/image-file.entity';
const A_DAY_IN_SECONDS = 24 * 60 * 60;

View File

@ -1,8 +1,8 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EarlyConfigModule } from '../../config/early/early-config.module';
import { ESysPreferenceBackend } from '../../models/entities/sys-preference.entity';
import { EUsrPreferenceBackend } from '../../models/entities/usr-preference.entity';
import { ESysPreferenceBackend } from '../../database/entities/sys-preference.entity';
import { EUsrPreferenceBackend } from '../../database/entities/usr-preference.entity';
import { PreferenceCommonService } from './preference-common.service';
import { PreferenceDefaultsService } from './preference-defaults.service';
import { SysPreferenceService } from './sys-preference-db.service';

View File

@ -8,14 +8,14 @@ import {
import { SysPreference } from 'picsur-shared/dist/dto/sys-preferences.enum';
import { AsyncFailable, Fail, FT, HasFailed } from 'picsur-shared/dist/types';
import { Repository } from 'typeorm';
import {
ESysPreferenceBackend,
ESysPreferenceSchema
} from '../../database/entities/sys-preference.entity';
import {
SysPreferenceList,
SysPreferenceValueTypes
} from '../../models/constants/syspreferences.const';
import {
ESysPreferenceBackend,
ESysPreferenceSchema
} from '../../models/entities/sys-preference.entity';
import { MutexFallBack } from '../../models/util/mutex-fallback';
import { PreferenceCommonService } from './preference-common.service';
import { PreferenceDefaultsService } from './preference-defaults.service';

View File

@ -8,14 +8,14 @@ import {
import { UsrPreference } from 'picsur-shared/dist/dto/usr-preferences.enum';
import { AsyncFailable, Fail, FT, HasFailed } from 'picsur-shared/dist/types';
import { Repository } from 'typeorm';
import {
EUsrPreferenceBackend,
EUsrPreferenceSchema
} from '../../database/entities/usr-preference.entity';
import {
UsrPreferenceList,
UsrPreferenceValueTypes
} from '../../models/constants/usrpreferences.const';
import {
EUsrPreferenceBackend,
EUsrPreferenceSchema
} from '../../models/entities/usr-preference.entity';
import { MutexFallBack } from '../../models/util/mutex-fallback';
import { PreferenceCommonService } from './preference-common.service';
import { PreferenceDefaultsService } from './preference-defaults.service';

View File

@ -3,12 +3,12 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { HasFailed } from 'picsur-shared/dist/types';
import { EarlyConfigModule } from '../../config/early/early-config.module';
import { HostConfigService } from '../../config/early/host.config.service';
import { ERoleBackend } from '../../database/entities/role.entity';
import {
ImmutableRolesList,
SystemRoleDefaults,
SystemRolesList
} from '../../models/constants/roles.const';
import { ERoleBackend } from '../../models/entities/role.entity';
import { RolesService } from './role-db.service';
@Module({

View File

@ -10,12 +10,12 @@ import {
} from 'picsur-shared/dist/types';
import { makeUnique } from 'picsur-shared/dist/util/unique';
import { In, Repository } from 'typeorm';
import { ERoleBackend } from '../../database/entities/role.entity';
import { Permissions } from '../../models/constants/permissions.const';
import {
ImmutableRolesList,
UndeletableRolesList
} from '../../models/constants/roles.const';
import { ERoleBackend } from '../../models/entities/role.entity';
@Injectable()
export class RolesService {
@ -24,7 +24,8 @@ export class RolesService {
constructor(
@InjectRepository(ERoleBackend)
private readonly rolesRepository: Repository<ERoleBackend>,
) {}
) {
}
public async create(
name: string,

View File

@ -4,7 +4,7 @@ import { HasFailed } from 'picsur-shared/dist/types';
import { generateRandomString } from 'picsur-shared/dist/util/random';
import { AuthConfigService } from '../../config/early/auth.config.service';
import { EarlyConfigModule } from '../../config/early/early-config.module';
import { EUserBackend } from '../../models/entities/user.entity';
import { EUserBackend } from '../../database/entities/user.entity';
import { PreferenceModule } from '../preference-db/preference-db.module';
import { RolesModule } from '../role-db/role-db.module';
import { UsersService } from './user-db.service';

View File

@ -12,6 +12,7 @@ import {
import { FindResult } from 'picsur-shared/dist/types/find-result';
import { makeUnique } from 'picsur-shared/dist/util/unique';
import { Repository } from 'typeorm';
import { EUserBackend } from '../../database/entities/user.entity';
import { Permissions } from '../../models/constants/permissions.const';
import {
DefaultRolesList,
@ -22,7 +23,6 @@ import {
LockedLoginUsersList,
UndeletableUsersList
} from '../../models/constants/special-users.const';
import { EUserBackend } from '../../models/entities/user.entity';
import { GetCols } from '../../models/util/collection';
import { SysPreferenceService } from '../preference-db/sys-preference-db.service';
import { RolesService } from '../role-db/role-db.service';

View File

@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ParseString } from 'picsur-shared/dist/util/parse-simple';
import { EnvPrefix } from '../config.static';
@Injectable()
@ -7,8 +8,8 @@ export class AuthConfigService {
constructor(private readonly configService: ConfigService) {}
public getDefaultAdminPassword(): string {
return this.configService.get<string>(
`${EnvPrefix}ADMIN_PASSWORD`,
return ParseString(
this.configService.get(`${EnvPrefix}ADMIN_PASSWORD`),
'admin',
);
}

View File

@ -1,5 +1,6 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ParseString } from 'picsur-shared/dist/util/parse-simple';
import { EnvPrefix } from '../config.static';
@Injectable()
@ -7,10 +8,14 @@ export class EarlyJwtConfigService {
constructor(private readonly configService: ConfigService) {}
public getJwtSecret(): string | undefined {
return this.configService.get<string>(`${EnvPrefix}JWT_SECRET`);
return (
ParseString(this.configService.get(`${EnvPrefix}JWT_SECRET`)) ?? undefined
);
}
public getJwtExpiresIn(): string | undefined {
return this.configService.get<string>(`${EnvPrefix}JWT_EXPIRY`);
return (
ParseString(this.configService.get(`${EnvPrefix}JWT_EXPIRY`)) ?? undefined
);
}
}

View File

@ -1,5 +1,10 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
ParseBool,
ParseInt,
ParseString
} from 'picsur-shared/dist/util/parse-simple';
import { EnvPrefix } from '../config.static';
@Injectable()
@ -7,6 +12,7 @@ export class HostConfigService {
private readonly logger = new Logger('HostConfigService');
constructor(private readonly configService: ConfigService) {
this.logger.log('Production: ' + this.isProduction());
this.logger.log('Host: ' + this.getHost());
this.logger.log('Port: ' + this.getPort());
this.logger.log('Demo: ' + this.isDemo());
@ -14,41 +20,29 @@ export class HostConfigService {
}
public getHost(): string {
const host = this.configService.get<string>(`${EnvPrefix}HOST`, '0.0.0.0');
return host;
return ParseString(this.configService.get(`${EnvPrefix}HOST`), '0.0.0.0');
}
public getPort(): number {
const port = this.configService.get<number>(`${EnvPrefix}PORT`, 8080);
return port;
return ParseInt(this.configService.get(`${EnvPrefix}PORT`), 8080);
}
public isDemo() {
const enabled = this.configService.get<boolean>(`${EnvPrefix}DEMO`, false);
return enabled;
return ParseBool(this.configService.get(`${EnvPrefix}DEMO`), false);
}
public getDemoInterval() {
const interval = this.configService.get<number>(
`${EnvPrefix}DEMO_INTERVAL`,
return ParseInt(
this.configService.get(`${EnvPrefix}DEMO_INTERVAL`),
1000 * 60 * 5,
);
return interval;
}
public isProduction() {
const enabled = this.configService.get<boolean>(
`${EnvPrefix}PRODUCTION`,
false,
);
return enabled;
return ParseBool(this.configService.get(`${EnvPrefix}PRODUCTION`), false);
}
public getVersion() {
const version = this.configService.get<string>(
`npm_package_version`,
'0.0.0',
);
return version;
return ParseString(this.configService.get(`npm_package_version`), '0.0.0');
}
}

View File

@ -1,5 +1,6 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ParseInt } from 'picsur-shared/dist/util/parse-simple';
import { EnvPrefix } from '../config.static';
@Injectable()
@ -11,8 +12,8 @@ export class MultipartConfigService {
}
public getMaxFileSize(): number {
return this.configService.get<number>(
`${EnvPrefix}MAX_FILE_SIZE`,
return ParseInt(
this.configService.get(`${EnvPrefix}MAX_FILE_SIZE`),
128000000,
);
}

View File

@ -5,6 +5,7 @@ import {
ServeStaticModuleOptionsFactory
} from '@nestjs/serve-static';
import { join } from 'path';
import { ParseString } from 'picsur-shared/dist/util/parse-simple';
import { EnvPrefix, PackageRoot } from '../config.static';
@Injectable()
@ -20,11 +21,12 @@ export class ServeStaticConfigService
}
public getStaticDirectory(): string {
const directory = this.configService.get<string>(
`${EnvPrefix}STATIC_FRONTEND_ROOT`,
this.defaultLocation,
return ParseString(
this.configService.get(
`${EnvPrefix}STATIC_FRONTEND_ROOT`,
this.defaultLocation,
),
);
return directory;
}
public createLoggerOptions(): ServeStaticModuleOptions[] {

View File

@ -1,7 +1,9 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from '@nestjs/typeorm';
import { EntityList } from '../../models/entities';
import { TypeOrmOptionsFactory } from '@nestjs/typeorm';
import { ParseInt, ParseString } from 'picsur-shared/dist/util/parse-simple';
import { EntityList } from '../../database/entities';
import { MigrationList } from '../../database/migrations';
import { DefaultName, EnvPrefix } from '../config.static';
import { HostConfigService } from './host.config.service';
@ -25,31 +27,45 @@ export class TypeOrmConfigService implements TypeOrmOptionsFactory {
public getTypeOrmServerOptions() {
const varOptions = {
host: this.configService.get<string>(`${EnvPrefix}DB_HOST`, 'localhost'),
port: this.configService.get<number>(`${EnvPrefix}DB_PORT`, 5432),
username: this.configService.get<string>(
`${EnvPrefix}DB_USERNAME`,
host: ParseString(
this.configService.get(`${EnvPrefix}DB_HOST`),
'localhost',
),
port: ParseInt(
this.configService.get<number>(`${EnvPrefix}DB_PORT`),
5432,
),
username: ParseString(
this.configService.get<string>(`${EnvPrefix}DB_USERNAME`),
DefaultName,
),
password: this.configService.get<string>(
`${EnvPrefix}DB_PASSWORD`,
password: ParseString(
this.configService.get<string>(`${EnvPrefix}DB_PASSWORD`),
DefaultName,
),
database: this.configService.get<string>(
`${EnvPrefix}DB_DATABASE`,
database: ParseString(
this.configService.get<string>(`${EnvPrefix}DB_DATABASE`),
DefaultName,
),
};
return varOptions;
}
public createTypeOrmOptions(connectionName?: string): TypeOrmModuleOptions {
public createTypeOrmOptions(connectionName?: string) {
const varOptions = this.getTypeOrmServerOptions();
return {
type: 'postgres',
synchronize: !this.hostService.isProduction(),
type: 'postgres' as 'postgres',
synchronize: false,
migrationsRun: true,
entities: EntityList,
migrations: MigrationList,
cli: {
migrationsDir: 'src/database/migrations',
entitiesDir: 'src/database/entities',
},
...varOptions,
};

View File

@ -1,5 +1,6 @@
import { FactoryProvider, Injectable, Logger } from '@nestjs/common';
import { JwtModuleOptions, JwtOptionsFactory } from '@nestjs/jwt';
import ms from 'ms';
import { ThrowIfFailed } from 'picsur-shared/dist/types';
import { SysPreferenceService } from '../../collections/preference-db/sys-preference-db.service';
@ -26,12 +27,17 @@ export class JwtConfigService implements JwtOptionsFactory {
return secret;
}
public async getJwtExpiresIn(): Promise<string> {
public async getJwtExpiresIn(): Promise<number> {
const expiresIn = ThrowIfFailed(
await this.prefService.getStringPreference('jwt_expires_in'),
);
return expiresIn;
let milliseconds = ms(expiresIn);
if (milliseconds === undefined) {
milliseconds = 1000 * 60 * 60 * 24; // 1 day
}
return milliseconds / 1000;
}
public async createJwtOptions(): Promise<JwtModuleOptions> {

View File

@ -0,0 +1,14 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EarlyConfigModule } from '../config/early/early-config.module';
import { TypeOrmConfigService } from '../config/early/type-orm.config.service';
@Module({
imports: [
TypeOrmModule.forRootAsync({
useExisting: TypeOrmConfigService,
imports: [EarlyConfigModule],
}),
],
})
export class DatabaseModule {}

View File

@ -1,6 +1,6 @@
import { ERole } from 'picsur-shared/dist/entities/role.entity';
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
import type { Permissions } from '../constants/permissions.const';
import type { Permissions } from '../../models/constants/permissions.const';
@Entity()
export class ERoleBackend implements ERole {

View File

@ -0,0 +1,44 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class V030A1661692206479 implements MigrationInterface {
name = 'V030A1661692206479'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "e_image_derivative_backend" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "image_id" character varying NOT NULL, "key" character varying NOT NULL, "filetype" character varying NOT NULL, "last_read" TIMESTAMP NOT NULL, "data" bytea NOT NULL, CONSTRAINT "UQ_fa03f5333afd74c5cc5ff780d75" UNIQUE ("image_id", "key"), CONSTRAINT "PK_ff1ecff935b8d7bdcea89087810" PRIMARY KEY ("_id"))`);
await queryRunner.query(`CREATE INDEX "IDX_37055605f39b3f8847232d604f" ON "e_image_derivative_backend" ("image_id") `);
await queryRunner.query(`CREATE INDEX "IDX_7dc534a666f442383341896062" ON "e_image_derivative_backend" ("key") `);
await queryRunner.query(`CREATE TABLE "e_image_file_backend" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "image_id" character varying NOT NULL, "variant" character varying NOT NULL, "filetype" character varying NOT NULL, "data" bytea NOT NULL, CONSTRAINT "UQ_872384f20feaf7bfd27e28b8d4a" UNIQUE ("image_id", "variant"), CONSTRAINT "PK_95953be58a506e5de46feec6186" PRIMARY KEY ("_id"))`);
await queryRunner.query(`CREATE INDEX "IDX_8055f37d3b9f52f421b94ee84d" ON "e_image_file_backend" ("image_id") `);
await queryRunner.query(`CREATE INDEX "IDX_d0500b00b0b4109b623f897c2d" ON "e_image_file_backend" ("variant") `);
await queryRunner.query(`CREATE TABLE "e_image_backend" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "user_id" character varying NOT NULL, "created" TIMESTAMP NOT NULL, CONSTRAINT "PK_5f7993001a7c82564ec5300540d" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE TABLE "e_role_backend" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "permissions" text array NOT NULL, CONSTRAINT "UQ_cbedb9f42a98a82d91422e7fedf" UNIQUE ("name"), CONSTRAINT "PK_af7ba6a46bf69a7b10c425f0367" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_cbedb9f42a98a82d91422e7fed" ON "e_role_backend" ("name") `);
await queryRunner.query(`CREATE TABLE "e_sys_preference_backend" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "key" character varying NOT NULL, "value" character varying NOT NULL, CONSTRAINT "UQ_b04e47c4814fb6e315c5879fa75" UNIQUE ("key"), CONSTRAINT "PK_b79f051e19b46e74cf255e9ba3b" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b04e47c4814fb6e315c5879fa7" ON "e_sys_preference_backend" ("key") `);
await queryRunner.query(`CREATE TABLE "e_user_backend" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "username" character varying NOT NULL, "roles" text array NOT NULL, "hashed_password" character varying NOT NULL, CONSTRAINT "UQ_ae538430fd08b28f4ab297eff09" UNIQUE ("username"), CONSTRAINT "PK_0b9d256d52e55a48d32e8b64d96" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_ae538430fd08b28f4ab297eff0" ON "e_user_backend" ("username") `);
await queryRunner.query(`CREATE TABLE "e_usr_preference_backend" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "key" character varying NOT NULL, "value" character varying NOT NULL, "user_id" character varying NOT NULL, CONSTRAINT "UQ_576678406a479d569123a33e132" UNIQUE ("key", "user_id"), CONSTRAINT "PK_8f8251016cd9283e7eb04c5498b" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_673fe530e2484ff7e31ac81099" ON "e_usr_preference_backend" ("key") `);
await queryRunner.query(`CREATE INDEX "IDX_f1a427e855045fa793c275861a" ON "e_usr_preference_backend" ("user_id") `);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX "public"."IDX_f1a427e855045fa793c275861a"`);
await queryRunner.query(`DROP INDEX "public"."IDX_673fe530e2484ff7e31ac81099"`);
await queryRunner.query(`DROP TABLE "e_usr_preference_backend"`);
await queryRunner.query(`DROP INDEX "public"."IDX_ae538430fd08b28f4ab297eff0"`);
await queryRunner.query(`DROP TABLE "e_user_backend"`);
await queryRunner.query(`DROP INDEX "public"."IDX_b04e47c4814fb6e315c5879fa7"`);
await queryRunner.query(`DROP TABLE "e_sys_preference_backend"`);
await queryRunner.query(`DROP INDEX "public"."IDX_cbedb9f42a98a82d91422e7fed"`);
await queryRunner.query(`DROP TABLE "e_role_backend"`);
await queryRunner.query(`DROP TABLE "e_image_backend"`);
await queryRunner.query(`DROP INDEX "public"."IDX_d0500b00b0b4109b623f897c2d"`);
await queryRunner.query(`DROP INDEX "public"."IDX_8055f37d3b9f52f421b94ee84d"`);
await queryRunner.query(`DROP TABLE "e_image_file_backend"`);
await queryRunner.query(`DROP INDEX "public"."IDX_7dc534a666f442383341896062"`);
await queryRunner.query(`DROP INDEX "public"."IDX_37055605f39b3f8847232d604f"`);
await queryRunner.query(`DROP TABLE "e_image_derivative_backend"`);
}
}

View File

@ -0,0 +1,3 @@
import { V030A1661692206479 } from './1661692206479-V_0_3_0_a';
export const MigrationList: Function[] = [V030A1661692206479];

35
backend/src/datasource.ts Normal file
View File

@ -0,0 +1,35 @@
import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter, NestFastifyApplication
} from '@nestjs/platform-fastify';
import { DataSource, InstanceChecker } from 'typeorm';
import { TypeOrmConfigService } from './config/early/type-orm.config.service';
import { DatabaseModule } from './database/database.module';
// TODO, upgrade to a version beyond typeorm 3.8, cause 3.8 is bugged
// So here we monkeypatch 3.7
function patchAsyncDataSourceSetup() {
const oldIsDataSource = InstanceChecker.isDataSource;
InstanceChecker.isDataSource = function (obj: unknown): obj is DataSource {
if (obj instanceof Promise) {
return true;
}
return oldIsDataSource(obj);
};
}
patchAsyncDataSourceSetup();
async function createDataSource() {
// Create nest app
const app = await NestFactory.create<NestFastifyApplication>(
DatabaseModule,
new FastifyAdapter(),
);
const configFactory = app.get(TypeOrmConfigService);
const config = await configFactory.createTypeOrmOptions();
return new DataSource(config);
}
export default createDataSource().catch(console.error);

View File

@ -5,7 +5,6 @@ import { HostConfigService } from '../config/early/host.config.service';
export class PicsurLoggerService extends ConsoleLogger {
constructor(hostService: HostConfigService) {
super();
if (hostService.isProduction()) {
super.setLogLevels(['error', 'warn', 'log']);
}

View File

@ -1,5 +1,5 @@
import fastifyHelmet from '@fastify/helmet';
import * as multipart from '@fastify/multipart';
import multipart from '@fastify/multipart';
import { NestFactory, Reflector } from '@nestjs/core';
import {
FastifyAdapter,
@ -31,6 +31,11 @@ async function bootstrap() {
},
);
// Configure logger
app.useLogger(app.get(PicsurLoggerService));
app.flushLogs();
app.useGlobalFilters(new MainExceptionFilter());
app.useGlobalInterceptors(new SuccessInterceptor(app.get(Reflector)));
app.useGlobalPipes(new ZodValidationPipe());
@ -38,9 +43,6 @@ async function bootstrap() {
new MainAuthGuard(app.get(Reflector), app.get(UsersService)),
);
// Configure logger
app.useLogger(app.get(PicsurLoggerService));
// Start app
const hostConfigService = app.get(HostConfigService);
await app.listen(hostConfigService.getPort(), hostConfigService.getHost());

View File

@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { HasFailed } from 'picsur-shared/dist/types';
import { UsersService } from '../../collections/user-db/user-db.service';
import { EUserBackend } from '../../models/entities/user.entity';
import { EUserBackend } from '../../database/entities/user.entity';
@Injectable()
export class GuestService {

View File

@ -16,9 +16,9 @@ import { ImageDBService } from '../../collections/image-db/image-db.service';
import { ImageFileDBService } from '../../collections/image-db/image-file-db.service';
import { SysPreferenceService } from '../../collections/preference-db/sys-preference-db.service';
import { UsrPreferenceService } from '../../collections/preference-db/usr-preference-db.service';
import { EImageDerivativeBackend } from '../../models/entities/image-derivative.entity';
import { EImageFileBackend } from '../../models/entities/image-file.entity';
import { EImageBackend } from '../../models/entities/image.entity';
import { EImageDerivativeBackend } from '../../database/entities/image-derivative.entity';
import { EImageFileBackend } from '../../database/entities/image-file.entity';
import { EImageBackend } from '../../database/entities/image.entity';
import { MutexFallBack } from '../../models/util/mutex-fallback';
import { ImageConverterService } from './image-converter.service';
import { ImageProcessorService } from './image-processor.service';

View File

@ -1,5 +1,5 @@
import { EUser } from 'picsur-shared/dist/entities/user.entity';
import { EUserBackend } from '../entities/user.entity';
import { EUserBackend } from '../../database/entities/user.entity';
export function EUserBackend2EUser(eUser: EUserBackend): EUser {
if (eUser.hashed_password === undefined) return eUser as EUser;

View File

@ -1,5 +0,0 @@
{
"extends": "./tsconfig.json",
"include": ["src/**/*.ts", "src/**/*.d.ts"],
"exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
}

View File

@ -1,5 +1,7 @@
{
"extends": "../tsconfig.base.json",
"include": ["src/**/*.ts", "src/**/*.d.ts"],
"exclude": ["node_modules", "dist", "test", "**/*spec.ts"],
"compilerOptions": {
"target": "es2020",
"module": "es2020",
@ -8,5 +10,8 @@
"declaration": true,
"sourceMap": true,
"emitDecoratorMetadata": true
},
"ts-node": {
"experimentalSpecifierResolution": "node"
}
}

View File

@ -21,7 +21,7 @@
"sourceRoot": "src",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
@ -37,15 +37,18 @@
"moment"
],
"optimization": true,
"webWorkerTsConfig": "tsconfig.worker.json"
"webWorkerTsConfig": "tsconfig.worker.json",
"customWebpackConfig": {
"path": "./custom-webpack.config.js"
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
"maximumWarning": "1500kb",
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",

View File

@ -0,0 +1,7 @@
import webpack from 'webpack';
export default {
plugins: [
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/),
],
};

View File

@ -5,6 +5,7 @@
"license": "GPL-3.0",
"repository": "https://github.com/rubikscraft/Picsur",
"author": "Rubikscraft <contact@rubikscraft.nl>",
"type": "module",
"scripts": {
"ng": "ng",
"start": "ng serve --host 0.0.0.0",
@ -41,6 +42,7 @@
"zone.js": "~0.11.8"
},
"devDependencies": {
"@angular-builders/custom-webpack": "^14.0.1",
"@angular-devkit/build-angular": "14.2.0",
"@angular/cli": "^14.2.0",
"@angular/compiler-cli": "^14.2.0",

View File

@ -10,7 +10,7 @@
"devdb:start": "docker-compose -f ./support/dev.docker-compose.yml up -d",
"devdb:stop": "docker-compose -f ./support/dev.docker-compose.yml down",
"devdb:restart": "docker-compose -f ./support/dev.docker-compose.yml restart",
"devdb:remove": "docker-compose -f ./support/dev.docker-compose.yml down --rmi all --volumes",
"devdb:remove": "docker-compose -f ./support/dev.docker-compose.yml down --rm all --volumes",
"build": "./support/build.sh",
"setversion": "./support/setversion.sh",
"purge": "rm -rf ./node_modules",
@ -18,7 +18,8 @@
},
"resolutions": {
"fastify": "^4.5.2",
"terser": "^5.14.2",
"terser": "5.14.2",
"typeorm": "0.3.7",
"fastify-static": "npm:@fastify/static@*",
"fastify-formbody": "npm:@fastify/formbody@*",
"minimist": "npm:minimist-lite@*"

View File

@ -2,16 +2,9 @@ import { z } from 'zod';
import { EImageSchema } from '../../entities/image.entity';
import { EUserSchema } from '../../entities/user.entity';
import { createZodDto } from '../../util/create-zod-dto';
import { ParseBool } from '../../util/parse-simple';
import { ImageEntryVariant } from '../image-entry-variant.enum';
const parseBool = (value: unknown): boolean | null => {
if (value === true || value === 'true' || value === '1' || value === 'yes')
return true;
if (value === false || value === 'false' || value === '0' || value === 'no')
return false;
return null;
};
export const ImageRequestParamsSchema = z
.object({
height: z.preprocess(Number, z.number().int().min(1).max(32767)),
@ -20,11 +13,11 @@ export const ImageRequestParamsSchema = z
Number,
z.number().int().multipleOf(90).min(0).max(360),
),
flipx: z.preprocess(parseBool, z.boolean()),
flipy: z.preprocess(parseBool, z.boolean()),
greyscale: z.preprocess(parseBool, z.boolean()),
noalpha: z.preprocess(parseBool, z.boolean()),
negative: z.preprocess(parseBool, z.boolean()),
flipx: z.preprocess(ParseBool, z.boolean()),
flipy: z.preprocess(ParseBool, z.boolean()),
greyscale: z.preprocess(ParseBool, z.boolean()),
noalpha: z.preprocess(ParseBool, z.boolean()),
negative: z.preprocess(ParseBool, z.boolean()),
quality: z.preprocess(Number, z.number().int().min(1).max(100)),
})
.partial();

View File

@ -0,0 +1,34 @@
export const ParseBool = <T extends boolean | null = null>(
value: unknown,
fallback?: T,
): boolean | T => {
if (value === true || value === 'true' || value === '1' || value === 'yes')
return true;
if (value === false || value === 'false' || value === '0' || value === 'no')
return false;
return fallback === undefined ? (null as T) : fallback;
};
export const ParseInt = <T extends number | null = null>(
value: unknown,
fallback?: T,
): number | T => {
if (typeof value === 'number') return value;
if (typeof value === 'boolean') return value ? 1 : 0;
if (typeof value === 'string') {
const parsed = parseInt(value);
if (!isNaN(parsed)) return parsed;
}
return fallback === undefined ? (null as T) : fallback;
};
export const ParseString = <T extends string | null = null>(
value: unknown,
fallback?: T,
): string | T => {
if (typeof value === 'string') return value;
if (typeof value === 'boolean') return value ? 'true' : 'false';
if (typeof value === 'number') return value.toString();
return fallback === undefined ? (null as T) : fallback;
};

View File

@ -10,13 +10,18 @@ VERSION=$(cat ../package.json | grep version | head -1 | awk -F: '{ print $2 }'
echo "Building version $VERSION"
docker build -t "$PACKAGE_URL:$VERSION" -t "$PACKAGE_URL:latest" -f ./skala.Dockerfile ..
docker build -t "$PACKAGE_URL:$VERSION" -t "$PACKAGE_URL:latest" -f ./picsur.Dockerfile ..
echo "Done"
echo "Pushing to registry"
# only push if argument is set to "push"
if [ "$1" == "push" ]; then
echo "Pushing to registry"
docker push "$PACKAGE_URL:$VERSION"
docker push "$PACKAGE_URL:latest"
docker push "$PACKAGE_URL:$VERSION"
docker push "$PACKAGE_URL:latest"
echo "Done"
echo "Done"
else
echo "Not pushing to registry"
fi

View File

@ -1,13 +1,11 @@
version: '3'
services:
devdb:
image: postgres:11-alpine
image: postgres:14-alpine
environment:
POSTGRES_DB: picsur
POSTGRES_PASSWORD: picsur
POSTGRES_USER: picsur
logging:
driver: 'none'
ports:
- '5432:5432'
restart: unless-stopped

View File

@ -1,4 +1,4 @@
FROM node:18.8
FROM node:18.8-bullseye
# Sorry for the humongous docker container this generates
# Maybe I'll trim it down some day
@ -8,16 +8,10 @@ ENV PICSUR_PRODUCTION=true
ADD . /picsur
WORKDIR /picsur
RUN yarn install --frozen-lockfile
RUN yarn install --immutable
WORKDIR /picsur/shared
RUN yarn build
WORKDIR /picsur/frontend
RUN yarn build
WORKDIR /picsur/backend
RUN yarn build
CMD ["yarn", "start:prod"]
RUN yarn workspace picsur-shared build
RUN yarn workspace picsur-frontend build
RUN yarn workspace picsur-backend build
CMD /bin/bash -c "yarn workspace picsur-backend start:prod"

View File

@ -10,10 +10,11 @@ services:
# PICSUR_PORT: 8080
PICSUR_DB_HOST: picsur_postgres
# PICSUR_DB_PORT: 5432
# PICSUR_DB_USER: picsur
# PICSUR_DB_USERNAME: picsur
# PICSUR_DB_PASSWORD: picsur
# PICSUR_DB_NAME: picsur
# PICSUR_DB_DATABASE: picsur
# PICSUR_ADMIN_PASSWORD: picsur
@ -24,7 +25,7 @@ services:
# PICSUR_STATIC_FRONTEND_ROOT: "/picsur/frontend/dist"
restart: unless-stopped
picsur_postgres:
image: postgres:11-alpine
image: postgres:14-alpine
container_name: picsur_postgres
environment:
POSTGRES_DB: picsur

View File

@ -1,8 +1,8 @@
#!/bin/bash
yarn workspace shared purge
yarn workspace backend purge
yarn workspace frontend purge
yarn workspace picsur-shared purge
yarn workspace picsur-backend purge
yarn workspace picsur-frontend purge
yarn devdb:purge
yarn devdb:remove
yarn purge

180
yarn.lock
View File

@ -22,6 +22,23 @@ __metadata:
languageName: node
linkType: hard
"@angular-builders/custom-webpack@npm:^14.0.1":
version: 14.0.1
resolution: "@angular-builders/custom-webpack@npm:14.0.1"
dependencies:
"@angular-devkit/architect": ">=0.1400.0 < 0.1500.0"
"@angular-devkit/build-angular": ^14.0.0
"@angular-devkit/core": ^14.0.0
lodash: ^4.17.15
ts-node: ^10.0.0
tsconfig-paths: ^3.9.0
webpack-merge: ^5.7.3
peerDependencies:
"@angular/compiler-cli": ^14.0.0
checksum: 6cae568d4f010588f251a5acb9dbf66584e7850243eba3c5c14059b73b48310d1c98a4e1d8b1789bf6d7c1ab2dcf434d3252fdde6a668670c1730a21c56d4150
languageName: node
linkType: hard
"@angular-devkit/architect@npm:0.1402.0":
version: 0.1402.0
resolution: "@angular-devkit/architect@npm:0.1402.0"
@ -32,6 +49,16 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/architect@npm:0.1402.1, @angular-devkit/architect@npm:>=0.1400.0 < 0.1500.0":
version: 0.1402.1
resolution: "@angular-devkit/architect@npm:0.1402.1"
dependencies:
"@angular-devkit/core": 14.2.1
rxjs: 6.6.7
checksum: 8338710b57ea761695cb77996053d948f76fd2dd4b6a68499a473ad554ec483df1fd2c690c7d2abcf95c60a51f666e15a70cc1e6edd8270104c90a64c57c5ee7
languageName: node
linkType: hard
"@angular-devkit/build-angular@npm:14.2.0":
version: 14.2.0
resolution: "@angular-devkit/build-angular@npm:14.2.0"
@ -128,6 +155,102 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/build-angular@npm:^14.0.0":
version: 14.2.1
resolution: "@angular-devkit/build-angular@npm:14.2.1"
dependencies:
"@ampproject/remapping": 2.2.0
"@angular-devkit/architect": 0.1402.1
"@angular-devkit/build-webpack": 0.1402.1
"@angular-devkit/core": 14.2.1
"@babel/core": 7.18.10
"@babel/generator": 7.18.12
"@babel/helper-annotate-as-pure": 7.18.6
"@babel/plugin-proposal-async-generator-functions": 7.18.10
"@babel/plugin-transform-async-to-generator": 7.18.6
"@babel/plugin-transform-runtime": 7.18.10
"@babel/preset-env": 7.18.10
"@babel/runtime": 7.18.9
"@babel/template": 7.18.10
"@discoveryjs/json-ext": 0.5.7
"@ngtools/webpack": 14.2.1
ansi-colors: 4.1.3
babel-loader: 8.2.5
babel-plugin-istanbul: 6.1.1
browserslist: ^4.9.1
cacache: 16.1.2
copy-webpack-plugin: 11.0.0
critters: 0.0.16
css-loader: 6.7.1
esbuild: 0.15.5
esbuild-wasm: 0.15.5
glob: 8.0.3
https-proxy-agent: 5.0.1
inquirer: 8.2.4
jsonc-parser: 3.1.0
karma-source-map-support: 1.4.0
less: 4.1.3
less-loader: 11.0.0
license-webpack-plugin: 4.0.2
loader-utils: 3.2.0
mini-css-extract-plugin: 2.6.1
minimatch: 5.1.0
open: 8.4.0
ora: 5.4.1
parse5-html-rewriting-stream: 6.0.1
piscina: 3.2.0
postcss: 8.4.16
postcss-import: 14.1.0
postcss-loader: 7.0.1
postcss-preset-env: 7.8.0
regenerator-runtime: 0.13.9
resolve-url-loader: 5.0.0
rxjs: 6.6.7
sass: 1.54.4
sass-loader: 13.0.2
semver: 7.3.7
source-map-loader: 4.0.0
source-map-support: 0.5.21
stylus: 0.59.0
stylus-loader: 7.0.0
terser: 5.14.2
text-table: 0.2.0
tree-kill: 1.2.2
tslib: 2.4.0
webpack: 5.74.0
webpack-dev-middleware: 5.3.3
webpack-dev-server: 4.10.0
webpack-merge: 5.8.0
webpack-subresource-integrity: 5.1.0
peerDependencies:
"@angular/compiler-cli": ^14.0.0
"@angular/localize": ^14.0.0
"@angular/service-worker": ^14.0.0
karma: ^6.3.0
ng-packagr: ^14.0.0
protractor: ^7.0.0
tailwindcss: ^2.0.0 || ^3.0.0
typescript: ">=4.6.2 <4.9"
dependenciesMeta:
esbuild:
optional: true
peerDependenciesMeta:
"@angular/localize":
optional: true
"@angular/service-worker":
optional: true
karma:
optional: true
ng-packagr:
optional: true
protractor:
optional: true
tailwindcss:
optional: true
checksum: 6b1fb478fc3ad890f15458f70d30b396e9aaa20ac0e015dc17713bfcc69218b959d03bae711a63ad063b1df34d50cf8f077d32d21d4b1b6f1635415a3734cb92
languageName: node
linkType: hard
"@angular-devkit/build-webpack@npm:0.1402.0":
version: 0.1402.0
resolution: "@angular-devkit/build-webpack@npm:0.1402.0"
@ -141,6 +264,19 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/build-webpack@npm:0.1402.1":
version: 0.1402.1
resolution: "@angular-devkit/build-webpack@npm:0.1402.1"
dependencies:
"@angular-devkit/architect": 0.1402.1
rxjs: 6.6.7
peerDependencies:
webpack: ^5.30.0
webpack-dev-server: ^4.0.0
checksum: b57c17b14f85bd48c1ae7637a5d576211dcfab90f4dec068258d95a4b09bc6db88bb02a9c6ad17d257726cd9477839b18537e8a0200d5e2d51f5bd7d60c1ca15
languageName: node
linkType: hard
"@angular-devkit/core@npm:14.0.5":
version: 14.0.5
resolution: "@angular-devkit/core@npm:14.0.5"
@ -177,6 +313,24 @@ __metadata:
languageName: node
linkType: hard
"@angular-devkit/core@npm:14.2.1, @angular-devkit/core@npm:^14.0.0":
version: 14.2.1
resolution: "@angular-devkit/core@npm:14.2.1"
dependencies:
ajv: 8.11.0
ajv-formats: 2.1.1
jsonc-parser: 3.1.0
rxjs: 6.6.7
source-map: 0.7.4
peerDependencies:
chokidar: ^3.5.2
peerDependenciesMeta:
chokidar:
optional: true
checksum: 67a4c57a6e51bbb7151fa57d5d27e2360dea55b125343ff79939fc7c15dc88b6d027303f98024c3bef3a994a901212ddb2f3d6fd6f6433760e1b7bb504f0648f
languageName: node
linkType: hard
"@angular-devkit/schematics-cli@npm:14.0.5":
version: 14.0.5
resolution: "@angular-devkit/schematics-cli@npm:14.0.5"
@ -2726,6 +2880,17 @@ __metadata:
languageName: node
linkType: hard
"@ngtools/webpack@npm:14.2.1":
version: 14.2.1
resolution: "@ngtools/webpack@npm:14.2.1"
peerDependencies:
"@angular/compiler-cli": ^14.0.0
typescript: ">=4.6.2 <4.9"
webpack: ^5.54.0
checksum: 6adf3ac922bc411ba82f7ded4c2bcdd2162b72570e78169ee39e694daac6b8c5b1c9968f888de159f534a6269dd787ef4dabd85627f5ac39b0f0a844a7d44487
languageName: node
linkType: hard
"@ngui/common@npm:^1.0.0":
version: 1.0.0
resolution: "@ngui/common@npm:1.0.0"
@ -7504,7 +7669,7 @@ __metadata:
languageName: node
linkType: hard
"lodash@npm:4.17.21, lodash@npm:^4.17.19, lodash@npm:^4.17.21":
"lodash@npm:4.17.21, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.21":
version: 4.17.21
resolution: "lodash@npm:4.17.21"
checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7
@ -8945,6 +9110,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "picsur-frontend@workspace:frontend"
dependencies:
"@angular-builders/custom-webpack": ^14.0.1
"@angular-devkit/build-angular": 14.2.0
"@angular/animations": ^14.2.0
"@angular/cdk": ^14.2.0
@ -11060,9 +11226,9 @@ __metadata:
languageName: node
linkType: hard
"terser@npm:^5.14.2":
version: 5.15.0
resolution: "terser@npm:5.15.0"
"terser@npm:5.14.2":
version: 5.14.2
resolution: "terser@npm:5.14.2"
dependencies:
"@jridgewell/source-map": ^0.3.2
acorn: ^8.5.0
@ -11070,7 +11236,7 @@ __metadata:
source-map-support: ~0.5.20
bin:
terser: bin/terser
checksum: b2358c989fcb76b4a1c265f60e175c950d3f776e5f619a9f58f54e8d2d792cd6b4cca86071834075f3b9943556d695357bafdd4ee2390de2fc9fd96ba3efa8c8
checksum: cabb50a640d6c2cfb351e4f43dc7bf7436f649755bb83eb78b2cacda426d5e0979bd44e6f92d713f3ca0f0866e322739b9ced888ebbce6508ad872d08de74fcc
languageName: node
linkType: hard
@ -11237,7 +11403,7 @@ __metadata:
languageName: node
linkType: hard
"ts-node@npm:^10.9.1":
"ts-node@npm:^10.0.0, ts-node@npm:^10.9.1":
version: 10.9.1
resolution: "ts-node@npm:10.9.1"
dependencies:
@ -11779,7 +11945,7 @@ __metadata:
languageName: node
linkType: hard
"webpack-merge@npm:5.8.0":
"webpack-merge@npm:5.8.0, webpack-merge@npm:^5.7.3":
version: 5.8.0
resolution: "webpack-merge@npm:5.8.0"
dependencies: