Hi… I am well aware that this diff view is very suboptimal. It will be fixed when the refactored server comes along!
Add SPDX license headers to Makefile, SQL, and CSS
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-FileContributor: Runxi Yu <https://runxiyu.org>
.PHONY: clean version.go CFLAGS = -Wall -Wextra -Werror -pedantic -std=c99 -D_GNU_SOURCE forge: $(filter-out forge,$(wildcard *)) version.go git_hooks_client/*.c git_hooks_client/git_hooks_client go mod vendor CGO_ENABLED=0 go build -o $@ -ldflags '-extldflags "-f no-PIC -static"' -tags 'osusergo netgo static_build' . git_hooks_client/git_hooks_client: version.go: printf 'package main\nconst VERSION="%s"\n' $(shell git describe --tags --always --dirty) > $@ clean: $(RM) forge version.go vendor
-- SPDX-License-Identifier: AGPL-3.0-only -- SPDX-FileContributor: Runxi Yu <https://runxiyu.org>
CREATE TABLE groups (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
description TEXT
);
CREATE TABLE repos (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE RESTRICT, -- I mean, should be CASCADE but deleting Git repos on disk also needs to be considered
contrib_requirements TEXT NOT NULL CHECK (contrib_requirements IN ('closed', 'registered_user', 'ssh_pubkey', 'public')),
name TEXT NOT NULL,
UNIQUE(group_id, name),
description TEXT,
filesystem_path TEXT
);
CREATE TABLE ticket_trackers (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE RESTRICT,
name TEXT NOT NULL,
UNIQUE(group_id, name),
description TEXT
);
CREATE TABLE tickets (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
tracker_id INTEGER NOT NULL REFERENCES ticket_trackers(id) ON DELETE CASCADE,
title TEXT NOT NULL,
description TEXT
);
CREATE TABLE mailing_lists (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE RESTRICT,
name TEXT NOT NULL,
UNIQUE(group_id, name),
description TEXT
);
CREATE TABLE mailing_list_emails (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
list_id INTEGER NOT NULL REFERENCES mailing_lists(id) ON DELETE CASCADE,
title TEXT NOT NULL,
sender TEXT NOT NULL,
date TIMESTAMP NOT NULL,
content BYTEA NOT NULL
);
CREATE TABLE users (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
username TEXT UNIQUE,
type TEXT NOT NULL CHECK (type IN ('pubkey_only', 'registered')),
password TEXT
);
CREATE TABLE ssh_public_keys (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
key_string TEXT NOT NULL,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT unique_key_string EXCLUDE USING HASH (key_string WITH =)
);
CREATE TABLE sessions (
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
session_id TEXT PRIMARY KEY NOT NULL,
UNIQUE(user_id, session_id)
);
-- TODO:
CREATE TABLE merge_requests (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
title TEXT,
repo_id INTEGER NOT NULL REFERENCES repos(id) ON DELETE CASCADE,
creator INTEGER REFERENCES users(id) ON DELETE SET NULL,
source_ref TEXT NOT NULL,
destination_branch TEXT,
status TEXT NOT NULL CHECK (status IN ('open', 'merged', 'closed')),
UNIQUE (repo_id, source_ref, destination_branch),
UNIQUE (repo_id, id)
);
CREATE TABLE user_group_roles (
group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY(user_id, group_id)
);
/* * SPDX-License-Identifier: AGPL-3.0-only * SPDX-FileContributor: Runxi Yu <https://runxiyu.org> */
@media (prefers-color-scheme: light) {
/* Background */ .bg { ; }
/* PreWrapper */ .chroma { ; }
/* Error */ .chroma .err { }
/* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
/* LineHighlight */ .chroma .hl { background-color: #e5e5e5 }
/* LineNumbersTable */ .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* LineNumbers */ .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* Line */ .chroma .line { display: flex; }
/* Keyword */ .chroma .k { color: #008000; font-weight: bold }
/* KeywordConstant */ .chroma .kc { color: #008000; font-weight: bold }
/* KeywordDeclaration */ .chroma .kd { color: #008000; font-weight: bold }
/* KeywordNamespace */ .chroma .kn { color: #008000; font-weight: bold }
/* KeywordPseudo */ .chroma .kp { color: #008000 }
/* KeywordReserved */ .chroma .kr { color: #008000; font-weight: bold }
/* KeywordType */ .chroma .kt { color: #b00040 }
/* NameAttribute */ .chroma .na { color: #7d9029 }
/* NameBuiltin */ .chroma .nb { color: #008000 }
/* NameClass */ .chroma .nc { color: #0000ff; font-weight: bold }
/* NameConstant */ .chroma .no { color: #880000 }
/* NameDecorator */ .chroma .nd { color: #aa22ff }
/* NameEntity */ .chroma .ni { color: #999999; font-weight: bold }
/* NameException */ .chroma .ne { color: #d2413a; font-weight: bold }
/* NameFunction */ .chroma .nf { color: #0000ff }
/* NameLabel */ .chroma .nl { color: #a0a000 }
/* NameNamespace */ .chroma .nn { color: #0000ff; font-weight: bold }
/* NameTag */ .chroma .nt { color: #008000; font-weight: bold }
/* NameVariable */ .chroma .nv { color: #19177c }
/* LiteralString */ .chroma .s { color: #ba2121 }
/* LiteralStringAffix */ .chroma .sa { color: #ba2121 }
/* LiteralStringBacktick */ .chroma .sb { color: #ba2121 }
/* LiteralStringChar */ .chroma .sc { color: #ba2121 }
/* LiteralStringDelimiter */ .chroma .dl { color: #ba2121 }
/* LiteralStringDoc */ .chroma .sd { color: #ba2121; font-style: italic }
/* LiteralStringDouble */ .chroma .s2 { color: #ba2121 }
/* LiteralStringEscape */ .chroma .se { color: #bb6622; font-weight: bold }
/* LiteralStringHeredoc */ .chroma .sh { color: #ba2121 }
/* LiteralStringInterpol */ .chroma .si { color: #bb6688; font-weight: bold }
/* LiteralStringOther */ .chroma .sx { color: #008000 }
/* LiteralStringRegex */ .chroma .sr { color: #bb6688 }
/* LiteralStringSingle */ .chroma .s1 { color: #ba2121 }
/* LiteralStringSymbol */ .chroma .ss { color: #19177c }
/* LiteralNumber */ .chroma .m { color: #666666 }
/* LiteralNumberBin */ .chroma .mb { color: #666666 }
/* LiteralNumberFloat */ .chroma .mf { color: #666666 }
/* LiteralNumberHex */ .chroma .mh { color: #666666 }
/* LiteralNumberInteger */ .chroma .mi { color: #666666 }
/* LiteralNumberIntegerLong */ .chroma .il { color: #666666 }
/* LiteralNumberOct */ .chroma .mo { color: #666666 }
/* Operator */ .chroma .o { color: #666666 }
/* OperatorWord */ .chroma .ow { color: #aa22ff; font-weight: bold }
/* Comment */ .chroma .c { color: #408080; font-style: italic }
/* CommentHashbang */ .chroma .ch { color: #408080; font-style: italic }
/* CommentMultiline */ .chroma .cm { color: #408080; font-style: italic }
/* CommentSingle */ .chroma .c1 { color: #408080; font-style: italic }
/* CommentSpecial */ .chroma .cs { color: #408080; font-style: italic }
/* CommentPreproc */ .chroma .cp { color: #bc7a00 }
/* CommentPreprocFile */ .chroma .cpf { color: #bc7a00 }
/* GenericDeleted */ .chroma .gd { color: #a00000 }
/* GenericEmph */ .chroma .ge { font-style: italic }
/* GenericError */ .chroma .gr { color: #ff0000 }
/* GenericHeading */ .chroma .gh { color: #000080; font-weight: bold }
/* GenericInserted */ .chroma .gi { color: #00a000 }
/* GenericOutput */ .chroma .go { color: #888888 }
/* GenericPrompt */ .chroma .gp { color: #000080; font-weight: bold }
/* GenericStrong */ .chroma .gs { font-weight: bold }
/* GenericSubheading */ .chroma .gu { color: #800080; font-weight: bold }
/* GenericTraceback */ .chroma .gt { color: #0044dd }
/* GenericUnderline */ .chroma .gl { text-decoration: underline }
/* TextWhitespace */ .chroma .w { color: #bbbbbb }
}
@media (prefers-color-scheme: dark) {
/* Background */ .bg { color: #e6edf3; background-color: #000000; }
/* PreWrapper */ .chroma { color: #e6edf3; background-color: #000000; }
/* Error */ .chroma .err { color: #f85149 }
/* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
/* LineHighlight */ .chroma .hl { background-color: #6e7681 }
/* LineNumbersTable */ .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #737679 }
/* LineNumbers */ .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #6e7681 }
/* Line */ .chroma .line { display: flex; }
/* Keyword */ .chroma .k { color: #ff7b72 }
/* KeywordConstant */ .chroma .kc { color: #79c0ff }
/* KeywordDeclaration */ .chroma .kd { color: #ff7b72 }
/* KeywordNamespace */ .chroma .kn { color: #ff7b72 }
/* KeywordPseudo */ .chroma .kp { color: #79c0ff }
/* KeywordReserved */ .chroma .kr { color: #ff7b72 }
/* KeywordType */ .chroma .kt { color: #ff7b72 }
/* NameClass */ .chroma .nc { color: #f0883e; font-weight: bold }
/* NameConstant */ .chroma .no { color: #79c0ff; font-weight: bold }
/* NameDecorator */ .chroma .nd { color: #d2a8ff; font-weight: bold }
/* NameEntity */ .chroma .ni { color: #ffa657 }
/* NameException */ .chroma .ne { color: #f0883e; font-weight: bold }
/* NameFunction */ .chroma .nf { color: #d2a8ff; font-weight: bold }
/* NameLabel */ .chroma .nl { color: #79c0ff; font-weight: bold }
/* NameNamespace */ .chroma .nn { color: #ff7b72 }
/* NameProperty */ .chroma .py { color: #79c0ff }
/* NameTag */ .chroma .nt { color: #7ee787 }
/* NameVariable */ .chroma .nv { color: #79c0ff }
/* Literal */ .chroma .l { color: #a5d6ff }
/* LiteralDate */ .chroma .ld { color: #79c0ff }
/* LiteralString */ .chroma .s { color: #a5d6ff }
/* LiteralStringAffix */ .chroma .sa { color: #79c0ff }
/* LiteralStringBacktick */ .chroma .sb { color: #a5d6ff }
/* LiteralStringChar */ .chroma .sc { color: #a5d6ff }
/* LiteralStringDelimiter */ .chroma .dl { color: #79c0ff }
/* LiteralStringDoc */ .chroma .sd { color: #a5d6ff }
/* LiteralStringDouble */ .chroma .s2 { color: #a5d6ff }
/* LiteralStringEscape */ .chroma .se { color: #79c0ff }
/* LiteralStringHeredoc */ .chroma .sh { color: #79c0ff }
/* LiteralStringInterpol */ .chroma .si { color: #a5d6ff }
/* LiteralStringOther */ .chroma .sx { color: #a5d6ff }
/* LiteralStringRegex */ .chroma .sr { color: #79c0ff }
/* LiteralStringSingle */ .chroma .s1 { color: #a5d6ff }
/* LiteralStringSymbol */ .chroma .ss { color: #a5d6ff }
/* LiteralNumber */ .chroma .m { color: #a5d6ff }
/* LiteralNumberBin */ .chroma .mb { color: #a5d6ff }
/* LiteralNumberFloat */ .chroma .mf { color: #a5d6ff }
/* LiteralNumberHex */ .chroma .mh { color: #a5d6ff }
/* LiteralNumberInteger */ .chroma .mi { color: #a5d6ff }
/* LiteralNumberIntegerLong */ .chroma .il { color: #a5d6ff }
/* LiteralNumberOct */ .chroma .mo { color: #a5d6ff }
/* Operator */ .chroma .o { color: #ff7b72; font-weight: bold }
/* OperatorWord */ .chroma .ow { color: #ff7b72; font-weight: bold }
/* Comment */ .chroma .c { color: #8b949e; font-style: italic }
/* CommentHashbang */ .chroma .ch { color: #8b949e; font-style: italic }
/* CommentMultiline */ .chroma .cm { color: #8b949e; font-style: italic }
/* CommentSingle */ .chroma .c1 { color: #8b949e; font-style: italic }
/* CommentSpecial */ .chroma .cs { color: #8b949e; font-weight: bold; font-style: italic }
/* CommentPreproc */ .chroma .cp { color: #8b949e; font-weight: bold; font-style: italic }
/* CommentPreprocFile */ .chroma .cpf { color: #8b949e; font-weight: bold; font-style: italic }
/* GenericDeleted */ .chroma .gd { color: #ffa198; background-color: #490202 }
/* GenericEmph */ .chroma .ge { font-style: italic }
/* GenericError */ .chroma .gr { color: #ffa198 }
/* GenericHeading */ .chroma .gh { color: #79c0ff; font-weight: bold }
/* GenericInserted */ .chroma .gi { color: #56d364; background-color: #0f5323 }
/* GenericOutput */ .chroma .go { color: #8b949e }
/* GenericPrompt */ .chroma .gp { color: #8b949e }
/* GenericStrong */ .chroma .gs { font-weight: bold }
/* GenericSubheading */ .chroma .gu { color: #79c0ff }
/* GenericTraceback */ .chroma .gt { color: #ff7b72 }
/* GenericUnderline */ .chroma .gl { text-decoration: underline }
/* TextWhitespace */ .chroma .w { color: #6e7681 }
}
/* * SPDX-License-Identifier: AGPL-3.0-only * SPDX-FileContributor: Runxi Yu <https://runxiyu.org> */
/* Base styles and variables */
html {
font-family: sans-serif;
background-color: var(--background-color);
color: var(--text-color);
--background-color: hsl(0, 0%, 100%);
--text-color: hsl(0, 0%, 0%);
--link-color: hsl(320, 50%, 36%);
--light-text-color: hsl(0, 0%, 45%);
--darker-border-color: hsl(0, 0%, 72%);
--lighter-border-color: hsl(0, 0%, 85%);
--text-decoration-color: hsl(0, 0%, 72%);
--darker-box-background-color: hsl(0, 0%, 92%);
--lighter-box-background-color: hsl(0, 0%, 95%);
--primary-color: hsl(320, 50%, 36%);
--primary-color-contrast: hsl(320, 0%, 100%);
--danger-color: hsl(0, 50%, 36%);
--danger-color-contrast: hsl(0, 0%, 100%);
}
/* Dark mode overrides */
@media (prefers-color-scheme: dark) {
html {
--background-color: hsl(0, 0%, 0%);
--text-color: hsl(0, 0%, 100%);
--link-color: hsl(320, 50%, 76%);
--light-text-color: hsl(0, 0%, 78%);
--darker-border-color: hsl(0, 0%, 35%);
--lighter-border-color: hsl(0, 0%, 25%);
--text-decoration-color: hsl(0, 0%, 30%);
--darker-box-background-color: hsl(0, 0%, 20%);
--lighter-box-background-color: hsl(0, 0%, 15%);
}
}
/* Global layout */
body {
margin: 0;
}
html, code, pre {
font-size: 0.96rem; /* TODO: Not always correct */
}
/* Toggle table controls */
.toggle-table-off, .toggle-table-on {
opacity: 0;
position: absolute;
}
.toggle-table-off:focus-visible + table > thead > tr > th > label,
.toggle-table-on:focus-visible + table > thead > tr > th > label {
outline: 1.5px var(--primary-color) solid;
}
.toggle-table-off + table > thead > tr > th, .toggle-table-on + table > thead > tr > th {
padding: 0;
}
.toggle-table-off + table > thead > tr > th > label, .toggle-table-on + table > thead > tr > th > label {
width: 100%;
display: inline-block;
padding: 3px 0;
cursor: pointer;
}
.toggle-table-off:checked + table > tbody {
display: none;
}
.toggle-table-on + table > tbody {
display: none;
}
.toggle-table-on:checked + table > tbody {
display: table-row-group;
}
/* Footer styles */
footer {
margin-top: 1rem;
margin-left: auto;
margin-right: auto;
display: block;
padding: 0 5px;
width: fit-content;
text-align: center;
color: var(--light-text-color);
}
footer a:link, footer a:visited {
color: inherit;
}
/* Padding containers */
.padding-wrapper {
margin: 1rem auto;
max-width: 60rem;
padding: 0 5px;
}
.padding {
padding: 0 5px;
}
/* Link styles */
a:link, a:visited {
text-decoration-color: var(--text-decoration-color);
color: var(--link-color);
}
/* Readme inline code styling */
#readme code:not(pre > code) {
background-color: var(--lighter-box-background-color);
border-radius: 2px;
padding: 2px;
}
/* Readme word breaks to avoid overfull hboxes */
#readme {
word-break: break-word;
}
/* Table styles */
table {
border: var(--lighter-border-color) solid 1px;
border-spacing: 0px;
border-collapse: collapse;
}
table.wide {
width: 100%;
}
td, th {
padding: 3px 5px;
border: var(--lighter-border-color) solid 1px;
}
.pad {
padding: 3px 5px;
}
th, thead, tfoot {
background-color: var(--lighter-box-background-color);
}
th[scope=row] {
text-align: left;
}
tr.title-row > th, th.title-row, .title-row {
background-color: var(--lighter-box-background-color);
}
td > pre {
margin: 0;
}
#readme > *:last-child {
margin-bottom: 0;
}
#readme > *:first-child {
margin-top: 0;
}
/* Table misc and scrolling */
.commit-id {
font-family: monospace;
word-break: break-word;
}
.scroll {
overflow-x: auto;
}
/* Diff/chunk styles */
.chunk-unchanged {
color: grey;
}
.chunk-addition {
background-color: green;
}
@media (prefers-color-scheme: dark) {
.chunk-addition {
background-color: lime;
}
}
.chunk-deletion {
background-color: red;
}
.chunk-unknown {
background-color: yellow;
}
pre.chunk {
margin-top: 0;
margin-bottom: 0;
}
.centering {
text-align: center;
}
/* Toggle content sections */
.toggle-off-wrapper, .toggle-on-wrapper {
border: var(--lighter-border-color) solid 1px;
}
.toggle-off-toggle, .toggle-on-toggle {
opacity: 0;
position: absolute;
}
.toggle-off-header, .toggle-on-header {
font-weight: bold;
cursor: pointer;
display: block;
width: 100%;
background-color: var(--lighter-box-background-color);
}
.toggle-off-header > div, .toggle-on-header > div {
padding: 3px 5px;
display: block;
}
.toggle-on-content {
display: none;
}
.toggle-on-toggle:focus-visible + .toggle-on-header, .toggle-off-toggle:focus-visible + .toggle-off-header {
outline: 1.5px var(--primary-color) solid;
}
.toggle-on-toggle:checked + .toggle-on-header + .toggle-on-content {
display: block;
}
.toggle-off-content {
display: block;
}
.toggle-off-toggle:checked + .toggle-off-header + .toggle-off-content {
display: none;
}
/* File display styles */
.file-patch + .file-patch {
margin-top: 0.5rem;
}
.file-content {
padding: 3px 5px;
}
.file-header {
font-family: monospace;
}
/* Form elements */
textarea {
box-sizing: border-box;
background-color: var(--lighter-box-background-color);
resize: vertical;
}
textarea,
input[type=text],
input[type=password] {
font-family: sans-serif;
font-size: smaller;
background-color: var(--lighter-box-background-color);
color: var(--text-color);
border: none;
padding: 0.3rem;
width: 100%;
box-sizing: border-box;
}
td.tdinput, th.tdinput {
padding: 0;
}
td.tdinput textarea,
td.tdinput input[type=text],
td.tdinput input[type=password],
th.tdinput textarea,
th.tdinput input[type=text],
th.tdinput input[type=password] {
background-color: transparent;
}
/* Button styles */
.btn-primary, a.btn-primary {
background: var(--primary-color);
color: var(--primary-color-contrast);
border: var(--lighter-border-color) 1px solid;
font-weight: bold;
}
.btn-danger, a.btn-danger {
background: var(--danger-color);
color: var(--danger-color-contrast);
border: var(--lighter-border-color) 1px solid;
font-weight: bold;
}
.btn-white, a.btn-white {
background: var(--primary-color-contrast);
color: var(--primary-color);
border: var(--lighter-border-color) 1px solid;
}
.btn-normal, a.btn-normal,
input[type=file]::file-selector-button {
background: var(--lighter-box-background-color);
border: var(--lighter-border-color) 1px solid !important;
color: var(--text-color);
}
.btn, .btn-white, .btn-danger, .btn-normal, .btn-primary,
input[type=submit],
input[type=file]::file-selector-button {
display: inline-block;
width: auto;
min-width: fit-content;
border-radius: 0;
padding: .1rem .75rem;
font-size: 0.9rem;
transition: background .1s linear;
cursor: pointer;
}
a.btn, a.btn-white, a.btn-danger, a.btn-normal, a.btn-primary {
text-decoration: none;
}
/* Header layout */
header#main-header {
background-color: var(--lighter-box-background-color);
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
}
header#main-header > div#main-header-forge-title {
flex-grow: 1;
}
header#main-header > div#main-header-user {
display: flex;
align-items: center;
}