Compare commits
No commits in common. "master" and "md-articles" have entirely different histories.
master
...
md-article
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
tmp/
|
||||
build/
|
||||
.vscode/
|
||||
a.out
|
||||
test_*
|
||||
**/exif.?
|
24
.vscode/launch.json
vendored
@ -1,24 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C makefile launch",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"targetArchitecture": "x64",
|
||||
"program": "${workspaceRoot}/build/server",
|
||||
"args": ["5000"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceRoot}",
|
||||
"environment": [],
|
||||
"externalConsole": true,
|
||||
"linux": {
|
||||
"MIMode": "gdb",
|
||||
},
|
||||
"miDebuggerArgs": ""
|
||||
},
|
||||
]
|
||||
}
|
5
.vscode/settings.json
vendored
@ -1,5 +0,0 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"utils.h": "c"
|
||||
}
|
||||
}
|
12
.vscode/tasks.json
vendored
@ -1,12 +0,0 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build makefile project",
|
||||
"type": "shell",
|
||||
"command": "make"
|
||||
}
|
||||
]
|
||||
}
|
21
Dockerfile
@ -1,21 +0,0 @@
|
||||
FROM alpine as builder
|
||||
|
||||
RUN apk add --no-cache build-base
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY Makefile .
|
||||
COPY src src/
|
||||
COPY include include/
|
||||
|
||||
RUN make
|
||||
|
||||
FROM alpine
|
||||
|
||||
WORKDIR /srv
|
||||
|
||||
COPY static static/
|
||||
|
||||
COPY --from=builder /app/build/server .
|
||||
|
||||
CMD ["./server", "5000"]
|
@ -9,7 +9,7 @@
|
||||
|
||||
#include "../../include/file_op/file.h"
|
||||
#include "../../include/utils_op/utils.h"
|
||||
#include "../../include/articles_p/process_md.h"
|
||||
#include "../../include/articles_op/process_md.h"
|
||||
|
||||
#define LINE_LENGTH 512
|
||||
|
||||
@ -28,6 +28,5 @@ typedef struct
|
||||
|
||||
int list_articles(article_info **articles);
|
||||
long get_article_contents(article_info *article);
|
||||
void free_article_info_arr(article_info **articles, int length);
|
||||
|
||||
#endif
|
@ -1,34 +0,0 @@
|
||||
#ifndef _LIST_GAL_H
|
||||
#define _LIST_GAL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct {
|
||||
char *description;
|
||||
char *path;
|
||||
char **tags;
|
||||
time_t date_taken;
|
||||
short int rating;
|
||||
} img_t;
|
||||
|
||||
typedef struct album_s
|
||||
{
|
||||
char *title;
|
||||
img_t *images;
|
||||
int img_am;
|
||||
struct album_s *next;
|
||||
} gallery_t;
|
||||
|
||||
gallery_t *get_album_list();
|
||||
gallery_t *new_album_item(char *title);
|
||||
void free_albums_list(gallery_t *albums_list);
|
||||
img_t new_img_item(char *path);
|
||||
void free_img_item(img_t img);
|
||||
int get_album_imgs(img_t **images_arr, int *size, char *title);
|
||||
char *gen_gallery_html();
|
||||
|
||||
#define GALLERY_ROOT "static/gallery/albums/"
|
||||
|
||||
#endif
|
@ -1,22 +0,0 @@
|
||||
#ifndef _PROJECTS_H
|
||||
#define _PROJECTS_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define LINE_LENGTH 512
|
||||
|
||||
typedef struct {
|
||||
char *title;
|
||||
char *description;
|
||||
char *lang;
|
||||
char *license;
|
||||
char *url;
|
||||
} project_t;
|
||||
|
||||
ssize_t read_list(project_t **list);
|
||||
char *gen_project_html();
|
||||
void free_proj_list(project_t **list, int len);
|
||||
|
||||
#endif
|
@ -5,14 +5,11 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
|
||||
void err_msg(char *msg);
|
||||
char *concat_to_front(char **str1, char *str2);
|
||||
char *get_status_message(int status_code);
|
||||
char *to_lower(char *str);
|
||||
char *trim(char *str);
|
||||
char *repair_spaces(char *str);
|
||||
ssize_t get_dir_list(char ***dir_list, char *path);
|
||||
|
||||
#endif
|
@ -1,8 +1 @@
|
||||
Webserver in c. !Experimental!
|
||||
|
||||
Deployment:
|
||||
|
||||
Docker and Heroku deployment are supported. To do Heroku deployment use https://github.com/heroku/heroku-buildpack-c buildpack. For Docker just build container image and run it exposing port:
|
||||
|
||||
docker build -t c-dmitriy.icu .
|
||||
docker run -dp 5000:5000 c-dmitriy.icu
|
@ -1,8 +1,8 @@
|
||||
#include "../../include/articles_p/article.h"
|
||||
#include "../../include/articles_op/article.h"
|
||||
|
||||
int list_articles(article_info **articles)
|
||||
{
|
||||
FILE *file = fopen("./static/articles/list.db", "r");
|
||||
FILE *file = fopen("./static/articles_list.db", "r");
|
||||
if (file == NULL)
|
||||
{
|
||||
perror("Couldn't open db file");
|
||||
@ -43,17 +43,11 @@ int list_articles(article_info **articles)
|
||||
if (strlen(rest) > 0)
|
||||
(*articles)[articles_amount - 1].title = strdup(rest);
|
||||
else
|
||||
(*articles)[articles_amount - 1].title = strdup("No title");
|
||||
(*articles)[articles_amount - 1].title = "No title";
|
||||
|
||||
(*articles)[articles_amount - 1].content = malloc(0);
|
||||
|
||||
free(tmp);
|
||||
|
||||
// printf("Found article \"%s\" posted on %ld\n", (*articles)[articles_amount - 1].time, (*articles)[articles_amount - 1].title);
|
||||
printf("%ld - \"%s\"\n", (*articles)[articles_amount - 1].time, (*articles)[articles_amount - 1].title);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
free(buff);
|
||||
|
||||
return articles_amount;
|
||||
@ -65,11 +59,10 @@ long get_article_contents(article_info *article)
|
||||
if (strcmp(article->title, "No title") == 0)
|
||||
{
|
||||
int line_length = snprintf(NULL, 0, "%ld", article->time) + 1;
|
||||
free(name);
|
||||
name = malloc(line_length);
|
||||
if (name == NULL)
|
||||
{
|
||||
article->content = strdup("500");
|
||||
article->content = "500";
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -80,18 +73,17 @@ long get_article_contents(article_info *article)
|
||||
char *path = malloc(line_length);
|
||||
if (path == NULL)
|
||||
{
|
||||
article->content = strdup("500");
|
||||
article->content = "500";
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(path, line_length, "./static/articles/%s/%s.md", name, name);
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
free(path);
|
||||
if (file == NULL)
|
||||
{
|
||||
perror("Couldn't open article file");
|
||||
article->content = strdup("404");
|
||||
article->content = "404";
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -104,23 +96,9 @@ long get_article_contents(article_info *article)
|
||||
|
||||
article->content[article->length] = '\0';
|
||||
|
||||
free(name);
|
||||
fclose(file);
|
||||
|
||||
return article->length;
|
||||
}
|
||||
|
||||
void free_article_info_arr(article_info **articles, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
free((*articles)[i].title);
|
||||
free((*articles)[i].content);
|
||||
}
|
||||
|
||||
free(*articles);
|
||||
}
|
||||
|
||||
// /* Only for testing purposes */
|
||||
// int main()
|
||||
// {
|
@ -1,6 +1,5 @@
|
||||
#include "../../include/articles_p/html.h"
|
||||
#include "../../include/articles_p/article.h"
|
||||
#include "../../include/utils_op/utils.h"
|
||||
#include "../../include/articles_op/html.h"
|
||||
#include "../../include/articles_op/article.h"
|
||||
|
||||
int gen_html_article(article_info article, char **out)
|
||||
{
|
||||
@ -20,12 +19,12 @@ int gen_html_article(article_info article, char **out)
|
||||
template_str[i] = fgetc(template);
|
||||
template_str[template_size] = '\0';
|
||||
|
||||
*out = malloc(template_size + article.length + 1);
|
||||
|
||||
char *content;
|
||||
|
||||
process_md(article, &content);
|
||||
|
||||
article.title = repair_spaces(article.title);
|
||||
|
||||
int line_length = snprintf(NULL, 0, template_str, article.title, article.title, content) + 1;
|
||||
*out = malloc(line_length);
|
||||
if (*out == NULL)
|
||||
@ -36,9 +35,7 @@ int gen_html_article(article_info article, char **out)
|
||||
|
||||
snprintf(*out, line_length, template_str, article.title, article.title, content);
|
||||
|
||||
fclose(template);
|
||||
free(template_str);
|
||||
free(content);
|
||||
|
||||
return line_length;
|
||||
}
|
||||
@ -56,8 +53,6 @@ int gen_html_article_list(article_info *articles, int n, char **out)
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
articles[i].title = repair_spaces(articles[i].title);
|
||||
|
||||
int line_length = snprintf(NULL, 0, "<li><a href=\"/article/%d\" >%s</a></li>\n", i, articles[i].title) + 1;
|
||||
|
||||
insert = malloc(line_length);
|
||||
@ -71,13 +66,13 @@ int gen_html_article_list(article_info *articles, int n, char **out)
|
||||
snprintf(insert, line_length, "<li><a href=\"/article/%d\" >%s</a></li>\n", i, articles[i].title);
|
||||
|
||||
strcat(*out, insert);
|
||||
|
||||
free(insert);
|
||||
}
|
||||
|
||||
*out = realloc(*out, strlen(*out) + strlen("</ul>") + 1);
|
||||
strcat(*out, "</ul>");
|
||||
|
||||
free(insert);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,60 +1,16 @@
|
||||
#include "../../include/articles_p/process_md.h"
|
||||
#include "../../include/articles_op/process_md.h"
|
||||
|
||||
int process_md(article_info article, char **out)
|
||||
{
|
||||
*out = malloc(1);
|
||||
(*out)[0] = '\0';
|
||||
char *rest = strdup(article.content), *free_rest = rest;
|
||||
char *rest = strdup(article.content);
|
||||
char *buff;
|
||||
|
||||
int is_in_code = 0;
|
||||
int is_in_list = 0;
|
||||
|
||||
while ((buff = strtok_r(rest, "\n", &rest)) != NULL)
|
||||
{
|
||||
if (strncmp(buff, "```", 3) == 0)
|
||||
{
|
||||
char *snippet;
|
||||
|
||||
if (is_in_code)
|
||||
{
|
||||
snippet = "</pre></code>\n";
|
||||
is_in_code = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
snippet = "<pre><code>";
|
||||
is_in_code = 1;
|
||||
}
|
||||
|
||||
char *tmp_out = realloc(*out, strlen(*out) + strlen(snippet) + 1);
|
||||
if (tmp_out == NULL)
|
||||
{
|
||||
perror("Couldn't allocate memory for new element");
|
||||
continue;
|
||||
}
|
||||
*out = tmp_out;
|
||||
strcat(*out, snippet);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_in_code)
|
||||
{
|
||||
char *tmp_out = realloc(*out, strlen(*out) + strlen(buff) + strlen("<br/>") + 1);
|
||||
if (tmp_out == NULL)
|
||||
{
|
||||
perror("Couldn't allocate memory for new element");
|
||||
continue;
|
||||
}
|
||||
*out = tmp_out;
|
||||
|
||||
strcat(*out, buff);
|
||||
strcat(*out, "<br/>");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((buff[0] == '*' && buff[1] == ' ') || (buff[0] == '-' && buff[1] == ' ') || (buff[0] == '+' && buff[1] == ' '))
|
||||
{
|
||||
char *begining = "";
|
||||
@ -217,7 +173,9 @@ int process_md(article_info article, char **out)
|
||||
while (buff[i + 2 + n] != ']')
|
||||
n++;
|
||||
|
||||
if (buff[i + 2 + n + 1] == '(') {
|
||||
if (buff[i + 2 + n + 1] != '(')
|
||||
continue;
|
||||
|
||||
int k = 0;
|
||||
while (buff[i + 2 + n + 2 + k] != ')')
|
||||
k++;
|
||||
@ -251,7 +209,6 @@ int process_md(article_info article, char **out)
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (buff[i] == '[')
|
||||
{
|
||||
@ -259,7 +216,9 @@ int process_md(article_info article, char **out)
|
||||
while (buff[i + 1 + n] != ']')
|
||||
n++;
|
||||
|
||||
if (buff[i + 1 + n + 1] == '(') {
|
||||
if (buff[i + 1 + n + 1] != '(')
|
||||
continue;
|
||||
|
||||
int k = 0;
|
||||
while (buff[i + 1 + n + 2 + k] != ')')
|
||||
k++;
|
||||
@ -291,9 +250,10 @@ int process_md(article_info article, char **out)
|
||||
free(href);
|
||||
free(internal_text);
|
||||
|
||||
printf("++%d-%c++\n", i, buff[i]);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len = strlen(*out);
|
||||
char *tmp_out = realloc(*out, len + 2);
|
||||
@ -305,6 +265,8 @@ int process_md(article_info article, char **out)
|
||||
*out = tmp_out;
|
||||
(*out)[len] = buff[i];
|
||||
(*out)[len + 1] = '\0';
|
||||
|
||||
printf("**%d-%c**\n", i, buff[i]);
|
||||
}
|
||||
|
||||
tmp_out = realloc(*out, strlen(*out) + strlen("</p>") + 1);
|
||||
@ -333,7 +295,5 @@ int process_md(article_info article, char **out)
|
||||
}
|
||||
(*out)[strlen(*out) - 1] = '\0';
|
||||
|
||||
free(free_rest);
|
||||
|
||||
return strlen(*out);
|
||||
}
|
@ -9,12 +9,13 @@
|
||||
*/
|
||||
char *gen_file_path(char *req_path)
|
||||
{
|
||||
char *path = strdup(req_path);
|
||||
char *path = (char *)malloc(strlen(req_path) + 1);
|
||||
strcpy(path, req_path);
|
||||
if (strchr(req_path, '.') == NULL)
|
||||
{
|
||||
if (req_path[strlen(req_path) - 1] != '/')
|
||||
{
|
||||
path = realloc(path, strlen(path) + 2);
|
||||
path = realloc(path, strlen(path) + 1);
|
||||
path = strcat(path, "/");
|
||||
}
|
||||
path = realloc(path, strlen(path) + strlen("index.html") + 1);
|
||||
@ -23,7 +24,7 @@ char *gen_file_path(char *req_path)
|
||||
|
||||
char *webroot = "static";
|
||||
|
||||
path = realloc(path, strlen(path) + strlen(webroot) + 1);
|
||||
path = realloc(path, strlen(path) + strlen(webroot));
|
||||
path = concat_to_front(&path, webroot);
|
||||
|
||||
return path;
|
||||
@ -45,6 +46,7 @@ int send_file(int cli_fd, struct file_s *file)
|
||||
}
|
||||
|
||||
close(file->fd);
|
||||
free(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -56,7 +58,7 @@ int send_file(int cli_fd, struct file_s *file)
|
||||
*/
|
||||
struct file_s *get_file_info(char *file_path)
|
||||
{
|
||||
struct file_s *file = malloc(sizeof(struct file_s));
|
||||
struct file_s *file = (struct file_s *)malloc(sizeof(struct file_s));
|
||||
|
||||
if (file == NULL)
|
||||
{
|
||||
|
@ -1,230 +0,0 @@
|
||||
#include "../../include/gallery_p/gallery.h"
|
||||
#include "../../include/utils_op/utils.h"
|
||||
#include "../../include/file_op/file.h"
|
||||
#include "../../include/utils_op/arr.h"
|
||||
|
||||
/**
|
||||
* @brief Get the list of albums on server
|
||||
*
|
||||
* @return gallery_t*
|
||||
*/
|
||||
gallery_t *get_album_list()
|
||||
{
|
||||
gallery_t *list = NULL, *curr = list;
|
||||
|
||||
char **albums_titles_list = malloc(0);
|
||||
|
||||
ssize_t albums_am = get_dir_list(&albums_titles_list, GALLERY_ROOT);
|
||||
|
||||
if (albums_am < 0)
|
||||
{
|
||||
err_msg("couldn't read albums list");
|
||||
return list;
|
||||
}
|
||||
if (albums_am == 0)
|
||||
return list;
|
||||
|
||||
list = new_album_item(albums_titles_list[0]);
|
||||
get_album_imgs(&list->images, &list->img_am, albums_titles_list[0]);
|
||||
|
||||
curr = list;
|
||||
|
||||
for (unsigned int i = 1; i < albums_am; i++)
|
||||
{
|
||||
curr->next = new_album_item(albums_titles_list[i]);
|
||||
|
||||
curr = curr->next;
|
||||
|
||||
get_album_imgs(&curr->images, &curr->img_am, albums_titles_list[i]);
|
||||
}
|
||||
|
||||
free_arr(albums_titles_list, albums_am);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free gallery_t structure
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void free_albums_list(gallery_t *albums_list)
|
||||
{
|
||||
gallery_t *curr_item = albums_list;
|
||||
|
||||
while (curr_item != NULL)
|
||||
{
|
||||
free(curr_item->title);
|
||||
for (int i = 0; i < curr_item->img_am; i++)
|
||||
free_img_item(curr_item->images[i]);
|
||||
free(curr_item->images);
|
||||
curr_item = curr_item->next;
|
||||
}
|
||||
|
||||
free(albums_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates new album item
|
||||
*
|
||||
* @param title
|
||||
* @return gallery_t*
|
||||
*/
|
||||
gallery_t *new_album_item(char *folder_name)
|
||||
{
|
||||
gallery_t *new = calloc(1, sizeof(gallery_t));
|
||||
|
||||
char *title = strdup(folder_name);
|
||||
title[strlen(title) - 1] = '\0';
|
||||
|
||||
new->title = strdup(repair_spaces(title));
|
||||
free(title);
|
||||
|
||||
new->img_am = 0;
|
||||
|
||||
new->images = NULL;
|
||||
new->next = NULL;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates new image item
|
||||
*
|
||||
* @param path
|
||||
* @return img_t
|
||||
*/
|
||||
img_t new_img_item(char *path)
|
||||
{
|
||||
img_t img;
|
||||
|
||||
img.date_taken = 0;
|
||||
img.description = NULL;
|
||||
img.path = strdup(path);
|
||||
img.rating = 0;
|
||||
img.tags = NULL;
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free img_t structure
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void free_img_item(img_t img)
|
||||
{
|
||||
free(img.description);
|
||||
free(img.path);
|
||||
free(img.tags);
|
||||
}
|
||||
|
||||
int get_album_imgs(img_t **images_arr, int *size, char *title)
|
||||
{
|
||||
char **photos_list = calloc(0, sizeof(char *));
|
||||
|
||||
char *album_path = strdup(GALLERY_ROOT);
|
||||
album_path = realloc(album_path, strlen(album_path) + strlen(title) + 1);
|
||||
album_path = strcat(album_path, title);
|
||||
|
||||
ssize_t photos_am = get_dir_list(&photos_list, album_path);
|
||||
free(album_path);
|
||||
|
||||
*images_arr = calloc(photos_am, sizeof(img_t));
|
||||
|
||||
for (int j = 0; j < photos_am; j++)
|
||||
{
|
||||
char *img_path = strdup("/gallery/albums/");
|
||||
img_path = realloc(img_path, strlen(img_path) + strlen(title) + strlen(photos_list[j]) + 1);
|
||||
img_path = strcat(img_path, title);
|
||||
img_path = strcat(img_path, photos_list[j]);
|
||||
|
||||
(*images_arr)[j] = new_img_item(img_path);
|
||||
|
||||
free(img_path);
|
||||
}
|
||||
|
||||
*size = photos_am;
|
||||
|
||||
free_arr(photos_list, photos_am);
|
||||
|
||||
return photos_am;
|
||||
}
|
||||
|
||||
char *gen_gallery_html()
|
||||
{
|
||||
FILE *album_template_fp = fopen("static/gallery/album.html", "r");
|
||||
FILE *image_template_fp = fopen("static/gallery/image.html", "r");
|
||||
|
||||
size_t album_file_size = get_file_size(album_template_fp) + 1;
|
||||
char *album_template = calloc(1, album_file_size);
|
||||
|
||||
size_t image_file_size = get_file_size(image_template_fp) + 1;
|
||||
char *image_template = calloc(1, image_file_size);
|
||||
|
||||
fread(album_template, album_file_size, 1, album_template_fp);
|
||||
fclose(album_template_fp);
|
||||
|
||||
fread(image_template, image_file_size, 1, image_template_fp);
|
||||
fclose(image_template_fp);
|
||||
|
||||
gallery_t *albums_list = get_album_list();
|
||||
gallery_t *albums_list_item = albums_list;
|
||||
|
||||
char *gallery_content = strdup("");
|
||||
|
||||
while (albums_list_item != NULL)
|
||||
{
|
||||
if (albums_list_item->img_am <= 0)
|
||||
{
|
||||
albums_list_item = albums_list_item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
char *album_content = strdup("");
|
||||
|
||||
for (int i = 0; i < albums_list_item->img_am; i++)
|
||||
{
|
||||
char *link = albums_list_item->images[i].path;
|
||||
size_t line_length = snprintf(NULL, 0, image_template, link, link) + 1;
|
||||
|
||||
char *image_content = calloc(1, line_length);
|
||||
if (image_content == NULL)
|
||||
return "500 Internal Error\n";
|
||||
|
||||
sprintf(image_content, image_template, link, link);
|
||||
|
||||
char *tmp = realloc(album_content, strlen(album_content) + strlen(image_content) + 1);
|
||||
if (tmp == NULL)
|
||||
return "500 Internal Error\n";
|
||||
|
||||
album_content = tmp;
|
||||
album_content = strcat(album_content, image_content);
|
||||
|
||||
free(image_content);
|
||||
}
|
||||
|
||||
size_t line_length = snprintf(NULL, 0, album_template, albums_list_item->title, album_content) + 1;
|
||||
|
||||
char *album_html = calloc(1, line_length);
|
||||
if (album_html == NULL)
|
||||
return "500 Internal Error\n";
|
||||
|
||||
sprintf(album_html, album_template, albums_list_item->title, album_content);
|
||||
|
||||
gallery_content = realloc(gallery_content, strlen(gallery_content) + line_length);
|
||||
|
||||
gallery_content = strcat(gallery_content, album_html);
|
||||
|
||||
free(album_content);
|
||||
free(album_html);
|
||||
|
||||
albums_list_item = albums_list_item->next;
|
||||
}
|
||||
|
||||
free(album_template);
|
||||
free(image_template);
|
||||
free_albums_list(albums_list);
|
||||
|
||||
return gallery_content;
|
||||
}
|
@ -2,10 +2,8 @@
|
||||
#include "../../include/utils_op/utils.h"
|
||||
#include "../../include/file_op/file.h"
|
||||
#include "../../include/file_op/mime.h"
|
||||
#include "../../include/articles_p/html.h"
|
||||
#include "../../include/articles_p/article.h"
|
||||
#include "../../include/gallery_p/gallery.h"
|
||||
#include "../../include/projects_p/projects.h"
|
||||
#include "../../include/articles_op/html.h"
|
||||
#include "../../include/articles_op/article.h"
|
||||
|
||||
/**
|
||||
* @brief Send 404 response
|
||||
@ -17,23 +15,19 @@ void res_404(int fd, char *path)
|
||||
{
|
||||
FILE *fp = fopen("static/404.html", "r");
|
||||
const ssize_t fsize = 512;
|
||||
char *buff = malloc(fsize), *msg = malloc(fsize);
|
||||
char buff[fsize], msg[fsize];
|
||||
|
||||
fread(buff, fsize, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
sprintf(msg, buff, path, path);
|
||||
free(buff);
|
||||
|
||||
struct header_s *header = gen_header(404, strlen(msg), "text/html");
|
||||
send(fd, header->str, header->size - 1, 0);
|
||||
free(header->str);
|
||||
free(header);
|
||||
|
||||
send(fd, msg, strlen(msg), 0);
|
||||
close(fd);
|
||||
|
||||
free(msg);
|
||||
printf("404 ERROR\n");
|
||||
}
|
||||
|
||||
@ -80,10 +74,10 @@ struct header_s *gen_header(int status_code, size_t file_size, char *mime_type)
|
||||
char *curr_date = asctime(ptm);
|
||||
|
||||
int header_length = snprintf(NULL, 0, "HTTP/1.1 %d %s\nDate: %sConnection: close\nContent-Length: %ld\nContent-Type: %s\r\n\r\n", status_code, status_message, curr_date, file_size, mime_type) + 1;
|
||||
char *header_string = malloc(header_length);
|
||||
char *header_string = (char *)malloc(header_length);
|
||||
snprintf(header_string, header_length, "HTTP/1.1 %d %s\nDate: %sConnection: close\nContent-Length: %ld\nContent-Type: %s\r\n\r\n", status_code, status_message, curr_date, file_size, mime_type);
|
||||
|
||||
struct header_s *header = malloc(header_length + sizeof(size_t));
|
||||
struct header_s *header = (struct header_s *)malloc(header_length + sizeof(size_t));
|
||||
|
||||
header->size = header_length;
|
||||
header->str = header_string;
|
||||
@ -108,7 +102,6 @@ int send_response(int fd, char *req_path)
|
||||
{
|
||||
res_500(fd);
|
||||
|
||||
free(file_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -116,8 +109,6 @@ int send_response(int fd, char *req_path)
|
||||
{
|
||||
res_404(fd, req_path);
|
||||
|
||||
free(file_path);
|
||||
free(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -141,11 +132,6 @@ int send_response(int fd, char *req_path)
|
||||
|
||||
close(fd);
|
||||
|
||||
free(header->str);
|
||||
free(header);
|
||||
|
||||
free(file);
|
||||
free(file_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -187,7 +173,6 @@ void handle_get_request(int fd, char *request)
|
||||
|
||||
fread(template, file_size, 1, fp);
|
||||
fclose(fp);
|
||||
template[file_size-1] = '\0';
|
||||
|
||||
article_info *articles = malloc(0);
|
||||
int amount = list_articles(&articles);
|
||||
@ -218,20 +203,12 @@ void handle_get_request(int fd, char *request)
|
||||
|
||||
printf("Sent home page\n");
|
||||
|
||||
free(header->str);
|
||||
free(header);
|
||||
|
||||
free(msg);
|
||||
free(template);
|
||||
free_article_info_arr(&articles, amount);
|
||||
free(articles_list_str);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp(path, "/article/", strlen("/article/")) == 0)
|
||||
{
|
||||
char *id_str = malloc(strlen(path) - strlen("/article/") + 1);
|
||||
char *id_str = (char *)malloc(strlen(path) - strlen("/article/") + 1);
|
||||
memmove(id_str, path + strlen("/article/"), strlen(path) - strlen("/article/") + 1);
|
||||
|
||||
char *remaining;
|
||||
@ -239,11 +216,8 @@ void handle_get_request(int fd, char *request)
|
||||
if (id < 0 || strcmp(id_str, remaining) == 0)
|
||||
{
|
||||
res_404(fd, path);
|
||||
|
||||
free(id_str);
|
||||
return;
|
||||
}
|
||||
free(id_str);
|
||||
|
||||
article_info *articles = malloc(0);
|
||||
int amount = list_articles(&articles);
|
||||
@ -253,7 +227,7 @@ void handle_get_request(int fd, char *request)
|
||||
return;
|
||||
}
|
||||
|
||||
if (id > amount - 1)
|
||||
if (id > amount-1)
|
||||
{
|
||||
res_404(fd, path);
|
||||
return;
|
||||
@ -261,7 +235,7 @@ void handle_get_request(int fd, char *request)
|
||||
|
||||
get_article_contents(&(articles[id]));
|
||||
|
||||
char *html = NULL;
|
||||
char *html;
|
||||
gen_html_article(articles[id], &html);
|
||||
|
||||
struct header_s *header = gen_header(200, strlen(html), "text/html");
|
||||
@ -272,93 +246,12 @@ void handle_get_request(int fd, char *request)
|
||||
|
||||
printf("Sent article with id=%d\n", id);
|
||||
|
||||
free(header->str);
|
||||
free(header);
|
||||
|
||||
free_article_info_arr(&articles, amount);
|
||||
free(articles);
|
||||
free(html);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(path, "/gallery") == 0 || strcmp(path, "/gallery/") == 0 || strcmp(path, "/gallery/index.html") == 0)
|
||||
{
|
||||
FILE *page_template_fp = fopen("static/gallery/index.html", "r");
|
||||
if (page_template_fp == NULL)
|
||||
{
|
||||
res_404(fd, path);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t page_file_size = get_file_size(page_template_fp) + 1;
|
||||
char *page_template = calloc(1, page_file_size);
|
||||
|
||||
fread(page_template, page_file_size, 1, page_template_fp);
|
||||
fclose(page_template_fp);
|
||||
|
||||
char *gallery_content = gen_gallery_html();
|
||||
|
||||
size_t line_length = snprintf(NULL, 0, page_template, gallery_content) + 1;
|
||||
char *res_page = calloc(1, line_length);
|
||||
sprintf(res_page, page_template, gallery_content);
|
||||
|
||||
struct header_s *header = gen_header(200, line_length, "text/html");
|
||||
send(fd, header->str, header->size - 1, 0);
|
||||
|
||||
send(fd, res_page, line_length, 0);
|
||||
close(fd);
|
||||
|
||||
printf("Sent gallery page\n");
|
||||
|
||||
free(header->str);
|
||||
free(header);
|
||||
|
||||
free(page_template);
|
||||
free(gallery_content);
|
||||
free(res_page);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(path, "/projects") == 0 || strcmp(path, "/projects/") == 0)
|
||||
{
|
||||
FILE *page_template_fp = fopen("static/projects/index.html", "r");
|
||||
if (page_template_fp == NULL)
|
||||
{
|
||||
res_404(fd, path);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t page_file_size = get_file_size(page_template_fp) + 1;
|
||||
char *page_template = calloc(1, page_file_size);
|
||||
|
||||
fread(page_template, page_file_size, 1, page_template_fp);
|
||||
fclose(page_template_fp);
|
||||
|
||||
char *projects_content = gen_project_html();
|
||||
|
||||
size_t line_length = snprintf(NULL, 0, page_template, projects_content) + 1;
|
||||
char *res_page = calloc(1, line_length);
|
||||
sprintf(res_page, page_template, projects_content);
|
||||
|
||||
struct header_s *header = gen_header(200, line_length, "text/html");
|
||||
send(fd, header->str, header->size - 1, 0);
|
||||
|
||||
send(fd, res_page, line_length, 0);
|
||||
close(fd);
|
||||
|
||||
printf("Sent projects page\n");
|
||||
|
||||
free(header->str);
|
||||
free(header);
|
||||
|
||||
free(page_template);
|
||||
free(projects_content);
|
||||
free(res_page);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (send_response(fd, path) < 0)
|
||||
{
|
||||
err_msg("couldn't send response");
|
||||
|
@ -1,104 +0,0 @@
|
||||
#include "../../include/projects_p/projects.h"
|
||||
#include "../../include/utils_op/arr.h"
|
||||
#include "../../include/file_op/file.h"
|
||||
|
||||
ssize_t read_list(project_t **list)
|
||||
{
|
||||
FILE *fp = fopen("static/projects/list.db", "r");
|
||||
if (fp == NULL)
|
||||
{
|
||||
perror("couldn't open projectDB file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *buff = NULL;
|
||||
size_t length = LINE_LENGTH;
|
||||
size_t projects_amount = 0;
|
||||
|
||||
while (getline(&buff, &length, fp) > 0)
|
||||
{
|
||||
if (buff[strlen(buff) - 1] == '\n')
|
||||
buff[strlen(buff) - 1] = '\0';
|
||||
|
||||
if (strlen(buff) == 0)
|
||||
continue;
|
||||
|
||||
projects_amount++;
|
||||
|
||||
*list = realloc(*list, sizeof(project_t) * projects_amount);
|
||||
|
||||
char *tmp = strdup(buff), *rest = tmp;
|
||||
|
||||
(*list)[projects_amount - 1].title = strdup(strtok_r(tmp, ";", &rest));
|
||||
|
||||
(*list)[projects_amount - 1].description = strdup(strtok_r(rest, ";", &rest));
|
||||
|
||||
(*list)[projects_amount - 1].lang = strdup(strtok_r(rest, ";", &rest));
|
||||
|
||||
(*list)[projects_amount - 1].license = strdup(strtok_r(rest, ";", &rest));
|
||||
|
||||
(*list)[projects_amount - 1].url = strdup(strtok_r(rest, ";", &rest));
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
free(buff);
|
||||
|
||||
return projects_amount;
|
||||
}
|
||||
|
||||
char *gen_project_html()
|
||||
{
|
||||
FILE *template_fp = fopen("static/projects/item.html", "r");
|
||||
if (template_fp == NULL)
|
||||
{
|
||||
perror("couldn't open project tesplate");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t template_file_size = get_file_size(template_fp) + 1;
|
||||
char *template = calloc(1, template_file_size);
|
||||
|
||||
fread(template, template_file_size, 1, template_fp);
|
||||
fclose(template_fp);
|
||||
|
||||
project_t *list = malloc(0);
|
||||
size_t length = read_list(&list);
|
||||
|
||||
char *content = strdup("");
|
||||
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
int line_length = snprintf(NULL, 0, template, list[i].url, list[i].title, list[i].description, list[i].lang, list[i].license) + 1;
|
||||
char *project_content = calloc(1, line_length);
|
||||
|
||||
sprintf(project_content, template, list[i].url, list[i].title, list[i].description, list[i].lang, list[i].license);
|
||||
|
||||
content = realloc(content, strlen(content) + line_length);
|
||||
|
||||
content = strcat(content, project_content);
|
||||
|
||||
free(project_content);
|
||||
}
|
||||
|
||||
free_proj_list(&list, length);
|
||||
free(template);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
void free_proj_list(project_t **list, int len)
|
||||
{
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
free((*list)[i].description);
|
||||
free((*list)[i].lang);
|
||||
free((*list)[i].license);
|
||||
free((*list)[i].title);
|
||||
free((*list)[i].url);
|
||||
}
|
||||
|
||||
free(*list);
|
||||
}
|
32
src/server.c
@ -1,10 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
@ -60,22 +58,6 @@ void handle_connection(int fd)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles child process removal (to prevent zombies)
|
||||
*
|
||||
* @param signum
|
||||
*/
|
||||
void handle_process_termination(int signum)
|
||||
{
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
do {
|
||||
pid = waitpid(-1, &status, WNOHANG);
|
||||
}
|
||||
while (pid > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main
|
||||
*/
|
||||
@ -85,18 +67,12 @@ int main(int argc, char *argv[])
|
||||
struct sockaddr_storage cli_addr;
|
||||
char s[INET6_ADDRSTRLEN];
|
||||
|
||||
char *port;
|
||||
|
||||
if (argc == 2)
|
||||
port = argv[1];
|
||||
else
|
||||
port = getenv("PORT");
|
||||
|
||||
if (port == NULL)
|
||||
if (argc != 2)
|
||||
{
|
||||
perror("Port must be specified either as an argument or environment variable \"PORT\"");
|
||||
err_msg("Usage: <Server Port>\n");
|
||||
exit(1);
|
||||
}
|
||||
char *port = argv[1];
|
||||
|
||||
int listenfd = get_listener_socket(port);
|
||||
if (listenfd < 0)
|
||||
@ -106,8 +82,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
printf("Waiting for connection on port %s...\n", port);
|
||||
|
||||
signal(SIGCHLD, handle_process_termination);
|
||||
|
||||
while (1)
|
||||
{
|
||||
socklen_t sin_size = sizeof cli_addr;
|
||||
|
@ -24,8 +24,6 @@ char *concat_to_front(char **str1, char *str2)
|
||||
strcpy(*str1, str2);
|
||||
strcat(*str1, tmp);
|
||||
|
||||
free(tmp);
|
||||
|
||||
return *str1;
|
||||
}
|
||||
|
||||
@ -88,56 +86,3 @@ char *trim(char *str)
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char *repair_spaces(char *str)
|
||||
{
|
||||
for (int i = 0; i < strlen(str); i++)
|
||||
{
|
||||
if (str[i] == '_')
|
||||
{
|
||||
str[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the list of files and directories at the specified path
|
||||
*
|
||||
* @param dir_list
|
||||
* @param path
|
||||
* @return ssize_t
|
||||
*/
|
||||
ssize_t get_dir_list(char ***dir_list, char *path)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
|
||||
if ((dir = opendir(path)) == NULL)
|
||||
{
|
||||
perror("\nOpendir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t n = 0;
|
||||
while ((ent = readdir(dir)) != NULL)
|
||||
{
|
||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
n++;
|
||||
*dir_list = realloc(*dir_list, sizeof(char *) * n);
|
||||
(*dir_list)[n - 1] = strdup(ent->d_name);
|
||||
|
||||
if (ent->d_type == DT_DIR)
|
||||
{
|
||||
(*dir_list)[n - 1] = realloc((*dir_list)[n - 1], strlen((*dir_list)[n - 1]) + 2);
|
||||
(*dir_list)[n - 1] = strcat((*dir_list)[n - 1], "/");
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
return n;
|
||||
}
|
@ -6,154 +6,17 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<title>About</title>
|
||||
<link href="https://pub.dm1sh.ru/@dm1sh" rel="me">
|
||||
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" id="logo">■</a>
|
||||
<!-- <a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a> -->
|
||||
<a href="/" id="logo"><img src="/logo.png" alt="logo" /></a>
|
||||
<a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a>
|
||||
</header>
|
||||
<main>
|
||||
<h1>About me</h1>
|
||||
<p>
|
||||
Hello, my name is Dmitriy Shishkov. I am a 17-year-old computer programmer. I
|
||||
also do photography as a hobby. As for autumn 2021, I study radiotechnics
|
||||
at "Saint Petersburg Electrotechnical University "LETI".
|
||||
</p>
|
||||
<p>
|
||||
I consider myself a full-stack web developer. All web services and applications
|
||||
I deploy on my home server, powered by Fedora Server. For it, I use Podman,
|
||||
Ansible, and Cockpit. I have also used PaaSes such as <a href="https://vercel.com">
|
||||
Vercel Now</a> or <a href="https://heroku.com">Heroku</a> for previews or small
|
||||
projects.
|
||||
</p>
|
||||
<p>
|
||||
I also do some C/C++ development for my workspace tools and to better
|
||||
understand how the software I use works. You can see some examples in the
|
||||
<a href="/projects">projects</a> section. Even this site is powered by a page
|
||||
generator written in C.
|
||||
</p>
|
||||
<p>Some of the technologies I have worked with:</p>
|
||||
<ul>
|
||||
<li>JS
|
||||
<ul>
|
||||
<li><a href="https://www.typescriptlang.org">TypeScript</a></li>
|
||||
<li><a href="https://reactjs.org">React</a>:
|
||||
<ul>
|
||||
<li><a href="https://reactrouter.com">React Router</a></li>
|
||||
<li><a href="https://github.com/molefrog/wouter">Wouter</a></li>
|
||||
<li><a href="https://www.framer.com/motion">Framer Motion</a></li>
|
||||
<li><a href="https://styled-components.com">Styled Components</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Web APIs:
|
||||
<ul>
|
||||
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">Service Worker</a></li>
|
||||
<li><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components">Web Components</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="https://redux.js.org">Redux</a></li>
|
||||
<li><a href="https://nextjs.org">Next.js</a></li>
|
||||
<li><a href="https://nodejs.org">NodeJS</a></li>
|
||||
<li><a href="https://graphql.org">Graphql</a>:
|
||||
<ul>
|
||||
<li><a href="https://www.apollographql.com">Apollo</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="https://www.prisma.io">Prisma</a></li>
|
||||
<li><a href="https://www.mongodb.com">MongoDB</a>
|
||||
<ul>
|
||||
<li><a href="https://mongoosejs.com">Mongoose</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="https://www.postgresql.org">PostgreSQL</a>
|
||||
<ul>
|
||||
<li><a href="https://node-postgres.com">Node-postgres</a></li>
|
||||
<li><a href="https://typeorm.io">TypeORM</a></li>
|
||||
<li><a href="https://www.cockroachlabs.com/product">CockroachDB</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Styling:
|
||||
<ul>
|
||||
<li><a href="https://mui.com/material-ui">Material UI</a></li>
|
||||
<li><a href="https://getbootstrap.com/">Bootstrap:
|
||||
<ul>
|
||||
<li><a href="https://react-bootstrap.github.io/">React Bootstrap</a></li>
|
||||
</ul>
|
||||
</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="https://www.npmjs.com/package/jsonwebtoken">jsonwebtoken</a></li>
|
||||
<li><a href="https://sendgrid.com">SendGrid</a></li>
|
||||
<li><a href="https://github.com/jakearchibald/idb">idb</a></li>
|
||||
<li><a href="https://github.com/websockets/ws">ws</a></li>
|
||||
<li><a href="https://immerjs.github.io/immer">Immer</a></li>
|
||||
<li><a href="https://expressjs.com">ExpressJS</a></li>
|
||||
<li>Dev environment:
|
||||
<ul>
|
||||
<li><a href="https://vitejs.dev">Vite</a></li>
|
||||
<li><a href="https://eslint.org">ESLint</a></li>
|
||||
<li><a href="https://prettier.io">Prettier</a></li>
|
||||
<li><a href="https://jestjs.io">Jest</a></li>
|
||||
<li><a href="https://mochajs.org">Mocha</a></li>
|
||||
<li><a href="https://webpack.js.org">Webpack</a></li>
|
||||
<li><a href="https://www.snowpack.dev">Snowpack</a></li>
|
||||
<li><a href="https://storybook.js.org">Storybook</a></li>
|
||||
<li><a href="https://esbuild.github.io">esbuild</a></li>
|
||||
<li><a href="https://babeljs.io">Babel</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Deployment:
|
||||
<ul>
|
||||
<li><a href="https://podman.io">Podman</a></li>
|
||||
<li><a href="https://www.docker.com">Docker</a></li>
|
||||
<li><a href="https://docs.docker.com/compose">docker-compose</a></li>
|
||||
<li><a href="https://www.ansible.com">Ansible</a></li>
|
||||
<li><a href="https://kubernetes.io">Kubernetes</a></li>
|
||||
<li><a href="https://dokku.com">Dokku</a></li>
|
||||
<li><a href="https://heroku.com">Heroku</a></li>
|
||||
<li><a href="https://vercel.com">Vercel Now</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="https://www.python.org">Python</a>
|
||||
<ul>
|
||||
<li><a href="https://ipython.org/notebook.html">Jupyter Notebook</a></li>
|
||||
<li><a href="https://matplotlib.org">Matplotlib</a></li>
|
||||
<li><a href="https://numpy.org">Numpy</a></li>
|
||||
<li><a href="https://fastapi.tiangolo.com">FastAPI</a></li>
|
||||
<li><a href="https://palletsprojects.com/p/jinja">Jinja2</a></li>
|
||||
<li><a href="https://www.sqlalchemy.org">SQLAlchemy</a></li>
|
||||
<li><a href="https://pytorch.org">PyTorch</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>C</li>
|
||||
<li>C++:
|
||||
<ul>
|
||||
<li><a href="https://www.wxwidgets.org">wxWidgets</a></li>
|
||||
<li><a href="https://www.arduino.cc">Arduino</a></li>
|
||||
<li><a href="https://learn.microsoft.com/en-us/cpp/mfc/mfc-desktop-applications">MFC</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="https://go.dev">Golang</a></li>
|
||||
<li><a href="https://www.lua.org">Lua</a></li>
|
||||
<li>C#
|
||||
<ul>
|
||||
<li><a href="https://dotnet.microsoft.com">.NET Core</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="https://kotlinlang.org">Kotlin</a></li>
|
||||
</ul>
|
||||
<p>See <a href="/contacts">contacts</a> to get in touch with me.</p>
|
||||
<p>You can reach my resume by following
|
||||
<a href="https://git.dm1sh.ru/dm1sh/dm1sh/src/branch/main/resume.md">this link</a>.
|
||||
</p>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,35 +0,0 @@
|
||||
First, decompile apk file with [apktool](https://apktool.org):
|
||||
|
||||
```
|
||||
apktool d <file_name>.apk
|
||||
```
|
||||
|
||||
Replace assets or edit *Manifest.xml*. [smali2java](https://github.com/AlexeySoshin/smali2java) can be of use for code modifications.
|
||||
|
||||
Recompile apk:
|
||||
|
||||
```
|
||||
apktool b <file_name>
|
||||
```
|
||||
|
||||
Alignment is necessary for modern android versions (I believe, 30+ SDK):
|
||||
|
||||
```
|
||||
zipalign -v -f -p 4 <file_name>/dist/<file_name>.apk aligned-<file_name>.apk
|
||||
```
|
||||
|
||||
Create a signing key. Fields values do not matter. Especially for personal use.
|
||||
|
||||
```
|
||||
keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 1000
|
||||
```
|
||||
|
||||
Actually sign apk with it:
|
||||
|
||||
```
|
||||
apksigner.jar sign -ks my-release-key.keystore --ks-key-alias alias_name aligned-<file_name>.apk
|
||||
```
|
||||
|
||||
It will overwrite the provided apk with a signed one.
|
||||
|
||||
We're awesome!
|
@ -1,6 +1,7 @@
|
||||
# My first article on this site
|
||||
This is my own site written in pure C with templates in html and some css styles. All articles are stored in markdown format and are processed by server to convert them to html and send to client. Links list on the homepage is also generated dynamically to show all the articles, currntly avaliable on disk.
|
||||
|
||||

|
||||

|
||||
*Data flow*
|
||||
|
||||
As for all the pages of this site are pure html, it can be read from really old computers or text-based browsers. Server Side Rendered (SSR) webpages are also good for search engines.
|
BIN
static/articles/First article/chart1.png
Normal file
After Width: | Height: | Size: 34 KiB |
@ -1,41 +0,0 @@
|
||||
If you dualboot Windows and Linux, you may need to transfer files between your bare-metal Linux and WSL.
|
||||
|
||||
Firstly, mount your Windows disk C: on Linux. For it, run:
|
||||
|
||||
```
|
||||
sudo pacman -S ntfs-3g
|
||||
sudo mkdir -p /mnt/c
|
||||
sudo mount /dev/<Windows partition name> /mnt/c
|
||||
```
|
||||
|
||||
WSL filesystem is stored as .vhdx file, so we will use qemu-nbd to mount it as usual disk drive.
|
||||
|
||||
Install and enable nbd kernel module:
|
||||
|
||||
```
|
||||
sudo pacman -S nbd
|
||||
sudo modprobe nbd
|
||||
```
|
||||
|
||||
Install qemu:
|
||||
|
||||
```
|
||||
sudo pacman -S qemu
|
||||
```
|
||||
|
||||
Connect .vhdx file to nbd device:
|
||||
|
||||
```
|
||||
sudo qemu-hbd -c /dev/nbd0 /mnt/c/<path to .vhdx file>
|
||||
```
|
||||
|
||||
By default, path must look like Users/[user]/AppData/Local/Packages/[distro]/LocalState/[distroPackageName]/ext4.vhdx
|
||||
|
||||
Finally, mount ndb device:
|
||||
|
||||
```
|
||||
sudo mkdir /mnt/wsl
|
||||
sudo mount /dev/ndb0 /mnt/wsl
|
||||
```
|
||||
|
||||
Now you can transfer files or chroot into it.
|
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 120 KiB |
@ -1,20 +0,0 @@
|
||||
I like reading books. Really like. But I can't bring paper ones everywhere I want and not all books are available in the local library. So I have to use my phone or laptop. Until now I tried lots of various ebook readers, but no one of them satisfied all my requirements. Some were too complicated for just reading text, some, on the other hand, lacked functionality, and some just had an awful design.
|
||||
|
||||
After some searches, I understood, that nothing can fit my needs but my own tool. So I created Publite — a web application for book reading. For it I also created a backend for converting EPUB and FB2 files to HTML After adding the book, you can read it offline. The book is split into pages fitting window size.
|
||||
|
||||
The first version of the application is already available at [publite.dmitriy.icu](https://publite.dmitriy.icu). It is a React.JS SPA using LocalStorage as book storage. To provide simple routing without dramatic bundle size growth I used [Wouter](https://github.com/molefrog/wouter) instead of React-Router.
|
||||
|
||||
But this implementation is not ideal. First of all, it would be difficult and ugly to add server synchronization to the current data flow. Secondly, pagination which I stole from [this article](https://blog.cacoveanu.com/2020/2020.08.14.pagination_in_ebooks.html) is too inefficient for my case.
|
||||
|
||||
I already started solving the first problem by switching from React context to fetches caught by ServiceWorker. Books will be stored in IndexedDB. I also added caching of static assets with browser Cache API. It allows me to turn the app into PWA and even publish it to Play Market (if I won't abandon this project).
|
||||
|
||||
As for pagination, it is also very important and I have a couple of ideas on how to improve it, but so far, we have to deal with it. Now I'm applying to university and traveling across Belarus. So I don't have much time for this project.
|
||||
|
||||

|
||||
*Book adding screenshot*
|
||||
|
||||

|
||||
*Book list screenshot*
|
||||
|
||||

|
||||
*Book view screenshot*
|
Before Width: | Height: | Size: 599 KiB |
@ -1,31 +0,0 @@
|
||||
The idea of creating my own virtual machine came to my mind a year ago. Then I didn't have enough skills and desire. But now, when I have my final exams in a month and a half, I started working on this project.
|
||||
|
||||
## Overview
|
||||
|
||||
It is a stack-based virtual machine that runs simple byte-code. Currently, it supports basic stack operations (push, pop), arithmetic operations, input-output, and strings. Assembly language is a sequence of commands with/(or without) parameters. Assembly language compiler and runner are combined to one binary with *build* and *run* commands resp.
|
||||
|
||||
Every command is represented as 4 bytes number. It can have arguments each of which is a 4 bytes number.
|
||||
|
||||
Every string is initialized with zero number in the stack. Currently, the only command using this structure is *OUTS*. It puts characters from stack to console until it reaches zero — end of the string.
|
||||
|
||||
## File structure
|
||||
|
||||
### Header
|
||||
|
||||
Total number of commands - 4 bytes
|
||||
|
||||
### Body
|
||||
|
||||
A sequence of command structures:
|
||||
|
||||
### Command
|
||||
|
||||
Command code - 4 bytes
|
||||
|
||||
(Optional) Arguments - 4 bytes per each
|
||||
|
||||
## Plans
|
||||
|
||||
It is just a test stand. After exams, I'm planning to dramatically refactor VM code, change bytecode structure by decreasing command size, and adding string declaration in the header. I'm also going to add conditions and jumps for loop support.
|
||||
|
||||
Also, there are plans for separating compiler and VM core. If I will read find information about bootloader development, I will also try to create a bootloader for this VM to run it natively.
|
@ -1,13 +0,0 @@
|
||||
On November 29 I took part in online hackathon "Югорский Хантатон". During it, our command developed a web application for interactional city tours. It is called KOMAP
|
||||
|
||||
I was a member of S.S.H&K.K. command. Besides me, there were 4 more members: Artem Stukalov as a backend developer, Timofey Sedov as a frontend developer, same as me, Dariya Kuznetsova and Victoria Kruk as designers.
|
||||
|
||||
This is an application description from the project repository:
|
||||
|
||||
> The main idea is to create routes passing through the sights of the cities of the Khanty-Mansi Autonomous Okrug (in particular Khanty-Mansiysk), along which people will be invited to walk. The routes will pass not only through the sights but also through cafes, shopping centers, etc. At the end of the route, people will be able to leave reviews, and after passing 3 basic routes they will be able to create their own, which will subsequently be moderated and added to the site as "new routes". We plan to cooperate with stores and coffee houses to build routes through them, thereby creating an unobtrusive advertisement. At the end of the route, we will provide discount coupons from partner stores, thereby we will be able to interest more people.
|
||||
|
||||
During the hackathon, we developed this idea, implemented a backend service for this application and a landing page presenting our product. Currently, this project is still not finished, but not abandoned. We took first place in the junior league and got a lot of new experience.
|
||||
|
||||
You can get source code of this project on [github](https://github.com/SSH-KK/hackathon2020_komap)
|
||||
|
||||

|
Before Width: | Height: | Size: 590 KiB |
@ -1,31 +0,0 @@
|
||||
First, we need to install an application, that'll provide MJPEG video stream. There is IP Webcam, but it is closed source, so it doesn't fit our requirements, through it could be a simplier solution. We will use [ScreenStream](https://github.com/dkrivoruchko/ScreenStream) instead, that is also [available](https://f-droid.org/packages/info.dvkr.screenstream) on F-Droid. With it we can share anything displayed on the phone screen, so, to stream video from camera, we can use any camera solution.
|
||||
|
||||
Next, we need to connect phone with our linux PC. For it you can connect them to the same network. But to lower thresold, you can connect them with a wire, enable USB debugging and forward port with adb:
|
||||
|
||||
```
|
||||
adb forward tcp:8000 tcp:8080
|
||||
```
|
||||
|
||||
Prior command will set up PC' port 8000 to phone's 8080. Also note, then if you use ScreenStream, you need to enable local host in its settings.
|
||||
|
||||
Next step is to set up a dummy interface for our camera. For it, install and enable v4l2loopback kernel module:
|
||||
|
||||
```
|
||||
sudo pacman -S v4l2loopback-dkms
|
||||
```
|
||||
|
||||
It will create a dummy video interface on `/dev/video0`. We'll assume, it is enough, but if you need more then one device, you can easely google, what params need to be passed while manually enabling the module.
|
||||
|
||||
Then, if you don't have ffmpeg, install it:
|
||||
|
||||
```
|
||||
sudo pacman -S ffmpeg
|
||||
```
|
||||
|
||||
And finally:
|
||||
|
||||
```
|
||||
sudo ffmpeg -i http://[localhost или ip адресс телефона]:8000/stream.mjpeg -fflags nobuffer -pix_fmt yuv420p -r 30 -f v4l2 /dev/video0
|
||||
```
|
||||
|
||||
That's all, now you can use your phone camera (or, any other app) just like your PC camera.
|
@ -1,21 +0,0 @@
|
||||
I've moved to Fedora Linux and new mjpeg streaming app was released, since the previous article was written. So, because I needed a camera again, I decided to update this manual.
|
||||
|
||||
First of all, there is a new app by Thomas SIMON: [RemoteCam](https://github.com/Ruddle/RemoteCam). It directly streams video from camera and permits multiple settings like video resolution and jpeg quality.
|
||||
|
||||
To create a dummy camera interface on Fedora, we need to install v4l2loopback packet:
|
||||
|
||||
```bash
|
||||
sudo dnf install v4l2loopback
|
||||
```
|
||||
|
||||
Next, after reboot, we're ready to start the stream with ffmpeg:
|
||||
|
||||
```bash
|
||||
sudo ffmpeg -f mjpeg -r 5 -i "http://<Phone IP>:8080/cam.mjpeg?fps=10" -r 10 -pix_fmt yuv420p -f v4l2 /dev/video0
|
||||
```
|
||||
|
||||
My Wi-Fi connection speed was enough for such stream. But it is still possible to use it with adb port forwarding over usb:
|
||||
|
||||
```bash
|
||||
adb forward tcp:8080 tcp:8080
|
||||
```
|
@ -6,16 +6,15 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<title>%s</title>
|
||||
<link href="https://pub.dm1sh.ru/@dm1sh" rel="me">
|
||||
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" id="logo">■</a>
|
||||
<!-- <a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a> -->
|
||||
<a href="/" id="logo"><img src="/logo.png" alt="logo" /></a>
|
||||
<a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a>
|
||||
</header>
|
||||
<main>
|
||||
<h1 id="header">%s</h1>
|
||||
|
@ -1,8 +0,0 @@
|
||||
1594048366000 My_first_article_on_this_site
|
||||
1606819380000 Ugra_Hantaton
|
||||
1619034957605 Stack_VM_V1.0
|
||||
1627839955679 Publite_-_an_Ebook_reader
|
||||
1646570601234 Mount_WSL_partition_on_Arch_Linux
|
||||
1653350638050 Use_phone_as_camera_for_linux_desktop
|
||||
1694581049776 Use_phone_camera_for_linux_desktop_(2023)
|
||||
1738537323514 APK_modification
|
1
static/articles_list.db
Normal file
@ -0,0 +1 @@
|
||||
1593768639 First article
|
@ -1,31 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<title>Contacts</title>
|
||||
<link href="https://pub.dm1sh.ru/@dm1sh" rel="me">
|
||||
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" id="logo">■</a>
|
||||
<!-- <a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a> -->
|
||||
</header>
|
||||
<main>
|
||||
<h1>Contacts:</h1>
|
||||
<ul>
|
||||
<li><a href="https://t.me/dm1sh">Telegram</a></li>
|
||||
<li><a href="mailto:me@dmitriy.icu">Mail (me@dmitriy.icu)</a></li>
|
||||
<li><a rel="me" href="https://pub.dm1sh.ru/@dm1sh">Mastodon</a></li>
|
||||
<li><a href="https://vk.com/dm1sh">VK</a></li>
|
||||
</ul>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,6 +0,0 @@
|
||||
<details>
|
||||
<summary title="Press to expand">
|
||||
<h2>%s</h2>
|
||||
</summary>
|
||||
<ul>%s</ul>
|
||||
</details>
|
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 180 KiB |
Before Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 178 KiB |
Before Width: | Height: | Size: 192 KiB |
Before Width: | Height: | Size: 282 KiB |
Before Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 292 KiB |
Before Width: | Height: | Size: 206 KiB |
Before Width: | Height: | Size: 322 KiB |
Before Width: | Height: | Size: 308 KiB |
@ -1,3 +0,0 @@
|
||||
<li>
|
||||
<a href="%s"><img src="%s"></a>
|
||||
</li>
|
@ -6,36 +6,17 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<title>Gallery</title>
|
||||
<link href="https://pub.dm1sh.ru/@dm1sh" rel="me">
|
||||
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" id="logo">■</a>
|
||||
<!-- <a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a> -->
|
||||
<a href="/" id="logo"><img src="./logo.png" alt="logo" /></a>
|
||||
<a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a>
|
||||
</header>
|
||||
<main>
|
||||
<h1>Gallery</h1>
|
||||
<p>Heare are some of my photos.</p>
|
||||
%s
|
||||
</main>
|
||||
<style>
|
||||
summary h2 {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
details>ul img {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
details>ul li {
|
||||
list-style-type: none;
|
||||
display: inline;
|
||||
}
|
||||
</style>
|
||||
<h1>Work in porgress</h1>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -5,17 +5,16 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<title>Shishkov Dmitriy</title>
|
||||
<link href="https://pub.dm1sh.ru/@dm1sh" rel="me">
|
||||
<title>Test</title>
|
||||
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" id="logo">■</a>
|
||||
<!-- <a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a> -->
|
||||
<a href="/" id="logo"><img src="/logo.png" alt="logo" /></a>
|
||||
<a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a>
|
||||
</header>
|
||||
<main>
|
||||
<h1>Home</h1>
|
||||
@ -23,7 +22,7 @@
|
||||
<li><a href="/about">About me</a></li>
|
||||
<li><a href="/gallery">Gallery</a></li>
|
||||
<li><a href="/projects">Projects</a></li>
|
||||
<li><a href="/contacts">Contacts</a></li>
|
||||
<li><a href="/philosophy">Philosophy</a></li>
|
||||
</ul>
|
||||
<h1>Articles</h1>
|
||||
%s
|
||||
|
BIN
static/logo.png
Normal file
After Width: | Height: | Size: 602 B |
@ -1,37 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<title>Projects</title>
|
||||
<link href="https://pub.dm1sh.ru/@dm1sh" rel="me">
|
||||
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<a href="/" id="logo">■</a>
|
||||
<!-- <a id="modern" href="/modern"
|
||||
title="If you have modern browser, you can visit more fancy version of my site">Modernize</a> -->
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h1 id="header">My projects</h1>
|
||||
<ul>%s</ul>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main>ul li {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
main>ul li>a {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,7 +0,0 @@
|
||||
<li>
|
||||
<a href="%s">
|
||||
%s
|
||||
</a>
|
||||
<p>%s - <b><i>%s</i></b></p>
|
||||
<span><i>%s</i></span>
|
||||
</li>
|
@ -1,11 +0,0 @@
|
||||
Porridger;Eco-oriented bulletin board;TypeScript+React+Python+FastAPI;None;https://git.dm1sh.ru/polka_billy/porridger
|
||||
The Invisible Centaur;Interface for human and AI integration, which helps to play Go game;JavaScript+React+Redux;GPL-3.0;https://github.com/SSH-KK/goHackathon
|
||||
Publite;EBook reader supporting Epub and FB2 file formats;TypeScript+React+Python+FastAPI;AGPL-3.0;https://git.dm1sh.ru/publite
|
||||
QuestionForm;Simplified Google Forms analog;TypeScript+React+Apollo+Prisma;MIT License;https://git.dm1sh.ru/dm1sh/QuestionForm
|
||||
timetable-generator;Classes scheduling application;TypeScript+React+Bootstrap;None;https://github.com/SSH-KK/timetable-generator
|
||||
school_documents;School documents (seminars and homeworks) archive;TypeScript+React;None;https://github.com/SSH-KK/school_documents
|
||||
KGS_LeaderBoard;Leaderboard application for KGS Go server;TypeScript+React+Brython;MIT License;https://github.com/SSH-KK/KGS_LeaderBoard
|
||||
QRCodeLibrary;Library for QR code generation from text or any sequence of bytes;C++;None;https://git.dm1sh.ru/dm1sh/QRCodeLibrary
|
||||
c-dmitriy.icu;HTTP/1 web server, supporting static content disposal and HTML-code generation from Markdown;C;None;https://git.dm1sh.ru/dm1sh/c-dmitriy.icu
|
||||
wxMahjong;Crossplatform game mahjong (solitare);wxWidgets+C++;MIT License;https://git.dm1sh.ru/dm1sh/wxMahjong
|
||||
mshell;A simple yet comfortable Unix shell;C;MIT License;https://git.dm1sh.ru/dm1sh/mshell
|
@ -2,31 +2,27 @@
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
|
||||
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--accent: #000000;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
|
||||
::selection {
|
||||
background-color: var(--accent);
|
||||
color: var(--background);
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--background);
|
||||
border-top: solid 5px var(--accent);
|
||||
background-color: #ffffff;
|
||||
border-top: solid 5px #000000;
|
||||
padding: 45px;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
a {
|
||||
font-family: monospace;
|
||||
color: var(--accent);
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
header {
|
||||
@ -38,60 +34,41 @@ header img {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#logo {
|
||||
text-decoration: none;
|
||||
font-size: 111px;
|
||||
line-height: 65px;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
/* header #modern {
|
||||
header #modern {
|
||||
line-height: 65px;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
margin-left: 30px;
|
||||
font-size: 18px;
|
||||
color: var(--accent);
|
||||
color: #000000;
|
||||
font-family: monospace;
|
||||
}
|
||||
header #modern:hover
|
||||
{
|
||||
text-decoration: none;
|
||||
} */
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
main > * {
|
||||
main>* {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
main img {
|
||||
max-width: 100%;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
main > ul {
|
||||
main>ul {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
main p,
|
||||
main blockquote,
|
||||
main li {
|
||||
main p, main blockquote, main li {
|
||||
font-size: 20px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
main pre {
|
||||
font-size: 17px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
main code {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
main a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
@ -99,15 +76,10 @@ main a:hover {
|
||||
main blockquote {
|
||||
margin-left: 20px;
|
||||
padding-left: 10px;
|
||||
border-left: var(--accent) solid 5px;
|
||||
border-left: #000000 solid 5px;
|
||||
}
|
||||
|
||||
main details,
|
||||
main summary {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
main > #header {
|
||||
main>#header {
|
||||
font-size: 45px;
|
||||
}
|
||||
|
||||
@ -119,14 +91,3 @@ main > #header {
|
||||
margin-left: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
ul ul > li {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #000000;
|
||||
--accent: #ffffff;
|
||||
}
|
||||
}
|
||||
|