Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
a55990db40 |
@ -1,17 +0,0 @@
|
||||
config:
|
||||
port: 9963
|
||||
app-name: pan-disk
|
||||
app-version: 1.1
|
||||
data-source:
|
||||
mysql:
|
||||
username: pan
|
||||
password: PhXCRiCfEmiGesEm
|
||||
host: 10.0.0.4
|
||||
port: 3307
|
||||
database: pan
|
||||
redis:
|
||||
host: 10.0.0.4
|
||||
port: 6380
|
||||
database: 0
|
||||
username: ''
|
||||
password: redis_kTJpsa
|
@ -22,7 +22,8 @@ version = VERSIONS
|
||||
|
||||
val libDir = rootDir.resolve("build").resolve("libs")
|
||||
val srcDir = rootDir.resolve("src").resolve("main").resolve("java")
|
||||
val webDir = rootDir.resolve("src").resolve("main").resolve("resources").resolve("html")
|
||||
val webDir =
|
||||
rootDir.resolve("src").resolve("main").resolve("resources").resolve("html")
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
@ -31,7 +32,10 @@ java {
|
||||
|
||||
allprojects {
|
||||
configurations.all {
|
||||
exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
|
||||
exclude(
|
||||
group = "org.springframework.boot",
|
||||
module = "spring-boot-starter-logging"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@ -71,6 +75,7 @@ dependencies {
|
||||
implementation("cn.dev33:sa-token-spring-boot3-starter:1.44.0")
|
||||
implementation("cn.dev33:sa-token-redis-jackson:1.44.0")
|
||||
implementation("com.baomidou:mybatis-plus-spring-boot3-starter:3.5.12")
|
||||
implementation("org.apache.tika:tika-core:3.2.0")
|
||||
}
|
||||
|
||||
private fun generateHash(file: File, string: String): String {
|
||||
@ -126,7 +131,8 @@ tasks.bootJar {
|
||||
attributes["Implementation-Version"] = VERSIONS
|
||||
attributes["Email"] = "minglipro@163.com"
|
||||
attributes["Implementation-Vendor"] = "minglipro|Armamem0t"
|
||||
attributes["Copyright"] = "Copyright 2026 minglipro All rights reserved."
|
||||
attributes["Copyright"] =
|
||||
"Copyright 2026 minglipro All rights reserved."
|
||||
attributes["Env"] = "prod"
|
||||
attributes["LICENSE"] = "Apache License 2.0"
|
||||
attributes["Created"] = "2025-06-26 09:13:51"
|
||||
|
@ -1,16 +1,33 @@
|
||||
package com.mingliqiye.disk;
|
||||
|
||||
import com.mingliqiye.disk.configuration.Config;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@SpringBootApplication
|
||||
@ConfigurationPropertiesScan
|
||||
@MapperScan("com.mingliqiye.disk.mappers")
|
||||
public class DiskApplication {
|
||||
private final Config config;
|
||||
|
||||
public DiskApplication(Config config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DiskApplication.class, args);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
File bolbFileDir = new File(config.getBolbPath());
|
||||
if (!bolbFileDir.exists()) {
|
||||
bolbFileDir.mkdirs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
27
src/main/java/com/mingliqiye/disk/config/AsyncConfig.java
Normal file
27
src/main/java/com/mingliqiye/disk/config/AsyncConfig.java
Normal file
@ -0,0 +1,27 @@
|
||||
package com.mingliqiye.disk.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
public class AsyncConfig {
|
||||
|
||||
@Bean("fileUploadTaskExecutor")
|
||||
public Executor fileUploadTaskExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(5); // 核心线程数
|
||||
executor.setMaxPoolSize(10); // 最大线程数
|
||||
executor.setQueueCapacity(100); // 队列容量
|
||||
executor.setThreadNamePrefix("upload-"); // 线程前缀
|
||||
executor.setRejectedExecutionHandler(
|
||||
new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.GenericToStringSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
@Configuration
|
||||
@ -13,11 +14,26 @@ public class RedisConfig {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
public RedisTemplate<String, Object> redisTemplateObj(
|
||||
RedisConnectionFactory connectionFactory
|
||||
) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
return template;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, Integer> redisTemplateInt(
|
||||
RedisConnectionFactory connectionFactory
|
||||
) {
|
||||
RedisTemplate<String, Integer> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(
|
||||
new GenericToStringSerializer<>(Integer.class));
|
||||
template.afterPropertiesSet();
|
||||
return template;
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,14 @@ package com.mingliqiye.disk.config;
|
||||
|
||||
import cn.dev33.satoken.stp.StpInterface;
|
||||
import com.mingliqiye.disk.mappers.UserMapper;
|
||||
import com.mingliqiye.disk.model.User;
|
||||
import com.mingliqiye.disk.model.UserModel;
|
||||
import com.mingliqiye.disk.uuid.UUID;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class StpInterfaceImpl implements StpInterface {
|
||||
@ -24,10 +25,12 @@ public class StpInterfaceImpl implements StpInterface {
|
||||
*/
|
||||
@Override
|
||||
public List<String> getPermissionList(Object loginId, String loginType) {
|
||||
User user = userMapper.selectById(UUID.ofString((String) loginId));
|
||||
UserModel user = userMapper.selectById(UUID.ofString((String) loginId));
|
||||
List<String> list = new ArrayList<>(user.getPrermissions());
|
||||
if (user.isAdmin()) list.add("*.*.*");
|
||||
user.getRoles().forEach(i -> list.add(String.format("%s.*.*", i)));
|
||||
if (user.isAdmin()) {
|
||||
list.add("*.*.*");
|
||||
}
|
||||
user.getRoles().forEach((i) -> list.add(String.format("%s.*.*", i)));
|
||||
log.info(String.valueOf(list));
|
||||
return list;
|
||||
}
|
||||
|
@ -10,5 +10,7 @@ public class Config {
|
||||
private String appName;
|
||||
private String appVersion;
|
||||
private Integer port;
|
||||
private String bolbPath;
|
||||
private String jwtSecretKey;
|
||||
private DataSource dataSource;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import com.mingliqiye.disk.dto.auth.Login;
|
||||
import com.mingliqiye.disk.exception.InternalServerException;
|
||||
import com.mingliqiye.disk.http.Respose;
|
||||
import com.mingliqiye.disk.mappers.UserMapper;
|
||||
import com.mingliqiye.disk.model.User;
|
||||
import com.mingliqiye.disk.model.UserModel;
|
||||
import com.mingliqiye.disk.util.PanStpUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
@ -32,7 +32,7 @@ public class AuthController {
|
||||
@Operation(summary = "登陆")
|
||||
@PostMapping("/login")
|
||||
public Respose<String> login(@Valid @RequestBody Login loginBody) {
|
||||
User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", loginBody.getUsername()));
|
||||
UserModel user = userMapper.selectOne(new QueryWrapper<UserModel>().eq("username", loginBody.getUsername()));
|
||||
if (user != null && BCrypt.checkpw(loginBody.getPassword(), user.getPassword())) {
|
||||
return Respose.builder(PanStpUtil.login(user.getId()));
|
||||
}
|
||||
@ -42,7 +42,7 @@ public class AuthController {
|
||||
@Operation(summary = "获取当前登陆用户的信息")
|
||||
@SecurityRequirement(name = "Authorization-Bearer-Token")
|
||||
@GetMapping("/who-is-me")
|
||||
public Respose<User> whoIsMe() {
|
||||
public Respose<UserModel> whoIsMe() {
|
||||
return Respose.builder().setData(userMapper.selectById(PanStpUtil.getLoginId()).setPasswordNull());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,50 @@
|
||||
package com.mingliqiye.disk.controller;
|
||||
|
||||
import com.mingliqiye.disk.dto.file.FileChunk;
|
||||
import com.mingliqiye.disk.http.Respose;
|
||||
import com.mingliqiye.disk.service.FileService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.SchemaProperty;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/apis/file")
|
||||
public class FileController {
|
||||
|
||||
private final FileService fileService;
|
||||
|
||||
public FileController(FileService fileService) {
|
||||
this.fileService = fileService;
|
||||
}
|
||||
|
||||
@RequestBody(content = @Content(mediaType = "multipart/form-data", schema = @Schema(type = "object"), schemaProperties = {
|
||||
@SchemaProperty(name = "file", schema = @Schema(type = "string", format = "binary"))}))
|
||||
@Operation(summary = "上传文件")
|
||||
@PostMapping("upload")
|
||||
|
||||
public Respose<Object> uploadMulti(
|
||||
@RequestParam("chunkNumber") Integer chunkNumber,
|
||||
@RequestParam("totalChunks") Integer totalChunks,
|
||||
@RequestParam("fileId") String fileId,
|
||||
@RequestParam("filename") String filename,
|
||||
@RequestParam("file") MultipartFile file
|
||||
) {
|
||||
FileChunk chunk = new FileChunk();
|
||||
chunk.setFileId(fileId);
|
||||
chunk.setChunkNumber(chunkNumber);
|
||||
chunk.setTotalChunks(totalChunks);
|
||||
chunk.setFilename(filename);
|
||||
chunk.setFile(file);
|
||||
chunk.setChunkSize(file.getSize());
|
||||
|
||||
fileService.uploadChunk(chunk);
|
||||
return Respose.builder();
|
||||
}
|
||||
}
|
22
src/main/java/com/mingliqiye/disk/dto/file/FileChunk.java
Normal file
22
src/main/java/com/mingliqiye/disk/dto/file/FileChunk.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.mingliqiye.disk.dto.file;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class FileChunk {
|
||||
|
||||
private String fileId;
|
||||
private Integer chunkNumber;
|
||||
private Long chunkSize;
|
||||
private Long totalSize;
|
||||
private Integer totalChunks;
|
||||
private String filename;
|
||||
private MultipartFile file;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.mingliqiye.disk.dto.file;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UploadProgress {
|
||||
|
||||
private String fileId;
|
||||
private int uploadedChunks;
|
||||
private int totalChunks;
|
||||
private double progress;
|
||||
}
|
@ -2,8 +2,9 @@ package com.mingliqiye.disk.mappers;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.mingliqiye.disk.cache.MyBatisRedisCache;
|
||||
import com.mingliqiye.disk.model.User;
|
||||
import com.mingliqiye.disk.model.UserModel;
|
||||
import org.apache.ibatis.annotations.CacheNamespace;
|
||||
|
||||
@CacheNamespace(implementation = MyBatisRedisCache.class)
|
||||
public interface UserMapper extends BaseMapper<User> {}
|
||||
public interface UserMapper extends BaseMapper<UserModel> {
|
||||
}
|
||||
|
22
src/main/java/com/mingliqiye/disk/model/BlobModel.java
Normal file
22
src/main/java/com/mingliqiye/disk/model/BlobModel.java
Normal file
@ -0,0 +1,22 @@
|
||||
package com.mingliqiye.disk.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.mingliqiye.disk.uuid.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@TableName("`blob`")
|
||||
public class BlobModel {
|
||||
|
||||
private UUID id;
|
||||
private String name;
|
||||
private String sha256;
|
||||
private Long size;
|
||||
private UUID father;
|
||||
}
|
27
src/main/java/com/mingliqiye/disk/model/FileModel.java
Normal file
27
src/main/java/com/mingliqiye/disk/model/FileModel.java
Normal file
@ -0,0 +1,27 @@
|
||||
package com.mingliqiye.disk.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.mingliqiye.disk.uuid.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@TableName("file")
|
||||
public class FileModel {
|
||||
|
||||
private UUID id;
|
||||
private String path;
|
||||
private String mimeType;
|
||||
private String sha256;
|
||||
private Long size;
|
||||
private UUID blobId;
|
||||
private UUID createUserId;
|
||||
private UUID createTime;
|
||||
private UUID updateUserId;
|
||||
private UUID updateTime;
|
||||
}
|
@ -6,18 +6,19 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import com.mingliqiye.disk.time.DateTime;
|
||||
import com.mingliqiye.disk.uuid.UUID;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@TableName(value = "users", autoResultMap = true)
|
||||
public class User {
|
||||
public class UserModel {
|
||||
|
||||
private UUID id;
|
||||
private String username;
|
||||
@ -46,7 +47,7 @@ public class User {
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private DateTime updateTime;
|
||||
|
||||
public User setPasswordNull() {
|
||||
public UserModel setPasswordNull() {
|
||||
setPassword("***");
|
||||
return this;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.mingliqiye.disk.repository;
|
||||
|
||||
import com.mingliqiye.disk.dto.file.FileChunk;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Repository
|
||||
public class RedisFileChunkRepository {
|
||||
|
||||
private final RedisTemplate<String, Integer> redisTemplate;
|
||||
|
||||
public RedisFileChunkRepository(
|
||||
RedisTemplate<String, Integer> redisTemplate
|
||||
) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
public void saveChunkInfo(FileChunk chunk) {
|
||||
redisTemplate.opsForSet()
|
||||
.add("file:" + chunk.getFileId(), chunk.getChunkNumber());
|
||||
}
|
||||
|
||||
public List<Integer> getUploadedChunks(String fileId) {
|
||||
Set<Integer> members =
|
||||
redisTemplate.opsForSet().members("file:" + fileId);
|
||||
return members != null ? new ArrayList<>(members) : new ArrayList<>();
|
||||
}
|
||||
|
||||
public int getUploadedChunksCount(String fileId) {
|
||||
Long count = redisTemplate.opsForSet().size("file:" + fileId);
|
||||
return count != null ? count.intValue() : 0;
|
||||
}
|
||||
|
||||
public boolean existsChunk(String fileId, Integer chunkNumber) {
|
||||
Boolean exists =
|
||||
redisTemplate.opsForSet().isMember("file:" + fileId, chunkNumber);
|
||||
return exists != null && exists;
|
||||
}
|
||||
|
||||
public void deleteByFileId(String fileId) {
|
||||
redisTemplate.delete("file:" + fileId);
|
||||
}
|
||||
}
|
12
src/main/java/com/mingliqiye/disk/service/FileService.java
Normal file
12
src/main/java/com/mingliqiye/disk/service/FileService.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.mingliqiye.disk.service;
|
||||
|
||||
import com.mingliqiye.disk.dto.file.FileChunk;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class FileService {
|
||||
|
||||
public void uploadChunk(FileChunk fileChunk){
|
||||
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
config:
|
||||
port: 9963
|
||||
app-name: pan-disk
|
||||
app-version: 1.1
|
||||
app-version: 0.1
|
||||
jwt-secret-key: pzmqljgvdyidabacksmoaghgyikxoasckjhgyuhij
|
||||
bolb-path: data/bolb
|
||||
data-source:
|
||||
mysql:
|
||||
username: pan
|
||||
@ -54,6 +56,11 @@ spring:
|
||||
web:
|
||||
resources:
|
||||
static-locations: classpath:/html/static
|
||||
config:
|
||||
import:
|
||||
- optional:file:data/cofing.yaml
|
||||
- optional:file:data/cofing.yml
|
||||
- optional:file:data/cofing.properties
|
||||
|
||||
|
||||
server:
|
||||
@ -68,7 +75,7 @@ sa-token:
|
||||
is-share: false
|
||||
token-style: random-128
|
||||
is-log: false
|
||||
jwt-secret-key: pzmqljgvdyidabacksmoaghgyikxoasckjhgyuhij
|
||||
jwt-secret-key: ${config.jwt-secret-key}
|
||||
token-prefix: Bearer
|
||||
is-read-body: false
|
||||
is-read-cookie: false
|
||||
|
12
src/main/resources/sql/blob.sql
Normal file
12
src/main/resources/sql/blob.sql
Normal file
@ -0,0 +1,12 @@
|
||||
DROP TABLE IF EXISTS `blob`;
|
||||
create table `blob`
|
||||
(
|
||||
id binary(16) default (uuid_to_bin(
|
||||
uuid(),
|
||||
1)) primary key,
|
||||
name varchar(256) unique,
|
||||
`path` varchar(256) unique,
|
||||
sha256 varchar(96) unique,
|
||||
`size` bigint,
|
||||
father binary(16)
|
||||
)
|
@ -1,3 +1,3 @@
|
||||
create database meven_repository collate utf8mb4_unicode_ci;
|
||||
create user 'meven_repository_admin'@'%' IDENTIFIED BY 'meven_repository_admin_password';
|
||||
GRANT ALL ON meven_repository.* TO 'meven_repository_admin'@'%'
|
||||
create database pan collate utf8mb4_unicode_ci;
|
||||
create user 'pan'@'%' IDENTIFIED BY 'PhXCRiCfEmiGesEm';
|
||||
GRANT ALL ON meven_repository.* TO 'pan'@'%'
|
||||
|
19
src/main/resources/sql/file.sql
Normal file
19
src/main/resources/sql/file.sql
Normal file
@ -0,0 +1,19 @@
|
||||
DROP TABLE IF EXISTS `file`;
|
||||
create table `file`
|
||||
(
|
||||
id binary(16) default (uuid_to_bin(
|
||||
uuid(),
|
||||
1)) primary key,
|
||||
path varchar(767) unique,
|
||||
mime_type varchar(100),
|
||||
sha256 varchar(96),
|
||||
blob_id binary(16) null,
|
||||
create_user_id binary(16) not null,
|
||||
create_time timestamp(6) default now(6) not null,
|
||||
update_user_id binary(16) null,
|
||||
update_time timestamp(6) default now(6),
|
||||
constraint foreign key (create_user_id) references users (id),
|
||||
constraint foreign key (update_user_id) references users (id),
|
||||
constraint foreign key (blob_id) references `blob` (id),
|
||||
index (mime_type)
|
||||
)
|
@ -201,6 +201,7 @@ html.dark {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
--atc-color: rgba(255, 255, 255, 0);
|
||||
margin-right: 10px;
|
||||
|
||||
&:hover {
|
||||
--atc-color: #ffd56f;
|
||||
|
Loading…
x
Reference in New Issue
Block a user