[OCaml] Mobile-friendly clone of cgit.
Reorganized everything.
Thank you DeepSeek!
Changed files
bin/dune
@@ -1,4 +1,4 @@
1
1
(executable
2
2
(public_name ogit)
3
3
(name main)
4
Removed:
(libraries dream handlers))
4
Added:
(libraries ogit dream))
bin/main.ml
@@ -2,7 +2,8 @@
2
2
Dream.run @@ Dream.logger
3
3
@@ Dream.router
4
4
[
5
Removed:
Dream.get "/" @@ Handlers.ogit_root;
6
Removed:
Dream.get "/:repo_name/" @@ Handlers.repo_root repo;
7
Removed:
(* Dream.get "/:repo_name/tree" Handlers.repo_tree; *)
5
Added:
Dream.get "/" (fun _ -> Ogit.Handlers.ogit_root ());
6
Added:
Dream.get "/:repo_name" (fun request ->
7
Added:
Ogit.Handlers.repo_root @@ Dream.param request "repo_name");
8
Added:
Dream.get "/static/**" (Dream.static "./lib/static");
8
9
]
lib/config.ml
@@ -0,0 +1,1 @@
1
Added:
let git_directory = Filename.concat (Unix.getenv "HOME") "git"
lib/dune
@@ -0,0 +1,5 @@
1
Added:
(library
2
Added:
(name ogit)
3
Added:
(libraries dream dream-html lwt_ppx git-unix)
4
Added:
(preprocess
5
Added:
(pps lwt_ppx)))
lib/handlers.ml
@@ -0,0 +1,4 @@
1
Added:
let ogit_root _ = Dream_html.respond @@ Views.Ogit_root.render ()
2
Added:
3
Added:
let repo_root repo_name =
4
Added:
Dream_html.respond @@ Lwt_main.run @@ Views.Repo_root.render repo_name
lib/handlers/dune
@@ -1,4 +0,0 @@
1
Removed:
(library
2
Removed:
(name handlers)
3
Removed:
(public_name ogit.handlers)
4
Removed:
(libraries dream views))
lib/handlers/handlers.ml
@@ -1,13 +0,0 @@
1
Removed:
let ogit_root _ = Dream_html.respond @@ Views.Ogit_root.render ()
2
Removed:
let repo_root repo = Dream_html.respond @@ Views.Repo.render repo
3
Removed:
4
Removed:
(* let repo_tree request = *)
5
Removed:
(* let repo_name = Dream.param request "repo_name" in *)
6
Removed:
(* let repo_path = Filename.concat (Unix.getenv "HOME") ("git/" ^ repo_name) in *)
7
Removed:
(* let content = *)
8
Removed:
(* if Sys.file_exists repo_path && Sys.is_directory repo_path then *)
9
Removed:
(* let files = Helpers.File_helpers.list_files repo_path in *)
10
Removed:
(* "<h1>Browsing repository: " ^ repo_name ^ "</h1><ul>" ^ files ^ "</ul>" *)
11
Removed:
(* else "<h1>Repository not found: " ^ repo_name ^ "</h1>" *)
12
Removed:
(* in *)
13
Removed:
(* Dream.html (page_layout ~content) *)
lib/static/styles.css
@@ -0,0 +1,27 @@
1
Added:
body {
2
Added:
font-family: sans-serif;
3
Added:
}
4
Added:
5
Added:
nav#top {
6
Added:
background-color: dimgray;
7
Added:
}
8
Added:
9
Added:
nav#top ul {
10
Added:
display: flex;
11
Added:
list-style-type: none;
12
Added:
}
13
Added:
14
Added:
nav#top ul li {
15
Added:
padding: 1em 0;
16
Added:
}
17
Added:
18
Added:
nav#top ul li a {
19
Added:
color: white;
20
Added:
text-decoration: none;
21
Added:
padding: 0.5em 1em;
22
Added:
}
23
Added:
24
Added:
nav#top ul li a:hover {
25
Added:
background-color: #555;
26
Added:
text-decoration: revert;
27
Added:
}
lib/views.ml
@@ -0,0 +1,136 @@
1
Added:
type page_data = { title : string; main_content : Dream_html.node list }
2
Added:
3
Added:
module Layout = struct
4
Added:
open Dream_html
5
Added:
open HTML
6
Added:
7
Added:
let header =
8
Added:
null
9
Added:
[
10
Added:
h1 [] [ txt "ogit" ];
11
Added:
h2 [] [ txt "A mobile-friendly Git repository viewer" ];
12
Added:
]
13
Added:
14
Added:
let topnav =
15
Added:
nav
16
Added:
[ id "top" ]
17
Added:
[
18
Added:
ul []
19
Added:
[
20
Added:
li [] [ a [ href "/" ] [ txt "Home" ] ];
21
Added:
li [] [ a [ href "/" ] [ txt "Refs" ] ];
22
Added:
li [] [ a [ href "/" ] [ txt "Log" ] ];
23
Added:
li [] [ a [ href "/" ] [ txt "Tree" ] ];
24
Added:
li [] [ a [ href "/" ] [ txt "Commit" ] ];
25
Added:
li [] [ a [ href "/" ] [ txt "Diff" ] ];
26
Added:
];
27
Added:
]
28
Added:
29
Added:
let footer name =
30
Added:
let today = Unix.localtime (Unix.time ()) in
31
Added:
let year = today.Unix.tm_year + 1900 |> string_of_int in
32
Added:
let space = " " in
33
Added:
let footer_text = String.concat space [ "©"; year; name ] in
34
Added:
footer [] [ txt "%s" footer_text ]
35
Added:
36
Added:
let application page =
37
Added:
html []
38
Added:
[
39
Added:
head [] [ title [] "%s" page.title ];
40
Added:
link [ rel "stylesheet"; href "/static/styles.css" ];
41
Added:
body []
42
Added:
[
43
Added:
header;
44
Added:
topnav;
45
Added:
div [ id "main" ] page.main_content;
46
Added:
footer "Marius PETER";
47
Added:
];
48
Added:
]
49
Added:
end
50
Added:
51
Added:
module Ogit_root = struct
52
Added:
open Dream_html
53
Added:
open HTML
54
Added:
55
Added:
let repositories_in directory =
56
Added:
let repositories = Sys.readdir directory |> Array.to_list
57
Added:
and li_of_repo repo = li [] [ a [ href "%s" repo ] [ txt "%s" repo ] ] in
58
Added:
div [ id "repositories" ] [ ul [] @@ List.map li_of_repo repositories ]
59
Added:
60
Added:
let page_data =
61
Added:
{
62
Added:
title = "My repositories";
63
Added:
main_content = [ repositories_in Config.git_directory ];
64
Added:
}
65
Added:
66
Added:
let render () = Layout.application page_data
67
Added:
end
68
Added:
69
Added:
module Repo_root = struct
70
Added:
open Dream_html
71
Added:
open HTML
72
Added:
open Git
73
Added:
open Lwt.Infix
74
Added:
75
Added:
let page_promise repo_name =
76
Added:
let repo_path = Filename.concat Config.git_directory repo_name in
77
Added:
78
Added:
(* 1. Open the Git repository *)
79
Added:
let%lwt repo =
80
Added:
Git_unix.Store.v (Fpath.v repo_path) >>= function
81
Added:
| Ok repo -> Lwt.return repo
82
Added:
| Error _ -> Lwt.fail_with "Could not open the Git repository."
83
Added:
in
84
Added:
85
Added:
(* 2. Resolve HEAD to get the latest commit hash *)
86
Added:
let%lwt commit_hash =
87
Added:
Git_unix.Store.Ref.resolve repo Reference.head >>= function
88
Added:
| Ok hash -> Lwt.return hash
89
Added:
| Error _ -> Lwt.fail_with "Failed to resolve HEAD"
90
Added:
in
91
Added:
92
Added:
(* (\* 3. Read the commit *\) *)
93
Added:
(* let%lwt commit = *)
94
Added:
(* Commit.v repo commit_hash >>= function *)
95
Added:
(* | Ok commit -> Lwt.return commit *)
96
Added:
(* | Error _ -> Lwt.fail_with "Failed to read the commit." *)
97
Added:
(* in *)
98
Added:
99
Added:
(* (\* 4. Read the tree from the commit *\) *)
100
Added:
(* let%lwt tree = *)
101
Added:
(* Tree.v commit.Commit.tree >>= function *)
102
Added:
(* | Ok tree -> Lwt.return tree *)
103
Added:
(* | Error _ -> Lwt.fail_with "Failed to read the tree." *)
104
Added:
(* in *)
105
Added:
106
Added:
(* (\* 5. Generate HTML list items for each tree entry *\) *)
107
Added:
(* let entries = Tree.entries tree in *)
108
Added:
(* let items = *)
109
Added:
(* List.map *)
110
Added:
(* (fun entry -> *)
111
Added:
(* let icon = match entry.Tree.perm with `Dir -> "📁 " | _ -> "📄 " in *)
112
Added:
(* li [] [ a [ href "#" ] [ txt (icon ^ entry.Tree.name) ] ]) *)
113
Added:
(* entries *)
114
Added:
(* in *)
115
Added:
116
Added:
(* 6. Assemble the page *)
117
Added:
let main_content =
118
Added:
[
119
Added:
ul []
120
Added:
[ li [] [ txt "%s" (commit_hash |> Digestif.SHA1.to_raw_string) ] ];
121
Added:
]
122
Added:
in
123
Added:
let page_data = { title = repo_name; main_content } in
124
Added:
Lwt.return (Layout.application page_data)
125
Added:
126
Added:
let render repo_name = page_promise repo_name >>= fun page -> Lwt.return page
127
Added:
end
128
Added:
129
Added:
module Repo_tree = struct
130
Added:
open Dream_html
131
Added:
132
Added:
let render repo_name =
133
Added:
let title = repo_name and main_content = [ txt "foobar" ] in
134
Added:
let page_data = { title; main_content } in
135
Added:
Layout.application page_data
136
Added:
end
lib/views/dune
@@ -1,4 +0,0 @@
1
Removed:
(library
2
Removed:
(name views)
3
Removed:
(public_name ogit.views)
4
Removed:
(libraries dream-html git-unix))
lib/views/layouts.ml
@@ -1,45 +0,0 @@
1
Removed:
open Dream_html
2
Removed:
open HTML
3
Removed:
4
Removed:
let header =
5
Removed:
div []
6
Removed:
[
7
Removed:
h1 [] [ txt "ogit" ];
8
Removed:
h2 [] [ txt "A mobile-friendly Git repository viewer" ];
9
Removed:
]
10
Removed:
11
Removed:
let topnav =
12
Removed:
nav
13
Removed:
[ id "top" ]
14
Removed:
[
15
Removed:
ul []
16
Removed:
[
17
Removed:
li [] [ a [ href "/" ] [ txt "Home" ] ];
18
Removed:
li [] [ a [ href "/" ] [ txt "Refs" ] ];
19
Removed:
li [] [ a [ href "/" ] [ txt "Log" ] ];
20
Removed:
li [] [ a [ href "/" ] [ txt "Tree" ] ];
21
Removed:
li [] [ a [ href "/" ] [ txt "Commit" ] ];
22
Removed:
li [] [ a [ href "/" ] [ txt "Diff" ] ];
23
Removed:
];
24
Removed:
]
25
Removed:
26
Removed:
let footer name =
27
Removed:
let today = Unix.localtime (Unix.time ()) in
28
Removed:
let year = string_of_int (today.Unix.tm_year + 1900) in
29
Removed:
let space = " " in
30
Removed:
let footer_text = String.concat space [ "©"; year; name ] in
31
Removed:
footer [] [ txt "%s" footer_text ]
32
Removed:
33
Removed:
let application ?(page_title = "ogit") ?(head_content = null []) ~main_content
34
Removed:
() =
35
Removed:
html []
36
Removed:
[
37
Removed:
head [] [ title [] "%s" page_title; head_content ];
38
Removed:
body []
39
Removed:
[
40
Removed:
header;
41
Removed:
topnav;
42
Removed:
div [ id "main" ] [ main_content ];
43
Removed:
footer "Marius PETER";
44
Removed:
];
45
Removed:
]
lib/views/ogit_root.ml
@@ -1,12 +0,0 @@
1
Removed:
open Dream_html
2
Removed:
open HTML
3
Removed:
4
Removed:
let git_directory = Filename.concat (Unix.getenv "HOME") "git"
5
Removed:
6
Removed:
let repositories_in directory =
7
Removed:
let repositories = Sys.readdir directory |> Array.to_list
8
Removed:
and li_of_repo repo = li [] [ a [ href "%s" repo ] [ txt "%s" repo ] ] in
9
Removed:
div [ id "repositories" ] [ ul [] @@ List.map li_of_repo repositories ]
10
Removed:
11
Removed:
let main_content = null [ txt "Hello World!"; repositories_in git_directory ]
12
Removed:
let render = Layouts.application ~page_title:"My repositories" ~main_content
lib/views/repo_root.ml
@@ -1,41 +0,0 @@
1
Removed:
open Dream_html
2
Removed:
open HTML
3
Removed:
open Git_unix
4
Removed:
5
Removed:
let git_directory = Filename.concat (Unix.getenv "HOME") "git"
6
Removed:
7
Removed:
let err_to_string err = Fmt.to_to_string Store.pp_error err
8
Removed:
9
Removed:
let get_head_commit repo_path =
10
Removed:
Lwt_main.run (
11
Removed:
let open Lwt.Infix in
12
Removed:
match Git.Reference.of_string "HEAD" with
13
Removed:
| Error err ->
14
Removed:
Lwt.return (Error ("Invalid HEAD reference string: " ^ err_to_string err))
15
Removed:
| Ok head_ref_name ->
16
Removed:
Store.v (Fpath.v repo_path) >>= function
17
Removed:
| Error err ->
18
Removed:
Lwt.return (Error ("Failed to open repository: " ^ err_to_string err))
19
Removed:
| Ok store ->
20
Removed:
Store.Ref.resolve store head_ref_name >>= function
21
Removed:
| Error err ->
22
Removed:
Lwt.return (Error ("Failed to resolve HEAD: " ^ err_to_string err))
23
Removed:
| Ok head_oid -> Lwt.return (Ok head_oid)
24
Removed:
(* Git.Commit.v store head_oid >>= function *)
25
Removed:
(* | Error err -> *)
26
Removed:
(* Lwt.return (Error ("Failed to get commit from hash: " ^ err_to_string err)) *)
27
Removed:
(* | Ok commit -> *)
28
Removed:
(* Lwt.return (Ok commit) *)
29
Removed:
)
30
Removed:
31
Removed:
let main_content =
32
Removed:
let open Lwt.Infix in
33
Removed:
Lwt.async (fun _ ->
34
Removed:
get_head_commit git_directory >>= function
35
Removed:
| Error msg ->
36
Removed:
Lwt.map (fun content -> [ p [] [ txt content ]]) (Error msg)
37
Removed:
| Ok head_oid ->
38
Removed:
Lwt.map (fun content -> [ p [] [ txt "HEAD commit hash: %s" content ]]) (Store.Hash.to_string head_oid)
39
Removed:
)
40
Removed:
41
Removed:
let render = Layouts.application ~page_title:"My repositories" ~main_content