mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 10:29:21 +03:00
directus setup
This commit is contained in:
parent
8b26bc8bbc
commit
a9061822f0
@ -1,3 +1,4 @@
|
|||||||
REDIS_HOST=redis-1234.p124.eu-central-5-5.ec2.cloud.redislabs.com
|
REDIS_HOST=redis-1234.p124.eu-central-5-5.ec2.cloud.redislabs.com
|
||||||
REDIS_PASSWORD=superpassword
|
REDIS_PASSWORD=superpassword
|
||||||
REDIS_PORT=1234
|
REDIS_PORT=1234
|
||||||
|
DIRECTUS_TOKEN=
|
||||||
@ -1,8 +1,9 @@
|
|||||||
import { defineConfig } from "astro/config";
|
import { defineConfig, envField } from "astro/config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integrations
|
* Integrations
|
||||||
*/
|
*/
|
||||||
|
import node from "@astrojs/node";
|
||||||
import sitemap from "@astrojs/sitemap";
|
import sitemap from "@astrojs/sitemap";
|
||||||
import solidJs from "@astrojs/solid-js";
|
import solidJs from "@astrojs/solid-js";
|
||||||
import icon from "astro-icon";
|
import icon from "astro-icon";
|
||||||
@ -32,6 +33,15 @@ const createAnchorLabel = (heading) => {
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: "https://yarkov.tech",
|
site: "https://yarkov.tech",
|
||||||
compressHTML: true,
|
compressHTML: true,
|
||||||
|
output: "server",
|
||||||
|
adapter: node({
|
||||||
|
mode: "standalone",
|
||||||
|
}),
|
||||||
|
env: {
|
||||||
|
schema: {
|
||||||
|
DIRECTUS_TOKEN: envField.string({ context: "server", access: "secret", default: "" }),
|
||||||
|
},
|
||||||
|
},
|
||||||
integrations: [
|
integrations: [
|
||||||
sitemap({
|
sitemap({
|
||||||
i18n: {
|
i18n: {
|
||||||
|
|||||||
578
directus-schema.ts
Normal file
578
directus-schema.ts
Normal file
@ -0,0 +1,578 @@
|
|||||||
|
export interface Article {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
tags?: ArticleTag[] | string[];
|
||||||
|
/** @required */
|
||||||
|
translations: ArticleTranslation[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ArticleTag {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
article_id?: Article | string | null;
|
||||||
|
tag_id?: Tag | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ArticleTranslation {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
article_id?: Article | string | null;
|
||||||
|
languages_code?: Language | string | null;
|
||||||
|
title?: string | null;
|
||||||
|
description?: string | null;
|
||||||
|
slug?: string | null;
|
||||||
|
content?: string | null;
|
||||||
|
status?: 'published' | 'draft' | 'archived' | null;
|
||||||
|
author?: DirectusUser | string | null;
|
||||||
|
pub_date?: string | null;
|
||||||
|
views?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Global {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
translations?: GlobalTranslation[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GlobalTranslation {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
global_id?: Global | string | null;
|
||||||
|
languages_code?: Language | string | null;
|
||||||
|
description?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Language {
|
||||||
|
/** @primaryKey */
|
||||||
|
code: string;
|
||||||
|
name?: string | null;
|
||||||
|
direction?: 'ltr' | 'rtl' | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tag {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
/** @required */
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusAccess {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
role?: DirectusRole | string | null;
|
||||||
|
user?: DirectusUser | string | null;
|
||||||
|
policy?: DirectusPolicy | string;
|
||||||
|
sort?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusActivity {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
action?: string;
|
||||||
|
user?: DirectusUser | string | null;
|
||||||
|
timestamp?: string;
|
||||||
|
ip?: string | null;
|
||||||
|
user_agent?: string | null;
|
||||||
|
collection?: string;
|
||||||
|
item?: string;
|
||||||
|
origin?: string | null;
|
||||||
|
revisions?: DirectusRevision[] | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusCollection {
|
||||||
|
/** @primaryKey */
|
||||||
|
collection: string;
|
||||||
|
icon?: string | null;
|
||||||
|
note?: string | null;
|
||||||
|
display_template?: string | null;
|
||||||
|
hidden?: boolean;
|
||||||
|
singleton?: boolean;
|
||||||
|
translations?: Array<{ language: string; translation: string; singular: string; plural: string }> | null;
|
||||||
|
archive_field?: string | null;
|
||||||
|
archive_app_filter?: boolean;
|
||||||
|
archive_value?: string | null;
|
||||||
|
unarchive_value?: string | null;
|
||||||
|
sort_field?: string | null;
|
||||||
|
accountability?: 'all' | 'activity' | null | null;
|
||||||
|
color?: string | null;
|
||||||
|
item_duplication_fields?: 'json' | null;
|
||||||
|
sort?: number | null;
|
||||||
|
group?: DirectusCollection | string | null;
|
||||||
|
collapse?: string;
|
||||||
|
preview_url?: string | null;
|
||||||
|
versioning?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusComment {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
collection?: DirectusCollection | string;
|
||||||
|
item?: string;
|
||||||
|
comment?: string;
|
||||||
|
date_created?: string | null;
|
||||||
|
date_updated?: string | null;
|
||||||
|
user_created?: DirectusUser | string | null;
|
||||||
|
user_updated?: DirectusUser | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusField {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
collection?: DirectusCollection | string;
|
||||||
|
field?: string;
|
||||||
|
special?: string[] | null;
|
||||||
|
interface?: string | null;
|
||||||
|
options?: 'json' | null;
|
||||||
|
display?: string | null;
|
||||||
|
display_options?: 'json' | null;
|
||||||
|
readonly?: boolean;
|
||||||
|
hidden?: boolean;
|
||||||
|
sort?: number | null;
|
||||||
|
width?: string | null;
|
||||||
|
translations?: 'json' | null;
|
||||||
|
note?: string | null;
|
||||||
|
conditions?: 'json' | null;
|
||||||
|
required?: boolean | null;
|
||||||
|
group?: DirectusField | string | null;
|
||||||
|
validation?: 'json' | null;
|
||||||
|
validation_message?: string | null;
|
||||||
|
searchable?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusFile {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
storage?: string;
|
||||||
|
filename_disk?: string | null;
|
||||||
|
filename_download?: string;
|
||||||
|
title?: string | null;
|
||||||
|
type?: string | null;
|
||||||
|
folder?: DirectusFolder | string | null;
|
||||||
|
uploaded_by?: DirectusUser | string | null;
|
||||||
|
created_on?: string;
|
||||||
|
modified_by?: DirectusUser | string | null;
|
||||||
|
modified_on?: string;
|
||||||
|
charset?: string | null;
|
||||||
|
filesize?: number | null;
|
||||||
|
width?: number | null;
|
||||||
|
height?: number | null;
|
||||||
|
duration?: number | null;
|
||||||
|
embed?: string | null;
|
||||||
|
description?: string | null;
|
||||||
|
location?: string | null;
|
||||||
|
tags?: string[] | null;
|
||||||
|
metadata?: 'json' | null;
|
||||||
|
focal_point_x?: number | null;
|
||||||
|
focal_point_y?: number | null;
|
||||||
|
tus_id?: string | null;
|
||||||
|
tus_data?: 'json' | null;
|
||||||
|
uploaded_on?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusFolder {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
parent?: DirectusFolder | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusMigration {
|
||||||
|
/** @primaryKey */
|
||||||
|
version: string;
|
||||||
|
name?: string;
|
||||||
|
timestamp?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusPermission {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
collection?: string;
|
||||||
|
action?: string;
|
||||||
|
permissions?: 'json' | null;
|
||||||
|
validation?: 'json' | null;
|
||||||
|
presets?: 'json' | null;
|
||||||
|
fields?: string[] | null;
|
||||||
|
policy?: DirectusPolicy | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusPolicy {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
/** @required */
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
description?: string | null;
|
||||||
|
ip_access?: string[] | null;
|
||||||
|
enforce_tfa?: boolean;
|
||||||
|
admin_access?: boolean;
|
||||||
|
app_access?: boolean;
|
||||||
|
permissions?: DirectusPermission[] | string[];
|
||||||
|
users?: DirectusAccess[] | string[];
|
||||||
|
roles?: DirectusAccess[] | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusPreset {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
bookmark?: string | null;
|
||||||
|
user?: DirectusUser | string | null;
|
||||||
|
role?: DirectusRole | string | null;
|
||||||
|
collection?: string | null;
|
||||||
|
search?: string | null;
|
||||||
|
layout?: string | null;
|
||||||
|
layout_query?: 'json' | null;
|
||||||
|
layout_options?: 'json' | null;
|
||||||
|
refresh_interval?: number | null;
|
||||||
|
filter?: 'json' | null;
|
||||||
|
icon?: string | null;
|
||||||
|
color?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusRelation {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
many_collection?: string;
|
||||||
|
many_field?: string;
|
||||||
|
one_collection?: string | null;
|
||||||
|
one_field?: string | null;
|
||||||
|
one_collection_field?: string | null;
|
||||||
|
one_allowed_collections?: string[] | null;
|
||||||
|
junction_field?: string | null;
|
||||||
|
sort_field?: string | null;
|
||||||
|
one_deselect_action?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusRevision {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
activity?: DirectusActivity | string;
|
||||||
|
collection?: string;
|
||||||
|
item?: string;
|
||||||
|
data?: 'json' | null;
|
||||||
|
delta?: 'json' | null;
|
||||||
|
parent?: DirectusRevision | string | null;
|
||||||
|
version?: DirectusVersion | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusRole {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
/** @required */
|
||||||
|
name: string;
|
||||||
|
icon?: string;
|
||||||
|
description?: string | null;
|
||||||
|
parent?: DirectusRole | string | null;
|
||||||
|
children?: DirectusRole[] | string[];
|
||||||
|
policies?: DirectusAccess[] | string[];
|
||||||
|
users?: DirectusUser[] | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusSession {
|
||||||
|
/** @primaryKey */
|
||||||
|
token: string;
|
||||||
|
user?: DirectusUser | string | null;
|
||||||
|
expires?: string;
|
||||||
|
ip?: string | null;
|
||||||
|
user_agent?: string | null;
|
||||||
|
share?: DirectusShare | string | null;
|
||||||
|
origin?: string | null;
|
||||||
|
next_token?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusSettings {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
project_name?: string;
|
||||||
|
project_url?: string | null;
|
||||||
|
project_color?: string;
|
||||||
|
project_logo?: DirectusFile | string | null;
|
||||||
|
public_foreground?: DirectusFile | string | null;
|
||||||
|
public_background?: DirectusFile | string | null;
|
||||||
|
public_note?: string | null;
|
||||||
|
auth_login_attempts?: number | null;
|
||||||
|
auth_password_policy?: null | `/^.{8,}$/` | `/(?=^.{8,}$)(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+}{';'?>.<,])(?!.*\\s).*$/` | null;
|
||||||
|
storage_asset_transform?: 'all' | 'none' | 'presets' | null;
|
||||||
|
storage_asset_presets?: Array<{ key: string; fit: 'contain' | 'cover' | 'inside' | 'outside'; width: number; height: number; quality: number; withoutEnlargement: boolean; format: 'auto' | 'jpeg' | 'png' | 'webp' | 'tiff' | 'avif'; transforms: 'json' }> | null;
|
||||||
|
custom_css?: string | null;
|
||||||
|
storage_default_folder?: DirectusFolder | string | null;
|
||||||
|
basemaps?: Array<{ name: string; type: 'raster' | 'tile' | 'style'; url: string; tileSize: number; attribution: string }> | null;
|
||||||
|
mapbox_key?: string | null;
|
||||||
|
module_bar?: 'json' | null;
|
||||||
|
project_descriptor?: string | null;
|
||||||
|
default_language?: string;
|
||||||
|
custom_aspect_ratios?: Array<{ text: string; value: number }> | null;
|
||||||
|
public_favicon?: DirectusFile | string | null;
|
||||||
|
default_appearance?: 'auto' | 'light' | 'dark';
|
||||||
|
default_theme_light?: string | null;
|
||||||
|
theme_light_overrides?: 'json' | null;
|
||||||
|
default_theme_dark?: string | null;
|
||||||
|
theme_dark_overrides?: 'json' | null;
|
||||||
|
report_error_url?: string | null;
|
||||||
|
report_bug_url?: string | null;
|
||||||
|
report_feature_url?: string | null;
|
||||||
|
public_registration?: boolean;
|
||||||
|
public_registration_verify_email?: boolean;
|
||||||
|
public_registration_role?: DirectusRole | string | null;
|
||||||
|
public_registration_email_filter?: 'json' | null;
|
||||||
|
visual_editor_urls?: Array<{ url: string }> | null;
|
||||||
|
project_id?: string | null;
|
||||||
|
mcp_enabled?: boolean;
|
||||||
|
mcp_allow_deletes?: boolean;
|
||||||
|
mcp_prompts_collection?: string | null;
|
||||||
|
mcp_system_prompt_enabled?: boolean;
|
||||||
|
mcp_system_prompt?: string | null;
|
||||||
|
project_owner?: string | null;
|
||||||
|
project_usage?: string | null;
|
||||||
|
org_name?: string | null;
|
||||||
|
product_updates?: boolean | null;
|
||||||
|
project_status?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusUser {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
first_name?: string | null;
|
||||||
|
last_name?: string | null;
|
||||||
|
email?: string | null;
|
||||||
|
password?: string | null;
|
||||||
|
location?: string | null;
|
||||||
|
title?: string | null;
|
||||||
|
description?: string | null;
|
||||||
|
tags?: string[] | null;
|
||||||
|
avatar?: DirectusFile | string | null;
|
||||||
|
language?: string | null;
|
||||||
|
tfa_secret?: string | null;
|
||||||
|
status?: 'draft' | 'invited' | 'unverified' | 'active' | 'suspended' | 'archived';
|
||||||
|
role?: DirectusRole | string | null;
|
||||||
|
token?: string | null;
|
||||||
|
last_access?: string | null;
|
||||||
|
last_page?: string | null;
|
||||||
|
provider?: string;
|
||||||
|
external_identifier?: string | null;
|
||||||
|
auth_data?: 'json' | null;
|
||||||
|
email_notifications?: boolean | null;
|
||||||
|
appearance?: null | 'auto' | 'light' | 'dark' | null;
|
||||||
|
theme_dark?: string | null;
|
||||||
|
theme_light?: string | null;
|
||||||
|
theme_light_overrides?: 'json' | null;
|
||||||
|
theme_dark_overrides?: 'json' | null;
|
||||||
|
text_direction?: 'auto' | 'ltr' | 'rtl';
|
||||||
|
policies?: DirectusAccess[] | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusWebhook {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
name?: string;
|
||||||
|
method?: null;
|
||||||
|
url?: string;
|
||||||
|
status?: 'active' | 'inactive';
|
||||||
|
data?: boolean;
|
||||||
|
actions?: 'create' | 'update' | 'delete';
|
||||||
|
collections?: string[];
|
||||||
|
headers?: Array<{ header: string; value: string }> | null;
|
||||||
|
was_active_before_deprecation?: boolean;
|
||||||
|
migrated_flow?: DirectusFlow | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusDashboard {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
icon?: string;
|
||||||
|
note?: string | null;
|
||||||
|
date_created?: string | null;
|
||||||
|
user_created?: DirectusUser | string | null;
|
||||||
|
color?: string | null;
|
||||||
|
panels?: DirectusPanel[] | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusPanel {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
dashboard?: DirectusDashboard | string;
|
||||||
|
name?: string | null;
|
||||||
|
icon?: string | null;
|
||||||
|
color?: string | null;
|
||||||
|
show_header?: boolean;
|
||||||
|
note?: string | null;
|
||||||
|
type?: string;
|
||||||
|
position_x?: number;
|
||||||
|
position_y?: number;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
options?: 'json' | null;
|
||||||
|
date_created?: string | null;
|
||||||
|
user_created?: DirectusUser | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusNotification {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: number;
|
||||||
|
timestamp?: string | null;
|
||||||
|
status?: string | null;
|
||||||
|
recipient?: DirectusUser | string;
|
||||||
|
sender?: DirectusUser | string | null;
|
||||||
|
subject?: string;
|
||||||
|
message?: string | null;
|
||||||
|
collection?: string | null;
|
||||||
|
item?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusShare {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
name?: string | null;
|
||||||
|
collection?: DirectusCollection | string;
|
||||||
|
item?: string;
|
||||||
|
role?: DirectusRole | string | null;
|
||||||
|
password?: string | null;
|
||||||
|
user_created?: DirectusUser | string | null;
|
||||||
|
date_created?: string | null;
|
||||||
|
date_start?: string | null;
|
||||||
|
date_end?: string | null;
|
||||||
|
times_used?: number | null;
|
||||||
|
max_uses?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusFlow {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
icon?: string | null;
|
||||||
|
color?: string | null;
|
||||||
|
description?: string | null;
|
||||||
|
status?: string;
|
||||||
|
trigger?: string | null;
|
||||||
|
accountability?: string | null;
|
||||||
|
options?: 'json' | null;
|
||||||
|
operation?: DirectusOperation | string | null;
|
||||||
|
date_created?: string | null;
|
||||||
|
user_created?: DirectusUser | string | null;
|
||||||
|
operations?: DirectusOperation[] | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusOperation {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
name?: string | null;
|
||||||
|
key?: string;
|
||||||
|
type?: string;
|
||||||
|
position_x?: number;
|
||||||
|
position_y?: number;
|
||||||
|
options?: 'json' | null;
|
||||||
|
resolve?: DirectusOperation | string | null;
|
||||||
|
reject?: DirectusOperation | string | null;
|
||||||
|
flow?: DirectusFlow | string;
|
||||||
|
date_created?: string | null;
|
||||||
|
user_created?: DirectusUser | string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusTranslation {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
/** @required */
|
||||||
|
language: string;
|
||||||
|
/** @required */
|
||||||
|
key: string;
|
||||||
|
/** @required */
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusVersion {
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
key?: string;
|
||||||
|
name?: string | null;
|
||||||
|
collection?: DirectusCollection | string;
|
||||||
|
item?: string;
|
||||||
|
hash?: string | null;
|
||||||
|
date_created?: string | null;
|
||||||
|
date_updated?: string | null;
|
||||||
|
user_created?: DirectusUser | string | null;
|
||||||
|
user_updated?: DirectusUser | string | null;
|
||||||
|
delta?: 'json' | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DirectusExtension {
|
||||||
|
enabled?: boolean;
|
||||||
|
/** @primaryKey */
|
||||||
|
id: string;
|
||||||
|
folder?: string;
|
||||||
|
source?: string;
|
||||||
|
bundle?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Schema {
|
||||||
|
article: Article[];
|
||||||
|
article_tag: ArticleTag[];
|
||||||
|
article_translations: ArticleTranslation[];
|
||||||
|
global: Global;
|
||||||
|
global_translations: GlobalTranslation[];
|
||||||
|
languages: Language[];
|
||||||
|
tag: Tag[];
|
||||||
|
directus_access: DirectusAccess[];
|
||||||
|
directus_activity: DirectusActivity[];
|
||||||
|
directus_collections: DirectusCollection[];
|
||||||
|
directus_comments: DirectusComment[];
|
||||||
|
directus_fields: DirectusField[];
|
||||||
|
directus_files: DirectusFile[];
|
||||||
|
directus_folders: DirectusFolder[];
|
||||||
|
directus_migrations: DirectusMigration[];
|
||||||
|
directus_permissions: DirectusPermission[];
|
||||||
|
directus_policies: DirectusPolicy[];
|
||||||
|
directus_presets: DirectusPreset[];
|
||||||
|
directus_relations: DirectusRelation[];
|
||||||
|
directus_revisions: DirectusRevision[];
|
||||||
|
directus_roles: DirectusRole[];
|
||||||
|
directus_sessions: DirectusSession[];
|
||||||
|
directus_settings: DirectusSettings;
|
||||||
|
directus_users: DirectusUser[];
|
||||||
|
directus_webhooks: DirectusWebhook[];
|
||||||
|
directus_dashboards: DirectusDashboard[];
|
||||||
|
directus_panels: DirectusPanel[];
|
||||||
|
directus_notifications: DirectusNotification[];
|
||||||
|
directus_shares: DirectusShare[];
|
||||||
|
directus_flows: DirectusFlow[];
|
||||||
|
directus_operations: DirectusOperation[];
|
||||||
|
directus_translations: DirectusTranslation[];
|
||||||
|
directus_versions: DirectusVersion[];
|
||||||
|
directus_extensions: DirectusExtension[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CollectionNames {
|
||||||
|
article = 'article',
|
||||||
|
article_tag = 'article_tag',
|
||||||
|
article_translations = 'article_translations',
|
||||||
|
global = 'global',
|
||||||
|
global_translations = 'global_translations',
|
||||||
|
languages = 'languages',
|
||||||
|
tag = 'tag',
|
||||||
|
directus_access = 'directus_access',
|
||||||
|
directus_activity = 'directus_activity',
|
||||||
|
directus_collections = 'directus_collections',
|
||||||
|
directus_comments = 'directus_comments',
|
||||||
|
directus_fields = 'directus_fields',
|
||||||
|
directus_files = 'directus_files',
|
||||||
|
directus_folders = 'directus_folders',
|
||||||
|
directus_migrations = 'directus_migrations',
|
||||||
|
directus_permissions = 'directus_permissions',
|
||||||
|
directus_policies = 'directus_policies',
|
||||||
|
directus_presets = 'directus_presets',
|
||||||
|
directus_relations = 'directus_relations',
|
||||||
|
directus_revisions = 'directus_revisions',
|
||||||
|
directus_roles = 'directus_roles',
|
||||||
|
directus_sessions = 'directus_sessions',
|
||||||
|
directus_settings = 'directus_settings',
|
||||||
|
directus_users = 'directus_users',
|
||||||
|
directus_webhooks = 'directus_webhooks',
|
||||||
|
directus_dashboards = 'directus_dashboards',
|
||||||
|
directus_panels = 'directus_panels',
|
||||||
|
directus_notifications = 'directus_notifications',
|
||||||
|
directus_shares = 'directus_shares',
|
||||||
|
directus_flows = 'directus_flows',
|
||||||
|
directus_operations = 'directus_operations',
|
||||||
|
directus_translations = 'directus_translations',
|
||||||
|
directus_versions = 'directus_versions',
|
||||||
|
directus_extensions = 'directus_extensions'
|
||||||
|
}
|
||||||
9
directus.ts
Normal file
9
directus.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { createDirectus, graphql, staticToken } from "@directus/sdk";
|
||||||
|
import { DIRECTUS_TOKEN } from "astro:env/server";
|
||||||
|
import type { Schema } from "./directus-schema";
|
||||||
|
|
||||||
|
const directus = createDirectus<Schema>("https://directus.yarkov.tech")
|
||||||
|
.with(staticToken(DIRECTUS_TOKEN))
|
||||||
|
.with(graphql());
|
||||||
|
|
||||||
|
export default directus;
|
||||||
10
netlify.toml
10
netlify.toml
@ -1,10 +0,0 @@
|
|||||||
[build]
|
|
||||||
command = "npm run build"
|
|
||||||
publish = "dist"
|
|
||||||
functions = "netlify/functions"
|
|
||||||
|
|
||||||
[[redirects]]
|
|
||||||
from = "/api/*" # simplify all calls to serverless functions
|
|
||||||
to = "/.netlify/functions/:splat" # all function calls will go to this path
|
|
||||||
status = 200 # ok code
|
|
||||||
force = true # ensure to always redirect
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
import type { Handler } from "@netlify/functions";
|
|
||||||
import { redisClient } from "../../redis";
|
|
||||||
|
|
||||||
const getViewsBySlug = async (slug: string): Promise<number> => {
|
|
||||||
let views = 1;
|
|
||||||
const data = await redisClient.get(`views:${slug}`);
|
|
||||||
if (data) views = Number.parseInt(data, 10);
|
|
||||||
if (Number.isNaN(views)) views = 1;
|
|
||||||
return views;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handler: Handler = async (event) => {
|
|
||||||
const slug = event.queryStringParameters?.slug;
|
|
||||||
|
|
||||||
switch (event.httpMethod) {
|
|
||||||
case "GET": {
|
|
||||||
if (!slug) return { statusCode: 400, body: "Bad request." };
|
|
||||||
const views = await getViewsBySlug(slug);
|
|
||||||
return { statusCode: 200, body: views.toString() };
|
|
||||||
}
|
|
||||||
case "POST": {
|
|
||||||
if (!slug) return { statusCode: 400, body: "Bad request." };
|
|
||||||
const views = await redisClient.incr(`views:${slug}`);
|
|
||||||
return { statusCode: 200, body: views.toString() };
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
body: "Unrecognized HTTP Method, must be one of GET/POST",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export { handler };
|
|
||||||
24267
package-lock.json
generated
24267
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@ -17,20 +17,20 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.5",
|
||||||
"@astrojs/rss": "^3.0.0",
|
"@astrojs/node": "^9.5.0",
|
||||||
"@astrojs/sitemap": "^3.0.0",
|
"@astrojs/rss": "^4.0.13",
|
||||||
"@astrojs/solid-js": "^3.0.1",
|
"@astrojs/sitemap": "^3.6.0",
|
||||||
|
"@astrojs/solid-js": "^5.1.3",
|
||||||
|
"@directus/sdk": "^20.1.1",
|
||||||
"@iconify-json/mdi": "^1.2.3",
|
"@iconify-json/mdi": "^1.2.3",
|
||||||
"@netlify/functions": "^2.1.0",
|
"@netlify/functions": "^2.1.0",
|
||||||
"@sindresorhus/slugify": "^2.2.1",
|
"@sindresorhus/slugify": "^2.2.1",
|
||||||
"astro": "^3.2.0",
|
"astro": "^5.15.8",
|
||||||
"astro-icon": "^1.1.5",
|
"astro-icon": "^1.1.5",
|
||||||
"decap-cms-app": "^3.6.3",
|
|
||||||
"hast-util-to-string": "^3.0.0",
|
"hast-util-to-string": "^3.0.0",
|
||||||
"hastscript": "^8.0.0",
|
"hastscript": "^8.0.0",
|
||||||
"html-escaper": "^3.0.3",
|
"html-escaper": "^3.0.3",
|
||||||
"ioredis": "^5.3.2",
|
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"quicklink": "^3.0.1",
|
"quicklink": "^3.0.1",
|
||||||
"rehype-autolink-headings": "^7.0.0",
|
"rehype-autolink-headings": "^7.0.0",
|
||||||
|
|||||||
@ -1,249 +0,0 @@
|
|||||||
editor:
|
|
||||||
preview: false
|
|
||||||
publish_mode: editorial_workflow
|
|
||||||
site_url: https://yarkov.tech
|
|
||||||
display_url: https://yarkov.tech
|
|
||||||
i18n:
|
|
||||||
locales: [ru, en]
|
|
||||||
default_locale: ru
|
|
||||||
structure: multiple_folders
|
|
||||||
locale: en
|
|
||||||
backend:
|
|
||||||
name: github
|
|
||||||
repo: sergeyyarkov/yarkov.tech
|
|
||||||
branch: main
|
|
||||||
media_folder: public/media
|
|
||||||
public_folder: "/media"
|
|
||||||
collections:
|
|
||||||
- name: articles
|
|
||||||
label: Публикации
|
|
||||||
label_singular: публикацию
|
|
||||||
description: Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой
|
|
||||||
folder: src/content/blog
|
|
||||||
slug: "{{slug}}"
|
|
||||||
create: true
|
|
||||||
delete: true
|
|
||||||
i18n: true
|
|
||||||
fields:
|
|
||||||
- name: coverImage
|
|
||||||
widget: image
|
|
||||||
label: Постер
|
|
||||||
required: false
|
|
||||||
i18n: true
|
|
||||||
- name: draft
|
|
||||||
label: В черновике
|
|
||||||
widget: boolean
|
|
||||||
hint: Опубликована ли статья
|
|
||||||
i18n: true
|
|
||||||
required: false
|
|
||||||
- name: author
|
|
||||||
widget: relation
|
|
||||||
value_field: "{{firstName}} {{lastName}}"
|
|
||||||
search_fields:
|
|
||||||
- firstName
|
|
||||||
- lastName
|
|
||||||
collection: authors
|
|
||||||
label: Автор
|
|
||||||
i18n: true
|
|
||||||
- name: title
|
|
||||||
widget: string
|
|
||||||
label: Заголовок
|
|
||||||
i18n: true
|
|
||||||
- name: description
|
|
||||||
widget: string
|
|
||||||
label: Описание
|
|
||||||
i18n: true
|
|
||||||
- name: tags
|
|
||||||
widget: relation
|
|
||||||
label: Тэги
|
|
||||||
collection: tags
|
|
||||||
multiple: true
|
|
||||||
value_field: title
|
|
||||||
search_fields:
|
|
||||||
- title
|
|
||||||
i18n: true
|
|
||||||
- name: pubDate
|
|
||||||
widget: datetime
|
|
||||||
label: Дата публикации
|
|
||||||
picker_utc: true
|
|
||||||
i18n: true
|
|
||||||
- name: updatedDate
|
|
||||||
widget: datetime
|
|
||||||
label: Дата обновления
|
|
||||||
picker_utc: true
|
|
||||||
i18n: true
|
|
||||||
- name: body
|
|
||||||
widget: markdown
|
|
||||||
label: Содержание
|
|
||||||
i18n: true
|
|
||||||
- name: tags
|
|
||||||
label: Тэги
|
|
||||||
label_singular: тэг
|
|
||||||
description: Используются для тегирования контента
|
|
||||||
folder: src/content/tags
|
|
||||||
create: true
|
|
||||||
delete: true
|
|
||||||
fields:
|
|
||||||
- name: title
|
|
||||||
widget: string
|
|
||||||
label: Название
|
|
||||||
required: true
|
|
||||||
- name: authors
|
|
||||||
label: Авторы
|
|
||||||
label_singular: автора
|
|
||||||
description: На странице статьи отображается имя автора
|
|
||||||
slug: "{{firstName}}-{{lastName}}"
|
|
||||||
folder: src/content/authors
|
|
||||||
create: true
|
|
||||||
delete: true
|
|
||||||
i18n: true
|
|
||||||
fields:
|
|
||||||
- label: Имя
|
|
||||||
name: firstName
|
|
||||||
widget: string
|
|
||||||
required: true
|
|
||||||
i18n: true
|
|
||||||
- label: Фамилия
|
|
||||||
name: lastName
|
|
||||||
widget: string
|
|
||||||
required: true
|
|
||||||
i18n: true
|
|
||||||
- label: Об авторе
|
|
||||||
name: body
|
|
||||||
widget: markdown
|
|
||||||
required: false
|
|
||||||
i18n: true
|
|
||||||
- name: projects
|
|
||||||
label: Проекты
|
|
||||||
label_singular: проект
|
|
||||||
description: Основные проекты, которые были разработаны в выложены в открытый
|
|
||||||
доступ
|
|
||||||
folder: src/content/projects
|
|
||||||
create: true
|
|
||||||
delete: true
|
|
||||||
i18n: true
|
|
||||||
fields:
|
|
||||||
- name: icon
|
|
||||||
label: Иконка
|
|
||||||
widget: string
|
|
||||||
required: true
|
|
||||||
i18n: true
|
|
||||||
- name: title
|
|
||||||
label: Название
|
|
||||||
widget: string
|
|
||||||
required: true
|
|
||||||
i18n: true
|
|
||||||
- name: year
|
|
||||||
label: В каком году разработал
|
|
||||||
widget: number
|
|
||||||
required: true
|
|
||||||
i18n: true
|
|
||||||
- name: description
|
|
||||||
label: Описание
|
|
||||||
widget: string
|
|
||||||
required: true
|
|
||||||
i18n: true
|
|
||||||
- name: sourceUrl
|
|
||||||
label: Ссылка на исходник
|
|
||||||
widget: string
|
|
||||||
required: true
|
|
||||||
i18n: true
|
|
||||||
- name: demoUrl
|
|
||||||
label: Ссылка на демонстрацию
|
|
||||||
widget: string
|
|
||||||
required: false
|
|
||||||
i18n: true
|
|
||||||
- name: articleUrl
|
|
||||||
label: Ссылка на статью
|
|
||||||
widget: string
|
|
||||||
required: false
|
|
||||||
i18n: true
|
|
||||||
- name: skills
|
|
||||||
label: Навыки
|
|
||||||
label_singular: навык
|
|
||||||
folder: src/content/skills
|
|
||||||
create: true
|
|
||||||
delete: true
|
|
||||||
description: Навыки отображаются на странице Обо мне
|
|
||||||
i18n: true
|
|
||||||
fields:
|
|
||||||
- name: title
|
|
||||||
label: Категория
|
|
||||||
widget: string
|
|
||||||
required: true
|
|
||||||
i18n: true
|
|
||||||
- name: list
|
|
||||||
label: Записи
|
|
||||||
required: true
|
|
||||||
widget: list
|
|
||||||
allow_add: true
|
|
||||||
min: 1
|
|
||||||
i18n: true
|
|
||||||
fields:
|
|
||||||
- name: icon
|
|
||||||
label: Иконка
|
|
||||||
widget: string
|
|
||||||
i18n: true
|
|
||||||
required: false
|
|
||||||
- name: title
|
|
||||||
label: Название
|
|
||||||
widget: string
|
|
||||||
i18n: true
|
|
||||||
- name: settings
|
|
||||||
label: Настройки
|
|
||||||
files:
|
|
||||||
- name: siteconfig
|
|
||||||
label: Настройки веб-сайта
|
|
||||||
file: siteconfig.json
|
|
||||||
fields:
|
|
||||||
- label: Название сайта
|
|
||||||
name: name
|
|
||||||
widget: string
|
|
||||||
hint: Отображается в шапке.
|
|
||||||
default: yarkov.tech
|
|
||||||
- label: Автор веб-сайта
|
|
||||||
name: author
|
|
||||||
widget: string
|
|
||||||
hint: Отображается в подвале.
|
|
||||||
- label: Глобальный заголовок
|
|
||||||
name: globalTitle
|
|
||||||
widget: string
|
|
||||||
hint: Отображется во вкладке браузера.
|
|
||||||
- label: В разработке
|
|
||||||
name: isSiteUnderConstruction
|
|
||||||
widget: boolean
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
hint: Отображает баннер на всех старницах о том, что веб-сайт находится в
|
|
||||||
разработке
|
|
||||||
- label: Счетчик Яндекс.Метрики
|
|
||||||
name: ymCounter
|
|
||||||
widget: number
|
|
||||||
hint: "Например: 90034656"
|
|
||||||
required: false
|
|
||||||
- label: Статьи
|
|
||||||
name: articles
|
|
||||||
widget: object
|
|
||||||
fields:
|
|
||||||
- name: recentLimit
|
|
||||||
label: Лимит на отображение
|
|
||||||
widget: number
|
|
||||||
hint: Кол-во публикаций на гл.странице
|
|
||||||
default: 5
|
|
||||||
- label: Контакты
|
|
||||||
name: contacts
|
|
||||||
widget: object
|
|
||||||
required: false
|
|
||||||
fields:
|
|
||||||
- label: Telegram
|
|
||||||
name: telegram
|
|
||||||
widget: string
|
|
||||||
required: false
|
|
||||||
- label: Почта
|
|
||||||
name: email
|
|
||||||
widget: string
|
|
||||||
required: false
|
|
||||||
- label: Github
|
|
||||||
name: github
|
|
||||||
widget: string
|
|
||||||
required: false
|
|
||||||
9
redis.ts
9
redis.ts
@ -1,9 +0,0 @@
|
|||||||
import Redis from "ioredis";
|
|
||||||
|
|
||||||
const redisClient = new Redis({
|
|
||||||
host: process.env.REDIS_HOST,
|
|
||||||
port: Number.parseInt(process.env.REDIS_PORT || "6379", 10),
|
|
||||||
password: process.env.REDIS_PASSWORD,
|
|
||||||
});
|
|
||||||
|
|
||||||
export { redisClient };
|
|
||||||
@ -4,18 +4,18 @@ import { URL_BLOG_PREFIX } from "@root/constants";
|
|||||||
import "./ArticleItem.scss";
|
import "./ArticleItem.scss";
|
||||||
|
|
||||||
export type ArticleItemProps = {
|
export type ArticleItemProps = {
|
||||||
id: string;
|
id: number;
|
||||||
title: string;
|
title: string;
|
||||||
pubDate: Date;
|
pubDate: Date;
|
||||||
pageLang: string;
|
pageLang: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArticleItem: Component<ArticleItemProps> = (props) => {
|
const ArticleItem: Component<ArticleItemProps> = (props) => {
|
||||||
const articleLang = props.id.split("/")[0];
|
const articleLang = "ru";
|
||||||
const href = createRelativeArticleUrl(
|
// const href = createRelativeArticleUrl(
|
||||||
{ id: props.id, title: props.title, pubDate: props.pubDate },
|
// { id: props.id, title: props.title, pubDate: props.pubDate },
|
||||||
URL_BLOG_PREFIX
|
// URL_BLOG_PREFIX
|
||||||
);
|
// );
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article
|
<article
|
||||||
@ -24,14 +24,14 @@ const ArticleItem: Component<ArticleItemProps> = (props) => {
|
|||||||
itemtype="http://schema.org/BlogPosting"
|
itemtype="http://schema.org/BlogPosting"
|
||||||
class="article-item"
|
class="article-item"
|
||||||
>
|
>
|
||||||
<a href={href}>
|
<a href={""}>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<h3 itemprop="headline">{props.title}</h3>
|
<h3 itemprop="headline">{props.title}</h3>
|
||||||
{articleLang !== props.pageLang && <sup>{articleLang.toLocaleUpperCase()}</sup>}
|
{articleLang !== props.pageLang && <sup>{articleLang.toLocaleUpperCase()}</sup>}
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<time datetime={props.pubDate.toISOString()}>
|
<time datetime={`props.pubDate.toISOString()`}>
|
||||||
{createDateFormatter(props.pageLang).format(props.pubDate)}
|
{/* {createDateFormatter(props.pageLang).format(props.pubDate)} */}
|
||||||
</time>
|
</time>
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -9,13 +9,13 @@ import TranslatedLink from "@components/TranslatedLink.astro";
|
|||||||
---
|
---
|
||||||
|
|
||||||
<header {...IS_SITE_UNDER_CONSTRUCTION ? { style: "top: 45px;" } : {}}>
|
<header {...IS_SITE_UNDER_CONSTRUCTION ? { style: "top: 45px;" } : {}}>
|
||||||
<div class="container">
|
<div class='container'>
|
||||||
<div class="logo">
|
<div class='logo'>
|
||||||
<TranslatedLink to="/">
|
<TranslatedLink to='/'>
|
||||||
{SITE_METADATA.name}
|
{SITE_METADATA.name}
|
||||||
</TranslatedLink>
|
</TranslatedLink>
|
||||||
</div>
|
</div>
|
||||||
<nav itemscope itemtype="http://schema.org/SiteNavigationElement">
|
<nav itemscope itemtype='http://schema.org/SiteNavigationElement'>
|
||||||
<ul>
|
<ul>
|
||||||
{
|
{
|
||||||
SITE_METADATA.nav.map((link) => (
|
SITE_METADATA.nav.map((link) => (
|
||||||
@ -23,8 +23,7 @@ import TranslatedLink from "@components/TranslatedLink.astro";
|
|||||||
<TranslatedLink
|
<TranslatedLink
|
||||||
itemprop={"url"}
|
itemprop={"url"}
|
||||||
to={link.url}
|
to={link.url}
|
||||||
class:list={[{ active: Astro.url.pathname.includes(link.url) }]}
|
class:list={[{ active: Astro.url.pathname.includes(link.url) }]}>
|
||||||
>
|
|
||||||
<UIString key={link.key} />
|
<UIString key={link.key} />
|
||||||
</TranslatedLink>
|
</TranslatedLink>
|
||||||
</li>
|
</li>
|
||||||
@ -33,23 +32,23 @@ import TranslatedLink from "@components/TranslatedLink.astro";
|
|||||||
</ul>
|
</ul>
|
||||||
{
|
{
|
||||||
SITE_METADATA.contacts && (
|
SITE_METADATA.contacts && (
|
||||||
<div class="contacts">
|
<div class='contacts'>
|
||||||
<a target="_blank" href={"mailto:" + SITE_METADATA.contacts.email}>
|
<a target='_blank' href={"mailto:" + SITE_METADATA.contacts.email}>
|
||||||
Email
|
Email
|
||||||
<Icon name="email" width="18" height="18" />
|
<Icon name='email' width='18' height='18' />
|
||||||
</a>
|
</a>
|
||||||
<a target="_blank" href={SITE_METADATA.contacts.github}>
|
<a target='_blank' href={SITE_METADATA.contacts.github}>
|
||||||
Github
|
Github
|
||||||
<Icon name="github_filled" width="18" height="18" />
|
<Icon name='github_filled' width='18' height='18' />
|
||||||
</a>
|
</a>
|
||||||
<a target="_blank" href={SITE_METADATA.contacts.telegram}>
|
<a target='_blank' href={SITE_METADATA.contacts.telegram}>
|
||||||
Telegram
|
Telegram
|
||||||
<Icon name="telegram" width="18" height="18" />
|
<Icon name='telegram' width='18' height='18' />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div class="flex settings">
|
<div class='flex settings'>
|
||||||
<LanguageSelect />
|
<LanguageSelect />
|
||||||
<ThemeSwitcher client:visible />
|
<ThemeSwitcher client:visible />
|
||||||
</div>
|
</div>
|
||||||
@ -58,7 +57,7 @@ import TranslatedLink from "@components/TranslatedLink.astro";
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang='scss'>
|
||||||
header {
|
header {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
---
|
---
|
||||||
import type { CollectionEntry } from "astro:content";
|
|
||||||
import siteconfig from "siteconfig.json";
|
import siteconfig from "siteconfig.json";
|
||||||
import { getLanguageFromURL } from "@i18n/utils";
|
import { getLanguageFromURL } from "@i18n/utils";
|
||||||
import * as utils from "@utils/index";
|
import * as utils from "@utils/index";
|
||||||
@ -7,13 +6,32 @@ import ArticleItem from "@components/ArticleItem";
|
|||||||
import EmptyList from "@components/EmptyList.astro";
|
import EmptyList from "@components/EmptyList.astro";
|
||||||
import UIString from "@components/UIString.astro";
|
import UIString from "@components/UIString.astro";
|
||||||
import TranslatedLink from "@components/TranslatedLink.astro";
|
import TranslatedLink from "@components/TranslatedLink.astro";
|
||||||
|
import directus from "@/directus";
|
||||||
|
import type { ArticleTranslation } from "@/directus-schema";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
articles: CollectionEntry<"blog">[];
|
|
||||||
length?: number | undefined;
|
length?: number | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { articles, length = siteconfig.articles.recentLimit } = Astro.props as Props;
|
type RecentArticlesQueryResult = {
|
||||||
|
article_translations: Pick<ArticleTranslation, "id" | "title" | "pub_date">[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await directus.query<RecentArticlesQueryResult>(`
|
||||||
|
query Article_translations {
|
||||||
|
article_translations {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
pub_date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const { article_translations: articles } = data;
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
const { length = siteconfig.articles.recentLimit } = Astro.props as Props;
|
||||||
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const filtered = utils.removeDuplicates(articles.slice(0, length), pageLang);
|
const filtered = utils.removeDuplicates(articles.slice(0, length), pageLang);
|
||||||
const isEmpty = articles.length === 0;
|
const isEmpty = articles.length === 0;
|
||||||
@ -24,18 +42,18 @@ const isEmpty = articles.length === 0;
|
|||||||
<h2>
|
<h2>
|
||||||
<UIString key={"recentArticles.heading"} />
|
<UIString key={"recentArticles.heading"} />
|
||||||
</h2>
|
</h2>
|
||||||
<TranslatedLink to="/articles/">
|
<TranslatedLink to='/articles/'>
|
||||||
<UIString key={"recentArticles.all"} />
|
<UIString key={"recentArticles.all"} />
|
||||||
</TranslatedLink>
|
</TranslatedLink>
|
||||||
</div>
|
</div>
|
||||||
<div itemscope itemtype="http://schema.org/Blog">
|
<div itemscope itemtype='http://schema.org/Blog'>
|
||||||
{
|
{
|
||||||
!isEmpty ? (
|
!isEmpty ? (
|
||||||
filtered.map((article) => (
|
articles.map((article) => (
|
||||||
<ArticleItem
|
<ArticleItem
|
||||||
id={article.id}
|
id={article.id}
|
||||||
title={article.data.title}
|
title={article.title}
|
||||||
pubDate={article.data.pubDate}
|
pubDate={article.pub_date}
|
||||||
pageLang={pageLang}
|
pageLang={pageLang}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
@ -46,7 +64,7 @@ const isEmpty = articles.length === 0;
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang='scss'>
|
||||||
section {
|
section {
|
||||||
& > div:nth-child(1) {
|
& > div:nth-child(1) {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
import type { CollectionEntry } from "astro:content";
|
import type { CollectionEntry } from "astro:content";
|
||||||
import { getCollection } from "astro:content";
|
import { getCollection, getEntry, render } from "astro:content";
|
||||||
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ArticleLayout from "@root/layouts/article.astro";
|
import ArticleLayout from "@root/layouts/article.astro";
|
||||||
import { createRelativeArticleUrl, removeLanguageCodeFromPath } from "@root/utils";
|
import { createRelativeArticleUrl, removeLanguageCodeFromPath } from "@root/utils";
|
||||||
|
|
||||||
@ -9,37 +10,41 @@ import { createRelativeArticleUrl, removeLanguageCodeFromPath } from "@root/util
|
|||||||
* The final url will be `http://website.com/blog/2023-01-01/my-article` with default language
|
* The final url will be `http://website.com/blog/2023-01-01/my-article` with default language
|
||||||
* and `http://website.com/{language-code}/blog/2023-01-01/my-article` with other languages.
|
* and `http://website.com/{language-code}/blog/2023-01-01/my-article` with other languages.
|
||||||
*/
|
*/
|
||||||
export async function getStaticPaths() {
|
// export async function getStaticPaths() {
|
||||||
const collection = await getCollection("blog", (e) => !e.data.draft);
|
// const collection = await getCollection("blog", (e) => !e.data.draft);
|
||||||
return collection.map((article) => {
|
// return collection.map((article) => {
|
||||||
const {
|
// const {
|
||||||
id,
|
// id,
|
||||||
data: { title, pubDate },
|
// data: { title, pubDate },
|
||||||
} = article;
|
// } = article;
|
||||||
const url = createRelativeArticleUrl({ id, title, pubDate });
|
// const url = createRelativeArticleUrl({ id, title, pubDate });
|
||||||
return {
|
// return {
|
||||||
params: { blog: url.slice(1) },
|
// params: { blog: url.slice(1) },
|
||||||
props: {
|
// props: {
|
||||||
article,
|
// article,
|
||||||
availableOnLangs: collection.filter((e) =>
|
// availableOnLangs: collection.filter((e) =>
|
||||||
e.id.includes(removeLanguageCodeFromPath(article.id))
|
// e.id.includes(removeLanguageCodeFromPath(article.id))
|
||||||
),
|
// ),
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
type Props = { article: CollectionEntry<"blog">; availableOnLangs: CollectionEntry<"blog">[] };
|
type Props = { article: CollectionEntry<"blog">; availableOnLangs: CollectionEntry<"blog">[] };
|
||||||
|
|
||||||
const { Content, headings } = await Astro.props.article.render();
|
const { blog } = Astro.params as { blog: string };
|
||||||
const { article, availableOnLangs } = Astro.props;
|
const collection = await getCollection("blog", (e) => !e.data.draft);
|
||||||
|
const slug = blog.split("/").at(-1);
|
||||||
|
const article = collection.find(
|
||||||
|
(a) => slugify(a.slug.split("/").at(-1) || "", { lowercase: true }) === slug
|
||||||
|
);
|
||||||
|
|
||||||
|
if (article === undefined) return Astro.redirect("/404");
|
||||||
|
|
||||||
|
const { Content, headings } = await render(article);
|
||||||
|
// const { article, availableOnLangs } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<ArticleLayout
|
<ArticleLayout lang={article.id.split("/")[0]} article={article} headings={headings}>
|
||||||
lang={article.id.split("/")[0]}
|
|
||||||
article={article}
|
|
||||||
headings={headings}
|
|
||||||
availableOnLangs={availableOnLangs}
|
|
||||||
>
|
|
||||||
<Content />
|
<Content />
|
||||||
</ArticleLayout>
|
</ArticleLayout>
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<meta name="robots" content="noindex" />
|
|
||||||
<link href="/admin/config.yml" type="text/yaml" rel="cms-config-url" />
|
|
||||||
<title>Content Manager</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src="https://unpkg.com/decap-cms@^3.1.2/dist/decap-cms.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -5,13 +5,27 @@ import * as utils from "@utils/index";
|
|||||||
import PageLayout from "@layouts/default.astro";
|
import PageLayout from "@layouts/default.astro";
|
||||||
import PageHeading from "@components/PageHeading.astro";
|
import PageHeading from "@components/PageHeading.astro";
|
||||||
import ArticlesList from "@root/components/ArticlesList";
|
import ArticlesList from "@root/components/ArticlesList";
|
||||||
|
import directus from "@/directus";
|
||||||
|
|
||||||
const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const articles = await getCollection("blog", (e) => !e.data.draft);
|
// const articles = await getCollection("blog", (e) => !e.data.draft);
|
||||||
const tags = Array.from(new Set((await getCollection("tags")).map((t) => t.data.title)));
|
// const filtered = utils.removeDuplicates(articles, pageLang);
|
||||||
const filtered = utils.removeDuplicates(articles, pageLang);
|
|
||||||
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
||||||
|
const tags = (await directus.query(`query Tag { tag { title } }`)).tag.map((t) => t.title);
|
||||||
|
|
||||||
|
const { article_translations } = await directus.query(`
|
||||||
|
query Article_translations {
|
||||||
|
article_translations {
|
||||||
|
languages_code {
|
||||||
|
code
|
||||||
|
}
|
||||||
|
slug
|
||||||
|
status
|
||||||
|
pub_date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout
|
<PageLayout
|
||||||
@ -25,7 +39,7 @@ const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
|||||||
<ArticlesList
|
<ArticlesList
|
||||||
client:load
|
client:load
|
||||||
i18n={{ "articles.empty": t("articles.empty"), "input.search": t("input.search") }}
|
i18n={{ "articles.empty": t("articles.empty"), "input.search": t("input.search") }}
|
||||||
articles={utils.formatToArticleBlocks(filtered)}
|
articles={utils.formatToArticleBlocks(article_translations)}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
pageLang={pageLang}
|
pageLang={pageLang}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -11,9 +11,6 @@ import { getLanguageFromURL, useTranslation } from "@root/i18n/utils";
|
|||||||
|
|
||||||
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
const articles = (await getCollection("blog", (e) => !e.data.draft)).sort(
|
|
||||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
|
|
||||||
);
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout description={t("site.description")} lang={pageLang}>
|
<PageLayout description={t("site.description")} lang={pageLang}>
|
||||||
@ -26,11 +23,11 @@ const articles = (await getCollection("blog", (e) => !e.data.draft)).sort(
|
|||||||
<p><UIString key={"splash.description"} /></p>
|
<p><UIString key={"splash.description"} /></p>
|
||||||
</SplashHeading>
|
</SplashHeading>
|
||||||
<SplashLinks>
|
<SplashLinks>
|
||||||
<SplashLink icon="information" href="/about/"><UIString key={"splash.about"} /></SplashLink>
|
<SplashLink icon='information' href='/about/'><UIString key={"splash.about"} /></SplashLink>
|
||||||
<SplashLink icon="email_filled" href="/contacts/">
|
<SplashLink icon='email_filled' href='/contacts/'>
|
||||||
<UIString key={"splash.contact"} />
|
<UIString key={"splash.contact"} />
|
||||||
</SplashLink>
|
</SplashLink>
|
||||||
</SplashLinks>
|
</SplashLinks>
|
||||||
</Splash>
|
</Splash>
|
||||||
<RecentArticles articles={articles} />
|
<RecentArticles />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
|||||||
@ -48,13 +48,13 @@ export function createRelativeArticleUrl(
|
|||||||
export function formatToArticleBlocks(articles: CollectionEntry<"blog">[]) {
|
export function formatToArticleBlocks(articles: CollectionEntry<"blog">[]) {
|
||||||
const blocks: Record<string, any[]> = {};
|
const blocks: Record<string, any[]> = {};
|
||||||
articles.forEach((a) => {
|
articles.forEach((a) => {
|
||||||
const year = a.data.pubDate.getFullYear();
|
const year = new Date(a.pub_date).getFullYear();
|
||||||
if (!blocks[year]) blocks[year] = [];
|
if (!blocks[year]) blocks[year] = [];
|
||||||
blocks[year].push({
|
blocks[year].push({
|
||||||
id: a.id,
|
id: a.id,
|
||||||
title: a.data.title,
|
title: a.title,
|
||||||
tags: a.data.tags,
|
// tags: a.tags,
|
||||||
pubDate: a.data.pubDate,
|
pubDate: a.pub_date,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return blocks;
|
return blocks;
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"@/*": ["./*"],
|
||||||
"@root/*": ["./src/*"],
|
"@root/*": ["./src/*"],
|
||||||
"@styles/*": ["./src/styles/*"],
|
"@styles/*": ["./src/styles/*"],
|
||||||
"@components/*": ["./src/components/*"],
|
"@components/*": ["./src/components/*"],
|
||||||
@ -19,5 +20,5 @@
|
|||||||
"typeRoots": ["./node_modules/@types", "./src/types"],
|
"typeRoots": ["./node_modules/@types", "./src/types"],
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"jsxImportSource": "solid-js"
|
"jsxImportSource": "solid-js"
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user