Compare commits

...

2 Commits

Author SHA1 Message Date
ngn
4e08375748
Merge branch 'main' of https://git.ngn.tf/ngn/website
Some checks failed
Build the docker image for the doc server / build (push) Failing after 15s
2025-05-22 20:12:41 +03:00
ngn
912bf616b6
update doc server to ctorm 1.8.1
Signed-off-by: ngn <ngn@ngn.tf>
2025-05-22 20:12:05 +03:00
29 changed files with 178 additions and 122 deletions

View File

@ -81,7 +81,7 @@ BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon BreakInheritanceList: BeforeColon
BreakStringLiterals: true BreakStringLiterals: true
ColumnLimit: 120 ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:' CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false CompactNamespaces: false
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4

35
doc/.clang-tidy Normal file
View File

@ -0,0 +1,35 @@
---
# "gnu-zero-variadic-macro-arguments" ignored because we are using GNU99
# standart
# "clang-diagnostic-language-extension-token" is ignored because we need the
# asm() extension token
# "DeprecatedOrUnsafeBufferHandling" ignored because C11 "_s" functions are not
# secure either
Checks: >-
clang-diagnostic-*,
-clang-diagnostic-gnu-zero-variadic-macro-arguments,
-clang-diagnostic-language-extension-token,
clang-analyzer-*,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
portability-*,
performance-*,
WarningsAsErrors: '*'
HeaderFileExtensions:
- ''
- h
- hh
- hpp
- hxx
ImplementationFileExtensions:
- c
- cc
- cpp
- cxx
HeaderFilterRegex: '.*'
ExcludeHeaderFilterRegex: ''
FormatStyle: file
SystemHeaders: false
...

View File

@ -1,4 +1,4 @@
FROM ghcr.io/ngn13/ctorm:1.7 FROM ghcr.io/ngn13/ctorm:1.8.1
WORKDIR /doc WORKDIR /doc

View File

@ -26,10 +26,13 @@ $(DISTDIR)/%.o: src/%.c
format: format:
clang-format -i -style=file $(CSRCS) $(HSRCS) clang-format -i -style=file $(CSRCS) $(HSRCS)
lint:
clang-tidy --warnings-as-errors --config= $(CSRCS) $(HSRCS)
clean: clean:
rm -rf $(DISTDIR) rm -rf $(DISTDIR)
run: run:
./doc.elf ./doc.elf
.PHONY: format clean run .PHONY: format lint clean run

View File

@ -1,4 +0,0 @@
{
"title": "privacy",
"desc": "Learn how I respect your privacy"
}

View File

@ -1,4 +0,0 @@
{
"title": "gizlilik",
"desc": "Gizliliğinize nasıl önem verdiğimi öğrenin"
}

View File

@ -3,13 +3,13 @@
#include <stdbool.h> #include <stdbool.h>
#include <dirent.h> #include <dirent.h>
#include "util.h" #include "file.h"
typedef struct { typedef struct {
DIR *dir; DIR *dir;
util_file_t *file; file_t *file;
char name[NAME_MAX + 1]; char name[NAME_MAX + 1];
char *lang; char *lang;
} docs_t; } docs_t;
bool docs_init(docs_t *docs, char *dir); bool docs_init(docs_t *docs, char *dir);

10
doc/inc/file.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
typedef struct {
char *content;
int64_t size;
} file_t;
file_t *file_load(int dirfd, char *path);
void file_free(file_t *file);

View File

@ -4,16 +4,8 @@
#include <stdint.h> #include <stdint.h>
#include <ctype.h> #include <ctype.h>
typedef struct { #define util_toupper(str) \
char *content; for (char *c = str; *c != 0; c++) \
uint64_t size;
} util_file_t;
#define util_toupper(str) \
for (char *c = str; *c != 0; c++) \
*c = toupper(*c) *c = toupper(*c)
uint64_t util_endswith(char *str, char *suf); uint64_t util_endswith(char *str, char *suf);
void util_send(ctorm_res_t *res, uint16_t code, cJSON *json); void util_send(ctorm_res_t *res, uint16_t code, cJSON *json);
util_file_t *util_file_load(int dirfd, char *path);
void util_file_free(util_file_t *file);
bool util_parse_doc_name(char *name, char **lang, const char *ext);

View File

@ -0,0 +1,4 @@
{
"title": "privacy",
"desc": "Privacy policy"
}

View File

@ -0,0 +1,4 @@
{
"title": "gizlilik",
"desc": "Gizlilik ilkesi"
}

View File

@ -11,22 +11,25 @@
option_t options[] = { option_t options[] = {
// name value requied // name value requied
{"host", "0.0.0.0:7003", true }, // host the server should listen on {"host", "0.0.0.0:7003", true }, // host the server should listen on
{"docs_dir", "./docs", true }, // documentation directory {"dir", "./pages", true }, // documentation pages directory
{"", NULL, false}, {"", NULL, false},
}; };
bool config_load(config_t *conf) { bool config_load(config_t *conf) {
bzero(conf, sizeof(*conf)); memset(conf, 0, sizeof(*conf));
char name_env[OPT_NAME_MAX + 10], name_copy[OPT_NAME_MAX], *value = NULL; char name_env[OPT_NAME_MAX + 10], name_copy[OPT_NAME_MAX], *value = NULL;
conf->options = options; conf->options = options;
for (option_t *opt = conf->options; opt->value != NULL; opt++, conf->count++) { for (option_t *opt = conf->options; opt->value != NULL;
strcpy(name_copy, opt->name); opt++, conf->count++) {
// convert option name to environment variable name
strncpy(name_copy, opt->name, OPT_NAME_MAX);
util_toupper(name_copy); util_toupper(name_copy);
snprintf(name_env, sizeof(name_env), "WEBSITE_%s", name_copy); snprintf(name_env, sizeof(name_env), "WEBSITE_%s", name_copy);
// attempt to load the value from the environment
if ((value = getenv(name_env)) != NULL) if ((value = getenv(name_env)) != NULL)
opt->value = value; opt->value = value;
@ -36,7 +39,9 @@ bool config_load(config_t *conf) {
if (!opt->required || NULL != opt->value) if (!opt->required || NULL != opt->value)
continue; continue;
ctorm_fail("please specify a value for the required config option: %s (%s)", opt->name, name_env); ctorm_fail("please specify a value for the required config option: %s (%s)",
opt->name,
name_env);
errno = EFAULT; errno = EFAULT;
return false; return false;
} }

View File

@ -1,6 +1,6 @@
#include <linux/limits.h> #include <linux/limits.h>
#include <dirent.h> #include <dirent.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
@ -9,7 +9,7 @@
#define DOCS_LANG_CODE_LEN 2 #define DOCS_LANG_CODE_LEN 2
bool __docs_parse_name(docs_t *docs, char *ext) { bool _docs_parse_name(docs_t *docs, char *ext) {
// check the extension // check the extension
uint64_t ext_pos = util_endswith(docs->name, ext); uint64_t ext_pos = util_endswith(docs->name, ext);
@ -22,7 +22,8 @@ bool __docs_parse_name(docs_t *docs, char *ext) {
// example.en\0json\0 // example.en\0json\0
// | // |
// `--- find this // `--- find this
for (docs->lang = docs->name; *docs->lang != 0 && *docs->lang != '.'; docs->lang++) for (docs->lang = docs->name; *docs->lang != 0 && *docs->lang != '.';
docs->lang++)
continue; continue;
if (*docs->lang != '.') if (*docs->lang != '.')
@ -39,11 +40,11 @@ bool __docs_parse_name(docs_t *docs, char *ext) {
return strlen(docs->lang) == DOCS_LANG_CODE_LEN && *docs->name != 0; return strlen(docs->lang) == DOCS_LANG_CODE_LEN && *docs->name != 0;
} }
void __docs_clean(docs_t *docs) { void _docs_clean(docs_t *docs) {
if (NULL == docs->file) if (NULL == docs->file)
return; return;
util_file_free(docs->file); file_free(docs->file);
docs->file = NULL; docs->file = NULL;
} }
@ -53,7 +54,7 @@ bool docs_init(docs_t *docs, char *dir) {
return false; return false;
} }
bzero(docs, sizeof(*docs)); memset(docs, 0, sizeof(*docs));
return NULL != (docs->dir = opendir(dir)); return NULL != (docs->dir = opendir(dir));
} }
@ -64,15 +65,15 @@ char *docs_next(docs_t *docs, char *name, bool content) {
} }
struct dirent *ent = NULL; struct dirent *ent = NULL;
__docs_clean(docs); _docs_clean(docs);
while (NULL != (ent = readdir(docs->dir))) { while (NULL != (ent = readdir(docs->dir))) {
if (*ent->d_name == '.') if (*ent->d_name == '.')
continue; continue;
strcpy(docs->name, ent->d_name); strncpy(docs->name, ent->d_name, NAME_MAX);
if (!__docs_parse_name(docs, content ? ".md" : ".json")) if (!_docs_parse_name(docs, content ? ".md" : ".json"))
continue; continue;
if (NULL == name || strncmp(docs->name, name, NAME_MAX) == 0) if (NULL == name || strncmp(docs->name, name, NAME_MAX) == 0)
@ -84,7 +85,7 @@ char *docs_next(docs_t *docs, char *name, bool content) {
return NULL; return NULL;
} }
if (NULL == (docs->file = util_file_load(dirfd(docs->dir), ent->d_name))) if (NULL == (docs->file = file_load(dirfd(docs->dir), ent->d_name)))
return NULL; return NULL;
return docs->file->content; return docs->file->content;
@ -99,8 +100,8 @@ void docs_free(docs_t *docs) {
if (NULL == docs) if (NULL == docs)
return; return;
__docs_clean(docs); _docs_clean(docs);
closedir(docs->dir); closedir(docs->dir);
bzero(docs, sizeof(*docs)); memset(docs, 0, sizeof(*docs));
} }

56
doc/src/file.c Normal file
View File

@ -0,0 +1,56 @@
#include <sys/mman.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include "file.h"
file_t *file_load(int dirfd, char *path) {
if (NULL == path) {
errno = EINVAL;
return NULL;
}
file_t *file = NULL;
int fd = -1;
// open the taarget file
if ((fd = openat(dirfd, path, O_RDONLY)) < 0)
goto end; // errno set by open
// allocate a new file structure
if (NULL == (file = calloc(1, sizeof(file_t))))
goto end; // errno set by malloc
// calculate the file size
if ((file->size = lseek(fd, 0, SEEK_END)) < 0)
goto end;
// memory map the file
if (NULL ==
(file->content = mmap(0, file->size, PROT_READ, MAP_PRIVATE, fd, 0)))
goto end; // errno set by mmap
end:
if (fd != -1)
close(fd);
if (NULL != file && NULL == file->content) {
free(file);
return NULL;
}
return file;
}
void file_free(file_t *file) {
if (NULL == file)
return;
munmap(file->content, file->size);
free(file);
}

View File

@ -1,3 +1,4 @@
#include <ctorm/app.h>
#include <ctorm/ctorm.h> #include <ctorm/ctorm.h>
#include <stdlib.h> #include <stdlib.h>
@ -19,25 +20,28 @@ int main() {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// initialize the app config
ctorm_config_new(&app_config); ctorm_config_new(&app_config);
app_config.disable_logging = true; app_config.disable_logging = true;
app = ctorm_app_new(&app_config);
// create a new app
app = ctorm_app_new(&app_config);
// middlewares // middlewares
MIDDLEWARE_ALL(app, "/*", route_cors); ALL(app, "/*", route_cors);
MIDDLEWARE_ALL(app, "/*/*", route_cors); ALL(app, "/*/*", route_cors);
// routes // routes
GET(app, "/list", route_list); GET(app, "/list", route_list);
GET(app, "/get/:name", route_get); GET(app, "/get/:name", route_get);
ctorm_app_all(app, route_notfound); ctorm_app_default(app, route_notfound);
ctorm_app_local(app, "config", &conf); ctorm_app_local(app, "config", &conf);
ctorm_info("starting the web server on %s", host); ctorm_info("starting the web server on %s", host);
if (!ctorm_app_run(app, host)) if (!ctorm_app_run(app, host))
ctorm_fail("failed to start the app: %s", ctorm_geterror()); ctorm_fail("failed to start the app: %s", ctorm_error());
ctorm_app_free(app); ctorm_app_free(app);
return EXIT_SUCCESS; return EXIT_SUCCESS;

View File

@ -3,7 +3,8 @@
void route_cors(ctorm_req_t *req, ctorm_res_t *res) { void route_cors(ctorm_req_t *req, ctorm_res_t *res) {
RES_SET("Access-Control-Allow-Origin", "*"); RES_SET("Access-Control-Allow-Origin", "*");
RES_SET("Access-Control-Allow-Headers", RES_SET("Access-Control-Allow-Headers",
"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, " "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, "
"Authorization, accept, origin, Cache-Control, "
"X-Requested-With"); "X-Requested-With");
RES_SET("Access-Control-Allow-Methods", "PUT, DELETE, GET"); RES_SET("Access-Control-Allow-Methods", "PUT, DELETE, GET");
} }

View File

@ -1,31 +1,30 @@
#include <cjson/cJSON.h>
#include <dirent.h>
#include <linux/limits.h> #include <linux/limits.h>
#include <cjson/cJSON.h>
#include <ctorm/ctorm.h> #include <ctorm/ctorm.h>
#include <string.h> #include <dirent.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include "routes.h" #include "routes.h"
#include "config.h" #include "config.h"
#include "util.h"
#include "docs.h" #include "docs.h"
void route_get(ctorm_req_t *req, ctorm_res_t *res) { void route_get(ctorm_req_t *req, ctorm_res_t *res) {
config_t *conf = REQ_LOCAL("config"); config_t *conf = REQ_LOCAL("config");
char *doc_name = REQ_PARAM("name"); char *name = REQ_PARAM("name");
char *docs_dir = config_get(conf, "docs_dir"), *doc_data = NULL; char *dir = config_get(conf, "dir"), *doc_data = NULL;
cJSON *json = NULL, *doc_json = NULL; cJSON *json = NULL, *doc_json = NULL;
docs_t docs; docs_t docs;
if (NULL == doc_name) { if (NULL == name) {
ctorm_fail("documentation name not specified (how did that even happend)"); ctorm_fail("documentation name not specified (how did that even happend)");
util_send(res, 500, NULL); util_send(res, 500, NULL);
goto end; goto end;
} }
if (!docs_init(&docs, docs_dir)) { if (!docs_init(&docs, dir)) {
ctorm_fail("docs_init failed: %s", ctorm_geterror()); ctorm_fail("docs_init failed: %s", ctorm_error());
util_send(res, 500, NULL); util_send(res, 500, NULL);
goto end; goto end;
} }
@ -36,7 +35,7 @@ void route_get(ctorm_req_t *req, ctorm_res_t *res) {
goto end; goto end;
} }
while (NULL != (doc_data = docs_next(&docs, doc_name, false))) { while (NULL != (doc_data = docs_next(&docs, name, false))) {
if (NULL == (doc_json = cJSON_Parse(doc_data))) { if (NULL == (doc_json = cJSON_Parse(doc_data))) {
ctorm_fail("failed to parse JSON: %s (%s)", docs.name, docs.lang); ctorm_fail("failed to parse JSON: %s (%s)", docs.name, docs.lang);
continue; continue;
@ -53,7 +52,7 @@ void route_get(ctorm_req_t *req, ctorm_res_t *res) {
docs_reset(&docs); docs_reset(&docs);
while (NULL != (doc_data = docs_next(&docs, doc_name, true))) { while (NULL != (doc_data = docs_next(&docs, name, true))) {
if (NULL == (doc_json = cJSON_GetObjectItem(json, docs.lang))) if (NULL == (doc_json = cJSON_GetObjectItem(json, docs.lang)))
continue; continue;

View File

@ -3,21 +3,21 @@
#include <ctorm/ctorm.h> #include <ctorm/ctorm.h>
#include <dirent.h> #include <dirent.h>
#include <string.h>
#include <stdio.h> #include <stdio.h>
#include "routes.h" #include "routes.h"
#include "config.h" #include "config.h"
#include "util.h"
#include "docs.h" #include "docs.h"
void route_list(ctorm_req_t *req, ctorm_res_t *res) { void route_list(ctorm_req_t *req, ctorm_res_t *res) {
config_t *conf = REQ_LOCAL("config"); config_t *conf = REQ_LOCAL("config");
char *docs_dir = config_get(conf, "docs_dir"), *doc_data = NULL; char *dir = config_get(conf, "dir"), *doc_data = NULL;
cJSON *array = NULL, *json = NULL, *doc_json = NULL; cJSON *array = NULL, *json = NULL, *doc_json = NULL;
docs_t docs; docs_t docs;
if (!docs_init(&docs, docs_dir)) { if (!docs_init(&docs, dir)) {
ctorm_fail("docs_init failed: %s", ctorm_geterror()); ctorm_fail("docs_init failed: %s", ctorm_error());
util_send(res, 500, NULL); util_send(res, 500, NULL);
goto end; goto end;
} }
@ -31,7 +31,8 @@ void route_list(ctorm_req_t *req, ctorm_res_t *res) {
while (NULL != (doc_data = docs_next(&docs, NULL, false))) { while (NULL != (doc_data = docs_next(&docs, NULL, false))) {
if (NULL == (array = cJSON_GetObjectItem(json, docs.lang)) && if (NULL == (array = cJSON_GetObjectItem(json, docs.lang)) &&
NULL == (array = cJSON_AddArrayToObject(json, docs.lang))) { NULL == (array = cJSON_AddArrayToObject(json, docs.lang))) {
ctorm_fail("failed to create an array object for the language %s", docs.lang); ctorm_fail(
"failed to create an array object for the language %s", docs.lang);
continue; continue;
} }

View File

@ -1,17 +1,8 @@
#include <linux/limits.h> #include <linux/limits.h>
#include <ctorm/ctorm.h> #include <ctorm/ctorm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdint.h> #include <stdint.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <errno.h> #include <errno.h>
#include "util.h" #include "util.h"
@ -57,45 +48,3 @@ void util_send(ctorm_res_t *res, uint16_t code, cJSON *json) {
RES_JSON(json); RES_JSON(json);
} }
util_file_t *util_file_load(int dirfd, char *path) {
if (NULL == path) {
errno = EINVAL;
return NULL;
}
util_file_t *file = NULL;
struct stat buf;
int fd = -1;
if (NULL == (file = malloc(sizeof(util_file_t))))
goto end; // errno set by malloc
if ((fd = openat(dirfd, path, O_RDONLY)) < 0)
goto end; // errno set by open
if (fstat(fd, &buf) < 0)
goto end; // errno set by fstat
if (NULL == (file->content = mmap(0, (file->size = buf.st_size), PROT_READ, MAP_PRIVATE, fd, 0)))
goto end; // errno set by mmap
end:
if (fd != -1)
close(fd);
if (NULL == file->content) {
free(file);
return NULL;
}
return file;
}
void util_file_free(util_file_t *file) {
if (NULL == file)
return;
munmap(file->content, file->size);
free(file);
}