Lindenii Project Forge
Commit info | |
---|---|
ID | bb8e3467f6797ccdf8f98f97485134de3018b9d3 |
Author | Runxi Yu<me@runxiyu.org> |
Author date | Mon, 03 Mar 2025 21:18:19 +0800 |
Committer | Runxi Yu<me@runxiyu.org> |
Committer date | Mon, 03 Mar 2025 21:18:19 +0800 |
Actions | Get patch |
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; }