Skip to content

Commit

Permalink
chore: Recreate main settings in AdminCP
Browse files Browse the repository at this point in the history
  • Loading branch information
aXenDeveloper committed Oct 28, 2024
1 parent 394d820 commit 99052bd
Show file tree
Hide file tree
Showing 26 changed files with 574 additions and 34 deletions.
4 changes: 2 additions & 2 deletions apps/frontend/src/app/[locale]/(main)/(vitnode)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// ! If you remove this then default page plugin will not work
import React from 'react';
import { getMiddlewareData } from 'vitnode-frontend/api/get-middleware-data';
// import { generateMetadataDefaultPage } from 'vitnode-frontend/views/theme/views/default-page';
import { generateMetadataDefaultPage } from 'vitnode-frontend/views/theme/views/default-page';

// export const generateMetadata = generateMetadataDefaultPage;
export const generateMetadata = generateMetadataDefaultPage;

export default async function Page() {
const { plugin_code_default } = await getMiddlewareData();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {
generateMetadataMainSettingsCoreAdmin,
MainSettingsCoreAdminView,
} from 'vitnode-frontend/views/admin/views/core/settings/main/main-settings-core-admin-view';

export const generateMetadata = generateMetadataMainSettingsCoreAdmin;

export default function Page() {
return <MainSettingsCoreAdminView />;
}
3 changes: 0 additions & 3 deletions apps/frontend/src/plugins/admin/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@
"description": {
"label": "Description Site"
},
"copyright": {
"label": "Copyright Site"
},
"contact_email": {
"label": "Contact Email",
"desc": "This email address will be applied to the contact form and other contact points on your website."
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/core/admin/admin.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Module } from '@nestjs/common';

import { AuthAdminModule } from './auth/auth.module';
import { SettingsAdminModule } from './settings/settings.module';
import { StaffAdminModule } from './staff/staff.module';

@Module({
imports: [AuthAdminModule, StaffAdminModule],
imports: [AuthAdminModule, StaffAdminModule, SettingsAdminModule],
})
export class AdminModule {}
2 changes: 1 addition & 1 deletion packages/backend/src/core/admin/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { ShowAuthAdminService } from './services/show.service';

@ApiTags('Admin')
@Controller('admin/auth')
@ApiSecurity('admin')
export class AuthAdminController {
constructor(private readonly showService: ShowAuthAdminService) {}

@Get()
@ApiSecurity('admin')
@ApiOkResponse({
type: ShowAuthAdminObj,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const coreNav: ShowAuthAdminObj['nav'] = [
children: [
{
code: 'main',
keywords: ['name', 'title', 'description', 'desc', 'copyright'],
keywords: ['name', 'title', 'description', 'desc'],
},
{
code: 'security',
Expand Down
124 changes: 124 additions & 0 deletions packages/backend/src/core/admin/settings/services/edit.main.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { ABSOLUTE_PATHS } from '@/app.module';
import { configPath, ConfigType, getConfigFile } from '@/helpers/config';
import { InternalDatabaseService } from '@/utils/database/internal_database.service';
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { readFile, writeFile } from 'fs/promises';
import { join } from 'path';
import { MainSettingsAdminBody } from 'vitnode-shared/admin/settings.dto';
import { ManifestWithLang } from 'vitnode-shared/manifest.dto';

@Injectable()
export class EditMainSettingsAdminService {
constructor(private readonly databaseService: InternalDatabaseService) {}

protected async updateDescription({
languages,
site_description,
}: {
languages: { code: string }[];
site_description: MainSettingsAdminBody['site_description'];
}) {
const update = await Promise.all(
(site_description ?? []).map(async el => {
const item =
el.value !== undefined
? el
: site_description?.find(el => el.language_code === 'en')?.value
? site_description.find(el => el.language_code === 'en')
: site_description?.find(el => el.value);

// Still not found?
if (!item) {
throw new InternalServerErrorException();
}

const path = join(
ABSOLUTE_PATHS.uploads.public,
'assets',
item.language_code,
'manifest.webmanifest',
);
const manifest: ManifestWithLang = JSON.parse(
await readFile(path, 'utf8'),
);
const newData: ManifestWithLang = {
...manifest,
lang: el.language_code,
description: item.value,
};

await writeFile(path, JSON.stringify(newData, null, 2), 'utf8');

return el.language_code;
}),
);

// Update rest of the languages
await Promise.all(
languages
.filter(item => !update.includes(item.code))
.map(async item => {
const value =
site_description?.find(el => el.language_code === 'en')?.value ??
site_description?.[0]?.value ??
'';

const path = join(
ABSOLUTE_PATHS.uploads.public,
'assets',
item.code,
'manifest.webmanifest',
);
const manifest: ManifestWithLang = JSON.parse(
await readFile(path, 'utf8'),
);
const newData: ManifestWithLang = {
...manifest,
description: value,
};

await writeFile(path, JSON.stringify(newData, null, 2), 'utf8');
}),
);
}

async edit({
site_description,
site_name,
site_short_name,
contact_email,
}: MainSettingsAdminBody): Promise<MainSettingsAdminBody> {
const config = getConfigFile();
const newData: ConfigType = {
...config,
settings: {
...config.settings,
main: {
...config.settings.main,
site_name,
site_short_name,
contact_email,
},
},
};
await writeFile(configPath, JSON.stringify(newData, null, 2), 'utf8');
const languages =
await this.databaseService.db.query.core_languages.findMany({
columns: {
code: true,
},
});

await this.updateDescription({
languages,
site_description,
});

return {
site_description,
site_name,
site_short_name,
contact_email,
};
}
}
19 changes: 19 additions & 0 deletions packages/backend/src/core/admin/settings/settings.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Body, Controller, Put } from '@nestjs/common';
import { ApiSecurity, ApiTags } from '@nestjs/swagger';
import { MainSettingsAdminBody } from 'vitnode-shared/admin/settings.dto';

import { EditMainSettingsAdminService } from './services/edit.main.service';

@ApiTags('Admin')
@Controller('admin/settings')
@ApiSecurity('admin')
export class SettingsAdminController {
constructor(private readonly editMainService: EditMainSettingsAdminService) {}

@Put('/main')
async editMainSettings(
@Body() body: MainSettingsAdminBody,
): Promise<MainSettingsAdminBody> {
return await this.editMainService.edit(body);
}
}
10 changes: 10 additions & 0 deletions packages/backend/src/core/admin/settings/settings.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';

import { EditMainSettingsAdminService } from './services/edit.main.service';
import { SettingsAdminController } from './settings.controller';

@Module({
providers: [EditMainSettingsAdminService],
controllers: [SettingsAdminController],
})
export class SettingsAdminModule {}
13 changes: 11 additions & 2 deletions packages/backend/src/core/middleware/services/show.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export class ShowMiddlewareService {
lang,
'manifest.webmanifest',
);
const manifest = JSON.parse(
const manifest: ManifestWithLang = JSON.parse(
await readFile(path, 'utf8'),
) as ManifestWithLang;
);

return manifest;
}),
Expand All @@ -49,6 +49,7 @@ export class ShowMiddlewareService {
default: true,
enabled: true,
name: true,
allow_in_input: true,
},
}),
]);
Expand All @@ -57,6 +58,9 @@ export class ShowMiddlewareService {
if (!plugin_code_default) {
throw new InternalServerErrorException('Plugin not found');
}
const manifest = await this.getManifest({
langCodes: langs.map(lang => lang.code),
});

return {
languages: langs,
Expand All @@ -76,6 +80,11 @@ export class ShowMiddlewareService {
},
},
plugin_code_default,
site_description: manifest.map(item => ({
language_code: item.lang,
value: item.description,
})),
contact_email: config.settings.main.contact_email,
};
}
}
5 changes: 0 additions & 5 deletions packages/backend/src/database/schema/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ export const core_languages = pgTable(
time_24: t.boolean().notNull().default(false),
locale: t.varchar({ length: 50 }).notNull().default('en'),
allow_in_input: t.boolean().default(true).notNull(),
site_copyright: t
.varchar({
length: 255,
})
.default(''),
}),
table => ({
code_idx: index('core_languages_code_idx').on(table.code),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { AutoFormComponentProps } from '@/components/form/auto-form';
import { FormControl, FormMessage } from '@/components/ui/form';
import { StringLanguageInput } from '@/components/ui/text-language-input';

import { AutoFormInputWrapper } from './common/input-wrapper';
import { AutoFormLabel } from './common/label';
import { AutoFormTooltip } from './common/tooltip';
import { AutoFormWrapper } from './common/wrapper';

export function AutoFormStringLanguageInput({
field,
label,
theme,
description,
isRequired,
isDisabled,
hideOptionalLabel,
zodInputProps,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
overrideOptions: _,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
shape: _shape,
wrapper,
classNameWrapper,
...props
}: AutoFormComponentProps &
Omit<
React.ComponentProps<typeof StringLanguageInput>,
'name' | 'onChange' | 'value'
>) {
return (
<AutoFormWrapper className={classNameWrapper} theme={theme}>
{label && (
<AutoFormLabel
description={description}
hideOptionalLabel={hideOptionalLabel}
isRequired={isRequired}
label={label}
theme={theme}
/>
)}

<AutoFormInputWrapper field={field} Wrapper={wrapper}>
<FormControl>
<StringLanguageInput
{...props}
{...zodInputProps}
disabled={isDisabled || props.disabled}
onBlur={field.onBlur || props.onBlur}
onChange={e => {
field.onChange(e);
}}
value={field.value ?? []}
/>
</FormControl>
</AutoFormInputWrapper>

{description && theme === 'vertical' && (
<AutoFormTooltip description={description} />
)}
<FormMessage />
</AutoFormWrapper>
);
}
Loading

0 comments on commit 99052bd

Please sign in to comment.