Compare commits

..

No commits in common. "9671be1ff349586ae0e50d6fecbf4c8f961281d6" and "e31edb55ada6e418468d9d96efe7f628d1cc0d69" have entirely different histories.

52 changed files with 456 additions and 171 deletions

15
.gitignore vendored
View File

@ -3,13 +3,13 @@ msg
tmp/
keys/
clippable-svc/target/
clippable-svc/dev/
clippable-svc/vids/
clippable-svc/static/js/
clippable-svc/static/dist/
clippable-svc/thumbs/
clippable-svc/*.db
api/target/
api/dev/
api/vids/
api/static/js/
api/static/dist/
api/thumbs/
api/*.db
build/
gitpage/public/
@ -29,4 +29,3 @@ ts/dist/
ts/node_modules/
.vscode/settings.json
api/.vscode/settings.json
api/.ycm_extra_conf.py

View File

@ -4,10 +4,6 @@ stages:
- build-backend
- deploy
include:
- local: 'ci/cargo.yml'
- local: 'ci/docker.yml'
pages:
image: shockrah/website:latest
stage: pages
@ -26,67 +22,62 @@ pages:
paths:
- public/
# Webpack bundles everything anyway so both admin/non-admin builds have
# the same frontend code.
build-frontend-js:
image: codesignal/typescript:v9.6.0
stage: build-frontend
stage: pages
only:
refs:
- master
script:
- cd ts/
- npm i
- npm run setup
- npm run build
artifacts:
paths:
- clippable-svc/static/
- api/static/
build-server-no-admin:
extends: .cargo-builder
# Literally both of these fail 99% of the time so I'm forgoing them completely
# for now until I find something doesn't suck
# Builds out the intended zip package
build-server-binaries:
image: rustlang/rust:nightly
stage: build-backend
needs:
- build-frontend-js
dependencies:
- build-frontend-js
stage: pages
only:
refs:
- master
script:
- cd clippable-svc/
- mkdir -p build
- cp api/templates/ api/static/ build/ -r
- cd api/
- cargo build --release
- cd ../
- cp api/target/release/clippable-server build/server
- cp ./scripts/ build/ -r
- cp readme.md build/
- sh ./scripts/default-rocket-toml.sh
artifacts:
paths:
- build/
build-server-admin-enabled:
extends: .cargo-builder
stage: build-backend
needs:
- build-frontend-js
dependencies:
- build-frontend-js
script:
- cd clippable-svc/
- cargo build --release --features admin
- cd ../
deploy-no-admin-docker:
# Upload built docker image to the local registry
deploy-docker-image:
stage: deploy
extends: .docker-deploy
image: docker:stable
only:
refs:
- master
needs:
- build-server-no-admin
- build-server-binaries
dependencies:
- build-server-no-admin
- build-server-binaries
services:
- docker:dind
script:
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
- docker build -t registry.gitlab.com/shockrah/clippable .
- docker push registry.gitlab.com/shockrah/clippable
deploy-admin-docker:
stage: deploy
extends: .docker-deploy
needs:
- build-server-admin-enabled
dependencies:
- build-server-admin-enabled
script:
- docker build -t registry.gitlab.com/shockrah/clippable:admin .
- docker push registry.gitlab.com/shockrah/clippable:admin

View File

@ -56,8 +56,6 @@ pub fn get_category_thumbnail(category: &str) -> std::io::Result<String> {
})
}
/// Returns a List of categories
/// Primarily used on the main page
/// WARN: misconfigured servers are just going to get shafted and serve up
/// a tonne of 500's
#[get("/categories")]

View File

@ -83,18 +83,3 @@ video {
#video-meta {
text-align: left;
}
.admin-video-li {
color: black;
text-shadow: none;
padding-right: 1em;
}
.admin-video-li:hover {
color: #0a58ca;
text-shadow: none;
}
.align-left {
text-align: left;
}

View File

@ -4,8 +4,6 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous"/>
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
<title>Clippable Admin Dashboard</title>
<link rel="shortcut icon" type="image/png" href="/static/favicon.png"/>
@ -62,10 +60,6 @@
<button type="button" class="btn btn-primary" id="confirm-upload-btn">Upload</button>
</div>
<div id="upload-response"></div>
<div class="vids-meta-list">
<h1>Videos</h1>
<ul class="list-group" id="videos-list"></ul>
</div>
</div>
</div>
</div>

14
aws/infra/ebs.tf Normal file
View File

@ -0,0 +1,14 @@
resource "aws_ebs_volume" "app_volume" {
availability_zone = var.availability_zone
size = 20
type = "standard"
tags = {
Name = "APP Video block storage"
}
}
resource "aws_volume_attachment" "ebs_att" {
device_name = "/dev/sdf"
volume_id = aws_ebs_volume.app_volume.id
instance_id = aws_instance.app_instance.id
}

35
aws/infra/ec2.tf Normal file
View File

@ -0,0 +1,35 @@
# This here module takes care of setting up the ec2 instances that our
# containers will bind to later on
variable "aws_key" {}
variable "aws_secret" {}
variable "aws_region" {}
variable "ami_id" {}
variable "instance_type" {}
variable "ssh_key_name" {}
variable "public_key_path" {}
variable "availability_zone" {}
provider "aws" {
access_key = var.aws_key
secret_key = var.aws_secret
region = var.aws_region
max_retries = 1
}
resource "aws_key_pair" "sshkey" {
key_name = var.ssh_key_name
public_key = file(var.public_key_path)
}
resource "aws_instance" "app_instance" {
ami = var.ami_id
instance_type = var.instance_type
key_name = var.ssh_key_name
security_groups = [ aws_security_group.app_security_group.id ]
subnet_id = aws_subnet.app_public_subnet.id
tags = {
Name = "Clippable App Instance"
}
}

7
aws/infra/eip.tf Normal file
View File

@ -0,0 +1,7 @@
resource "aws_eip" "app_eip" {
instance = aws_instance.app_instance.id
vpc = true
tags = {
Name = "Clippable EIP"
}
}

6
aws/infra/gateway.tf Normal file
View File

@ -0,0 +1,6 @@
resource "aws_internet_gateway" "app_gateway" {
vpc_id = aws_vpc.app_vpc.id
tags = {
Name = "Clippable app internet gateway"
}
}

12
aws/infra/route-table.tf Normal file
View File

@ -0,0 +1,12 @@
resource "aws_route_table" "app_route_table" {
vpc_id = aws_vpc.app_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.app_gateway.id
}
}
resource "aws_route_table_association" "app_subnet_assoc" {
subnet_id = aws_subnet.app_public_subnet.id
route_table_id = aws_route_table.app_route_table.id
}

View File

@ -0,0 +1,32 @@
#!/bin/sh
# This script runs in order to to set things up for so that we don't have to do
# much else by ourselves
# No harm in using sudo even as root its just a little pointless
# Doing this with our ami however means we don't have to check if we're root
# for privileged operations at provision-time
apt="sudo apt"
server_name=$1
if [ -z "$server_name" ];then
echo A servername must be given as an argument
fi
$apt update
$apt upgrade
$apt install -y nginx certbot
sudo mkdir -p /var/www/clippable
# Creating the reverse proxy configuration for nginx
# WARN: Also we're assuming that the webserver has the default port
# Only this because certbot does the rest
cat << EOF > /etc/nginx/sites-available/clippable
server {
server_name $server_name;
location / {
proxy_pass http://0.0.0.0:8482;
};
}
EOF

View File

@ -0,0 +1,39 @@
resource "aws_security_group" "app_security_group" {
name = "App sec group"
description = "Allowing SSH and web traffic"
vpc_id = aws_vpc.app_vpc.id
ingress {
cidr_blocks = ["0.0.0.0/0"]
from_port = 443
to_port = 443
protocol = "tcp"
}
ingress {
cidr_blocks = ["0.0.0.0/0"]
from_port = 80
to_port = 80
protocol = "tcp"
}
ingress {
cidr_blocks = ["0.0.0.0/0"]
from_port = 22
to_port = 22
protocol = "tcp"
}
# These are so that we can update the system regularly using apt and sometimes
# with tarballs if we're updating something from source
egress {
cidr_blocks = ["0.0.0.0/0"]
from_port = 443
to_port = 443
protocol = "tcp"
}
egress {
cidr_blocks = ["0.0.0.0/0"]
from_port = 80
to_port = 80
protocol = "tcp"
}
}

5
aws/infra/subnet.tf Normal file
View File

@ -0,0 +1,5 @@
resource "aws_subnet" "app_public_subnet" {
vpc_id = aws_vpc.app_vpc.id
cidr_block = "10.0.0.128/26"
availability_zone = var.availability_zone
}

10
aws/infra/vpc.tf Normal file
View File

@ -0,0 +1,10 @@
resource "aws_vpc" "app_vpc" {
cidr_block = "10.0.0.128/26"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "Clippable APP VPC"
}
}

57
aws/playbooks/README.md Normal file
View File

@ -0,0 +1,57 @@
Role Name
=========
This role is dedicated to making the setup and administration of a clippable
server a little bit easier for those intending on running their own instance.
There are playbooks for maintaining this service both as a container and as a
service running on System D.
Role Variables
--------------
Vars in: `defaults/main.yml`
* `remote_user`: Default username to use for regular tasks
Set to `admin` by default.
* `remote_app_dir`: Directory to install application files into
Set to `/home/{{remote_user}}/app` by default
This includes things like the server binary and HTML template files.
You only need to worry about this if you're not going to run this
in a container.
* `main_host`
Set to `main` by default.
Host that you intend on targeting.
Dependencies
------------
* community.docker
This is only required if you are planning on using any of the docker playbooks.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
License
-------
GPL V3
Author Information
------------------
Author: Shockrah
Email: dev@shockrah.xyz

View File

@ -0,0 +1,4 @@
---
remote_user: admin
remote_app_dir: "/home/{{remote_user}}/app"
main_host: main

View File

@ -0,0 +1,2 @@
---
# handlers file for playbooks

View File

@ -0,0 +1,52 @@
galaxy_info:
author: your name
description: your role description
company: your company (optional)
# If the issue tracker for your role is not on github, uncomment the
# next line and provide a value
# issue_tracker_url: http://example.com/issue/tracker
# Choose a valid license ID from https://spdx.org - some suggested licenses:
# - BSD-3-Clause (default)
# - MIT
# - GPL-2.0-or-later
# - GPL-3.0-only
# - Apache-2.0
# - CC-BY-4.0
license: license (GPL-2.0-or-later, MIT, etc)
min_ansible_version: 2.1
# If this a Container Enabled role, provide the minimum Ansible Container version.
# min_ansible_container_version:
#
# Provide a list of supported platforms, and for each platform a list of versions.
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
# To view available platforms and versions (or releases), visit:
# https://galaxy.ansible.com/api/v1/platforms/
#
# platforms:
# - name: Fedora
# versions:
# - all
# - 25
# - name: SomePlatform
# versions:
# - all
# - 1.0
# - 7
# - 99.99
galaxy_tags: []
# List tags for your role here, one per line. A tag is a keyword that describes
# and categorizes the role. Users find roles by searching for tags. Be sure to
# remove the '[]' above, if you add tags to this list.
#
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
# Maximum 20 tags per role.
dependencies: []
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
# if you add dependencies to this list.

View File

@ -0,0 +1,41 @@
---
tasks:
- name: Install docker dependencies
become: yes
become_method: sudo
apt:
name: "{{item}}"
update_cache: yes
loop:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- software-properties-common
- lsb-release
- name: Install docker GPG key
become: yes
become_method: sudo
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker Apt Repo
become: yes
become_method: sudo
apt_repository:
repo: deb https://download.docker.com/linux/ubuntu impish stable
state: present
- name: Install Docker components
become: yes
become_method: sudo
apt:
name: "{{item}}"
update_cache: yes
loop:
- docker-ce
- docker-ce-cli
- containerd.io

View File

@ -0,0 +1,9 @@
# This playbook is setup to install docker on debian based systems
---
- hosts: main
tasks:
- include_tasks: 'debian.yml'
when:
ansible_distribution: Debian

View File

@ -0,0 +1,59 @@
# WHEN TO USE THIS PLAYBOOK:
# Use this if you're running clippable under systemd
# WHAT THIS PLAYBOOK DOES:
# This playbooks basically takes a build/ directory similar to what the Gitlab
# pipelines generate and uploads those files to the desired directory
---
- hosts: {{ main_host }}
remote_user: {{ remote_user }}
tasks:
- name: Build skeleton root directory
file:
path: '{{remote_app_dir}}'
state: directory
- name: Build skeleton static directory
file:
path: '{{remote_app_dir}}/static'
state: directory
- name: Build skeleton css directory
file:
path: '{{remote_app_dir}}/static/css'
state: directory
- name: Build skeleton js directory
file:
path: '{{remote_app_dir}}/static/js'
state: directory
- name: Build skeleton templates directory
file:
path: '{{remote_app_dir}}/templates'
state: directory
- name: Update Binary installation
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
with_items:
- { src: ../../build/static/css/style.css, dest: '{{remote_app_dir}}/static/css/'}
- { src: ../../build/static/js/index.js, dest: '{{remote_app_dir}}/static/js/'}
- { src: ../../build/static/js/category.js, dest: '{{remote_app_dir}}/static//js/'}
- { src: ../../build/static/cantfindshit.jpg, dest: '{{remote_app_dir}}/static/'}
- { src: ../../build/static/favicon.png, dest: '{{remote_app_dir}}/static/'}
- { src: ../../build/templates/list.html.tera, dest: '{{remote_app_dir}}/templates/list.html.tera'}
- { src: ../../build/templates/video.html.tera, dest: '{{remote_app_dir}}/templates/video.html.tera'}
- { src: ../../build/Rocket.toml, dest: '{{remote_app_dir}}/'}
- { src: ../../build/server, dest: '{{remote_app_dir}}/'}
- name: Restart web service
become: yes
become_method: sudo
service:
name: app
state: restarted

View File

@ -0,0 +1,2 @@
localhost

View File

@ -0,0 +1,5 @@
---
- hosts: localhost
remote_user: root
roles:
- playbooks

View File

@ -0,0 +1,2 @@
---
# vars file for playbooks

8
aws/readme.md Normal file
View File

@ -0,0 +1,8 @@
# AWS Configuration
For those that would like to deploy a minimal instance to AWS via terraform
this directory will basically have everything you need to get a working instance
up and running for very cheap with an EC2 instance.
There is still the question of preparing the EC2 instance itself however the
amount of configuration is very light.

5
aws/sample.ini Normal file
View File

@ -0,0 +1,5 @@
# NOTE: sample inventory either use your own inventory file or just
# replace the hostname/ip below
[main]
1.1.1.1

View File

@ -1,16 +0,0 @@
.cargo-builder:
image: rustlang/rust:nightly
only:
refs:
- master
before_script:
- mkdir -p build
- cp clippable-svc/templates/ clippable-svc/static/ build/ -r
after_script:
- cp clippable-svc/target/release/clippable-server build/server
- cp ./scripts/ build/ -r
- cp readme.md build/
- sh ./scripts/default-rocket-toml.sh
artifacts:
paths:
- build/

View File

@ -1,9 +0,0 @@
.docker-deploy:
image: docker:stable
services:
- docker:dind
only:
refs:
- master
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"

View File

@ -1,3 +0,0 @@
{
"rust.all_features": true
}

View File

@ -1,9 +0,0 @@
def Settings(**kwargs):
return {
'ls': {
'cargo': {
'features': ['admin'],
'noDefaultFeatures': True
}
}
}

View File

@ -10,9 +10,9 @@ cargo build --release
mkdir -p build/
cp target/release/clippable-svc build/server
cp clippable-svc/templates/ build/ -r
cp clippable-svc/static/ build -r
cp target/release/api build/server
cp api/templates/ build/ -r
cp api/static/ build -r
bash ./scripts/default-rocket-toml.sh
docker build -t registry.gitlab.com/shockrah/clippable .

View File

@ -1,8 +1,8 @@
// This module serves as convenience for admin users to upload/remove videos
// from their clippable instance. There are no fancy tricks as this is meant
// purely to be a UX thing.
import { fetch_category_videos, VideoMeta } from './category'
import { fetch_categories } from './index'
//import { fetch_category_videos } from './category'
//import { fetch_categories } from './index'
let UID: null|string = null
@ -79,6 +79,7 @@ export function populate_meta_form() {
let file = document.getElementById('video-file') as HTMLInputElement
// When we remove the file this array becomes 0 so the check is required
console.log('files found', file.files.length)
if(file.files.length == 0) {
document.getElementById('video-meta').hidden = true
} else {
@ -92,24 +93,6 @@ export function populate_meta_form() {
}
}
async function populate_video_list() {
const categories = await fetch_categories()
let videos: Array<VideoMeta> = []
for(const cat of categories) {
const vids = await fetch_category_videos(cat.name)
for(const v of vids) {
videos.push(v)
}
}
const list_ref = document.getElementById("videos-list")
for(const video of videos) {
list_ref.appendChild(video.as_li())
}
}
document.addEventListener('DOMContentLoaded', () => {
/*
* Setting up hooks required for functionality
@ -117,7 +100,4 @@ document.addEventListener('DOMContentLoaded', () => {
document.getElementById('video-file').onchange = populate_meta_form
document.getElementById('verify-login-btn').onclick = confirm_auth
document.getElementById('confirm-upload-btn').onclick = upload_video
populate_video_list()
.then(value => console.log('succesful list population: ', value))
.catch(reason => console.log('Failure in populate_video_list', reason))
})

View File

@ -1,9 +1,8 @@
export class VideoMeta {
class VideoMeta {
name: string|null
thumbnail: string|null
category: string
basename: string|null
href: string
constructor(raw: any) {
this.name = raw['name']
@ -15,8 +14,6 @@ export class VideoMeta {
this.basename = this.name ?
this.name.slice(0, this.name.lastIndexOf('.')) :
null
this.href = `/clip/${this.category}/${this.basename}`
}
private clean_link(link: string) : string {
@ -31,7 +28,7 @@ export class VideoMeta {
let container = document.createElement('h2')
let link = document.createElement('a')
if(this.name) {
link.href = this.href
link.href = `/clip/${this.category}/${this.basename}`
link.text = this.clean_link(this.name)
} else {
link.href = '#'
@ -80,38 +77,10 @@ export class VideoMeta {
return container
}
public as_li() : HTMLElement {
const li = document.createElement('li')
li.className = 'align-left list-group-item'
const link = document.createElement('a')
link.href = `/clip/${this.category}/${this.name}`
link.target = '_blank'
link.textContent = this.name
link.className = 'admin-video-li btn'
li.appendChild(link)
const thumbnail_link = document.createElement('a')
thumbnail_link.href = `/thumbnail/${this.category}/${this.name}.jpg`
link.target = '_blank'
thumbnail_link.innerHTML = '<i class="fa-solid fa-image"></i>'
thumbnail_link.className = 'admin-video-li btn'
li.appendChild(thumbnail_link)
const delete_btn = document.createElement('button')
delete_btn.type = 'button'
delete_btn.className = 'btn btn-danger'
delete_btn.innerHTML = '<i class="fa-solid fa-trash"></i>'
li.appendChild(delete_btn)
return li
}
}
export async function fetch_category_videos(name?: string) : Promise<Array<VideoMeta>> {
const category = name ? name : window.location.pathname
const endpoint = window.location.origin + `/api/category/${category}`
export async function fetch_category_videos() : Promise<Array<VideoMeta>> {
const endpoint = window.location.origin + '/api' + window.location.pathname
let videos: Array<VideoMeta> = []
const response = await fetch(endpoint)
if(response.headers.get('Content-Type') == 'application/json') {