mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-08 12:18:43 +00:00
feat(web/image_hosting): image upload
This commit is contained in:
parent
2d9d99e1a6
commit
0a7a6cbe9e
@ -837,16 +837,20 @@ UNLOCK TABLES;
|
|||||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
/*!40101 SET character_set_client = utf8mb4 */;
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
CREATE TABLE `users_images` (
|
CREATE TABLE `users_images` (
|
||||||
`id` varchar(30) NOT NULL,
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`username` varchar(20) NOT NULL,
|
`path` varchar(100) NOT NULL,
|
||||||
|
`uploader` varchar(20) NOT NULL,
|
||||||
`width` int(11) NOT NULL,
|
`width` int(11) NOT NULL,
|
||||||
`height` int(11) NOT NULL,
|
`height` int(11) NOT NULL,
|
||||||
`upload_time` datetime NOT NULL,
|
`upload_time` datetime NOT NULL,
|
||||||
`size` int(11) NOT NULL,
|
`size` int(11) NOT NULL,
|
||||||
|
`hash` varchar(70) NOT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `username` (`username`),
|
KEY `uploader` (`uploader`),
|
||||||
|
KET `path` (`path`),
|
||||||
KEY `upload_time` (`upload_time`),
|
KEY `upload_time` (`upload_time`),
|
||||||
KEY `size` (`size`)
|
KEY `size` (`size`),
|
||||||
|
KEY `hash` (`hash`)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
@ -11,6 +11,14 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
- MYSQL_DATABASE=app_uoj233
|
- MYSQL_DATABASE=app_uoj233
|
||||||
- MYSQL_ROOT_PASSWORD=root
|
- MYSQL_ROOT_PASSWORD=root
|
||||||
|
|
||||||
|
phpmyadmin:
|
||||||
|
image: phpmyadmin
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 28080:80
|
||||||
|
environment:
|
||||||
|
- PMA_ARBITRARY=1
|
||||||
|
|
||||||
uoj-judger:
|
uoj-judger:
|
||||||
build:
|
build:
|
||||||
|
@ -1,21 +1,279 @@
|
|||||||
<?php
|
<?php
|
||||||
requirePHPLib('form');
|
use Gregwar\Captcha\PhraseBuilder;
|
||||||
|
use Gregwar\Captcha\CaptchaBuilder;
|
||||||
|
|
||||||
if (!Auth::check() && UOJConfig::$data['switch']['force-login']) {
|
requirePHPLib('form');
|
||||||
|
requireLib('bootstrap5');
|
||||||
|
|
||||||
|
if (!Auth::check()) {
|
||||||
redirectToLogin();
|
redirectToLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isNormalUser($myUser) && UOJConfig::$data['switch']['force-login']) {
|
if (!isNormalUser($myUser)) {
|
||||||
become403Page();
|
become403Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
$REQUIRE_LIB['bootstrap5'] = '';
|
$limit = $myUser['images_size_limit'];
|
||||||
|
$used = DB::select("SELECT SUM(size) FROM `users_images` WHERE uploader = {$myUser['username']}")["SUM(size)"];
|
||||||
|
|
||||||
|
function throwError($msg) {
|
||||||
|
die(json_encode(['status' => 'error', 'message' => $msg]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedTypes = [IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF];
|
||||||
|
if ($_POST['image_upload_file_submit'] == 'submit') {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
if (!crsf_check()) {
|
||||||
|
throwError('expired');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_SESSION['phrase']) || !PhraseBuilder::comparePhrases($_SESSION['phrase'], $_POST['captcha'])) {
|
||||||
|
throwError("bad_captcha");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_FILES["image_upload_file"]["error"] > 0) {
|
||||||
|
throwError($_FILES["image_upload_file"]["error"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_FILES["image_upload_file"]["size"] > 5242880) { // 5 MB
|
||||||
|
throwError('too_large');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($used + $_FILES["image_upload_file"]["size"] > $limit) {
|
||||||
|
throwError('storage_limit_exceeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
$size = getimagesize($_FILES['image_upload_file']['tmp_name']);
|
||||||
|
|
||||||
|
if (!$size || !in_array($size[2], $allowedTypes)) {
|
||||||
|
throwError('not_a_image');
|
||||||
|
}
|
||||||
|
|
||||||
|
list($width, $height, $type) = $size;
|
||||||
|
$hash = hash_file("sha256", $_FILES['image_upload_file']['tmp_name']);
|
||||||
|
|
||||||
|
$watermark_text = UOJConfig::$data['profile']['oj-name-short'];
|
||||||
|
if (isSuperUser($myUser) && $_POST['watermark'] == 'no_watermark') {
|
||||||
|
$watermark_text = "";
|
||||||
|
$hash += "__no";
|
||||||
|
} elseif ($_POST['watermark'] == 'site_shortname_and_username') {
|
||||||
|
$watermark_text .= ' @'.Auth::id();
|
||||||
|
$hash += "__id";
|
||||||
|
}
|
||||||
|
|
||||||
|
$existing_image = DB::selectFirst("SELECT * FROM users_images WHERE `hash` = '$hash'");
|
||||||
|
|
||||||
|
if ($existing_image) {
|
||||||
|
die(json_encode(['status' => 'success', 'path' => $existing_image['path']]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$img = imagecreatefromstring(file_get_contents($_FILES["image_upload_file"]["tmp_name"]));
|
||||||
|
$white = imagecolorallocate($img, 255, 255, 255);
|
||||||
|
$black = imagecolorallocate($img, 150, 150, 150);
|
||||||
|
|
||||||
|
imagettftext($img, '16', 0, 10 + 1, max(0, $height - 20) + 1, $black, UOJContext::documentRoot().'/fonts/roboto-mono/RobotoMono-Bold.ttf', $watermark_text);
|
||||||
|
imagettftext($img, '16', 0, 10, max(0, $height - 20), $white, UOJContext::documentRoot().'/fonts/roboto-mono/RobotoMono-Bold.ttf', $watermark_text);
|
||||||
|
imagepng($img, $_FILES["image_upload_file"]["tmp_name"]);
|
||||||
|
imagedestroy($img);
|
||||||
|
|
||||||
|
$filename = uojRandAvaiableFileName('/image_hosting/', 10, '.png');
|
||||||
|
if (!move_uploaded_file($_FILES["image_upload_file"]["tmp_name"], UOJContext::storagePath().$filename)) {
|
||||||
|
throwError('unknown error');
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::insert("INSERT INTO users_images (`path`, uploader, width, height, upload_time, size, `hash`) VALUES ('$filename', '{$myUser['username']}', $width, $height, now(), {$_FILES["image_upload_file"]["size"]}, '$hash')");
|
||||||
|
|
||||||
|
die(json_encode(['status' => 'success', 'path' => $filename]));
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<?php echoUOJPageHeader(UOJLocale::get('image hosting')) ?>
|
<?php echoUOJPageHeader(UOJLocale::get('image hosting')) ?>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.drop {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-self: center;
|
||||||
|
flex-grow: 0 !important;
|
||||||
|
width: 9em;
|
||||||
|
height: 8.75em;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 0;
|
||||||
|
background: #fafafa;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drop:hover {
|
||||||
|
border-color: #89d1f5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<h1 class="h2">
|
<h1 class="h2">
|
||||||
<?= UOJLocale::get('image hosting') ?>
|
<?= UOJLocale::get('image hosting') ?>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
<div class="card card-default">
|
||||||
|
<div class="card-body">
|
||||||
|
<form class="row m-0" id="image-upload-form" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="col-12 col-md-3 drop mx-auto" id="image-upload-form-drop">
|
||||||
|
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" width="56" class="mb-2">
|
||||||
|
<g>
|
||||||
|
<path fill="#3498db" d="M424.49 120.48a12 12 0 0 0-17 0L272 256l-39.51-39.52a12 12 0 0 0-17 0L160 272v48h352V208zM64 336V128H48a48 48 0 0 0-48 48v256a48 48 0 0 0 48 48h384a48 48 0 0 0 48-48v-16H144a80.09 80.09 0 0 1-80-80z"></path>
|
||||||
|
<path fill="#89d1f5" d="M528 32H144a48 48 0 0 0-48 48v256a48 48 0 0 0 48 48h384a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48zM208 80a48 48 0 1 1-48 48 48 48 0 0 1 48-48zm304 240H160v-48l55.52-55.52a12 12 0 0 1 17 0L272 256l135.52-135.52a12 12 0 0 1 17 0L512 208z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
<span class="small">点击此处选择图片</span>
|
||||||
|
</div>
|
||||||
|
<input id="image_upload_file" name="image_upload_file" type="file" accept="image/*" style="display: none;" />
|
||||||
|
<div class="modal fade" id="image-upload-modal" tabindex="-1" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title fs-5" id="exampleModalLabel">上传图片</h1>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
您确定要上传图片吗?
|
||||||
|
</div>
|
||||||
|
<div class="mb-3" id="modal-file-info"></div>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control" id="input-captcha" name="captcha" placeholder="<?= UOJLocale::get('enter verification code') ?>" maxlength="20" />
|
||||||
|
<span class="input-group-text p-0 overflow-hidden rounded-0" style="border-bottom-right-radius: var(--bs-border-radius) !important">
|
||||||
|
<img id="captcha" class="col w-100 h-100" src="/captcha">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3" id="modal-help-message" style="display: none"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||||
|
<button type="submit" class="btn btn-primary">确定</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3 mt-3 mt-md-0 ms-md-2">
|
||||||
|
<h2 class="h4">水印</h2>
|
||||||
|
<?php if (isSuperUser($myUser)): ?>
|
||||||
|
<div class="form-check d-inline-block d-md-block me-2">
|
||||||
|
<input class="form-check-input" type="radio" name="watermark" id="watermark-no_watermark" data-value="no_watermark">
|
||||||
|
<label class="form-check-label" for="watermark-no_watermark">
|
||||||
|
无水印
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<?php endif ?>
|
||||||
|
<div class="form-check d-inline-block d-md-block me-2">
|
||||||
|
<input class="form-check-input" type="radio" name="watermark" id="watermark-site_shortname" data-value="site_shortname" checked>
|
||||||
|
<label class="form-check-label" for="watermark-site_shortname">
|
||||||
|
<?= UOJConfig::$data['profile']['oj-name-short'] ?>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check d-inline-block d-md-block me-2">
|
||||||
|
<input class="form-check-input" type="radio" name="watermark" id="watermark-site_shortname_and_username" data-value="site_shortname_and_username">
|
||||||
|
<label class="form-check-label" for="watermark-site_shortname_and_username">
|
||||||
|
<?= UOJConfig::$data['profile']['oj-name-short'] ?> @<?= Auth::id() ?>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col mt-3 mt-md-0 ms-md-2">
|
||||||
|
<h2 class="h4">上传须知</h2>
|
||||||
|
<ul>
|
||||||
|
<li>上传的图片必须符合法律与社会道德;</li>
|
||||||
|
<li>图床仅供 S2OJ 站内使用;</li>
|
||||||
|
<li>在合适的地方插入图片即可引用。</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var image_upload_modal = new bootstrap.Modal('#image-upload-modal');
|
||||||
|
|
||||||
|
function refreshCaptcha() {
|
||||||
|
var timestamp = new Date().getTime();
|
||||||
|
$("#captcha").attr("src", "/captcha" + '?' + timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#captcha").click(function(e) {
|
||||||
|
refreshCaptcha();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#image-upload-form').submit(function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var data = new FormData();
|
||||||
|
data.append('_token', "<?= crsf_token() ?>");
|
||||||
|
data.append('image_upload_file_submit', 'submit');
|
||||||
|
data.append('image_upload_file', $('#image_upload_file').prop('files')[0]);
|
||||||
|
data.append('watermark', $('input[name=watermark]:checked', this).data('value'));
|
||||||
|
data.append('captcha', $('#input-captcha').val());
|
||||||
|
|
||||||
|
if ($('#image_upload_file').prop('files')[0].size > 5242880) {
|
||||||
|
$('#modal-help-message').html('图片大小不能超过 5 MB。').show();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#modal-help-message').html('上传中...').show();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
data: data,
|
||||||
|
success: function(data) {
|
||||||
|
if (data.status === 'success') {
|
||||||
|
image_upload_modal.hide();
|
||||||
|
location.reload();
|
||||||
|
} else {
|
||||||
|
if (data.message === 'bad_captcha') {
|
||||||
|
refreshCaptcha();
|
||||||
|
$('#modal-help-message').html('验证码错误。').show();
|
||||||
|
} else if (data.message === 'expired') {
|
||||||
|
$('#modal-help-message').html('页面过期,请刷新重试。').show();
|
||||||
|
} else if (data.message === 'storage_limit_exceeded') {
|
||||||
|
$('#modal-help-message').html('存储超限,请联系管理员提升限制。').show();
|
||||||
|
} else if (data.message === 'not_a_image') {
|
||||||
|
$('#modal-help-message').html('文件格式不受支持。').show();
|
||||||
|
} else if (data.message === 'too_large') {
|
||||||
|
$('#modal-help-message').html('图片大小不能超过 5 MB。').show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$('#image-upload-form-drop').click(function() {
|
||||||
|
$('#image_upload_file').click();
|
||||||
|
});
|
||||||
|
$('#image_upload_file').change(function() {
|
||||||
|
if ($(this).prop('files')) {
|
||||||
|
refreshCaptcha();
|
||||||
|
var watermark_type = $('input[name=watermark]:checked', '#image-upload-form').data('value');
|
||||||
|
var html = '';
|
||||||
|
|
||||||
|
html += '大小:<b>'+($(this).prop('files')[0].size / 1024).toFixed(2)+'</b> KB。';
|
||||||
|
|
||||||
|
if (watermark_type === 'no_watermark') {
|
||||||
|
html += '不添加水印。';
|
||||||
|
} else if (watermark_type === 'site_shortname_and_username') {
|
||||||
|
html += '使用水印:<?= UOJConfig::$data['profile']['oj-name-short'] ?> @<?= Auth::id() ?>。';
|
||||||
|
} else {
|
||||||
|
html += '使用水印:<?= UOJConfig::$data['profile']['oj-name-short'] ?>。';
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#modal-file-info').html(html);
|
||||||
|
$('#modal-help-message').html('').hide();
|
||||||
|
image_upload_modal.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<?php echoUOJPageFooter() ?>
|
<?php echoUOJPageFooter() ?>
|
||||||
|
@ -17,7 +17,7 @@ function uojRandAvaiableFileName($dir, $length = 20, $suffix = '') {
|
|||||||
do {
|
do {
|
||||||
$fileName = $dir . uojRandString($length);
|
$fileName = $dir . uojRandString($length);
|
||||||
} while (file_exists(UOJContext::storagePath().$fileName.$suffix));
|
} while (file_exists(UOJContext::storagePath().$fileName.$suffix));
|
||||||
return $fileName;
|
return $fileName.$suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
function uojRandAvaiableTmpFileName() {
|
function uojRandAvaiableTmpFileName() {
|
||||||
|
@ -81,8 +81,8 @@ Route::group([
|
|||||||
Route::any('/click-zan', '/click_zan.php');
|
Route::any('/click-zan', '/click_zan.php');
|
||||||
|
|
||||||
// Image Hosting
|
// Image Hosting
|
||||||
Route::any('/image-hosting', '/image_hosting/index.php');
|
Route::any('/image_hosting', '/image_hosting/index.php');
|
||||||
Route::get('/image-hosting/{image_name}.png', '/image_hosting/get_image.php');
|
Route::get('/image_hosting/{image_name}.png', '/image_hosting/get_image.php');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -6,16 +6,20 @@ ALTER TABLE `user_info` ADD COLUMN `images_size_limit` int(11) UNSIGNED NOT NULL
|
|||||||
|
|
||||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
/*!40101 SET character_set_client = utf8mb4 */;
|
/*!40101 SET character_set_client = utf8mb4 */;
|
||||||
CREATE TABLE IF NOT EXISTS `users_images` (
|
CREATE TABLE `users_images` (
|
||||||
`id` varchar(30) NOT NULL,
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
`username` varchar(20) NOT NULL,
|
`path` varchar(100) NOT NULL,
|
||||||
|
`uploader` varchar(20) NOT NULL,
|
||||||
`width` int(11) NOT NULL,
|
`width` int(11) NOT NULL,
|
||||||
`height` int(11) NOT NULL,
|
`height` int(11) NOT NULL,
|
||||||
`upload_time` datetime NOT NULL,
|
`upload_time` datetime NOT NULL,
|
||||||
`size` int(11) NOT NULL,
|
`size` int(11) NOT NULL,
|
||||||
|
`hash` varchar(70) NOT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `username` (`username`),
|
KEY `uploader` (`uploader`),
|
||||||
|
KEY `path` (`path`),
|
||||||
KEY `upload_time` (`upload_time`),
|
KEY `upload_time` (`upload_time`),
|
||||||
KEY `size` (`size`)
|
KEY `size` (`size`),
|
||||||
|
KEY `hash` (`hash`)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
@ -111,7 +111,7 @@ mb-4" role="navigation">
|
|||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="<?= HTML::url('/image-hosting') ?>">
|
<a class="dropdown-item" href="<?= HTML::url('/image_hosting') ?>">
|
||||||
<i class="bi bi-images"></i>
|
<i class="bi bi-images"></i>
|
||||||
<?= UOJLocale::get('image hosting') ?>
|
<?= UOJLocale::get('image hosting') ?>
|
||||||
</a>
|
</a>
|
||||||
|
BIN
web/fonts/roboto-mono/RobotoMono-Bold.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-Bold.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-BoldItalic.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-ExtraLight.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-ExtraLightItalic.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-ExtraLightItalic.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-Italic.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-Italic.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-Light.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-Light.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-LightItalic.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-LightItalic.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-Medium.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-Medium.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-MediumItalic.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-Regular.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-Regular.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-SemiBold.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-SemiBold.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-SemiBoldItalic.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-SemiBoldItalic.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-Thin.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-Thin.ttf
Normal file
Binary file not shown.
BIN
web/fonts/roboto-mono/RobotoMono-ThinItalic.ttf
Normal file
BIN
web/fonts/roboto-mono/RobotoMono-ThinItalic.ttf
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user