mirror of
https://github.com/Alexander-D-Karpov/webring.git
synced 2026-03-16 22:07:41 +03:00
Compare commits
No commits in common. "master" and "v-2028362" have entirely different histories.
44
.air.toml
44
.air.toml
|
|
@ -1,44 +0,0 @@
|
||||||
root = "."
|
|
||||||
testdata_dir = "testdata"
|
|
||||||
tmp_dir = "tmp"
|
|
||||||
|
|
||||||
[build]
|
|
||||||
args_bin = []
|
|
||||||
bin = "./tmp/main"
|
|
||||||
cmd = "go build -o ./tmp/main ./cmd/server"
|
|
||||||
delay = 1000
|
|
||||||
exclude_dir = ["assets", "tmp", "vendor", "testdata", "docs"]
|
|
||||||
exclude_file = []
|
|
||||||
exclude_regex = ["_test.go"]
|
|
||||||
exclude_unchanged = false
|
|
||||||
follow_symlink = false
|
|
||||||
full_bin = ""
|
|
||||||
include_dir = []
|
|
||||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
|
||||||
include_file = []
|
|
||||||
kill_delay = "0s"
|
|
||||||
log = "build-errors.log"
|
|
||||||
poll = false
|
|
||||||
poll_interval = 0
|
|
||||||
rerun = false
|
|
||||||
rerun_delay = 500
|
|
||||||
send_interrupt = false
|
|
||||||
stop_on_root = false
|
|
||||||
|
|
||||||
[color]
|
|
||||||
app = ""
|
|
||||||
build = "yellow"
|
|
||||||
main = "magenta"
|
|
||||||
runner = "green"
|
|
||||||
watcher = "cyan"
|
|
||||||
|
|
||||||
[log]
|
|
||||||
main_only = false
|
|
||||||
time = false
|
|
||||||
|
|
||||||
[misc]
|
|
||||||
clean_on_exit = false
|
|
||||||
|
|
||||||
[screen]
|
|
||||||
clear_on_rebuild = false
|
|
||||||
keep_scroll = true
|
|
||||||
|
|
@ -1,15 +1,5 @@
|
||||||
PORT=8080
|
PORT=8000
|
||||||
DB_CONNECTION_STRING=postgres://postgres:postgres@localhost:5432/webring?sslmode=disable
|
DB_CONNECTION_STRING=postgres://postgres:postgres@localhost:5432/webring?sslmode=disable
|
||||||
DASHBOARD_USER=admin
|
DASHBOARD_USER=admin
|
||||||
DASHBOARD_PASSWORD=admin
|
DASHBOARD_PASSWORD=admin
|
||||||
CONTACT_LINK=mailto:webring@example.com
|
CONTACT_LINK=mailto:webring@example.com
|
||||||
TELEGRAM_BOT_TOKEN=your_bot_token
|
|
||||||
TELEGRAM_BOT_USERNAME=your_bot_username
|
|
||||||
SESSION_TTL_HOURS=2160 # 90 days
|
|
||||||
SESSION_SECURE_COOKIE=true # Set to true if using HTTPS, false for HTTP
|
|
||||||
CSRF_AUTH_KEY=your_csrf_auth_key
|
|
||||||
CSRF_TRUSTED_ORIGINS=
|
|
||||||
REQUIRE_LOGIN_FOR_SUBMIT=false
|
|
||||||
CHECKER_WORKERS=5
|
|
||||||
CHECKER_DOWN_THRESHOLD=3
|
|
||||||
MESSAGES_DIR=messages
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
version: 2
|
|
||||||
|
|
||||||
secret_scanning:
|
|
||||||
# Paths to exclude from scanning
|
|
||||||
ignored_paths:
|
|
||||||
- '**/.env.template'
|
|
||||||
- '**/.env.example'
|
|
||||||
- '**/testdata/**'
|
|
||||||
- '**/test/**'
|
|
||||||
- '**/*_test.go'
|
|
||||||
- 'docs/**'
|
|
||||||
|
|
||||||
# Specific detectors to ignore
|
|
||||||
ignored_detectors:
|
|
||||||
- generic_high_entropy_secret
|
|
||||||
|
|
||||||
ignored_patterns:
|
|
||||||
- name: "Template environment variables"
|
|
||||||
pattern: 'your_bot_token|your_bot_username|example\.com'
|
|
||||||
|
|
||||||
- name: "Localhost database strings"
|
|
||||||
pattern: 'postgres://postgres:postgres@localhost'
|
|
||||||
|
|
||||||
- name: "Test credentials"
|
|
||||||
pattern: 'postgres|postgres|test_.*'
|
|
||||||
|
|
||||||
additional_config:
|
|
||||||
high_entropy_threshold: 4.5
|
|
||||||
ignored_matches:
|
|
||||||
- match: 'TELEGRAM_BOT_TOKEN=your_bot_token'
|
|
||||||
reason: "Template placeholder"
|
|
||||||
- match: 'TELEGRAM_BOT_USERNAME=your_bot_username'
|
|
||||||
reason: "Template placeholder"
|
|
||||||
- match: 'postgres://postgres:postgres@localhost'
|
|
||||||
reason: "Local development database"
|
|
||||||
265
.github/workflows/go.yml
vendored
265
.github/workflows/go.yml
vendored
File diff suppressed because it is too large
Load Diff
|
|
@ -1,83 +0,0 @@
|
||||||
version: "2"
|
|
||||||
|
|
||||||
run:
|
|
||||||
timeout: 5m
|
|
||||||
issues-exit-code: 1
|
|
||||||
tests: true
|
|
||||||
|
|
||||||
output:
|
|
||||||
formats:
|
|
||||||
text:
|
|
||||||
path: stdout
|
|
||||||
print-issued-lines: true
|
|
||||||
print-linter-name: true
|
|
||||||
|
|
||||||
formatters:
|
|
||||||
enable:
|
|
||||||
- gofmt
|
|
||||||
- goimports
|
|
||||||
settings:
|
|
||||||
gofmt:
|
|
||||||
simplify: true
|
|
||||||
goimports:
|
|
||||||
local-prefixes:
|
|
||||||
- webring
|
|
||||||
|
|
||||||
linters:
|
|
||||||
enable:
|
|
||||||
- bodyclose
|
|
||||||
- copyloopvar
|
|
||||||
- dogsled
|
|
||||||
- dupl
|
|
||||||
- errcheck
|
|
||||||
- exhaustive
|
|
||||||
- gochecknoinits
|
|
||||||
- goconst
|
|
||||||
- gocritic
|
|
||||||
- goprintffuncname
|
|
||||||
- gosec
|
|
||||||
- govet
|
|
||||||
- ineffassign
|
|
||||||
- lll
|
|
||||||
- misspell
|
|
||||||
- nakedret
|
|
||||||
- nolintlint
|
|
||||||
- rowserrcheck
|
|
||||||
- staticcheck
|
|
||||||
- unconvert
|
|
||||||
- unparam
|
|
||||||
- unused
|
|
||||||
- whitespace
|
|
||||||
|
|
||||||
settings:
|
|
||||||
errcheck:
|
|
||||||
check-type-assertions: true
|
|
||||||
check-blank: true
|
|
||||||
|
|
||||||
goconst:
|
|
||||||
min-len: 3
|
|
||||||
min-occurrences: 10
|
|
||||||
|
|
||||||
gocritic:
|
|
||||||
enabled-tags: [diagnostic, experimental, opinionated, performance, style]
|
|
||||||
|
|
||||||
govet:
|
|
||||||
enable: [shadow]
|
|
||||||
disable: [fieldalignment]
|
|
||||||
|
|
||||||
lll:
|
|
||||||
line-length: 120
|
|
||||||
|
|
||||||
misspell:
|
|
||||||
locale: US
|
|
||||||
|
|
||||||
nakedret:
|
|
||||||
max-func-lines: 30
|
|
||||||
|
|
||||||
prealloc:
|
|
||||||
simple: true
|
|
||||||
range-loops: true
|
|
||||||
for-loops: false
|
|
||||||
|
|
||||||
unparam:
|
|
||||||
check-exported: false
|
|
||||||
46
Dockerfile
46
Dockerfile
|
|
@ -1,46 +0,0 @@
|
||||||
# Build stage
|
|
||||||
FROM golang:1.25-alpine AS builder
|
|
||||||
|
|
||||||
# Install git and ca-certificates
|
|
||||||
RUN apk add --no-cache git ca-certificates tzdata
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /build
|
|
||||||
|
|
||||||
# Copy go mod files
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
|
|
||||||
# Download dependencies
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
# Copy source code
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Build the application
|
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags="-s -w" -o webring cmd/server/main.go
|
|
||||||
|
|
||||||
# Final stage
|
|
||||||
FROM alpine:latest
|
|
||||||
|
|
||||||
# Install ca-certificates for HTTPS requests
|
|
||||||
RUN apk --no-cache add ca-certificates tzdata
|
|
||||||
|
|
||||||
WORKDIR /root/
|
|
||||||
|
|
||||||
# Copy the binary from builder stage
|
|
||||||
COPY --from=builder /build/webring .
|
|
||||||
COPY --from=builder /build/docs ./docs
|
|
||||||
COPY --from=builder /build/messages ./messages
|
|
||||||
|
|
||||||
# Create media directory
|
|
||||||
RUN mkdir -p media
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 8080
|
|
||||||
|
|
||||||
# Set default environment variables
|
|
||||||
ENV PORT=8080
|
|
||||||
ENV MEDIA_FOLDER=media
|
|
||||||
|
|
||||||
# Run the binary
|
|
||||||
CMD ["./webring"]
|
|
||||||
46
README.md
46
README.md
|
|
@ -5,12 +5,8 @@ This project is a webring relay service built with Go. It manages a list of webs
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Dashboard for managing websites in the webring
|
- Dashboard for managing websites in the webring
|
||||||
- Automatic uptime checking of websites (with proxy support)
|
- Automatic uptime checking of websites
|
||||||
- API endpoints for navigating the webring
|
- API endpoints for navigating the webring
|
||||||
- Telegram authentication and user management
|
|
||||||
- Site submission and update request workflow with admin approval
|
|
||||||
- Telegram notifications for status changes, submissions and approvals
|
|
||||||
- Customizable notification messages via template files
|
|
||||||
- Basic authentication for the dashboard
|
- Basic authentication for the dashboard
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
@ -21,6 +17,7 @@ This project is a webring relay service built with Go. It manages a list of webs
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
edit .env to set correct path to database
|
edit .env to set correct path to database
|
||||||
|
|
||||||
```
|
```
|
||||||
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
|
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
@ -29,51 +26,30 @@ make migrate-up
|
||||||
```
|
```
|
||||||
|
|
||||||
## Local Run
|
## Local Run
|
||||||
|
|
||||||
```
|
```
|
||||||
go run cmd/server/main.go
|
go run cmd/server/main.go
|
||||||
```
|
```
|
||||||
|
|
||||||
or download prebuild version
|
or download prebuild version
|
||||||
|
|
||||||
```
|
```
|
||||||
wget https://github.com/Alexander-D-Karpov/webring/releases/latest/download/webring
|
wget https://github.com/Alexander-D-Karpov/webring/releases/latest/download/webring
|
||||||
chmod +x webring
|
chmod +x webring
|
||||||
./webring
|
./webring
|
||||||
```
|
```
|
||||||
|
|
||||||
## Customizing Notification Messages
|
|
||||||
|
|
||||||
Telegram notification templates live in the `messages/` directory (configurable via `MESSAGES_DIR` env var).
|
|
||||||
Each file is plain text with Go template syntax and MarkdownV2 formatting.
|
|
||||||
|
|
||||||
To customize a message, edit the corresponding `.txt` file.
|
|
||||||
|
|
||||||
Available templates:
|
|
||||||
|
|
||||||
| File | Event |
|
|
||||||
|-----------------------------|---------------------------------------------|
|
|
||||||
| `new_request_create.txt` | Admin notification: new site submitted |
|
|
||||||
| `new_request_update.txt` | Admin notification: site update requested |
|
|
||||||
| `approved_create.txt` | User notification: site submission approved |
|
|
||||||
| `approved_update.txt` | User notification: site update approved |
|
|
||||||
| `declined_create.txt` | User notification: site submission declined |
|
|
||||||
| `declined_update.txt` | User notification: site update declined |
|
|
||||||
| `admin_approved_create.txt` | Other admins: site creation approved |
|
|
||||||
| `admin_approved_update.txt` | Other admins: site update approved |
|
|
||||||
| `admin_declined_create.txt` | Other admins: site creation declined |
|
|
||||||
| `admin_declined_update.txt` | Other admins: site update declined |
|
|
||||||
| `site_online.txt` | Owner notification: site back online |
|
|
||||||
| `site_offline.txt` | Owner notification: site went offline |
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
- Access the dashboard at `http://localhost:8080/dashboard` (use the credentials set in your `.env` file)
|
- Access the dashboard at `http://localhost:8080/dashboard` (use the credentials set in your `.env` file)
|
||||||
- API endpoints:
|
- API endpoints:
|
||||||
- Next site: `GET /{slug}/next/data`
|
- Next site: `GET /{id}/next/`
|
||||||
- Previous site: `GET /{slug}/prev/data`
|
- Previous site: `GET /{id}/prev/`
|
||||||
- Random site: `GET /{slug}/random/data`
|
- Random site: `GET /{id}/random/`
|
||||||
- Full data for a site: `GET /{slug}/data`
|
- Full data for a site: `GET /{id}/data`
|
||||||
- Redirect endpoints:
|
- Redirect endpoints:
|
||||||
- Visit site: `GET /{slug}`
|
- Next site: `GET /{id}/next`
|
||||||
- Next site: `GET /{slug}/next`
|
- Previous site: `GET /{id}/prev`
|
||||||
- Previous site: `GET /{slug}/prev`
|
- Random site: `GET /{id}/random`
|
||||||
- Random site: `GET /{slug}/random`
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,30 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Webring API Documentation</title>
|
|
||||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui.css" />
|
|
||||||
<style>
|
|
||||||
html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }
|
|
||||||
*, *:before, *:after { box-sizing: inherit; }
|
|
||||||
body { margin:0; background: #fafafa; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="swagger-ui"></div>
|
|
||||||
<script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-bundle.js"></script>
|
|
||||||
<script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-standalone-preset.js"></script>
|
|
||||||
<script>
|
|
||||||
window.onload = function() {
|
|
||||||
const ui = SwaggerUIBundle({
|
|
||||||
url: '/api/docs/swagger.json',
|
|
||||||
dom_id: '#swagger-ui',
|
|
||||||
deepLinking: true,
|
|
||||||
presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset ],
|
|
||||||
plugins: [ SwaggerUIBundle.plugins.DownloadUrl ],
|
|
||||||
layout: "StandaloneLayout"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -4,5 +4,5 @@ import (
|
||||||
"embed"
|
"embed"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed static internal/dashboard/templates internal/public/templates internal/user/templates docs
|
//go:embed static internal/dashboard/templates internal/public/templates
|
||||||
var Files embed.FS
|
var Files embed.FS
|
||||||
|
|
|
||||||
61
flake.lock
61
flake.lock
|
|
@ -1,61 +0,0 @@
|
||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1731533236,
|
|
||||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1762844143,
|
|
||||||
"narHash": "sha256-SlybxLZ1/e4T2lb1czEtWVzDCVSTvk9WLwGhmxFmBxI=",
|
|
||||||
"owner": "nixos",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "9da7f1cf7f8a6e2a7cb3001b048546c92a8258b4",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nixos",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
||||||
44
flake.nix
44
flake.nix
|
|
@ -1,44 +0,0 @@
|
||||||
{
|
|
||||||
description = "webring";
|
|
||||||
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
|
||||||
flake-utils.lib.eachDefaultSystem (system:
|
|
||||||
let
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
|
||||||
webring = pkgs.callPackage ./nix/package.nix {};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
devShells.default = pkgs.mkShell {
|
|
||||||
packages = with pkgs; [
|
|
||||||
go
|
|
||||||
postgresql
|
|
||||||
gnumake
|
|
||||||
go-migrate.overrideAttrs(oldAttrs: {
|
|
||||||
tags = ["postgres"];
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
shellHook = ''
|
|
||||||
${pkgs.go}/bin/go mod tidy
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
apps.default = { type = "app"; program = "${webring}/bin/webring-server"; };
|
|
||||||
apps.webring = self.apps.${system}.default;
|
|
||||||
|
|
||||||
packages = {
|
|
||||||
inherit webring;
|
|
||||||
default = webring;
|
|
||||||
};
|
|
||||||
}) // {
|
|
||||||
overlays.default = final: prev: {
|
|
||||||
webring = prev.callPackage ./nix/package.nix {};
|
|
||||||
};
|
|
||||||
nixosModules.default = { imports = [./nix/module.nix]; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
12
go.mod
12
go.mod
|
|
@ -1,17 +1,15 @@
|
||||||
module webring
|
module webring
|
||||||
|
|
||||||
go 1.25.0
|
go 1.22.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.11.0
|
github.com/PuerkitoBio/goquery v1.9.2
|
||||||
github.com/gorilla/csrf v1.7.3
|
|
||||||
github.com/gorilla/mux v1.8.1
|
github.com/gorilla/mux v1.8.1
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/lib/pq v1.11.2
|
github.com/lib/pq v1.10.9
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
golang.org/x/net v0.24.0 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
48
go.sum
48
go.sum
|
|
@ -1,59 +1,28 @@
|
||||||
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
|
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
|
||||||
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
|
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
|
||||||
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
|
||||||
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
|
||||||
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
||||||
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
||||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
|
||||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/gorilla/csrf v1.7.3 h1:BHWt6FTLZAb2HtWT5KDBf6qgpZzvtbp9QWDRKZMXJC0=
|
|
||||||
github.com/gorilla/csrf v1.7.3/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
|
|
||||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
|
||||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
|
|
||||||
github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
|
@ -61,34 +30,17 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
|
||||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
|
||||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -4,18 +4,22 @@ import "net/http"
|
||||||
|
|
||||||
func CORSMiddleware(next http.Handler) http.Handler {
|
func CORSMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Allow all origins
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
|
||||||
|
// Allow common HTTP methods
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||||
|
|
||||||
allowedHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, " +
|
// Allow common HTTP headers
|
||||||
"X-CSRF-Token, Authorization"
|
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||||
w.Header().Set("Access-Control-Allow-Headers", allowedHeaders)
|
|
||||||
|
|
||||||
|
// Handle preflight requests
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call the next handler
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,75 +0,0 @@
|
||||||
package auth
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TelegramUser struct {
|
|
||||||
ID int64 `json:"id"`
|
|
||||||
FirstName string `json:"first_name"`
|
|
||||||
LastName string `json:"last_name,omitempty"`
|
|
||||||
Username string `json:"username,omitempty"`
|
|
||||||
PhotoURL string `json:"photo_url,omitempty"`
|
|
||||||
AuthDate int64 `json:"auth_date"`
|
|
||||||
Hash string `json:"hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func VerifyTelegramAuth(values url.Values, botToken string) (*TelegramUser, error) {
|
|
||||||
hash := values.Get("hash")
|
|
||||||
if hash == "" {
|
|
||||||
return nil, fmt.Errorf("missing hash parameter")
|
|
||||||
}
|
|
||||||
|
|
||||||
var dataStrings []string
|
|
||||||
for key, value := range values {
|
|
||||||
if key != "hash" && len(value) > 0 {
|
|
||||||
dataStrings = append(dataStrings, fmt.Sprintf("%s=%s", key, value[0]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(dataStrings)
|
|
||||||
dataString := strings.Join(dataStrings, "\n")
|
|
||||||
|
|
||||||
// Create secret key
|
|
||||||
secretKey := sha256.Sum256([]byte(botToken))
|
|
||||||
|
|
||||||
// Create HMAC
|
|
||||||
h := hmac.New(sha256.New, secretKey[:])
|
|
||||||
h.Write([]byte(dataString))
|
|
||||||
expectedHash := hex.EncodeToString(h.Sum(nil))
|
|
||||||
|
|
||||||
if hash != expectedHash {
|
|
||||||
return nil, fmt.Errorf("invalid hash")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse user data
|
|
||||||
id, err := strconv.ParseInt(values.Get("id"), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid id")
|
|
||||||
}
|
|
||||||
|
|
||||||
authDate, err := strconv.ParseInt(values.Get("auth_date"), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid auth_date")
|
|
||||||
}
|
|
||||||
if time.Since(time.Unix(authDate, 0)) > 24*time.Hour {
|
|
||||||
return nil, fmt.Errorf("stale login payload")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &TelegramUser{
|
|
||||||
ID: id,
|
|
||||||
FirstName: values.Get("first_name"),
|
|
||||||
LastName: values.Get("last_name"),
|
|
||||||
Username: values.Get("username"),
|
|
||||||
PhotoURL: values.Get("photo_url"),
|
|
||||||
AuthDate: authDate,
|
|
||||||
Hash: hash,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -2,9 +2,8 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
_ "github.com/lib/pq"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
_ "github.com/lib/pq" // PostgreSQL driver
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Connect() (*sql.DB, error) {
|
func Connect() (*sql.DB, error) {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,21 +1,16 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
type Site struct {
|
type Site struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Slug string `json:"slug"`
|
Name string `json:"name"`
|
||||||
Name string `json:"name"`
|
URL string `json:"url"`
|
||||||
URL string `json:"url"`
|
IsUp bool `json:"is_up"`
|
||||||
IsUp bool `json:"is_up"`
|
LastCheck float64 `json:"last_check"`
|
||||||
Enabled bool `json:"enabled"`
|
Favicon *string `json:"favicon"`
|
||||||
LastCheck float64 `json:"last_check"`
|
|
||||||
Favicon *string `json:"favicon"`
|
|
||||||
UserID *int `json:"user_id"`
|
|
||||||
User *User `json:"user,omitempty"`
|
|
||||||
TelegramUsername *string `json:"telegram_username,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PublicSite struct {
|
type PublicSite struct {
|
||||||
Slug string `json:"slug"`
|
ID int `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Favicon *string `json:"favicon"`
|
Favicon *string `json:"favicon"`
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
package models
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
ID int `json:"id"`
|
|
||||||
TelegramID int64 `json:"telegram_id"`
|
|
||||||
TelegramUsername *string `json:"telegram_username"`
|
|
||||||
FirstName *string `json:"first_name"`
|
|
||||||
LastName *string `json:"last_name"`
|
|
||||||
IsAdmin bool `json:"is_admin"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Session struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
UserID int `json:"user_id"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
|
||||||
ExpiresAt time.Time `json:"expires_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdateRequest struct {
|
|
||||||
ID int `json:"id"`
|
|
||||||
UserID int `json:"user_id"`
|
|
||||||
SiteID *int `json:"site_id"`
|
|
||||||
RequestType string `json:"request_type"`
|
|
||||||
ChangedFields map[string]interface{} `json:"changed_fields"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
|
||||||
User *User `json:"user,omitempty"`
|
|
||||||
Site *Site `json:"site,omitempty"`
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,39 +15,6 @@
|
||||||
<i class="ri-bubble-chart-fill"></i>
|
<i class="ri-bubble-chart-fill"></i>
|
||||||
Webring Listing
|
Webring Listing
|
||||||
</h1>
|
</h1>
|
||||||
{{if .User}}
|
|
||||||
<div class="user-bar">
|
|
||||||
<span>Welcome, {{if .User.FirstName}}{{.User.FirstName}}{{else}}{{.User.TelegramUsername}}{{end}}!</span>
|
|
||||||
<div class="user-actions">
|
|
||||||
{{if .User.IsAdmin}}
|
|
||||||
<a href="/admin" class="user-action admin">
|
|
||||||
<i class="ri-settings-line"></i>
|
|
||||||
Manage Sites
|
|
||||||
</a>
|
|
||||||
<a href="/admin/requests" class="user-action admin">
|
|
||||||
<i class="ri-shield-user-line"></i>
|
|
||||||
Admin Dashboard
|
|
||||||
</a>
|
|
||||||
{{else}}
|
|
||||||
<a href="/user" class="user-action">
|
|
||||||
<i class="ri-user-line"></i>
|
|
||||||
My Dashboard
|
|
||||||
</a>
|
|
||||||
{{end}}
|
|
||||||
<a href="/api/docs/" class="user-action docs">
|
|
||||||
<i class="ri-book-line"></i>
|
|
||||||
API Docs
|
|
||||||
</a>
|
|
||||||
<form action="/logout" method="POST" style="display: inline;">
|
|
||||||
{{csrfField .Request}}
|
|
||||||
<button type="submit" class="user-action logout">
|
|
||||||
<i class="ri-logout-circle-line"></i>
|
|
||||||
Logout
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<ul class="site-list">
|
<ul class="site-list">
|
||||||
|
|
@ -64,38 +31,13 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{if .ContactLink}}
|
||||||
|
<li class="join-link">
|
||||||
|
<i class="ri-user-add-line"></i>
|
||||||
|
<a href="{{.ContactLink}}" target="_blank">...and maybe you?</a>
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{{if not .User}}
|
|
||||||
<section class="action-section">
|
|
||||||
<h2>Get Involved</h2>
|
|
||||||
<div class="action-cards">
|
|
||||||
<a href="/submit" class="action-card action-card-primary">
|
|
||||||
<i class="ri-add-circle-line"></i>
|
|
||||||
<div class="action-content">
|
|
||||||
<div class="action-title">Submit Your Site</div>
|
|
||||||
<div class="action-description">Join the webring with your website</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/login" class="action-card">
|
|
||||||
<i class="ri-login-circle-line"></i>
|
|
||||||
<div class="action-content">
|
|
||||||
<div class="action-title">Login</div>
|
|
||||||
<div class="action-description">Manage your websites in the ring</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/api/docs/" class="action-card">
|
|
||||||
<i class="ri-book-line"></i>
|
|
||||||
<div class="action-content">
|
|
||||||
<div class="action-title">API Docs</div>
|
|
||||||
<div class="action-description">Developer documentation</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{{end}}
|
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<a href="https://github.com/Alexander-D-Karpov/webring">
|
<a href="https://github.com/Alexander-D-Karpov/webring">
|
||||||
|
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Submit Site - Webring</title>
|
|
||||||
<link rel="stylesheet" href="/static/public.css">
|
|
||||||
<link rel="preconnect" href="https://rsms.me/">
|
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remixicon@4.3.0/fonts/remixicon.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>
|
|
||||||
<i class="ri-bubble-chart-fill"></i>
|
|
||||||
Submit Your Site
|
|
||||||
</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
{{if .Error}}
|
|
||||||
<div style="max-width: 400px; margin: 0 auto 1.5rem auto; padding: 1rem; background: rgba(185, 28, 28, 0.1); border: 1px solid rgba(185, 28, 28, 0.3); border-left: 4px solid #b91c1c; border-radius: 6px;">
|
|
||||||
<div style="display: flex; align-items: center; gap: 0.75rem;">
|
|
||||||
<i class="ri-error-warning-line" style="color: #fca5a5; font-size: 1.25rem;"></i>
|
|
||||||
<span style="color: #fecaca; font-size: 0.875rem;">{{.Error}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
<form action="/submit" method="POST" style="max-width: 400px; margin: 0 auto;">
|
|
||||||
{{csrfField .Request}}
|
|
||||||
<div style="margin-bottom: 1rem;">
|
|
||||||
<label for="slug">Slug (unique identifier):</label>
|
|
||||||
<input type="text" id="slug" name="slug" pattern="[a-z0-9-]{3,50}" required
|
|
||||||
placeholder="my-awesome-site" style="width: 100%; padding: 0.5rem; margin-top: 0.5rem;">
|
|
||||||
<small style="color: var(--color-gray-400);">Only lowercase letters, numbers, and hyphens. 3-50 characters.</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-bottom: 1rem;">
|
|
||||||
<label for="name">Site Name:</label>
|
|
||||||
<input type="text" id="name" name="name" required
|
|
||||||
placeholder="My Awesome Site" style="width: 100%; padding: 0.5rem; margin-top: 0.5rem;">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-bottom: 1rem;">
|
|
||||||
<label for="url">Site URL:</label>
|
|
||||||
<input type="url" id="url" name="url" required
|
|
||||||
placeholder="https://example.com" style="width: 100%; padding: 0.5rem; margin-top: 0.5rem;">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-bottom: 1rem;">
|
|
||||||
<label for="telegram_username">Your Telegram Username (optional):</label>
|
|
||||||
<input type="text" id="telegram_username" name="telegram_username"
|
|
||||||
placeholder="username" style="width: 100%; padding: 0.5rem; margin-top: 0.5rem;">
|
|
||||||
<small style="color: var(--color-gray-400);">
|
|
||||||
Enter your Telegram username (without @) to Manage your websites later. (Optional)
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" style="width: 100%; padding: 0.75rem; background: var(--color-primary-900); color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
||||||
Submit Site for Review
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p style="text-align: center; margin-top: 2rem; color: var(--color-gray-400);">
|
|
||||||
<a href="/">← Back to site listing</a>
|
|
||||||
</p>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Submission Successful - Webring</title>
|
|
||||||
<link rel="stylesheet" href="/static/public.css">
|
|
||||||
<link rel="preconnect" href="https://rsms.me/">
|
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remixicon@4.3.0/fonts/remixicon.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>
|
|
||||||
<i class="ri-check-circle-fill"></i>
|
|
||||||
Site Submitted Successfully!
|
|
||||||
</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<div style="text-align: center;">
|
|
||||||
<p>Your site has been submitted for review. An admin will review it shortly.</p>
|
|
||||||
<p style="margin-top: 2rem;">
|
|
||||||
<a href="/">← Back to site listing</a> |
|
|
||||||
<a href="/user">Go to your dashboard</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,22 +0,0 @@
|
||||||
package user
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"webring/internal/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
type contextKey string
|
|
||||||
|
|
||||||
const userContextKey contextKey = "user"
|
|
||||||
|
|
||||||
func SetUserContext(ctx context.Context, user *models.User) context.Context {
|
|
||||||
return context.WithValue(ctx, userContextKey, user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUserFromContext(ctx context.Context) *models.User {
|
|
||||||
if user, ok := ctx.Value(userContextKey).(*models.User); ok {
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,36 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Login - Webring</title>
|
|
||||||
<link rel="stylesheet" href="/static/public.css">
|
|
||||||
<link rel="preconnect" href="https://rsms.me/">
|
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remixicon@4.3.0/fonts/remixicon.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>
|
|
||||||
<i class="ri-bubble-chart-fill"></i>
|
|
||||||
Login to Webring
|
|
||||||
</h1>
|
|
||||||
</header>
|
|
||||||
<main>
|
|
||||||
<div style="text-align: center; margin: 2rem 0;">
|
|
||||||
<p>Login with your Telegram account to Manage your websites in the webring.</p>
|
|
||||||
|
|
||||||
<script async src="https://telegram.org/js/telegram-widget.js?22"
|
|
||||||
data-telegram-login="{{.BotUsername}}"
|
|
||||||
data-size="large"
|
|
||||||
data-auth-url="/auth/telegram"
|
|
||||||
data-request-access="write">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<p style="margin-top: 2rem; color: var(--color-gray-400);">
|
|
||||||
<a href="/">← Back to site listing</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Request Error - Webring</title>
|
|
||||||
<link rel="stylesheet" href="/static/dashboard.css">
|
|
||||||
<link rel="preconnect" href="https://rsms.me/">
|
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/remixicon@4.3.0/fonts/remixicon.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<header class="admin-header">
|
|
||||||
<div class="header-content">
|
|
||||||
<div class="header-left">
|
|
||||||
<a href="/admin" class="logo-link">
|
|
||||||
<div class="logo">
|
|
||||||
<i class="ri-bubble-chart-fill"></i>
|
|
||||||
<span class="logo-text">Webring</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<div class="page-info">
|
|
||||||
<h1>Request Error</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="header-right">
|
|
||||||
<nav class="header-nav">
|
|
||||||
<a href="/admin/requests" class="nav-item">
|
|
||||||
<i class="ri-arrow-left-line"></i>
|
|
||||||
<span>Back to Requests</span>
|
|
||||||
</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<main>
|
|
||||||
<div class="error-card">
|
|
||||||
<div class="error-icon">
|
|
||||||
<i class="ri-error-warning-line"></i>
|
|
||||||
</div>
|
|
||||||
<div class="error-content">
|
|
||||||
<h2>Unable to Process Request</h2>
|
|
||||||
<p class="error-message">{{.Error}}</p>
|
|
||||||
|
|
||||||
{{if .Request}}
|
|
||||||
<div class="error-details">
|
|
||||||
<h3>Request Details</h3>
|
|
||||||
{{if eq .Request.RequestType "create"}}
|
|
||||||
<p><strong>Type:</strong> Site Creation</p>
|
|
||||||
{{else}}
|
|
||||||
<p><strong>Type:</strong> Site Update</p>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .Request.ChangedFields}}
|
|
||||||
<div class="changed-fields">
|
|
||||||
<strong>Requested Fields:</strong>
|
|
||||||
<ul>
|
|
||||||
{{range $key, $value := .Request.ChangedFields}}
|
|
||||||
<li><strong>{{$key}}:</strong> {{$value}}</li>
|
|
||||||
{{end}}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
<div class="error-actions">
|
|
||||||
<a href="/admin/requests" class="btn btn-primary">
|
|
||||||
<i class="ri-arrow-left-line"></i>
|
|
||||||
Back to Requests
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="error-note">
|
|
||||||
<p><strong>Note:</strong> The request has not been deleted and can still be reviewed. You may need to contact the user to choose a different slug or make other changes.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +0,0 @@
|
||||||
*Request Approved*
|
|
||||||
|
|
||||||
*Admin:* {{.AdminName}}
|
|
||||||
*Action:* Approved site creation
|
|
||||||
*User:* {{.UserName}}
|
|
||||||
*Site:* {{.SiteName}}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
*Update Approved*
|
|
||||||
|
|
||||||
*Admin:* {{.AdminName}}
|
|
||||||
*Action:* Approved site update
|
|
||||||
*User:* {{.UserName}}
|
|
||||||
*Site:* {{.SiteName}}
|
|
||||||
{{- if .Changes}}
|
|
||||||
|
|
||||||
*Changes:*
|
|
||||||
{{- range .Changes}}
|
|
||||||
• *{{.Key}}:* {{.Value}}
|
|
||||||
{{- end}}
|
|
||||||
{{- end}}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
*Request Declined*
|
|
||||||
|
|
||||||
*Admin:* {{.AdminName}}
|
|
||||||
*Action:* Declined site creation
|
|
||||||
*User:* {{.UserName}}
|
|
||||||
*Site:* {{.SiteName}}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
*Update Declined*
|
|
||||||
|
|
||||||
*Admin:* {{.AdminName}}
|
|
||||||
*Action:* Declined site update
|
|
||||||
*User:* {{.UserName}}
|
|
||||||
*Site:* {{.SiteName}}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
*Request Approved*
|
|
||||||
|
|
||||||
Your site submission has been approved!
|
|
||||||
|
|
||||||
*Site:* {{.SiteName}}
|
|
||||||
|
|
||||||
Your site is now part of the webring.
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
*Update Approved*
|
|
||||||
|
|
||||||
Your site update request has been approved and the changes have been applied.
|
|
||||||
{{- if .Changes}}
|
|
||||||
|
|
||||||
*Applied changes:*
|
|
||||||
{{- range .Changes}}
|
|
||||||
• *{{.Key}}:* {{.Value}}
|
|
||||||
{{- end}}
|
|
||||||
{{- end}}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
*Request Declined*
|
|
||||||
|
|
||||||
Your site submission request for *{{.SiteName}}* has been declined by an administrator.
|
|
||||||
|
|
||||||
If you have questions, please contact the webring administrator.
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user